/**
 * @fileoverview This is a templated file were will be inserted the module-loader
 *   ( ModuleLoader ), the compiled content of PropertySets module-robotized files
 *   ( MRCompileContent ) and the exports for this module ( DistributionExports ).
 */


(function() {

  // Obfuscated name we will use to save the 'include' and 'exportModule' functions in the global object.
  var incName = '731C5F31F5FB7B5572F8126AB507CD6E3BD84701588731AED25203ED768BE97B_Adsk_Forge_include';
  var expName = '731C5F31F5FB7B5572F8126AB507CD6E3BD84701588731AED25203ED768BE97B_Adsk_Forge_exportModule';
  var savedState = {};


  /**
   * This is where the magic happens.
   */
  function main() {

    var existingFcts = saveStateOfGlobals();
    var include = existingFcts.include;
    var exportModule = existingFcts.exportModule;

    // If neither 'include' nor 'exportModule' exist, get module-loader to define them.
    if (!include && !exportModule) {

      var mlExports = moduleLoader();

      include = mlExports.include;
      exportModule = mlExports.exportModule;

    } else if (!include) {
      console.error('\'exportModule\' is defined, but not \'include\'. You might have a problem with Module Loader.');
    } else if (!exportModule) {
      console.error('\'include\' is defined, but not \'exportModule\'. You might have a problem with Module Loader.');
    }


    // Executes the code resulting from the execution of MRCompile. This is what exports
    // all the Lynx goodies.
    compiledContent(include, exportModule);

    // From the content, define what is exported publicly.
    defineExports(include, exportModule);

    // Return the window/global object in the state it was before we executed module-loader.
    restoreStateOfGlobals();
  }


  /**
   * Look into the global objects for the presence of 'include' and 'exportModule' and save
   * those into the global saveStateOfGlobals object. If the ADSK Forge version of
   * 'include' and 'exportModules' are already in the global object, use these definitions
   * here.
   *
   * @return {object} If there are 'include' and 'exportModule' in the global namespaces
   *                  return them in this object.
   */
  function saveStateOfGlobals() {
    var retVal = {};

    // Overwrite whatever 'include' and 'exportModule' functions that already exist
    // in the global objects. Save a copy before hand so that we can restore what was
    // there at the end of the process.
    if (typeof window !== 'undefined') {
      savedState.include      = window.include      || null;
      savedState.exportModule = window.exportModule || null;

      if (window[incName]) {
        retVal.include = window[incName];
      }
      if (window[expName]) {
        retVal.exportModule = window[expName];
      }
    } else if (typeof require !== 'undefined') {
      savedState.include      = global.include      || null;
      savedState.exportModule = global.exportModule || null;

      if (global[incName]) {
        retVal.include = global[incName];
      }
      if (global[expName]) {
        retVal.exportModule = global[expName];
      }
    } else {
      console.error('We seems to be in a state where neither \'window\' nor \'global\' exist');
      throw new Error('Invalid namespace state.');
    }

    return retVal;
  }

  /**
   * Restore the 'include' and 'exportModule' functions into the global objects
   * to the original state.
   */
  function restoreStateOfGlobals() {

    // Return the window/global object in the state it was before we executed module-loader.
    if (typeof window !== 'undefined') {
      if (savedState.include) {
        window['include'] = savedState.include;
      } else {
        delete window['include'];
      }
      if (savedState.exportModule) {
        window['exportModule'] = savedState.exportModule;
      } else {
        delete window['exportModule'];
      }
    } else if (typeof require !== 'undefined') {
      if (savedState.include) {
        global['include'] = savedState.include;
      } else {
        delete global['include'];
      }
      if (savedState.exportModule) {
        global['exportModule'] = savedState.exportModule;
      } else {
        delete global['exportModule'];
      }
    }
  }

  /**
   * Module loader implementation
   * @return {object} An object with 'include' and 'exportModule' properties.
   */
  function moduleLoader() {
    var include;
    var exportModule;

    (function() {

      var MODULE_NAMESPACE_DELIMITER = '.';
      var EVENT_ALREADY_REGISTERED = 'Event already registered in ModuleLoader.';

      var MSG = {
        NAMESPACE_ERROR: 'You cannot export to a module path that is part of another module\'s namespace: ',
        DUPLICATE_LEAF: 'You cannot re-use a module in a namespace that already has a module: '
      };

      /**
       * A node in the module graph
       * @param {string} in_moduleNamespace The module, or module path you wish to save in a node.
       * @param {*} in_interface The module that is exported.
       */
      var Node = function(in_moduleNamespace, in_interface) {
        var moduleNamespaceSegments = in_moduleNamespace.split(MODULE_NAMESPACE_DELIMITER);
        this._id = moduleNamespaceSegments[moduleNamespaceSegments.length - 1];
        this._moduleNamespace = moduleNamespaceSegments;
        this._parent = null;
        this._inEdges = {};
        this._interface = in_interface || {};
        this._changeCallbacks = [];
        this._leaf = null;
      };
      Node.prototype.onChange = function(callback) {
        this._changeCallbacks.push(callback);
      };
      Node.prototype.triggerChange = function() {
        this._changeCallbacks.forEach(function(changeCallback) {
          changeCallback();
        });
      };
      Node.prototype.hasInput = function(nodeId) {
        return !!this._inEdges[nodeId];
      };
      Node.prototype.addInput = function(childNode) {
        // This is forbidden
        // exportModule('foo', 'Hello World');
        // exportModule('foo.bar', 'Hello World');
        // In this example, 'foo' is already a module
        // so it cannot also be part of the namespace of another module
        console.assert(this._leaf !== true, MSG.DUPLICATE_LEAF + this._moduleNamespace);
        var childNodeId = childNode.getId();
        this._inEdges[childNodeId] = childNode;
        this._interface[childNodeId] = childNode.getInterface();
        var that = this;
        childNode.onChange(function() {
          that._interface[childNodeId] = childNode.getInterface();
        });
        this._leaf = false;
      };
      Node.prototype.getInput = function(nodeId) {
        return this._inEdges[nodeId];
      };

      Node.prototype.getId = function() {
        return this._id;
      };
      Node.prototype.getInterface = function() {
        return this._interface;
      };
      Node.prototype.setInterface = function(in_interface) {
        // This is forbidden
        // exportModule('foo.bar', 'Hello World');
        // exportModule('foo', 'Hello World');
        // In this example, 'foo' is already part of a module namespace
        // so it cannot also be its own module
        console.assert(this._leaf !== false, MSG.NAMESPACE_ERROR + this._moduleNamespace);
        this._interface = in_interface;
        this.triggerChange();
        this._leaf = true;
      };

      /**
       * ModuleRegistrar
       * the central registry for modules in the front-end.
       * @constructor
       * @alias LYNX.ModuleRobot.ModuleRegistrar
       */
      var ModuleRegistrar = function() {
        this._root = new Node('root');
        this._onIncludeEvents = {};
        this._onExportEvents  = {};
        this._delayedRequest = {};
        this._exportedModules = {};
      };

      ModuleRegistrar.prototype.constructor = ModuleRegistrar;

      /**
       * Internal method used to expand from root.
       * @param {String} in_path the path to expand
       * @param {object} in_option some options
       * @param {boolean} in_option.lazy Whether or not it's okay if we don't find the module right-away
       * @return {Object} the node at the end of the path.
       * @private
       */
      ModuleRegistrar.prototype._expand = function( in_path, in_option ) {
        var node = this._root;

        var pathArr = in_path.split(MODULE_NAMESPACE_DELIMITER);
        for (var i = 0; i < pathArr.length; i++) {
          if (node.hasInput(pathArr[i])) {
            node = node.getInput(pathArr[i]);
          } else if (in_option.lazy) {
            node = null;
            break;
          } else {
            var moduleNamespace = pathArr.join('.');
            var errorMessage = "Module Error: path '" + moduleNamespace + "' doesn't exist\n";
            // Produce an interesting stack. Get the current stack and remove all the internal
            // module loader functions
            var stack = (new Error()).stack;
            var stackMessage = '';
            if (stack) {
              var stackLines = stack.split('\n');
              while (stackLines.length && stackLines[0].indexOf('ModuleRegistrar.include') === -1) {
                stackLines.shift(1);
              }
              if (stackLines.length) {
                stackLines.shift(1);
              }
              stackMessage = stackLines.join('\n');
            }
            throw new Error(errorMessage + stackMessage);
          }
        }

        return node;
      };

      /**
       * An expand method to make sure the path exists.
       * @param {String} in_moduleNamespace the path to expand.
       * @return {Object} the node at the end of the path.
       * @private
       */
      ModuleRegistrar.prototype._assertExpand = function( in_moduleNamespace ) {
        var moduleNamespaceSegments = in_moduleNamespace.split(MODULE_NAMESPACE_DELIMITER);

        var module = this._root;
        var moduleNamespace = moduleNamespaceSegments[0];
        for (var i = 0; i < moduleNamespaceSegments.length; i++) {
          if (i !== 0) {
            moduleNamespace = moduleNamespace + MODULE_NAMESPACE_DELIMITER + moduleNamespaceSegments[i];
          }

          var moduleNamespaceSegment = moduleNamespaceSegments[i];
          var nextModule = module.getInput(moduleNamespaceSegment);
          if (nextModule) {
            module = nextModule;
          } else {
            nextModule = new Node(moduleNamespace);
            module.addInput(nextModule);
            module = nextModule;
          }
        }

        return module;
      };

      /**
       * A method to register OnInclude events – this will get fired at every
       * inclusion call.
       * @param {String} in_key the key we give to this event
       * @param {Function} in_function the function to call when the event happens.
       */
      ModuleRegistrar.prototype.registerOnIncludeEvent = function( in_key, in_function ) {
        if (!this._onIncludeEvents[in_key]) {
          this._onIncludeEvents[in_key] = in_function;
        } else {
          throw new Error( EVENT_ALREADY_REGISTERED + ' key: ' + in_key + ' function: ' + in_function );
        }
      };

      /**
       * A internal method to delay callbacks
       * @param {String} in_moduleNamespace the ID of the module.
       * @param {Function} in_function the callback
       * @private
       */
      ModuleRegistrar.prototype._callbackWhenAvailable = function( in_moduleNamespace, in_function ) {
        if (!this._delayedRequest[ in_moduleNamespace ]) {
          this._delayedRequest[ in_moduleNamespace ] = [];
        }
        this._delayedRequest[ in_moduleNamespace].push( in_function );
      };

      /**
       * Internal method to run include callback events
       * @param {String} in_path the path of the module being included.
       * @private
       */
      ModuleRegistrar.prototype._runIncludeEvents = function(in_path) {
        var keys = Object.keys(this._onIncludeEvents);
        var l = keys.length;
        for (var i = 0; i < l; i++) {
          this._onIncludeEvents[keys[i]](in_path);
        }
      };

      /**
       * The module, or module path you wish to include. There are a couple ways to
       * make an inclusion, and it depends on the intent you are trying to express.
       * The first one is by simply saying exactly what you wish to
       * include, and associate it to a local variable:
       *
       * var RGB = include("Color.RGB");
       * In this case RGB will be the interface that was registered at "Color.RGB".
       *
       * Another way is to include the entire namespace:
       *
       * var Color = include("Color");
       * This gives a bit more freedom, but it is also more vague and you may be
       * including things that you don't need. The general recommendation is to be
       * as specific as possible, so to produce a clean dependency graph.
       * In the above example, Color.RGB, Color.CMYK and Color.LAB would be possible
       * values of the object returned by the include.
       *
       * Lastly, you can return a specific set within a namespace, for example:
       * var Color = include("Color", ["RGB", "CMYK"] );
       *
       * In this case LAB would not be available in this inclusion of the
       * Color module.
       *
       * The specialization of the keys also allow for namespacing:
       * include( "Color", [ "Utils.Randomizer", "RGB" ] );
       *
       * @param {string} in_moduleNamespace The module, or module path you wish to include
       * @param {Array.<string>=} in_specialize a specific set within a namespace
       * @return {*} The module found at the given module path.
       */
      ModuleRegistrar.prototype.include = function( in_moduleNamespace, in_specialize ) {
        var that = this;
        var module;

        if (in_specialize) {
          var specializedInterface = {};

          in_specialize.forEach(function(specialize) {
            var specificPath = in_moduleNamespace + MODULE_NAMESPACE_DELIMITER + specialize;

            // if we are specializing we will take into account this can be a definition
            // that only happens later in time – i.e. on cyclic loads. In which case
            // we keep the request in a queue for later.

            that._runIncludeEvents( specificPath );
            var subModule = that._expand(specificPath, {lazy: true});
            if (subModule) {
              var subInterface = subModule.getInterface();
              specializedInterface[specialize] = subInterface;
            } else {
              that._callbackWhenAvailable(specificPath, function( availableSubInterface ) {
                specializedInterface[specialize] = availableSubInterface;
              });
            }
          });

          module = new Node('tmp', specializedInterface);
        } else {
          this._runIncludeEvents(in_moduleNamespace);
          module = this._expand(in_moduleNamespace, {lazy: false});
        }

        return module.getInterface();

      };

      ModuleRegistrar.prototype._addExportedModule = function( in_moduleNamespace, in_interface ) {
        this._exportedModules[ in_moduleNamespace ] = in_interface;
      };

      /**
       * @param {string} in_moduleNamespace - module namespace
       * @return {*|undefined} the module or undefined if the module does not exist
       */
      ModuleRegistrar.prototype.includeOptional = function( in_moduleNamespace ) {
        return this._exportedModules[ in_moduleNamespace ];
      };

      ModuleRegistrar.prototype._executeAwaitingRequests = function( in_moduleNamespace, in_interface ) {
        // execute awaiting requests
        var requests = this._delayedRequest[ in_moduleNamespace ];
        if (requests) {
          while (requests.length > 0) {
            requests.pop()(in_interface); // empty the requests
          }

          delete this._delayedRequest[in_moduleNamespace];
        }
      };

      /**
       * Exports a module interface
       * @param {String} in_moduleNamespace the path to this module, for example "Color.RGB"
       * or "MyLibrary.Math.Vector".
       * @param {*} in_interface the interface you wish to export. It can be any
       * value, for example:
       *
       * // Exporting a Value
       * exportModule( "Math.HalfPi", Math.PI/2 );
       *
       * // Exporting a class
       * var RGBClass = function(){
       *   this.r = 0;
       *   this.g = 0;
       *   this.b = 0;
       * }
       *
       * exportModule( "Color.RGB", RGBClass );
       *
       * // Exporting a module interface
       * exportModule( "Math.Utils.CONSTANTS", {
       *   MAX_INT : Math.pow(2,53),
       *   ONE_DEGREE_IN_RADIAN : 0.017453292519943295;
       * });
       */
      ModuleRegistrar.prototype.exportModule = function(in_moduleNamespace, in_interface) {
        if ( !this.includeOptional( in_moduleNamespace ) ) {

          // run export events
          var keys = Object.keys(this._onExportEvents);
          var l = keys.length;
          for (var i = 0; i < l; i++) {
            this._onExportEvents[keys[i]](in_moduleNamespace, in_interface);
          }

          var node = this._assertExpand(in_moduleNamespace);

          // We may not have an interface, in which case we will take this was
          // intended to be a new namespace.
          if (in_interface !== undefined) {
            node.setInterface(in_interface);  // extend node
            this._executeAwaitingRequests(in_moduleNamespace, in_interface);
            this._addExportedModule(in_moduleNamespace, in_interface);
          }

        } else {
          console.warn( 'Module already exists ' + in_moduleNamespace +
            '. This is OK if and only if both module instances have the same version.');
        }
      };

      /**
       * Create our central registrar object
       * @type {ModuleRegistrar}
       */
      var registrar = new ModuleRegistrar();

      /**
       * Tells the module system to include a module when loading this file.
       * @global
       * @return {*}
       */
      var include = registrar.include.bind(registrar);

      /**
       * Query for and return module if it it exists or return undefined if it doesn't
       * @global
       * @return {*}
       */
      var includeOptional = registrar.includeOptional.bind(registrar);

      /**
       * Tell the modules system that this file exports a symbol.
       * @global
       */
      var exportModule = registrar.exportModule.bind(registrar);

      /**
       * Identify our environment and get our global object to expose module registrar
       */
      if (typeof window !== 'undefined') {
        if (window.exportModule || window.include) {
          console.warn('[ModuleLoader] The global variables `include` and `exportModule` already exist');
          console.warn('[ModuleLoader] Two instances of ModuleRobot were probably created');
        }
        window.exportModule = window.exportModule || exportModule;
        window.include = window.include || include;
        window.includeOptional = window.includeOptional || includeOptional;
      }
      if (typeof require !== 'undefined') {
        if (global.exportModule || global.include) {
          console.warn('[ModuleLoader] The global variables `include` and `exportModule` already exist');
          console.warn('[ModuleLoader] Two instances of ModuleRobot were probably created');
        }
        module.exports = function() {
          global.exportModule = global.exportModule || exportModule;
          global.include = global.include || include;
          global.includeOptional = global.includeOptional || includeOptional;
        };
      }

      /**
       * Use the module loader to export an interface to itself
       */
      exportModule( 'ModuleLoader', {
        registerOnIncludeEvent:  registrar.registerOnIncludeEvent.bind(registrar),
        exportModule:            registrar.exportModule.bind(registrar),
        include:                 registrar.include.bind(registrar),
        includeOptional: registrar.includeOptional.bind(registrar)
      });

    })();


    if (typeof window !== 'undefined') {
      include = window.include;
      exportModule = window.exportModule;

      // Insert the include/exportModule coming from the module-loader into a properties
      // that are highly improbable to exist before-hand.
      window[incName] = include;
      window[expName] = exportModule;
    } else if (typeof require !== 'undefined') {
      module.exports();  // This will export the 'include' and 'exportModule' into global.

      include = global.include;
      exportModule = global.exportModule;

      // Insert the include/exportModule coming from the module-loader into a properties
      // that are highly improbable to exist before-hand.
      global[incName] = include;
      global[expName] = exportModule;
    }

    return {
      include: include,
      exportModule: exportModule
    };
  }

  /**
   * This is the concatenation of all exports in PropertySets. This content is
   * generated by MRCompile and inserted here.
   * @param {function} include The 'include' function to be used by the compiled content.
   * @param {function} exportModule The 'exportModule' function to be used by the compiled content.
   */
  function compiledContent(include, exportModule) {

    /**
     * @fileoverview implements an effective repeatString routine which repeats a string $n$ times.
     */
    /**
     * @fileoverview Utility functions related to handling GUIDs
     */
    /**
     * Underscore.js 1.8.3
     * https://github.com/jashkenas/underscore
     *
     * Copyright (c) 2009-2017 Jeremy Ashkenas, DocumentCloud and Investigative
     * Reporters & Editors
     *
     * Permission is hereby granted, free of charge, to any person
     * obtaining a copy of this software and associated documentation
     * files (the "Software"), to deal in the Software without
     * restriction, including without limitation the rights to use,
     * copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the
     * Software is furnished to do so, subject to the following
     * conditions:
     *
     * The above copyright notice and this permission notice shall be
     * included in all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
     * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
     * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
     * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
     * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     * OTHER DEALINGS IN THE SOFTWARE.
     *
     * @license
     */
    /**
     * @fileoverview Implement a general Eventing mechanism for decoupled objects.
     * Certain kinds of events (DocumentAdd, DocumentRemove, etc...) cannot be
     * registered against an object because the object may be volatile – i.e. the
     * Document may not yet exist, or it may change. Still there are times where
     * you want your events to be always called regardless of how volatile the object
     * is. For this the volatile object must implement the triggering – as it would do
     * with EventEmitter, and the receiving end has to register with Core.Events singleton
     */
    /**
     * Async version 2.1.4
     * https://github.com/caolan/async/blob/master/dist/async.js
     *
     * Copyright (c) 2010-2017 Caolan McMahon
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     *
     * @license
     */
    /**
     * Browser Request
     *
     * 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.
     *
     * @license
     */
    /**
     * A message format for messages exchanges between collaborators
    * @param {object} params - Construction parameters.
     * @param {string=} params.room - The message room
     * @param {string=} params.event - What type of event is this. Basically it sub-types the room's channel
     * @param {object=} params.data - Any data to transmit
     * @private
     * @constructor
     */
    /**
     * @fileOverview This class manages the state (the set of Collaborators) of all of the Rooms
     */
    /**
     * @fileOverview This class manages the state of a single Collaborator
     */
    /**
     * @fileOverview This class manages the state (the set of Collaborators) of a single Room
     */
    /* eslint-disable no-use-before-define */
    /**
     * A wrapper around log4js that supports setting per module levels dynamically.
     */
    /**
     * @fileOverview A shared adapter for retreiving bearer tokens
     */
    /**
     * @fileoverview
     * URL service specialized for the LynxCollaborationServer
     */
    /**
     * @fileoverview This file contains the implementation of the NodeProperty class
     */
    /**
     * @fileoverview
     * BaseCollaborator class describing an interface to communicating with the backend
     */
    /**
     * @fileOverview A browser-side class that allows for collaboration with other collaborators
     * through the CollaborationServer
     */
    /**
     * @fileoverview
     * Simple chronometer
     */
    /**
     * @fileoverview Definition of the ConsolePrinter class
     */
    /**
     * @fileOverview This is a RESTful client to the LyncCollaborationServer API
     */
    /**
     * @fileoverview
     * URL service specialized for the PropertySetServer
     */
    /**
     * @fileoverview
     * Declaration of CONSTANTS
     */
    /**
     * @fileoverview PropertySetsServer base client
     * The BrowserClient and NodeClient inherit from this class, but this class
     * is not their superconstructor
     */
    /**
     * @fileOverview PropertySetsServer browser client
     */
    /**
     * semver
     * https://github.com/npm/node-semver
     *
     * The ISC License
     *
     * Copyright (c) Isaac Z. Schlueter and Contributors
     *
     * Permission to use, copy, modify, and/or distribute this software for any
     * purpose with or without fee is hereby granted, provided that the above
     * copyright notice and this permission notice appear in all copies.
     *
     * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
     * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     *
     * @license
     */
    /**
     * ajv 4.11.2: Another JSON Schema Validator
     * https://github.com/epoberezkin/ajv
     *
     * The MIT License (MIT)
     *
     * Copyright (c) 2015 Evgeny Poberezkin
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in all
     * copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     * SOFTWARE.
     *
     * @license
     */
    /**
     * traverse
     * https://github.com/substack/js-traverse
     *
     * Copyright 2010 James Halliday (mail@substack.net)
     *
     * This project is free software released under the MIT/X11 license:
     * http://www.opensource.org/licenses/mit-license.php
     *
     * Copyright 2010 James Halliday (mail@substack.net)
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     *
     * @license
     */
    /**
     * querystring
     *
     * Copyright Joyent, Inc. and other Node contributors.
     *
     * Permission is hereby granted, free of charge, to any person obtaining a
     * copy of this software and associated documentation files (the
     * "Software"), to deal in the Software without restriction, including
     * without limitation the rights to use, copy, modify, merge, publish,
     * distribute, sublicense, and/or sell copies of the Software, and to permit
     * persons to whom the Software is furnished to do so, subject to the
     * following conditions:
     *
     * The above copyright notice and this permission notice shall be included
     * in all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
     * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
     * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
     * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
     * USE OR OTHER DEALINGS IN THE SOFTWARE.
     *
     * @license
     */
    /**
     * The MIT License (MIT)
     * Copyright (c) 2014 Guillermo Rauch
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     *
     * @license
     */
    /* eslint accessor-pairs: [2, { "getWithoutSet": false }] */
    /**
     * @fileoverview Collection class definition
     * @module Core
     */
    /**
     * @fileoverview
     * Declaration of the SortedCollection class
     */
    /* eslint accessor-pairs: [2, { "getWithoutSet": false }] */
    /**
     @fileoverview The data arrays definition file.
     */
    /**
     * @fileoverview implements (U)Int64 Datastructures
     */
    /**
     * @fileoverview implementation of a doubly-linked list.
     *
     * Inherit from List.Node or add your data, whatever you want.
     *
     * Visit in this way:
     *  for (var i=this.begin(); i!==this.end(); i=i.next()) {
     *    callback(i);
     *  }
     *
     * or use iterate(callback)
     *
     * There's an rend() rbegin() style code, but since there aren't iterators
     * it's a little odd (you wouldn't want to do previous normally)
     *
     *  for (var i=this.rbegin(); i!==this.rend(); i=i.previous()) {
     *    callback(i);
     *  }
     */
    /**
     * @fileoverview implementation of an lru (least recently used) cache. You can do things like
     * check if it is there, insert entries, remove them, get (making it 'recently used'), as well
     * as peeking and poking at the data without affecting the recently used state.
     */
    /**
     * @fileoverview A simple datastructure that stores values based on a hierarchy of keys, like a tree.
     * When 'getting', if a path in the hierarchy doesn't exist, an ancestor of the value is looked for.
     *
     * Example:
     * map.insert(4, 'a', 'b');
     * map.insert(5, 'a');
     * map.get('a', 'b') -> 4
     * map.get('a') -> 5
     * map.get('a', 'c') -> 5 << no specialization for 'c' exists, falls back to 'a'
     */
    /**
     * @fileoverview Definition of the Sequence datatype. This is a very simple
     * double linked list.
     */
    /**
     * @fileoverview implements a Deep Copy routine to clone arbitrary objects.
     */
    /**
     * @fileoverview Utilities for creation and consumption of Lynx URNs
     */
    /**
     * @fileoverview implements a joinPaths routine to merge two paths.
     */
    /**
     * @fileoverview
     *
     * A few helper functions to make sure that an entry exists/gets deleted in a nested hierarchy of JS objects
     */
    /**
     * @fileoverview Environment Utils
     */
    /**
     * @fileoverview Helper methods for printing pretty console messages
     */
    /* eslint valid-jsdoc:1 */
    /* eslint valid-jsdoc:1 */
    /* eslint valid-jsdoc:1 */
    /* eslint valid-jsdoc:1 */
    /**
     * A base graph implementation to build Evaluators
     */
    /* eslint valid-jsdoc:1 */
    /**
     * @fileoverview
     * Utilities useful for for integrating with Apigee
     */
    /**
     * @fileoverview Implementation of an observable, bindable attribute system.
     * @module DataBindings
     */
    /* eslint-disable no-use-before-define */
    /**
     * Creates a Promise that can be fulfilled or rejected later in an arbitrary manner (rather than
     * through the constructor's executor).
     * For example, a deferred promise could be fulfilled after waiting for many asynchronous
     * tasks to terminate. This class becomes useful when combining classic async calls with promises.
     */
    /**
     * @fileoverview Utility class to compute an hash for a given set of variables
     * using the murmur3 hash (see https://code.google.com/p/smhasher/)
     *
     * This is a reimplementation of the original C++ code in JavaScript. The original
     * code is licensed under a MIT license.
     */
    /**
     * @fileoverview Helper class to create a deterministic sequence of random numbers.
     */
    /* eslint-disable no-extend-native */
    /**
     * Exports a utility function that makes it possible to serialize native Error objects to JSON.
     * Without this, calling:
     *
     * JSON.stringify(new Error('foo'));
     *
     * yields: '{}'
     *
     * See http://stackoverflow.com/questions/18391212/is-it-not-possible-to-stringify-an-error-using-json-stringify
     */
    /**
     * @fileOverview Helper function which simplifies a http request
     */
    /**
     * Promise conversion tools that allow converting from node callbacks to promises and vice versa.
     */
    /**
     * @fileoverview Shallow copy routines
     */
    /**
     * @fileoverview Utilities for management of URLs and query strings
     */
    /* eslint-disable no-use-before-define */
    /**
     * An operation error maintains additional information compared to a plain {@link #Error}:
     * - The operation name
     * - A status code
     * - Extensible flags. {@see ExtendedError.FLAGS}.
     */
    /**
     * @fileoverview
     * Declaration of the TemplateSchema module
     * The TemplateSchema is used for validating PropertySet templates that code is attempting to register
     */
    /**
     * @fileoverview Helper functions to work with typeid strings
     */
    /**
     * @fileoverview
     * Declaration of the PropertyTemplate module
     * PropertyTemplate is used to describe a static property
     */
    /**
     * @fileoverview Helper functions to work with path strings
     */
    /* eslint accessor-pairs: [2, { "getWithoutSet": false }] */
    /**
     * @fileoverview
     * The ValidationResultBuilder maintains validation context that ultimately gets returned as a
     * single result.
     */
    /**
     * @fileoverview
     * The TemplateValidator can examine consecutive versions of a given template to detect semantic
     * versioning (semver) errors and warn about incorrect versioning practices.
     */
    /* eslint-disable no-unused-vars */
    /* eslint-disable no-use-before-define */
    /**
     * @fileoverview Serialized representation of the changes in a repository
     */
    /**
     * @fileoverview Helper functions and classes to work with array ChangeSets
     */
    /**
     * @fileoverview Abstract base class for indexed collections (sets and maps)
     */
    /**
     * @namespace LYNX.Property
     */
    /**
     * @fileoverview Definition of the named property class
     */
    /**
     * @fileoverview This file contains the implementation of the NodeProperty class
     */
    /**
     * @fileoverview Definition of the named node property class
     */
    /**
     * A simple graph that supports Nodes, Edges, Cycle Checking and the description
     * of paths to Nodes through NodePaths
     */
    /**
     * @fileoverview The Commit Node for usage in commit graphs.

     */
    /**
     * @fileoverview
     * Port used for linking local and remote branch in the commit graph
     */
    /**
     * @fileoverview A node to connect branches to commit heads in CommitGraphs
     */
    /**
     * @fileoverview Definition of the repository reference property class
     */
    /**
     * @fileoverview Helper functions to cast a JavaScript type to a value that is compatible with a given primitive type
     */
    /**
     * @fileoverview Definition of the Uint32Property class
     */
    /**
     * @fileoverview Definition of the StringProperty class
     */
    /**
     * @fileoverview Definition of the Float*Property classes
     */
    /**
     * @fileoverview Definition of the Int*Property classes
     */
    /**
     * @fileoverview Definition of the BooleanProperty class
     */
    /**
     * @fileoverview Definition of the ReferenceProperty class
     */
    /**
     * @fileoverview Definition of the EnumProperty classes
     */
    /**
     * @fileoverview Definition of the set property class
     */
    /**
     * @fileoverview Definition of the map property class
     */
    /**
     * @fileoverview Definition of the reference array property class
     */
    /**
     * @fileoverview Definition of the map property class
     */
    /**
     * @fileoverview Definition of the reference map property class
     */
    /**
     * @fileoverview
     * Declaration of the JSONSchemaToPropertySetsTemplateConverter class
     * Responsible for converting JSON schema to Property Sets Templates
     */
    /**
     * @fileoverview
     * Declaration of the PropertyFactory class
     * Responsible for creating property sets and registering property templates
     */
    /**
     * @fileoverview Helper functions and classes to work with ChangeSets with indexed collections (sets and maps)
     */
    /**
     * @fileoverview This contains an internal helper class that is used to compute the order in which repository
     * update operations have to be performed
     */
    /**
     * @namespace LYNX.Property._internal
     * @alias LYNX.Property._internal
     * Namespace containing internal helper functions/classes used by the PropertySets library
     */
    /**
     * @fileoverview The data-view represents a checked-out view on a slice through the commit graph starting at a
     * supplied base commit
     */
    /**
     * @fileoverview The Commit Graph stores the commit topology via CommitNodes
     * It is always easier to imagine this graph rooted on the left, where the
     * commit history topology looks like this:
     *<pre>
     *   root --o----o----o-------o----o <-- BranchNode
     *           \               /
     *            o------o----o-' <-- BranchNode
     *</pre>
     *
     *  The connectivity is represented via CommitNodes with the following ports:
     *     inputs [ parent ]
     *     ouputs [ children ]
     *
     *  Each node may have one or more parents (in case of merges there will be at
     *  least two connections).
     *
     *  BranchNodes are simply nodes that point to a particular commit. The branch
     *  path itself is directly inferred from the graph topology.
     *
     *  A CommitNode may be part of one or more branches.
     *
     */
    /**
     * @namespace LYNX.Property
     */
    /**
     * @fileoverview The Repository Property represents the state of a Property Set
     * at a given point in time. The state is built by the Property Graph.
     *
     * When properties change over time the changes arrive at random times.
     * A Repository Property allows you to take a slice of this information and
     * 'checkout' the repository at a given point in time. Making one nice
     * complete picture of the data at any point in the change history.
     */
    /**
     * @fileoverview
     * Declaration of the Workspace class
     */
    /**
     * @fileoverview
     */
    /**
     * @fileoverview This file contains helper functions to upload/download files from within the browser.
     *
     * WARNING: Currently, these functions are only for debugging purposes, as this functionality is not fully
     *          supported on all target browsers.
     */
    (function () {
      /**
       * @namespace LYNX.Utils.Strings
       * @alias LYNX.Utils.Strings
       *
       * Helper functions for string processing
       */
      /**
       * Repeats a string pattern $n$ times effectively. The pattern will be changed by the function. If the number of
       * requested repeats is less than 1, the function behaves as a no-op.
       *
       * @param {String} io_pattern         - The string to be repeated
       * @param {number} in_count           - How many times (may be 0 or negative)
       *
       * @return {String} The repeated string
       * @alias LYNX.Utils.Strings.repeatString
       */
      var repeatString = function (io_pattern, in_count) {
        if (in_count < 1)
          return '';
        var result = '';
        while (in_count > 1) {
          if (in_count & 1)
            result += io_pattern;
          in_count >>= 1;
          io_pattern += io_pattern;
        }
        return result + io_pattern;
      };
      exportModule('LYNX.Utils.Strings.repeatString', repeatString);
    }());
    (function () {
      var repeatString = include('LYNX.Utils.Strings.repeatString');
      /**
       * Fast high quality 32 bit RNG for consistent GUIDs.
       *
       * Good "randomness" (distribution); Period is > 2^95
       */
      var guidRNG = {
        // To check if the RNG has been already initialized
        isInitialized: false,
        // Internal RNG state
        rngA: 4027999010,
        rngB: 3993266363,
        rngC: 3605298456
      };
      /**
       * 32 bit bit vector rotation helper function
       *
       * @param {number} in_x - 32 bit integer
       * @param {number} in_r - amount to shift
       * @return {number} the 32-bit integer rotated by r bits.
       */
      guidRNG.rotl32 = function (in_x, in_r) {
        return (in_x << in_r >>> 0 | in_x >>> 32 - in_r) >>> 0;
      };
      /**
       * Initialize RNG.
       * This function need to be called once, before the first GUID gets created.
       *
       * @param {number} [in_seed] optional 32-bit seed for GUID RNG;
       *                           If no seed is given, a combination of system's
       *                           local time and Math.random() is used.
       * @param {boolean} [in_enforceReInitialization] optionally enforce re-initialization with another seed
       *
       * @return {number} The seed used to initialize the RNG;
       *                  If re-initialization is not enforced,
       *                  a zero indicates that the RNG was not re-seeded.
       * @alias LYNX.Utils.initializeGUIDGenerator
       */
      guidRNG.initialize = function (in_seed, in_enforceReInitialization) {
        // Quit if the RNG has already been initialized and we do not
        // want to enforce a re-initialization with a new seed
        if (guidRNG.isInitialized && !in_enforceReInitialization) {
          return 0;
        } else if (guidRNG.isInitialized) {
          guidRNG.rngA = 4027999010;
          guidRNG.rngB = 3993266363;
          guidRNG.rngC = 3605298456;
        }
        guidRNG.isInitialized = true;
        var seed;
        // Check if a seed is provided
        if (in_seed !== undefined) {
          seed = in_seed;
        } else {
          // If this is not the case we use a default seed
          // based on local time and Math.random()
          var date = new Date();
          seed = date.getTime();
          seed = seed ^ Math.floor(Math.random() * 2147483647 + 1);
        }
        guidRNG.rngA += seed & 2097151;
        guidRNG.rngB += seed >> 7 & 524287;
        guidRNG.rngC += seed >> 13;
        return seed;
      };
      /**
       * @return {number} 32-bit random number based on the RNGs internal state
       */
      guidRNG.genRandUInt32 = function () {
        // Actual generator of the RNG
        guidRNG.rngA = ~(2911329625 * guidRNG.rngA);
        guidRNG.rngA = guidRNG.rotl32(guidRNG.rngA, 17);
        guidRNG.rngB = 4031235431 * guidRNG.rngB;
        guidRNG.rngB = guidRNG.rotl32(guidRNG.rngB, 15);
        guidRNG.rngC = 3286325185 - guidRNG.rotl32(guidRNG.rngC, 19);
        return guidRNG.rngA + guidRNG.rngB ^ guidRNG.rngC;
      };
      /**
       * Helper function to create a GUID string from an array with 32Bit values
       *
       * @param {Uint32Array | Int32Array | Array.<number>} in_guidArray - Array with the 32 bit values
       *
       * @return {string} The GUID.
       * @alias LYNX.Utils.uint32x4ToGUID
       */
      var uint32x4ToGUID = function (in_guidArray) {
        // Convert to hexadecimal string
        var str = '';
        for (var i = 0; i < 4; i++) {
          var hex = in_guidArray[i].toString(16);
          str += repeatString('0', 8 - hex.length) + hex;
        }
        return str.substr(0, 8) + '-' + str.substr(8, 4) + '-' + str.substr(12, 4) + '-' + str.substr(16, 4) + '-' + str.substr(20, 12);
      };
      /**
       * Convert a hex string GUID to four 32Bit values.
       *
       * @param {string} in_guid - The guid to convert
       * @param {Uint32Array | Array.<number>} [io_result] - An optional array to write the result to;
       *                                If no array is given, a new one gets created
       * @return {Uint32Array | Array.<number>} Four 32-bit values.
       *
       * @alias LYNX.Utils.guidToUint32x4
       */
      var guidToUint32x4 = function (in_guid, io_result) {
        var result = io_result;
        if (result === undefined) {
          result = new Uint32Array(4);
        }
        result[0] = parseInt('0x' + in_guid.substr(0, 8), 16);
        result[1] = parseInt('0x' + in_guid.substr(9, 4) + in_guid.substr(14, 4), 16);
        result[2] = parseInt('0x' + in_guid.substr(19, 4) + in_guid.substr(24, 4), 16);
        result[3] = parseInt('0x' + in_guid.substr(28, 8), 16);
        return result;
      };
      /**
       * Generate a 128 bit hex string guid
       * with the following format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxx
       *
       * This function is *not* thread safe!
       *
       * @return {string} the GUID.
       * @alias LYNX.Utils.generateGUID
       */
      var generateGUID = function () {
        var rnds = new Uint32Array(4);
        // Random numbers for GUID (4x32 bit)
        rnds[0] = guidRNG.genRandUInt32();
        rnds[1] = guidRNG.genRandUInt32();
        rnds[2] = guidRNG.genRandUInt32();
        rnds[3] = guidRNG.genRandUInt32();
        return uint32x4ToGUID(rnds);
      };
      /**
       * Routine used to check whether the given string is a valid guid
       *
       * @param {string} in_guid The GUID to test.
       * @return {boolean} true if the parameter is a valid GUID, false otherwise.
       * @alias LYNX.Utils.isGUID
       */
      var isGUID = function (in_guid) {
        return /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(in_guid);
      };
      // Make sure the RNG is initialized
      guidRNG.initialize();
      exportModule('LYNX.Utils.uint32x4ToGUID', uint32x4ToGUID);
      exportModule('LYNX.Utils.guidToUint32x4', guidToUint32x4);
      exportModule('LYNX.Utils.initializeGUIDGenerator', guidRNG.initialize);
      exportModule('LYNX.Utils.generateGUID', generateGUID);
      exportModule('LYNX.Utils.isGUID', isGUID);
    }());
    (function () {
      // Baseline setup
      // --------------
      // Establish the root object, `window` in the browser, or `exports` on the server.
      var root = this;
      // Save the previous value of the `_` variable.
      var previousUnderscore = root._;
      // Save bytes in the minified (but not gzipped) version:
      var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
      // Create quick reference variables for speed access to core prototypes.
      var push = ArrayProto.push, slice = ArrayProto.slice, toString = ObjProto.toString, hasOwnProperty = ObjProto.hasOwnProperty;
      // All **ECMAScript 5** native function implementations that we hope to use
      // are declared here.
      var nativeIsArray = Array.isArray, nativeKeys = Object.keys, nativeBind = FuncProto.bind, nativeCreate = Object.create;
      // Naked function reference for surrogate-prototype-swapping.
      var Ctor = function () {
      };
      // Create a safe reference to the Underscore object for use below.
      var _ = function (obj) {
        if (obj instanceof _)
          return obj;
        if (!(this instanceof _))
          return new _(obj);
        this._wrapped = obj;
      };
      // Export the Underscore object for **Node.js**, with
      // backwards-compatibility for the old `require()` API. If we're in
      // the browser, add `_` as a global object.
      if (typeof exports !== 'undefined') {
        if (typeof module !== 'undefined' && module.exports) {
          exports = module.exports = _;
        }
        exports._ = _;
      } else {
        root._ = _;
      }
      // Current version.
      _.VERSION = '1.8.3';
      // Internal function that returns an efficient (for current engines) version
      // of the passed-in callback, to be repeatedly applied in other Underscore
      // functions.
      var optimizeCb = function (func, context, argCount) {
        if (context === void 0)
          return func;
        switch (argCount == null ? 3 : argCount) {
        case 1:
          return function (value) {
            return func.call(context, value);
          };
        case 2:
          return function (value, other) {
            return func.call(context, value, other);
          };
        case 3:
          return function (value, index, collection) {
            return func.call(context, value, index, collection);
          };
        case 4:
          return function (accumulator, value, index, collection) {
            return func.call(context, accumulator, value, index, collection);
          };
        }
        return function () {
          return func.apply(context, arguments);
        };
      };
      // A mostly-internal function to generate callbacks that can be applied
      // to each element in a collection, returning the desired result — either
      // identity, an arbitrary callback, a property matcher, or a property accessor.
      var cb = function (value, context, argCount) {
        if (value == null)
          return _.identity;
        if (_.isFunction(value))
          return optimizeCb(value, context, argCount);
        if (_.isObject(value))
          return _.matcher(value);
        return _.property(value);
      };
      _.iteratee = function (value, context) {
        return cb(value, context, Infinity);
      };
      // An internal function for creating assigner functions.
      var createAssigner = function (keysFunc, undefinedOnly) {
        return function (obj) {
          var length = arguments.length;
          if (length < 2 || obj == null)
            return obj;
          for (var index = 1; index < length; index++) {
            var source = arguments[index], keys = keysFunc(source), l = keys.length;
            for (var i = 0; i < l; i++) {
              var key = keys[i];
              if (!undefinedOnly || obj[key] === void 0)
                obj[key] = source[key];
            }
          }
          return obj;
        };
      };
      // An internal function for creating a new object that inherits from another.
      var baseCreate = function (prototype) {
        if (!_.isObject(prototype))
          return {};
        if (nativeCreate)
          return nativeCreate(prototype);
        Ctor.prototype = prototype;
        var result = new Ctor();
        Ctor.prototype = null;
        return result;
      };
      var property = function (key) {
        return function (obj) {
          return obj == null ? void 0 : obj[key];
        };
      };
      // Helper for collection methods to determine whether a collection
      // should be iterated as an array or as an object
      // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
      // Avoids a very nasty iOS 8 JIT bug on ARM-64. #2094
      var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
      var getLength = property('length');
      var isArrayLike = function (collection) {
        var length = getLength(collection);
        return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
      };
      // Collection Functions
      // --------------------
      // The cornerstone, an `each` implementation, aka `forEach`.
      // Handles raw objects in addition to array-likes. Treats all
      // sparse array-likes as if they were dense.
      _.each = _.forEach = function (obj, iteratee, context) {
        iteratee = optimizeCb(iteratee, context);
        var i, length;
        if (isArrayLike(obj)) {
          for (i = 0, length = obj.length; i < length; i++) {
            iteratee(obj[i], i, obj);
          }
        } else {
          var keys = _.keys(obj);
          for (i = 0, length = keys.length; i < length; i++) {
            iteratee(obj[keys[i]], keys[i], obj);
          }
        }
        return obj;
      };
      // Return the results of applying the iteratee to each element.
      _.map = _.collect = function (obj, iteratee, context) {
        iteratee = cb(iteratee, context);
        var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length, results = Array(length);
        for (var index = 0; index < length; index++) {
          var currentKey = keys ? keys[index] : index;
          results[index] = iteratee(obj[currentKey], currentKey, obj);
        }
        return results;
      };
      // Create a reducing function iterating left or right.
      function createReduce(dir) {
        // Optimized iterator function as using arguments.length
        // in the main function will deoptimize the, see #1991.
        function iterator(obj, iteratee, memo, keys, index, length) {
          for (; index >= 0 && index < length; index += dir) {
            var currentKey = keys ? keys[index] : index;
            memo = iteratee(memo, obj[currentKey], currentKey, obj);
          }
          return memo;
        }
        return function (obj, iteratee, memo, context) {
          iteratee = optimizeCb(iteratee, context, 4);
          var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length, index = dir > 0 ? 0 : length - 1;
          // Determine the initial value if none is provided.
          if (arguments.length < 3) {
            memo = obj[keys ? keys[index] : index];
            index += dir;
          }
          return iterator(obj, iteratee, memo, keys, index, length);
        };
      }
      // **Reduce** builds up a single result from a list of values, aka `inject`,
      // or `foldl`.
      _.reduce = _.foldl = _.inject = createReduce(1);
      // The right-associative version of reduce, also known as `foldr`.
      _.reduceRight = _.foldr = createReduce(-1);
      // Return the first value which passes a truth test. Aliased as `detect`.
      _.find = _.detect = function (obj, predicate, context) {
        var key;
        if (isArrayLike(obj)) {
          key = _.findIndex(obj, predicate, context);
        } else {
          key = _.findKey(obj, predicate, context);
        }
        if (key !== void 0 && key !== -1)
          return obj[key];
      };
      // Return all the elements that pass a truth test.
      // Aliased as `select`.
      _.filter = _.select = function (obj, predicate, context) {
        var results = [];
        predicate = cb(predicate, context);
        _.each(obj, function (value, index, list) {
          if (predicate(value, index, list))
            results.push(value);
        });
        return results;
      };
      // Return all the elements for which a truth test fails.
      _.reject = function (obj, predicate, context) {
        return _.filter(obj, _.negate(cb(predicate)), context);
      };
      // Determine whether all of the elements match a truth test.
      // Aliased as `all`.
      _.every = _.all = function (obj, predicate, context) {
        predicate = cb(predicate, context);
        var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length;
        for (var index = 0; index < length; index++) {
          var currentKey = keys ? keys[index] : index;
          if (!predicate(obj[currentKey], currentKey, obj))
            return false;
        }
        return true;
      };
      // Determine if at least one element in the object matches a truth test.
      // Aliased as `any`.
      _.some = _.any = function (obj, predicate, context) {
        predicate = cb(predicate, context);
        var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length;
        for (var index = 0; index < length; index++) {
          var currentKey = keys ? keys[index] : index;
          if (predicate(obj[currentKey], currentKey, obj))
            return true;
        }
        return false;
      };
      // Determine if the array or object contains a given item (using `===`).
      // Aliased as `includes` and `include`.
      _.contains = _.includes = _.include = function (obj, item, fromIndex, guard) {
        if (!isArrayLike(obj))
          obj = _.values(obj);
        if (typeof fromIndex != 'number' || guard)
          fromIndex = 0;
        return _.indexOf(obj, item, fromIndex) >= 0;
      };
      // Invoke a method (with arguments) on every item in a collection.
      _.invoke = function (obj, method) {
        var args = slice.call(arguments, 2);
        var isFunc = _.isFunction(method);
        return _.map(obj, function (value) {
          var func = isFunc ? method : value[method];
          return func == null ? func : func.apply(value, args);
        });
      };
      // Convenience version of a common use case of `map`: fetching a property.
      _.pluck = function (obj, key) {
        return _.map(obj, _.property(key));
      };
      // Convenience version of a common use case of `filter`: selecting only objects
      // containing specific `key:value` pairs.
      _.where = function (obj, attrs) {
        return _.filter(obj, _.matcher(attrs));
      };
      // Convenience version of a common use case of `find`: getting the first object
      // containing specific `key:value` pairs.
      _.findWhere = function (obj, attrs) {
        return _.find(obj, _.matcher(attrs));
      };
      // Return the maximum element (or element-based computation).
      _.max = function (obj, iteratee, context) {
        var result = -Infinity, lastComputed = -Infinity, value, computed;
        if (iteratee == null && obj != null) {
          obj = isArrayLike(obj) ? obj : _.values(obj);
          for (var i = 0, length = obj.length; i < length; i++) {
            value = obj[i];
            if (value > result) {
              result = value;
            }
          }
        } else {
          iteratee = cb(iteratee, context);
          _.each(obj, function (value, index, list) {
            computed = iteratee(value, index, list);
            if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
              result = value;
              lastComputed = computed;
            }
          });
        }
        return result;
      };
      // Return the minimum element (or element-based computation).
      _.min = function (obj, iteratee, context) {
        var result = Infinity, lastComputed = Infinity, value, computed;
        if (iteratee == null && obj != null) {
          obj = isArrayLike(obj) ? obj : _.values(obj);
          for (var i = 0, length = obj.length; i < length; i++) {
            value = obj[i];
            if (value < result) {
              result = value;
            }
          }
        } else {
          iteratee = cb(iteratee, context);
          _.each(obj, function (value, index, list) {
            computed = iteratee(value, index, list);
            if (computed < lastComputed || computed === Infinity && result === Infinity) {
              result = value;
              lastComputed = computed;
            }
          });
        }
        return result;
      };
      // Shuffle a collection, using the modern version of the
      // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
      _.shuffle = function (obj) {
        var set = isArrayLike(obj) ? obj : _.values(obj);
        var length = set.length;
        var shuffled = Array(length);
        for (var index = 0, rand; index < length; index++) {
          rand = _.random(0, index);
          if (rand !== index)
            shuffled[index] = shuffled[rand];
          shuffled[rand] = set[index];
        }
        return shuffled;
      };
      // Sample **n** random values from a collection.
      // If **n** is not specified, returns a single random element.
      // The internal `guard` argument allows it to work with `map`.
      _.sample = function (obj, n, guard) {
        if (n == null || guard) {
          if (!isArrayLike(obj))
            obj = _.values(obj);
          return obj[_.random(obj.length - 1)];
        }
        return _.shuffle(obj).slice(0, Math.max(0, n));
      };
      // Sort the object's values by a criterion produced by an iteratee.
      _.sortBy = function (obj, iteratee, context) {
        iteratee = cb(iteratee, context);
        return _.pluck(_.map(obj, function (value, index, list) {
          return {
            value: value,
            index: index,
            criteria: iteratee(value, index, list)
          };
        }).sort(function (left, right) {
          var a = left.criteria;
          var b = right.criteria;
          if (a !== b) {
            if (a > b || a === void 0)
              return 1;
            if (a < b || b === void 0)
              return -1;
          }
          return left.index - right.index;
        }), 'value');
      };
      // An internal function used for aggregate "group by" operations.
      var group = function (behavior) {
        return function (obj, iteratee, context) {
          var result = {};
          iteratee = cb(iteratee, context);
          _.each(obj, function (value, index) {
            var key = iteratee(value, index, obj);
            behavior(result, value, key);
          });
          return result;
        };
      };
      // Groups the object's values by a criterion. Pass either a string attribute
      // to group by, or a function that returns the criterion.
      _.groupBy = group(function (result, value, key) {
        if (_.has(result, key))
          result[key].push(value);
        else
          result[key] = [value];
      });
      // Indexes the object's values by a criterion, similar to `groupBy`, but for
      // when you know that your index values will be unique.
      _.indexBy = group(function (result, value, key) {
        result[key] = value;
      });
      // Counts instances of an object that group by a certain criterion. Pass
      // either a string attribute to count by, or a function that returns the
      // criterion.
      _.countBy = group(function (result, value, key) {
        if (_.has(result, key))
          result[key]++;
        else
          result[key] = 1;
      });
      // Safely create a real, live array from anything iterable.
      _.toArray = function (obj) {
        if (!obj)
          return [];
        if (_.isArray(obj))
          return slice.call(obj);
        if (isArrayLike(obj))
          return _.map(obj, _.identity);
        return _.values(obj);
      };
      // Return the number of elements in an object.
      _.size = function (obj) {
        if (obj == null)
          return 0;
        return isArrayLike(obj) ? obj.length : _.keys(obj).length;
      };
      // Split a collection into two arrays: one whose elements all satisfy the given
      // predicate, and one whose elements all do not satisfy the predicate.
      _.partition = function (obj, predicate, context) {
        predicate = cb(predicate, context);
        var pass = [], fail = [];
        _.each(obj, function (value, key, obj) {
          (predicate(value, key, obj) ? pass : fail).push(value);
        });
        return [
          pass,
          fail
        ];
      };
      // Array Functions
      // ---------------
      // Get the first element of an array. Passing **n** will return the first N
      // values in the array. Aliased as `head` and `take`. The **guard** check
      // allows it to work with `_.map`.
      _.first = _.head = _.take = function (array, n, guard) {
        if (array == null)
          return void 0;
        if (n == null || guard)
          return array[0];
        return _.initial(array, array.length - n);
      };
      // Returns everything but the last entry of the array. Especially useful on
      // the arguments object. Passing **n** will return all the values in
      // the array, excluding the last N.
      _.initial = function (array, n, guard) {
        return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
      };
      // Get the last element of an array. Passing **n** will return the last N
      // values in the array.
      _.last = function (array, n, guard) {
        if (array == null)
          return void 0;
        if (n == null || guard)
          return array[array.length - 1];
        return _.rest(array, Math.max(0, array.length - n));
      };
      // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
      // Especially useful on the arguments object. Passing an **n** will return
      // the rest N values in the array.
      _.rest = _.tail = _.drop = function (array, n, guard) {
        return slice.call(array, n == null || guard ? 1 : n);
      };
      // Trim out all falsy values from an array.
      _.compact = function (array) {
        return _.filter(array, _.identity);
      };
      // Internal implementation of a recursive `flatten` function.
      var flatten = function (input, shallow, strict, startIndex) {
        var output = [], idx = 0;
        for (var i = startIndex || 0, length = getLength(input); i < length; i++) {
          var value = input[i];
          if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
            //flatten current level of array or arguments object
            if (!shallow)
              value = flatten(value, shallow, strict);
            var j = 0, len = value.length;
            output.length += len;
            while (j < len) {
              output[idx++] = value[j++];
            }
          } else if (!strict) {
            output[idx++] = value;
          }
        }
        return output;
      };
      // Flatten out an array, either recursively (by default), or just one level.
      _.flatten = function (array, shallow) {
        return flatten(array, shallow, false);
      };
      // Return a version of the array that does not contain the specified value(s).
      _.without = function (array) {
        return _.difference(array, slice.call(arguments, 1));
      };
      // Produce a duplicate-free version of the array. If the array has already
      // been sorted, you have the option of using a faster algorithm.
      // Aliased as `unique`.
      _.uniq = _.unique = function (array, isSorted, iteratee, context) {
        if (!_.isBoolean(isSorted)) {
          context = iteratee;
          iteratee = isSorted;
          isSorted = false;
        }
        if (iteratee != null)
          iteratee = cb(iteratee, context);
        var result = [];
        var seen = [];
        for (var i = 0, length = getLength(array); i < length; i++) {
          var value = array[i], computed = iteratee ? iteratee(value, i, array) : value;
          if (isSorted) {
            if (!i || seen !== computed)
              result.push(value);
            seen = computed;
          } else if (iteratee) {
            if (!_.contains(seen, computed)) {
              seen.push(computed);
              result.push(value);
            }
          } else if (!_.contains(result, value)) {
            result.push(value);
          }
        }
        return result;
      };
      // Produce an array that contains the union: each distinct element from all of
      // the passed-in arrays.
      _.union = function () {
        return _.uniq(flatten(arguments, true, true));
      };
      // Produce an array that contains every item shared between all the
      // passed-in arrays.
      _.intersection = function (array) {
        var result = [];
        var argsLength = arguments.length;
        for (var i = 0, length = getLength(array); i < length; i++) {
          var item = array[i];
          if (_.contains(result, item))
            continue;
          for (var j = 1; j < argsLength; j++) {
            if (!_.contains(arguments[j], item))
              break;
          }
          if (j === argsLength)
            result.push(item);
        }
        return result;
      };
      // Take the difference between one array and a number of other arrays.
      // Only the elements present in just the first array will remain.
      _.difference = function (array) {
        var rest = flatten(arguments, true, true, 1);
        return _.filter(array, function (value) {
          return !_.contains(rest, value);
        });
      };
      // Zip together multiple lists into a single array -- elements that share
      // an index go together.
      _.zip = function () {
        return _.unzip(arguments);
      };
      // Complement of _.zip. Unzip accepts an array of arrays and groups
      // each array's elements on shared indices
      _.unzip = function (array) {
        var length = array && _.max(array, getLength).length || 0;
        var result = Array(length);
        for (var index = 0; index < length; index++) {
          result[index] = _.pluck(array, index);
        }
        return result;
      };
      // Converts lists into objects. Pass either a single array of `[key, value]`
      // pairs, or two parallel arrays of the same length -- one of keys, and one of
      // the corresponding values.
      _.object = function (list, values) {
        var result = {};
        for (var i = 0, length = getLength(list); i < length; i++) {
          if (values) {
            result[list[i]] = values[i];
          } else {
            result[list[i][0]] = list[i][1];
          }
        }
        return result;
      };
      // Generator function to create the findIndex and findLastIndex functions
      function createPredicateIndexFinder(dir) {
        return function (array, predicate, context) {
          predicate = cb(predicate, context);
          var length = getLength(array);
          var index = dir > 0 ? 0 : length - 1;
          for (; index >= 0 && index < length; index += dir) {
            if (predicate(array[index], index, array))
              return index;
          }
          return -1;
        };
      }
      // Returns the first index on an array-like that passes a predicate test
      _.findIndex = createPredicateIndexFinder(1);
      _.findLastIndex = createPredicateIndexFinder(-1);
      // Use a comparator function to figure out the smallest index at which
      // an object should be inserted so as to maintain order. Uses binary search.
      _.sortedIndex = function (array, obj, iteratee, context) {
        iteratee = cb(iteratee, context, 1);
        var value = iteratee(obj);
        var low = 0, high = getLength(array);
        while (low < high) {
          var mid = Math.floor((low + high) / 2);
          if (iteratee(array[mid]) < value)
            low = mid + 1;
          else
            high = mid;
        }
        return low;
      };
      // Generator function to create the indexOf and lastIndexOf functions
      function createIndexFinder(dir, predicateFind, sortedIndex) {
        return function (array, item, idx) {
          var i = 0, length = getLength(array);
          if (typeof idx == 'number') {
            if (dir > 0) {
              i = idx >= 0 ? idx : Math.max(idx + length, i);
            } else {
              length = idx >= 0 ? Math.min(idx + 1, length) : idx + length + 1;
            }
          } else if (sortedIndex && idx && length) {
            idx = sortedIndex(array, item);
            return array[idx] === item ? idx : -1;
          }
          if (item !== item) {
            idx = predicateFind(slice.call(array, i, length), _.isNaN);
            return idx >= 0 ? idx + i : -1;
          }
          for (idx = dir > 0 ? i : length - 1; idx >= 0 && idx < length; idx += dir) {
            if (array[idx] === item)
              return idx;
          }
          return -1;
        };
      }
      // Return the position of the first occurrence of an item in an array,
      // or -1 if the item is not included in the array.
      // If the array is large and already in sort order, pass `true`
      // for **isSorted** to use binary search.
      _.indexOf = createIndexFinder(1, _.findIndex, _.sortedIndex);
      _.lastIndexOf = createIndexFinder(-1, _.findLastIndex);
      // Generate an integer Array containing an arithmetic progression. A port of
      // the native Python `range()` function. See
      // [the Python documentation](http://docs.python.org/library/functions.html#range).
      _.range = function (start, stop, step) {
        if (stop == null) {
          stop = start || 0;
          start = 0;
        }
        step = step || 1;
        var length = Math.max(Math.ceil((stop - start) / step), 0);
        var range = Array(length);
        for (var idx = 0; idx < length; idx++, start += step) {
          range[idx] = start;
        }
        return range;
      };
      // function(ahem) Functions
      // ------------------
      // Determines whether to execute a function as a constructor
      // or a normal function with the provided arguments
      var executeBound = function (sourceFunc, boundFunc, context, callingContext, args) {
        if (!(callingContext instanceof boundFunc))
          return sourceFunc.apply(context, args);
        var self = baseCreate(sourceFunc.prototype);
        var result = sourceFunc.apply(self, args);
        if (_.isObject(result))
          return result;
        return self;
      };
      // Create a function bound to a given object (assigning `this`, and arguments,
      // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
      // available.
      _.bind = function (func, context) {
        if (nativeBind && func.bind === nativeBind)
          return nativeBind.apply(func, slice.call(arguments, 1));
        if (!_.isFunction(func))
          throw new TypeError('Bind must be called on a function');
        var args = slice.call(arguments, 2);
        var bound = function () {
          return executeBound(func, bound, context, this, args.concat(slice.call(arguments)));
        };
        return bound;
      };
      // Partially apply a function by creating a version that has had some of its
      // arguments pre-filled, without changing its dynamic `this` context. _ acts
      // as a placeholder, allowing any combination of arguments to be pre-filled.
      _.partial = function (func) {
        var boundArgs = slice.call(arguments, 1);
        var bound = function () {
          var position = 0, length = boundArgs.length;
          var args = Array(length);
          for (var i = 0; i < length; i++) {
            args[i] = boundArgs[i] === _ ? arguments[position++] : boundArgs[i];
          }
          while (position < arguments.length)
            args.push(arguments[position++]);
          return executeBound(func, bound, this, this, args);
        };
        return bound;
      };
      // Bind a number of an object's methods to that object. Remaining arguments
      // are the method names to be bound. Useful for ensuring that all callbacks
      // defined on an object belong to it.
      _.bindAll = function (obj) {
        var i, length = arguments.length, key;
        if (length <= 1)
          throw new Error('bindAll must be passed function names');
        for (i = 1; i < length; i++) {
          key = arguments[i];
          obj[key] = _.bind(obj[key], obj);
        }
        return obj;
      };
      // Memoize an expensive function by storing its results.
      _.memoize = function (func, hasher) {
        var memoize = function (key) {
          var cache = memoize.cache;
          var address = '' + (hasher ? hasher.apply(this, arguments) : key);
          if (!_.has(cache, address))
            cache[address] = func.apply(this, arguments);
          return cache[address];
        };
        memoize.cache = {};
        return memoize;
      };
      // Delays a function for the given number of milliseconds, and then calls
      // it with the arguments supplied.
      _.delay = function (func, wait) {
        var args = slice.call(arguments, 2);
        return setTimeout(function () {
          return func.apply(null, args);
        }, wait);
      };
      // Defers a function, scheduling it to run after the current call stack has
      // cleared.
      _.defer = _.partial(_.delay, _, 1);
      // Returns a function, that, when invoked, will only be triggered at most once
      // during a given window of time. Normally, the throttled function will run
      // as much as it can, without ever going more than once per `wait` duration;
      // but if you'd like to disable the execution on the leading edge, pass
      // `{leading: false}`. To disable execution on the trailing edge, ditto.
      _.throttle = function (func, wait, options) {
        var context, args, result;
        var timeout = null;
        var previous = 0;
        if (!options)
          options = {};
        var later = function () {
          previous = options.leading === false ? 0 : _.now();
          timeout = null;
          result = func.apply(context, args);
          if (!timeout)
            context = args = null;
        };
        return function () {
          var now = _.now();
          if (!previous && options.leading === false)
            previous = now;
          var remaining = wait - (now - previous);
          context = this;
          args = arguments;
          if (remaining <= 0 || remaining > wait) {
            if (timeout) {
              clearTimeout(timeout);
              timeout = null;
            }
            previous = now;
            result = func.apply(context, args);
            if (!timeout)
              context = args = null;
          } else if (!timeout && options.trailing !== false) {
            timeout = setTimeout(later, remaining);
          }
          return result;
        };
      };
      // Returns a function, that, as long as it continues to be invoked, will not
      // be triggered. The function will be called after it stops being called for
      // N milliseconds. If `immediate` is passed, trigger the function on the
      // leading edge, instead of the trailing.
      _.debounce = function (func, wait, immediate) {
        var timeout, args, context, timestamp, result;
        var later = function () {
          var last = _.now() - timestamp;
          if (last < wait && last >= 0) {
            timeout = setTimeout(later, wait - last);
          } else {
            timeout = null;
            if (!immediate) {
              result = func.apply(context, args);
              if (!timeout)
                context = args = null;
            }
          }
        };
        return function () {
          context = this;
          args = arguments;
          timestamp = _.now();
          var callNow = immediate && !timeout;
          if (!timeout)
            timeout = setTimeout(later, wait);
          if (callNow) {
            result = func.apply(context, args);
            context = args = null;
          }
          return result;
        };
      };
      // Returns the first function passed as an argument to the second,
      // allowing you to adjust arguments, run code before and after, and
      // conditionally execute the original function.
      _.wrap = function (func, wrapper) {
        return _.partial(wrapper, func);
      };
      // Returns a negated version of the passed-in predicate.
      _.negate = function (predicate) {
        return function () {
          return !predicate.apply(this, arguments);
        };
      };
      // Returns a function that is the composition of a list of functions, each
      // consuming the return value of the function that follows.
      _.compose = function () {
        var args = arguments;
        var start = args.length - 1;
        return function () {
          var i = start;
          var result = args[start].apply(this, arguments);
          while (i--)
            result = args[i].call(this, result);
          return result;
        };
      };
      // Returns a function that will only be executed on and after the Nth call.
      _.after = function (times, func) {
        return function () {
          if (--times < 1) {
            return func.apply(this, arguments);
          }
        };
      };
      // Returns a function that will only be executed up to (but not including) the Nth call.
      _.before = function (times, func) {
        var memo;
        return function () {
          if (--times > 0) {
            memo = func.apply(this, arguments);
          }
          if (times <= 1)
            func = null;
          return memo;
        };
      };
      // Returns a function that will be executed at most one time, no matter how
      // often you call it. Useful for lazy initialization.
      _.once = _.partial(_.before, 2);
      // Object Functions
      // ----------------
      // Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed.
      var hasEnumBug = !{ toString: null }.propertyIsEnumerable('toString');
      var nonEnumerableProps = [
        'valueOf',
        'isPrototypeOf',
        'toString',
        'propertyIsEnumerable',
        'hasOwnProperty',
        'toLocaleString'
      ];
      function collectNonEnumProps(obj, keys) {
        var nonEnumIdx = nonEnumerableProps.length;
        var constructor = obj.constructor;
        var proto = _.isFunction(constructor) && constructor.prototype || ObjProto;
        // Constructor is a special case.
        var prop = 'constructor';
        if (_.has(obj, prop) && !_.contains(keys, prop))
          keys.push(prop);
        while (nonEnumIdx--) {
          prop = nonEnumerableProps[nonEnumIdx];
          if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
            keys.push(prop);
          }
        }
      }
      // Retrieve the names of an object's own properties.
      // Delegates to **ECMAScript 5**'s native `Object.keys`
      _.keys = function (obj) {
        if (!_.isObject(obj))
          return [];
        if (nativeKeys)
          return nativeKeys(obj);
        var keys = [];
        for (var key in obj)
          if (_.has(obj, key))
            keys.push(key);
        // Ahem, IE < 9.
        if (hasEnumBug)
          collectNonEnumProps(obj, keys);
        return keys;
      };
      // Retrieve all the property names of an object.
      _.allKeys = function (obj) {
        if (!_.isObject(obj))
          return [];
        var keys = [];
        for (var key in obj)
          keys.push(key);
        // Ahem, IE < 9.
        if (hasEnumBug)
          collectNonEnumProps(obj, keys);
        return keys;
      };
      // Retrieve the values of an object's properties.
      _.values = function (obj) {
        var keys = _.keys(obj);
        var length = keys.length;
        var values = Array(length);
        for (var i = 0; i < length; i++) {
          values[i] = obj[keys[i]];
        }
        return values;
      };
      // Returns the results of applying the iteratee to each element of the object
      // In contrast to _.map it returns an object
      _.mapObject = function (obj, iteratee, context) {
        iteratee = cb(iteratee, context);
        var keys = _.keys(obj), length = keys.length, results = {}, currentKey;
        for (var index = 0; index < length; index++) {
          currentKey = keys[index];
          results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
        }
        return results;
      };
      // Convert an object into a list of `[key, value]` pairs.
      _.pairs = function (obj) {
        var keys = _.keys(obj);
        var length = keys.length;
        var pairs = Array(length);
        for (var i = 0; i < length; i++) {
          pairs[i] = [
            keys[i],
            obj[keys[i]]
          ];
        }
        return pairs;
      };
      // Invert the keys and values of an object. The values must be serializable.
      _.invert = function (obj) {
        var result = {};
        var keys = _.keys(obj);
        for (var i = 0, length = keys.length; i < length; i++) {
          result[obj[keys[i]]] = keys[i];
        }
        return result;
      };
      // Return a sorted list of the function names available on the object.
      // Aliased as `methods`
      _.functions = _.methods = function (obj) {
        var names = [];
        for (var key in obj) {
          if (_.isFunction(obj[key]))
            names.push(key);
        }
        return names.sort();
      };
      // Extend a given object with all the properties in passed-in object(s).
      _.extend = createAssigner(_.allKeys);
      // Assigns a given object with all the own properties in the passed-in object(s)
      // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
      _.extendOwn = _.assign = createAssigner(_.keys);
      // Returns the first key on an object that passes a predicate test
      _.findKey = function (obj, predicate, context) {
        predicate = cb(predicate, context);
        var keys = _.keys(obj), key;
        for (var i = 0, length = keys.length; i < length; i++) {
          key = keys[i];
          if (predicate(obj[key], key, obj))
            return key;
        }
      };
      // Return a copy of the object only containing the whitelisted properties.
      _.pick = function (object, oiteratee, context) {
        var result = {}, obj = object, iteratee, keys;
        if (obj == null)
          return result;
        if (_.isFunction(oiteratee)) {
          keys = _.allKeys(obj);
          iteratee = optimizeCb(oiteratee, context);
        } else {
          keys = flatten(arguments, false, false, 1);
          iteratee = function (value, key, obj) {
            return key in obj;
          };
          obj = Object(obj);
        }
        for (var i = 0, length = keys.length; i < length; i++) {
          var key = keys[i];
          var value = obj[key];
          if (iteratee(value, key, obj))
            result[key] = value;
        }
        return result;
      };
      // Return a copy of the object without the blacklisted properties.
      _.omit = function (obj, iteratee, context) {
        if (_.isFunction(iteratee)) {
          iteratee = _.negate(iteratee);
        } else {
          var keys = _.map(flatten(arguments, false, false, 1), String);
          iteratee = function (value, key) {
            return !_.contains(keys, key);
          };
        }
        return _.pick(obj, iteratee, context);
      };
      // Fill in a given object with default properties.
      _.defaults = createAssigner(_.allKeys, true);
      // Creates an object that inherits from the given prototype object.
      // If additional properties are provided then they will be added to the
      // created object.
      _.create = function (prototype, props) {
        var result = baseCreate(prototype);
        if (props)
          _.extendOwn(result, props);
        return result;
      };
      // Create a (shallow-cloned) duplicate of an object.
      _.clone = function (obj) {
        if (!_.isObject(obj))
          return obj;
        return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
      };
      // Invokes interceptor with the obj, and then returns obj.
      // The primary purpose of this method is to "tap into" a method chain, in
      // order to perform operations on intermediate results within the chain.
      _.tap = function (obj, interceptor) {
        interceptor(obj);
        return obj;
      };
      // Returns whether an object has a given set of `key:value` pairs.
      _.isMatch = function (object, attrs) {
        var keys = _.keys(attrs), length = keys.length;
        if (object == null)
          return !length;
        var obj = Object(object);
        for (var i = 0; i < length; i++) {
          var key = keys[i];
          if (attrs[key] !== obj[key] || !(key in obj))
            return false;
        }
        return true;
      };
      // Internal recursive comparison function for `isEqual`.
      var eq = function (a, b, aStack, bStack) {
        // Identical objects are equal. `0 === -0`, but they aren't identical.
        // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
        if (a === b)
          return a !== 0 || 1 / a === 1 / b;
        // A strict comparison is necessary because `null == undefined`.
        if (a == null || b == null)
          return a === b;
        // Unwrap any wrapped objects.
        if (a instanceof _)
          a = a._wrapped;
        if (b instanceof _)
          b = b._wrapped;
        // Compare `[[Class]]` names.
        var className = toString.call(a);
        if (className !== toString.call(b))
          return false;
        switch (className) {
        // Strings, numbers, regular expressions, dates, and booleans are compared by value.
        case '[object RegExp]':
        // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
        case '[object String]':
          // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
          // equivalent to `new String("5")`.
          return '' + a === '' + b;
        case '[object Number]':
          // `NaN`s are equivalent, but non-reflexive.
          // Object(NaN) is equivalent to NaN
          if (+a !== +a)
            return +b !== +b;
          // An `egal` comparison is performed for other numeric values.
          return +a === 0 ? 1 / +a === 1 / b : +a === +b;
        case '[object Date]':
        case '[object Boolean]':
          // Coerce dates and booleans to numeric primitive values. Dates are compared by their
          // millisecond representations. Note that invalid dates with millisecond representations
          // of `NaN` are not equivalent.
          return +a === +b;
        }
        var areArrays = className === '[object Array]';
        if (!areArrays) {
          if (typeof a != 'object' || typeof b != 'object')
            return false;
          // Objects with different constructors are not equivalent, but `Object`s or `Array`s
          // from different frames are.
          var aCtor = a.constructor, bCtor = b.constructor;
          if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor && _.isFunction(bCtor) && bCtor instanceof bCtor) && ('constructor' in a && 'constructor' in b)) {
            return false;
          }
        }
        // Assume equality for cyclic structures. The algorithm for detecting cyclic
        // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
        // Initializing stack of traversed objects.
        // It's done here since we only need them for objects and arrays comparison.
        aStack = aStack || [];
        bStack = bStack || [];
        var length = aStack.length;
        while (length--) {
          // Linear search. Performance is inversely proportional to the number of
          // unique nested structures.
          if (aStack[length] === a)
            return bStack[length] === b;
        }
        // Add the first object to the stack of traversed objects.
        aStack.push(a);
        bStack.push(b);
        // Recursively compare objects and arrays.
        if (areArrays) {
          // Compare array lengths to determine if a deep comparison is necessary.
          length = a.length;
          if (length !== b.length)
            return false;
          // Deep compare the contents, ignoring non-numeric properties.
          while (length--) {
            if (!eq(a[length], b[length], aStack, bStack))
              return false;
          }
        } else {
          // Deep compare objects.
          var keys = _.keys(a), key;
          length = keys.length;
          // Ensure that both objects contain the same number of properties before comparing deep equality.
          if (_.keys(b).length !== length)
            return false;
          while (length--) {
            // Deep compare each member
            key = keys[length];
            if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack)))
              return false;
          }
        }
        // Remove the first object from the stack of traversed objects.
        aStack.pop();
        bStack.pop();
        return true;
      };
      // Perform a deep comparison to check if two objects are equal.
      _.isEqual = function (a, b) {
        return eq(a, b);
      };
      // Is a given array, string, or object empty?
      // An "empty" object has no enumerable own-properties.
      _.isEmpty = function (obj) {
        if (obj == null)
          return true;
        if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj)))
          return obj.length === 0;
        return _.keys(obj).length === 0;
      };
      // Is a given value a DOM element?
      _.isElement = function (obj) {
        return !!(obj && obj.nodeType === 1);
      };
      // Is a given value an array?
      // Delegates to ECMA5's native Array.isArray
      _.isArray = nativeIsArray || function (obj) {
        return toString.call(obj) === '[object Array]';
      };
      // Is a given variable an object?
      _.isObject = function (obj) {
        var type = typeof obj;
        return type === 'function' || type === 'object' && !!obj;
      };
      // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError.
      _.each([
        'Arguments',
        'Function',
        'String',
        'Number',
        'Date',
        'RegExp',
        'Error'
      ], function (name) {
        _['is' + name] = function (obj) {
          return toString.call(obj) === '[object ' + name + ']';
        };
      });
      // Define a fallback version of the method in browsers (ahem, IE < 9), where
      // there isn't any inspectable "Arguments" type.
      if (!_.isArguments(arguments)) {
        _.isArguments = function (obj) {
          return _.has(obj, 'callee');
        };
      }
      // Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8,
      // IE 11 (#1621), and in Safari 8 (#1929).
      if (typeof /./ != 'function' && typeof Int8Array != 'object') {
        _.isFunction = function (obj) {
          return typeof obj == 'function' || false;
        };
      }
      // Is a given object a finite number?
      _.isFinite = function (obj) {
        return isFinite(obj) && !isNaN(parseFloat(obj));
      };
      // Is the given value `NaN`? (NaN is the only number which does not equal itself).
      _.isNaN = function (obj) {
        return _.isNumber(obj) && obj !== +obj;
      };
      // Is a given value a boolean?
      _.isBoolean = function (obj) {
        return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
      };
      // Is a given value equal to null?
      _.isNull = function (obj) {
        return obj === null;
      };
      // Is a given variable undefined?
      _.isUndefined = function (obj) {
        return obj === void 0;
      };
      // Shortcut function for checking if an object has a given property directly
      // on itself (in other words, not on a prototype).
      _.has = function (obj, key) {
        return obj != null && hasOwnProperty.call(obj, key);
      };
      // Utility Functions
      // -----------------
      // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
      // previous owner. Returns a reference to the Underscore object.
      _.noConflict = function () {
        root._ = previousUnderscore;
        return this;
      };
      // Keep the identity function around for default iteratees.
      _.identity = function (value) {
        return value;
      };
      // Predicate-generating functions. Often useful outside of Underscore.
      _.constant = function (value) {
        return function () {
          return value;
        };
      };
      _.noop = function () {
      };
      _.property = property;
      // Generates a function for a given object that returns a given property.
      _.propertyOf = function (obj) {
        return obj == null ? function () {
        } : function (key) {
          return obj[key];
        };
      };
      // Returns a predicate for checking whether an object has a given set of
      // `key:value` pairs.
      _.matcher = _.matches = function (attrs) {
        attrs = _.extendOwn({}, attrs);
        return function (obj) {
          return _.isMatch(obj, attrs);
        };
      };
      // Run a function **n** times.
      _.times = function (n, iteratee, context) {
        var accum = Array(Math.max(0, n));
        iteratee = optimizeCb(iteratee, context, 1);
        for (var i = 0; i < n; i++)
          accum[i] = iteratee(i);
        return accum;
      };
      // Return a random integer between min and max (inclusive).
      _.random = function (min, max) {
        if (max == null) {
          max = min;
          min = 0;
        }
        return min + Math.floor(Math.random() * (max - min + 1));
      };
      // A (possibly faster) way to get the current timestamp as an integer.
      _.now = Date.now || function () {
        return new Date().getTime();
      };
      // List of HTML entities for escaping.
      var escapeMap = {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;',
        '"': '&quot;',
        '\'': '&#x27;',
        '`': '&#x60;'
      };
      var unescapeMap = _.invert(escapeMap);
      // Functions for escaping and unescaping strings to/from HTML interpolation.
      var createEscaper = function (map) {
        var escaper = function (match) {
          return map[match];
        };
        // Regexes for identifying a key that needs to be escaped
        var source = '(?:' + _.keys(map).join('|') + ')';
        var testRegexp = RegExp(source);
        var replaceRegexp = RegExp(source, 'g');
        return function (string) {
          string = string == null ? '' : '' + string;
          return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
        };
      };
      _.escape = createEscaper(escapeMap);
      _.unescape = createEscaper(unescapeMap);
      // If the value of the named `property` is a function then invoke it with the
      // `object` as context; otherwise, return it.
      _.result = function (object, property, fallback) {
        var value = object == null ? void 0 : object[property];
        if (value === void 0) {
          value = fallback;
        }
        return _.isFunction(value) ? value.call(object) : value;
      };
      // Generate a unique integer id (unique within the entire client session).
      // Useful for temporary DOM ids.
      var idCounter = 0;
      _.uniqueId = function (prefix) {
        var id = ++idCounter + '';
        return prefix ? prefix + id : id;
      };
      // By default, Underscore uses ERB-style template delimiters, change the
      // following template settings to use alternative delimiters.
      _.templateSettings = {
        evaluate: /<%([\s\S]+?)%>/g,
        interpolate: /<%=([\s\S]+?)%>/g,
        escape: /<%-([\s\S]+?)%>/g
      };
      // When customizing `templateSettings`, if you don't want to define an
      // interpolation, evaluation or escaping regex, we need one that is
      // guaranteed not to match.
      var noMatch = /(.)^/;
      // Certain characters need to be escaped so that they can be put into a
      // string literal.
      var escapes = {
        '\'': '\'',
        '\\': '\\',
        '\r': 'r',
        '\n': 'n',
        '\u2028': 'u2028',
        '\u2029': 'u2029'
      };
      var escaper = /\\|'|\r|\n|\u2028|\u2029/g;
      var escapeChar = function (match) {
        return '\\' + escapes[match];
      };
      // JavaScript micro-templating, similar to John Resig's implementation.
      // Underscore templating handles arbitrary delimiters, preserves whitespace,
      // and correctly escapes quotes within interpolated code.
      // NB: `oldSettings` only exists for backwards compatibility.
      _.template = function (text, settings, oldSettings) {
        if (!settings && oldSettings)
          settings = oldSettings;
        settings = _.defaults({}, settings, _.templateSettings);
        // Combine delimiters into one regular expression via alternation.
        var matcher = RegExp([
          (settings.escape || noMatch).source,
          (settings.interpolate || noMatch).source,
          (settings.evaluate || noMatch).source
        ].join('|') + '|$', 'g');
        // Compile the template source, escaping string literals appropriately.
        var index = 0;
        var source = '__p+=\'';
        text.replace(matcher, function (match, escape, interpolate, evaluate, offset) {
          source += text.slice(index, offset).replace(escaper, escapeChar);
          index = offset + match.length;
          if (escape) {
            source += '\'+\n((__t=(' + escape + '))==null?\'\':_.escape(__t))+\n\'';
          } else if (interpolate) {
            source += '\'+\n((__t=(' + interpolate + '))==null?\'\':__t)+\n\'';
          } else if (evaluate) {
            source += '\';\n' + evaluate + '\n__p+=\'';
          }
          // Adobe VMs need the match returned to produce the correct offest.
          return match;
        });
        source += '\';\n';
        // If a variable is not specified, place data values in local scope.
        if (!settings.variable)
          source = 'with(obj||{}) {\n' + source + '}\n';
        source = 'var __t,__p=\'\',__j=Array.prototype.join,' + 'print=function() {__p+=__j.call(arguments,\'\');};\n' + source + 'return __p;\n';
        try {
          var render = new Function(settings.variable || 'obj', '_', source);
        } catch (e) {
          e.source = source;
          throw e;
        }
        var template = function (data) {
          return render.call(this, data, _);
        };
        // Provide the compiled source as a convenience for precompilation.
        var argument = settings.variable || 'obj';
        template.source = 'function(' + argument + ') {\n' + source + '}';
        return template;
      };
      // Add a "chain" function. Start chaining a wrapped Underscore object.
      _.chain = function (obj) {
        var instance = _(obj);
        instance._chain = true;
        return instance;
      };
      // OOP
      // ---------------
      // If Underscore is called as a function, it returns a wrapped object that
      // can be used OO-style. This wrapper holds altered versions of all the
      // underscore functions. Wrapped objects may be chained.
      // Helper function to continue chaining intermediate results.
      var result = function (instance, obj) {
        return instance._chain ? _(obj).chain() : obj;
      };
      // Add your own custom functions to the Underscore object.
      _.mixin = function (obj) {
        _.each(_.functions(obj), function (name) {
          var func = _[name] = obj[name];
          _.prototype[name] = function () {
            var args = [this._wrapped];
            push.apply(args, arguments);
            return result(this, func.apply(_, args));
          };
        });
      };
      // Add all of the Underscore functions to the wrapper object.
      _.mixin(_);
      // Add all mutator Array functions to the wrapper.
      _.each([
        'pop',
        'push',
        'reverse',
        'shift',
        'sort',
        'splice',
        'unshift'
      ], function (name) {
        var method = ArrayProto[name];
        _.prototype[name] = function () {
          var obj = this._wrapped;
          method.apply(obj, arguments);
          if ((name === 'shift' || name === 'splice') && obj.length === 0)
            delete obj[0];
          return result(this, obj);
        };
      });
      // Add all accessor Array functions to the wrapper.
      _.each([
        'concat',
        'join',
        'slice'
      ], function (name) {
        var method = ArrayProto[name];
        _.prototype[name] = function () {
          return result(this, method.apply(this._wrapped, arguments));
        };
      });
      // Extracts the result from a wrapped and chained object.
      _.prototype.value = function () {
        return this._wrapped;
      };
      // Provide unwrapping proxy for some methods used in engine operations
      // such as arithmetic and JSON stringification.
      _.prototype.valueOf = _.prototype.toJSON = _.prototype.value;
      _.prototype.toString = function () {
        return '' + this._wrapped;
      };
      // AMD registration happens at the end for compatibility with AMD loaders
      // that may not enforce next-turn semantics on modules. Even though general
      // practice for AMD registration is to be anonymous, underscore registers
      // as a named module because, like jQuery, it is a base library that is
      // popular enough to be bundled in a third party lib, but not be part of
      // an AMD load request. Those cases could generate an error when an
      // anonymous define() is called outside of a loader request.
      if (typeof define === 'function' && define.amd) {
        define('underscore', [], function () {
          return _;
        });
      }
      exportModule('underscore', _);
    }.call(this));
    (function () {
      var generateGUID = include('LYNX.Utils.generateGUID');
      var _ = include('underscore');
      // Copyright Joyent, Inc. and other Node contributors.
      //
      // Permission is hereby granted, free of charge, to any person obtaining a
      // copy of this software and associated documentation files (the
      // "Software"), to deal in the Software without restriction, including
      // without limitation the rights to use, copy, modify, merge, publish,
      // distribute, sublicense, and/or sell copies of the Software, and to permit
      // persons to whom the Software is furnished to do so, subject to the
      // following conditions:
      //
      // The above copyright notice and this permission notice shall be included
      // in all copies or substantial portions of the Software.
      //
      // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
      // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
      // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
      // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
      // USE OR OTHER DEALINGS IN THE SOFTWARE.
      var isFunction = function (arg) {
        return typeof arg === 'function';
      };
      var isNumber = function (arg) {
        return typeof arg === 'number';
      };
      var isObject = function (arg) {
        return typeof arg === 'object' && arg !== null;
      };
      var isUndefined = function (arg) {
        return arg === undefined;
      };
      var EventEmitter = function () {
        this._events = this._events || {};
        this._maxListeners = this._maxListeners || undefined;
        /**
         * Backwards compatibility shim.
         * Maps the event registration keys to the callback functions
         * @type {Object}
         */
        this._keyToFunctionMap = {};
      };
      // Backwards-compat with node 0.10.x
      EventEmitter.EventEmitter = EventEmitter;
      EventEmitter.prototype._events = undefined;
      EventEmitter.prototype._maxListeners = undefined;
      // By default EventEmitters will print a warning if more than 10 listeners are
      // added to it. This is a useful default which helps finding memory leaks.
      EventEmitter.defaultMaxListeners = 10;
      // Obviously not all Emitters should be limited to 10. This function allows
      // that to be increased. Set to zero for unlimited.
      EventEmitter.prototype.setMaxListeners = function (n) {
        if (!isNumber(n) || n < 0 || isNaN(n)) {
          throw new TypeError('n must be a positive number');
        }
        this._maxListeners = n;
        return this;
      };
      EventEmitter.prototype.emit = function (type) {
        var er, handler, len, args, i, listeners;
        if (!this._events) {
          this._events = {};
        }
        // If there is no 'error' event listener then throw.
        if (type === 'error') {
          if (!this._events.error || isObject(this._events.error) && !this._events.error.length) {
            er = arguments[1];
            if (er instanceof Error) {
              throw er;  // Unhandled 'error' event
            } else {
              throw new TypeError('Uncaught, unspecified "error" event.');
            }
          }
        }
        handler = this._events[type];
        if (isUndefined(handler)) {
          return false;
        }
        if (isFunction(handler)) {
          switch (arguments.length) {
          // fast cases
          case 1:
            handler.call(this);
            break;
          case 2:
            handler.call(this, arguments[1]);
            break;
          case 3:
            handler.call(this, arguments[1], arguments[2]);
            break;
          // slower
          default:
            args = Array.prototype.slice.call(arguments, 1);
            handler.apply(this, args);
          }
        } else if (isObject(handler)) {
          args = Array.prototype.slice.call(arguments, 1);
          listeners = handler.slice();
          len = listeners.length;
          for (i = 0; i < len; i++) {
            listeners[i].apply(this, args);
          }
        }
        return true;
      };
      EventEmitter.prototype.addListener = function (type, listener) {
        var m;
        if (!isFunction(listener)) {
          throw new TypeError('listener must be a function');
        }
        if (!this._events) {
          this._events = {};
        }
        // To avoid recursion in the case that type === "newListener"! Before
        // adding it to the listeners, first emit "newListener".
        if (this._events.newListener) {
          this.emit('newListener', type, isFunction(listener.listener) ? listener.listener : listener);
        }
        if (!this._events[type]) {
          // Optimize the case of one listener. Don't need the extra array object.
          this._events[type] = listener;
        } else if (isObject(this._events[type])) {
          // If we've already got an array, just append.
          this._events[type].push(listener);
        } else {
          // Adding the second element, need to change to array.
          this._events[type] = [
            this._events[type],
            listener
          ];
        }
        // Check for listener leak
        if (isObject(this._events[type]) && !this._events[type].warned) {
          if (isUndefined(this._maxListeners)) {
            m = EventEmitter.defaultMaxListeners;
          } else {
            m = this._maxListeners;
          }
          if (m && m > 0 && this._events[type].length > m) {
            this._events[type].warned = true;
            console.error('(node) warning: possible EventEmitter memory ' + 'leak detected. %d listeners added. ' + 'Use emitter.setMaxListeners() to increase limit.', this._events[type].length);
            if (typeof console.trace === 'function') {
              // not supported in IE 10
              console.trace();
            }
          }
        }
        return this;
      };
      EventEmitter.prototype.on = EventEmitter.prototype.addListener;
      EventEmitter.prototype.once = function (type, listener) {
        if (!isFunction(listener)) {
          throw new TypeError('listener must be a function');
        }
        var fired = false;
        var that = this;
        var g = function () {
          that.removeListener(type, g);
          if (!fired) {
            fired = true;
            listener.apply(this, arguments);
          }
        };
        g.listener = listener;
        this.on(type, g);
        return this;
      };
      // emits a 'removeListener' event iff the listener was removed
      EventEmitter.prototype.removeListener = function (type, listener) {
        var list, position, length, i;
        if (!isFunction(listener)) {
          throw new TypeError('listener must be a function');
        }
        if (!this._events || !this._events[type]) {
          return this;
        }
        list = this._events[type];
        length = list.length;
        position = -1;
        if (list === listener || isFunction(list.listener) && list.listener === listener) {
          delete this._events[type];
          if (this._events.removeListener) {
            this.emit('removeListener', type, listener);
          }
        } else if (isObject(list)) {
          for (i = length; i-- > 0;) {
            if (list[i] === listener || list[i].listener && list[i].listener === listener) {
              position = i;
              break;
            }
          }
          if (position < 0) {
            return this;
          }
          if (list.length === 1) {
            list.length = 0;
            delete this._events[type];
          } else {
            list.splice(position, 1);
          }
          if (this._events.removeListener) {
            this.emit('removeListener', type, listener);
          }
        }
        return this;
      };
      EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
      EventEmitter.prototype.removeAllListeners = function (type) {
        var key, listeners;
        if (!this._events) {
          return this;
        }
        // not listening for removeListener, no need to emit
        if (!this._events.removeListener) {
          if (arguments.length === 0) {
            this._events = {};
          } else if (this._events[type]) {
            delete this._events[type];
          }
          return this;
        }
        // emit removeListener for all listeners on all events
        if (arguments.length === 0) {
          var keys = _.keys(this._events);
          var l = keys.length;
          for (var i = 0; i < l; i++) {
            var key = keys[i];
            if (key === 'removeListener') {
              continue;
            }
            this.removeAllListeners(key);
          }
          this.removeAllListeners('removeListener');
          this._events = {};
          return this;
        }
        listeners = this._events[type];
        if (isFunction(listeners)) {
          this.removeListener(type, listeners);
        } else if (listeners) {
          // LIFO order
          while (listeners.length) {
            this.removeListener(type, listeners[listeners.length - 1]);
          }
        }
        delete this._events[type];
        return this;
      };
      EventEmitter.prototype.listeners = function (type) {
        var ret;
        if (!this._events || !this._events[type]) {
          ret = [];
        } else if (isFunction(this._events[type])) {
          ret = [this._events[type]];
        } else {
          ret = this._events[type].slice();
        }
        return ret;
      };
      EventEmitter.prototype.listenerCount = function (type) {
        if (this._events) {
          var evlistener = this._events[type];
          if (isFunction(evlistener)) {
            return 1;
          } else if (evlistener) {
            return evlistener.length;
          }
        }
        return 0;
      };
      EventEmitter.listenerCount = function (emitter, type) {
        return emitter.listenerCount(type);
      };
      /**
       * Trigger an event
       * @param {string} in_event Event handle
       * @param {object} in_caller The context in which we want to invoke the callback
       * @param {Array|*|undefined} [in_argsArr] Optionnally, a single argument
       *    or ( if necessary ) an Array containing all the arguments to pass along to
       *    the listener.
       */
      EventEmitter.prototype.trigger = function (in_event, in_caller, in_argsArr) {
        var listeners = this.listeners(in_event);
        if (listeners.length > 0) {
          if (arguments.length >= 3 && !_.isArray(in_argsArr)) {
            in_argsArr = [in_argsArr];
          }
          listeners.forEach(function (listener) {
            listener.apply(in_caller, in_argsArr);
          });
        }
      };
      /**
       * Register to an event.
       * @param {string} in_event Event handle to register to
       * @param {function} in_cb Callback function
       * @return {string} Unique key associated with the registration. This key
       * should be given to the unregister() method
       */
      EventEmitter.prototype.register = function (in_event, in_cb) {
        var key = generateGUID();
        this._keyToFunctionMap[key] = in_cb;
        this.on(in_event, in_cb);
        return key;
      };
      /**
       * Unregister an event callback, based on the key returned by .register
       * @param {string} in_event - id to register for
       * @param {string} in_key - key given by .register
       * @return {boolean} - true iff the callback was unregistered
       */
      EventEmitter.prototype.unregister = function (in_event, in_key) {
        var callback = this._keyToFunctionMap[in_key];
        if (callback) {
          delete this._keyToFunctionMap[in_key];
          this.off(in_event, callback);
        }
        return !!callback;
      };
      /**
       * Mutate the prototype of a constructor so as to mix in the EventEmitter
       * methods. This is useful when you want to make your class an EventEmitter,
       * but it inherits from some non-EventEmitter class.
       *
       * @param {function} in_constructor -
       * @return {function} the constructor passed in
       */
      EventEmitter.makeEventEmitter = function (in_constructor) {
        in_constructor.prototype.setMaxListeners = EventEmitter.prototype.setMaxListeners;
        in_constructor.prototype.addListener = EventEmitter.prototype.addListener;
        in_constructor.prototype.on = EventEmitter.prototype.on;
        in_constructor.prototype.once = EventEmitter.prototype.once;
        in_constructor.prototype.removeListener = EventEmitter.prototype.removeListener;
        in_constructor.prototype.off = EventEmitter.prototype.off;
        in_constructor.prototype.removeAllListeners = EventEmitter.prototype.removeAllListeners;
        in_constructor.prototype.listeners = EventEmitter.prototype.listeners;
        in_constructor.prototype.emit = EventEmitter.prototype.emit;
        in_constructor.prototype.trigger = EventEmitter.prototype.trigger;
        in_constructor.prototype.listenerCount = EventEmitter.prototype.listenerCount;
        in_constructor.prototype.register = EventEmitter.prototype.register;
        in_constructor.prototype.unregister = EventEmitter.prototype.unregister;
        return in_constructor;
      };
      exportModule('LYNX.Utils.Events.Singleton', new EventEmitter());
      exportModule('LYNX.Utils.Events.EventEmitter', EventEmitter);
    }());
    (function () {
      'use strict';
      /**
       * A faster alternative to `Function#apply`, this function invokes `func`
       * with the `this` binding of `thisArg` and the arguments of `args`.
       *
       * @private
       * @param {Function} func The function to invoke.
       * @param {*} thisArg The `this` binding of `func`.
       * @param {Array} args The arguments to invoke `func` with.
       * @returns {*} Returns the result of `func`.
       */
      function apply(func, thisArg, args) {
        switch (args.length) {
        case 0:
          return func.call(thisArg);
        case 1:
          return func.call(thisArg, args[0]);
        case 2:
          return func.call(thisArg, args[0], args[1]);
        case 3:
          return func.call(thisArg, args[0], args[1], args[2]);
        }
        return func.apply(thisArg, args);
      }
      /* Built-in method references for those with the same name as other `lodash` methods. */
      var nativeMax = Math.max;
      /**
       * A specialized version of `baseRest` which transforms the rest array.
       *
       * @private
       * @param {Function} func The function to apply a rest parameter to.
       * @param {number} [start=func.length-1] The start position of the rest parameter.
       * @param {Function} transform The rest array transform.
       * @returns {Function} Returns the new function.
       */
      function overRest$1(func, start, transform) {
        start = nativeMax(start === undefined ? func.length - 1 : start, 0);
        return function () {
          var args = arguments, index = -1, length = nativeMax(args.length - start, 0), array = Array(length);
          while (++index < length) {
            array[index] = args[start + index];
          }
          index = -1;
          var otherArgs = Array(start + 1);
          while (++index < start) {
            otherArgs[index] = args[index];
          }
          otherArgs[start] = transform(array);
          return apply(func, this, otherArgs);
        };
      }
      /**
       * This method returns the first argument it receives.
       *
       * @static
       * @since 0.1.0
       * @memberOf _
       * @category Util
       * @param {*} value Any value.
       * @returns {*} Returns `value`.
       * @example
       *
       * var object = { 'a': 1 };
       *
       * console.log(_.identity(object) === object);
       * // => true
       */
      function identity(value) {
        return value;
      }
      // Lodash rest function without function.toString()
      // remappings
      function rest(func, start) {
        return overRest$1(func, start, identity);
      }
      var initialParams = function (fn) {
        return rest(function (args) {
          var callback = args.pop();
          fn.call(this, args, callback);
        });
      };
      function applyEach$1(eachfn) {
        return rest(function (fns, args) {
          var go = initialParams(function (args, callback) {
            var that = this;
            return eachfn(fns, function (fn, cb) {
              fn.apply(that, args.concat(cb));
            }, callback);
          });
          if (args.length) {
            return go.apply(this, args);
          } else {
            return go;
          }
        });
      }
      /** Detect free variable `global` from Node.js. */
      var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
      /** Detect free variable `self`. */
      var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
      /** Used as a reference to the global object. */
      var root = freeGlobal || freeSelf || Function('return this')();
      /** Built-in value references. */
      var Symbol$1 = root.Symbol;
      /** Used for built-in method references. */
      var objectProto = Object.prototype;
      /** Used to check objects for own properties. */
      var hasOwnProperty = objectProto.hasOwnProperty;
      /**
       * Used to resolve the
       * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
       * of values.
       */
      var nativeObjectToString = objectProto.toString;
      /** Built-in value references. */
      var symToStringTag$1 = Symbol$1 ? Symbol$1.toStringTag : undefined;
      /**
       * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
       *
       * @private
       * @param {*} value The value to query.
       * @returns {string} Returns the raw `toStringTag`.
       */
      function getRawTag(value) {
        var isOwn = hasOwnProperty.call(value, symToStringTag$1), tag = value[symToStringTag$1];
        try {
          value[symToStringTag$1] = undefined;
          var unmasked = true;
        } catch (e) {
        }
        var result = nativeObjectToString.call(value);
        if (unmasked) {
          if (isOwn) {
            value[symToStringTag$1] = tag;
          } else {
            delete value[symToStringTag$1];
          }
        }
        return result;
      }
      /** Used for built-in method references. */
      var objectProto$1 = Object.prototype;
      /**
       * Used to resolve the
       * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
       * of values.
       */
      var nativeObjectToString$1 = objectProto$1.toString;
      /**
       * Converts `value` to a string using `Object.prototype.toString`.
       *
       * @private
       * @param {*} value The value to convert.
       * @returns {string} Returns the converted string.
       */
      function objectToString(value) {
        return nativeObjectToString$1.call(value);
      }
      /** `Object#toString` result references. */
      var nullTag = '[object Null]';
      var undefinedTag = '[object Undefined]';
      /** Built-in value references. */
      var symToStringTag = Symbol$1 ? Symbol$1.toStringTag : undefined;
      /**
       * The base implementation of `getTag` without fallbacks for buggy environments.
       *
       * @private
       * @param {*} value The value to query.
       * @returns {string} Returns the `toStringTag`.
       */
      function baseGetTag(value) {
        if (value == null) {
          return value === undefined ? undefinedTag : nullTag;
        }
        value = Object(value);
        return symToStringTag && symToStringTag in value ? getRawTag(value) : objectToString(value);
      }
      /**
       * Checks if `value` is the
       * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
       * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
       *
       * @static
       * @memberOf _
       * @since 0.1.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is an object, else `false`.
       * @example
       *
       * _.isObject({});
       * // => true
       *
       * _.isObject([1, 2, 3]);
       * // => true
       *
       * _.isObject(_.noop);
       * // => true
       *
       * _.isObject(null);
       * // => false
       */
      function isObject(value) {
        var type = typeof value;
        return value != null && (type == 'object' || type == 'function');
      }
      /** `Object#toString` result references. */
      var asyncTag = '[object AsyncFunction]';
      var funcTag = '[object Function]';
      var genTag = '[object GeneratorFunction]';
      var proxyTag = '[object Proxy]';
      /**
       * Checks if `value` is classified as a `Function` object.
       *
       * @static
       * @memberOf _
       * @since 0.1.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is a function, else `false`.
       * @example
       *
       * _.isFunction(_);
       * // => true
       *
       * _.isFunction(/abc/);
       * // => false
       */
      function isFunction(value) {
        if (!isObject(value)) {
          return false;
        }
        // The use of `Object#toString` avoids issues with the `typeof` operator
        // in Safari 9 which returns 'object' for typed arrays and other constructors.
        var tag = baseGetTag(value);
        return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
      }
      /** Used as references for various `Number` constants. */
      var MAX_SAFE_INTEGER = 9007199254740991;
      /**
       * Checks if `value` is a valid array-like length.
       *
       * **Note:** This method is loosely based on
       * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).
       *
       * @static
       * @memberOf _
       * @since 4.0.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
       * @example
       *
       * _.isLength(3);
       * // => true
       *
       * _.isLength(Number.MIN_VALUE);
       * // => false
       *
       * _.isLength(Infinity);
       * // => false
       *
       * _.isLength('3');
       * // => false
       */
      function isLength(value) {
        return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
      }
      /**
       * Checks if `value` is array-like. A value is considered array-like if it's
       * not a function and has a `value.length` that's an integer greater than or
       * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
       *
       * @static
       * @memberOf _
       * @since 4.0.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
       * @example
       *
       * _.isArrayLike([1, 2, 3]);
       * // => true
       *
       * _.isArrayLike(document.body.children);
       * // => true
       *
       * _.isArrayLike('abc');
       * // => true
       *
       * _.isArrayLike(_.noop);
       * // => false
       */
      function isArrayLike(value) {
        return value != null && isLength(value.length) && !isFunction(value);
      }
      /**
       * This method returns `undefined`.
       *
       * @static
       * @memberOf _
       * @since 2.3.0
       * @category Util
       * @example
       *
       * _.times(2, _.noop);
       * // => [undefined, undefined]
       */
      function noop() {
      }
      function once(fn) {
        return function () {
          if (fn === null)
            return;
          var callFn = fn;
          fn = null;
          callFn.apply(this, arguments);
        };
      }
      var iteratorSymbol = typeof Symbol === 'function' && Symbol.iterator;
      var getIterator = function (coll) {
        return iteratorSymbol && coll[iteratorSymbol] && coll[iteratorSymbol]();
      };
      /**
       * The base implementation of `_.times` without support for iteratee shorthands
       * or max array length checks.
       *
       * @private
       * @param {number} n The number of times to invoke `iteratee`.
       * @param {Function} iteratee The function invoked per iteration.
       * @returns {Array} Returns the array of results.
       */
      function baseTimes(n, iteratee) {
        var index = -1, result = Array(n);
        while (++index < n) {
          result[index] = iteratee(index);
        }
        return result;
      }
      /**
       * Checks if `value` is object-like. A value is object-like if it's not `null`
       * and has a `typeof` result of "object".
       *
       * @static
       * @memberOf _
       * @since 4.0.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
       * @example
       *
       * _.isObjectLike({});
       * // => true
       *
       * _.isObjectLike([1, 2, 3]);
       * // => true
       *
       * _.isObjectLike(_.noop);
       * // => false
       *
       * _.isObjectLike(null);
       * // => false
       */
      function isObjectLike(value) {
        return value != null && typeof value == 'object';
      }
      /** `Object#toString` result references. */
      var argsTag = '[object Arguments]';
      /**
       * The base implementation of `_.isArguments`.
       *
       * @private
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is an `arguments` object,
       */
      function baseIsArguments(value) {
        return isObjectLike(value) && baseGetTag(value) == argsTag;
      }
      /** Used for built-in method references. */
      var objectProto$3 = Object.prototype;
      /** Used to check objects for own properties. */
      var hasOwnProperty$2 = objectProto$3.hasOwnProperty;
      /** Built-in value references. */
      var propertyIsEnumerable = objectProto$3.propertyIsEnumerable;
      /**
       * Checks if `value` is likely an `arguments` object.
       *
       * @static
       * @memberOf _
       * @since 0.1.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is an `arguments` object,
       *  else `false`.
       * @example
       *
       * _.isArguments(function() { return arguments; }());
       * // => true
       *
       * _.isArguments([1, 2, 3]);
       * // => false
       */
      var isArguments = baseIsArguments(function () {
        return arguments;
      }()) ? baseIsArguments : function (value) {
        return isObjectLike(value) && hasOwnProperty$2.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee');
      };
      /**
       * Checks if `value` is classified as an `Array` object.
       *
       * @static
       * @memberOf _
       * @since 0.1.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is an array, else `false`.
       * @example
       *
       * _.isArray([1, 2, 3]);
       * // => true
       *
       * _.isArray(document.body.children);
       * // => false
       *
       * _.isArray('abc');
       * // => false
       *
       * _.isArray(_.noop);
       * // => false
       */
      var isArray = Array.isArray;
      /**
       * This method returns `false`.
       *
       * @static
       * @memberOf _
       * @since 4.13.0
       * @category Util
       * @returns {boolean} Returns `false`.
       * @example
       *
       * _.times(2, _.stubFalse);
       * // => [false, false]
       */
      function stubFalse() {
        return false;
      }
      /** Detect free variable `exports`. */
      var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
      /** Detect free variable `module`. */
      var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
      /** Detect the popular CommonJS extension `module.exports`. */
      var moduleExports = freeModule && freeModule.exports === freeExports;
      /** Built-in value references. */
      var Buffer = moduleExports ? root.Buffer : undefined;
      /* Built-in method references for those with the same name as other `lodash` methods. */
      var nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined;
      /**
       * Checks if `value` is a buffer.
       *
       * @static
       * @memberOf _
       * @since 4.3.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.
       * @example
       *
       * _.isBuffer(new Buffer(2));
       * // => true
       *
       * _.isBuffer(new Uint8Array(2));
       * // => false
       */
      var isBuffer = nativeIsBuffer || stubFalse;
      /** Used as references for various `Number` constants. */
      var MAX_SAFE_INTEGER$1 = 9007199254740991;
      /** Used to detect unsigned integer values. */
      var reIsUint = /^(?:0|[1-9]\d*)$/;
      /**
       * Checks if `value` is a valid array-like index.
       *
       * @private
       * @param {*} value The value to check.
       * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
       * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
       */
      function isIndex(value, length) {
        length = length == null ? MAX_SAFE_INTEGER$1 : length;
        return !!length && (typeof value == 'number' || reIsUint.test(value)) && (value > -1 && value % 1 == 0 && value < length);
      }
      /** `Object#toString` result references. */
      var argsTag$1 = '[object Arguments]';
      var arrayTag = '[object Array]';
      var boolTag = '[object Boolean]';
      var dateTag = '[object Date]';
      var errorTag = '[object Error]';
      var funcTag$1 = '[object Function]';
      var mapTag = '[object Map]';
      var numberTag = '[object Number]';
      var objectTag = '[object Object]';
      var regexpTag = '[object RegExp]';
      var setTag = '[object Set]';
      var stringTag = '[object String]';
      var weakMapTag = '[object WeakMap]';
      var arrayBufferTag = '[object ArrayBuffer]';
      var dataViewTag = '[object DataView]';
      var float32Tag = '[object Float32Array]';
      var float64Tag = '[object Float64Array]';
      var int8Tag = '[object Int8Array]';
      var int16Tag = '[object Int16Array]';
      var int32Tag = '[object Int32Array]';
      var uint8Tag = '[object Uint8Array]';
      var uint8ClampedTag = '[object Uint8ClampedArray]';
      var uint16Tag = '[object Uint16Array]';
      var uint32Tag = '[object Uint32Array]';
      /** Used to identify `toStringTag` values of typed arrays. */
      var typedArrayTags = {};
      typedArrayTags[float32Tag] = typedArrayTags[float64Tag] = typedArrayTags[int8Tag] = typedArrayTags[int16Tag] = typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] = typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] = typedArrayTags[uint32Tag] = true;
      typedArrayTags[argsTag$1] = typedArrayTags[arrayTag] = typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] = typedArrayTags[dataViewTag] = typedArrayTags[dateTag] = typedArrayTags[errorTag] = typedArrayTags[funcTag$1] = typedArrayTags[mapTag] = typedArrayTags[numberTag] = typedArrayTags[objectTag] = typedArrayTags[regexpTag] = typedArrayTags[setTag] = typedArrayTags[stringTag] = typedArrayTags[weakMapTag] = false;
      /**
       * The base implementation of `_.isTypedArray` without Node.js optimizations.
       *
       * @private
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
       */
      function baseIsTypedArray(value) {
        return isObjectLike(value) && isLength(value.length) && !!typedArrayTags[baseGetTag(value)];
      }
      /**
       * The base implementation of `_.unary` without support for storing metadata.
       *
       * @private
       * @param {Function} func The function to cap arguments for.
       * @returns {Function} Returns the new capped function.
       */
      function baseUnary(func) {
        return function (value) {
          return func(value);
        };
      }
      /** Detect free variable `exports`. */
      var freeExports$1 = typeof exports == 'object' && exports && !exports.nodeType && exports;
      /** Detect free variable `module`. */
      var freeModule$1 = freeExports$1 && typeof module == 'object' && module && !module.nodeType && module;
      /** Detect the popular CommonJS extension `module.exports`. */
      var moduleExports$1 = freeModule$1 && freeModule$1.exports === freeExports$1;
      /** Detect free variable `process` from Node.js. */
      var freeProcess = moduleExports$1 && freeGlobal.process;
      /** Used to access faster Node.js helpers. */
      var nodeUtil = function () {
        try {
          return freeProcess && freeProcess.binding('util');
        } catch (e) {
        }
      }();
      /* Node.js helper references. */
      var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;
      /**
       * Checks if `value` is classified as a typed array.
       *
       * @static
       * @memberOf _
       * @since 3.0.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
       * @example
       *
       * _.isTypedArray(new Uint8Array);
       * // => true
       *
       * _.isTypedArray([]);
       * // => false
       */
      var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;
      /** Used for built-in method references. */
      var objectProto$2 = Object.prototype;
      /** Used to check objects for own properties. */
      var hasOwnProperty$1 = objectProto$2.hasOwnProperty;
      /**
       * Creates an array of the enumerable property names of the array-like `value`.
       *
       * @private
       * @param {*} value The value to query.
       * @param {boolean} inherited Specify returning inherited property names.
       * @returns {Array} Returns the array of property names.
       */
      function arrayLikeKeys(value, inherited) {
        var isArr = isArray(value), isArg = !isArr && isArguments(value), isBuff = !isArr && !isArg && isBuffer(value), isType = !isArr && !isArg && !isBuff && isTypedArray(value), skipIndexes = isArr || isArg || isBuff || isType, result = skipIndexes ? baseTimes(value.length, String) : [], length = result.length;
        for (var key in value) {
          if ((inherited || hasOwnProperty$1.call(value, key)) && !(skipIndexes && // Safari 9 has enumerable `arguments.length` in strict mode.
            (key == 'length' || isBuff && (key == 'offset' || key == 'parent') || isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset') || // Skip index properties.
            isIndex(key, length)))) {
            result.push(key);
          }
        }
        return result;
      }
      /** Used for built-in method references. */
      var objectProto$5 = Object.prototype;
      /**
       * Checks if `value` is likely a prototype object.
       *
       * @private
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
       */
      function isPrototype(value) {
        var Ctor = value && value.constructor, proto = typeof Ctor == 'function' && Ctor.prototype || objectProto$5;
        return value === proto;
      }
      /**
       * Creates a unary function that invokes `func` with its argument transformed.
       *
       * @private
       * @param {Function} func The function to wrap.
       * @param {Function} transform The argument transform.
       * @returns {Function} Returns the new function.
       */
      function overArg(func, transform) {
        return function (arg) {
          return func(transform(arg));
        };
      }
      /* Built-in method references for those with the same name as other `lodash` methods. */
      var nativeKeys = overArg(Object.keys, Object);
      /** Used for built-in method references. */
      var objectProto$4 = Object.prototype;
      /** Used to check objects for own properties. */
      var hasOwnProperty$3 = objectProto$4.hasOwnProperty;
      /**
       * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.
       *
       * @private
       * @param {Object} object The object to query.
       * @returns {Array} Returns the array of property names.
       */
      function baseKeys(object) {
        if (!isPrototype(object)) {
          return nativeKeys(object);
        }
        var result = [];
        for (var key in Object(object)) {
          if (hasOwnProperty$3.call(object, key) && key != 'constructor') {
            result.push(key);
          }
        }
        return result;
      }
      /**
       * Creates an array of the own enumerable property names of `object`.
       *
       * **Note:** Non-object values are coerced to objects. See the
       * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
       * for more details.
       *
       * @static
       * @since 0.1.0
       * @memberOf _
       * @category Object
       * @param {Object} object The object to query.
       * @returns {Array} Returns the array of property names.
       * @example
       *
       * function Foo() {
       *   this.a = 1;
       *   this.b = 2;
       * }
       *
       * Foo.prototype.c = 3;
       *
       * _.keys(new Foo);
       * // => ['a', 'b'] (iteration order is not guaranteed)
       *
       * _.keys('hi');
       * // => ['0', '1']
       */
      function keys(object) {
        return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);
      }
      function createArrayIterator(coll) {
        var i = -1;
        var len = coll.length;
        return function next() {
          return ++i < len ? {
            value: coll[i],
            key: i
          } : null;
        };
      }
      function createES2015Iterator(iterator) {
        var i = -1;
        return function next() {
          var item = iterator.next();
          if (item.done)
            return null;
          i++;
          return {
            value: item.value,
            key: i
          };
        };
      }
      function createObjectIterator(obj) {
        var okeys = keys(obj);
        var i = -1;
        var len = okeys.length;
        return function next() {
          var key = okeys[++i];
          return i < len ? {
            value: obj[key],
            key: key
          } : null;
        };
      }
      function iterator(coll) {
        if (isArrayLike(coll)) {
          return createArrayIterator(coll);
        }
        var iterator = getIterator(coll);
        return iterator ? createES2015Iterator(iterator) : createObjectIterator(coll);
      }
      function onlyOnce(fn) {
        return function () {
          if (fn === null)
            throw new Error('Callback was already called.');
          var callFn = fn;
          fn = null;
          callFn.apply(this, arguments);
        };
      }
      // A temporary value used to identify if the loop should be broken.
      // See #1064, #1293
      var breakLoop = {};
      function _eachOfLimit(limit) {
        return function (obj, iteratee, callback) {
          callback = once(callback || noop);
          if (limit <= 0 || !obj) {
            return callback(null);
          }
          var nextElem = iterator(obj);
          var done = false;
          var running = 0;
          function iterateeCallback(err, value) {
            running -= 1;
            if (err) {
              done = true;
              callback(err);
            } else if (value === breakLoop || done && running <= 0) {
              done = true;
              return callback(null);
            } else {
              replenish();
            }
          }
          function replenish() {
            while (running < limit && !done) {
              var elem = nextElem();
              if (elem === null) {
                done = true;
                if (running <= 0) {
                  callback(null);
                }
                return;
              }
              running += 1;
              iteratee(elem.value, elem.key, onlyOnce(iterateeCallback));
            }
          }
          replenish();
        };
      }
      /**
       * The same as [`eachOf`]{@link module:Collections.eachOf} but runs a maximum of `limit` async operations at a
       * time.
       *
       * @name eachOfLimit
       * @static
       * @memberOf module:Collections
       * @method
       * @see [async.eachOf]{@link module:Collections.eachOf}
       * @alias forEachOfLimit
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {number} limit - The maximum number of async operations at a time.
       * @param {Function} iteratee - A function to apply to each
       * item in `coll`. The `key` is the item's key, or index in the case of an
       * array. The iteratee is passed a `callback(err)` which must be called once it
       * has completed. If no error has occurred, the callback should be run without
       * arguments or with an explicit `null` argument. Invoked with
       * (item, key, callback).
       * @param {Function} [callback] - A callback which is called when all
       * `iteratee` functions have finished, or an error occurs. Invoked with (err).
       */
      function eachOfLimit(coll, limit, iteratee, callback) {
        _eachOfLimit(limit)(coll, iteratee, callback);
      }
      function doLimit(fn, limit) {
        return function (iterable, iteratee, callback) {
          return fn(iterable, limit, iteratee, callback);
        };
      }
      // eachOf implementation optimized for array-likes
      function eachOfArrayLike(coll, iteratee, callback) {
        callback = once(callback || noop);
        var index = 0, completed = 0, length = coll.length;
        if (length === 0) {
          callback(null);
        }
        function iteratorCallback(err) {
          if (err) {
            callback(err);
          } else if (++completed === length) {
            callback(null);
          }
        }
        for (; index < length; index++) {
          iteratee(coll[index], index, onlyOnce(iteratorCallback));
        }
      }
      // a generic version of eachOf which can handle array, object, and iterator cases.
      var eachOfGeneric = doLimit(eachOfLimit, Infinity);
      /**
       * Like [`each`]{@link module:Collections.each}, except that it passes the key (or index) as the second argument
       * to the iteratee.
       *
       * @name eachOf
       * @static
       * @memberOf module:Collections
       * @method
       * @alias forEachOf
       * @category Collection
       * @see [async.each]{@link module:Collections.each}
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {Function} iteratee - A function to apply to each
       * item in `coll`. The `key` is the item's key, or index in the case of an
       * array. The iteratee is passed a `callback(err)` which must be called once it
       * has completed. If no error has occurred, the callback should be run without
       * arguments or with an explicit `null` argument. Invoked with
       * (item, key, callback).
       * @param {Function} [callback] - A callback which is called when all
       * `iteratee` functions have finished, or an error occurs. Invoked with (err).
       * @example
       *
       * var obj = {dev: "/dev.json", test: "/test.json", prod: "/prod.json"};
       * var configs = {};
       *
       * async.forEachOf(obj, function (value, key, callback) {
       *     fs.readFile(__dirname + value, "utf8", function (err, data) {
       *         if (err) return callback(err);
       *         try {
       *             configs[key] = JSON.parse(data);
       *         } catch (e) {
       *             return callback(e);
       *         }
       *         callback();
       *     });
       * }, function (err) {
       *     if (err) console.error(err.message);
       *     // configs is now a map of JSON data
       *     doSomethingWith(configs);
       * });
       */
      var eachOf = function (coll, iteratee, callback) {
        var eachOfImplementation = isArrayLike(coll) ? eachOfArrayLike : eachOfGeneric;
        eachOfImplementation(coll, iteratee, callback);
      };
      function doParallel(fn) {
        return function (obj, iteratee, callback) {
          return fn(eachOf, obj, iteratee, callback);
        };
      }
      function _asyncMap(eachfn, arr, iteratee, callback) {
        callback = callback || noop;
        arr = arr || [];
        var results = [];
        var counter = 0;
        eachfn(arr, function (value, _, callback) {
          var index = counter++;
          iteratee(value, function (err, v) {
            results[index] = v;
            callback(err);
          });
        }, function (err) {
          callback(err, results);
        });
      }
      /**
       * Produces a new collection of values by mapping each value in `coll` through
       * the `iteratee` function. The `iteratee` is called with an item from `coll`
       * and a callback for when it has finished processing. Each of these callback
       * takes 2 arguments: an `error`, and the transformed item from `coll`. If
       * `iteratee` passes an error to its callback, the main `callback` (for the
       * `map` function) is immediately called with the error.
       *
       * Note, that since this function applies the `iteratee` to each item in
       * parallel, there is no guarantee that the `iteratee` functions will complete
       * in order. However, the results array will be in the same order as the
       * original `coll`.
       *
       * If `map` is passed an Object, the results will be an Array.  The results
       * will roughly be in the order of the original Objects' keys (but this can
       * vary across JavaScript engines)
       *
       * @name map
       * @static
       * @memberOf module:Collections
       * @method
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {Function} iteratee - A function to apply to each item in `coll`.
       * The iteratee is passed a `callback(err, transformed)` which must be called
       * once it has completed with an error (which can be `null`) and a
       * transformed item. Invoked with (item, callback).
       * @param {Function} [callback] - A callback which is called when all `iteratee`
       * functions have finished, or an error occurs. Results is an Array of the
       * transformed items from the `coll`. Invoked with (err, results).
       * @example
       *
       * async.map(['file1','file2','file3'], fs.stat, function(err, results) {
       *     // results is now an array of stats for each file
       * });
       */
      var map = doParallel(_asyncMap);
      /**
       * Applies the provided arguments to each function in the array, calling
       * `callback` after all functions have completed. If you only provide the first
       * argument, `fns`, then it will return a function which lets you pass in the
       * arguments as if it were a single function call. If more arguments are
       * provided, `callback` is required while `args` is still optional.
       *
       * @name applyEach
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @category Control Flow
       * @param {Array|Iterable|Object} fns - A collection of asynchronous functions
       * to all call with the same arguments
       * @param {...*} [args] - any number of separate arguments to pass to the
       * function.
       * @param {Function} [callback] - the final argument should be the callback,
       * called when all functions have completed processing.
       * @returns {Function} - If only the first argument, `fns`, is provided, it will
       * return a function which lets you pass in the arguments as if it were a single
       * function call. The signature is `(..args, callback)`. If invoked with any
       * arguments, `callback` is required.
       * @example
       *
       * async.applyEach([enableSearch, updateSchema], 'bucket', callback);
       *
       * // partial application example:
       * async.each(
       *     buckets,
       *     async.applyEach([enableSearch, updateSchema]),
       *     callback
       * );
       */
      var applyEach = applyEach$1(map);
      function doParallelLimit(fn) {
        return function (obj, limit, iteratee, callback) {
          return fn(_eachOfLimit(limit), obj, iteratee, callback);
        };
      }
      /**
       * The same as [`map`]{@link module:Collections.map} but runs a maximum of `limit` async operations at a time.
       *
       * @name mapLimit
       * @static
       * @memberOf module:Collections
       * @method
       * @see [async.map]{@link module:Collections.map}
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {number} limit - The maximum number of async operations at a time.
       * @param {Function} iteratee - A function to apply to each item in `coll`.
       * The iteratee is passed a `callback(err, transformed)` which must be called
       * once it has completed with an error (which can be `null`) and a transformed
       * item. Invoked with (item, callback).
       * @param {Function} [callback] - A callback which is called when all `iteratee`
       * functions have finished, or an error occurs. Results is an array of the
       * transformed items from the `coll`. Invoked with (err, results).
       */
      var mapLimit = doParallelLimit(_asyncMap);
      /**
       * The same as [`map`]{@link module:Collections.map} but runs only a single async operation at a time.
       *
       * @name mapSeries
       * @static
       * @memberOf module:Collections
       * @method
       * @see [async.map]{@link module:Collections.map}
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {Function} iteratee - A function to apply to each item in `coll`.
       * The iteratee is passed a `callback(err, transformed)` which must be called
       * once it has completed with an error (which can be `null`) and a
       * transformed item. Invoked with (item, callback).
       * @param {Function} [callback] - A callback which is called when all `iteratee`
       * functions have finished, or an error occurs. Results is an array of the
       * transformed items from the `coll`. Invoked with (err, results).
       */
      var mapSeries = doLimit(mapLimit, 1);
      /**
       * The same as [`applyEach`]{@link module:ControlFlow.applyEach} but runs only a single async operation at a time.
       *
       * @name applyEachSeries
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @see [async.applyEach]{@link module:ControlFlow.applyEach}
       * @category Control Flow
       * @param {Array|Iterable|Object} fns - A collection of asynchronous functions to all
       * call with the same arguments
       * @param {...*} [args] - any number of separate arguments to pass to the
       * function.
       * @param {Function} [callback] - the final argument should be the callback,
       * called when all functions have completed processing.
       * @returns {Function} - If only the first argument is provided, it will return
       * a function which lets you pass in the arguments as if it were a single
       * function call.
       */
      var applyEachSeries = applyEach$1(mapSeries);
      /**
       * Creates a continuation function with some arguments already applied.
       *
       * Useful as a shorthand when combined with other control flow functions. Any
       * arguments passed to the returned function are added to the arguments
       * originally passed to apply.
       *
       * @name apply
       * @static
       * @memberOf module:Utils
       * @method
       * @category Util
       * @param {Function} function - The function you want to eventually apply all
       * arguments to. Invokes with (arguments...).
       * @param {...*} arguments... - Any number of arguments to automatically apply
       * when the continuation is called.
       * @example
       *
       * // using apply
       * async.parallel([
       *     async.apply(fs.writeFile, 'testfile1', 'test1'),
       *     async.apply(fs.writeFile, 'testfile2', 'test2')
       * ]);
       *
       *
       * // the same process without using apply
       * async.parallel([
       *     function(callback) {
       *         fs.writeFile('testfile1', 'test1', callback);
       *     },
       *     function(callback) {
       *         fs.writeFile('testfile2', 'test2', callback);
       *     }
       * ]);
       *
       * // It's possible to pass any number of additional arguments when calling the
       * // continuation:
       *
       * node> var fn = async.apply(sys.puts, 'one');
       * node> fn('two', 'three');
       * one
       * two
       * three
       */
      var apply$2 = rest(function (fn, args) {
        return rest(function (callArgs) {
          return fn.apply(null, args.concat(callArgs));
        });
      });
      /**
       * Take a sync function and make it async, passing its return value to a
       * callback. This is useful for plugging sync functions into a waterfall,
       * series, or other async functions. Any arguments passed to the generated
       * function will be passed to the wrapped function (except for the final
       * callback argument). Errors thrown will be passed to the callback.
       *
       * If the function passed to `asyncify` returns a Promise, that promises's
       * resolved/rejected state will be used to call the callback, rather than simply
       * the synchronous return value.
       *
       * This also means you can asyncify ES2016 `async` functions.
       *
       * @name asyncify
       * @static
       * @memberOf module:Utils
       * @method
       * @alias wrapSync
       * @category Util
       * @param {Function} func - The synchronous function to convert to an
       * asynchronous function.
       * @returns {Function} An asynchronous wrapper of the `func`. To be invoked with
       * (callback).
       * @example
       *
       * // passing a regular synchronous function
       * async.waterfall([
       *     async.apply(fs.readFile, filename, "utf8"),
       *     async.asyncify(JSON.parse),
       *     function (data, next) {
       *         // data is the result of parsing the text.
       *         // If there was a parsing error, it would have been caught.
       *     }
       * ], callback);
       *
       * // passing a function returning a promise
       * async.waterfall([
       *     async.apply(fs.readFile, filename, "utf8"),
       *     async.asyncify(function (contents) {
       *         return db.model.create(contents);
       *     }),
       *     function (model, next) {
       *         // `model` is the instantiated model object.
       *         // If there was an error, this function would be skipped.
       *     }
       * ], callback);
       *
       * // es6 example
       * var q = async.queue(async.asyncify(async function(file) {
       *     var intermediateStep = await processFile(file);
       *     return await somePromise(intermediateStep)
       * }));
       *
       * q.push(files);
       */
      function asyncify(func) {
        return initialParams(function (args, callback) {
          var result;
          try {
            result = func.apply(this, args);
          } catch (e) {
            return callback(e);
          }
          // if result is Promise object
          if (isObject(result) && typeof result.then === 'function') {
            result.then(function (value) {
              callback(null, value);
            }, function (err) {
              callback(err.message ? err : new Error(err));
            });
          } else {
            callback(null, result);
          }
        });
      }
      /**
       * A specialized version of `_.forEach` for arrays without support for
       * iteratee shorthands.
       *
       * @private
       * @param {Array} [array] The array to iterate over.
       * @param {Function} iteratee The function invoked per iteration.
       * @returns {Array} Returns `array`.
       */
      function arrayEach(array, iteratee) {
        var index = -1, length = array == null ? 0 : array.length;
        while (++index < length) {
          if (iteratee(array[index], index, array) === false) {
            break;
          }
        }
        return array;
      }
      /**
       * Creates a base function for methods like `_.forIn` and `_.forOwn`.
       *
       * @private
       * @param {boolean} [fromRight] Specify iterating from right to left.
       * @returns {Function} Returns the new base function.
       */
      function createBaseFor(fromRight) {
        return function (object, iteratee, keysFunc) {
          var index = -1, iterable = Object(object), props = keysFunc(object), length = props.length;
          while (length--) {
            var key = props[fromRight ? length : ++index];
            if (iteratee(iterable[key], key, iterable) === false) {
              break;
            }
          }
          return object;
        };
      }
      /**
       * The base implementation of `baseForOwn` which iterates over `object`
       * properties returned by `keysFunc` and invokes `iteratee` for each property.
       * Iteratee functions may exit iteration early by explicitly returning `false`.
       *
       * @private
       * @param {Object} object The object to iterate over.
       * @param {Function} iteratee The function invoked per iteration.
       * @param {Function} keysFunc The function to get the keys of `object`.
       * @returns {Object} Returns `object`.
       */
      var baseFor = createBaseFor();
      /**
       * The base implementation of `_.forOwn` without support for iteratee shorthands.
       *
       * @private
       * @param {Object} object The object to iterate over.
       * @param {Function} iteratee The function invoked per iteration.
       * @returns {Object} Returns `object`.
       */
      function baseForOwn(object, iteratee) {
        return object && baseFor(object, iteratee, keys);
      }
      /**
       * The base implementation of `_.findIndex` and `_.findLastIndex` without
       * support for iteratee shorthands.
       *
       * @private
       * @param {Array} array The array to inspect.
       * @param {Function} predicate The function invoked per iteration.
       * @param {number} fromIndex The index to search from.
       * @param {boolean} [fromRight] Specify iterating from right to left.
       * @returns {number} Returns the index of the matched value, else `-1`.
       */
      function baseFindIndex(array, predicate, fromIndex, fromRight) {
        var length = array.length, index = fromIndex + (fromRight ? 1 : -1);
        while (fromRight ? index-- : ++index < length) {
          if (predicate(array[index], index, array)) {
            return index;
          }
        }
        return -1;
      }
      /**
       * The base implementation of `_.isNaN` without support for number objects.
       *
       * @private
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
       */
      function baseIsNaN(value) {
        return value !== value;
      }
      /**
       * A specialized version of `_.indexOf` which performs strict equality
       * comparisons of values, i.e. `===`.
       *
       * @private
       * @param {Array} array The array to inspect.
       * @param {*} value The value to search for.
       * @param {number} fromIndex The index to search from.
       * @returns {number} Returns the index of the matched value, else `-1`.
       */
      function strictIndexOf(array, value, fromIndex) {
        var index = fromIndex - 1, length = array.length;
        while (++index < length) {
          if (array[index] === value) {
            return index;
          }
        }
        return -1;
      }
      /**
       * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
       *
       * @private
       * @param {Array} array The array to inspect.
       * @param {*} value The value to search for.
       * @param {number} fromIndex The index to search from.
       * @returns {number} Returns the index of the matched value, else `-1`.
       */
      function baseIndexOf(array, value, fromIndex) {
        return value === value ? strictIndexOf(array, value, fromIndex) : baseFindIndex(array, baseIsNaN, fromIndex);
      }
      /**
       * Determines the best order for running the functions in `tasks`, based on
       * their requirements. Each function can optionally depend on other functions
       * being completed first, and each function is run as soon as its requirements
       * are satisfied.
       *
       * If any of the functions pass an error to their callback, the `auto` sequence
       * will stop. Further tasks will not execute (so any other functions depending
       * on it will not run), and the main `callback` is immediately called with the
       * error.
       *
       * Functions also receive an object containing the results of functions which
       * have completed so far as the first argument, if they have dependencies. If a
       * task function has no dependencies, it will only be passed a callback.
       *
       * @name auto
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @category Control Flow
       * @param {Object} tasks - An object. Each of its properties is either a
       * function or an array of requirements, with the function itself the last item
       * in the array. The object's key of a property serves as the name of the task
       * defined by that property, i.e. can be used when specifying requirements for
       * other tasks. The function receives one or two arguments:
       * * a `results` object, containing the results of the previously executed
       *   functions, only passed if the task has any dependencies,
       * * a `callback(err, result)` function, which must be called when finished,
       *   passing an `error` (which can be `null`) and the result of the function's
       *   execution.
       * @param {number} [concurrency=Infinity] - An optional `integer` for
       * determining the maximum number of tasks that can be run in parallel. By
       * default, as many as possible.
       * @param {Function} [callback] - An optional callback which is called when all
       * the tasks have been completed. It receives the `err` argument if any `tasks`
       * pass an error to their callback. Results are always returned; however, if an
       * error occurs, no further `tasks` will be performed, and the results object
       * will only contain partial results. Invoked with (err, results).
       * @returns undefined
       * @example
       *
       * async.auto({
       *     // this function will just be passed a callback
       *     readData: async.apply(fs.readFile, 'data.txt', 'utf-8'),
       *     showData: ['readData', function(results, cb) {
       *         // results.readData is the file's contents
       *         // ...
       *     }]
       * }, callback);
       *
       * async.auto({
       *     get_data: function(callback) {
       *         console.log('in get_data');
       *         // async code to get some data
       *         callback(null, 'data', 'converted to array');
       *     },
       *     make_folder: function(callback) {
       *         console.log('in make_folder');
       *         // async code to create a directory to store a file in
       *         // this is run at the same time as getting the data
       *         callback(null, 'folder');
       *     },
       *     write_file: ['get_data', 'make_folder', function(results, callback) {
       *         console.log('in write_file', JSON.stringify(results));
       *         // once there is some data and the directory exists,
       *         // write the data to a file in the directory
       *         callback(null, 'filename');
       *     }],
       *     email_link: ['write_file', function(results, callback) {
       *         console.log('in email_link', JSON.stringify(results));
       *         // once the file is written let's email a link to it...
       *         // results.write_file contains the filename returned by write_file.
       *         callback(null, {'file':results.write_file, 'email':'user@example.com'});
       *     }]
       * }, function(err, results) {
       *     console.log('err = ', err);
       *     console.log('results = ', results);
       * });
       */
      var auto = function (tasks, concurrency, callback) {
        if (typeof concurrency === 'function') {
          // concurrency is optional, shift the args.
          callback = concurrency;
          concurrency = null;
        }
        callback = once(callback || noop);
        var keys$$1 = keys(tasks);
        var numTasks = keys$$1.length;
        if (!numTasks) {
          return callback(null);
        }
        if (!concurrency) {
          concurrency = numTasks;
        }
        var results = {};
        var runningTasks = 0;
        var hasError = false;
        var listeners = {};
        var readyTasks = [];
        // for cycle detection:
        var readyToCheck = [];
        // tasks that have been identified as reachable
        // without the possibility of returning to an ancestor task
        var uncheckedDependencies = {};
        baseForOwn(tasks, function (task, key) {
          if (!isArray(task)) {
            // no dependencies
            enqueueTask(key, [task]);
            readyToCheck.push(key);
            return;
          }
          var dependencies = task.slice(0, task.length - 1);
          var remainingDependencies = dependencies.length;
          if (remainingDependencies === 0) {
            enqueueTask(key, task);
            readyToCheck.push(key);
            return;
          }
          uncheckedDependencies[key] = remainingDependencies;
          arrayEach(dependencies, function (dependencyName) {
            if (!tasks[dependencyName]) {
              throw new Error('async.auto task `' + key + '` has a non-existent dependency in ' + dependencies.join(', '));
            }
            addListener(dependencyName, function () {
              remainingDependencies--;
              if (remainingDependencies === 0) {
                enqueueTask(key, task);
              }
            });
          });
        });
        checkForDeadlocks();
        processQueue();
        function enqueueTask(key, task) {
          readyTasks.push(function () {
            runTask(key, task);
          });
        }
        function processQueue() {
          if (readyTasks.length === 0 && runningTasks === 0) {
            return callback(null, results);
          }
          while (readyTasks.length && runningTasks < concurrency) {
            var run = readyTasks.shift();
            run();
          }
        }
        function addListener(taskName, fn) {
          var taskListeners = listeners[taskName];
          if (!taskListeners) {
            taskListeners = listeners[taskName] = [];
          }
          taskListeners.push(fn);
        }
        function taskComplete(taskName) {
          var taskListeners = listeners[taskName] || [];
          arrayEach(taskListeners, function (fn) {
            fn();
          });
          processQueue();
        }
        function runTask(key, task) {
          if (hasError)
            return;
          var taskCallback = onlyOnce(rest(function (err, args) {
            runningTasks--;
            if (args.length <= 1) {
              args = args[0];
            }
            if (err) {
              var safeResults = {};
              baseForOwn(results, function (val, rkey) {
                safeResults[rkey] = val;
              });
              safeResults[key] = args;
              hasError = true;
              listeners = [];
              callback(err, safeResults);
            } else {
              results[key] = args;
              taskComplete(key);
            }
          }));
          runningTasks++;
          var taskFn = task[task.length - 1];
          if (task.length > 1) {
            taskFn(results, taskCallback);
          } else {
            taskFn(taskCallback);
          }
        }
        function checkForDeadlocks() {
          // Kahn's algorithm
          // https://en.wikipedia.org/wiki/Topological_sorting#Kahn.27s_algorithm
          // http://connalle.blogspot.com/2013/10/topological-sortingkahn-algorithm.html
          var currentTask;
          var counter = 0;
          while (readyToCheck.length) {
            currentTask = readyToCheck.pop();
            counter++;
            arrayEach(getDependents(currentTask), function (dependent) {
              if (--uncheckedDependencies[dependent] === 0) {
                readyToCheck.push(dependent);
              }
            });
          }
          if (counter !== numTasks) {
            throw new Error('async.auto cannot execute tasks due to a recursive dependency');
          }
        }
        function getDependents(taskName) {
          var result = [];
          baseForOwn(tasks, function (task, key) {
            if (isArray(task) && baseIndexOf(task, taskName, 0) >= 0) {
              result.push(key);
            }
          });
          return result;
        }
      };
      /**
       * A specialized version of `_.map` for arrays without support for iteratee
       * shorthands.
       *
       * @private
       * @param {Array} [array] The array to iterate over.
       * @param {Function} iteratee The function invoked per iteration.
       * @returns {Array} Returns the new mapped array.
       */
      function arrayMap(array, iteratee) {
        var index = -1, length = array == null ? 0 : array.length, result = Array(length);
        while (++index < length) {
          result[index] = iteratee(array[index], index, array);
        }
        return result;
      }
      /** `Object#toString` result references. */
      var symbolTag = '[object Symbol]';
      /**
       * Checks if `value` is classified as a `Symbol` primitive or object.
       *
       * @static
       * @memberOf _
       * @since 4.0.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
       * @example
       *
       * _.isSymbol(Symbol.iterator);
       * // => true
       *
       * _.isSymbol('abc');
       * // => false
       */
      function isSymbol(value) {
        return typeof value == 'symbol' || isObjectLike(value) && baseGetTag(value) == symbolTag;
      }
      /** Used as references for various `Number` constants. */
      var INFINITY = 1 / 0;
      /** Used to convert symbols to primitives and strings. */
      var symbolProto = Symbol$1 ? Symbol$1.prototype : undefined;
      var symbolToString = symbolProto ? symbolProto.toString : undefined;
      /**
       * The base implementation of `_.toString` which doesn't convert nullish
       * values to empty strings.
       *
       * @private
       * @param {*} value The value to process.
       * @returns {string} Returns the string.
       */
      function baseToString(value) {
        // Exit early for strings to avoid a performance hit in some environments.
        if (typeof value == 'string') {
          return value;
        }
        if (isArray(value)) {
          // Recursively convert values (susceptible to call stack limits).
          return arrayMap(value, baseToString) + '';
        }
        if (isSymbol(value)) {
          return symbolToString ? symbolToString.call(value) : '';
        }
        var result = value + '';
        return result == '0' && 1 / value == -INFINITY ? '-0' : result;
      }
      /**
       * The base implementation of `_.slice` without an iteratee call guard.
       *
       * @private
       * @param {Array} array The array to slice.
       * @param {number} [start=0] The start position.
       * @param {number} [end=array.length] The end position.
       * @returns {Array} Returns the slice of `array`.
       */
      function baseSlice(array, start, end) {
        var index = -1, length = array.length;
        if (start < 0) {
          start = -start > length ? 0 : length + start;
        }
        end = end > length ? length : end;
        if (end < 0) {
          end += length;
        }
        length = start > end ? 0 : end - start >>> 0;
        start >>>= 0;
        var result = Array(length);
        while (++index < length) {
          result[index] = array[index + start];
        }
        return result;
      }
      /**
       * Casts `array` to a slice if it's needed.
       *
       * @private
       * @param {Array} array The array to inspect.
       * @param {number} start The start position.
       * @param {number} [end=array.length] The end position.
       * @returns {Array} Returns the cast slice.
       */
      function castSlice(array, start, end) {
        var length = array.length;
        end = end === undefined ? length : end;
        return !start && end >= length ? array : baseSlice(array, start, end);
      }
      /**
       * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol
       * that is not found in the character symbols.
       *
       * @private
       * @param {Array} strSymbols The string symbols to inspect.
       * @param {Array} chrSymbols The character symbols to find.
       * @returns {number} Returns the index of the last unmatched string symbol.
       */
      function charsEndIndex(strSymbols, chrSymbols) {
        var index = strSymbols.length;
        while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {
        }
        return index;
      }
      /**
       * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol
       * that is not found in the character symbols.
       *
       * @private
       * @param {Array} strSymbols The string symbols to inspect.
       * @param {Array} chrSymbols The character symbols to find.
       * @returns {number} Returns the index of the first unmatched string symbol.
       */
      function charsStartIndex(strSymbols, chrSymbols) {
        var index = -1, length = strSymbols.length;
        while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {
        }
        return index;
      }
      /**
       * Converts an ASCII `string` to an array.
       *
       * @private
       * @param {string} string The string to convert.
       * @returns {Array} Returns the converted array.
       */
      function asciiToArray(string) {
        return string.split('');
      }
      /** Used to compose unicode character classes. */
      var rsAstralRange = '\\ud800-\\udfff';
      var rsComboMarksRange = '\\u0300-\\u036f\\ufe20-\\ufe23';
      var rsComboSymbolsRange = '\\u20d0-\\u20f0';
      var rsVarRange = '\\ufe0e\\ufe0f';
      /** Used to compose unicode capture groups. */
      var rsZWJ = '\\u200d';
      /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */
      var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboMarksRange + rsComboSymbolsRange + rsVarRange + ']');
      /**
       * Checks if `string` contains Unicode symbols.
       *
       * @private
       * @param {string} string The string to inspect.
       * @returns {boolean} Returns `true` if a symbol is found, else `false`.
       */
      function hasUnicode(string) {
        return reHasUnicode.test(string);
      }
      /** Used to compose unicode character classes. */
      var rsAstralRange$1 = '\\ud800-\\udfff';
      var rsComboMarksRange$1 = '\\u0300-\\u036f\\ufe20-\\ufe23';
      var rsComboSymbolsRange$1 = '\\u20d0-\\u20f0';
      var rsVarRange$1 = '\\ufe0e\\ufe0f';
      /** Used to compose unicode capture groups. */
      var rsAstral = '[' + rsAstralRange$1 + ']';
      var rsCombo = '[' + rsComboMarksRange$1 + rsComboSymbolsRange$1 + ']';
      var rsFitz = '\\ud83c[\\udffb-\\udfff]';
      var rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')';
      var rsNonAstral = '[^' + rsAstralRange$1 + ']';
      var rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}';
      var rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]';
      var rsZWJ$1 = '\\u200d';
      /** Used to compose unicode regexes. */
      var reOptMod = rsModifier + '?';
      var rsOptVar = '[' + rsVarRange$1 + ']?';
      var rsOptJoin = '(?:' + rsZWJ$1 + '(?:' + [
        rsNonAstral,
        rsRegional,
        rsSurrPair
      ].join('|') + ')' + rsOptVar + reOptMod + ')*';
      var rsSeq = rsOptVar + reOptMod + rsOptJoin;
      var rsSymbol = '(?:' + [
        rsNonAstral + rsCombo + '?',
        rsCombo,
        rsRegional,
        rsSurrPair,
        rsAstral
      ].join('|') + ')';
      /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */
      var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');
      /**
       * Converts a Unicode `string` to an array.
       *
       * @private
       * @param {string} string The string to convert.
       * @returns {Array} Returns the converted array.
       */
      function unicodeToArray(string) {
        return string.match(reUnicode) || [];
      }
      /**
       * Converts `string` to an array.
       *
       * @private
       * @param {string} string The string to convert.
       * @returns {Array} Returns the converted array.
       */
      function stringToArray(string) {
        return hasUnicode(string) ? unicodeToArray(string) : asciiToArray(string);
      }
      /**
       * Converts `value` to a string. An empty string is returned for `null`
       * and `undefined` values. The sign of `-0` is preserved.
       *
       * @static
       * @memberOf _
       * @since 4.0.0
       * @category Lang
       * @param {*} value The value to convert.
       * @returns {string} Returns the converted string.
       * @example
       *
       * _.toString(null);
       * // => ''
       *
       * _.toString(-0);
       * // => '-0'
       *
       * _.toString([1, 2, 3]);
       * // => '1,2,3'
       */
      function toString(value) {
        return value == null ? '' : baseToString(value);
      }
      /** Used to match leading and trailing whitespace. */
      var reTrim = /^\s+|\s+$/g;
      /**
       * Removes leading and trailing whitespace or specified characters from `string`.
       *
       * @static
       * @memberOf _
       * @since 3.0.0
       * @category String
       * @param {string} [string=''] The string to trim.
       * @param {string} [chars=whitespace] The characters to trim.
       * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
       * @returns {string} Returns the trimmed string.
       * @example
       *
       * _.trim('  abc  ');
       * // => 'abc'
       *
       * _.trim('-_-abc-_-', '_-');
       * // => 'abc'
       *
       * _.map(['  foo  ', '  bar  '], _.trim);
       * // => ['foo', 'bar']
       */
      function trim(string, chars, guard) {
        string = toString(string);
        if (string && (guard || chars === undefined)) {
          return string.replace(reTrim, '');
        }
        if (!string || !(chars = baseToString(chars))) {
          return string;
        }
        var strSymbols = stringToArray(string), chrSymbols = stringToArray(chars), start = charsStartIndex(strSymbols, chrSymbols), end = charsEndIndex(strSymbols, chrSymbols) + 1;
        return castSlice(strSymbols, start, end).join('');
      }
      var FN_ARGS = /^(function)?\s*[^\(]*\(\s*([^\)]*)\)/m;
      var FN_ARG_SPLIT = /,/;
      var FN_ARG = /(=.+)?(\s*)$/;
      var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm;
      function parseParams(func) {
        func = func.toString().replace(STRIP_COMMENTS, '');
        func = func.match(FN_ARGS)[2].replace(' ', '');
        func = func ? func.split(FN_ARG_SPLIT) : [];
        func = func.map(function (arg) {
          return trim(arg.replace(FN_ARG, ''));
        });
        return func;
      }
      /**
       * A dependency-injected version of the [async.auto]{@link module:ControlFlow.auto} function. Dependent
       * tasks are specified as parameters to the function, after the usual callback
       * parameter, with the parameter names matching the names of the tasks it
       * depends on. This can provide even more readable task graphs which can be
       * easier to maintain.
       *
       * If a final callback is specified, the task results are similarly injected,
       * specified as named parameters after the initial error parameter.
       *
       * The autoInject function is purely syntactic sugar and its semantics are
       * otherwise equivalent to [async.auto]{@link module:ControlFlow.auto}.
       *
       * @name autoInject
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @see [async.auto]{@link module:ControlFlow.auto}
       * @category Control Flow
       * @param {Object} tasks - An object, each of whose properties is a function of
       * the form 'func([dependencies...], callback). The object's key of a property
       * serves as the name of the task defined by that property, i.e. can be used
       * when specifying requirements for other tasks.
       * * The `callback` parameter is a `callback(err, result)` which must be called
       *   when finished, passing an `error` (which can be `null`) and the result of
       *   the function's execution. The remaining parameters name other tasks on
       *   which the task is dependent, and the results from those tasks are the
       *   arguments of those parameters.
       * @param {Function} [callback] - An optional callback which is called when all
       * the tasks have been completed. It receives the `err` argument if any `tasks`
       * pass an error to their callback, and a `results` object with any completed
       * task results, similar to `auto`.
       * @example
       *
       * //  The example from `auto` can be rewritten as follows:
       * async.autoInject({
       *     get_data: function(callback) {
       *         // async code to get some data
       *         callback(null, 'data', 'converted to array');
       *     },
       *     make_folder: function(callback) {
       *         // async code to create a directory to store a file in
       *         // this is run at the same time as getting the data
       *         callback(null, 'folder');
       *     },
       *     write_file: function(get_data, make_folder, callback) {
       *         // once there is some data and the directory exists,
       *         // write the data to a file in the directory
       *         callback(null, 'filename');
       *     },
       *     email_link: function(write_file, callback) {
       *         // once the file is written let's email a link to it...
       *         // write_file contains the filename returned by write_file.
       *         callback(null, {'file':write_file, 'email':'user@example.com'});
       *     }
       * }, function(err, results) {
       *     console.log('err = ', err);
       *     console.log('email_link = ', results.email_link);
       * });
       *
       * // If you are using a JS minifier that mangles parameter names, `autoInject`
       * // will not work with plain functions, since the parameter names will be
       * // collapsed to a single letter identifier.  To work around this, you can
       * // explicitly specify the names of the parameters your task function needs
       * // in an array, similar to Angular.js dependency injection.
       *
       * // This still has an advantage over plain `auto`, since the results a task
       * // depends on are still spread into arguments.
       * async.autoInject({
       *     //...
       *     write_file: ['get_data', 'make_folder', function(get_data, make_folder, callback) {
       *         callback(null, 'filename');
       *     }],
       *     email_link: ['write_file', function(write_file, callback) {
       *         callback(null, {'file':write_file, 'email':'user@example.com'});
       *     }]
       *     //...
       * }, function(err, results) {
       *     console.log('err = ', err);
       *     console.log('email_link = ', results.email_link);
       * });
       */
      function autoInject(tasks, callback) {
        var newTasks = {};
        baseForOwn(tasks, function (taskFn, key) {
          var params;
          if (isArray(taskFn)) {
            params = taskFn.slice(0, -1);
            taskFn = taskFn[taskFn.length - 1];
            newTasks[key] = params.concat(params.length > 0 ? newTask : taskFn);
          } else if (taskFn.length === 1) {
            // no dependencies, use the function as-is
            newTasks[key] = taskFn;
          } else {
            params = parseParams(taskFn);
            if (taskFn.length === 0 && params.length === 0) {
              throw new Error('autoInject task functions require explicit parameters.');
            }
            params.pop();
            newTasks[key] = params.concat(newTask);
          }
          function newTask(results, taskCb) {
            var newArgs = arrayMap(params, function (name) {
              return results[name];
            });
            newArgs.push(taskCb);
            taskFn.apply(null, newArgs);
          }
        });
        auto(newTasks, callback);
      }
      var hasSetImmediate = typeof setImmediate === 'function' && setImmediate;
      var hasNextTick = typeof process === 'object' && typeof process.nextTick === 'function';
      function fallback(fn) {
        setTimeout(fn, 0);
      }
      function wrap(defer) {
        return rest(function (fn, args) {
          defer(function () {
            fn.apply(null, args);
          });
        });
      }
      var _defer;
      if (hasSetImmediate) {
        _defer = setImmediate;
      } else if (hasNextTick) {
        _defer = process.nextTick;
      } else {
        _defer = fallback;
      }
      var setImmediate$1 = wrap(_defer);
      // Simple doubly linked list (https://en.wikipedia.org/wiki/Doubly_linked_list) implementation
      // used for queues. This implementation assumes that the node provided by the user can be modified
      // to adjust the next and last properties. We implement only the minimal functionality
      // for queue support.
      function DLL() {
        this.head = this.tail = null;
        this.length = 0;
      }
      function setInitial(dll, node) {
        dll.length = 1;
        dll.head = dll.tail = node;
      }
      DLL.prototype.removeLink = function (node) {
        if (node.prev)
          node.prev.next = node.next;
        else
          this.head = node.next;
        if (node.next)
          node.next.prev = node.prev;
        else
          this.tail = node.prev;
        node.prev = node.next = null;
        this.length -= 1;
        return node;
      };
      DLL.prototype.empty = DLL;
      DLL.prototype.insertAfter = function (node, newNode) {
        newNode.prev = node;
        newNode.next = node.next;
        if (node.next)
          node.next.prev = newNode;
        else
          this.tail = newNode;
        node.next = newNode;
        this.length += 1;
      };
      DLL.prototype.insertBefore = function (node, newNode) {
        newNode.prev = node.prev;
        newNode.next = node;
        if (node.prev)
          node.prev.next = newNode;
        else
          this.head = newNode;
        node.prev = newNode;
        this.length += 1;
      };
      DLL.prototype.unshift = function (node) {
        if (this.head)
          this.insertBefore(this.head, node);
        else
          setInitial(this, node);
      };
      DLL.prototype.push = function (node) {
        if (this.tail)
          this.insertAfter(this.tail, node);
        else
          setInitial(this, node);
      };
      DLL.prototype.shift = function () {
        return this.head && this.removeLink(this.head);
      };
      DLL.prototype.pop = function () {
        return this.tail && this.removeLink(this.tail);
      };
      function queue(worker, concurrency, payload) {
        if (concurrency == null) {
          concurrency = 1;
        } else if (concurrency === 0) {
          throw new Error('Concurrency must not be zero');
        }
        function _insert(data, insertAtFront, callback) {
          if (callback != null && typeof callback !== 'function') {
            throw new Error('task callback must be a function');
          }
          q.started = true;
          if (!isArray(data)) {
            data = [data];
          }
          if (data.length === 0 && q.idle()) {
            // call drain immediately if there are no tasks
            return setImmediate$1(function () {
              q.drain();
            });
          }
          for (var i = 0, l = data.length; i < l; i++) {
            var item = {
              data: data[i],
              callback: callback || noop
            };
            if (insertAtFront) {
              q._tasks.unshift(item);
            } else {
              q._tasks.push(item);
            }
          }
          setImmediate$1(q.process);
        }
        function _next(tasks) {
          return rest(function (args) {
            workers -= 1;
            for (var i = 0, l = tasks.length; i < l; i++) {
              var task = tasks[i];
              var index = baseIndexOf(workersList, task, 0);
              if (index >= 0) {
                workersList.splice(index);
              }
              task.callback.apply(task, args);
              if (args[0] != null) {
                q.error(args[0], task.data);
              }
            }
            if (workers <= q.concurrency - q.buffer) {
              q.unsaturated();
            }
            if (q.idle()) {
              q.drain();
            }
            q.process();
          });
        }
        var workers = 0;
        var workersList = [];
        var q = {
          _tasks: new DLL(),
          concurrency: concurrency,
          payload: payload,
          saturated: noop,
          unsaturated: noop,
          buffer: concurrency / 4,
          empty: noop,
          drain: noop,
          error: noop,
          started: false,
          paused: false,
          push: function (data, callback) {
            _insert(data, false, callback);
          },
          kill: function () {
            q.drain = noop;
            q._tasks.empty();
          },
          unshift: function (data, callback) {
            _insert(data, true, callback);
          },
          process: function () {
            while (!q.paused && workers < q.concurrency && q._tasks.length) {
              var tasks = [], data = [];
              var l = q._tasks.length;
              if (q.payload)
                l = Math.min(l, q.payload);
              for (var i = 0; i < l; i++) {
                var node = q._tasks.shift();
                tasks.push(node);
                data.push(node.data);
              }
              if (q._tasks.length === 0) {
                q.empty();
              }
              workers += 1;
              workersList.push(tasks[0]);
              if (workers === q.concurrency) {
                q.saturated();
              }
              var cb = onlyOnce(_next(tasks));
              worker(data, cb);
            }
          },
          length: function () {
            return q._tasks.length;
          },
          running: function () {
            return workers;
          },
          workersList: function () {
            return workersList;
          },
          idle: function () {
            return q._tasks.length + workers === 0;
          },
          pause: function () {
            q.paused = true;
          },
          resume: function () {
            if (q.paused === false) {
              return;
            }
            q.paused = false;
            var resumeCount = Math.min(q.concurrency, q._tasks.length);
            // Need to call q.process once per concurrent
            // worker to preserve full concurrency after pause
            for (var w = 1; w <= resumeCount; w++) {
              setImmediate$1(q.process);
            }
          }
        };
        return q;
      }
      /**
       * A cargo of tasks for the worker function to complete. Cargo inherits all of
       * the same methods and event callbacks as [`queue`]{@link module:ControlFlow.queue}.
       * @typedef {Object} CargoObject
       * @memberOf module:ControlFlow
       * @property {Function} length - A function returning the number of items
       * waiting to be processed. Invoke like `cargo.length()`.
       * @property {number} payload - An `integer` for determining how many tasks
       * should be process per round. This property can be changed after a `cargo` is
       * created to alter the payload on-the-fly.
       * @property {Function} push - Adds `task` to the `queue`. The callback is
       * called once the `worker` has finished processing the task. Instead of a
       * single task, an array of `tasks` can be submitted. The respective callback is
       * used for every task in the list. Invoke like `cargo.push(task, [callback])`.
       * @property {Function} saturated - A callback that is called when the
       * `queue.length()` hits the concurrency and further tasks will be queued.
       * @property {Function} empty - A callback that is called when the last item
       * from the `queue` is given to a `worker`.
       * @property {Function} drain - A callback that is called when the last item
       * from the `queue` has returned from the `worker`.
       * @property {Function} idle - a function returning false if there are items
       * waiting or being processed, or true if not. Invoke like `cargo.idle()`.
       * @property {Function} pause - a function that pauses the processing of tasks
       * until `resume()` is called. Invoke like `cargo.pause()`.
       * @property {Function} resume - a function that resumes the processing of
       * queued tasks when the queue is paused. Invoke like `cargo.resume()`.
       * @property {Function} kill - a function that removes the `drain` callback and
       * empties remaining tasks from the queue forcing it to go idle. Invoke like `cargo.kill()`.
       */
      /**
       * Creates a `cargo` object with the specified payload. Tasks added to the
       * cargo will be processed altogether (up to the `payload` limit). If the
       * `worker` is in progress, the task is queued until it becomes available. Once
       * the `worker` has completed some tasks, each callback of those tasks is
       * called. Check out [these](https://camo.githubusercontent.com/6bbd36f4cf5b35a0f11a96dcd2e97711ffc2fb37/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313637363837312f36383130382f62626330636662302d356632392d313165322d393734662d3333393763363464633835382e676966) [animations](https://camo.githubusercontent.com/f4810e00e1c5f5f8addbe3e9f49064fd5d102699/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313637363837312f36383130312f38346339323036362d356632392d313165322d383134662d3964336430323431336266642e676966)
       * for how `cargo` and `queue` work.
       *
       * While [`queue`]{@link module:ControlFlow.queue} passes only one task to one of a group of workers
       * at a time, cargo passes an array of tasks to a single worker, repeating
       * when the worker is finished.
       *
       * @name cargo
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @see [async.queue]{@link module:ControlFlow.queue}
       * @category Control Flow
       * @param {Function} worker - An asynchronous function for processing an array
       * of queued tasks, which must call its `callback(err)` argument when finished,
       * with an optional `err` argument. Invoked with `(tasks, callback)`.
       * @param {number} [payload=Infinity] - An optional `integer` for determining
       * how many tasks should be processed per round; if omitted, the default is
       * unlimited.
       * @returns {module:ControlFlow.CargoObject} A cargo object to manage the tasks. Callbacks can
       * attached as certain properties to listen for specific events during the
       * lifecycle of the cargo and inner queue.
       * @example
       *
       * // create a cargo object with payload 2
       * var cargo = async.cargo(function(tasks, callback) {
       *     for (var i=0; i<tasks.length; i++) {
       *         console.log('hello ' + tasks[i].name);
       *     }
       *     callback();
       * }, 2);
       *
       * // add some items
       * cargo.push({name: 'foo'}, function(err) {
       *     console.log('finished processing foo');
       * });
       * cargo.push({name: 'bar'}, function(err) {
       *     console.log('finished processing bar');
       * });
       * cargo.push({name: 'baz'}, function(err) {
       *     console.log('finished processing baz');
       * });
       */
      function cargo(worker, payload) {
        return queue(worker, 1, payload);
      }
      /**
       * The same as [`eachOf`]{@link module:Collections.eachOf} but runs only a single async operation at a time.
       *
       * @name eachOfSeries
       * @static
       * @memberOf module:Collections
       * @method
       * @see [async.eachOf]{@link module:Collections.eachOf}
       * @alias forEachOfSeries
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {Function} iteratee - A function to apply to each item in `coll`. The
       * `key` is the item's key, or index in the case of an array. The iteratee is
       * passed a `callback(err)` which must be called once it has completed. If no
       * error has occurred, the callback should be run without arguments or with an
       * explicit `null` argument. Invoked with (item, key, callback).
       * @param {Function} [callback] - A callback which is called when all `iteratee`
       * functions have finished, or an error occurs. Invoked with (err).
       */
      var eachOfSeries = doLimit(eachOfLimit, 1);
      /**
       * Reduces `coll` into a single value using an async `iteratee` to return each
       * successive step. `memo` is the initial state of the reduction. This function
       * only operates in series.
       *
       * For performance reasons, it may make sense to split a call to this function
       * into a parallel map, and then use the normal `Array.prototype.reduce` on the
       * results. This function is for situations where each step in the reduction
       * needs to be async; if you can get the data before reducing it, then it's
       * probably a good idea to do so.
       *
       * @name reduce
       * @static
       * @memberOf module:Collections
       * @method
       * @alias inject
       * @alias foldl
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {*} memo - The initial state of the reduction.
       * @param {Function} iteratee - A function applied to each item in the
       * array to produce the next step in the reduction. The `iteratee` is passed a
       * `callback(err, reduction)` which accepts an optional error as its first
       * argument, and the state of the reduction as the second. If an error is
       * passed to the callback, the reduction is stopped and the main `callback` is
       * immediately called with the error. Invoked with (memo, item, callback).
       * @param {Function} [callback] - A callback which is called after all the
       * `iteratee` functions have finished. Result is the reduced value. Invoked with
       * (err, result).
       * @example
       *
       * async.reduce([1,2,3], 0, function(memo, item, callback) {
       *     // pointless async:
       *     process.nextTick(function() {
       *         callback(null, memo + item)
       *     });
       * }, function(err, result) {
       *     // result is now equal to the last value of memo, which is 6
       * });
       */
      function reduce(coll, memo, iteratee, callback) {
        callback = once(callback || noop);
        eachOfSeries(coll, function (x, i, callback) {
          iteratee(memo, x, function (err, v) {
            memo = v;
            callback(err);
          });
        }, function (err) {
          callback(err, memo);
        });
      }
      /**
       * Version of the compose function that is more natural to read. Each function
       * consumes the return value of the previous function. It is the equivalent of
       * [compose]{@link module:ControlFlow.compose} with the arguments reversed.
       *
       * Each function is executed with the `this` binding of the composed function.
       *
       * @name seq
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @see [async.compose]{@link module:ControlFlow.compose}
       * @category Control Flow
       * @param {...Function} functions - the asynchronous functions to compose
       * @returns {Function} a function that composes the `functions` in order
       * @example
       *
       * // Requires lodash (or underscore), express3 and dresende's orm2.
       * // Part of an app, that fetches cats of the logged user.
       * // This example uses `seq` function to avoid overnesting and error
       * // handling clutter.
       * app.get('/cats', function(request, response) {
       *     var User = request.models.User;
       *     async.seq(
       *         _.bind(User.get, User),  // 'User.get' has signature (id, callback(err, data))
       *         function(user, fn) {
       *             user.getCats(fn);      // 'getCats' has signature (callback(err, data))
       *         }
       *     )(req.session.user_id, function (err, cats) {
       *         if (err) {
       *             console.error(err);
       *             response.json({ status: 'error', message: err.message });
       *         } else {
       *             response.json({ status: 'ok', message: 'Cats found', data: cats });
       *         }
       *     });
       * });
       */
      var seq$1 = rest(function seq(functions) {
        return rest(function (args) {
          var that = this;
          var cb = args[args.length - 1];
          if (typeof cb == 'function') {
            args.pop();
          } else {
            cb = noop;
          }
          reduce(functions, args, function (newargs, fn, cb) {
            fn.apply(that, newargs.concat(rest(function (err, nextargs) {
              cb(err, nextargs);
            })));
          }, function (err, results) {
            cb.apply(that, [err].concat(results));
          });
        });
      });
      /**
       * Creates a function which is a composition of the passed asynchronous
       * functions. Each function consumes the return value of the function that
       * follows. Composing functions `f()`, `g()`, and `h()` would produce the result
       * of `f(g(h()))`, only this version uses callbacks to obtain the return values.
       *
       * Each function is executed with the `this` binding of the composed function.
       *
       * @name compose
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @category Control Flow
       * @param {...Function} functions - the asynchronous functions to compose
       * @returns {Function} an asynchronous function that is the composed
       * asynchronous `functions`
       * @example
       *
       * function add1(n, callback) {
       *     setTimeout(function () {
       *         callback(null, n + 1);
       *     }, 10);
       * }
       *
       * function mul3(n, callback) {
       *     setTimeout(function () {
       *         callback(null, n * 3);
       *     }, 10);
       * }
       *
       * var add1mul3 = async.compose(mul3, add1);
       * add1mul3(4, function (err, result) {
       *     // result now equals 15
       * });
       */
      var compose = rest(function (args) {
        return seq$1.apply(null, args.reverse());
      });
      function concat$1(eachfn, arr, fn, callback) {
        var result = [];
        eachfn(arr, function (x, index, cb) {
          fn(x, function (err, y) {
            result = result.concat(y || []);
            cb(err);
          });
        }, function (err) {
          callback(err, result);
        });
      }
      /**
       * Applies `iteratee` to each item in `coll`, concatenating the results. Returns
       * the concatenated list. The `iteratee`s are called in parallel, and the
       * results are concatenated as they return. There is no guarantee that the
       * results array will be returned in the original order of `coll` passed to the
       * `iteratee` function.
       *
       * @name concat
       * @static
       * @memberOf module:Collections
       * @method
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {Function} iteratee - A function to apply to each item in `coll`.
       * The iteratee is passed a `callback(err, results)` which must be called once
       * it has completed with an error (which can be `null`) and an array of results.
       * Invoked with (item, callback).
       * @param {Function} [callback(err)] - A callback which is called after all the
       * `iteratee` functions have finished, or an error occurs. Results is an array
       * containing the concatenated results of the `iteratee` function. Invoked with
       * (err, results).
       * @example
       *
       * async.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files) {
       *     // files is now a list of filenames that exist in the 3 directories
       * });
       */
      var concat = doParallel(concat$1);
      function doSeries(fn) {
        return function (obj, iteratee, callback) {
          return fn(eachOfSeries, obj, iteratee, callback);
        };
      }
      /**
       * The same as [`concat`]{@link module:Collections.concat} but runs only a single async operation at a time.
       *
       * @name concatSeries
       * @static
       * @memberOf module:Collections
       * @method
       * @see [async.concat]{@link module:Collections.concat}
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {Function} iteratee - A function to apply to each item in `coll`.
       * The iteratee is passed a `callback(err, results)` which must be called once
       * it has completed with an error (which can be `null`) and an array of results.
       * Invoked with (item, callback).
       * @param {Function} [callback(err)] - A callback which is called after all the
       * `iteratee` functions have finished, or an error occurs. Results is an array
       * containing the concatenated results of the `iteratee` function. Invoked with
       * (err, results).
       */
      var concatSeries = doSeries(concat$1);
      /**
       * Returns a function that when called, calls-back with the values provided.
       * Useful as the first function in a [`waterfall`]{@link module:ControlFlow.waterfall}, or for plugging values in to
       * [`auto`]{@link module:ControlFlow.auto}.
       *
       * @name constant
       * @static
       * @memberOf module:Utils
       * @method
       * @category Util
       * @param {...*} arguments... - Any number of arguments to automatically invoke
       * callback with.
       * @returns {Function} Returns a function that when invoked, automatically
       * invokes the callback with the previous given arguments.
       * @example
       *
       * async.waterfall([
       *     async.constant(42),
       *     function (value, next) {
       *         // value === 42
       *     },
       *     //...
       * ], callback);
       *
       * async.waterfall([
       *     async.constant(filename, "utf8"),
       *     fs.readFile,
       *     function (fileData, next) {
       *         //...
       *     }
       *     //...
       * ], callback);
       *
       * async.auto({
       *     hostname: async.constant("https://server.net/"),
       *     port: findFreePort,
       *     launchServer: ["hostname", "port", function (options, cb) {
       *         startServer(options, cb);
       *     }],
       *     //...
       * }, callback);
       */
      var constant = rest(function (values) {
        var args = [null].concat(values);
        return initialParams(function (ignoredArgs, callback) {
          return callback.apply(this, args);
        });
      });
      function _createTester(eachfn, check, getResult) {
        return function (arr, limit, iteratee, cb) {
          function done() {
            if (cb) {
              cb(null, getResult(false));
            }
          }
          function wrappedIteratee(x, _, callback) {
            if (!cb)
              return callback();
            iteratee(x, function (err, v) {
              // Check cb as another iteratee may have resolved with a
              // value or error since we started this iteratee
              if (cb && (err || check(v))) {
                if (err)
                  cb(err);
                else
                  cb(err, getResult(true, x));
                cb = iteratee = false;
                callback(err, breakLoop);
              } else {
                callback();
              }
            });
          }
          if (arguments.length > 3) {
            cb = cb || noop;
            eachfn(arr, limit, wrappedIteratee, done);
          } else {
            cb = iteratee;
            cb = cb || noop;
            iteratee = limit;
            eachfn(arr, wrappedIteratee, done);
          }
        };
      }
      function _findGetResult(v, x) {
        return x;
      }
      /**
           * Returns the first value in `coll` that passes an async truth test. The
           * `iteratee` is applied in parallel, meaning the first iteratee to return
           * `true` will fire the detect `callback` with that result. That means the
           * result might not be the first item in the original `coll` (in terms of order)
           * that passes the test.

          * If order within the original `coll` is important, then look at
          * [`detectSeries`]{@link module:Collections.detectSeries}.
          *
          * @name detect
          * @static
          * @memberOf module:Collections
          * @method
          * @alias find
          * @category Collections
          * @param {Array|Iterable|Object} coll - A collection to iterate over.
          * @param {Function} iteratee - A truth test to apply to each item in `coll`.
          * The iteratee is passed a `callback(err, truthValue)` which must be called
          * with a boolean argument once it has completed. Invoked with (item, callback).
          * @param {Function} [callback] - A callback which is called as soon as any
          * iteratee returns `true`, or after all the `iteratee` functions have finished.
          * Result will be the first item in the array that passes the truth test
          * (iteratee) or the value `undefined` if none passed. Invoked with
          * (err, result).
          * @example
          *
          * async.detect(['file1','file2','file3'], function(filePath, callback) {
          *     fs.access(filePath, function(err) {
          *         callback(null, !err)
          *     });
          * }, function(err, result) {
          *     // result now equals the first file in the list that exists
          * });
          */
      var detect = _createTester(eachOf, identity, _findGetResult);
      /**
       * The same as [`detect`]{@link module:Collections.detect} but runs a maximum of `limit` async operations at a
       * time.
       *
       * @name detectLimit
       * @static
       * @memberOf module:Collections
       * @method
       * @see [async.detect]{@link module:Collections.detect}
       * @alias findLimit
       * @category Collections
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {number} limit - The maximum number of async operations at a time.
       * @param {Function} iteratee - A truth test to apply to each item in `coll`.
       * The iteratee is passed a `callback(err, truthValue)` which must be called
       * with a boolean argument once it has completed. Invoked with (item, callback).
       * @param {Function} [callback] - A callback which is called as soon as any
       * iteratee returns `true`, or after all the `iteratee` functions have finished.
       * Result will be the first item in the array that passes the truth test
       * (iteratee) or the value `undefined` if none passed. Invoked with
       * (err, result).
       */
      var detectLimit = _createTester(eachOfLimit, identity, _findGetResult);
      /**
       * The same as [`detect`]{@link module:Collections.detect} but runs only a single async operation at a time.
       *
       * @name detectSeries
       * @static
       * @memberOf module:Collections
       * @method
       * @see [async.detect]{@link module:Collections.detect}
       * @alias findSeries
       * @category Collections
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {Function} iteratee - A truth test to apply to each item in `coll`.
       * The iteratee is passed a `callback(err, truthValue)` which must be called
       * with a boolean argument once it has completed. Invoked with (item, callback).
       * @param {Function} [callback] - A callback which is called as soon as any
       * iteratee returns `true`, or after all the `iteratee` functions have finished.
       * Result will be the first item in the array that passes the truth test
       * (iteratee) or the value `undefined` if none passed. Invoked with
       * (err, result).
       */
      var detectSeries = _createTester(eachOfSeries, identity, _findGetResult);
      function consoleFunc(name) {
        return rest(function (fn, args) {
          fn.apply(null, args.concat(rest(function (err, args) {
            if (typeof console === 'object') {
              if (err) {
                if (console.error) {
                  console.error(err);
                }
              } else if (console[name]) {
                arrayEach(args, function (x) {
                  console[name](x);
                });
              }
            }
          })));
        });
      }
      /**
       * Logs the result of an `async` function to the `console` using `console.dir`
       * to display the properties of the resulting object. Only works in Node.js or
       * in browsers that support `console.dir` and `console.error` (such as FF and
       * Chrome). If multiple arguments are returned from the async function,
       * `console.dir` is called on each argument in order.
       *
       * @name dir
       * @static
       * @memberOf module:Utils
       * @method
       * @category Util
       * @param {Function} function - The function you want to eventually apply all
       * arguments to.
       * @param {...*} arguments... - Any number of arguments to apply to the function.
       * @example
       *
       * // in a module
       * var hello = function(name, callback) {
       *     setTimeout(function() {
       *         callback(null, {hello: name});
       *     }, 1000);
       * };
       *
       * // in the node repl
       * node> async.dir(hello, 'world');
       * {hello: 'world'}
       */
      var dir = consoleFunc('dir');
      /**
       * The post-check version of [`during`]{@link module:ControlFlow.during}. To reflect the difference in
       * the order of operations, the arguments `test` and `fn` are switched.
       *
       * Also a version of [`doWhilst`]{@link module:ControlFlow.doWhilst} with asynchronous `test` function.
       * @name doDuring
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @see [async.during]{@link module:ControlFlow.during}
       * @category Control Flow
       * @param {Function} fn - A function which is called each time `test` passes.
       * The function is passed a `callback(err)`, which must be called once it has
       * completed with an optional `err` argument. Invoked with (callback).
       * @param {Function} test - asynchronous truth test to perform before each
       * execution of `fn`. Invoked with (...args, callback), where `...args` are the
       * non-error args from the previous callback of `fn`.
       * @param {Function} [callback] - A callback which is called after the test
       * function has failed and repeated execution of `fn` has stopped. `callback`
       * will be passed an error if one occured, otherwise `null`.
       */
      function doDuring(fn, test, callback) {
        callback = onlyOnce(callback || noop);
        var next = rest(function (err, args) {
          if (err)
            return callback(err);
          args.push(check);
          test.apply(this, args);
        });
        function check(err, truth) {
          if (err)
            return callback(err);
          if (!truth)
            return callback(null);
          fn(next);
        }
        check(null, true);
      }
      /**
       * The post-check version of [`whilst`]{@link module:ControlFlow.whilst}. To reflect the difference in
       * the order of operations, the arguments `test` and `iteratee` are switched.
       *
       * `doWhilst` is to `whilst` as `do while` is to `while` in plain JavaScript.
       *
       * @name doWhilst
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @see [async.whilst]{@link module:ControlFlow.whilst}
       * @category Control Flow
       * @param {Function} iteratee - A function which is called each time `test`
       * passes. The function is passed a `callback(err)`, which must be called once
       * it has completed with an optional `err` argument. Invoked with (callback).
       * @param {Function} test - synchronous truth test to perform after each
       * execution of `iteratee`. Invoked with the non-error callback results of
       * `iteratee`.
       * @param {Function} [callback] - A callback which is called after the test
       * function has failed and repeated execution of `iteratee` has stopped.
       * `callback` will be passed an error and any arguments passed to the final
       * `iteratee`'s callback. Invoked with (err, [results]);
       */
      function doWhilst(iteratee, test, callback) {
        callback = onlyOnce(callback || noop);
        var next = rest(function (err, args) {
          if (err)
            return callback(err);
          if (test.apply(this, args))
            return iteratee(next);
          callback.apply(null, [null].concat(args));
        });
        iteratee(next);
      }
      /**
       * Like ['doWhilst']{@link module:ControlFlow.doWhilst}, except the `test` is inverted. Note the
       * argument ordering differs from `until`.
       *
       * @name doUntil
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @see [async.doWhilst]{@link module:ControlFlow.doWhilst}
       * @category Control Flow
       * @param {Function} fn - A function which is called each time `test` fails.
       * The function is passed a `callback(err)`, which must be called once it has
       * completed with an optional `err` argument. Invoked with (callback).
       * @param {Function} test - synchronous truth test to perform after each
       * execution of `fn`. Invoked with the non-error callback results of `fn`.
       * @param {Function} [callback] - A callback which is called after the test
       * function has passed and repeated execution of `fn` has stopped. `callback`
       * will be passed an error and any arguments passed to the final `fn`'s
       * callback. Invoked with (err, [results]);
       */
      function doUntil(fn, test, callback) {
        doWhilst(fn, function () {
          return !test.apply(this, arguments);
        }, callback);
      }
      /**
       * Like [`whilst`]{@link module:ControlFlow.whilst}, except the `test` is an asynchronous function that
       * is passed a callback in the form of `function (err, truth)`. If error is
       * passed to `test` or `fn`, the main callback is immediately called with the
       * value of the error.
       *
       * @name during
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @see [async.whilst]{@link module:ControlFlow.whilst}
       * @category Control Flow
       * @param {Function} test - asynchronous truth test to perform before each
       * execution of `fn`. Invoked with (callback).
       * @param {Function} fn - A function which is called each time `test` passes.
       * The function is passed a `callback(err)`, which must be called once it has
       * completed with an optional `err` argument. Invoked with (callback).
       * @param {Function} [callback] - A callback which is called after the test
       * function has failed and repeated execution of `fn` has stopped. `callback`
       * will be passed an error, if one occured, otherwise `null`.
       * @example
       *
       * var count = 0;
       *
       * async.during(
       *     function (callback) {
       *         return callback(null, count < 5);
       *     },
       *     function (callback) {
       *         count++;
       *         setTimeout(callback, 1000);
       *     },
       *     function (err) {
       *         // 5 seconds have passed
       *     }
       * );
       */
      function during(test, fn, callback) {
        callback = onlyOnce(callback || noop);
        function next(err) {
          if (err)
            return callback(err);
          test(check);
        }
        function check(err, truth) {
          if (err)
            return callback(err);
          if (!truth)
            return callback(null);
          fn(next);
        }
        test(check);
      }
      function _withoutIndex(iteratee) {
        return function (value, index, callback) {
          return iteratee(value, callback);
        };
      }
      /**
       * Applies the function `iteratee` to each item in `coll`, in parallel.
       * The `iteratee` is called with an item from the list, and a callback for when
       * it has finished. If the `iteratee` passes an error to its `callback`, the
       * main `callback` (for the `each` function) is immediately called with the
       * error.
       *
       * Note, that since this function applies `iteratee` to each item in parallel,
       * there is no guarantee that the iteratee functions will complete in order.
       *
       * @name each
       * @static
       * @memberOf module:Collections
       * @method
       * @alias forEach
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {Function} iteratee - A function to apply to each item
       * in `coll`. The iteratee is passed a `callback(err)` which must be called once
       * it has completed. If no error has occurred, the `callback` should be run
       * without arguments or with an explicit `null` argument. The array index is not
       * passed to the iteratee. Invoked with (item, callback). If you need the index,
       * use `eachOf`.
       * @param {Function} [callback] - A callback which is called when all
       * `iteratee` functions have finished, or an error occurs. Invoked with (err).
       * @example
       *
       * // assuming openFiles is an array of file names and saveFile is a function
       * // to save the modified contents of that file:
       *
       * async.each(openFiles, saveFile, function(err){
       *   // if any of the saves produced an error, err would equal that error
       * });
       *
       * // assuming openFiles is an array of file names
       * async.each(openFiles, function(file, callback) {
       *
       *     // Perform operation on file here.
       *     console.log('Processing file ' + file);
       *
       *     if( file.length > 32 ) {
       *       console.log('This file name is too long');
       *       callback('File name too long');
       *     } else {
       *       // Do work to process file here
       *       console.log('File processed');
       *       callback();
       *     }
       * }, function(err) {
       *     // if any of the file processing produced an error, err would equal that error
       *     if( err ) {
       *       // One of the iterations produced an error.
       *       // All processing will now stop.
       *       console.log('A file failed to process');
       *     } else {
       *       console.log('All files have been processed successfully');
       *     }
       * });
       */
      function eachLimit(coll, iteratee, callback) {
        eachOf(coll, _withoutIndex(iteratee), callback);
      }
      /**
       * The same as [`each`]{@link module:Collections.each} but runs a maximum of `limit` async operations at a time.
       *
       * @name eachLimit
       * @static
       * @memberOf module:Collections
       * @method
       * @see [async.each]{@link module:Collections.each}
       * @alias forEachLimit
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {number} limit - The maximum number of async operations at a time.
       * @param {Function} iteratee - A function to apply to each item in `coll`. The
       * iteratee is passed a `callback(err)` which must be called once it has
       * completed. If no error has occurred, the `callback` should be run without
       * arguments or with an explicit `null` argument. The array index is not passed
       * to the iteratee. Invoked with (item, callback). If you need the index, use
       * `eachOfLimit`.
       * @param {Function} [callback] - A callback which is called when all
       * `iteratee` functions have finished, or an error occurs. Invoked with (err).
       */
      function eachLimit$1(coll, limit, iteratee, callback) {
        _eachOfLimit(limit)(coll, _withoutIndex(iteratee), callback);
      }
      /**
       * The same as [`each`]{@link module:Collections.each} but runs only a single async operation at a time.
       *
       * @name eachSeries
       * @static
       * @memberOf module:Collections
       * @method
       * @see [async.each]{@link module:Collections.each}
       * @alias forEachSeries
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {Function} iteratee - A function to apply to each
       * item in `coll`. The iteratee is passed a `callback(err)` which must be called
       * once it has completed. If no error has occurred, the `callback` should be run
       * without arguments or with an explicit `null` argument. The array index is
       * not passed to the iteratee. Invoked with (item, callback). If you need the
       * index, use `eachOfSeries`.
       * @param {Function} [callback] - A callback which is called when all
       * `iteratee` functions have finished, or an error occurs. Invoked with (err).
       */
      var eachSeries = doLimit(eachLimit$1, 1);
      /**
       * Wrap an async function and ensure it calls its callback on a later tick of
       * the event loop.  If the function already calls its callback on a next tick,
       * no extra deferral is added. This is useful for preventing stack overflows
       * (`RangeError: Maximum call stack size exceeded`) and generally keeping
       * [Zalgo](http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony)
       * contained.
       *
       * @name ensureAsync
       * @static
       * @memberOf module:Utils
       * @method
       * @category Util
       * @param {Function} fn - an async function, one that expects a node-style
       * callback as its last argument.
       * @returns {Function} Returns a wrapped function with the exact same call
       * signature as the function passed in.
       * @example
       *
       * function sometimesAsync(arg, callback) {
       *     if (cache[arg]) {
       *         return callback(null, cache[arg]); // this would be synchronous!!
       *     } else {
       *         doSomeIO(arg, callback); // this IO would be asynchronous
       *     }
       * }
       *
       * // this has a risk of stack overflows if many results are cached in a row
       * async.mapSeries(args, sometimesAsync, done);
       *
       * // this will defer sometimesAsync's callback if necessary,
       * // preventing stack overflows
       * async.mapSeries(args, async.ensureAsync(sometimesAsync), done);
       */
      function ensureAsync(fn) {
        return initialParams(function (args, callback) {
          var sync = true;
          args.push(function () {
            var innerArgs = arguments;
            if (sync) {
              setImmediate$1(function () {
                callback.apply(null, innerArgs);
              });
            } else {
              callback.apply(null, innerArgs);
            }
          });
          fn.apply(this, args);
          sync = false;
        });
      }
      function notId(v) {
        return !v;
      }
      /**
       * Returns `true` if every element in `coll` satisfies an async test. If any
       * iteratee call returns `false`, the main `callback` is immediately called.
       *
       * @name every
       * @static
       * @memberOf module:Collections
       * @method
       * @alias all
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {Function} iteratee - A truth test to apply to each item in the
       * collection in parallel. The iteratee is passed a `callback(err, truthValue)`
       * which must be called with a  boolean argument once it has completed. Invoked
       * with (item, callback).
       * @param {Function} [callback] - A callback which is called after all the
       * `iteratee` functions have finished. Result will be either `true` or `false`
       * depending on the values of the async tests. Invoked with (err, result).
       * @example
       *
       * async.every(['file1','file2','file3'], function(filePath, callback) {
       *     fs.access(filePath, function(err) {
       *         callback(null, !err)
       *     });
       * }, function(err, result) {
       *     // if result is true then every file exists
       * });
       */
      var every = _createTester(eachOf, notId, notId);
      /**
       * The same as [`every`]{@link module:Collections.every} but runs a maximum of `limit` async operations at a time.
       *
       * @name everyLimit
       * @static
       * @memberOf module:Collections
       * @method
       * @see [async.every]{@link module:Collections.every}
       * @alias allLimit
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {number} limit - The maximum number of async operations at a time.
       * @param {Function} iteratee - A truth test to apply to each item in the
       * collection in parallel. The iteratee is passed a `callback(err, truthValue)`
       * which must be called with a  boolean argument once it has completed. Invoked
       * with (item, callback).
       * @param {Function} [callback] - A callback which is called after all the
       * `iteratee` functions have finished. Result will be either `true` or `false`
       * depending on the values of the async tests. Invoked with (err, result).
       */
      var everyLimit = _createTester(eachOfLimit, notId, notId);
      /**
       * The same as [`every`]{@link module:Collections.every} but runs only a single async operation at a time.
       *
       * @name everySeries
       * @static
       * @memberOf module:Collections
       * @method
       * @see [async.every]{@link module:Collections.every}
       * @alias allSeries
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {Function} iteratee - A truth test to apply to each item in the
       * collection in parallel. The iteratee is passed a `callback(err, truthValue)`
       * which must be called with a  boolean argument once it has completed. Invoked
       * with (item, callback).
       * @param {Function} [callback] - A callback which is called after all the
       * `iteratee` functions have finished. Result will be either `true` or `false`
       * depending on the values of the async tests. Invoked with (err, result).
       */
      var everySeries = doLimit(everyLimit, 1);
      /**
       * The base implementation of `_.property` without support for deep paths.
       *
       * @private
       * @param {string} key The key of the property to get.
       * @returns {Function} Returns the new accessor function.
       */
      function baseProperty(key) {
        return function (object) {
          return object == null ? undefined : object[key];
        };
      }
      function filterArray(eachfn, arr, iteratee, callback) {
        var truthValues = new Array(arr.length);
        eachfn(arr, function (x, index, callback) {
          iteratee(x, function (err, v) {
            truthValues[index] = !!v;
            callback(err);
          });
        }, function (err) {
          if (err)
            return callback(err);
          var results = [];
          for (var i = 0; i < arr.length; i++) {
            if (truthValues[i])
              results.push(arr[i]);
          }
          callback(null, results);
        });
      }
      function filterGeneric(eachfn, coll, iteratee, callback) {
        var results = [];
        eachfn(coll, function (x, index, callback) {
          iteratee(x, function (err, v) {
            if (err) {
              callback(err);
            } else {
              if (v) {
                results.push({
                  index: index,
                  value: x
                });
              }
              callback();
            }
          });
        }, function (err) {
          if (err) {
            callback(err);
          } else {
            callback(null, arrayMap(results.sort(function (a, b) {
              return a.index - b.index;
            }), baseProperty('value')));
          }
        });
      }
      function _filter(eachfn, coll, iteratee, callback) {
        var filter = isArrayLike(coll) ? filterArray : filterGeneric;
        filter(eachfn, coll, iteratee, callback || noop);
      }
      /**
       * Returns a new array of all the values in `coll` which pass an async truth
       * test. This operation is performed in parallel, but the results array will be
       * in the same order as the original.
       *
       * @name filter
       * @static
       * @memberOf module:Collections
       * @method
       * @alias select
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {Function} iteratee - A truth test to apply to each item in `coll`.
       * The `iteratee` is passed a `callback(err, truthValue)`, which must be called
       * with a boolean argument once it has completed. Invoked with (item, callback).
       * @param {Function} [callback] - A callback which is called after all the
       * `iteratee` functions have finished. Invoked with (err, results).
       * @example
       *
       * async.filter(['file1','file2','file3'], function(filePath, callback) {
       *     fs.access(filePath, function(err) {
       *         callback(null, !err)
       *     });
       * }, function(err, results) {
       *     // results now equals an array of the existing files
       * });
       */
      var filter = doParallel(_filter);
      /**
       * The same as [`filter`]{@link module:Collections.filter} but runs a maximum of `limit` async operations at a
       * time.
       *
       * @name filterLimit
       * @static
       * @memberOf module:Collections
       * @method
       * @see [async.filter]{@link module:Collections.filter}
       * @alias selectLimit
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {number} limit - The maximum number of async operations at a time.
       * @param {Function} iteratee - A truth test to apply to each item in `coll`.
       * The `iteratee` is passed a `callback(err, truthValue)`, which must be called
       * with a boolean argument once it has completed. Invoked with (item, callback).
       * @param {Function} [callback] - A callback which is called after all the
       * `iteratee` functions have finished. Invoked with (err, results).
       */
      var filterLimit = doParallelLimit(_filter);
      /**
       * The same as [`filter`]{@link module:Collections.filter} but runs only a single async operation at a time.
       *
       * @name filterSeries
       * @static
       * @memberOf module:Collections
       * @method
       * @see [async.filter]{@link module:Collections.filter}
       * @alias selectSeries
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {Function} iteratee - A truth test to apply to each item in `coll`.
       * The `iteratee` is passed a `callback(err, truthValue)`, which must be called
       * with a boolean argument once it has completed. Invoked with (item, callback).
       * @param {Function} [callback] - A callback which is called after all the
       * `iteratee` functions have finished. Invoked with (err, results)
       */
      var filterSeries = doLimit(filterLimit, 1);
      /**
           * Calls the asynchronous function `fn` with a callback parameter that allows it
           * to call itself again, in series, indefinitely.

          * If an error is passed to the
          * callback then `errback` is called with the error, and execution stops,
          * otherwise it will never be called.
          *
          * @name forever
          * @static
          * @memberOf module:ControlFlow
          * @method
          * @category Control Flow
          * @param {Function} fn - a function to call repeatedly. Invoked with (next).
          * @param {Function} [errback] - when `fn` passes an error to it's callback,
          * this function will be called, and execution stops. Invoked with (err).
          * @example
          *
          * async.forever(
          *     function(next) {
          *         // next is suitable for passing to things that need a callback(err [, whatever]);
          *         // it will result in this function being called again.
          *     },
          *     function(err) {
          *         // if next is called with a value in its first parameter, it will appear
          *         // in here as 'err', and execution will stop.
          *     }
          * );
          */
      function forever(fn, errback) {
        var done = onlyOnce(errback || noop);
        var task = ensureAsync(fn);
        function next(err) {
          if (err)
            return done(err);
          task(next);
        }
        next();
      }
      /**
       * Logs the result of an `async` function to the `console`. Only works in
       * Node.js or in browsers that support `console.log` and `console.error` (such
       * as FF and Chrome). If multiple arguments are returned from the async
       * function, `console.log` is called on each argument in order.
       *
       * @name log
       * @static
       * @memberOf module:Utils
       * @method
       * @category Util
       * @param {Function} function - The function you want to eventually apply all
       * arguments to.
       * @param {...*} arguments... - Any number of arguments to apply to the function.
       * @example
       *
       * // in a module
       * var hello = function(name, callback) {
       *     setTimeout(function() {
       *         callback(null, 'hello ' + name);
       *     }, 1000);
       * };
       *
       * // in the node repl
       * node> async.log(hello, 'world');
       * 'hello world'
       */
      var log = consoleFunc('log');
      /**
       * The same as [`mapValues`]{@link module:Collections.mapValues} but runs a maximum of `limit` async operations at a
       * time.
       *
       * @name mapValuesLimit
       * @static
       * @memberOf module:Collections
       * @method
       * @see [async.mapValues]{@link module:Collections.mapValues}
       * @category Collection
       * @param {Object} obj - A collection to iterate over.
       * @param {number} limit - The maximum number of async operations at a time.
       * @param {Function} iteratee - A function to apply to each value in `obj`.
       * The iteratee is passed a `callback(err, transformed)` which must be called
       * once it has completed with an error (which can be `null`) and a
       * transformed value. Invoked with (value, key, callback).
       * @param {Function} [callback] - A callback which is called when all `iteratee`
       * functions have finished, or an error occurs. `result` is a new object consisting
       * of each key from `obj`, with each transformed value on the right-hand side.
       * Invoked with (err, result).
       */
      function mapValuesLimit(obj, limit, iteratee, callback) {
        callback = once(callback || noop);
        var newObj = {};
        eachOfLimit(obj, limit, function (val, key, next) {
          iteratee(val, key, function (err, result) {
            if (err)
              return next(err);
            newObj[key] = result;
            next();
          });
        }, function (err) {
          callback(err, newObj);
        });
      }
      /**
       * A relative of [`map`]{@link module:Collections.map}, designed for use with objects.
       *
       * Produces a new Object by mapping each value of `obj` through the `iteratee`
       * function. The `iteratee` is called each `value` and `key` from `obj` and a
       * callback for when it has finished processing. Each of these callbacks takes
       * two arguments: an `error`, and the transformed item from `obj`. If `iteratee`
       * passes an error to its callback, the main `callback` (for the `mapValues`
       * function) is immediately called with the error.
       *
       * Note, the order of the keys in the result is not guaranteed.  The keys will
       * be roughly in the order they complete, (but this is very engine-specific)
       *
       * @name mapValues
       * @static
       * @memberOf module:Collections
       * @method
       * @category Collection
       * @param {Object} obj - A collection to iterate over.
       * @param {Function} iteratee - A function to apply to each value and key in
       * `coll`. The iteratee is passed a `callback(err, transformed)` which must be
       * called once it has completed with an error (which can be `null`) and a
       * transformed value. Invoked with (value, key, callback).
       * @param {Function} [callback] - A callback which is called when all `iteratee`
       * functions have finished, or an error occurs. `result` is a new object consisting
       * of each key from `obj`, with each transformed value on the right-hand side.
       * Invoked with (err, result).
       * @example
       *
       * async.mapValues({
       *     f1: 'file1',
       *     f2: 'file2',
       *     f3: 'file3'
       * }, function (file, key, callback) {
       *   fs.stat(file, callback);
       * }, function(err, result) {
       *     // result is now a map of stats for each file, e.g.
       *     // {
       *     //     f1: [stats for file1],
       *     //     f2: [stats for file2],
       *     //     f3: [stats for file3]
       *     // }
       * });
       */
      var mapValues = doLimit(mapValuesLimit, Infinity);
      /**
       * The same as [`mapValues`]{@link module:Collections.mapValues} but runs only a single async operation at a time.
       *
       * @name mapValuesSeries
       * @static
       * @memberOf module:Collections
       * @method
       * @see [async.mapValues]{@link module:Collections.mapValues}
       * @category Collection
       * @param {Object} obj - A collection to iterate over.
       * @param {Function} iteratee - A function to apply to each value in `obj`.
       * The iteratee is passed a `callback(err, transformed)` which must be called
       * once it has completed with an error (which can be `null`) and a
       * transformed value. Invoked with (value, key, callback).
       * @param {Function} [callback] - A callback which is called when all `iteratee`
       * functions have finished, or an error occurs. `result` is a new object consisting
       * of each key from `obj`, with each transformed value on the right-hand side.
       * Invoked with (err, result).
       */
      var mapValuesSeries = doLimit(mapValuesLimit, 1);
      function has(obj, key) {
        return key in obj;
      }
      /**
       * Caches the results of an `async` function. When creating a hash to store
       * function results against, the callback is omitted from the hash and an
       * optional hash function can be used.
       *
       * If no hash function is specified, the first argument is used as a hash key,
       * which may work reasonably if it is a string or a data type that converts to a
       * distinct string. Note that objects and arrays will not behave reasonably.
       * Neither will cases where the other arguments are significant. In such cases,
       * specify your own hash function.
       *
       * The cache of results is exposed as the `memo` property of the function
       * returned by `memoize`.
       *
       * @name memoize
       * @static
       * @memberOf module:Utils
       * @method
       * @category Util
       * @param {Function} fn - The function to proxy and cache results from.
       * @param {Function} hasher - An optional function for generating a custom hash
       * for storing results. It has all the arguments applied to it apart from the
       * callback, and must be synchronous.
       * @returns {Function} a memoized version of `fn`
       * @example
       *
       * var slow_fn = function(name, callback) {
       *     // do something
       *     callback(null, result);
       * };
       * var fn = async.memoize(slow_fn);
       *
       * // fn can now be used as if it were slow_fn
       * fn('some name', function() {
       *     // callback
       * });
       */
      function memoize(fn, hasher) {
        var memo = Object.create(null);
        var queues = Object.create(null);
        hasher = hasher || identity;
        var memoized = initialParams(function memoized(args, callback) {
          var key = hasher.apply(null, args);
          if (has(memo, key)) {
            setImmediate$1(function () {
              callback.apply(null, memo[key]);
            });
          } else if (has(queues, key)) {
            queues[key].push(callback);
          } else {
            queues[key] = [callback];
            fn.apply(null, args.concat(rest(function (args) {
              memo[key] = args;
              var q = queues[key];
              delete queues[key];
              for (var i = 0, l = q.length; i < l; i++) {
                q[i].apply(null, args);
              }
            })));
          }
        });
        memoized.memo = memo;
        memoized.unmemoized = fn;
        return memoized;
      }
      /**
       * Calls `callback` on a later loop around the event loop. In Node.js this just
       * calls `setImmediate`.  In the browser it will use `setImmediate` if
       * available, otherwise `setTimeout(callback, 0)`, which means other higher
       * priority events may precede the execution of `callback`.
       *
       * This is used internally for browser-compatibility purposes.
       *
       * @name nextTick
       * @static
       * @memberOf module:Utils
       * @method
       * @alias setImmediate
       * @category Util
       * @param {Function} callback - The function to call on a later loop around
       * the event loop. Invoked with (args...).
       * @param {...*} args... - any number of additional arguments to pass to the
       * callback on the next tick.
       * @example
       *
       * var call_order = [];
       * async.nextTick(function() {
       *     call_order.push('two');
       *     // call_order now equals ['one','two']
       * });
       * call_order.push('one');
       *
       * async.setImmediate(function (a, b, c) {
       *     // a, b, and c equal 1, 2, and 3
       * }, 1, 2, 3);
       */
      var _defer$1;
      if (hasNextTick) {
        _defer$1 = process.nextTick;
      } else if (hasSetImmediate) {
        _defer$1 = setImmediate;
      } else {
        _defer$1 = fallback;
      }
      var nextTick = wrap(_defer$1);
      function _parallel(eachfn, tasks, callback) {
        callback = callback || noop;
        var results = isArrayLike(tasks) ? [] : {};
        eachfn(tasks, function (task, key, callback) {
          task(rest(function (err, args) {
            if (args.length <= 1) {
              args = args[0];
            }
            results[key] = args;
            callback(err);
          }));
        }, function (err) {
          callback(err, results);
        });
      }
      /**
       * Run the `tasks` collection of functions in parallel, without waiting until
       * the previous function has completed. If any of the functions pass an error to
       * its callback, the main `callback` is immediately called with the value of the
       * error. Once the `tasks` have completed, the results are passed to the final
       * `callback` as an array.
       *
       * **Note:** `parallel` is about kicking-off I/O tasks in parallel, not about
       * parallel execution of code.  If your tasks do not use any timers or perform
       * any I/O, they will actually be executed in series.  Any synchronous setup
       * sections for each task will happen one after the other.  JavaScript remains
       * single-threaded.
       *
       * It is also possible to use an object instead of an array. Each property will
       * be run as a function and the results will be passed to the final `callback`
       * as an object instead of an array. This can be a more readable way of handling
       * results from {@link async.parallel}.
       *
       * @name parallel
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @category Control Flow
       * @param {Array|Iterable|Object} tasks - A collection containing functions to run.
       * Each function is passed a `callback(err, result)` which it must call on
       * completion with an error `err` (which can be `null`) and an optional `result`
       * value.
       * @param {Function} [callback] - An optional callback to run once all the
       * functions have completed successfully. This function gets a results array
       * (or object) containing all the result arguments passed to the task callbacks.
       * Invoked with (err, results).
       * @example
       * async.parallel([
       *     function(callback) {
       *         setTimeout(function() {
       *             callback(null, 'one');
       *         }, 200);
       *     },
       *     function(callback) {
       *         setTimeout(function() {
       *             callback(null, 'two');
       *         }, 100);
       *     }
       * ],
       * // optional callback
       * function(err, results) {
       *     // the results array will equal ['one','two'] even though
       *     // the second function had a shorter timeout.
       * });
       *
       * // an example using an object instead of an array
       * async.parallel({
       *     one: function(callback) {
       *         setTimeout(function() {
       *             callback(null, 1);
       *         }, 200);
       *     },
       *     two: function(callback) {
       *         setTimeout(function() {
       *             callback(null, 2);
       *         }, 100);
       *     }
       * }, function(err, results) {
       *     // results is now equals to: {one: 1, two: 2}
       * });
       */
      function parallelLimit(tasks, callback) {
        _parallel(eachOf, tasks, callback);
      }
      /**
       * The same as [`parallel`]{@link module:ControlFlow.parallel} but runs a maximum of `limit` async operations at a
       * time.
       *
       * @name parallelLimit
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @see [async.parallel]{@link module:ControlFlow.parallel}
       * @category Control Flow
       * @param {Array|Collection} tasks - A collection containing functions to run.
       * Each function is passed a `callback(err, result)` which it must call on
       * completion with an error `err` (which can be `null`) and an optional `result`
       * value.
       * @param {number} limit - The maximum number of async operations at a time.
       * @param {Function} [callback] - An optional callback to run once all the
       * functions have completed successfully. This function gets a results array
       * (or object) containing all the result arguments passed to the task callbacks.
       * Invoked with (err, results).
       */
      function parallelLimit$1(tasks, limit, callback) {
        _parallel(_eachOfLimit(limit), tasks, callback);
      }
      /**
       * A queue of tasks for the worker function to complete.
       * @typedef {Object} QueueObject
       * @memberOf module:ControlFlow
       * @property {Function} length - a function returning the number of items
       * waiting to be processed. Invoke with `queue.length()`.
       * @property {boolean} started - a boolean indicating whether or not any
       * items have been pushed and processed by the queue.
       * @property {Function} running - a function returning the number of items
       * currently being processed. Invoke with `queue.running()`.
       * @property {Function} workersList - a function returning the array of items
       * currently being processed. Invoke with `queue.workersList()`.
       * @property {Function} idle - a function returning false if there are items
       * waiting or being processed, or true if not. Invoke with `queue.idle()`.
       * @property {number} concurrency - an integer for determining how many `worker`
       * functions should be run in parallel. This property can be changed after a
       * `queue` is created to alter the concurrency on-the-fly.
       * @property {Function} push - add a new task to the `queue`. Calls `callback`
       * once the `worker` has finished processing the task. Instead of a single task,
       * a `tasks` array can be submitted. The respective callback is used for every
       * task in the list. Invoke with `queue.push(task, [callback])`,
       * @property {Function} unshift - add a new task to the front of the `queue`.
       * Invoke with `queue.unshift(task, [callback])`.
       * @property {Function} saturated - a callback that is called when the number of
       * running workers hits the `concurrency` limit, and further tasks will be
       * queued.
       * @property {Function} unsaturated - a callback that is called when the number
       * of running workers is less than the `concurrency` & `buffer` limits, and
       * further tasks will not be queued.
       * @property {number} buffer - A minimum threshold buffer in order to say that
       * the `queue` is `unsaturated`.
       * @property {Function} empty - a callback that is called when the last item
       * from the `queue` is given to a `worker`.
       * @property {Function} drain - a callback that is called when the last item
       * from the `queue` has returned from the `worker`.
       * @property {Function} error - a callback that is called when a task errors.
       * Has the signature `function(error, task)`.
       * @property {boolean} paused - a boolean for determining whether the queue is
       * in a paused state.
       * @property {Function} pause - a function that pauses the processing of tasks
       * until `resume()` is called. Invoke with `queue.pause()`.
       * @property {Function} resume - a function that resumes the processing of
       * queued tasks when the queue is paused. Invoke with `queue.resume()`.
       * @property {Function} kill - a function that removes the `drain` callback and
       * empties remaining tasks from the queue forcing it to go idle. Invoke with `queue.kill()`.
       */
      /**
       * Creates a `queue` object with the specified `concurrency`. Tasks added to the
       * `queue` are processed in parallel (up to the `concurrency` limit). If all
       * `worker`s are in progress, the task is queued until one becomes available.
       * Once a `worker` completes a `task`, that `task`'s callback is called.
       *
       * @name queue
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @category Control Flow
       * @param {Function} worker - An asynchronous function for processing a queued
       * task, which must call its `callback(err)` argument when finished, with an
       * optional `error` as an argument.  If you want to handle errors from an
       * individual task, pass a callback to `q.push()`. Invoked with
       * (task, callback).
       * @param {number} [concurrency=1] - An `integer` for determining how many
       * `worker` functions should be run in parallel.  If omitted, the concurrency
       * defaults to `1`.  If the concurrency is `0`, an error is thrown.
       * @returns {module:ControlFlow.QueueObject} A queue object to manage the tasks. Callbacks can
       * attached as certain properties to listen for specific events during the
       * lifecycle of the queue.
       * @example
       *
       * // create a queue object with concurrency 2
       * var q = async.queue(function(task, callback) {
       *     console.log('hello ' + task.name);
       *     callback();
       * }, 2);
       *
       * // assign a callback
       * q.drain = function() {
       *     console.log('all items have been processed');
       * };
       *
       * // add some items to the queue
       * q.push({name: 'foo'}, function(err) {
       *     console.log('finished processing foo');
       * });
       * q.push({name: 'bar'}, function (err) {
       *     console.log('finished processing bar');
       * });
       *
       * // add some items to the queue (batch-wise)
       * q.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function(err) {
       *     console.log('finished processing item');
       * });
       *
       * // add some items to the front of the queue
       * q.unshift({name: 'bar'}, function (err) {
       *     console.log('finished processing bar');
       * });
       */
      var queue$1 = function (worker, concurrency) {
        return queue(function (items, cb) {
          worker(items[0], cb);
        }, concurrency, 1);
      };
      /**
       * The same as [async.queue]{@link module:ControlFlow.queue} only tasks are assigned a priority and
       * completed in ascending priority order.
       *
       * @name priorityQueue
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @see [async.queue]{@link module:ControlFlow.queue}
       * @category Control Flow
       * @param {Function} worker - An asynchronous function for processing a queued
       * task, which must call its `callback(err)` argument when finished, with an
       * optional `error` as an argument.  If you want to handle errors from an
       * individual task, pass a callback to `q.push()`. Invoked with
       * (task, callback).
       * @param {number} concurrency - An `integer` for determining how many `worker`
       * functions should be run in parallel.  If omitted, the concurrency defaults to
       * `1`.  If the concurrency is `0`, an error is thrown.
       * @returns {module:ControlFlow.QueueObject} A priorityQueue object to manage the tasks. There are two
       * differences between `queue` and `priorityQueue` objects:
       * * `push(task, priority, [callback])` - `priority` should be a number. If an
       *   array of `tasks` is given, all tasks will be assigned the same priority.
       * * The `unshift` method was removed.
       */
      var priorityQueue = function (worker, concurrency) {
        // Start with a normal queue
        var q = queue$1(worker, concurrency);
        // Override push to accept second parameter representing priority
        q.push = function (data, priority, callback) {
          if (callback == null)
            callback = noop;
          if (typeof callback !== 'function') {
            throw new Error('task callback must be a function');
          }
          q.started = true;
          if (!isArray(data)) {
            data = [data];
          }
          if (data.length === 0) {
            // call drain immediately if there are no tasks
            return setImmediate$1(function () {
              q.drain();
            });
          }
          priority = priority || 0;
          var nextNode = q._tasks.head;
          while (nextNode && priority >= nextNode.priority) {
            nextNode = nextNode.next;
          }
          for (var i = 0, l = data.length; i < l; i++) {
            var item = {
              data: data[i],
              priority: priority,
              callback: callback
            };
            if (nextNode) {
              q._tasks.insertBefore(nextNode, item);
            } else {
              q._tasks.push(item);
            }
          }
          setImmediate$1(q.process);
        };
        // Remove unshift function
        delete q.unshift;
        return q;
      };
      /**
       * Runs the `tasks` array of functions in parallel, without waiting until the
       * previous function has completed. Once any of the `tasks` complete or pass an
       * error to its callback, the main `callback` is immediately called. It's
       * equivalent to `Promise.race()`.
       *
       * @name race
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @category Control Flow
       * @param {Array} tasks - An array containing functions to run. Each function
       * is passed a `callback(err, result)` which it must call on completion with an
       * error `err` (which can be `null`) and an optional `result` value.
       * @param {Function} callback - A callback to run once any of the functions have
       * completed. This function gets an error or result from the first function that
       * completed. Invoked with (err, result).
       * @returns undefined
       * @example
       *
       * async.race([
       *     function(callback) {
       *         setTimeout(function() {
       *             callback(null, 'one');
       *         }, 200);
       *     },
       *     function(callback) {
       *         setTimeout(function() {
       *             callback(null, 'two');
       *         }, 100);
       *     }
       * ],
       * // main callback
       * function(err, result) {
       *     // the result will be equal to 'two' as it finishes earlier
       * });
       */
      function race(tasks, callback) {
        callback = once(callback || noop);
        if (!isArray(tasks))
          return callback(new TypeError('First argument to race must be an array of functions'));
        if (!tasks.length)
          return callback();
        for (var i = 0, l = tasks.length; i < l; i++) {
          tasks[i](callback);
        }
      }
      var slice = Array.prototype.slice;
      /**
       * Same as [`reduce`]{@link module:Collections.reduce}, only operates on `array` in reverse order.
       *
       * @name reduceRight
       * @static
       * @memberOf module:Collections
       * @method
       * @see [async.reduce]{@link module:Collections.reduce}
       * @alias foldr
       * @category Collection
       * @param {Array} array - A collection to iterate over.
       * @param {*} memo - The initial state of the reduction.
       * @param {Function} iteratee - A function applied to each item in the
       * array to produce the next step in the reduction. The `iteratee` is passed a
       * `callback(err, reduction)` which accepts an optional error as its first
       * argument, and the state of the reduction as the second. If an error is
       * passed to the callback, the reduction is stopped and the main `callback` is
       * immediately called with the error. Invoked with (memo, item, callback).
       * @param {Function} [callback] - A callback which is called after all the
       * `iteratee` functions have finished. Result is the reduced value. Invoked with
       * (err, result).
       */
      function reduceRight(array, memo, iteratee, callback) {
        var reversed = slice.call(array).reverse();
        reduce(reversed, memo, iteratee, callback);
      }
      /**
       * Wraps the function in another function that always returns data even when it
       * errors.
       *
       * The object returned has either the property `error` or `value`.
       *
       * @name reflect
       * @static
       * @memberOf module:Utils
       * @method
       * @category Util
       * @param {Function} fn - The function you want to wrap
       * @returns {Function} - A function that always passes null to it's callback as
       * the error. The second argument to the callback will be an `object` with
       * either an `error` or a `value` property.
       * @example
       *
       * async.parallel([
       *     async.reflect(function(callback) {
       *         // do some stuff ...
       *         callback(null, 'one');
       *     }),
       *     async.reflect(function(callback) {
       *         // do some more stuff but error ...
       *         callback('bad stuff happened');
       *     }),
       *     async.reflect(function(callback) {
       *         // do some more stuff ...
       *         callback(null, 'two');
       *     })
       * ],
       * // optional callback
       * function(err, results) {
       *     // values
       *     // results[0].value = 'one'
       *     // results[1].error = 'bad stuff happened'
       *     // results[2].value = 'two'
       * });
       */
      function reflect(fn) {
        return initialParams(function reflectOn(args, reflectCallback) {
          args.push(rest(function callback(err, cbArgs) {
            if (err) {
              reflectCallback(null, { error: err });
            } else {
              var value = null;
              if (cbArgs.length === 1) {
                value = cbArgs[0];
              } else if (cbArgs.length > 1) {
                value = cbArgs;
              }
              reflectCallback(null, { value: value });
            }
          }));
          return fn.apply(this, args);
        });
      }
      function reject$1(eachfn, arr, iteratee, callback) {
        _filter(eachfn, arr, function (value, cb) {
          iteratee(value, function (err, v) {
            cb(err, !v);
          });
        }, callback);
      }
      /**
       * The opposite of [`filter`]{@link module:Collections.filter}. Removes values that pass an `async` truth test.
       *
       * @name reject
       * @static
       * @memberOf module:Collections
       * @method
       * @see [async.filter]{@link module:Collections.filter}
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {Function} iteratee - A truth test to apply to each item in `coll`.
       * The `iteratee` is passed a `callback(err, truthValue)`, which must be called
       * with a boolean argument once it has completed. Invoked with (item, callback).
       * @param {Function} [callback] - A callback which is called after all the
       * `iteratee` functions have finished. Invoked with (err, results).
       * @example
       *
       * async.reject(['file1','file2','file3'], function(filePath, callback) {
       *     fs.access(filePath, function(err) {
       *         callback(null, !err)
       *     });
       * }, function(err, results) {
       *     // results now equals an array of missing files
       *     createFiles(results);
       * });
       */
      var reject = doParallel(reject$1);
      /**
       * A helper function that wraps an array or an object of functions with reflect.
       *
       * @name reflectAll
       * @static
       * @memberOf module:Utils
       * @method
       * @see [async.reflect]{@link module:Utils.reflect}
       * @category Util
       * @param {Array} tasks - The array of functions to wrap in `async.reflect`.
       * @returns {Array} Returns an array of functions, each function wrapped in
       * `async.reflect`
       * @example
       *
       * let tasks = [
       *     function(callback) {
       *         setTimeout(function() {
       *             callback(null, 'one');
       *         }, 200);
       *     },
       *     function(callback) {
       *         // do some more stuff but error ...
       *         callback(new Error('bad stuff happened'));
       *     },
       *     function(callback) {
       *         setTimeout(function() {
       *             callback(null, 'two');
       *         }, 100);
       *     }
       * ];
       *
       * async.parallel(async.reflectAll(tasks),
       * // optional callback
       * function(err, results) {
       *     // values
       *     // results[0].value = 'one'
       *     // results[1].error = Error('bad stuff happened')
       *     // results[2].value = 'two'
       * });
       *
       * // an example using an object instead of an array
       * let tasks = {
       *     one: function(callback) {
       *         setTimeout(function() {
       *             callback(null, 'one');
       *         }, 200);
       *     },
       *     two: function(callback) {
       *         callback('two');
       *     },
       *     three: function(callback) {
       *         setTimeout(function() {
       *             callback(null, 'three');
       *         }, 100);
       *     }
       * };
       *
       * async.parallel(async.reflectAll(tasks),
       * // optional callback
       * function(err, results) {
       *     // values
       *     // results.one.value = 'one'
       *     // results.two.error = 'two'
       *     // results.three.value = 'three'
       * });
       */
      function reflectAll(tasks) {
        var results;
        if (isArray(tasks)) {
          results = arrayMap(tasks, reflect);
        } else {
          results = {};
          baseForOwn(tasks, function (task, key) {
            results[key] = reflect.call(this, task);
          });
        }
        return results;
      }
      /**
       * The same as [`reject`]{@link module:Collections.reject} but runs a maximum of `limit` async operations at a
       * time.
       *
       * @name rejectLimit
       * @static
       * @memberOf module:Collections
       * @method
       * @see [async.reject]{@link module:Collections.reject}
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {number} limit - The maximum number of async operations at a time.
       * @param {Function} iteratee - A truth test to apply to each item in `coll`.
       * The `iteratee` is passed a `callback(err, truthValue)`, which must be called
       * with a boolean argument once it has completed. Invoked with (item, callback).
       * @param {Function} [callback] - A callback which is called after all the
       * `iteratee` functions have finished. Invoked with (err, results).
       */
      var rejectLimit = doParallelLimit(reject$1);
      /**
       * The same as [`reject`]{@link module:Collections.reject} but runs only a single async operation at a time.
       *
       * @name rejectSeries
       * @static
       * @memberOf module:Collections
       * @method
       * @see [async.reject]{@link module:Collections.reject}
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {Function} iteratee - A truth test to apply to each item in `coll`.
       * The `iteratee` is passed a `callback(err, truthValue)`, which must be called
       * with a boolean argument once it has completed. Invoked with (item, callback).
       * @param {Function} [callback] - A callback which is called after all the
       * `iteratee` functions have finished. Invoked with (err, results).
       */
      var rejectSeries = doLimit(rejectLimit, 1);
      /**
       * Creates a function that returns `value`.
       *
       * @static
       * @memberOf _
       * @since 2.4.0
       * @category Util
       * @param {*} value The value to return from the new function.
       * @returns {Function} Returns the new constant function.
       * @example
       *
       * var objects = _.times(2, _.constant({ 'a': 1 }));
       *
       * console.log(objects);
       * // => [{ 'a': 1 }, { 'a': 1 }]
       *
       * console.log(objects[0] === objects[1]);
       * // => true
       */
      function constant$1(value) {
        return function () {
          return value;
        };
      }
      /**
       * Attempts to get a successful response from `task` no more than `times` times
       * before returning an error. If the task is successful, the `callback` will be
       * passed the result of the successful task. If all attempts fail, the callback
       * will be passed the error and result (if any) of the final attempt.
       *
       * @name retry
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @category Control Flow
       * @param {Object|number} [opts = {times: 5, interval: 0}| 5] - Can be either an
       * object with `times` and `interval` or a number.
       * * `times` - The number of attempts to make before giving up.  The default
       *   is `5`.
       * * `interval` - The time to wait between retries, in milliseconds.  The
       *   default is `0`. The interval may also be specified as a function of the
       *   retry count (see example).
       * * `errorFilter` - An optional synchronous function that is invoked on
       *   erroneous result. If it returns `true` the retry attempts will continue;
       *   if the function returns `false` the retry flow is aborted with the current
       *   attempt's error and result being returned to the final callback.
       *   Invoked with (err).
       * * If `opts` is a number, the number specifies the number of times to retry,
       *   with the default interval of `0`.
       * @param {Function} task - A function which receives two arguments: (1) a
       * `callback(err, result)` which must be called when finished, passing `err`
       * (which can be `null`) and the `result` of the function's execution, and (2)
       * a `results` object, containing the results of the previously executed
       * functions (if nested inside another control flow). Invoked with
       * (callback, results).
       * @param {Function} [callback] - An optional callback which is called when the
       * task has succeeded, or after the final failed attempt. It receives the `err`
       * and `result` arguments of the last attempt at completing the `task`. Invoked
       * with (err, results).
       * @example
       *
       * // The `retry` function can be used as a stand-alone control flow by passing
       * // a callback, as shown below:
       *
       * // try calling apiMethod 3 times
       * async.retry(3, apiMethod, function(err, result) {
       *     // do something with the result
       * });
       *
       * // try calling apiMethod 3 times, waiting 200 ms between each retry
       * async.retry({times: 3, interval: 200}, apiMethod, function(err, result) {
       *     // do something with the result
       * });
       *
       * // try calling apiMethod 10 times with exponential backoff
       * // (i.e. intervals of 100, 200, 400, 800, 1600, ... milliseconds)
       * async.retry({
       *   times: 10,
       *   interval: function(retryCount) {
       *     return 50 * Math.pow(2, retryCount);
       *   }
       * }, apiMethod, function(err, result) {
       *     // do something with the result
       * });
       *
       * // try calling apiMethod the default 5 times no delay between each retry
       * async.retry(apiMethod, function(err, result) {
       *     // do something with the result
       * });
       *
       * // try calling apiMethod only when error condition satisfies, all other
       * // errors will abort the retry control flow and return to final callback
       * async.retry({
       *   errorFilter: function(err) {
       *     return err.message === 'Temporary error'; // only retry on a specific error
       *   }
       * }, apiMethod, function(err, result) {
       *     // do something with the result
       * });
       *
       * // It can also be embedded within other control flow functions to retry
       * // individual methods that are not as reliable, like this:
       * async.auto({
       *     users: api.getUsers.bind(api),
       *     payments: async.retry(3, api.getPayments.bind(api))
       * }, function(err, results) {
       *     // do something with the results
       * });
       *
       */
      function retry(opts, task, callback) {
        var DEFAULT_TIMES = 5;
        var DEFAULT_INTERVAL = 0;
        var options = {
          times: DEFAULT_TIMES,
          intervalFunc: constant$1(DEFAULT_INTERVAL)
        };
        function parseTimes(acc, t) {
          if (typeof t === 'object') {
            acc.times = +t.times || DEFAULT_TIMES;
            acc.intervalFunc = typeof t.interval === 'function' ? t.interval : constant$1(+t.interval || DEFAULT_INTERVAL);
            acc.errorFilter = t.errorFilter;
          } else if (typeof t === 'number' || typeof t === 'string') {
            acc.times = +t || DEFAULT_TIMES;
          } else {
            throw new Error('Invalid arguments for async.retry');
          }
        }
        if (arguments.length < 3 && typeof opts === 'function') {
          callback = task || noop;
          task = opts;
        } else {
          parseTimes(options, opts);
          callback = callback || noop;
        }
        if (typeof task !== 'function') {
          throw new Error('Invalid arguments for async.retry');
        }
        var attempt = 1;
        function retryAttempt() {
          task(function (err) {
            if (err && attempt++ < options.times && (typeof options.errorFilter != 'function' || options.errorFilter(err))) {
              setTimeout(retryAttempt, options.intervalFunc(attempt));
            } else {
              callback.apply(null, arguments);
            }
          });
        }
        retryAttempt();
      }
      /**
       * A close relative of [`retry`]{@link module:ControlFlow.retry}.  This method wraps a task and makes it
       * retryable, rather than immediately calling it with retries.
       *
       * @name retryable
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @see [async.retry]{@link module:ControlFlow.retry}
       * @category Control Flow
       * @param {Object|number} [opts = {times: 5, interval: 0}| 5] - optional
       * options, exactly the same as from `retry`
       * @param {Function} task - the asynchronous function to wrap
       * @returns {Functions} The wrapped function, which when invoked, will retry on
       * an error, based on the parameters specified in `opts`.
       * @example
       *
       * async.auto({
       *     dep1: async.retryable(3, getFromFlakyService),
       *     process: ["dep1", async.retryable(3, function (results, cb) {
       *         maybeProcessData(results.dep1, cb);
       *     })]
       * }, callback);
       */
      var retryable = function (opts, task) {
        if (!task) {
          task = opts;
          opts = null;
        }
        return initialParams(function (args, callback) {
          function taskFn(cb) {
            task.apply(null, args.concat(cb));
          }
          if (opts)
            retry(opts, taskFn, callback);
          else
            retry(taskFn, callback);
        });
      };
      /**
       * Run the functions in the `tasks` collection in series, each one running once
       * the previous function has completed. If any functions in the series pass an
       * error to its callback, no more functions are run, and `callback` is
       * immediately called with the value of the error. Otherwise, `callback`
       * receives an array of results when `tasks` have completed.
       *
       * It is also possible to use an object instead of an array. Each property will
       * be run as a function, and the results will be passed to the final `callback`
       * as an object instead of an array. This can be a more readable way of handling
       *  results from {@link async.series}.
       *
       * **Note** that while many implementations preserve the order of object
       * properties, the [ECMAScript Language Specification](http://www.ecma-international.org/ecma-262/5.1/#sec-8.6)
       * explicitly states that
       *
       * > The mechanics and order of enumerating the properties is not specified.
       *
       * So if you rely on the order in which your series of functions are executed,
       * and want this to work on all platforms, consider using an array.
       *
       * @name series
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @category Control Flow
       * @param {Array|Iterable|Object} tasks - A collection containing functions to run, each
       * function is passed a `callback(err, result)` it must call on completion with
       * an error `err` (which can be `null`) and an optional `result` value.
       * @param {Function} [callback] - An optional callback to run once all the
       * functions have completed. This function gets a results array (or object)
       * containing all the result arguments passed to the `task` callbacks. Invoked
       * with (err, result).
       * @example
       * async.series([
       *     function(callback) {
       *         // do some stuff ...
       *         callback(null, 'one');
       *     },
       *     function(callback) {
       *         // do some more stuff ...
       *         callback(null, 'two');
       *     }
       * ],
       * // optional callback
       * function(err, results) {
       *     // results is now equal to ['one', 'two']
       * });
       *
       * async.series({
       *     one: function(callback) {
       *         setTimeout(function() {
       *             callback(null, 1);
       *         }, 200);
       *     },
       *     two: function(callback){
       *         setTimeout(function() {
       *             callback(null, 2);
       *         }, 100);
       *     }
       * }, function(err, results) {
       *     // results is now equal to: {one: 1, two: 2}
       * });
       */
      function series(tasks, callback) {
        _parallel(eachOfSeries, tasks, callback);
      }
      /**
       * Returns `true` if at least one element in the `coll` satisfies an async test.
       * If any iteratee call returns `true`, the main `callback` is immediately
       * called.
       *
       * @name some
       * @static
       * @memberOf module:Collections
       * @method
       * @alias any
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {Function} iteratee - A truth test to apply to each item in the array
       * in parallel. The iteratee is passed a `callback(err, truthValue)` which must
       * be called with a boolean argument once it has completed. Invoked with
       * (item, callback).
       * @param {Function} [callback] - A callback which is called as soon as any
       * iteratee returns `true`, or after all the iteratee functions have finished.
       * Result will be either `true` or `false` depending on the values of the async
       * tests. Invoked with (err, result).
       * @example
       *
       * async.some(['file1','file2','file3'], function(filePath, callback) {
       *     fs.access(filePath, function(err) {
       *         callback(null, !err)
       *     });
       * }, function(err, result) {
       *     // if result is true then at least one of the files exists
       * });
       */
      var some = _createTester(eachOf, Boolean, identity);
      /**
       * The same as [`some`]{@link module:Collections.some} but runs a maximum of `limit` async operations at a time.
       *
       * @name someLimit
       * @static
       * @memberOf module:Collections
       * @method
       * @see [async.some]{@link module:Collections.some}
       * @alias anyLimit
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {number} limit - The maximum number of async operations at a time.
       * @param {Function} iteratee - A truth test to apply to each item in the array
       * in parallel. The iteratee is passed a `callback(err, truthValue)` which must
       * be called with a boolean argument once it has completed. Invoked with
       * (item, callback).
       * @param {Function} [callback] - A callback which is called as soon as any
       * iteratee returns `true`, or after all the iteratee functions have finished.
       * Result will be either `true` or `false` depending on the values of the async
       * tests. Invoked with (err, result).
       */
      var someLimit = _createTester(eachOfLimit, Boolean, identity);
      /**
       * The same as [`some`]{@link module:Collections.some} but runs only a single async operation at a time.
       *
       * @name someSeries
       * @static
       * @memberOf module:Collections
       * @method
       * @see [async.some]{@link module:Collections.some}
       * @alias anySeries
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {Function} iteratee - A truth test to apply to each item in the array
       * in parallel. The iteratee is passed a `callback(err, truthValue)` which must
       * be called with a boolean argument once it has completed. Invoked with
       * (item, callback).
       * @param {Function} [callback] - A callback which is called as soon as any
       * iteratee returns `true`, or after all the iteratee functions have finished.
       * Result will be either `true` or `false` depending on the values of the async
       * tests. Invoked with (err, result).
       */
      var someSeries = doLimit(someLimit, 1);
      /**
       * Sorts a list by the results of running each `coll` value through an async
       * `iteratee`.
       *
       * @name sortBy
       * @static
       * @memberOf module:Collections
       * @method
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {Function} iteratee - A function to apply to each item in `coll`.
       * The iteratee is passed a `callback(err, sortValue)` which must be called once
       * it has completed with an error (which can be `null`) and a value to use as
       * the sort criteria. Invoked with (item, callback).
       * @param {Function} callback - A callback which is called after all the
       * `iteratee` functions have finished, or an error occurs. Results is the items
       * from the original `coll` sorted by the values returned by the `iteratee`
       * calls. Invoked with (err, results).
       * @example
       *
       * async.sortBy(['file1','file2','file3'], function(file, callback) {
       *     fs.stat(file, function(err, stats) {
       *         callback(err, stats.mtime);
       *     });
       * }, function(err, results) {
       *     // results is now the original array of files sorted by
       *     // modified date
       * });
       *
       * // By modifying the callback parameter the
       * // sorting order can be influenced:
       *
       * // ascending order
       * async.sortBy([1,9,3,5], function(x, callback) {
       *     callback(null, x);
       * }, function(err,result) {
       *     // result callback
       * });
       *
       * // descending order
       * async.sortBy([1,9,3,5], function(x, callback) {
       *     callback(null, x*-1);    //<- x*-1 instead of x, turns the order around
       * }, function(err,result) {
       *     // result callback
       * });
       */
      function sortBy(coll, iteratee, callback) {
        map(coll, function (x, callback) {
          iteratee(x, function (err, criteria) {
            if (err)
              return callback(err);
            callback(null, {
              value: x,
              criteria: criteria
            });
          });
        }, function (err, results) {
          if (err)
            return callback(err);
          callback(null, arrayMap(results.sort(comparator), baseProperty('value')));
        });
        function comparator(left, right) {
          var a = left.criteria, b = right.criteria;
          return a < b ? -1 : a > b ? 1 : 0;
        }
      }
      /**
       * Sets a time limit on an asynchronous function. If the function does not call
       * its callback within the specified milliseconds, it will be called with a
       * timeout error. The code property for the error object will be `'ETIMEDOUT'`.
       *
       * @name timeout
       * @static
       * @memberOf module:Utils
       * @method
       * @category Util
       * @param {Function} asyncFn - The asynchronous function you want to set the
       * time limit.
       * @param {number} milliseconds - The specified time limit.
       * @param {*} [info] - Any variable you want attached (`string`, `object`, etc)
       * to timeout Error for more information..
       * @returns {Function} Returns a wrapped function that can be used with any of
       * the control flow functions. Invoke this function with the same
       * parameters as you would `asyncFunc`.
       * @example
       *
       * function myFunction(foo, callback) {
       *     doAsyncTask(foo, function(err, data) {
       *         // handle errors
       *         if (err) return callback(err);
       *
       *         // do some stuff ...
       *
       *         // return processed data
       *         return callback(null, data);
       *     });
       * }
       *
       * var wrapped = async.timeout(myFunction, 1000);
       *
       * // call `wrapped` as you would `myFunction`
       * wrapped({ bar: 'bar' }, function(err, data) {
       *     // if `myFunction` takes < 1000 ms to execute, `err`
       *     // and `data` will have their expected values
       *
       *     // else `err` will be an Error with the code 'ETIMEDOUT'
       * });
       */
      function timeout(asyncFn, milliseconds, info) {
        var originalCallback, timer;
        var timedOut = false;
        function injectedCallback() {
          if (!timedOut) {
            originalCallback.apply(null, arguments);
            clearTimeout(timer);
          }
        }
        function timeoutCallback() {
          var name = asyncFn.name || 'anonymous';
          var error = new Error('Callback function "' + name + '" timed out.');
          error.code = 'ETIMEDOUT';
          if (info) {
            error.info = info;
          }
          timedOut = true;
          originalCallback(error);
        }
        return initialParams(function (args, origCallback) {
          originalCallback = origCallback;
          // setup timer and call original function
          timer = setTimeout(timeoutCallback, milliseconds);
          asyncFn.apply(null, args.concat(injectedCallback));
        });
      }
      /* Built-in method references for those with the same name as other `lodash` methods. */
      var nativeCeil = Math.ceil;
      var nativeMax$1 = Math.max;
      /**
       * The base implementation of `_.range` and `_.rangeRight` which doesn't
       * coerce arguments.
       *
       * @private
       * @param {number} start The start of the range.
       * @param {number} end The end of the range.
       * @param {number} step The value to increment or decrement by.
       * @param {boolean} [fromRight] Specify iterating from right to left.
       * @returns {Array} Returns the range of numbers.
       */
      function baseRange(start, end, step, fromRight) {
        var index = -1, length = nativeMax$1(nativeCeil((end - start) / (step || 1)), 0), result = Array(length);
        while (length--) {
          result[fromRight ? length : ++index] = start;
          start += step;
        }
        return result;
      }
      /**
       * The same as [times]{@link module:ControlFlow.times} but runs a maximum of `limit` async operations at a
       * time.
       *
       * @name timesLimit
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @see [async.times]{@link module:ControlFlow.times}
       * @category Control Flow
       * @param {number} count - The number of times to run the function.
       * @param {number} limit - The maximum number of async operations at a time.
       * @param {Function} iteratee - The function to call `n` times. Invoked with the
       * iteration index and a callback (n, next).
       * @param {Function} callback - see [async.map]{@link module:Collections.map}.
       */
      function timeLimit(count, limit, iteratee, callback) {
        mapLimit(baseRange(0, count, 1), limit, iteratee, callback);
      }
      /**
       * Calls the `iteratee` function `n` times, and accumulates results in the same
       * manner you would use with [map]{@link module:Collections.map}.
       *
       * @name times
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @see [async.map]{@link module:Collections.map}
       * @category Control Flow
       * @param {number} n - The number of times to run the function.
       * @param {Function} iteratee - The function to call `n` times. Invoked with the
       * iteration index and a callback (n, next).
       * @param {Function} callback - see {@link module:Collections.map}.
       * @example
       *
       * // Pretend this is some complicated async factory
       * var createUser = function(id, callback) {
       *     callback(null, {
       *         id: 'user' + id
       *     });
       * };
       *
       * // generate 5 users
       * async.times(5, function(n, next) {
       *     createUser(n, function(err, user) {
       *         next(err, user);
       *     });
       * }, function(err, users) {
       *     // we should now have 5 users
       * });
       */
      var times = doLimit(timeLimit, Infinity);
      /**
       * The same as [times]{@link module:ControlFlow.times} but runs only a single async operation at a time.
       *
       * @name timesSeries
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @see [async.times]{@link module:ControlFlow.times}
       * @category Control Flow
       * @param {number} n - The number of times to run the function.
       * @param {Function} iteratee - The function to call `n` times. Invoked with the
       * iteration index and a callback (n, next).
       * @param {Function} callback - see {@link module:Collections.map}.
       */
      var timesSeries = doLimit(timeLimit, 1);
      /**
       * A relative of `reduce`.  Takes an Object or Array, and iterates over each
       * element in series, each step potentially mutating an `accumulator` value.
       * The type of the accumulator defaults to the type of collection passed in.
       *
       * @name transform
       * @static
       * @memberOf module:Collections
       * @method
       * @category Collection
       * @param {Array|Iterable|Object} coll - A collection to iterate over.
       * @param {*} [accumulator] - The initial state of the transform.  If omitted,
       * it will default to an empty Object or Array, depending on the type of `coll`
       * @param {Function} iteratee - A function applied to each item in the
       * collection that potentially modifies the accumulator. The `iteratee` is
       * passed a `callback(err)` which accepts an optional error as its first
       * argument. If an error is passed to the callback, the transform is stopped
       * and the main `callback` is immediately called with the error.
       * Invoked with (accumulator, item, key, callback).
       * @param {Function} [callback] - A callback which is called after all the
       * `iteratee` functions have finished. Result is the transformed accumulator.
       * Invoked with (err, result).
       * @example
       *
       * async.transform([1,2,3], function(acc, item, index, callback) {
       *     // pointless async:
       *     process.nextTick(function() {
       *         acc.push(item * 2)
       *         callback(null)
       *     });
       * }, function(err, result) {
       *     // result is now equal to [2, 4, 6]
       * });
       *
       * @example
       *
       * async.transform({a: 1, b: 2, c: 3}, function (obj, val, key, callback) {
       *     setImmediate(function () {
       *         obj[key] = val * 2;
       *         callback();
       *     })
       * }, function (err, result) {
       *     // result is equal to {a: 2, b: 4, c: 6}
       * })
       */
      function transform(coll, accumulator, iteratee, callback) {
        if (arguments.length === 3) {
          callback = iteratee;
          iteratee = accumulator;
          accumulator = isArray(coll) ? [] : {};
        }
        callback = once(callback || noop);
        eachOf(coll, function (v, k, cb) {
          iteratee(accumulator, v, k, cb);
        }, function (err) {
          callback(err, accumulator);
        });
      }
      /**
       * Undoes a [memoize]{@link module:Utils.memoize}d function, reverting it to the original,
       * unmemoized form. Handy for testing.
       *
       * @name unmemoize
       * @static
       * @memberOf module:Utils
       * @method
       * @see [async.memoize]{@link module:Utils.memoize}
       * @category Util
       * @param {Function} fn - the memoized function
       * @returns {Function} a function that calls the original unmemoized function
       */
      function unmemoize(fn) {
        return function () {
          return (fn.unmemoized || fn).apply(null, arguments);
        };
      }
      /**
       * Repeatedly call `iteratee`, while `test` returns `true`. Calls `callback` when
       * stopped, or an error occurs.
       *
       * @name whilst
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @category Control Flow
       * @param {Function} test - synchronous truth test to perform before each
       * execution of `iteratee`. Invoked with ().
       * @param {Function} iteratee - A function which is called each time `test` passes.
       * The function is passed a `callback(err)`, which must be called once it has
       * completed with an optional `err` argument. Invoked with (callback).
       * @param {Function} [callback] - A callback which is called after the test
       * function has failed and repeated execution of `iteratee` has stopped. `callback`
       * will be passed an error and any arguments passed to the final `iteratee`'s
       * callback. Invoked with (err, [results]);
       * @returns undefined
       * @example
       *
       * var count = 0;
       * async.whilst(
       *     function() { return count < 5; },
       *     function(callback) {
       *         count++;
       *         setTimeout(function() {
       *             callback(null, count);
       *         }, 1000);
       *     },
       *     function (err, n) {
       *         // 5 seconds have passed, n = 5
       *     }
       * );
       */
      function whilst(test, iteratee, callback) {
        callback = onlyOnce(callback || noop);
        if (!test())
          return callback(null);
        var next = rest(function (err, args) {
          if (err)
            return callback(err);
          if (test())
            return iteratee(next);
          callback.apply(null, [null].concat(args));
        });
        iteratee(next);
      }
      /**
       * Repeatedly call `fn` until `test` returns `true`. Calls `callback` when
       * stopped, or an error occurs. `callback` will be passed an error and any
       * arguments passed to the final `fn`'s callback.
       *
       * The inverse of [whilst]{@link module:ControlFlow.whilst}.
       *
       * @name until
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @see [async.whilst]{@link module:ControlFlow.whilst}
       * @category Control Flow
       * @param {Function} test - synchronous truth test to perform before each
       * execution of `fn`. Invoked with ().
       * @param {Function} fn - A function which is called each time `test` fails.
       * The function is passed a `callback(err)`, which must be called once it has
       * completed with an optional `err` argument. Invoked with (callback).
       * @param {Function} [callback] - A callback which is called after the test
       * function has passed and repeated execution of `fn` has stopped. `callback`
       * will be passed an error and any arguments passed to the final `fn`'s
       * callback. Invoked with (err, [results]);
       */
      function until(test, fn, callback) {
        whilst(function () {
          return !test.apply(this, arguments);
        }, fn, callback);
      }
      /**
       * Runs the `tasks` array of functions in series, each passing their results to
       * the next in the array. However, if any of the `tasks` pass an error to their
       * own callback, the next function is not executed, and the main `callback` is
       * immediately called with the error.
       *
       * @name waterfall
       * @static
       * @memberOf module:ControlFlow
       * @method
       * @category Control Flow
       * @param {Array} tasks - An array of functions to run, each function is passed
       * a `callback(err, result1, result2, ...)` it must call on completion. The
       * first argument is an error (which can be `null`) and any further arguments
       * will be passed as arguments in order to the next task.
       * @param {Function} [callback] - An optional callback to run once all the
       * functions have completed. This will be passed the results of the last task's
       * callback. Invoked with (err, [results]).
       * @returns undefined
       * @example
       *
       * async.waterfall([
       *     function(callback) {
       *         callback(null, 'one', 'two');
       *     },
       *     function(arg1, arg2, callback) {
       *         // arg1 now equals 'one' and arg2 now equals 'two'
       *         callback(null, 'three');
       *     },
       *     function(arg1, callback) {
       *         // arg1 now equals 'three'
       *         callback(null, 'done');
       *     }
       * ], function (err, result) {
       *     // result now equals 'done'
       * });
       *
       * // Or, with named functions:
       * async.waterfall([
       *     myFirstFunction,
       *     mySecondFunction,
       *     myLastFunction,
       * ], function (err, result) {
       *     // result now equals 'done'
       * });
       * function myFirstFunction(callback) {
       *     callback(null, 'one', 'two');
       * }
       * function mySecondFunction(arg1, arg2, callback) {
       *     // arg1 now equals 'one' and arg2 now equals 'two'
       *     callback(null, 'three');
       * }
       * function myLastFunction(arg1, callback) {
       *     // arg1 now equals 'three'
       *     callback(null, 'done');
       * }
       */
      var waterfall = function (tasks, callback) {
        callback = once(callback || noop);
        if (!isArray(tasks))
          return callback(new Error('First argument to waterfall must be an array of functions'));
        if (!tasks.length)
          return callback();
        var taskIndex = 0;
        function nextTask(args) {
          if (taskIndex === tasks.length) {
            return callback.apply(null, [null].concat(args));
          }
          var taskCallback = onlyOnce(rest(function (err, args) {
            if (err) {
              return callback.apply(null, [err].concat(args));
            }
            nextTask(args);
          }));
          args.push(taskCallback);
          var task = tasks[taskIndex++];
          task.apply(null, args);
        }
        nextTask([]);
      };
      /**
       * Async is a utility module which provides straight-forward, powerful functions
       * for working with asynchronous JavaScript. Although originally designed for
       * use with [Node.js](http://nodejs.org) and installable via
       * `npm install --save async`, it can also be used directly in the browser.
       * @module async
       */
      /**
       * A collection of `async` functions for manipulating collections, such as
       * arrays and objects.
       * @module Collections
       */
      /**
       * A collection of `async` functions for controlling the flow through a script.
       * @module ControlFlow
       */
      /**
       * A collection of `async` utility functions.
       * @module Utils
       */
      var index = {
        applyEach: applyEach,
        applyEachSeries: applyEachSeries,
        apply: apply$2,
        asyncify: asyncify,
        auto: auto,
        autoInject: autoInject,
        cargo: cargo,
        compose: compose,
        concat: concat,
        concatSeries: concatSeries,
        constant: constant,
        detect: detect,
        detectLimit: detectLimit,
        detectSeries: detectSeries,
        dir: dir,
        doDuring: doDuring,
        doUntil: doUntil,
        doWhilst: doWhilst,
        during: during,
        each: eachLimit,
        eachLimit: eachLimit$1,
        eachOf: eachOf,
        eachOfLimit: eachOfLimit,
        eachOfSeries: eachOfSeries,
        eachSeries: eachSeries,
        ensureAsync: ensureAsync,
        every: every,
        everyLimit: everyLimit,
        everySeries: everySeries,
        filter: filter,
        filterLimit: filterLimit,
        filterSeries: filterSeries,
        forever: forever,
        log: log,
        map: map,
        mapLimit: mapLimit,
        mapSeries: mapSeries,
        mapValues: mapValues,
        mapValuesLimit: mapValuesLimit,
        mapValuesSeries: mapValuesSeries,
        memoize: memoize,
        nextTick: nextTick,
        parallel: parallelLimit,
        parallelLimit: parallelLimit$1,
        priorityQueue: priorityQueue,
        queue: queue$1,
        race: race,
        reduce: reduce,
        reduceRight: reduceRight,
        reflect: reflect,
        reflectAll: reflectAll,
        reject: reject,
        rejectLimit: rejectLimit,
        rejectSeries: rejectSeries,
        retry: retry,
        retryable: retryable,
        seq: seq$1,
        series: series,
        setImmediate: setImmediate$1,
        some: some,
        someLimit: someLimit,
        someSeries: someSeries,
        sortBy: sortBy,
        timeout: timeout,
        times: times,
        timesLimit: timeLimit,
        timesSeries: timesSeries,
        transform: transform,
        unmemoize: unmemoize,
        until: until,
        waterfall: waterfall,
        whilst: whilst,
        // aliases
        all: every,
        any: some,
        forEach: eachLimit,
        forEachSeries: eachSeries,
        forEachLimit: eachLimit$1,
        forEachOf: eachOf,
        forEachOfSeries: eachOfSeries,
        forEachOfLimit: eachOfLimit,
        inject: reduce,
        foldl: reduce,
        foldr: reduceRight,
        select: filter,
        selectLimit: filterLimit,
        selectSeries: filterSeries,
        wrapSync: asyncify
      };
      var exports = {};
      exports['default'] = index;
      exports.applyEach = applyEach;
      exports.applyEachSeries = applyEachSeries;
      exports.apply = apply$2;
      exports.asyncify = asyncify;
      exports.auto = auto;
      exports.autoInject = autoInject;
      exports.cargo = cargo;
      exports.compose = compose;
      exports.concat = concat;
      exports.concatSeries = concatSeries;
      exports.constant = constant;
      exports.detect = detect;
      exports.detectLimit = detectLimit;
      exports.detectSeries = detectSeries;
      exports.dir = dir;
      exports.doDuring = doDuring;
      exports.doUntil = doUntil;
      exports.doWhilst = doWhilst;
      exports.during = during;
      exports.each = eachLimit;
      exports.eachLimit = eachLimit$1;
      exports.eachOf = eachOf;
      exports.eachOfLimit = eachOfLimit;
      exports.eachOfSeries = eachOfSeries;
      exports.eachSeries = eachSeries;
      exports.ensureAsync = ensureAsync;
      exports.every = every;
      exports.everyLimit = everyLimit;
      exports.everySeries = everySeries;
      exports.filter = filter;
      exports.filterLimit = filterLimit;
      exports.filterSeries = filterSeries;
      exports.forever = forever;
      exports.log = log;
      exports.map = map;
      exports.mapLimit = mapLimit;
      exports.mapSeries = mapSeries;
      exports.mapValues = mapValues;
      exports.mapValuesLimit = mapValuesLimit;
      exports.mapValuesSeries = mapValuesSeries;
      exports.memoize = memoize;
      exports.nextTick = nextTick;
      exports.parallel = parallelLimit;
      exports.parallelLimit = parallelLimit$1;
      exports.priorityQueue = priorityQueue;
      exports.queue = queue$1;
      exports.race = race;
      exports.reduce = reduce;
      exports.reduceRight = reduceRight;
      exports.reflect = reflect;
      exports.reflectAll = reflectAll;
      exports.reject = reject;
      exports.rejectLimit = rejectLimit;
      exports.rejectSeries = rejectSeries;
      exports.retry = retry;
      exports.retryable = retryable;
      exports.seq = seq$1;
      exports.series = series;
      exports.setImmediate = setImmediate$1;
      exports.some = some;
      exports.someLimit = someLimit;
      exports.someSeries = someSeries;
      exports.sortBy = sortBy;
      exports.timeout = timeout;
      exports.times = times;
      exports.timesLimit = timeLimit;
      exports.timesSeries = timesSeries;
      exports.transform = transform;
      exports.unmemoize = unmemoize;
      exports.until = until;
      exports.waterfall = waterfall;
      exports.whilst = whilst;
      exports.all = every;
      exports.allLimit = everyLimit;
      exports.allSeries = everySeries;
      exports.any = some;
      exports.anyLimit = someLimit;
      exports.anySeries = someSeries;
      exports.find = detect;
      exports.findLimit = detectLimit;
      exports.findSeries = detectSeries;
      exports.forEach = eachLimit;
      exports.forEachSeries = eachSeries;
      exports.forEachLimit = eachLimit$1;
      exports.forEachOf = eachOf;
      exports.forEachOfSeries = eachOfSeries;
      exports.forEachOfLimit = eachOfLimit;
      exports.inject = reduce;
      exports.foldl = reduce;
      exports.foldr = reduceRight;
      exports.select = filter;
      exports.selectLimit = filterLimit;
      exports.selectSeries = filterSeries;
      exports.wrapSync = asyncify;
      Object.defineProperty(exports, '__esModule', { value: true });
      exportModule('LYNX.Utils.ThirdParty.async', exports);
    }());
    var toExport;
    if (typeof module !== 'undefined' && module.exports) {
      toExport = require('request');
    } else {
      toExport = function () {
        var XHR = XMLHttpRequest;
        if (!XHR)
          throw new Error('missing XMLHttpRequest');
        request.log = {
          'trace': noop,
          'debug': noop,
          'info': noop,
          'warn': noop,
          'error': noop
        };
        var DEFAULT_TIMEOUT = 3 * 60 * 1000;
        // 3 minutes
        //
        // request
        //
        function request(options, callback) {
          // The entry-point to the API: prep the options object and pass the real work to run_xhr.
          if (typeof callback !== 'function')
            throw new Error('Bad callback given: ' + callback);
          if (!options)
            throw new Error('No options given');
          var options_onResponse = options.onResponse;
          // Save this for later.
          var tmpBody;
          if (options.use_upstream_body) {
            tmpBody = options.body;
          }
          if (typeof options === 'string')
            options = { 'uri': options };
          else
            options = JSON.parse(JSON.stringify(options));
          // Use a duplicate for mutating.
          if (options.use_upstream_body) {
            options.body = tmpBody;
          }
          options.onResponse = options_onResponse;
          // And put it back.
          if (options.verbose)
            request.log = getLogger();
          if (options.url) {
            options.uri = options.url;
            delete options.url;
          }
          if (!options.uri && options.uri !== '')
            throw new Error('options.uri is a required argument');
          if (typeof options.uri != 'string')
            throw new Error('options.uri must be a string');
          var unsupported_options = [
            'proxy',
            '_redirectsFollowed',
            'maxRedirects',
            'followRedirect'
          ];
          for (var i = 0; i < unsupported_options.length; i++)
            if (options[unsupported_options[i]])
              throw new Error('options.' + unsupported_options[i] + ' is not supported');
          options.callback = callback;
          options.method = options.method || 'GET';
          options.headers = options.headers || {};
          options.body = options.body || null;
          options.timeout = options.timeout || request.DEFAULT_TIMEOUT;
          if (options.headers.host)
            throw new Error('Options.headers.host is not supported');
          if (options.json) {
            options.headers.accept = options.headers.accept || 'application/json';
            if (options.method !== 'GET')
              options.headers['content-type'] = 'application/json';
            if (typeof options.json !== 'boolean')
              options.body = JSON.stringify(options.json);
            else if (typeof options.body !== 'string')
              options.body = JSON.stringify(options.body);
          }
          //BEGIN QS Hack
          var serialize = function (obj) {
            var str = [];
            for (var p in obj)
              if (obj.hasOwnProperty(p)) {
                str.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p]));
              }
            return str.join('&');
          };
          if (options.qs) {
            var qs = typeof options.qs == 'string' ? options.qs : serialize(options.qs);
            if (options.uri.indexOf('?') !== -1) {
              //no get params
              options.uri = options.uri + '&' + qs;
            } else {
              //existing get params
              options.uri = options.uri + '?' + qs;
            }
          }
          //END QS Hack
          //BEGIN FORM Hack
          var multipart = function (obj) {
            //todo: support file type (useful?)
            var result = {};
            result.boundry = '-------------------------------' + Math.floor(Math.random() * 1000000000);
            var lines = [];
            for (var p in obj) {
              if (obj.hasOwnProperty(p)) {
                lines.push('--' + result.boundry + '\n' + 'Content-Disposition: form-data; name="' + p + '"' + '\n' + '\n' + obj[p] + '\n');
              }
            }
            lines.push('--' + result.boundry + '--');
            result.body = lines.join('');
            result.length = result.body.length;
            result.type = 'multipart/form-data; boundary=' + result.boundry;
            return result;
          };
          if (options.form) {
            if (typeof options.form == 'string')
              throw 'form name unsupported';
            if (options.method === 'POST') {
              var encoding = (options.encoding || 'application/x-www-form-urlencoded').toLowerCase();
              options.headers['content-type'] = encoding;
              switch (encoding) {
              case 'application/x-www-form-urlencoded':
                options.body = serialize(options.form).replace(/%20/g, '+');
                break;
              case 'multipart/form-data':
                var multi = multipart(options.form);
                //options.headers['content-length'] = multi.length;
                options.body = multi.body;
                options.headers['content-type'] = multi.type;
                break;
              default:
                throw new Error('unsupported encoding:' + encoding);
              }
            }
          }
          //END FORM Hack
          // If onResponse is boolean true, call back immediately when the response is known,
          // not when the full request is complete.
          options.onResponse = options.onResponse || noop;
          if (options.onResponse === true) {
            options.onResponse = callback;
            options.callback = noop;
          }
          // XXX Browsers do not like this.
          //if(options.body)
          //  options.headers['content-length'] = options.body.length;
          // HTTP basic authentication
          if (!options.headers.authorization && options.auth)
            options.headers.authorization = 'Basic ' + b64_enc(options.auth.username + ':' + options.auth.password);
          return run_xhr(options);
        }
        var req_seq = 0;
        function run_xhr(options) {
          var xhr = new XHR(), timed_out = false, is_cors = is_crossDomain(options.uri), supports_cors = 'withCredentials' in xhr;
          req_seq += 1;
          xhr.seq_id = req_seq;
          xhr.id = req_seq + ': ' + options.method + ' ' + options.uri;
          xhr._id = xhr.id;
          // I know I will type "_id" from habit all the time.
          if (is_cors && !supports_cors) {
            var cors_err = new Error('Browser does not support cross-origin request: ' + options.uri);
            cors_err.cors = 'unsupported';
            return options.callback(cors_err, xhr);
          }
          xhr.timeoutTimer = setTimeout(too_late, options.timeout);
          function too_late() {
            timed_out = true;
            var er = new Error('ETIMEDOUT');
            er.code = 'ETIMEDOUT';
            er.duration = options.timeout;
            request.log.error('Timeout', {
              'id': xhr._id,
              'milliseconds': options.timeout
            });
            return options.callback(er, xhr);
          }
          // Some states can be skipped over, so remember what is still incomplete.
          var did = {
            'response': false,
            'loading': false,
            'end': false
          };
          xhr.onreadystatechange = on_state_change;
          if (options.responseType) {
            xhr.responseType = options.responseType;
          }
          xhr.open(options.method, options.uri, true);
          // asynchronous
          if (is_cors)
            xhr.withCredentials = !!options.withCredentials;
          xhr.send(options.body);
          return xhr;
          function on_state_change(event) {
            if (timed_out)
              return request.log.debug('Ignoring timed out state change', {
                'state': xhr.readyState,
                'id': xhr.id
              });
            request.log.debug('State change', {
              'state': xhr.readyState,
              'id': xhr.id,
              'timed_out': timed_out
            });
            if (xhr.readyState === XHR.OPENED) {
              request.log.debug('Request started', { 'id': xhr.id });
              for (var key in options.headers)
                xhr.setRequestHeader(key, options.headers[key]);
            } else if (xhr.readyState === XHR.HEADERS_RECEIVED)
              on_response();
            else if (xhr.readyState === XHR.LOADING) {
              on_response();
              on_loading();
            } else if (xhr.readyState === XHR.DONE) {
              on_response();
              on_loading();
              on_end();
            }
          }
          function on_response() {
            if (did.response)
              return;
            did.response = true;
            request.log.debug('Got response', {
              'id': xhr.id,
              'status': xhr.status
            });
            clearTimeout(xhr.timeoutTimer);
            xhr.statusCode = xhr.status;
            // Node request compatibility
            // Detect failed CORS requests.
            if (is_cors && xhr.statusCode == 0) {
              var cors_err = new Error('CORS request rejected: ' + options.uri);
              cors_err.cors = 'rejected';
              // Do not process this request further.
              did.loading = true;
              did.end = true;
              return options.callback(cors_err, xhr);
            }
            options.onResponse(null, xhr);
          }
          function on_loading() {
            if (did.loading)
              return;
            did.loading = true;
            request.log.debug('Response body loading', { 'id': xhr.id })  // TODO: Maybe simulate "data" events by watching xhr.responseText
    ;
          }
          function on_end() {
            if (did.end)
              return;
            did.end = true;
            request.log.debug('Request done', { 'id': xhr.id });
            if (xhr.responseType === '') {
              xhr.body = xhr.responseText;
            }
            if (options.json) {
              try {
                xhr.body = JSON.parse(xhr.responseText);
              } catch (er) {
                return options.callback(er, xhr);
              }
            }
            options.callback(null, xhr, xhr.body);
          }
        }
        // request
        request.withCredentials = false;
        request.DEFAULT_TIMEOUT = DEFAULT_TIMEOUT;
        //
        // defaults
        //
        request.defaults = function (options, requester) {
          var def = function (method) {
            var d = function (params, callback) {
              if (typeof params === 'string')
                params = { 'uri': params };
              else {
                params = JSON.parse(JSON.stringify(params));
              }
              for (var i in options) {
                if (params[i] === undefined)
                  params[i] = options[i];
              }
              return method(params, callback);
            };
            return d;
          };
          var de = def(request);
          de.get = def(request.get);
          de.post = def(request.post);
          de.put = def(request.put);
          de.head = def(request.head);
          return de;
        };
        //
        // HTTP method shortcuts
        //
        var shortcuts = [
          'get',
          'put',
          'post',
          'head'
        ];
        shortcuts.forEach(function (shortcut) {
          var method = shortcut.toUpperCase();
          var func = shortcut.toLowerCase();
          request[func] = function (opts) {
            if (typeof opts === 'string')
              opts = {
                'method': method,
                'uri': opts
              };
            else {
              opts = JSON.parse(JSON.stringify(opts));
              opts.method = method;
            }
            var args = [opts].concat(Array.prototype.slice.apply(arguments, [1]));
            return request.apply(this, args);
          };
        });
        //
        // CouchDB shortcut
        //
        request.couch = function (options, callback) {
          if (typeof options === 'string')
            options = { 'uri': options };
          // Just use the request API to do JSON.
          options.json = true;
          if (options.body)
            options.json = options.body;
          delete options.body;
          callback = callback || noop;
          var xhr = request(options, couch_handler);
          return xhr;
          function couch_handler(er, resp, body) {
            if (er)
              return callback(er, resp, body);
            if ((resp.statusCode < 200 || resp.statusCode > 299) && body.error) {
              // The body is a Couch JSON object indicating the error.
              er = new Error('CouchDB error: ' + (body.error.reason || body.error.error));
              for (var key in body)
                er[key] = body[key];
              return callback(er, resp, body);
            }
            return callback(er, resp, body);
          }
        };
        //
        // Utility
        //
        function noop() {
        }
        function getLogger() {
          var logger = {}, levels = [
              'trace',
              'debug',
              'info',
              'warn',
              'error'
            ], level, i;
          for (i = 0; i < levels.length; i++) {
            level = levels[i];
            logger[level] = noop;
            if (typeof console !== 'undefined' && console && console[level])
              logger[level] = formatted(console, level);
          }
          return logger;
        }
        function formatted(obj, method) {
          return formatted_logger;
          function formatted_logger(str, context) {
            if (typeof context === 'object')
              str += ' ' + JSON.stringify(context);
            return obj[method].call(obj, str);
          }
        }
        // Return whether a URL is a cross-domain request.
        function is_crossDomain(url) {
          var rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/;
          // jQuery #8138, IE may throw an exception when accessing
          // a field from window.location if document.domain has been set
          var ajaxLocation;
          try {
            ajaxLocation = location.href;
          } catch (e) {
            // Use the href attribute of an A element since IE will modify it given document.location
            ajaxLocation = document.createElement('a');
            ajaxLocation.href = '';
            ajaxLocation = ajaxLocation.href;
          }
          var ajaxLocParts = rurl.exec(ajaxLocation.toLowerCase()) || [], parts = rurl.exec(url.toLowerCase());
          var result = !!(parts && (parts[1] != ajaxLocParts[1] || parts[2] != ajaxLocParts[2] || (parts[3] || (parts[1] === 'http:' ? 80 : 443)) != (ajaxLocParts[3] || (ajaxLocParts[1] === 'http:' ? 80 : 443))));
          //console.debug('is_crossDomain('+url+') -> ' + result)
          return result;
        }
        // MIT License from http://phpjs.org/functions/base64_encode:358
        function b64_enc(data) {
          // Encodes string using MIME base64 algorithm
          var b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
          var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, enc = '', tmp_arr = [];
          if (!data) {
            return data;
          }
          // assume utf8 data
          // data = this.utf8_encode(data+'');
          do {
            // pack three octets into four hexets
            o1 = data.charCodeAt(i++);
            o2 = data.charCodeAt(i++);
            o3 = data.charCodeAt(i++);
            bits = o1 << 16 | o2 << 8 | o3;
            h1 = bits >> 18 & 63;
            h2 = bits >> 12 & 63;
            h3 = bits >> 6 & 63;
            h4 = bits & 63;
            // use hexets to index into b64, and append result to encoded string
            tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
          } while (i < data.length);
          enc = tmp_arr.join('');
          switch (data.length % 3) {
          case 1:
            enc = enc.slice(0, -2) + '==';
            break;
          case 2:
            enc = enc.slice(0, -1) + '=';
            break;
          }
          return enc;
        }
        return request;
      }();
    }
    exportModule('LYNX.Utils.ThirdParty.request', toExport);
    (function () {
      /**
       * Class extending Error with HTTP-specific error information like statusCode and statusMessage
       * @param {string} message The error message
       * @param {number} statusCode A numeric HTTP status code
       * @param {string} statusMessage A string message representing the response status message
       * @constructor
       * @alias LYNX.Utils.HTTPError
       */
      var HTTPError = function (message, statusCode, statusMessage) {
        this.name = 'HTTPError';
        this.message = message;
        this.statusCode = statusCode;
        this.statusMessage = statusMessage;
        this.stack = new Error().stack;
        Error.call(this);
      };
      HTTPError.prototype = Object.create(Error.prototype);
      /**
       * Returns a string representing the HTTPError object
       * @return {string} a string representing the HTTPError object
       */
      HTTPError.prototype.toString = function () {
        var name = this.name;
        var msg = this.message;
        msg = msg === undefined ? '' : String(msg);
        var statusCode = this.statusCode;
        statusCode = statusCode === undefined ? '' : String(statusCode);
        var statusMessage = this.statusMessage;
        statusMessage = statusMessage === undefined ? '' : String(statusMessage);
        var stack = this.stack;
        stack = stack === undefined ? '' : String(stack);
        return name + ': ' + msg + ', status code=' + statusCode + ', status message:' + statusMessage + ', stack:' + stack;
      };
      exportModule('LYNX.Utils.HTTPError', HTTPError);
    }());
    (function () {
      var _ = include('underscore');
      var async = include('LYNX.Utils.ThirdParty.async');
      var request = include('LYNX.Utils.ThirdParty.request');
      var HTTPError = include('LYNX.Utils.HTTPError');
      var RequestUtils = {};
      // Pre-compile a regexp
      RequestUtils.httpStatusRegex = new RegExp('HTTP Status \\[(\\d+)\\]');
      /**
       * Process the request error and turn it into a Error object
       * @param {Error|string|undefined} in_error Error object returned by the request
       * @param {object} in_response Response object (may contain errors)
       * @return {Error} The Error object returned from building the request error
       */
      RequestUtils.buildErrorMessage = function (in_error, in_response) {
        if (in_error) {
          if (_.isString(in_error)) {
            return new HTTPError(in_error, in_response && in_response.statusCode, in_response && in_response.statusMessage);
          } else
            return in_error;
        } else if (in_response.body && in_response.body.errors) {
          var errorMessages = _.map(in_response.body.errors, function (err) {
            return err.message;
          }).join('\n');
          return new HTTPError(errorMessages, in_response && in_response.statusCode, in_response && in_response.statusMessage);
        } else if (in_response.statusCode < 200 || in_response.statusCode > 399) {
          // Allowing redirects...
          var errorMsg = 'HTTP Status [' + in_response.statusCode + ']';
          if (in_response.body) {
            errorMsg += '. Error: ';
            if (typeof in_response.body === 'string') {
              errorMsg += in_response.body;
            } else {
              errorMsg += JSON.stringify(in_response.body);
            }
          }
          return new HTTPError(errorMsg, in_response.statusCode, in_response.statusMessage);
        }
        return null;  // No error
      };
      /**
       * Handle REST request callback which passes only body to callback
       * @param {function(?Error, ?body)} in_callback Callback to invoke
       * @param {Error|string|undefined} in_error Error object returned by the request
       * @param {object} in_response Response object (may contain errors)
       */
      RequestUtils.handleRequestCallback = function (in_callback, in_error, in_response) {
        in_callback(RequestUtils.buildErrorMessage(in_error, in_response), in_response && in_response.body);
      };
      /**
       * Handle REST request callback which passes both body and response object to callback
       * @param {function(?Error, ?body, ?response)} in_callback Callback to invoke
       * @param {Error|string|undefined} in_error Error object returned by the request
       * @param {object} in_response Response object (may contain errors)
       */
      RequestUtils.handleRequestCallbackWithResponse = function (in_callback, in_error, in_response) {
        in_callback(RequestUtils.buildErrorMessage(in_error, in_response), in_response && in_response.body, in_response);
      };
      /**
       * Retry request with exponential backoff
       * @param {object} in_params Input parameters
       * @param {number=} in_params.retries Number of retries to attempt before failure (default: 10)
       * @param {boolean=} in_params.useExponentialBackoff Use exponential or fixed backoff (default: true)
       * @param {number=} in_params.initalRetryInterval First retry interval (default: 100)
       * @param {object} in_params.requestParams Parameters to pass to request function
       * @param {array=}  in_params.retryStatusCodes Array of HTTP status codes to retry (default: [502, 503, 504])
       * @param {array=}  in_params.retryErrorStrings Array of strings to search in Error message.  If found, retry.
       *   (default: [ 'ECONNREFUSED' ])
       * @param {string=} in_params.logMessage message to print on each error (default: message with url)
       * @param {function=} in_params.logger function to invoke to log error message (default: console.log)
       * @param {function=} in_params.requestCallbackHandler function called with request callback, error,
       *   and response parameters.  Defaults to RequestUtils.handleRequestCallback
       * @param {function(?Error, ?body)} in_callback Callback to invoke with Error or body
       */
      RequestUtils.requestWithRetries = function (in_params, in_callback) {
        console.assert(in_params.requestParams, '[RequestUtils.requestWithRetries] in_params.requestParams must be supplied.');
        if (typeof in_params.useExponentialBackoff !== 'boolean')
          in_params.useExponentialBackoff = true;
        in_params.retryStatusCodes = in_params.retryStatusCodes || [
          502,
          503,
          504
        ];
        in_params.retryErrorStrings = in_params.retryErrorStrings || [
          'ECONNREFUSED',
          'Parse Error'
        ];
        in_params.initialRetryInterval = in_params.initialRetryInterval || 100;
        in_params.logger = in_params.logger || console.log;
        in_params.logMessage = in_params.logMessage || 'Error requesting ' + in_params.requestParams.url + ' ';
        in_params.requestCallbackHandler = in_params.requestCallbackHandler || RequestUtils.handleRequestCallback;
        // Retry request up to in_params.retries times with exponential backoff
        // but chooses a random time between 0 and the exponential backoff interval
        // (i.e. intervals of 100, 200, 400, 800, 1600, ... milliseconds)
        // See http://caolan.github.io/async/docs.html#retry
        async.retry(// Retry options
        {
          times: in_params.retries || 10,
          // Calculate exponential interval, but choose random time between 0 and max interval
          interval: function (retryCount) {
            var exponent = in_params.useExponentialBackoff ? Math.pow(2, retryCount - 1) : 1;
            return Math.floor(Math.random() * in_params.initialRetryInterval * exponent);
          },
          errorFilter: function (err) {
            // Log error message
            if (in_params.logMessage && in_params.logger && typeof in_params.logger === 'function') {
              in_params.logger(in_params.logMessage + err.message);
            }
            // Find error status and check if its in the retry status code list
            if (Array.isArray(in_params.retryStatusCodes)) {
              var results = err.message.match(RequestUtils.httpStatusRegex);
              if (results) {
                var httpStatus = parseInt(results[1], 10);
                if (in_params.retryStatusCodes.indexOf(httpStatus) !== -1) {
                  // Retry
                  return true;
                }
              }
            }
            // Search error message string for retry error strings
            if (Array.isArray(in_params.retryErrorStrings) && _.find(in_params.retryErrorStrings, function (item) {
                return err.message.includes(item);
              })) {
              return true;
            }
            // Do not retry
            return false;
          }
        }, // Task to execute
        function (callback, results) {
          request(in_params.requestParams, in_params.requestCallbackHandler.bind(null, callback));
        }, // Callback called after success or final retry
        in_callback);
      };
      exportModule('LYNX.Utils.RequestUtils', RequestUtils);
    }());
    var CollaborationMessage = function (params) {
      this.room = params.room;
      this.event = params.event;
      this.data = params.data;
    };
    exportModule('LYNX.CollaborationServerAPI.CollaborationMessage', CollaborationMessage);
    var CONSTANTS = {
      CHANNELS: {
        JOIN: 'join',
        ONJOIN: 'onjoin',
        LEAVE: 'leave',
        ONLEAVE: 'onleave',
        BROADCAST: 'broadcast',
        EMIT: 'emit',
        COLLABORATION_SERVER: 'collaboration_server',
        SETUP: 'setup'
      },
      COLLABORATOR_TYPES: {
        // This is a robot collaborator. A robot is simply doing "work" requested by the human user.
        // Robot collaborators are automatically removed from a channel
        // if there are no other human collaborators in the session.
        ROBOT: 'robot',
        // This is a human collaborator.
        HUMAN: 'human',
        // A robot that acts on behalf of the human user.
        // To the eyes of the collaboration server, this is a human user in the session and as long as it is present,
        // the server will not kick any robots out of the channel even when no other human collaborators are present.
        ANDROID: 'android'
      }
    };
    exportModule('LYNX.CollaborationServerAPI.CONSTANTS', CONSTANTS);
    (function () {
      /**
       * @description A listen-only socket object
       * @param {socket} socket to listen
       */
      var SocketListener = function (socket) {
        this._socket = socket;
      };
      SocketListener.prototype.on = function () {
        this._socket.on.apply(this._socket, arguments);
      };
      SocketListener.prototype.addEventListener = SocketListener.prototype.on;
      SocketListener.prototype.addListener = SocketListener.prototype.on;
      SocketListener.prototype.registerToEvent = SocketListener.prototype.on;
      SocketListener.prototype.once = function () {
        this._socket.once.apply(this._socket, arguments);
      };
      SocketListener.prototype.off = function () {
        this._socket.off.apply(this._socket, arguments);
      };
      SocketListener.prototype.removeEventListener = SocketListener.prototype.off;
      SocketListener.prototype.removeListener = SocketListener.prototype.off;
      SocketListener.prototype.unregisterFromEvent = SocketListener.prototype.off;
      SocketListener.prototype.listeners = function () {
        return this._socket.listeners.apply(this._socket, arguments);
      };
      SocketListener.prototype.listenerCount = function (eventName) {
        return this.listeners(eventName).length;
      };
      SocketListener.prototype.removeAllListeners = function (eventName) {
        var listeners = this.listeners(eventName);
        var that = this;
        listeners.forEach(function (listener) {
          that._socket.removeListener(eventName, listener);
        });
      };
      exportModule('LYNX.CollaborationClient.SocketListener', SocketListener);
    }());
    (function () {
      var _ = include('underscore');
      var CHANNELS = include('LYNX.CollaborationServerAPI.CONSTANTS').CHANNELS;
      /**
       * A class that keeps track of all of the other collaborators in each room that your collaborator
       * occupies
       * @private
       * @alias LYNX.CollaborationClient.CollaboratorManager
       * @constructor
       */
      var CollaboratorManager = function () {
        /**
         * A mapping from roomName -> collaboratorGuid -> Collaborator
         * @type {Object}
         */
        this._rooms = {};
      };
      /**
       * @param {Socket} socket
       */
      CollaboratorManager.prototype.setSocket = function (socket) {
        var that = this;
        socket.on(CHANNELS.ONJOIN, function (message) {
          that.addCollaborator(message.room, message.data.collaborator);
        });
        socket.on(CHANNELS.ONLEAVE, function (message) {
          that._removeCollaborator(message.room, message.data.collaborator);
        });
      };
      /**
       * Reset the state of this object
       */
      CollaboratorManager.prototype.reset = function () {
        this._rooms = {};
      };
      /**
       * @return {Array<LYNX.CollaborationClient.Room>} The rooms that we're connected to
       */
      CollaboratorManager.prototype.getRooms = function () {
        return _.values(this._rooms);
      };
      /**
       * Add a new room to track
       * @param {LYNX.CollaborationClient.Room} room
       */
      CollaboratorManager.prototype.addRoom = function (room) {
        this._rooms[room.name] = room;
      };
      /**
       * Remove a room from tracking
       * @param {string} roomName
       */
      CollaboratorManager.prototype.leaveRoom = function (roomName) {
        delete this._rooms[roomName];
      };
      /**
       * Is this user in the given room?
       * @param {string} roomName
       */
      CollaboratorManager.prototype.hasJoined = function (roomName) {
        return !!this._rooms[roomName];
      };
      /**
       * @param {string} roomName
       * @param {object} collaborator - See Collaborator
       */
      CollaboratorManager.prototype.addCollaborator = function (roomName, collaborator) {
        console.assert(_.isString(roomName));
        return this._rooms[roomName].addCollaborator(collaborator);
      };
      /**
       * @param {string} roomName
       * @param {object} collaborator - See Collaborator
       */
      CollaboratorManager.prototype._removeCollaborator = function (roomName, collaborator) {
        console.assert(_.isString(roomName));
        this._rooms[roomName].removeCollaborator(collaborator);
      };
      /**
       * @param {string} roomName
       * @param  {string} guid The collaborator guid
       * @return {Collaborator|undefined}
       */
      CollaboratorManager.prototype.getCollaborator = function (roomName, guid) {
        console.assert(_.isString(roomName), 'roomName must be a string: ' + roomName);
        var room = this._rooms[roomName];
        if (room) {
          return room.getCollaborator(guid);
        } else {
          console.warn('Room not found: ' + roomName);
          return undefined;
        }
      };
      /**
       * @param {string} roomName
       * @return {Array<Collaborator>}
       */
      CollaboratorManager.prototype.getCollaborators = function (roomName) {
        console.assert(_.isString(roomName), 'roomName must be a string: ' + roomName);
        var room = this._rooms[roomName];
        if (room) {
          return room.getCollaborators();
        } else {
          console.warn('Room not found: ' + roomName);
          return [];
        }
      };
      exportModule('LYNX.CollaborationClient.CollaboratorManager', CollaboratorManager);
    }());
    (function () {
      var _ = include('underscore');
      /**
       * Objects of this class represent a collaborator (human, android or robot) in a room with you.
       * TODO: The same collaborator may exist in the multiple rooms with you.
       * Right now there will be multiple instances of this class for the same user in each of those rooms.
       * @param {string} roomName
       * @param {object} collaboratorJSON
       * @param {object} collaboratorJSON.guid - A unique globally unique ID for the Collaborator
       * @param {object} collaboratorJSON.type - human, android or robot
       * @param {object} collaboratorJSON.id - A non-changing ID for the specific Collaborator. Should be unique per-room.
       * @param {object} collaboratorJSON.friendlyName - A nice friendly name for display purposes. No uniqueness constraint
       * @alias LYNX.CollaborationClient.Collaborator
       * @constructor
       * @private
       */
      var Collaborator = function (roomName, collaboratorJSON) {
        console.assert(_.isString(roomName), 'roomName must be a string' + roomName);
        console.assert(_.isString(collaboratorJSON.guid), 'guid must be a string' + collaboratorJSON.guid);
        this.roomName = roomName;
        this.guid = collaboratorJSON.guid;
        this.type = collaboratorJSON.type;
        this.id = collaboratorJSON.id;
        this.friendlyName = collaboratorJSON.friendlyName;
      };
      exportModule('LYNX.CollaborationClient.Collaborator', Collaborator);
    }());
    (function () {
      var Collaborator = include('LYNX.CollaborationClient.Collaborator');
      var _ = include('underscore');
      /**
       * A state object for the rooms that a client joins
       * @param {string} name
       * @param {object=} options
       * @param {Array<object>=} collaborators
       * @alias LYNX.CollaborationClient.Room
       * @private
       * @constructor
       */
      var Room = function (name, options, collaborators) {
        console.assert(_.isString(name), 'Room name must be a string: ' + name);
        this.name = name;
        this.options = options;
        this._collaborators = {};
        var that = this;
        if (collaborators) {
          _.each(collaborators, function (collaborator) {
            that.addCollaborator(collaborator);
          });
        }
      };
      /**
       * Add a collaborator to track
       * @param {object} collaborator - See Collaborator
       */
      Room.prototype.addCollaborator = function (collaborator) {
        this._collaborators[collaborator.guid] = new Collaborator(this.name, collaborator);
      };
      /**
       * @param  {object} collaborator
       * @param  {string} collaborator.guid
       */
      Room.prototype.removeCollaborator = function (collaborator) {
        delete this._collaborators[collaborator.guid];
      };
      /**
       * @param  {string} guid
       * @return {Collaborator}
       */
      Room.prototype.getCollaborator = function (guid) {
        console.assert(_.isString(guid));
        return this._collaborators[guid];
      };
      /**
       * @return {Array<Collaborator>}
       */
      Room.prototype.getCollaborators = function () {
        return _.values(this._collaborators);
      };
      exportModule('LYNX.CollaborationClient.Room', Room);
    }());
    (function () {
      var chalk;
      if (typeof require !== 'undefined') {
        chalk = require('chalk');
      } else {
        var noColorFn = function (string) {
          return string;
        };
        var noStyle = {
          open: '',
          close: ''
        };
        chalk = {
          supportsColor: false,
          stripColor: noColorFn,
          dim: noColorFn,
          reset: noColorFn,
          bold: noColorFn,
          italic: noColorFn,
          underline: noColorFn,
          inverse: noColorFn,
          hidden: noColorFn,
          strikethrough: noColorFn,
          green: noColorFn,
          cyan: noColorFn,
          blue: noColorFn,
          yellow: noColorFn,
          red: noColorFn,
          black: noColorFn,
          magenta: noColorFn,
          white: noColorFn,
          gray: noColorFn,
          bgBlack: noColorFn,
          bgRed: noColorFn,
          bgGreen: noColorFn,
          bgYellow: noColorFn,
          bgBlue: noColorFn,
          bgMagenta: noColorFn,
          bgCyan: noColorFn,
          bgWhite: noColorFn,
          styles: {
            dim: noStyle,
            reset: noStyle,
            bold: noStyle,
            italic: noStyle,
            underline: noStyle,
            inverse: noStyle,
            hidden: noStyle,
            strikethrough: noStyle,
            green: noStyle,
            cyan: noStyle,
            blue: noStyle,
            yellow: noStyle,
            red: noStyle,
            black: noStyle,
            magenta: noStyle,
            white: noStyle,
            gray: noStyle,
            bgBlack: noStyle,
            bgRed: noStyle,
            bgGreen: noStyle,
            bgYellow: noStyle,
            bgBlue: noStyle,
            bgMagenta: noStyle,
            bgCyan: noStyle,
            bgWhite: noStyle
          }
        };
      }
      exportModule('LYNX.Logger.ThirdParty.chalk', chalk);
    }());
    (function e(t, n, r) {
      function s(o, u) {
        if (!n[o]) {
          if (!t[o]) {
            var a = typeof require == 'function' && require;
            if (!u && a)
              return a(o, !0);
            if (i)
              return i(o, !0);
            var f = new Error('Cannot find module \'' + o + '\'');
            throw f.code = 'MODULE_NOT_FOUND', f;
          }
          var l = n[o] = { exports: {} };
          t[o][0].call(l.exports, function (e) {
            var n = t[o][1][e];
            return s(n ? n : e);
          }, l, l.exports, e, t, n, r);
        }
        return n[o].exports;
      }
      var i = typeof require == 'function' && require;
      for (var o = 0; o < r.length; o++)
        s(r[o]);
      return s;
    }({
      1: [
        function (require, module, exports) {
        },
        {}
      ],
      2: [
        function (require, module, exports) {
          // shim for using process in browser
          var process = module.exports = {};
          // cached from whatever global is present so that test runners that stub it
          // don't break things.  But we need to wrap it in a try catch in case it is
          // wrapped in strict mode code which doesn't define any globals.  It's inside a
          // function because try/catches deoptimize in certain engines.
          var cachedSetTimeout;
          var cachedClearTimeout;
          function defaultSetTimout() {
            throw new Error('setTimeout has not been defined');
          }
          function defaultClearTimeout() {
            throw new Error('clearTimeout has not been defined');
          }
          (function () {
            try {
              if (typeof setTimeout === 'function') {
                cachedSetTimeout = setTimeout;
              } else {
                cachedSetTimeout = defaultSetTimout;
              }
            } catch (e) {
              cachedSetTimeout = defaultSetTimout;
            }
            try {
              if (typeof clearTimeout === 'function') {
                cachedClearTimeout = clearTimeout;
              } else {
                cachedClearTimeout = defaultClearTimeout;
              }
            } catch (e) {
              cachedClearTimeout = defaultClearTimeout;
            }
          }());
          function runTimeout(fun) {
            if (cachedSetTimeout === setTimeout) {
              //normal enviroments in sane situations
              return setTimeout(fun, 0);
            }
            // if setTimeout wasn't available but was latter defined
            if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
              cachedSetTimeout = setTimeout;
              return setTimeout(fun, 0);
            }
            try {
              // when when somebody has screwed with setTimeout but no I.E. maddness
              return cachedSetTimeout(fun, 0);
            } catch (e) {
              try {
                // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
                return cachedSetTimeout.call(null, fun, 0);
              } catch (e) {
                // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
                return cachedSetTimeout.call(this, fun, 0);
              }
            }
          }
          function runClearTimeout(marker) {
            if (cachedClearTimeout === clearTimeout) {
              //normal enviroments in sane situations
              return clearTimeout(marker);
            }
            // if clearTimeout wasn't available but was latter defined
            if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
              cachedClearTimeout = clearTimeout;
              return clearTimeout(marker);
            }
            try {
              // when when somebody has screwed with setTimeout but no I.E. maddness
              return cachedClearTimeout(marker);
            } catch (e) {
              try {
                // When we are in I.E. but the script has been evaled so I.E. doesn't  trust the global object when called normally
                return cachedClearTimeout.call(null, marker);
              } catch (e) {
                // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
                // Some versions of I.E. have different rules for clearTimeout vs setTimeout
                return cachedClearTimeout.call(this, marker);
              }
            }
          }
          var queue = [];
          var draining = false;
          var currentQueue;
          var queueIndex = -1;
          function cleanUpNextTick() {
            if (!draining || !currentQueue) {
              return;
            }
            draining = false;
            if (currentQueue.length) {
              queue = currentQueue.concat(queue);
            } else {
              queueIndex = -1;
            }
            if (queue.length) {
              drainQueue();
            }
          }
          function drainQueue() {
            if (draining) {
              return;
            }
            var timeout = runTimeout(cleanUpNextTick);
            draining = true;
            var len = queue.length;
            while (len) {
              currentQueue = queue;
              queue = [];
              while (++queueIndex < len) {
                if (currentQueue) {
                  currentQueue[queueIndex].run();
                }
              }
              queueIndex = -1;
              len = queue.length;
            }
            currentQueue = null;
            draining = false;
            runClearTimeout(timeout);
          }
          process.nextTick = function (fun) {
            var args = new Array(arguments.length - 1);
            if (arguments.length > 1) {
              for (var i = 1; i < arguments.length; i++) {
                args[i - 1] = arguments[i];
              }
            }
            queue.push(new Item(fun, args));
            if (queue.length === 1 && !draining) {
              runTimeout(drainQueue);
            }
          };
          // v8 likes predictible objects
          function Item(fun, array) {
            this.fun = fun;
            this.array = array;
          }
          Item.prototype.run = function () {
            this.fun.apply(null, this.array);
          };
          process.title = 'browser';
          process.browser = true;
          process.env = {};
          process.argv = [];
          process.version = '';
          // empty string to avoid regexp issues
          process.versions = {};
          function noop() {
          }
          process.on = noop;
          process.addListener = noop;
          process.once = noop;
          process.off = noop;
          process.removeListener = noop;
          process.removeAllListeners = noop;
          process.emit = noop;
          process.binding = function (name) {
            throw new Error('process.binding is not supported');
          };
          process.cwd = function () {
            return '/';
          };
          process.chdir = function (dir) {
            throw new Error('process.chdir is not supported');
          };
          process.umask = function () {
            return 0;
          };
        },
        {}
      ],
      3: [
        function (require, module, exports) {
          if (typeof Object.create === 'function') {
            // implementation from standard node.js 'util' module
            module.exports = function inherits(ctor, superCtor) {
              ctor.super_ = superCtor;
              ctor.prototype = Object.create(superCtor.prototype, {
                constructor: {
                  value: ctor,
                  enumerable: false,
                  writable: true,
                  configurable: true
                }
              });
            };
          } else {
            // old school shim for old browsers
            module.exports = function inherits(ctor, superCtor) {
              ctor.super_ = superCtor;
              var TempCtor = function () {
              };
              TempCtor.prototype = superCtor.prototype;
              ctor.prototype = new TempCtor();
              ctor.prototype.constructor = ctor;
            };
          }
        },
        {}
      ],
      4: [
        function (require, module, exports) {
          module.exports = function isBuffer(arg) {
            return arg && typeof arg === 'object' && typeof arg.copy === 'function' && typeof arg.fill === 'function' && typeof arg.readUInt8 === 'function';
          };
        },
        {}
      ],
      5: [
        function (require, module, exports) {
          (function (process, global) {
            // Copyright Joyent, Inc. and other Node contributors.
            //
            // Permission is hereby granted, free of charge, to any person obtaining a
            // copy of this software and associated documentation files (the
            // "Software"), to deal in the Software without restriction, including
            // without limitation the rights to use, copy, modify, merge, publish,
            // distribute, sublicense, and/or sell copies of the Software, and to permit
            // persons to whom the Software is furnished to do so, subject to the
            // following conditions:
            //
            // The above copyright notice and this permission notice shall be included
            // in all copies or substantial portions of the Software.
            //
            // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
            // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
            // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
            // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
            // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
            // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
            // USE OR OTHER DEALINGS IN THE SOFTWARE.
            var formatRegExp = /%[sdj%]/g;
            exports.format = function (f) {
              if (!isString(f)) {
                var objects = [];
                for (var i = 0; i < arguments.length; i++) {
                  objects.push(inspect(arguments[i]));
                }
                return objects.join(' ');
              }
              var i = 1;
              var args = arguments;
              var len = args.length;
              var str = String(f).replace(formatRegExp, function (x) {
                if (x === '%%')
                  return '%';
                if (i >= len)
                  return x;
                switch (x) {
                case '%s':
                  return String(args[i++]);
                case '%d':
                  return Number(args[i++]);
                case '%j':
                  try {
                    return JSON.stringify(args[i++]);
                  } catch (_) {
                    return '[Circular]';
                  }
                default:
                  return x;
                }
              });
              for (var x = args[i]; i < len; x = args[++i]) {
                if (isNull(x) || !isObject(x)) {
                  str += ' ' + x;
                } else {
                  str += ' ' + inspect(x);
                }
              }
              return str;
            };
            // Mark that a method should not be used.
            // Returns a modified function which warns once by default.
            // If --no-deprecation is set, then it is a no-op.
            exports.deprecate = function (fn, msg) {
              // Allow for deprecating things in the process of starting up.
              if (isUndefined(global.process)) {
                return function () {
                  return exports.deprecate(fn, msg).apply(this, arguments);
                };
              }
              if (process.noDeprecation === true) {
                return fn;
              }
              var warned = false;
              function deprecated() {
                if (!warned) {
                  if (process.throwDeprecation) {
                    throw new Error(msg);
                  } else if (process.traceDeprecation) {
                    console.trace(msg);
                  } else {
                    console.error(msg);
                  }
                  warned = true;
                }
                return fn.apply(this, arguments);
              }
              return deprecated;
            };
            var debugs = {};
            var debugEnviron;
            exports.debuglog = function (set) {
              if (isUndefined(debugEnviron))
                debugEnviron = process.env.NODE_DEBUG || '';
              set = set.toUpperCase();
              if (!debugs[set]) {
                if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
                  var pid = process.pid;
                  debugs[set] = function () {
                    var msg = exports.format.apply(exports, arguments);
                    console.error('%s %d: %s', set, pid, msg);
                  };
                } else {
                  debugs[set] = function () {
                  };
                }
              }
              return debugs[set];
            };
            /**
             * Echos the value of a value. Trys to print the value out
             * in the best way possible given the different types.
             *
             * @param {Object} obj The object to print out.
             * @param {Object} opts Optional options object that alters the output.
             */
            /* legacy: obj, showHidden, depth, colors*/
            function inspect(obj, opts) {
              // default options
              var ctx = {
                seen: [],
                stylize: stylizeNoColor
              };
              // legacy...
              if (arguments.length >= 3)
                ctx.depth = arguments[2];
              if (arguments.length >= 4)
                ctx.colors = arguments[3];
              if (isBoolean(opts)) {
                // legacy...
                ctx.showHidden = opts;
              } else if (opts) {
                // got an "options" object
                exports._extend(ctx, opts);
              }
              // set default options
              if (isUndefined(ctx.showHidden))
                ctx.showHidden = false;
              if (isUndefined(ctx.depth))
                ctx.depth = 2;
              if (isUndefined(ctx.colors))
                ctx.colors = false;
              if (isUndefined(ctx.customInspect))
                ctx.customInspect = true;
              if (ctx.colors)
                ctx.stylize = stylizeWithColor;
              return formatValue(ctx, obj, ctx.depth);
            }
            exports.inspect = inspect;
            // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
            inspect.colors = {
              'bold': [
                1,
                22
              ],
              'italic': [
                3,
                23
              ],
              'underline': [
                4,
                24
              ],
              'inverse': [
                7,
                27
              ],
              'white': [
                37,
                39
              ],
              'grey': [
                90,
                39
              ],
              'black': [
                30,
                39
              ],
              'blue': [
                34,
                39
              ],
              'cyan': [
                36,
                39
              ],
              'green': [
                32,
                39
              ],
              'magenta': [
                35,
                39
              ],
              'red': [
                31,
                39
              ],
              'yellow': [
                33,
                39
              ]
            };
            // Don't use 'blue' not visible on cmd.exe
            inspect.styles = {
              'special': 'cyan',
              'number': 'yellow',
              'boolean': 'yellow',
              'undefined': 'grey',
              'null': 'bold',
              'string': 'green',
              'date': 'magenta',
              // "name": intentionally not styling
              'regexp': 'red'
            };
            function stylizeWithColor(str, styleType) {
              var style = inspect.styles[styleType];
              if (style) {
                return '\x1B[' + inspect.colors[style][0] + 'm' + str + '\x1B[' + inspect.colors[style][1] + 'm';
              } else {
                return str;
              }
            }
            function stylizeNoColor(str, styleType) {
              return str;
            }
            function arrayToHash(array) {
              var hash = {};
              array.forEach(function (val, idx) {
                hash[val] = true;
              });
              return hash;
            }
            function formatValue(ctx, value, recurseTimes) {
              // Provide a hook for user-specified inspect functions.
              // Check that value is an object with an inspect function on it
              if (ctx.customInspect && value && isFunction(value.inspect) && // Filter out the util module, it's inspect function is special
                value.inspect !== exports.inspect && // Also filter out any prototype objects using the circular check.
                !(value.constructor && value.constructor.prototype === value)) {
                var ret = value.inspect(recurseTimes, ctx);
                if (!isString(ret)) {
                  ret = formatValue(ctx, ret, recurseTimes);
                }
                return ret;
              }
              // Primitive types cannot have properties
              var primitive = formatPrimitive(ctx, value);
              if (primitive) {
                return primitive;
              }
              // Look up the keys of the object.
              var keys = Object.keys(value);
              var visibleKeys = arrayToHash(keys);
              if (ctx.showHidden) {
                keys = Object.getOwnPropertyNames(value);
              }
              // IE doesn't make error fields non-enumerable
              // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx
              if (isError(value) && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) {
                return formatError(value);
              }
              // Some type of object without properties can be shortcutted.
              if (keys.length === 0) {
                if (isFunction(value)) {
                  var name = value.name ? ': ' + value.name : '';
                  return ctx.stylize('[Function' + name + ']', 'special');
                }
                if (isRegExp(value)) {
                  return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
                }
                if (isDate(value)) {
                  return ctx.stylize(Date.prototype.toString.call(value), 'date');
                }
                if (isError(value)) {
                  return formatError(value);
                }
              }
              var base = '', array = false, braces = [
                  '{',
                  '}'
                ];
              // Make Array say that they are Array
              if (isArray(value)) {
                array = true;
                braces = [
                  '[',
                  ']'
                ];
              }
              // Make functions say that they are functions
              if (isFunction(value)) {
                var n = value.name ? ': ' + value.name : '';
                base = ' [Function' + n + ']';
              }
              // Make RegExps say that they are RegExps
              if (isRegExp(value)) {
                base = ' ' + RegExp.prototype.toString.call(value);
              }
              // Make dates with properties first say the date
              if (isDate(value)) {
                base = ' ' + Date.prototype.toUTCString.call(value);
              }
              // Make error with message first say the error
              if (isError(value)) {
                base = ' ' + formatError(value);
              }
              if (keys.length === 0 && (!array || value.length == 0)) {
                return braces[0] + base + braces[1];
              }
              if (recurseTimes < 0) {
                if (isRegExp(value)) {
                  return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
                } else {
                  return ctx.stylize('[Object]', 'special');
                }
              }
              ctx.seen.push(value);
              var output;
              if (array) {
                output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
              } else {
                output = keys.map(function (key) {
                  return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
                });
              }
              ctx.seen.pop();
              return reduceToSingleString(output, base, braces);
            }
            function formatPrimitive(ctx, value) {
              if (isUndefined(value))
                return ctx.stylize('undefined', 'undefined');
              if (isString(value)) {
                var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '').replace(/'/g, '\\\'').replace(/\\"/g, '"') + '\'';
                return ctx.stylize(simple, 'string');
              }
              if (isNumber(value))
                return ctx.stylize('' + value, 'number');
              if (isBoolean(value))
                return ctx.stylize('' + value, 'boolean');
              // For some reason typeof null is "object", so special case here.
              if (isNull(value))
                return ctx.stylize('null', 'null');
            }
            function formatError(value) {
              return '[' + Error.prototype.toString.call(value) + ']';
            }
            function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
              var output = [];
              for (var i = 0, l = value.length; i < l; ++i) {
                if (hasOwnProperty(value, String(i))) {
                  output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, String(i), true));
                } else {
                  output.push('');
                }
              }
              keys.forEach(function (key) {
                if (!key.match(/^\d+$/)) {
                  output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, key, true));
                }
              });
              return output;
            }
            function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
              var name, str, desc;
              desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
              if (desc.get) {
                if (desc.set) {
                  str = ctx.stylize('[Getter/Setter]', 'special');
                } else {
                  str = ctx.stylize('[Getter]', 'special');
                }
              } else {
                if (desc.set) {
                  str = ctx.stylize('[Setter]', 'special');
                }
              }
              if (!hasOwnProperty(visibleKeys, key)) {
                name = '[' + key + ']';
              }
              if (!str) {
                if (ctx.seen.indexOf(desc.value) < 0) {
                  if (isNull(recurseTimes)) {
                    str = formatValue(ctx, desc.value, null);
                  } else {
                    str = formatValue(ctx, desc.value, recurseTimes - 1);
                  }
                  if (str.indexOf('\n') > -1) {
                    if (array) {
                      str = str.split('\n').map(function (line) {
                        return '  ' + line;
                      }).join('\n').substr(2);
                    } else {
                      str = '\n' + str.split('\n').map(function (line) {
                        return '   ' + line;
                      }).join('\n');
                    }
                  }
                } else {
                  str = ctx.stylize('[Circular]', 'special');
                }
              }
              if (isUndefined(name)) {
                if (array && key.match(/^\d+$/)) {
                  return str;
                }
                name = JSON.stringify('' + key);
                if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
                  name = name.substr(1, name.length - 2);
                  name = ctx.stylize(name, 'name');
                } else {
                  name = name.replace(/'/g, '\\\'').replace(/\\"/g, '"').replace(/(^"|"$)/g, '\'');
                  name = ctx.stylize(name, 'string');
                }
              }
              return name + ': ' + str;
            }
            function reduceToSingleString(output, base, braces) {
              var numLinesEst = 0;
              var length = output.reduce(function (prev, cur) {
                numLinesEst++;
                if (cur.indexOf('\n') >= 0)
                  numLinesEst++;
                return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
              }, 0);
              if (length > 60) {
                return braces[0] + (base === '' ? '' : base + '\n ') + ' ' + output.join(',\n  ') + ' ' + braces[1];
              }
              return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
            }
            // NOTE: These type checking functions intentionally don't use `instanceof`
            // because it is fragile and can be easily faked with `Object.create()`.
            function isArray(ar) {
              return Array.isArray(ar);
            }
            exports.isArray = isArray;
            function isBoolean(arg) {
              return typeof arg === 'boolean';
            }
            exports.isBoolean = isBoolean;
            function isNull(arg) {
              return arg === null;
            }
            exports.isNull = isNull;
            function isNullOrUndefined(arg) {
              return arg == null;
            }
            exports.isNullOrUndefined = isNullOrUndefined;
            function isNumber(arg) {
              return typeof arg === 'number';
            }
            exports.isNumber = isNumber;
            function isString(arg) {
              return typeof arg === 'string';
            }
            exports.isString = isString;
            function isSymbol(arg) {
              return typeof arg === 'symbol';
            }
            exports.isSymbol = isSymbol;
            function isUndefined(arg) {
              return arg === void 0;
            }
            exports.isUndefined = isUndefined;
            function isRegExp(re) {
              return isObject(re) && objectToString(re) === '[object RegExp]';
            }
            exports.isRegExp = isRegExp;
            function isObject(arg) {
              return typeof arg === 'object' && arg !== null;
            }
            exports.isObject = isObject;
            function isDate(d) {
              return isObject(d) && objectToString(d) === '[object Date]';
            }
            exports.isDate = isDate;
            function isError(e) {
              return isObject(e) && (objectToString(e) === '[object Error]' || e instanceof Error);
            }
            exports.isError = isError;
            function isFunction(arg) {
              return typeof arg === 'function';
            }
            exports.isFunction = isFunction;
            function isPrimitive(arg) {
              return arg === null || typeof arg === 'boolean' || typeof arg === 'number' || typeof arg === 'string' || typeof arg === 'symbol' || // ES6 symbol
              typeof arg === 'undefined';
            }
            exports.isPrimitive = isPrimitive;
            exports.isBuffer = require('./support/isBuffer');
            function objectToString(o) {
              return Object.prototype.toString.call(o);
            }
            function pad(n) {
              return n < 10 ? '0' + n.toString(10) : n.toString(10);
            }
            var months = [
              'Jan',
              'Feb',
              'Mar',
              'Apr',
              'May',
              'Jun',
              'Jul',
              'Aug',
              'Sep',
              'Oct',
              'Nov',
              'Dec'
            ];
            // 26 Feb 16:19:34
            function timestamp() {
              var d = new Date();
              var time = [
                pad(d.getHours()),
                pad(d.getMinutes()),
                pad(d.getSeconds())
              ].join(':');
              return [
                d.getDate(),
                months[d.getMonth()],
                time
              ].join(' ');
            }
            // log is just a thin wrapper to console.log that prepends a timestamp
            exports.log = function () {
              console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
            };
            /**
             * Inherit the prototype methods from one constructor into another.
             *
             * The Function.prototype.inherits from lang.js rewritten as a standalone
             * function (not on Function.prototype). NOTE: If this file is to be loaded
             * during bootstrapping this function needs to be rewritten using some native
             * functions as prototype setup using normal JavaScript does not work as
             * expected during bootstrapping (see mirror.js in r114903).
             *
             * @param {function} ctor Constructor function which needs to inherit the
             *     prototype.
             * @param {function} superCtor Constructor function to inherit prototype from.
             */
            exports.inherits = require('inherits');
            exports._extend = function (origin, add) {
              // Don't do anything if add isn't an object
              if (!add || !isObject(add))
                return origin;
              var keys = Object.keys(add);
              var i = keys.length;
              while (i--) {
                origin[keys[i]] = add[keys[i]];
              }
              return origin;
            };
            function hasOwnProperty(obj, prop) {
              return Object.prototype.hasOwnProperty.call(obj, prop);
            }
          }.call(this, require('_process'), typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : typeof window !== 'undefined' ? window : {}));
        },
        {
          './support/isBuffer': 4,
          '_process': 2,
          'inherits': 3
        }
      ],
      6: [
        function (require, module, exports) {
          (function (process) {
            'use strict';
            var layouts = require('../layouts');
            function stdoutAppender(layout, timezoneOffset) {
              layout = layout || layouts.colouredLayout;
              return function (loggingEvent) {
                process.stdout.write(layout(loggingEvent, timezoneOffset) + '\n');
              };
            }
            function configure(config) {
              var layout;
              if (config.layout) {
                layout = layouts.layout(config.layout.type, config.layout);
              }
              return stdoutAppender(layout, config.timezoneOffset);
            }
            var exps = {
              appender: stdoutAppender,
              configure: configure
            };
            module.exports = exps;
            exportModule('LYNX.Logger.ThirdParty.appender.stdout', exps);
          }.call(this, require('_process')));
        },
        {
          '../layouts': 8,
          '_process': 2
        }
      ],
      7: [
        function (require, module, exports) {
          'use strict';
          exports.ISO8601_FORMAT = 'yyyy-MM-dd hh:mm:ss.SSS';
          exports.ISO8601_WITH_TZ_OFFSET_FORMAT = 'yyyy-MM-ddThh:mm:ssO';
          exports.DATETIME_FORMAT = 'dd MM yyyy hh:mm:ss.SSS';
          exports.ABSOLUTETIME_FORMAT = 'hh:mm:ss.SSS';
          function padWithZeros(vNumber, width) {
            var numAsString = vNumber + '';
            while (numAsString.length < width) {
              numAsString = '0' + numAsString;
            }
            return numAsString;
          }
          function addZero(vNumber) {
            return padWithZeros(vNumber, 2);
          }
          /**
           * Formats the TimeOffest
           * Thanks to http://www.svendtofte.com/code/date_format/
           * @private
           */
          function offset(timezoneOffset) {
            // Difference to Greenwich time (GMT) in hours
            var os = Math.abs(timezoneOffset);
            var h = String(Math.floor(os / 60));
            var m = String(os % 60);
            if (h.length == 1) {
              h = '0' + h;
            }
            if (m.length == 1) {
              m = '0' + m;
            }
            return timezoneOffset < 0 ? '+' + h + m : '-' + h + m;
          }
          exports.asString = function (date, timezoneOffset) {
            /*jshint -W071 */
            var format = exports.ISO8601_FORMAT;
            if (typeof date === 'string') {
              format = arguments[0];
              date = arguments[1];
              timezoneOffset = arguments[2];
            }
            // make the date independent of the system timezone by working with UTC
            if (timezoneOffset === undefined) {
              timezoneOffset = date.getTimezoneOffset();
            }
            date.setUTCMinutes(date.getUTCMinutes() - timezoneOffset);
            var vDay = addZero(date.getUTCDate());
            var vMonth = addZero(date.getUTCMonth() + 1);
            var vYearLong = addZero(date.getUTCFullYear());
            var vYearShort = addZero(date.getUTCFullYear().toString().substring(2, 4));
            var vYear = format.indexOf('yyyy') > -1 ? vYearLong : vYearShort;
            var vHour = addZero(date.getUTCHours());
            var vMinute = addZero(date.getUTCMinutes());
            var vSecond = addZero(date.getUTCSeconds());
            var vMillisecond = padWithZeros(date.getUTCMilliseconds(), 3);
            var vTimeZone = offset(timezoneOffset);
            date.setUTCMinutes(date.getUTCMinutes() + timezoneOffset);
            var formatted = format.replace(/dd/g, vDay).replace(/MM/g, vMonth).replace(/y{1,4}/g, vYear).replace(/hh/g, vHour).replace(/mm/g, vMinute).replace(/ss/g, vSecond).replace(/SSS/g, vMillisecond).replace(/O/g, vTimeZone);
            return formatted;
          };  /*jshint +W071 */
        },
        {}
      ],
      8: [
        function (require, module, exports) {
          (function (process) {
            'use strict';
            var dateFormat = require('./date_format'), os = require('os'), eol = os.EOL || '\n', util = require('util'), semver = require('semver'), replacementRegExp = /%[sdj]/g, layoutMakers = {
                'messagePassThrough': function () {
                  return messagePassThroughLayout;
                },
                'basic': function () {
                  return basicLayout;
                },
                'colored': function () {
                  return colouredLayout;
                },
                'coloured': function () {
                  return colouredLayout;
                },
                'pattern': function (config) {
                  return patternLayout(config && config.pattern, config && config.tokens);
                },
                'dummy': function () {
                  return dummyLayout;
                }
              }, colours = {
                ALL: 'grey',
                TRACE: 'blue',
                DEBUG: 'cyan',
                INFO: 'green',
                WARN: 'yellow',
                ERROR: 'red',
                FATAL: 'magenta',
                OFF: 'grey'
              };
            function wrapErrorsWithInspect(items) {
              return items.map(function (item) {
                if (item instanceof Error && item.stack) {
                  return {
                    inspect: function () {
                      if (semver.satisfies(process.version, '>=6')) {
                        return util.format(item);
                      } else {
                        return util.format(item) + '\n' + item.stack;
                      }
                    }
                  };
                } else {
                  return item;
                }
              });
            }
            function formatLogData(logData) {
              var data = Array.isArray(logData) ? logData : Array.prototype.slice.call(arguments);
              return util.format.apply(util, wrapErrorsWithInspect(data));
            }
            var styles = {
              //styles
              'bold': [
                1,
                22
              ],
              'italic': [
                3,
                23
              ],
              'underline': [
                4,
                24
              ],
              'inverse': [
                7,
                27
              ],
              //grayscale
              'white': [
                37,
                39
              ],
              'grey': [
                90,
                39
              ],
              'black': [
                90,
                39
              ],
              //colors
              'blue': [
                34,
                39
              ],
              'cyan': [
                36,
                39
              ],
              'green': [
                32,
                39
              ],
              'magenta': [
                35,
                39
              ],
              'red': [
                31,
                39
              ],
              'yellow': [
                33,
                39
              ]
            };
            function colorizeStart(style) {
              return style ? '\x1B[' + styles[style][0] + 'm' : '';
            }
            function colorizeEnd(style) {
              return style ? '\x1B[' + styles[style][1] + 'm' : '';
            }
            /**
             * Taken from masylum's fork (https://github.com/masylum/log4js-node)
             */
            function colorize(str, style) {
              return colorizeStart(style) + str + colorizeEnd(style);
            }
            function timestampLevelAndCategory(loggingEvent, colour, timezoneOffest) {
              var output = colorize(formatLogData('[%s] [%s] %s - ', dateFormat.asString(loggingEvent.startTime, timezoneOffest), loggingEvent.level, loggingEvent.categoryName), colour);
              return output;
            }
            /**
             * BasicLayout is a simple layout for storing the logs. The logs are stored
             * in following format:
             * <pre>
             * [startTime] [logLevel] categoryName - message\n
             * </pre>
             *
             * @author Stephan Strittmatter
             */
            function basicLayout(loggingEvent, timezoneOffset) {
              return timestampLevelAndCategory(loggingEvent, undefined, timezoneOffset) + formatLogData(loggingEvent.data);
            }
            /**
             * colouredLayout - taken from masylum's fork.
             * same as basicLayout, but with colours.
             */
            function colouredLayout(loggingEvent, timezoneOffset) {
              return timestampLevelAndCategory(loggingEvent, colours[loggingEvent.level.toString()], timezoneOffset) + formatLogData(loggingEvent.data);
            }
            function messagePassThroughLayout(loggingEvent) {
              return formatLogData(loggingEvent.data);
            }
            function dummyLayout(loggingEvent) {
              return loggingEvent.data[0];
            }
            /**
             * PatternLayout
             * Format for specifiers is %[padding].[truncation][field]{[format]}
             * e.g. %5.10p - left pad the log level by 5 characters, up to a max of 10
             * Fields can be any of:
             *  - %r time in toLocaleTimeString format
             *  - %p log level
             *  - %c log category
             *  - %h hostname
             *  - %m log data
             *  - %d date in various formats
             *  - %% %
             *  - %n newline
             *  - %z pid
             *  - %x{<tokenname>} add dynamic tokens to your log. Tokens are specified in the tokens parameter
             * You can use %[ and %] to define a colored block.
             *
             * Tokens are specified as simple key:value objects.
             * The key represents the token name whereas the value can be a string or function
             * which is called to extract the value to put in the log message. If token is not
             * found, it doesn't replace the field.
             *
             * A sample token would be: { "pid" : function() { return process.pid; } }
             *
             * Takes a pattern string, array of tokens and returns a layout function.
             * @param {String} Log format pattern String
             * @param {object} map object of different tokens
             * @param {number} timezone offset in minutes
             * @return {Function}
             * @author Stephan Strittmatter
             * @author Jan Schmidle
             */
            function patternLayout(pattern, tokens, timezoneOffset) {
              // jshint maxstatements:22
              var TTCC_CONVERSION_PATTERN = '%r %p %c - %m%n';
              var regex = /%(-?[0-9]+)?(\.?[0-9]+)?([\[\]cdhmnprzxy%])(\{([^\}]+)\})?|([^%]+)/;
              pattern = pattern || TTCC_CONVERSION_PATTERN;
              function categoryName(loggingEvent, specifier) {
                var loggerName = loggingEvent.categoryName;
                if (specifier) {
                  var precision = parseInt(specifier, 10);
                  var loggerNameBits = loggerName.split('.');
                  if (precision < loggerNameBits.length) {
                    loggerName = loggerNameBits.slice(loggerNameBits.length - precision).join('.');
                  }
                }
                return loggerName;
              }
              function formatAsDate(loggingEvent, specifier) {
                var format = dateFormat.ISO8601_FORMAT;
                if (specifier) {
                  format = specifier;
                  // Pick up special cases
                  if (format == 'ISO8601') {
                    format = dateFormat.ISO8601_FORMAT;
                  } else if (format == 'ISO8601_WITH_TZ_OFFSET') {
                    format = dateFormat.ISO8601_WITH_TZ_OFFSET_FORMAT;
                  } else if (format == 'ABSOLUTE') {
                    format = dateFormat.ABSOLUTETIME_FORMAT;
                  } else if (format == 'DATE') {
                    format = dateFormat.DATETIME_FORMAT;
                  }
                }
                // Format the date
                return dateFormat.asString(format, loggingEvent.startTime, timezoneOffset);
              }
              function hostname() {
                return os.hostname().toString();
              }
              function formatMessage(loggingEvent) {
                return formatLogData(loggingEvent.data);
              }
              function endOfLine() {
                return eol;
              }
              function logLevel(loggingEvent) {
                return loggingEvent.level.toString();
              }
              function startTime(loggingEvent) {
                return dateFormat.asString('hh:mm:ss', loggingEvent.startTime, timezoneOffset);
              }
              function startColour(loggingEvent) {
                return colorizeStart(colours[loggingEvent.level.toString()]);
              }
              function endColour(loggingEvent) {
                return colorizeEnd(colours[loggingEvent.level.toString()]);
              }
              function percent() {
                return '%';
              }
              function pid(loggingEvent) {
                if (loggingEvent && loggingEvent.pid) {
                  return loggingEvent.pid;
                } else {
                  return process.pid;
                }
              }
              function clusterInfo(loggingEvent, specifier) {
                if (loggingEvent.cluster && specifier) {
                  return specifier.replace('%m', loggingEvent.cluster.master).replace('%w', loggingEvent.cluster.worker).replace('%i', loggingEvent.cluster.workerId);
                } else if (loggingEvent.cluster) {
                  return loggingEvent.cluster.worker + '@' + loggingEvent.cluster.master;
                } else {
                  return pid();
                }
              }
              function userDefined(loggingEvent, specifier) {
                if (typeof tokens[specifier] !== 'undefined') {
                  if (typeof tokens[specifier] === 'function') {
                    return tokens[specifier](loggingEvent);
                  } else {
                    return tokens[specifier];
                  }
                }
                return null;
              }
              var replacers = {
                'c': categoryName,
                'd': formatAsDate,
                'h': hostname,
                'm': formatMessage,
                'n': endOfLine,
                'p': logLevel,
                'r': startTime,
                '[': startColour,
                ']': endColour,
                'y': clusterInfo,
                'z': pid,
                '%': percent,
                'x': userDefined
              };
              function replaceToken(conversionCharacter, loggingEvent, specifier) {
                return replacers[conversionCharacter](loggingEvent, specifier);
              }
              function truncate(truncation, toTruncate) {
                var len;
                if (truncation) {
                  len = parseInt(truncation.substr(1), 10);
                  return toTruncate.substring(0, len);
                }
                return toTruncate;
              }
              function pad(padding, toPad) {
                var len;
                if (padding) {
                  if (padding.charAt(0) == '-') {
                    len = parseInt(padding.substr(1), 10);
                    // Right pad with spaces
                    while (toPad.length < len) {
                      toPad += ' ';
                    }
                  } else {
                    len = parseInt(padding, 10);
                    // Left pad with spaces
                    while (toPad.length < len) {
                      toPad = ' ' + toPad;
                    }
                  }
                }
                return toPad;
              }
              function truncateAndPad(toTruncAndPad, truncation, padding) {
                var replacement = toTruncAndPad;
                replacement = truncate(truncation, replacement);
                replacement = pad(padding, replacement);
                return replacement;
              }
              return function (loggingEvent) {
                var formattedString = '';
                var result;
                var searchString = pattern;
                while (result = regex.exec(searchString)) {
                  var matchedString = result[0];
                  var padding = result[1];
                  var truncation = result[2];
                  var conversionCharacter = result[3];
                  var specifier = result[5];
                  var text = result[6];
                  // Check if the pattern matched was just normal text
                  if (text) {
                    formattedString += '' + text;
                  } else {
                    // Create a raw replacement string based on the conversion
                    // character and specifier
                    var replacement = replaceToken(conversionCharacter, loggingEvent, specifier);
                    formattedString += truncateAndPad(replacement, truncation, padding);
                  }
                  searchString = searchString.substr(result.index + result[0].length);
                }
                return formattedString;
              };
            }
            module.exports = {
              basicLayout: basicLayout,
              messagePassThroughLayout: messagePassThroughLayout,
              patternLayout: patternLayout,
              colouredLayout: colouredLayout,
              coloredLayout: colouredLayout,
              dummyLayout: dummyLayout,
              addLayout: function (name, serializerGenerator) {
                layoutMakers[name] = serializerGenerator;
              },
              layout: function (name, config) {
                return layoutMakers[name] && layoutMakers[name](config);
              }
            };
          }.call(this, require('_process')));
        },
        {
          './date_format': 7,
          '_process': 2,
          'os': 1,
          'semver': 9,
          'util': 5
        }
      ],
      9: [
        function (require, module, exports) {
          (function (process) {
            exports = module.exports = SemVer;
            // The debug function is excluded entirely from the minified version.
            /* nomin */
            var debug;
            /* nomin */
            if (typeof process === 'object' && /* nomin */
              process.env && /* nomin */
              process.env.NODE_DEBUG && /* nomin */
              /\bsemver\b/i.test(process.env.NODE_DEBUG))
              /* nomin */
              debug = function () {
                /* nomin */
                var args = Array.prototype.slice.call(arguments, 0);
                /* nomin */
                args.unshift('SEMVER');
                /* nomin */
                console.log.apply(console, args);  /* nomin */
              };  /* nomin */
            else
              /* nomin */
              debug = function () {
              };
            // Note: this is the semver.org version of the spec that it implements
            // Not necessarily the package version of this code.
            exports.SEMVER_SPEC_VERSION = '2.0.0';
            var MAX_LENGTH = 256;
            var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;
            // The actual regexps go on exports.re
            var re = exports.re = [];
            var src = exports.src = [];
            var R = 0;
            // The following Regular Expressions can be used for tokenizing,
            // validating, and parsing SemVer version strings.
            // ## Numeric Identifier
            // A single `0`, or a non-zero digit followed by zero or more digits.
            var NUMERICIDENTIFIER = R++;
            src[NUMERICIDENTIFIER] = '0|[1-9]\\d*';
            var NUMERICIDENTIFIERLOOSE = R++;
            src[NUMERICIDENTIFIERLOOSE] = '[0-9]+';
            // ## Non-numeric Identifier
            // Zero or more digits, followed by a letter or hyphen, and then zero or
            // more letters, digits, or hyphens.
            var NONNUMERICIDENTIFIER = R++;
            src[NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*';
            // ## Main Version
            // Three dot-separated numeric identifiers.
            var MAINVERSION = R++;
            src[MAINVERSION] = '(' + src[NUMERICIDENTIFIER] + ')\\.' + '(' + src[NUMERICIDENTIFIER] + ')\\.' + '(' + src[NUMERICIDENTIFIER] + ')';
            var MAINVERSIONLOOSE = R++;
            src[MAINVERSIONLOOSE] = '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + '(' + src[NUMERICIDENTIFIERLOOSE] + ')';
            // ## Pre-release Version Identifier
            // A numeric identifier, or a non-numeric identifier.
            var PRERELEASEIDENTIFIER = R++;
            src[PRERELEASEIDENTIFIER] = '(?:' + src[NUMERICIDENTIFIER] + '|' + src[NONNUMERICIDENTIFIER] + ')';
            var PRERELEASEIDENTIFIERLOOSE = R++;
            src[PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[NUMERICIDENTIFIERLOOSE] + '|' + src[NONNUMERICIDENTIFIER] + ')';
            // ## Pre-release Version
            // Hyphen, followed by one or more dot-separated pre-release version
            // identifiers.
            var PRERELEASE = R++;
            src[PRERELEASE] = '(?:-(' + src[PRERELEASEIDENTIFIER] + '(?:\\.' + src[PRERELEASEIDENTIFIER] + ')*))';
            var PRERELEASELOOSE = R++;
            src[PRERELEASELOOSE] = '(?:-?(' + src[PRERELEASEIDENTIFIERLOOSE] + '(?:\\.' + src[PRERELEASEIDENTIFIERLOOSE] + ')*))';
            // ## Build Metadata Identifier
            // Any combination of digits, letters, or hyphens.
            var BUILDIDENTIFIER = R++;
            src[BUILDIDENTIFIER] = '[0-9A-Za-z-]+';
            // ## Build Metadata
            // Plus sign, followed by one or more period-separated build metadata
            // identifiers.
            var BUILD = R++;
            src[BUILD] = '(?:\\+(' + src[BUILDIDENTIFIER] + '(?:\\.' + src[BUILDIDENTIFIER] + ')*))';
            // ## Full Version String
            // A main version, followed optionally by a pre-release version and
            // build metadata.
            // Note that the only major, minor, patch, and pre-release sections of
            // the version string are capturing groups.  The build metadata is not a
            // capturing group, because it should not ever be used in version
            // comparison.
            var FULL = R++;
            var FULLPLAIN = 'v?' + src[MAINVERSION] + src[PRERELEASE] + '?' + src[BUILD] + '?';
            src[FULL] = '^' + FULLPLAIN + '$';
            // like full, but allows v1.2.3 and =1.2.3, which people do sometimes.
            // also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty
            // common in the npm registry.
            var LOOSEPLAIN = '[v=\\s]*' + src[MAINVERSIONLOOSE] + src[PRERELEASELOOSE] + '?' + src[BUILD] + '?';
            var LOOSE = R++;
            src[LOOSE] = '^' + LOOSEPLAIN + '$';
            var GTLT = R++;
            src[GTLT] = '((?:<|>)?=?)';
            // Something like "2.*" or "1.2.x".
            // Note that "x.x" is a valid xRange identifer, meaning "any version"
            // Only the first item is strictly required.
            var XRANGEIDENTIFIERLOOSE = R++;
            src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + '|x|X|\\*';
            var XRANGEIDENTIFIER = R++;
            src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + '|x|X|\\*';
            var XRANGEPLAIN = R++;
            src[XRANGEPLAIN] = '[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + '(?:' + src[PRERELEASE] + ')?' + src[BUILD] + '?' + ')?)?';
            var XRANGEPLAINLOOSE = R++;
            src[XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + '(?:' + src[PRERELEASELOOSE] + ')?' + src[BUILD] + '?' + ')?)?';
            var XRANGE = R++;
            src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$';
            var XRANGELOOSE = R++;
            src[XRANGELOOSE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAINLOOSE] + '$';
            // Tilde ranges.
            // Meaning is "reasonably at or greater than"
            var LONETILDE = R++;
            src[LONETILDE] = '(?:~>?)';
            var TILDETRIM = R++;
            src[TILDETRIM] = '(\\s*)' + src[LONETILDE] + '\\s+';
            re[TILDETRIM] = new RegExp(src[TILDETRIM], 'g');
            var tildeTrimReplace = '$1~';
            var TILDE = R++;
            src[TILDE] = '^' + src[LONETILDE] + src[XRANGEPLAIN] + '$';
            var TILDELOOSE = R++;
            src[TILDELOOSE] = '^' + src[LONETILDE] + src[XRANGEPLAINLOOSE] + '$';
            // Caret ranges.
            // Meaning is "at least and backwards compatible with"
            var LONECARET = R++;
            src[LONECARET] = '(?:\\^)';
            var CARETTRIM = R++;
            src[CARETTRIM] = '(\\s*)' + src[LONECARET] + '\\s+';
            re[CARETTRIM] = new RegExp(src[CARETTRIM], 'g');
            var caretTrimReplace = '$1^';
            var CARET = R++;
            src[CARET] = '^' + src[LONECARET] + src[XRANGEPLAIN] + '$';
            var CARETLOOSE = R++;
            src[CARETLOOSE] = '^' + src[LONECARET] + src[XRANGEPLAINLOOSE] + '$';
            // A simple gt/lt/eq thing, or just "" to indicate "any version"
            var COMPARATORLOOSE = R++;
            src[COMPARATORLOOSE] = '^' + src[GTLT] + '\\s*(' + LOOSEPLAIN + ')$|^$';
            var COMPARATOR = R++;
            src[COMPARATOR] = '^' + src[GTLT] + '\\s*(' + FULLPLAIN + ')$|^$';
            // An expression to strip any whitespace between the gtlt and the thing
            // it modifies, so that `> 1.2.3` ==> `>1.2.3`
            var COMPARATORTRIM = R++;
            src[COMPARATORTRIM] = '(\\s*)' + src[GTLT] + '\\s*(' + LOOSEPLAIN + '|' + src[XRANGEPLAIN] + ')';
            // this one has to use the /g flag
            re[COMPARATORTRIM] = new RegExp(src[COMPARATORTRIM], 'g');
            var comparatorTrimReplace = '$1$2$3';
            // Something like `1.2.3 - 1.2.4`
            // Note that these all use the loose form, because they'll be
            // checked against either the strict or loose comparator form
            // later.
            var HYPHENRANGE = R++;
            src[HYPHENRANGE] = '^\\s*(' + src[XRANGEPLAIN] + ')' + '\\s+-\\s+' + '(' + src[XRANGEPLAIN] + ')' + '\\s*$';
            var HYPHENRANGELOOSE = R++;
            src[HYPHENRANGELOOSE] = '^\\s*(' + src[XRANGEPLAINLOOSE] + ')' + '\\s+-\\s+' + '(' + src[XRANGEPLAINLOOSE] + ')' + '\\s*$';
            // Star ranges basically just allow anything at all.
            var STAR = R++;
            src[STAR] = '(<|>)?=?\\s*\\*';
            // Compile to actual regexp objects.
            // All are flag-free, unless they were created above with a flag.
            for (var i = 0; i < R; i++) {
              debug(i, src[i]);
              if (!re[i])
                re[i] = new RegExp(src[i]);
            }
            exports.parse = parse;
            function parse(version, loose) {
              if (version instanceof SemVer)
                return version;
              if (typeof version !== 'string')
                return null;
              if (version.length > MAX_LENGTH)
                return null;
              var r = loose ? re[LOOSE] : re[FULL];
              if (!r.test(version))
                return null;
              try {
                return new SemVer(version, loose);
              } catch (er) {
                return null;
              }
            }
            exports.valid = valid;
            function valid(version, loose) {
              var v = parse(version, loose);
              return v ? v.version : null;
            }
            exports.clean = clean;
            function clean(version, loose) {
              var s = parse(version.trim().replace(/^[=v]+/, ''), loose);
              return s ? s.version : null;
            }
            exports.SemVer = SemVer;
            function SemVer(version, loose) {
              if (version instanceof SemVer) {
                if (version.loose === loose)
                  return version;
                else
                  version = version.version;
              } else if (typeof version !== 'string') {
                throw new TypeError('Invalid Version: ' + version);
              }
              if (version.length > MAX_LENGTH)
                throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters');
              if (!(this instanceof SemVer))
                return new SemVer(version, loose);
              debug('SemVer', version, loose);
              this.loose = loose;
              var m = version.trim().match(loose ? re[LOOSE] : re[FULL]);
              if (!m)
                throw new TypeError('Invalid Version: ' + version);
              this.raw = version;
              // these are actually numbers
              this.major = +m[1];
              this.minor = +m[2];
              this.patch = +m[3];
              if (this.major > MAX_SAFE_INTEGER || this.major < 0)
                throw new TypeError('Invalid major version');
              if (this.minor > MAX_SAFE_INTEGER || this.minor < 0)
                throw new TypeError('Invalid minor version');
              if (this.patch > MAX_SAFE_INTEGER || this.patch < 0)
                throw new TypeError('Invalid patch version');
              // numberify any prerelease numeric ids
              if (!m[4])
                this.prerelease = [];
              else
                this.prerelease = m[4].split('.').map(function (id) {
                  if (/^[0-9]+$/.test(id)) {
                    var num = +id;
                    if (num >= 0 && num < MAX_SAFE_INTEGER)
                      return num;
                  }
                  return id;
                });
              this.build = m[5] ? m[5].split('.') : [];
              this.format();
            }
            SemVer.prototype.format = function () {
              this.version = this.major + '.' + this.minor + '.' + this.patch;
              if (this.prerelease.length)
                this.version += '-' + this.prerelease.join('.');
              return this.version;
            };
            SemVer.prototype.toString = function () {
              return this.version;
            };
            SemVer.prototype.compare = function (other) {
              debug('SemVer.compare', this.version, this.loose, other);
              if (!(other instanceof SemVer))
                other = new SemVer(other, this.loose);
              return this.compareMain(other) || this.comparePre(other);
            };
            SemVer.prototype.compareMain = function (other) {
              if (!(other instanceof SemVer))
                other = new SemVer(other, this.loose);
              return compareIdentifiers(this.major, other.major) || compareIdentifiers(this.minor, other.minor) || compareIdentifiers(this.patch, other.patch);
            };
            SemVer.prototype.comparePre = function (other) {
              if (!(other instanceof SemVer))
                other = new SemVer(other, this.loose);
              // NOT having a prerelease is > having one
              if (this.prerelease.length && !other.prerelease.length)
                return -1;
              else if (!this.prerelease.length && other.prerelease.length)
                return 1;
              else if (!this.prerelease.length && !other.prerelease.length)
                return 0;
              var i = 0;
              do {
                var a = this.prerelease[i];
                var b = other.prerelease[i];
                debug('prerelease compare', i, a, b);
                if (a === undefined && b === undefined)
                  return 0;
                else if (b === undefined)
                  return 1;
                else if (a === undefined)
                  return -1;
                else if (a === b)
                  continue;
                else
                  return compareIdentifiers(a, b);
              } while (++i);
            };
            // preminor will bump the version up to the next minor release, and immediately
            // down to pre-release. premajor and prepatch work the same way.
            SemVer.prototype.inc = function (release, identifier) {
              switch (release) {
              case 'premajor':
                this.prerelease.length = 0;
                this.patch = 0;
                this.minor = 0;
                this.major++;
                this.inc('pre', identifier);
                break;
              case 'preminor':
                this.prerelease.length = 0;
                this.patch = 0;
                this.minor++;
                this.inc('pre', identifier);
                break;
              case 'prepatch':
                // If this is already a prerelease, it will bump to the next version
                // drop any prereleases that might already exist, since they are not
                // relevant at this point.
                this.prerelease.length = 0;
                this.inc('patch', identifier);
                this.inc('pre', identifier);
                break;
              // If the input is a non-prerelease version, this acts the same as
              // prepatch.
              case 'prerelease':
                if (this.prerelease.length === 0)
                  this.inc('patch', identifier);
                this.inc('pre', identifier);
                break;
              case 'major':
                // If this is a pre-major version, bump up to the same major version.
                // Otherwise increment major.
                // 1.0.0-5 bumps to 1.0.0
                // 1.1.0 bumps to 2.0.0
                if (this.minor !== 0 || this.patch !== 0 || this.prerelease.length === 0)
                  this.major++;
                this.minor = 0;
                this.patch = 0;
                this.prerelease = [];
                break;
              case 'minor':
                // If this is a pre-minor version, bump up to the same minor version.
                // Otherwise increment minor.
                // 1.2.0-5 bumps to 1.2.0
                // 1.2.1 bumps to 1.3.0
                if (this.patch !== 0 || this.prerelease.length === 0)
                  this.minor++;
                this.patch = 0;
                this.prerelease = [];
                break;
              case 'patch':
                // If this is not a pre-release version, it will increment the patch.
                // If it is a pre-release it will bump up to the same patch version.
                // 1.2.0-5 patches to 1.2.0
                // 1.2.0 patches to 1.2.1
                if (this.prerelease.length === 0)
                  this.patch++;
                this.prerelease = [];
                break;
              // This probably shouldn't be used publicly.
              // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction.
              case 'pre':
                if (this.prerelease.length === 0)
                  this.prerelease = [0];
                else {
                  var i = this.prerelease.length;
                  while (--i >= 0) {
                    if (typeof this.prerelease[i] === 'number') {
                      this.prerelease[i]++;
                      i = -2;
                    }
                  }
                  if (i === -1)
                    // didn't increment anything
                    this.prerelease.push(0);
                }
                if (identifier) {
                  // 1.2.0-beta.1 bumps to 1.2.0-beta.2,
                  // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0
                  if (this.prerelease[0] === identifier) {
                    if (isNaN(this.prerelease[1]))
                      this.prerelease = [
                        identifier,
                        0
                      ];
                  } else
                    this.prerelease = [
                      identifier,
                      0
                    ];
                }
                break;
              default:
                throw new Error('invalid increment argument: ' + release);
              }
              this.format();
              this.raw = this.version;
              return this;
            };
            exports.inc = inc;
            function inc(version, release, loose, identifier) {
              if (typeof loose === 'string') {
                identifier = loose;
                loose = undefined;
              }
              try {
                return new SemVer(version, loose).inc(release, identifier).version;
              } catch (er) {
                return null;
              }
            }
            exports.diff = diff;
            function diff(version1, version2) {
              if (eq(version1, version2)) {
                return null;
              } else {
                var v1 = parse(version1);
                var v2 = parse(version2);
                if (v1.prerelease.length || v2.prerelease.length) {
                  for (var key in v1) {
                    if (key === 'major' || key === 'minor' || key === 'patch') {
                      if (v1[key] !== v2[key]) {
                        return 'pre' + key;
                      }
                    }
                  }
                  return 'prerelease';
                }
                for (var key in v1) {
                  if (key === 'major' || key === 'minor' || key === 'patch') {
                    if (v1[key] !== v2[key]) {
                      return key;
                    }
                  }
                }
              }
            }
            exports.compareIdentifiers = compareIdentifiers;
            var numeric = /^[0-9]+$/;
            function compareIdentifiers(a, b) {
              var anum = numeric.test(a);
              var bnum = numeric.test(b);
              if (anum && bnum) {
                a = +a;
                b = +b;
              }
              return anum && !bnum ? -1 : bnum && !anum ? 1 : a < b ? -1 : a > b ? 1 : 0;
            }
            exports.rcompareIdentifiers = rcompareIdentifiers;
            function rcompareIdentifiers(a, b) {
              return compareIdentifiers(b, a);
            }
            exports.major = major;
            function major(a, loose) {
              return new SemVer(a, loose).major;
            }
            exports.minor = minor;
            function minor(a, loose) {
              return new SemVer(a, loose).minor;
            }
            exports.patch = patch;
            function patch(a, loose) {
              return new SemVer(a, loose).patch;
            }
            exports.compare = compare;
            function compare(a, b, loose) {
              return new SemVer(a, loose).compare(b);
            }
            exports.compareLoose = compareLoose;
            function compareLoose(a, b) {
              return compare(a, b, true);
            }
            exports.rcompare = rcompare;
            function rcompare(a, b, loose) {
              return compare(b, a, loose);
            }
            exports.sort = sort;
            function sort(list, loose) {
              return list.sort(function (a, b) {
                return exports.compare(a, b, loose);
              });
            }
            exports.rsort = rsort;
            function rsort(list, loose) {
              return list.sort(function (a, b) {
                return exports.rcompare(a, b, loose);
              });
            }
            exports.gt = gt;
            function gt(a, b, loose) {
              return compare(a, b, loose) > 0;
            }
            exports.lt = lt;
            function lt(a, b, loose) {
              return compare(a, b, loose) < 0;
            }
            exports.eq = eq;
            function eq(a, b, loose) {
              return compare(a, b, loose) === 0;
            }
            exports.neq = neq;
            function neq(a, b, loose) {
              return compare(a, b, loose) !== 0;
            }
            exports.gte = gte;
            function gte(a, b, loose) {
              return compare(a, b, loose) >= 0;
            }
            exports.lte = lte;
            function lte(a, b, loose) {
              return compare(a, b, loose) <= 0;
            }
            exports.cmp = cmp;
            function cmp(a, op, b, loose) {
              var ret;
              switch (op) {
              case '===':
                if (typeof a === 'object')
                  a = a.version;
                if (typeof b === 'object')
                  b = b.version;
                ret = a === b;
                break;
              case '!==':
                if (typeof a === 'object')
                  a = a.version;
                if (typeof b === 'object')
                  b = b.version;
                ret = a !== b;
                break;
              case '':
              case '=':
              case '==':
                ret = eq(a, b, loose);
                break;
              case '!=':
                ret = neq(a, b, loose);
                break;
              case '>':
                ret = gt(a, b, loose);
                break;
              case '>=':
                ret = gte(a, b, loose);
                break;
              case '<':
                ret = lt(a, b, loose);
                break;
              case '<=':
                ret = lte(a, b, loose);
                break;
              default:
                throw new TypeError('Invalid operator: ' + op);
              }
              return ret;
            }
            exports.Comparator = Comparator;
            function Comparator(comp, loose) {
              if (comp instanceof Comparator) {
                if (comp.loose === loose)
                  return comp;
                else
                  comp = comp.value;
              }
              if (!(this instanceof Comparator))
                return new Comparator(comp, loose);
              debug('comparator', comp, loose);
              this.loose = loose;
              this.parse(comp);
              if (this.semver === ANY)
                this.value = '';
              else
                this.value = this.operator + this.semver.version;
              debug('comp', this);
            }
            var ANY = {};
            Comparator.prototype.parse = function (comp) {
              var r = this.loose ? re[COMPARATORLOOSE] : re[COMPARATOR];
              var m = comp.match(r);
              if (!m)
                throw new TypeError('Invalid comparator: ' + comp);
              this.operator = m[1];
              if (this.operator === '=')
                this.operator = '';
              // if it literally is just '>' or '' then allow anything.
              if (!m[2])
                this.semver = ANY;
              else
                this.semver = new SemVer(m[2], this.loose);
            };
            Comparator.prototype.toString = function () {
              return this.value;
            };
            Comparator.prototype.test = function (version) {
              debug('Comparator.test', version, this.loose);
              if (this.semver === ANY)
                return true;
              if (typeof version === 'string')
                version = new SemVer(version, this.loose);
              return cmp(version, this.operator, this.semver, this.loose);
            };
            exports.Range = Range;
            function Range(range, loose) {
              if (range instanceof Range && range.loose === loose)
                return range;
              if (!(this instanceof Range))
                return new Range(range, loose);
              this.loose = loose;
              // First, split based on boolean or ||
              this.raw = range;
              this.set = range.split(/\s*\|\|\s*/).map(function (range) {
                return this.parseRange(range.trim());
              }, this).filter(function (c) {
                // throw out any that are not relevant for whatever reason
                return c.length;
              });
              if (!this.set.length) {
                throw new TypeError('Invalid SemVer Range: ' + range);
              }
              this.format();
            }
            Range.prototype.format = function () {
              this.range = this.set.map(function (comps) {
                return comps.join(' ').trim();
              }).join('||').trim();
              return this.range;
            };
            Range.prototype.toString = function () {
              return this.range;
            };
            Range.prototype.parseRange = function (range) {
              var loose = this.loose;
              range = range.trim();
              debug('range', range, loose);
              // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`
              var hr = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE];
              range = range.replace(hr, hyphenReplace);
              debug('hyphen replace', range);
              // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
              range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace);
              debug('comparator trim', range, re[COMPARATORTRIM]);
              // `~ 1.2.3` => `~1.2.3`
              range = range.replace(re[TILDETRIM], tildeTrimReplace);
              // `^ 1.2.3` => `^1.2.3`
              range = range.replace(re[CARETTRIM], caretTrimReplace);
              // normalize spaces
              range = range.split(/\s+/).join(' ');
              // At this point, the range is completely trimmed and
              // ready to be split into comparators.
              var compRe = loose ? re[COMPARATORLOOSE] : re[COMPARATOR];
              var set = range.split(' ').map(function (comp) {
                return parseComparator(comp, loose);
              }).join(' ').split(/\s+/);
              if (this.loose) {
                // in loose mode, throw out any that are not valid comparators
                set = set.filter(function (comp) {
                  return !!comp.match(compRe);
                });
              }
              set = set.map(function (comp) {
                return new Comparator(comp, loose);
              });
              return set;
            };
            // Mostly just for testing and legacy API reasons
            exports.toComparators = toComparators;
            function toComparators(range, loose) {
              return new Range(range, loose).set.map(function (comp) {
                return comp.map(function (c) {
                  return c.value;
                }).join(' ').trim().split(' ');
              });
            }
            // comprised of xranges, tildes, stars, and gtlt's at this point.
            // already replaced the hyphen ranges
            // turn into a set of JUST comparators.
            function parseComparator(comp, loose) {
              debug('comp', comp);
              comp = replaceCarets(comp, loose);
              debug('caret', comp);
              comp = replaceTildes(comp, loose);
              debug('tildes', comp);
              comp = replaceXRanges(comp, loose);
              debug('xrange', comp);
              comp = replaceStars(comp, loose);
              debug('stars', comp);
              return comp;
            }
            function isX(id) {
              return !id || id.toLowerCase() === 'x' || id === '*';
            }
            // ~, ~> --> * (any, kinda silly)
            // ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0
            // ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0
            // ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0
            // ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0
            // ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0
            function replaceTildes(comp, loose) {
              return comp.trim().split(/\s+/).map(function (comp) {
                return replaceTilde(comp, loose);
              }).join(' ');
            }
            function replaceTilde(comp, loose) {
              var r = loose ? re[TILDELOOSE] : re[TILDE];
              return comp.replace(r, function (_, M, m, p, pr) {
                debug('tilde', comp, _, M, m, p, pr);
                var ret;
                if (isX(M))
                  ret = '';
                else if (isX(m))
                  ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0';
                else if (isX(p))
                  // ~1.2 == >=1.2.0 <1.3.0
                  ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0';
                else if (pr) {
                  debug('replaceTilde pr', pr);
                  if (pr.charAt(0) !== '-')
                    pr = '-' + pr;
                  ret = '>=' + M + '.' + m + '.' + p + pr + ' <' + M + '.' + (+m + 1) + '.0';
                } else
                  // ~1.2.3 == >=1.2.3 <1.3.0
                  ret = '>=' + M + '.' + m + '.' + p + ' <' + M + '.' + (+m + 1) + '.0';
                debug('tilde return', ret);
                return ret;
              });
            }
            // ^ --> * (any, kinda silly)
            // ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0
            // ^2.0, ^2.0.x --> >=2.0.0 <3.0.0
            // ^1.2, ^1.2.x --> >=1.2.0 <2.0.0
            // ^1.2.3 --> >=1.2.3 <2.0.0
            // ^1.2.0 --> >=1.2.0 <2.0.0
            function replaceCarets(comp, loose) {
              return comp.trim().split(/\s+/).map(function (comp) {
                return replaceCaret(comp, loose);
              }).join(' ');
            }
            function replaceCaret(comp, loose) {
              debug('caret', comp, loose);
              var r = loose ? re[CARETLOOSE] : re[CARET];
              return comp.replace(r, function (_, M, m, p, pr) {
                debug('caret', comp, _, M, m, p, pr);
                var ret;
                if (isX(M))
                  ret = '';
                else if (isX(m))
                  ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0';
                else if (isX(p)) {
                  if (M === '0')
                    ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0';
                  else
                    ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0';
                } else if (pr) {
                  debug('replaceCaret pr', pr);
                  if (pr.charAt(0) !== '-')
                    pr = '-' + pr;
                  if (M === '0') {
                    if (m === '0')
                      ret = '>=' + M + '.' + m + '.' + p + pr + ' <' + M + '.' + m + '.' + (+p + 1);
                    else
                      ret = '>=' + M + '.' + m + '.' + p + pr + ' <' + M + '.' + (+m + 1) + '.0';
                  } else
                    ret = '>=' + M + '.' + m + '.' + p + pr + ' <' + (+M + 1) + '.0.0';
                } else {
                  debug('no pr');
                  if (M === '0') {
                    if (m === '0')
                      ret = '>=' + M + '.' + m + '.' + p + ' <' + M + '.' + m + '.' + (+p + 1);
                    else
                      ret = '>=' + M + '.' + m + '.' + p + ' <' + M + '.' + (+m + 1) + '.0';
                  } else
                    ret = '>=' + M + '.' + m + '.' + p + ' <' + (+M + 1) + '.0.0';
                }
                debug('caret return', ret);
                return ret;
              });
            }
            function replaceXRanges(comp, loose) {
              debug('replaceXRanges', comp, loose);
              return comp.split(/\s+/).map(function (comp) {
                return replaceXRange(comp, loose);
              }).join(' ');
            }
            function replaceXRange(comp, loose) {
              comp = comp.trim();
              var r = loose ? re[XRANGELOOSE] : re[XRANGE];
              return comp.replace(r, function (ret, gtlt, M, m, p, pr) {
                debug('xRange', comp, ret, gtlt, M, m, p, pr);
                var xM = isX(M);
                var xm = xM || isX(m);
                var xp = xm || isX(p);
                var anyX = xp;
                if (gtlt === '=' && anyX)
                  gtlt = '';
                if (xM) {
                  if (gtlt === '>' || gtlt === '<') {
                    // nothing is allowed
                    ret = '<0.0.0';
                  } else {
                    // nothing is forbidden
                    ret = '*';
                  }
                } else if (gtlt && anyX) {
                  // replace X with 0
                  if (xm)
                    m = 0;
                  if (xp)
                    p = 0;
                  if (gtlt === '>') {
                    // >1 => >=2.0.0
                    // >1.2 => >=1.3.0
                    // >1.2.3 => >= 1.2.4
                    gtlt = '>=';
                    if (xm) {
                      M = +M + 1;
                      m = 0;
                      p = 0;
                    } else if (xp) {
                      m = +m + 1;
                      p = 0;
                    }
                  } else if (gtlt === '<=') {
                    // <=0.7.x is actually <0.8.0, since any 0.7.x should
                    // pass.  Similarly, <=7.x is actually <8.0.0, etc.
                    gtlt = '<';
                    if (xm)
                      M = +M + 1;
                    else
                      m = +m + 1;
                  }
                  ret = gtlt + M + '.' + m + '.' + p;
                } else if (xm) {
                  ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0';
                } else if (xp) {
                  ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0';
                }
                debug('xRange return', ret);
                return ret;
              });
            }
            // Because * is AND-ed with everything else in the comparator,
            // and '' means "any version", just remove the *s entirely.
            function replaceStars(comp, loose) {
              debug('replaceStars', comp, loose);
              // Looseness is ignored here.  star is always as loose as it gets!
              return comp.trim().replace(re[STAR], '');
            }
            // This function is passed to string.replace(re[HYPHENRANGE])
            // M, m, patch, prerelease, build
            // 1.2 - 3.4.5 => >=1.2.0 <=3.4.5
            // 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do
            // 1.2 - 3.4 => >=1.2.0 <3.5.0
            function hyphenReplace($0, from, fM, fm, fp, fpr, fb, to, tM, tm, tp, tpr, tb) {
              if (isX(fM))
                from = '';
              else if (isX(fm))
                from = '>=' + fM + '.0.0';
              else if (isX(fp))
                from = '>=' + fM + '.' + fm + '.0';
              else
                from = '>=' + from;
              if (isX(tM))
                to = '';
              else if (isX(tm))
                to = '<' + (+tM + 1) + '.0.0';
              else if (isX(tp))
                to = '<' + tM + '.' + (+tm + 1) + '.0';
              else if (tpr)
                to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr;
              else
                to = '<=' + to;
              return (from + ' ' + to).trim();
            }
            // if ANY of the sets match ALL of its comparators, then pass
            Range.prototype.test = function (version) {
              if (!version)
                return false;
              if (typeof version === 'string')
                version = new SemVer(version, this.loose);
              for (var i = 0; i < this.set.length; i++) {
                if (testSet(this.set[i], version))
                  return true;
              }
              return false;
            };
            function testSet(set, version) {
              for (var i = 0; i < set.length; i++) {
                if (!set[i].test(version))
                  return false;
              }
              if (version.prerelease.length) {
                // Find the set of versions that are allowed to have prereleases
                // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0
                // That should allow `1.2.3-pr.2` to pass.
                // However, `1.2.4-alpha.notready` should NOT be allowed,
                // even though it's within the range set by the comparators.
                for (var i = 0; i < set.length; i++) {
                  debug(set[i].semver);
                  if (set[i].semver === ANY)
                    continue;
                  if (set[i].semver.prerelease.length > 0) {
                    var allowed = set[i].semver;
                    if (allowed.major === version.major && allowed.minor === version.minor && allowed.patch === version.patch)
                      return true;
                  }
                }
                // Version has a -pre, but it's not one of the ones we like.
                return false;
              }
              return true;
            }
            exports.satisfies = satisfies;
            function satisfies(version, range, loose) {
              try {
                range = new Range(range, loose);
              } catch (er) {
                return false;
              }
              return range.test(version);
            }
            exports.maxSatisfying = maxSatisfying;
            function maxSatisfying(versions, range, loose) {
              return versions.filter(function (version) {
                return satisfies(version, range, loose);
              }).sort(function (a, b) {
                return rcompare(a, b, loose);
              })[0] || null;
            }
            exports.minSatisfying = minSatisfying;
            function minSatisfying(versions, range, loose) {
              return versions.filter(function (version) {
                return satisfies(version, range, loose);
              }).sort(function (a, b) {
                return compare(a, b, loose);
              })[0] || null;
            }
            exports.validRange = validRange;
            function validRange(range, loose) {
              try {
                // Return '*' instead of '' so that truthiness works.
                // This will throw if it's invalid anyway
                return new Range(range, loose).range || '*';
              } catch (er) {
                return null;
              }
            }
            // Determine if version is less than all the versions possible in the range
            exports.ltr = ltr;
            function ltr(version, range, loose) {
              return outside(version, range, '<', loose);
            }
            // Determine if version is greater than all the versions possible in the range.
            exports.gtr = gtr;
            function gtr(version, range, loose) {
              return outside(version, range, '>', loose);
            }
            exports.outside = outside;
            function outside(version, range, hilo, loose) {
              version = new SemVer(version, loose);
              range = new Range(range, loose);
              var gtfn, ltefn, ltfn, comp, ecomp;
              switch (hilo) {
              case '>':
                gtfn = gt;
                ltefn = lte;
                ltfn = lt;
                comp = '>';
                ecomp = '>=';
                break;
              case '<':
                gtfn = lt;
                ltefn = gte;
                ltfn = gt;
                comp = '<';
                ecomp = '<=';
                break;
              default:
                throw new TypeError('Must provide a hilo val of "<" or ">"');
              }
              // If it satisifes the range it is not outside
              if (satisfies(version, range, loose)) {
                return false;
              }
              // From now on, variable terms are as if we're in "gtr" mode.
              // but note that everything is flipped for the "ltr" function.
              for (var i = 0; i < range.set.length; ++i) {
                var comparators = range.set[i];
                var high = null;
                var low = null;
                comparators.forEach(function (comparator) {
                  if (comparator.semver === ANY) {
                    comparator = new Comparator('>=0.0.0');
                  }
                  high = high || comparator;
                  low = low || comparator;
                  if (gtfn(comparator.semver, high.semver, loose)) {
                    high = comparator;
                  } else if (ltfn(comparator.semver, low.semver, loose)) {
                    low = comparator;
                  }
                });
                // If the edge version comparator has a operator then our version
                // isn't outside it
                if (high.operator === comp || high.operator === ecomp) {
                  return false;
                }
                // If the lowest version comparator has an operator and our version
                // is less than it then it isn't higher than the range
                if ((!low.operator || low.operator === comp) && ltefn(version, low.semver)) {
                  return false;
                } else if (low.operator === ecomp && ltfn(version, low.semver)) {
                  return false;
                }
              }
              return true;
            }
            exports.prerelease = prerelease;
            function prerelease(version, loose) {
              var parsed = parse(version, loose);
              return parsed && parsed.prerelease.length ? parsed.prerelease : null;
            }
          }.call(this, require('_process')));
        },
        { '_process': 2 }
      ]
    }, {}, [6]));
    (function e(t, n, r) {
      function s(o, u) {
        if (!n[o]) {
          if (!t[o]) {
            var a = typeof require == 'function' && require;
            if (!u && a)
              return a(o, !0);
            if (i)
              return i(o, !0);
            var f = new Error('Cannot find module \'' + o + '\'');
            throw f.code = 'MODULE_NOT_FOUND', f;
          }
          var l = n[o] = { exports: {} };
          t[o][0].call(l.exports, function (e) {
            var n = t[o][1][e];
            return s(n ? n : e);
          }, l, l.exports, e, t, n, r);
        }
        return n[o].exports;
      }
      var i = typeof require == 'function' && require;
      for (var o = 0; o < r.length; o++)
        s(r[o]);
      return s;
    }({
      1: [
        function (require, module, exports) {
        },
        {}
      ],
      2: [
        function (require, module, exports) {
          // shim for using process in browser
          var process = module.exports = {};
          // cached from whatever global is present so that test runners that stub it
          // don't break things.  But we need to wrap it in a try catch in case it is
          // wrapped in strict mode code which doesn't define any globals.  It's inside a
          // function because try/catches deoptimize in certain engines.
          var cachedSetTimeout;
          var cachedClearTimeout;
          function defaultSetTimout() {
            throw new Error('setTimeout has not been defined');
          }
          function defaultClearTimeout() {
            throw new Error('clearTimeout has not been defined');
          }
          (function () {
            try {
              if (typeof setTimeout === 'function') {
                cachedSetTimeout = setTimeout;
              } else {
                cachedSetTimeout = defaultSetTimout;
              }
            } catch (e) {
              cachedSetTimeout = defaultSetTimout;
            }
            try {
              if (typeof clearTimeout === 'function') {
                cachedClearTimeout = clearTimeout;
              } else {
                cachedClearTimeout = defaultClearTimeout;
              }
            } catch (e) {
              cachedClearTimeout = defaultClearTimeout;
            }
          }());
          function runTimeout(fun) {
            if (cachedSetTimeout === setTimeout) {
              //normal enviroments in sane situations
              return setTimeout(fun, 0);
            }
            // if setTimeout wasn't available but was latter defined
            if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
              cachedSetTimeout = setTimeout;
              return setTimeout(fun, 0);
            }
            try {
              // when when somebody has screwed with setTimeout but no I.E. maddness
              return cachedSetTimeout(fun, 0);
            } catch (e) {
              try {
                // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
                return cachedSetTimeout.call(null, fun, 0);
              } catch (e) {
                // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
                return cachedSetTimeout.call(this, fun, 0);
              }
            }
          }
          function runClearTimeout(marker) {
            if (cachedClearTimeout === clearTimeout) {
              //normal enviroments in sane situations
              return clearTimeout(marker);
            }
            // if clearTimeout wasn't available but was latter defined
            if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
              cachedClearTimeout = clearTimeout;
              return clearTimeout(marker);
            }
            try {
              // when when somebody has screwed with setTimeout but no I.E. maddness
              return cachedClearTimeout(marker);
            } catch (e) {
              try {
                // When we are in I.E. but the script has been evaled so I.E. doesn't  trust the global object when called normally
                return cachedClearTimeout.call(null, marker);
              } catch (e) {
                // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
                // Some versions of I.E. have different rules for clearTimeout vs setTimeout
                return cachedClearTimeout.call(this, marker);
              }
            }
          }
          var queue = [];
          var draining = false;
          var currentQueue;
          var queueIndex = -1;
          function cleanUpNextTick() {
            if (!draining || !currentQueue) {
              return;
            }
            draining = false;
            if (currentQueue.length) {
              queue = currentQueue.concat(queue);
            } else {
              queueIndex = -1;
            }
            if (queue.length) {
              drainQueue();
            }
          }
          function drainQueue() {
            if (draining) {
              return;
            }
            var timeout = runTimeout(cleanUpNextTick);
            draining = true;
            var len = queue.length;
            while (len) {
              currentQueue = queue;
              queue = [];
              while (++queueIndex < len) {
                if (currentQueue) {
                  currentQueue[queueIndex].run();
                }
              }
              queueIndex = -1;
              len = queue.length;
            }
            currentQueue = null;
            draining = false;
            runClearTimeout(timeout);
          }
          process.nextTick = function (fun) {
            var args = new Array(arguments.length - 1);
            if (arguments.length > 1) {
              for (var i = 1; i < arguments.length; i++) {
                args[i - 1] = arguments[i];
              }
            }
            queue.push(new Item(fun, args));
            if (queue.length === 1 && !draining) {
              runTimeout(drainQueue);
            }
          };
          // v8 likes predictible objects
          function Item(fun, array) {
            this.fun = fun;
            this.array = array;
          }
          Item.prototype.run = function () {
            this.fun.apply(null, this.array);
          };
          process.title = 'browser';
          process.browser = true;
          process.env = {};
          process.argv = [];
          process.version = '';
          // empty string to avoid regexp issues
          process.versions = {};
          function noop() {
          }
          process.on = noop;
          process.addListener = noop;
          process.once = noop;
          process.off = noop;
          process.removeListener = noop;
          process.removeAllListeners = noop;
          process.emit = noop;
          process.binding = function (name) {
            throw new Error('process.binding is not supported');
          };
          process.cwd = function () {
            return '/';
          };
          process.chdir = function (dir) {
            throw new Error('process.chdir is not supported');
          };
          process.umask = function () {
            return 0;
          };
        },
        {}
      ],
      3: [
        function (require, module, exports) {
          if (typeof Object.create === 'function') {
            // implementation from standard node.js 'util' module
            module.exports = function inherits(ctor, superCtor) {
              ctor.super_ = superCtor;
              ctor.prototype = Object.create(superCtor.prototype, {
                constructor: {
                  value: ctor,
                  enumerable: false,
                  writable: true,
                  configurable: true
                }
              });
            };
          } else {
            // old school shim for old browsers
            module.exports = function inherits(ctor, superCtor) {
              ctor.super_ = superCtor;
              var TempCtor = function () {
              };
              TempCtor.prototype = superCtor.prototype;
              ctor.prototype = new TempCtor();
              ctor.prototype.constructor = ctor;
            };
          }
        },
        {}
      ],
      4: [
        function (require, module, exports) {
          module.exports = function isBuffer(arg) {
            return arg && typeof arg === 'object' && typeof arg.copy === 'function' && typeof arg.fill === 'function' && typeof arg.readUInt8 === 'function';
          };
        },
        {}
      ],
      5: [
        function (require, module, exports) {
          (function (process, global) {
            // Copyright Joyent, Inc. and other Node contributors.
            //
            // Permission is hereby granted, free of charge, to any person obtaining a
            // copy of this software and associated documentation files (the
            // "Software"), to deal in the Software without restriction, including
            // without limitation the rights to use, copy, modify, merge, publish,
            // distribute, sublicense, and/or sell copies of the Software, and to permit
            // persons to whom the Software is furnished to do so, subject to the
            // following conditions:
            //
            // The above copyright notice and this permission notice shall be included
            // in all copies or substantial portions of the Software.
            //
            // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
            // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
            // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
            // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
            // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
            // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
            // USE OR OTHER DEALINGS IN THE SOFTWARE.
            var formatRegExp = /%[sdj%]/g;
            exports.format = function (f) {
              if (!isString(f)) {
                var objects = [];
                for (var i = 0; i < arguments.length; i++) {
                  objects.push(inspect(arguments[i]));
                }
                return objects.join(' ');
              }
              var i = 1;
              var args = arguments;
              var len = args.length;
              var str = String(f).replace(formatRegExp, function (x) {
                if (x === '%%')
                  return '%';
                if (i >= len)
                  return x;
                switch (x) {
                case '%s':
                  return String(args[i++]);
                case '%d':
                  return Number(args[i++]);
                case '%j':
                  try {
                    return JSON.stringify(args[i++]);
                  } catch (_) {
                    return '[Circular]';
                  }
                default:
                  return x;
                }
              });
              for (var x = args[i]; i < len; x = args[++i]) {
                if (isNull(x) || !isObject(x)) {
                  str += ' ' + x;
                } else {
                  str += ' ' + inspect(x);
                }
              }
              return str;
            };
            // Mark that a method should not be used.
            // Returns a modified function which warns once by default.
            // If --no-deprecation is set, then it is a no-op.
            exports.deprecate = function (fn, msg) {
              // Allow for deprecating things in the process of starting up.
              if (isUndefined(global.process)) {
                return function () {
                  return exports.deprecate(fn, msg).apply(this, arguments);
                };
              }
              if (process.noDeprecation === true) {
                return fn;
              }
              var warned = false;
              function deprecated() {
                if (!warned) {
                  if (process.throwDeprecation) {
                    throw new Error(msg);
                  } else if (process.traceDeprecation) {
                    console.trace(msg);
                  } else {
                    console.error(msg);
                  }
                  warned = true;
                }
                return fn.apply(this, arguments);
              }
              return deprecated;
            };
            var debugs = {};
            var debugEnviron;
            exports.debuglog = function (set) {
              if (isUndefined(debugEnviron))
                debugEnviron = process.env.NODE_DEBUG || '';
              set = set.toUpperCase();
              if (!debugs[set]) {
                if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
                  var pid = process.pid;
                  debugs[set] = function () {
                    var msg = exports.format.apply(exports, arguments);
                    console.error('%s %d: %s', set, pid, msg);
                  };
                } else {
                  debugs[set] = function () {
                  };
                }
              }
              return debugs[set];
            };
            /**
             * Echos the value of a value. Trys to print the value out
             * in the best way possible given the different types.
             *
             * @param {Object} obj The object to print out.
             * @param {Object} opts Optional options object that alters the output.
             */
            /* legacy: obj, showHidden, depth, colors*/
            function inspect(obj, opts) {
              // default options
              var ctx = {
                seen: [],
                stylize: stylizeNoColor
              };
              // legacy...
              if (arguments.length >= 3)
                ctx.depth = arguments[2];
              if (arguments.length >= 4)
                ctx.colors = arguments[3];
              if (isBoolean(opts)) {
                // legacy...
                ctx.showHidden = opts;
              } else if (opts) {
                // got an "options" object
                exports._extend(ctx, opts);
              }
              // set default options
              if (isUndefined(ctx.showHidden))
                ctx.showHidden = false;
              if (isUndefined(ctx.depth))
                ctx.depth = 2;
              if (isUndefined(ctx.colors))
                ctx.colors = false;
              if (isUndefined(ctx.customInspect))
                ctx.customInspect = true;
              if (ctx.colors)
                ctx.stylize = stylizeWithColor;
              return formatValue(ctx, obj, ctx.depth);
            }
            exports.inspect = inspect;
            // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
            inspect.colors = {
              'bold': [
                1,
                22
              ],
              'italic': [
                3,
                23
              ],
              'underline': [
                4,
                24
              ],
              'inverse': [
                7,
                27
              ],
              'white': [
                37,
                39
              ],
              'grey': [
                90,
                39
              ],
              'black': [
                30,
                39
              ],
              'blue': [
                34,
                39
              ],
              'cyan': [
                36,
                39
              ],
              'green': [
                32,
                39
              ],
              'magenta': [
                35,
                39
              ],
              'red': [
                31,
                39
              ],
              'yellow': [
                33,
                39
              ]
            };
            // Don't use 'blue' not visible on cmd.exe
            inspect.styles = {
              'special': 'cyan',
              'number': 'yellow',
              'boolean': 'yellow',
              'undefined': 'grey',
              'null': 'bold',
              'string': 'green',
              'date': 'magenta',
              // "name": intentionally not styling
              'regexp': 'red'
            };
            function stylizeWithColor(str, styleType) {
              var style = inspect.styles[styleType];
              if (style) {
                return '\x1B[' + inspect.colors[style][0] + 'm' + str + '\x1B[' + inspect.colors[style][1] + 'm';
              } else {
                return str;
              }
            }
            function stylizeNoColor(str, styleType) {
              return str;
            }
            function arrayToHash(array) {
              var hash = {};
              array.forEach(function (val, idx) {
                hash[val] = true;
              });
              return hash;
            }
            function formatValue(ctx, value, recurseTimes) {
              // Provide a hook for user-specified inspect functions.
              // Check that value is an object with an inspect function on it
              if (ctx.customInspect && value && isFunction(value.inspect) && // Filter out the util module, it's inspect function is special
                value.inspect !== exports.inspect && // Also filter out any prototype objects using the circular check.
                !(value.constructor && value.constructor.prototype === value)) {
                var ret = value.inspect(recurseTimes, ctx);
                if (!isString(ret)) {
                  ret = formatValue(ctx, ret, recurseTimes);
                }
                return ret;
              }
              // Primitive types cannot have properties
              var primitive = formatPrimitive(ctx, value);
              if (primitive) {
                return primitive;
              }
              // Look up the keys of the object.
              var keys = Object.keys(value);
              var visibleKeys = arrayToHash(keys);
              if (ctx.showHidden) {
                keys = Object.getOwnPropertyNames(value);
              }
              // IE doesn't make error fields non-enumerable
              // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx
              if (isError(value) && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) {
                return formatError(value);
              }
              // Some type of object without properties can be shortcutted.
              if (keys.length === 0) {
                if (isFunction(value)) {
                  var name = value.name ? ': ' + value.name : '';
                  return ctx.stylize('[Function' + name + ']', 'special');
                }
                if (isRegExp(value)) {
                  return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
                }
                if (isDate(value)) {
                  return ctx.stylize(Date.prototype.toString.call(value), 'date');
                }
                if (isError(value)) {
                  return formatError(value);
                }
              }
              var base = '', array = false, braces = [
                  '{',
                  '}'
                ];
              // Make Array say that they are Array
              if (isArray(value)) {
                array = true;
                braces = [
                  '[',
                  ']'
                ];
              }
              // Make functions say that they are functions
              if (isFunction(value)) {
                var n = value.name ? ': ' + value.name : '';
                base = ' [Function' + n + ']';
              }
              // Make RegExps say that they are RegExps
              if (isRegExp(value)) {
                base = ' ' + RegExp.prototype.toString.call(value);
              }
              // Make dates with properties first say the date
              if (isDate(value)) {
                base = ' ' + Date.prototype.toUTCString.call(value);
              }
              // Make error with message first say the error
              if (isError(value)) {
                base = ' ' + formatError(value);
              }
              if (keys.length === 0 && (!array || value.length == 0)) {
                return braces[0] + base + braces[1];
              }
              if (recurseTimes < 0) {
                if (isRegExp(value)) {
                  return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
                } else {
                  return ctx.stylize('[Object]', 'special');
                }
              }
              ctx.seen.push(value);
              var output;
              if (array) {
                output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
              } else {
                output = keys.map(function (key) {
                  return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
                });
              }
              ctx.seen.pop();
              return reduceToSingleString(output, base, braces);
            }
            function formatPrimitive(ctx, value) {
              if (isUndefined(value))
                return ctx.stylize('undefined', 'undefined');
              if (isString(value)) {
                var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '').replace(/'/g, '\\\'').replace(/\\"/g, '"') + '\'';
                return ctx.stylize(simple, 'string');
              }
              if (isNumber(value))
                return ctx.stylize('' + value, 'number');
              if (isBoolean(value))
                return ctx.stylize('' + value, 'boolean');
              // For some reason typeof null is "object", so special case here.
              if (isNull(value))
                return ctx.stylize('null', 'null');
            }
            function formatError(value) {
              return '[' + Error.prototype.toString.call(value) + ']';
            }
            function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
              var output = [];
              for (var i = 0, l = value.length; i < l; ++i) {
                if (hasOwnProperty(value, String(i))) {
                  output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, String(i), true));
                } else {
                  output.push('');
                }
              }
              keys.forEach(function (key) {
                if (!key.match(/^\d+$/)) {
                  output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, key, true));
                }
              });
              return output;
            }
            function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
              var name, str, desc;
              desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
              if (desc.get) {
                if (desc.set) {
                  str = ctx.stylize('[Getter/Setter]', 'special');
                } else {
                  str = ctx.stylize('[Getter]', 'special');
                }
              } else {
                if (desc.set) {
                  str = ctx.stylize('[Setter]', 'special');
                }
              }
              if (!hasOwnProperty(visibleKeys, key)) {
                name = '[' + key + ']';
              }
              if (!str) {
                if (ctx.seen.indexOf(desc.value) < 0) {
                  if (isNull(recurseTimes)) {
                    str = formatValue(ctx, desc.value, null);
                  } else {
                    str = formatValue(ctx, desc.value, recurseTimes - 1);
                  }
                  if (str.indexOf('\n') > -1) {
                    if (array) {
                      str = str.split('\n').map(function (line) {
                        return '  ' + line;
                      }).join('\n').substr(2);
                    } else {
                      str = '\n' + str.split('\n').map(function (line) {
                        return '   ' + line;
                      }).join('\n');
                    }
                  }
                } else {
                  str = ctx.stylize('[Circular]', 'special');
                }
              }
              if (isUndefined(name)) {
                if (array && key.match(/^\d+$/)) {
                  return str;
                }
                name = JSON.stringify('' + key);
                if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
                  name = name.substr(1, name.length - 2);
                  name = ctx.stylize(name, 'name');
                } else {
                  name = name.replace(/'/g, '\\\'').replace(/\\"/g, '"').replace(/(^"|"$)/g, '\'');
                  name = ctx.stylize(name, 'string');
                }
              }
              return name + ': ' + str;
            }
            function reduceToSingleString(output, base, braces) {
              var numLinesEst = 0;
              var length = output.reduce(function (prev, cur) {
                numLinesEst++;
                if (cur.indexOf('\n') >= 0)
                  numLinesEst++;
                return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
              }, 0);
              if (length > 60) {
                return braces[0] + (base === '' ? '' : base + '\n ') + ' ' + output.join(',\n  ') + ' ' + braces[1];
              }
              return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
            }
            // NOTE: These type checking functions intentionally don't use `instanceof`
            // because it is fragile and can be easily faked with `Object.create()`.
            function isArray(ar) {
              return Array.isArray(ar);
            }
            exports.isArray = isArray;
            function isBoolean(arg) {
              return typeof arg === 'boolean';
            }
            exports.isBoolean = isBoolean;
            function isNull(arg) {
              return arg === null;
            }
            exports.isNull = isNull;
            function isNullOrUndefined(arg) {
              return arg == null;
            }
            exports.isNullOrUndefined = isNullOrUndefined;
            function isNumber(arg) {
              return typeof arg === 'number';
            }
            exports.isNumber = isNumber;
            function isString(arg) {
              return typeof arg === 'string';
            }
            exports.isString = isString;
            function isSymbol(arg) {
              return typeof arg === 'symbol';
            }
            exports.isSymbol = isSymbol;
            function isUndefined(arg) {
              return arg === void 0;
            }
            exports.isUndefined = isUndefined;
            function isRegExp(re) {
              return isObject(re) && objectToString(re) === '[object RegExp]';
            }
            exports.isRegExp = isRegExp;
            function isObject(arg) {
              return typeof arg === 'object' && arg !== null;
            }
            exports.isObject = isObject;
            function isDate(d) {
              return isObject(d) && objectToString(d) === '[object Date]';
            }
            exports.isDate = isDate;
            function isError(e) {
              return isObject(e) && (objectToString(e) === '[object Error]' || e instanceof Error);
            }
            exports.isError = isError;
            function isFunction(arg) {
              return typeof arg === 'function';
            }
            exports.isFunction = isFunction;
            function isPrimitive(arg) {
              return arg === null || typeof arg === 'boolean' || typeof arg === 'number' || typeof arg === 'string' || typeof arg === 'symbol' || // ES6 symbol
              typeof arg === 'undefined';
            }
            exports.isPrimitive = isPrimitive;
            exports.isBuffer = require('./support/isBuffer');
            function objectToString(o) {
              return Object.prototype.toString.call(o);
            }
            function pad(n) {
              return n < 10 ? '0' + n.toString(10) : n.toString(10);
            }
            var months = [
              'Jan',
              'Feb',
              'Mar',
              'Apr',
              'May',
              'Jun',
              'Jul',
              'Aug',
              'Sep',
              'Oct',
              'Nov',
              'Dec'
            ];
            // 26 Feb 16:19:34
            function timestamp() {
              var d = new Date();
              var time = [
                pad(d.getHours()),
                pad(d.getMinutes()),
                pad(d.getSeconds())
              ].join(':');
              return [
                d.getDate(),
                months[d.getMonth()],
                time
              ].join(' ');
            }
            // log is just a thin wrapper to console.log that prepends a timestamp
            exports.log = function () {
              console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
            };
            /**
             * Inherit the prototype methods from one constructor into another.
             *
             * The Function.prototype.inherits from lang.js rewritten as a standalone
             * function (not on Function.prototype). NOTE: If this file is to be loaded
             * during bootstrapping this function needs to be rewritten using some native
             * functions as prototype setup using normal JavaScript does not work as
             * expected during bootstrapping (see mirror.js in r114903).
             *
             * @param {function} ctor Constructor function which needs to inherit the
             *     prototype.
             * @param {function} superCtor Constructor function to inherit prototype from.
             */
            exports.inherits = require('inherits');
            exports._extend = function (origin, add) {
              // Don't do anything if add isn't an object
              if (!add || !isObject(add))
                return origin;
              var keys = Object.keys(add);
              var i = keys.length;
              while (i--) {
                origin[keys[i]] = add[keys[i]];
              }
              return origin;
            };
            function hasOwnProperty(obj, prop) {
              return Object.prototype.hasOwnProperty.call(obj, prop);
            }
          }.call(this, require('_process'), typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : typeof window !== 'undefined' ? window : {}));
        },
        {
          './support/isBuffer': 4,
          '_process': 2,
          'inherits': 3
        }
      ],
      6: [
        function (require, module, exports) {
          'use strict';
          var layouts = require('../layouts'), consoleLog = console.log.bind(console);
          function consoleAppender(layout, timezoneOffset) {
            layout = layout || layouts.colouredLayout;
            return function (loggingEvent) {
              consoleLog(layout(loggingEvent, timezoneOffset));
            };
          }
          function configure(config) {
            var layout;
            if (config.layout) {
              layout = layouts.layout(config.layout.type, config.layout);
            }
            return consoleAppender(layout, config.timezoneOffset);
          }
          var exps = {
            appender: consoleAppender,
            configure: configure
          };
          module.exports = exps;
          exportModule('LYNX.Logger.ThirdParty.appender.console', exps);
        },
        { '../layouts': 8 }
      ],
      7: [
        function (require, module, exports) {
          'use strict';
          exports.ISO8601_FORMAT = 'yyyy-MM-dd hh:mm:ss.SSS';
          exports.ISO8601_WITH_TZ_OFFSET_FORMAT = 'yyyy-MM-ddThh:mm:ssO';
          exports.DATETIME_FORMAT = 'dd MM yyyy hh:mm:ss.SSS';
          exports.ABSOLUTETIME_FORMAT = 'hh:mm:ss.SSS';
          function padWithZeros(vNumber, width) {
            var numAsString = vNumber + '';
            while (numAsString.length < width) {
              numAsString = '0' + numAsString;
            }
            return numAsString;
          }
          function addZero(vNumber) {
            return padWithZeros(vNumber, 2);
          }
          /**
           * Formats the TimeOffest
           * Thanks to http://www.svendtofte.com/code/date_format/
           * @private
           */
          function offset(timezoneOffset) {
            // Difference to Greenwich time (GMT) in hours
            var os = Math.abs(timezoneOffset);
            var h = String(Math.floor(os / 60));
            var m = String(os % 60);
            if (h.length == 1) {
              h = '0' + h;
            }
            if (m.length == 1) {
              m = '0' + m;
            }
            return timezoneOffset < 0 ? '+' + h + m : '-' + h + m;
          }
          exports.asString = function (date, timezoneOffset) {
            /*jshint -W071 */
            var format = exports.ISO8601_FORMAT;
            if (typeof date === 'string') {
              format = arguments[0];
              date = arguments[1];
              timezoneOffset = arguments[2];
            }
            // make the date independent of the system timezone by working with UTC
            if (timezoneOffset === undefined) {
              timezoneOffset = date.getTimezoneOffset();
            }
            date.setUTCMinutes(date.getUTCMinutes() - timezoneOffset);
            var vDay = addZero(date.getUTCDate());
            var vMonth = addZero(date.getUTCMonth() + 1);
            var vYearLong = addZero(date.getUTCFullYear());
            var vYearShort = addZero(date.getUTCFullYear().toString().substring(2, 4));
            var vYear = format.indexOf('yyyy') > -1 ? vYearLong : vYearShort;
            var vHour = addZero(date.getUTCHours());
            var vMinute = addZero(date.getUTCMinutes());
            var vSecond = addZero(date.getUTCSeconds());
            var vMillisecond = padWithZeros(date.getUTCMilliseconds(), 3);
            var vTimeZone = offset(timezoneOffset);
            date.setUTCMinutes(date.getUTCMinutes() + timezoneOffset);
            var formatted = format.replace(/dd/g, vDay).replace(/MM/g, vMonth).replace(/y{1,4}/g, vYear).replace(/hh/g, vHour).replace(/mm/g, vMinute).replace(/ss/g, vSecond).replace(/SSS/g, vMillisecond).replace(/O/g, vTimeZone);
            return formatted;
          };  /*jshint +W071 */
        },
        {}
      ],
      8: [
        function (require, module, exports) {
          (function (process) {
            'use strict';
            var dateFormat = require('./date_format'), os = require('os'), eol = os.EOL || '\n', util = require('util'), semver = require('semver'), replacementRegExp = /%[sdj]/g, layoutMakers = {
                'messagePassThrough': function () {
                  return messagePassThroughLayout;
                },
                'basic': function () {
                  return basicLayout;
                },
                'colored': function () {
                  return colouredLayout;
                },
                'coloured': function () {
                  return colouredLayout;
                },
                'pattern': function (config) {
                  return patternLayout(config && config.pattern, config && config.tokens);
                },
                'dummy': function () {
                  return dummyLayout;
                }
              }, colours = {
                ALL: 'grey',
                TRACE: 'blue',
                DEBUG: 'cyan',
                INFO: 'green',
                WARN: 'yellow',
                ERROR: 'red',
                FATAL: 'magenta',
                OFF: 'grey'
              };
            function wrapErrorsWithInspect(items) {
              return items.map(function (item) {
                if (item instanceof Error && item.stack) {
                  return {
                    inspect: function () {
                      if (semver.satisfies(process.version, '>=6')) {
                        return util.format(item);
                      } else {
                        return util.format(item) + '\n' + item.stack;
                      }
                    }
                  };
                } else {
                  return item;
                }
              });
            }
            function formatLogData(logData) {
              var data = Array.isArray(logData) ? logData : Array.prototype.slice.call(arguments);
              return util.format.apply(util, wrapErrorsWithInspect(data));
            }
            var styles = {
              //styles
              'bold': [
                1,
                22
              ],
              'italic': [
                3,
                23
              ],
              'underline': [
                4,
                24
              ],
              'inverse': [
                7,
                27
              ],
              //grayscale
              'white': [
                37,
                39
              ],
              'grey': [
                90,
                39
              ],
              'black': [
                90,
                39
              ],
              //colors
              'blue': [
                34,
                39
              ],
              'cyan': [
                36,
                39
              ],
              'green': [
                32,
                39
              ],
              'magenta': [
                35,
                39
              ],
              'red': [
                31,
                39
              ],
              'yellow': [
                33,
                39
              ]
            };
            function colorizeStart(style) {
              return style ? '\x1B[' + styles[style][0] + 'm' : '';
            }
            function colorizeEnd(style) {
              return style ? '\x1B[' + styles[style][1] + 'm' : '';
            }
            /**
             * Taken from masylum's fork (https://github.com/masylum/log4js-node)
             */
            function colorize(str, style) {
              return colorizeStart(style) + str + colorizeEnd(style);
            }
            function timestampLevelAndCategory(loggingEvent, colour, timezoneOffest) {
              var output = colorize(formatLogData('[%s] [%s] %s - ', dateFormat.asString(loggingEvent.startTime, timezoneOffest), loggingEvent.level, loggingEvent.categoryName), colour);
              return output;
            }
            /**
             * BasicLayout is a simple layout for storing the logs. The logs are stored
             * in following format:
             * <pre>
             * [startTime] [logLevel] categoryName - message\n
             * </pre>
             *
             * @author Stephan Strittmatter
             */
            function basicLayout(loggingEvent, timezoneOffset) {
              return timestampLevelAndCategory(loggingEvent, undefined, timezoneOffset) + formatLogData(loggingEvent.data);
            }
            /**
             * colouredLayout - taken from masylum's fork.
             * same as basicLayout, but with colours.
             */
            function colouredLayout(loggingEvent, timezoneOffset) {
              return timestampLevelAndCategory(loggingEvent, colours[loggingEvent.level.toString()], timezoneOffset) + formatLogData(loggingEvent.data);
            }
            function messagePassThroughLayout(loggingEvent) {
              return formatLogData(loggingEvent.data);
            }
            function dummyLayout(loggingEvent) {
              return loggingEvent.data[0];
            }
            /**
             * PatternLayout
             * Format for specifiers is %[padding].[truncation][field]{[format]}
             * e.g. %5.10p - left pad the log level by 5 characters, up to a max of 10
             * Fields can be any of:
             *  - %r time in toLocaleTimeString format
             *  - %p log level
             *  - %c log category
             *  - %h hostname
             *  - %m log data
             *  - %d date in various formats
             *  - %% %
             *  - %n newline
             *  - %z pid
             *  - %x{<tokenname>} add dynamic tokens to your log. Tokens are specified in the tokens parameter
             * You can use %[ and %] to define a colored block.
             *
             * Tokens are specified as simple key:value objects.
             * The key represents the token name whereas the value can be a string or function
             * which is called to extract the value to put in the log message. If token is not
             * found, it doesn't replace the field.
             *
             * A sample token would be: { "pid" : function() { return process.pid; } }
             *
             * Takes a pattern string, array of tokens and returns a layout function.
             * @param {String} Log format pattern String
             * @param {object} map object of different tokens
             * @param {number} timezone offset in minutes
             * @return {Function}
             * @author Stephan Strittmatter
             * @author Jan Schmidle
             */
            function patternLayout(pattern, tokens, timezoneOffset) {
              // jshint maxstatements:22
              var TTCC_CONVERSION_PATTERN = '%r %p %c - %m%n';
              var regex = /%(-?[0-9]+)?(\.?[0-9]+)?([\[\]cdhmnprzxy%])(\{([^\}]+)\})?|([^%]+)/;
              pattern = pattern || TTCC_CONVERSION_PATTERN;
              function categoryName(loggingEvent, specifier) {
                var loggerName = loggingEvent.categoryName;
                if (specifier) {
                  var precision = parseInt(specifier, 10);
                  var loggerNameBits = loggerName.split('.');
                  if (precision < loggerNameBits.length) {
                    loggerName = loggerNameBits.slice(loggerNameBits.length - precision).join('.');
                  }
                }
                return loggerName;
              }
              function formatAsDate(loggingEvent, specifier) {
                var format = dateFormat.ISO8601_FORMAT;
                if (specifier) {
                  format = specifier;
                  // Pick up special cases
                  if (format == 'ISO8601') {
                    format = dateFormat.ISO8601_FORMAT;
                  } else if (format == 'ISO8601_WITH_TZ_OFFSET') {
                    format = dateFormat.ISO8601_WITH_TZ_OFFSET_FORMAT;
                  } else if (format == 'ABSOLUTE') {
                    format = dateFormat.ABSOLUTETIME_FORMAT;
                  } else if (format == 'DATE') {
                    format = dateFormat.DATETIME_FORMAT;
                  }
                }
                // Format the date
                return dateFormat.asString(format, loggingEvent.startTime, timezoneOffset);
              }
              function hostname() {
                return os.hostname().toString();
              }
              function formatMessage(loggingEvent) {
                return formatLogData(loggingEvent.data);
              }
              function endOfLine() {
                return eol;
              }
              function logLevel(loggingEvent) {
                return loggingEvent.level.toString();
              }
              function startTime(loggingEvent) {
                return dateFormat.asString('hh:mm:ss', loggingEvent.startTime, timezoneOffset);
              }
              function startColour(loggingEvent) {
                return colorizeStart(colours[loggingEvent.level.toString()]);
              }
              function endColour(loggingEvent) {
                return colorizeEnd(colours[loggingEvent.level.toString()]);
              }
              function percent() {
                return '%';
              }
              function pid(loggingEvent) {
                if (loggingEvent && loggingEvent.pid) {
                  return loggingEvent.pid;
                } else {
                  return process.pid;
                }
              }
              function clusterInfo(loggingEvent, specifier) {
                if (loggingEvent.cluster && specifier) {
                  return specifier.replace('%m', loggingEvent.cluster.master).replace('%w', loggingEvent.cluster.worker).replace('%i', loggingEvent.cluster.workerId);
                } else if (loggingEvent.cluster) {
                  return loggingEvent.cluster.worker + '@' + loggingEvent.cluster.master;
                } else {
                  return pid();
                }
              }
              function userDefined(loggingEvent, specifier) {
                if (typeof tokens[specifier] !== 'undefined') {
                  if (typeof tokens[specifier] === 'function') {
                    return tokens[specifier](loggingEvent);
                  } else {
                    return tokens[specifier];
                  }
                }
                return null;
              }
              var replacers = {
                'c': categoryName,
                'd': formatAsDate,
                'h': hostname,
                'm': formatMessage,
                'n': endOfLine,
                'p': logLevel,
                'r': startTime,
                '[': startColour,
                ']': endColour,
                'y': clusterInfo,
                'z': pid,
                '%': percent,
                'x': userDefined
              };
              function replaceToken(conversionCharacter, loggingEvent, specifier) {
                return replacers[conversionCharacter](loggingEvent, specifier);
              }
              function truncate(truncation, toTruncate) {
                var len;
                if (truncation) {
                  len = parseInt(truncation.substr(1), 10);
                  return toTruncate.substring(0, len);
                }
                return toTruncate;
              }
              function pad(padding, toPad) {
                var len;
                if (padding) {
                  if (padding.charAt(0) == '-') {
                    len = parseInt(padding.substr(1), 10);
                    // Right pad with spaces
                    while (toPad.length < len) {
                      toPad += ' ';
                    }
                  } else {
                    len = parseInt(padding, 10);
                    // Left pad with spaces
                    while (toPad.length < len) {
                      toPad = ' ' + toPad;
                    }
                  }
                }
                return toPad;
              }
              function truncateAndPad(toTruncAndPad, truncation, padding) {
                var replacement = toTruncAndPad;
                replacement = truncate(truncation, replacement);
                replacement = pad(padding, replacement);
                return replacement;
              }
              return function (loggingEvent) {
                var formattedString = '';
                var result;
                var searchString = pattern;
                while (result = regex.exec(searchString)) {
                  var matchedString = result[0];
                  var padding = result[1];
                  var truncation = result[2];
                  var conversionCharacter = result[3];
                  var specifier = result[5];
                  var text = result[6];
                  // Check if the pattern matched was just normal text
                  if (text) {
                    formattedString += '' + text;
                  } else {
                    // Create a raw replacement string based on the conversion
                    // character and specifier
                    var replacement = replaceToken(conversionCharacter, loggingEvent, specifier);
                    formattedString += truncateAndPad(replacement, truncation, padding);
                  }
                  searchString = searchString.substr(result.index + result[0].length);
                }
                return formattedString;
              };
            }
            module.exports = {
              basicLayout: basicLayout,
              messagePassThroughLayout: messagePassThroughLayout,
              patternLayout: patternLayout,
              colouredLayout: colouredLayout,
              coloredLayout: colouredLayout,
              dummyLayout: dummyLayout,
              addLayout: function (name, serializerGenerator) {
                layoutMakers[name] = serializerGenerator;
              },
              layout: function (name, config) {
                return layoutMakers[name] && layoutMakers[name](config);
              }
            };
          }.call(this, require('_process')));
        },
        {
          './date_format': 7,
          '_process': 2,
          'os': 1,
          'semver': 9,
          'util': 5
        }
      ],
      9: [
        function (require, module, exports) {
          (function (process) {
            exports = module.exports = SemVer;
            // The debug function is excluded entirely from the minified version.
            /* nomin */
            var debug;
            /* nomin */
            if (typeof process === 'object' && /* nomin */
              process.env && /* nomin */
              process.env.NODE_DEBUG && /* nomin */
              /\bsemver\b/i.test(process.env.NODE_DEBUG))
              /* nomin */
              debug = function () {
                /* nomin */
                var args = Array.prototype.slice.call(arguments, 0);
                /* nomin */
                args.unshift('SEMVER');
                /* nomin */
                console.log.apply(console, args);  /* nomin */
              };  /* nomin */
            else
              /* nomin */
              debug = function () {
              };
            // Note: this is the semver.org version of the spec that it implements
            // Not necessarily the package version of this code.
            exports.SEMVER_SPEC_VERSION = '2.0.0';
            var MAX_LENGTH = 256;
            var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;
            // The actual regexps go on exports.re
            var re = exports.re = [];
            var src = exports.src = [];
            var R = 0;
            // The following Regular Expressions can be used for tokenizing,
            // validating, and parsing SemVer version strings.
            // ## Numeric Identifier
            // A single `0`, or a non-zero digit followed by zero or more digits.
            var NUMERICIDENTIFIER = R++;
            src[NUMERICIDENTIFIER] = '0|[1-9]\\d*';
            var NUMERICIDENTIFIERLOOSE = R++;
            src[NUMERICIDENTIFIERLOOSE] = '[0-9]+';
            // ## Non-numeric Identifier
            // Zero or more digits, followed by a letter or hyphen, and then zero or
            // more letters, digits, or hyphens.
            var NONNUMERICIDENTIFIER = R++;
            src[NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*';
            // ## Main Version
            // Three dot-separated numeric identifiers.
            var MAINVERSION = R++;
            src[MAINVERSION] = '(' + src[NUMERICIDENTIFIER] + ')\\.' + '(' + src[NUMERICIDENTIFIER] + ')\\.' + '(' + src[NUMERICIDENTIFIER] + ')';
            var MAINVERSIONLOOSE = R++;
            src[MAINVERSIONLOOSE] = '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + '(' + src[NUMERICIDENTIFIERLOOSE] + ')';
            // ## Pre-release Version Identifier
            // A numeric identifier, or a non-numeric identifier.
            var PRERELEASEIDENTIFIER = R++;
            src[PRERELEASEIDENTIFIER] = '(?:' + src[NUMERICIDENTIFIER] + '|' + src[NONNUMERICIDENTIFIER] + ')';
            var PRERELEASEIDENTIFIERLOOSE = R++;
            src[PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[NUMERICIDENTIFIERLOOSE] + '|' + src[NONNUMERICIDENTIFIER] + ')';
            // ## Pre-release Version
            // Hyphen, followed by one or more dot-separated pre-release version
            // identifiers.
            var PRERELEASE = R++;
            src[PRERELEASE] = '(?:-(' + src[PRERELEASEIDENTIFIER] + '(?:\\.' + src[PRERELEASEIDENTIFIER] + ')*))';
            var PRERELEASELOOSE = R++;
            src[PRERELEASELOOSE] = '(?:-?(' + src[PRERELEASEIDENTIFIERLOOSE] + '(?:\\.' + src[PRERELEASEIDENTIFIERLOOSE] + ')*))';
            // ## Build Metadata Identifier
            // Any combination of digits, letters, or hyphens.
            var BUILDIDENTIFIER = R++;
            src[BUILDIDENTIFIER] = '[0-9A-Za-z-]+';
            // ## Build Metadata
            // Plus sign, followed by one or more period-separated build metadata
            // identifiers.
            var BUILD = R++;
            src[BUILD] = '(?:\\+(' + src[BUILDIDENTIFIER] + '(?:\\.' + src[BUILDIDENTIFIER] + ')*))';
            // ## Full Version String
            // A main version, followed optionally by a pre-release version and
            // build metadata.
            // Note that the only major, minor, patch, and pre-release sections of
            // the version string are capturing groups.  The build metadata is not a
            // capturing group, because it should not ever be used in version
            // comparison.
            var FULL = R++;
            var FULLPLAIN = 'v?' + src[MAINVERSION] + src[PRERELEASE] + '?' + src[BUILD] + '?';
            src[FULL] = '^' + FULLPLAIN + '$';
            // like full, but allows v1.2.3 and =1.2.3, which people do sometimes.
            // also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty
            // common in the npm registry.
            var LOOSEPLAIN = '[v=\\s]*' + src[MAINVERSIONLOOSE] + src[PRERELEASELOOSE] + '?' + src[BUILD] + '?';
            var LOOSE = R++;
            src[LOOSE] = '^' + LOOSEPLAIN + '$';
            var GTLT = R++;
            src[GTLT] = '((?:<|>)?=?)';
            // Something like "2.*" or "1.2.x".
            // Note that "x.x" is a valid xRange identifer, meaning "any version"
            // Only the first item is strictly required.
            var XRANGEIDENTIFIERLOOSE = R++;
            src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + '|x|X|\\*';
            var XRANGEIDENTIFIER = R++;
            src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + '|x|X|\\*';
            var XRANGEPLAIN = R++;
            src[XRANGEPLAIN] = '[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + '(?:' + src[PRERELEASE] + ')?' + src[BUILD] + '?' + ')?)?';
            var XRANGEPLAINLOOSE = R++;
            src[XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + '(?:' + src[PRERELEASELOOSE] + ')?' + src[BUILD] + '?' + ')?)?';
            var XRANGE = R++;
            src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$';
            var XRANGELOOSE = R++;
            src[XRANGELOOSE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAINLOOSE] + '$';
            // Tilde ranges.
            // Meaning is "reasonably at or greater than"
            var LONETILDE = R++;
            src[LONETILDE] = '(?:~>?)';
            var TILDETRIM = R++;
            src[TILDETRIM] = '(\\s*)' + src[LONETILDE] + '\\s+';
            re[TILDETRIM] = new RegExp(src[TILDETRIM], 'g');
            var tildeTrimReplace = '$1~';
            var TILDE = R++;
            src[TILDE] = '^' + src[LONETILDE] + src[XRANGEPLAIN] + '$';
            var TILDELOOSE = R++;
            src[TILDELOOSE] = '^' + src[LONETILDE] + src[XRANGEPLAINLOOSE] + '$';
            // Caret ranges.
            // Meaning is "at least and backwards compatible with"
            var LONECARET = R++;
            src[LONECARET] = '(?:\\^)';
            var CARETTRIM = R++;
            src[CARETTRIM] = '(\\s*)' + src[LONECARET] + '\\s+';
            re[CARETTRIM] = new RegExp(src[CARETTRIM], 'g');
            var caretTrimReplace = '$1^';
            var CARET = R++;
            src[CARET] = '^' + src[LONECARET] + src[XRANGEPLAIN] + '$';
            var CARETLOOSE = R++;
            src[CARETLOOSE] = '^' + src[LONECARET] + src[XRANGEPLAINLOOSE] + '$';
            // A simple gt/lt/eq thing, or just "" to indicate "any version"
            var COMPARATORLOOSE = R++;
            src[COMPARATORLOOSE] = '^' + src[GTLT] + '\\s*(' + LOOSEPLAIN + ')$|^$';
            var COMPARATOR = R++;
            src[COMPARATOR] = '^' + src[GTLT] + '\\s*(' + FULLPLAIN + ')$|^$';
            // An expression to strip any whitespace between the gtlt and the thing
            // it modifies, so that `> 1.2.3` ==> `>1.2.3`
            var COMPARATORTRIM = R++;
            src[COMPARATORTRIM] = '(\\s*)' + src[GTLT] + '\\s*(' + LOOSEPLAIN + '|' + src[XRANGEPLAIN] + ')';
            // this one has to use the /g flag
            re[COMPARATORTRIM] = new RegExp(src[COMPARATORTRIM], 'g');
            var comparatorTrimReplace = '$1$2$3';
            // Something like `1.2.3 - 1.2.4`
            // Note that these all use the loose form, because they'll be
            // checked against either the strict or loose comparator form
            // later.
            var HYPHENRANGE = R++;
            src[HYPHENRANGE] = '^\\s*(' + src[XRANGEPLAIN] + ')' + '\\s+-\\s+' + '(' + src[XRANGEPLAIN] + ')' + '\\s*$';
            var HYPHENRANGELOOSE = R++;
            src[HYPHENRANGELOOSE] = '^\\s*(' + src[XRANGEPLAINLOOSE] + ')' + '\\s+-\\s+' + '(' + src[XRANGEPLAINLOOSE] + ')' + '\\s*$';
            // Star ranges basically just allow anything at all.
            var STAR = R++;
            src[STAR] = '(<|>)?=?\\s*\\*';
            // Compile to actual regexp objects.
            // All are flag-free, unless they were created above with a flag.
            for (var i = 0; i < R; i++) {
              debug(i, src[i]);
              if (!re[i])
                re[i] = new RegExp(src[i]);
            }
            exports.parse = parse;
            function parse(version, loose) {
              if (version instanceof SemVer)
                return version;
              if (typeof version !== 'string')
                return null;
              if (version.length > MAX_LENGTH)
                return null;
              var r = loose ? re[LOOSE] : re[FULL];
              if (!r.test(version))
                return null;
              try {
                return new SemVer(version, loose);
              } catch (er) {
                return null;
              }
            }
            exports.valid = valid;
            function valid(version, loose) {
              var v = parse(version, loose);
              return v ? v.version : null;
            }
            exports.clean = clean;
            function clean(version, loose) {
              var s = parse(version.trim().replace(/^[=v]+/, ''), loose);
              return s ? s.version : null;
            }
            exports.SemVer = SemVer;
            function SemVer(version, loose) {
              if (version instanceof SemVer) {
                if (version.loose === loose)
                  return version;
                else
                  version = version.version;
              } else if (typeof version !== 'string') {
                throw new TypeError('Invalid Version: ' + version);
              }
              if (version.length > MAX_LENGTH)
                throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters');
              if (!(this instanceof SemVer))
                return new SemVer(version, loose);
              debug('SemVer', version, loose);
              this.loose = loose;
              var m = version.trim().match(loose ? re[LOOSE] : re[FULL]);
              if (!m)
                throw new TypeError('Invalid Version: ' + version);
              this.raw = version;
              // these are actually numbers
              this.major = +m[1];
              this.minor = +m[2];
              this.patch = +m[3];
              if (this.major > MAX_SAFE_INTEGER || this.major < 0)
                throw new TypeError('Invalid major version');
              if (this.minor > MAX_SAFE_INTEGER || this.minor < 0)
                throw new TypeError('Invalid minor version');
              if (this.patch > MAX_SAFE_INTEGER || this.patch < 0)
                throw new TypeError('Invalid patch version');
              // numberify any prerelease numeric ids
              if (!m[4])
                this.prerelease = [];
              else
                this.prerelease = m[4].split('.').map(function (id) {
                  if (/^[0-9]+$/.test(id)) {
                    var num = +id;
                    if (num >= 0 && num < MAX_SAFE_INTEGER)
                      return num;
                  }
                  return id;
                });
              this.build = m[5] ? m[5].split('.') : [];
              this.format();
            }
            SemVer.prototype.format = function () {
              this.version = this.major + '.' + this.minor + '.' + this.patch;
              if (this.prerelease.length)
                this.version += '-' + this.prerelease.join('.');
              return this.version;
            };
            SemVer.prototype.toString = function () {
              return this.version;
            };
            SemVer.prototype.compare = function (other) {
              debug('SemVer.compare', this.version, this.loose, other);
              if (!(other instanceof SemVer))
                other = new SemVer(other, this.loose);
              return this.compareMain(other) || this.comparePre(other);
            };
            SemVer.prototype.compareMain = function (other) {
              if (!(other instanceof SemVer))
                other = new SemVer(other, this.loose);
              return compareIdentifiers(this.major, other.major) || compareIdentifiers(this.minor, other.minor) || compareIdentifiers(this.patch, other.patch);
            };
            SemVer.prototype.comparePre = function (other) {
              if (!(other instanceof SemVer))
                other = new SemVer(other, this.loose);
              // NOT having a prerelease is > having one
              if (this.prerelease.length && !other.prerelease.length)
                return -1;
              else if (!this.prerelease.length && other.prerelease.length)
                return 1;
              else if (!this.prerelease.length && !other.prerelease.length)
                return 0;
              var i = 0;
              do {
                var a = this.prerelease[i];
                var b = other.prerelease[i];
                debug('prerelease compare', i, a, b);
                if (a === undefined && b === undefined)
                  return 0;
                else if (b === undefined)
                  return 1;
                else if (a === undefined)
                  return -1;
                else if (a === b)
                  continue;
                else
                  return compareIdentifiers(a, b);
              } while (++i);
            };
            // preminor will bump the version up to the next minor release, and immediately
            // down to pre-release. premajor and prepatch work the same way.
            SemVer.prototype.inc = function (release, identifier) {
              switch (release) {
              case 'premajor':
                this.prerelease.length = 0;
                this.patch = 0;
                this.minor = 0;
                this.major++;
                this.inc('pre', identifier);
                break;
              case 'preminor':
                this.prerelease.length = 0;
                this.patch = 0;
                this.minor++;
                this.inc('pre', identifier);
                break;
              case 'prepatch':
                // If this is already a prerelease, it will bump to the next version
                // drop any prereleases that might already exist, since they are not
                // relevant at this point.
                this.prerelease.length = 0;
                this.inc('patch', identifier);
                this.inc('pre', identifier);
                break;
              // If the input is a non-prerelease version, this acts the same as
              // prepatch.
              case 'prerelease':
                if (this.prerelease.length === 0)
                  this.inc('patch', identifier);
                this.inc('pre', identifier);
                break;
              case 'major':
                // If this is a pre-major version, bump up to the same major version.
                // Otherwise increment major.
                // 1.0.0-5 bumps to 1.0.0
                // 1.1.0 bumps to 2.0.0
                if (this.minor !== 0 || this.patch !== 0 || this.prerelease.length === 0)
                  this.major++;
                this.minor = 0;
                this.patch = 0;
                this.prerelease = [];
                break;
              case 'minor':
                // If this is a pre-minor version, bump up to the same minor version.
                // Otherwise increment minor.
                // 1.2.0-5 bumps to 1.2.0
                // 1.2.1 bumps to 1.3.0
                if (this.patch !== 0 || this.prerelease.length === 0)
                  this.minor++;
                this.patch = 0;
                this.prerelease = [];
                break;
              case 'patch':
                // If this is not a pre-release version, it will increment the patch.
                // If it is a pre-release it will bump up to the same patch version.
                // 1.2.0-5 patches to 1.2.0
                // 1.2.0 patches to 1.2.1
                if (this.prerelease.length === 0)
                  this.patch++;
                this.prerelease = [];
                break;
              // This probably shouldn't be used publicly.
              // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction.
              case 'pre':
                if (this.prerelease.length === 0)
                  this.prerelease = [0];
                else {
                  var i = this.prerelease.length;
                  while (--i >= 0) {
                    if (typeof this.prerelease[i] === 'number') {
                      this.prerelease[i]++;
                      i = -2;
                    }
                  }
                  if (i === -1)
                    // didn't increment anything
                    this.prerelease.push(0);
                }
                if (identifier) {
                  // 1.2.0-beta.1 bumps to 1.2.0-beta.2,
                  // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0
                  if (this.prerelease[0] === identifier) {
                    if (isNaN(this.prerelease[1]))
                      this.prerelease = [
                        identifier,
                        0
                      ];
                  } else
                    this.prerelease = [
                      identifier,
                      0
                    ];
                }
                break;
              default:
                throw new Error('invalid increment argument: ' + release);
              }
              this.format();
              this.raw = this.version;
              return this;
            };
            exports.inc = inc;
            function inc(version, release, loose, identifier) {
              if (typeof loose === 'string') {
                identifier = loose;
                loose = undefined;
              }
              try {
                return new SemVer(version, loose).inc(release, identifier).version;
              } catch (er) {
                return null;
              }
            }
            exports.diff = diff;
            function diff(version1, version2) {
              if (eq(version1, version2)) {
                return null;
              } else {
                var v1 = parse(version1);
                var v2 = parse(version2);
                if (v1.prerelease.length || v2.prerelease.length) {
                  for (var key in v1) {
                    if (key === 'major' || key === 'minor' || key === 'patch') {
                      if (v1[key] !== v2[key]) {
                        return 'pre' + key;
                      }
                    }
                  }
                  return 'prerelease';
                }
                for (var key in v1) {
                  if (key === 'major' || key === 'minor' || key === 'patch') {
                    if (v1[key] !== v2[key]) {
                      return key;
                    }
                  }
                }
              }
            }
            exports.compareIdentifiers = compareIdentifiers;
            var numeric = /^[0-9]+$/;
            function compareIdentifiers(a, b) {
              var anum = numeric.test(a);
              var bnum = numeric.test(b);
              if (anum && bnum) {
                a = +a;
                b = +b;
              }
              return anum && !bnum ? -1 : bnum && !anum ? 1 : a < b ? -1 : a > b ? 1 : 0;
            }
            exports.rcompareIdentifiers = rcompareIdentifiers;
            function rcompareIdentifiers(a, b) {
              return compareIdentifiers(b, a);
            }
            exports.major = major;
            function major(a, loose) {
              return new SemVer(a, loose).major;
            }
            exports.minor = minor;
            function minor(a, loose) {
              return new SemVer(a, loose).minor;
            }
            exports.patch = patch;
            function patch(a, loose) {
              return new SemVer(a, loose).patch;
            }
            exports.compare = compare;
            function compare(a, b, loose) {
              return new SemVer(a, loose).compare(b);
            }
            exports.compareLoose = compareLoose;
            function compareLoose(a, b) {
              return compare(a, b, true);
            }
            exports.rcompare = rcompare;
            function rcompare(a, b, loose) {
              return compare(b, a, loose);
            }
            exports.sort = sort;
            function sort(list, loose) {
              return list.sort(function (a, b) {
                return exports.compare(a, b, loose);
              });
            }
            exports.rsort = rsort;
            function rsort(list, loose) {
              return list.sort(function (a, b) {
                return exports.rcompare(a, b, loose);
              });
            }
            exports.gt = gt;
            function gt(a, b, loose) {
              return compare(a, b, loose) > 0;
            }
            exports.lt = lt;
            function lt(a, b, loose) {
              return compare(a, b, loose) < 0;
            }
            exports.eq = eq;
            function eq(a, b, loose) {
              return compare(a, b, loose) === 0;
            }
            exports.neq = neq;
            function neq(a, b, loose) {
              return compare(a, b, loose) !== 0;
            }
            exports.gte = gte;
            function gte(a, b, loose) {
              return compare(a, b, loose) >= 0;
            }
            exports.lte = lte;
            function lte(a, b, loose) {
              return compare(a, b, loose) <= 0;
            }
            exports.cmp = cmp;
            function cmp(a, op, b, loose) {
              var ret;
              switch (op) {
              case '===':
                if (typeof a === 'object')
                  a = a.version;
                if (typeof b === 'object')
                  b = b.version;
                ret = a === b;
                break;
              case '!==':
                if (typeof a === 'object')
                  a = a.version;
                if (typeof b === 'object')
                  b = b.version;
                ret = a !== b;
                break;
              case '':
              case '=':
              case '==':
                ret = eq(a, b, loose);
                break;
              case '!=':
                ret = neq(a, b, loose);
                break;
              case '>':
                ret = gt(a, b, loose);
                break;
              case '>=':
                ret = gte(a, b, loose);
                break;
              case '<':
                ret = lt(a, b, loose);
                break;
              case '<=':
                ret = lte(a, b, loose);
                break;
              default:
                throw new TypeError('Invalid operator: ' + op);
              }
              return ret;
            }
            exports.Comparator = Comparator;
            function Comparator(comp, loose) {
              if (comp instanceof Comparator) {
                if (comp.loose === loose)
                  return comp;
                else
                  comp = comp.value;
              }
              if (!(this instanceof Comparator))
                return new Comparator(comp, loose);
              debug('comparator', comp, loose);
              this.loose = loose;
              this.parse(comp);
              if (this.semver === ANY)
                this.value = '';
              else
                this.value = this.operator + this.semver.version;
              debug('comp', this);
            }
            var ANY = {};
            Comparator.prototype.parse = function (comp) {
              var r = this.loose ? re[COMPARATORLOOSE] : re[COMPARATOR];
              var m = comp.match(r);
              if (!m)
                throw new TypeError('Invalid comparator: ' + comp);
              this.operator = m[1];
              if (this.operator === '=')
                this.operator = '';
              // if it literally is just '>' or '' then allow anything.
              if (!m[2])
                this.semver = ANY;
              else
                this.semver = new SemVer(m[2], this.loose);
            };
            Comparator.prototype.toString = function () {
              return this.value;
            };
            Comparator.prototype.test = function (version) {
              debug('Comparator.test', version, this.loose);
              if (this.semver === ANY)
                return true;
              if (typeof version === 'string')
                version = new SemVer(version, this.loose);
              return cmp(version, this.operator, this.semver, this.loose);
            };
            exports.Range = Range;
            function Range(range, loose) {
              if (range instanceof Range && range.loose === loose)
                return range;
              if (!(this instanceof Range))
                return new Range(range, loose);
              this.loose = loose;
              // First, split based on boolean or ||
              this.raw = range;
              this.set = range.split(/\s*\|\|\s*/).map(function (range) {
                return this.parseRange(range.trim());
              }, this).filter(function (c) {
                // throw out any that are not relevant for whatever reason
                return c.length;
              });
              if (!this.set.length) {
                throw new TypeError('Invalid SemVer Range: ' + range);
              }
              this.format();
            }
            Range.prototype.format = function () {
              this.range = this.set.map(function (comps) {
                return comps.join(' ').trim();
              }).join('||').trim();
              return this.range;
            };
            Range.prototype.toString = function () {
              return this.range;
            };
            Range.prototype.parseRange = function (range) {
              var loose = this.loose;
              range = range.trim();
              debug('range', range, loose);
              // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`
              var hr = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE];
              range = range.replace(hr, hyphenReplace);
              debug('hyphen replace', range);
              // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
              range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace);
              debug('comparator trim', range, re[COMPARATORTRIM]);
              // `~ 1.2.3` => `~1.2.3`
              range = range.replace(re[TILDETRIM], tildeTrimReplace);
              // `^ 1.2.3` => `^1.2.3`
              range = range.replace(re[CARETTRIM], caretTrimReplace);
              // normalize spaces
              range = range.split(/\s+/).join(' ');
              // At this point, the range is completely trimmed and
              // ready to be split into comparators.
              var compRe = loose ? re[COMPARATORLOOSE] : re[COMPARATOR];
              var set = range.split(' ').map(function (comp) {
                return parseComparator(comp, loose);
              }).join(' ').split(/\s+/);
              if (this.loose) {
                // in loose mode, throw out any that are not valid comparators
                set = set.filter(function (comp) {
                  return !!comp.match(compRe);
                });
              }
              set = set.map(function (comp) {
                return new Comparator(comp, loose);
              });
              return set;
            };
            // Mostly just for testing and legacy API reasons
            exports.toComparators = toComparators;
            function toComparators(range, loose) {
              return new Range(range, loose).set.map(function (comp) {
                return comp.map(function (c) {
                  return c.value;
                }).join(' ').trim().split(' ');
              });
            }
            // comprised of xranges, tildes, stars, and gtlt's at this point.
            // already replaced the hyphen ranges
            // turn into a set of JUST comparators.
            function parseComparator(comp, loose) {
              debug('comp', comp);
              comp = replaceCarets(comp, loose);
              debug('caret', comp);
              comp = replaceTildes(comp, loose);
              debug('tildes', comp);
              comp = replaceXRanges(comp, loose);
              debug('xrange', comp);
              comp = replaceStars(comp, loose);
              debug('stars', comp);
              return comp;
            }
            function isX(id) {
              return !id || id.toLowerCase() === 'x' || id === '*';
            }
            // ~, ~> --> * (any, kinda silly)
            // ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0
            // ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0
            // ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0
            // ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0
            // ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0
            function replaceTildes(comp, loose) {
              return comp.trim().split(/\s+/).map(function (comp) {
                return replaceTilde(comp, loose);
              }).join(' ');
            }
            function replaceTilde(comp, loose) {
              var r = loose ? re[TILDELOOSE] : re[TILDE];
              return comp.replace(r, function (_, M, m, p, pr) {
                debug('tilde', comp, _, M, m, p, pr);
                var ret;
                if (isX(M))
                  ret = '';
                else if (isX(m))
                  ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0';
                else if (isX(p))
                  // ~1.2 == >=1.2.0 <1.3.0
                  ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0';
                else if (pr) {
                  debug('replaceTilde pr', pr);
                  if (pr.charAt(0) !== '-')
                    pr = '-' + pr;
                  ret = '>=' + M + '.' + m + '.' + p + pr + ' <' + M + '.' + (+m + 1) + '.0';
                } else
                  // ~1.2.3 == >=1.2.3 <1.3.0
                  ret = '>=' + M + '.' + m + '.' + p + ' <' + M + '.' + (+m + 1) + '.0';
                debug('tilde return', ret);
                return ret;
              });
            }
            // ^ --> * (any, kinda silly)
            // ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0
            // ^2.0, ^2.0.x --> >=2.0.0 <3.0.0
            // ^1.2, ^1.2.x --> >=1.2.0 <2.0.0
            // ^1.2.3 --> >=1.2.3 <2.0.0
            // ^1.2.0 --> >=1.2.0 <2.0.0
            function replaceCarets(comp, loose) {
              return comp.trim().split(/\s+/).map(function (comp) {
                return replaceCaret(comp, loose);
              }).join(' ');
            }
            function replaceCaret(comp, loose) {
              debug('caret', comp, loose);
              var r = loose ? re[CARETLOOSE] : re[CARET];
              return comp.replace(r, function (_, M, m, p, pr) {
                debug('caret', comp, _, M, m, p, pr);
                var ret;
                if (isX(M))
                  ret = '';
                else if (isX(m))
                  ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0';
                else if (isX(p)) {
                  if (M === '0')
                    ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0';
                  else
                    ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0';
                } else if (pr) {
                  debug('replaceCaret pr', pr);
                  if (pr.charAt(0) !== '-')
                    pr = '-' + pr;
                  if (M === '0') {
                    if (m === '0')
                      ret = '>=' + M + '.' + m + '.' + p + pr + ' <' + M + '.' + m + '.' + (+p + 1);
                    else
                      ret = '>=' + M + '.' + m + '.' + p + pr + ' <' + M + '.' + (+m + 1) + '.0';
                  } else
                    ret = '>=' + M + '.' + m + '.' + p + pr + ' <' + (+M + 1) + '.0.0';
                } else {
                  debug('no pr');
                  if (M === '0') {
                    if (m === '0')
                      ret = '>=' + M + '.' + m + '.' + p + ' <' + M + '.' + m + '.' + (+p + 1);
                    else
                      ret = '>=' + M + '.' + m + '.' + p + ' <' + M + '.' + (+m + 1) + '.0';
                  } else
                    ret = '>=' + M + '.' + m + '.' + p + ' <' + (+M + 1) + '.0.0';
                }
                debug('caret return', ret);
                return ret;
              });
            }
            function replaceXRanges(comp, loose) {
              debug('replaceXRanges', comp, loose);
              return comp.split(/\s+/).map(function (comp) {
                return replaceXRange(comp, loose);
              }).join(' ');
            }
            function replaceXRange(comp, loose) {
              comp = comp.trim();
              var r = loose ? re[XRANGELOOSE] : re[XRANGE];
              return comp.replace(r, function (ret, gtlt, M, m, p, pr) {
                debug('xRange', comp, ret, gtlt, M, m, p, pr);
                var xM = isX(M);
                var xm = xM || isX(m);
                var xp = xm || isX(p);
                var anyX = xp;
                if (gtlt === '=' && anyX)
                  gtlt = '';
                if (xM) {
                  if (gtlt === '>' || gtlt === '<') {
                    // nothing is allowed
                    ret = '<0.0.0';
                  } else {
                    // nothing is forbidden
                    ret = '*';
                  }
                } else if (gtlt && anyX) {
                  // replace X with 0
                  if (xm)
                    m = 0;
                  if (xp)
                    p = 0;
                  if (gtlt === '>') {
                    // >1 => >=2.0.0
                    // >1.2 => >=1.3.0
                    // >1.2.3 => >= 1.2.4
                    gtlt = '>=';
                    if (xm) {
                      M = +M + 1;
                      m = 0;
                      p = 0;
                    } else if (xp) {
                      m = +m + 1;
                      p = 0;
                    }
                  } else if (gtlt === '<=') {
                    // <=0.7.x is actually <0.8.0, since any 0.7.x should
                    // pass.  Similarly, <=7.x is actually <8.0.0, etc.
                    gtlt = '<';
                    if (xm)
                      M = +M + 1;
                    else
                      m = +m + 1;
                  }
                  ret = gtlt + M + '.' + m + '.' + p;
                } else if (xm) {
                  ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0';
                } else if (xp) {
                  ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0';
                }
                debug('xRange return', ret);
                return ret;
              });
            }
            // Because * is AND-ed with everything else in the comparator,
            // and '' means "any version", just remove the *s entirely.
            function replaceStars(comp, loose) {
              debug('replaceStars', comp, loose);
              // Looseness is ignored here.  star is always as loose as it gets!
              return comp.trim().replace(re[STAR], '');
            }
            // This function is passed to string.replace(re[HYPHENRANGE])
            // M, m, patch, prerelease, build
            // 1.2 - 3.4.5 => >=1.2.0 <=3.4.5
            // 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do
            // 1.2 - 3.4 => >=1.2.0 <3.5.0
            function hyphenReplace($0, from, fM, fm, fp, fpr, fb, to, tM, tm, tp, tpr, tb) {
              if (isX(fM))
                from = '';
              else if (isX(fm))
                from = '>=' + fM + '.0.0';
              else if (isX(fp))
                from = '>=' + fM + '.' + fm + '.0';
              else
                from = '>=' + from;
              if (isX(tM))
                to = '';
              else if (isX(tm))
                to = '<' + (+tM + 1) + '.0.0';
              else if (isX(tp))
                to = '<' + tM + '.' + (+tm + 1) + '.0';
              else if (tpr)
                to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr;
              else
                to = '<=' + to;
              return (from + ' ' + to).trim();
            }
            // if ANY of the sets match ALL of its comparators, then pass
            Range.prototype.test = function (version) {
              if (!version)
                return false;
              if (typeof version === 'string')
                version = new SemVer(version, this.loose);
              for (var i = 0; i < this.set.length; i++) {
                if (testSet(this.set[i], version))
                  return true;
              }
              return false;
            };
            function testSet(set, version) {
              for (var i = 0; i < set.length; i++) {
                if (!set[i].test(version))
                  return false;
              }
              if (version.prerelease.length) {
                // Find the set of versions that are allowed to have prereleases
                // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0
                // That should allow `1.2.3-pr.2` to pass.
                // However, `1.2.4-alpha.notready` should NOT be allowed,
                // even though it's within the range set by the comparators.
                for (var i = 0; i < set.length; i++) {
                  debug(set[i].semver);
                  if (set[i].semver === ANY)
                    continue;
                  if (set[i].semver.prerelease.length > 0) {
                    var allowed = set[i].semver;
                    if (allowed.major === version.major && allowed.minor === version.minor && allowed.patch === version.patch)
                      return true;
                  }
                }
                // Version has a -pre, but it's not one of the ones we like.
                return false;
              }
              return true;
            }
            exports.satisfies = satisfies;
            function satisfies(version, range, loose) {
              try {
                range = new Range(range, loose);
              } catch (er) {
                return false;
              }
              return range.test(version);
            }
            exports.maxSatisfying = maxSatisfying;
            function maxSatisfying(versions, range, loose) {
              return versions.filter(function (version) {
                return satisfies(version, range, loose);
              }).sort(function (a, b) {
                return rcompare(a, b, loose);
              })[0] || null;
            }
            exports.minSatisfying = minSatisfying;
            function minSatisfying(versions, range, loose) {
              return versions.filter(function (version) {
                return satisfies(version, range, loose);
              }).sort(function (a, b) {
                return compare(a, b, loose);
              })[0] || null;
            }
            exports.validRange = validRange;
            function validRange(range, loose) {
              try {
                // Return '*' instead of '' so that truthiness works.
                // This will throw if it's invalid anyway
                return new Range(range, loose).range || '*';
              } catch (er) {
                return null;
              }
            }
            // Determine if version is less than all the versions possible in the range
            exports.ltr = ltr;
            function ltr(version, range, loose) {
              return outside(version, range, '<', loose);
            }
            // Determine if version is greater than all the versions possible in the range.
            exports.gtr = gtr;
            function gtr(version, range, loose) {
              return outside(version, range, '>', loose);
            }
            exports.outside = outside;
            function outside(version, range, hilo, loose) {
              version = new SemVer(version, loose);
              range = new Range(range, loose);
              var gtfn, ltefn, ltfn, comp, ecomp;
              switch (hilo) {
              case '>':
                gtfn = gt;
                ltefn = lte;
                ltfn = lt;
                comp = '>';
                ecomp = '>=';
                break;
              case '<':
                gtfn = lt;
                ltefn = gte;
                ltfn = gt;
                comp = '<';
                ecomp = '<=';
                break;
              default:
                throw new TypeError('Must provide a hilo val of "<" or ">"');
              }
              // If it satisifes the range it is not outside
              if (satisfies(version, range, loose)) {
                return false;
              }
              // From now on, variable terms are as if we're in "gtr" mode.
              // but note that everything is flipped for the "ltr" function.
              for (var i = 0; i < range.set.length; ++i) {
                var comparators = range.set[i];
                var high = null;
                var low = null;
                comparators.forEach(function (comparator) {
                  if (comparator.semver === ANY) {
                    comparator = new Comparator('>=0.0.0');
                  }
                  high = high || comparator;
                  low = low || comparator;
                  if (gtfn(comparator.semver, high.semver, loose)) {
                    high = comparator;
                  } else if (ltfn(comparator.semver, low.semver, loose)) {
                    low = comparator;
                  }
                });
                // If the edge version comparator has a operator then our version
                // isn't outside it
                if (high.operator === comp || high.operator === ecomp) {
                  return false;
                }
                // If the lowest version comparator has an operator and our version
                // is less than it then it isn't higher than the range
                if ((!low.operator || low.operator === comp) && ltefn(version, low.semver)) {
                  return false;
                } else if (low.operator === ecomp && ltfn(version, low.semver)) {
                  return false;
                }
              }
              return true;
            }
            exports.prerelease = prerelease;
            function prerelease(version, loose) {
              var parsed = parse(version, loose);
              return parsed && parsed.prerelease.length ? parsed.prerelease : null;
            }
          }.call(this, require('_process')));
        },
        { '_process': 2 }
      ]
    }, {}, [6]));
    (function e(t, n, r) {
      function s(o, u) {
        if (!n[o]) {
          if (!t[o]) {
            var a = typeof require == 'function' && require;
            if (!u && a)
              return a(o, !0);
            if (i)
              return i(o, !0);
            var f = new Error('Cannot find module \'' + o + '\'');
            throw f.code = 'MODULE_NOT_FOUND', f;
          }
          var l = n[o] = { exports: {} };
          t[o][0].call(l.exports, function (e) {
            var n = t[o][1][e];
            return s(n ? n : e);
          }, l, l.exports, e, t, n, r);
        }
        return n[o].exports;
      }
      var i = typeof require == 'function' && require;
      for (var o = 0; o < r.length; o++)
        s(r[o]);
      return s;
    }({
      1: [
        function (require, module, exports) {
        },
        {}
      ],
      2: [
        function (require, module, exports) {
          arguments[4][1][0].apply(exports, arguments);
        },
        { 'dup': 1 }
      ],
      3: [
        function (require, module, exports) {
          // Copyright Joyent, Inc. and other Node contributors.
          //
          // Permission is hereby granted, free of charge, to any person obtaining a
          // copy of this software and associated documentation files (the
          // "Software"), to deal in the Software without restriction, including
          // without limitation the rights to use, copy, modify, merge, publish,
          // distribute, sublicense, and/or sell copies of the Software, and to permit
          // persons to whom the Software is furnished to do so, subject to the
          // following conditions:
          //
          // The above copyright notice and this permission notice shall be included
          // in all copies or substantial portions of the Software.
          //
          // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
          // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
          // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
          // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
          // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
          // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
          // USE OR OTHER DEALINGS IN THE SOFTWARE.
          function EventEmitter() {
            this._events = this._events || {};
            this._maxListeners = this._maxListeners || undefined;
          }
          module.exports = EventEmitter;
          // Backwards-compat with node 0.10.x
          EventEmitter.EventEmitter = EventEmitter;
          EventEmitter.prototype._events = undefined;
          EventEmitter.prototype._maxListeners = undefined;
          // By default EventEmitters will print a warning if more than 10 listeners are
          // added to it. This is a useful default which helps finding memory leaks.
          EventEmitter.defaultMaxListeners = 10;
          // Obviously not all Emitters should be limited to 10. This function allows
          // that to be increased. Set to zero for unlimited.
          EventEmitter.prototype.setMaxListeners = function (n) {
            if (!isNumber(n) || n < 0 || isNaN(n))
              throw TypeError('n must be a positive number');
            this._maxListeners = n;
            return this;
          };
          EventEmitter.prototype.emit = function (type) {
            var er, handler, len, args, i, listeners;
            if (!this._events)
              this._events = {};
            // If there is no 'error' event listener then throw.
            if (type === 'error') {
              if (!this._events.error || isObject(this._events.error) && !this._events.error.length) {
                er = arguments[1];
                if (er instanceof Error) {
                  throw er;  // Unhandled 'error' event
                } else {
                  // At least give some kind of context to the user
                  var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
                  err.context = er;
                  throw err;
                }
              }
            }
            handler = this._events[type];
            if (isUndefined(handler))
              return false;
            if (isFunction(handler)) {
              switch (arguments.length) {
              // fast cases
              case 1:
                handler.call(this);
                break;
              case 2:
                handler.call(this, arguments[1]);
                break;
              case 3:
                handler.call(this, arguments[1], arguments[2]);
                break;
              // slower
              default:
                args = Array.prototype.slice.call(arguments, 1);
                handler.apply(this, args);
              }
            } else if (isObject(handler)) {
              args = Array.prototype.slice.call(arguments, 1);
              listeners = handler.slice();
              len = listeners.length;
              for (i = 0; i < len; i++)
                listeners[i].apply(this, args);
            }
            return true;
          };
          EventEmitter.prototype.addListener = function (type, listener) {
            var m;
            if (!isFunction(listener))
              throw TypeError('listener must be a function');
            if (!this._events)
              this._events = {};
            // To avoid recursion in the case that type === "newListener"! Before
            // adding it to the listeners, first emit "newListener".
            if (this._events.newListener)
              this.emit('newListener', type, isFunction(listener.listener) ? listener.listener : listener);
            if (!this._events[type])
              // Optimize the case of one listener. Don't need the extra array object.
              this._events[type] = listener;
            else if (isObject(this._events[type]))
              // If we've already got an array, just append.
              this._events[type].push(listener);
            else
              // Adding the second element, need to change to array.
              this._events[type] = [
                this._events[type],
                listener
              ];
            // Check for listener leak
            if (isObject(this._events[type]) && !this._events[type].warned) {
              if (!isUndefined(this._maxListeners)) {
                m = this._maxListeners;
              } else {
                m = EventEmitter.defaultMaxListeners;
              }
              if (m && m > 0 && this._events[type].length > m) {
                this._events[type].warned = true;
                console.error('(node) warning: possible EventEmitter memory ' + 'leak detected. %d listeners added. ' + 'Use emitter.setMaxListeners() to increase limit.', this._events[type].length);
                if (typeof console.trace === 'function') {
                  // not supported in IE 10
                  console.trace();
                }
              }
            }
            return this;
          };
          EventEmitter.prototype.on = EventEmitter.prototype.addListener;
          EventEmitter.prototype.once = function (type, listener) {
            if (!isFunction(listener))
              throw TypeError('listener must be a function');
            var fired = false;
            function g() {
              this.removeListener(type, g);
              if (!fired) {
                fired = true;
                listener.apply(this, arguments);
              }
            }
            g.listener = listener;
            this.on(type, g);
            return this;
          };
          // emits a 'removeListener' event iff the listener was removed
          EventEmitter.prototype.removeListener = function (type, listener) {
            var list, position, length, i;
            if (!isFunction(listener))
              throw TypeError('listener must be a function');
            if (!this._events || !this._events[type])
              return this;
            list = this._events[type];
            length = list.length;
            position = -1;
            if (list === listener || isFunction(list.listener) && list.listener === listener) {
              delete this._events[type];
              if (this._events.removeListener)
                this.emit('removeListener', type, listener);
            } else if (isObject(list)) {
              for (i = length; i-- > 0;) {
                if (list[i] === listener || list[i].listener && list[i].listener === listener) {
                  position = i;
                  break;
                }
              }
              if (position < 0)
                return this;
              if (list.length === 1) {
                list.length = 0;
                delete this._events[type];
              } else {
                list.splice(position, 1);
              }
              if (this._events.removeListener)
                this.emit('removeListener', type, listener);
            }
            return this;
          };
          EventEmitter.prototype.removeAllListeners = function (type) {
            var key, listeners;
            if (!this._events)
              return this;
            // not listening for removeListener, no need to emit
            if (!this._events.removeListener) {
              if (arguments.length === 0)
                this._events = {};
              else if (this._events[type])
                delete this._events[type];
              return this;
            }
            // emit removeListener for all listeners on all events
            if (arguments.length === 0) {
              for (key in this._events) {
                if (key === 'removeListener')
                  continue;
                this.removeAllListeners(key);
              }
              this.removeAllListeners('removeListener');
              this._events = {};
              return this;
            }
            listeners = this._events[type];
            if (isFunction(listeners)) {
              this.removeListener(type, listeners);
            } else if (listeners) {
              // LIFO order
              while (listeners.length)
                this.removeListener(type, listeners[listeners.length - 1]);
            }
            delete this._events[type];
            return this;
          };
          EventEmitter.prototype.listeners = function (type) {
            var ret;
            if (!this._events || !this._events[type])
              ret = [];
            else if (isFunction(this._events[type]))
              ret = [this._events[type]];
            else
              ret = this._events[type].slice();
            return ret;
          };
          EventEmitter.prototype.listenerCount = function (type) {
            if (this._events) {
              var evlistener = this._events[type];
              if (isFunction(evlistener))
                return 1;
              else if (evlistener)
                return evlistener.length;
            }
            return 0;
          };
          EventEmitter.listenerCount = function (emitter, type) {
            return emitter.listenerCount(type);
          };
          function isFunction(arg) {
            return typeof arg === 'function';
          }
          function isNumber(arg) {
            return typeof arg === 'number';
          }
          function isObject(arg) {
            return typeof arg === 'object' && arg !== null;
          }
          function isUndefined(arg) {
            return arg === void 0;
          }
        },
        {}
      ],
      4: [
        function (require, module, exports) {
          (function (process) {
            // Copyright Joyent, Inc. and other Node contributors.
            //
            // Permission is hereby granted, free of charge, to any person obtaining a
            // copy of this software and associated documentation files (the
            // "Software"), to deal in the Software without restriction, including
            // without limitation the rights to use, copy, modify, merge, publish,
            // distribute, sublicense, and/or sell copies of the Software, and to permit
            // persons to whom the Software is furnished to do so, subject to the
            // following conditions:
            //
            // The above copyright notice and this permission notice shall be included
            // in all copies or substantial portions of the Software.
            //
            // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
            // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
            // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
            // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
            // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
            // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
            // USE OR OTHER DEALINGS IN THE SOFTWARE.
            // resolves . and .. elements in a path array with directory names there
            // must be no slashes, empty elements, or device names (c:\) in the array
            // (so also no leading and trailing slashes - it does not distinguish
            // relative and absolute paths)
            function normalizeArray(parts, allowAboveRoot) {
              // if the path tries to go above the root, `up` ends up > 0
              var up = 0;
              for (var i = parts.length - 1; i >= 0; i--) {
                var last = parts[i];
                if (last === '.') {
                  parts.splice(i, 1);
                } else if (last === '..') {
                  parts.splice(i, 1);
                  up++;
                } else if (up) {
                  parts.splice(i, 1);
                  up--;
                }
              }
              // if the path is allowed to go above the root, restore leading ..s
              if (allowAboveRoot) {
                for (; up--; up) {
                  parts.unshift('..');
                }
              }
              return parts;
            }
            // Split a filename into [root, dir, basename, ext], unix version
            // 'root' is just a slash, or nothing.
            var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
            var splitPath = function (filename) {
              return splitPathRe.exec(filename).slice(1);
            };
            // path.resolve([from ...], to)
            // posix version
            exports.resolve = function () {
              var resolvedPath = '', resolvedAbsolute = false;
              for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
                var path = i >= 0 ? arguments[i] : process.cwd();
                // Skip empty and invalid entries
                if (typeof path !== 'string') {
                  throw new TypeError('Arguments to path.resolve must be strings');
                } else if (!path) {
                  continue;
                }
                resolvedPath = path + '/' + resolvedPath;
                resolvedAbsolute = path.charAt(0) === '/';
              }
              // At this point the path should be resolved to a full absolute path, but
              // handle relative paths to be safe (might happen when process.cwd() fails)
              // Normalize the path
              resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function (p) {
                return !!p;
              }), !resolvedAbsolute).join('/');
              return (resolvedAbsolute ? '/' : '') + resolvedPath || '.';
            };
            // path.normalize(path)
            // posix version
            exports.normalize = function (path) {
              var isAbsolute = exports.isAbsolute(path), trailingSlash = substr(path, -1) === '/';
              // Normalize the path
              path = normalizeArray(filter(path.split('/'), function (p) {
                return !!p;
              }), !isAbsolute).join('/');
              if (!path && !isAbsolute) {
                path = '.';
              }
              if (path && trailingSlash) {
                path += '/';
              }
              return (isAbsolute ? '/' : '') + path;
            };
            // posix version
            exports.isAbsolute = function (path) {
              return path.charAt(0) === '/';
            };
            // posix version
            exports.join = function () {
              var paths = Array.prototype.slice.call(arguments, 0);
              return exports.normalize(filter(paths, function (p, index) {
                if (typeof p !== 'string') {
                  throw new TypeError('Arguments to path.join must be strings');
                }
                return p;
              }).join('/'));
            };
            // path.relative(from, to)
            // posix version
            exports.relative = function (from, to) {
              from = exports.resolve(from).substr(1);
              to = exports.resolve(to).substr(1);
              function trim(arr) {
                var start = 0;
                for (; start < arr.length; start++) {
                  if (arr[start] !== '')
                    break;
                }
                var end = arr.length - 1;
                for (; end >= 0; end--) {
                  if (arr[end] !== '')
                    break;
                }
                if (start > end)
                  return [];
                return arr.slice(start, end - start + 1);
              }
              var fromParts = trim(from.split('/'));
              var toParts = trim(to.split('/'));
              var length = Math.min(fromParts.length, toParts.length);
              var samePartsLength = length;
              for (var i = 0; i < length; i++) {
                if (fromParts[i] !== toParts[i]) {
                  samePartsLength = i;
                  break;
                }
              }
              var outputParts = [];
              for (var i = samePartsLength; i < fromParts.length; i++) {
                outputParts.push('..');
              }
              outputParts = outputParts.concat(toParts.slice(samePartsLength));
              return outputParts.join('/');
            };
            exports.sep = '/';
            exports.delimiter = ':';
            exports.dirname = function (path) {
              var result = splitPath(path), root = result[0], dir = result[1];
              if (!root && !dir) {
                // No dirname whatsoever
                return '.';
              }
              if (dir) {
                // It has a dirname, strip trailing slash
                dir = dir.substr(0, dir.length - 1);
              }
              return root + dir;
            };
            exports.basename = function (path, ext) {
              var f = splitPath(path)[2];
              // TODO: make this comparison case-insensitive on windows?
              if (ext && f.substr(-1 * ext.length) === ext) {
                f = f.substr(0, f.length - ext.length);
              }
              return f;
            };
            exports.extname = function (path) {
              return splitPath(path)[3];
            };
            function filter(xs, f) {
              if (xs.filter)
                return xs.filter(f);
              var res = [];
              for (var i = 0; i < xs.length; i++) {
                if (f(xs[i], i, xs))
                  res.push(xs[i]);
              }
              return res;
            }
            // String.prototype.substr - negative index don't work in IE8
            var substr = 'ab'.substr(-1) === 'b' ? function (str, start, len) {
              return str.substr(start, len);
            } : function (str, start, len) {
              if (start < 0)
                start = str.length + start;
              return str.substr(start, len);
            };
          }.call(this, require('_process')));
        },
        { '_process': 5 }
      ],
      5: [
        function (require, module, exports) {
          // shim for using process in browser
          var process = module.exports = {};
          // cached from whatever global is present so that test runners that stub it
          // don't break things.  But we need to wrap it in a try catch in case it is
          // wrapped in strict mode code which doesn't define any globals.  It's inside a
          // function because try/catches deoptimize in certain engines.
          var cachedSetTimeout;
          var cachedClearTimeout;
          function defaultSetTimout() {
            throw new Error('setTimeout has not been defined');
          }
          function defaultClearTimeout() {
            throw new Error('clearTimeout has not been defined');
          }
          (function () {
            try {
              if (typeof setTimeout === 'function') {
                cachedSetTimeout = setTimeout;
              } else {
                cachedSetTimeout = defaultSetTimout;
              }
            } catch (e) {
              cachedSetTimeout = defaultSetTimout;
            }
            try {
              if (typeof clearTimeout === 'function') {
                cachedClearTimeout = clearTimeout;
              } else {
                cachedClearTimeout = defaultClearTimeout;
              }
            } catch (e) {
              cachedClearTimeout = defaultClearTimeout;
            }
          }());
          function runTimeout(fun) {
            if (cachedSetTimeout === setTimeout) {
              //normal enviroments in sane situations
              return setTimeout(fun, 0);
            }
            // if setTimeout wasn't available but was latter defined
            if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
              cachedSetTimeout = setTimeout;
              return setTimeout(fun, 0);
            }
            try {
              // when when somebody has screwed with setTimeout but no I.E. maddness
              return cachedSetTimeout(fun, 0);
            } catch (e) {
              try {
                // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
                return cachedSetTimeout.call(null, fun, 0);
              } catch (e) {
                // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
                return cachedSetTimeout.call(this, fun, 0);
              }
            }
          }
          function runClearTimeout(marker) {
            if (cachedClearTimeout === clearTimeout) {
              //normal enviroments in sane situations
              return clearTimeout(marker);
            }
            // if clearTimeout wasn't available but was latter defined
            if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
              cachedClearTimeout = clearTimeout;
              return clearTimeout(marker);
            }
            try {
              // when when somebody has screwed with setTimeout but no I.E. maddness
              return cachedClearTimeout(marker);
            } catch (e) {
              try {
                // When we are in I.E. but the script has been evaled so I.E. doesn't  trust the global object when called normally
                return cachedClearTimeout.call(null, marker);
              } catch (e) {
                // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
                // Some versions of I.E. have different rules for clearTimeout vs setTimeout
                return cachedClearTimeout.call(this, marker);
              }
            }
          }
          var queue = [];
          var draining = false;
          var currentQueue;
          var queueIndex = -1;
          function cleanUpNextTick() {
            if (!draining || !currentQueue) {
              return;
            }
            draining = false;
            if (currentQueue.length) {
              queue = currentQueue.concat(queue);
            } else {
              queueIndex = -1;
            }
            if (queue.length) {
              drainQueue();
            }
          }
          function drainQueue() {
            if (draining) {
              return;
            }
            var timeout = runTimeout(cleanUpNextTick);
            draining = true;
            var len = queue.length;
            while (len) {
              currentQueue = queue;
              queue = [];
              while (++queueIndex < len) {
                if (currentQueue) {
                  currentQueue[queueIndex].run();
                }
              }
              queueIndex = -1;
              len = queue.length;
            }
            currentQueue = null;
            draining = false;
            runClearTimeout(timeout);
          }
          process.nextTick = function (fun) {
            var args = new Array(arguments.length - 1);
            if (arguments.length > 1) {
              for (var i = 1; i < arguments.length; i++) {
                args[i - 1] = arguments[i];
              }
            }
            queue.push(new Item(fun, args));
            if (queue.length === 1 && !draining) {
              runTimeout(drainQueue);
            }
          };
          // v8 likes predictible objects
          function Item(fun, array) {
            this.fun = fun;
            this.array = array;
          }
          Item.prototype.run = function () {
            this.fun.apply(null, this.array);
          };
          process.title = 'browser';
          process.browser = true;
          process.env = {};
          process.argv = [];
          process.version = '';
          // empty string to avoid regexp issues
          process.versions = {};
          function noop() {
          }
          process.on = noop;
          process.addListener = noop;
          process.once = noop;
          process.off = noop;
          process.removeListener = noop;
          process.removeAllListeners = noop;
          process.emit = noop;
          process.binding = function (name) {
            throw new Error('process.binding is not supported');
          };
          process.cwd = function () {
            return '/';
          };
          process.chdir = function (dir) {
            throw new Error('process.chdir is not supported');
          };
          process.umask = function () {
            return 0;
          };
        },
        {}
      ],
      6: [
        function (require, module, exports) {
          if (typeof Object.create === 'function') {
            // implementation from standard node.js 'util' module
            module.exports = function inherits(ctor, superCtor) {
              ctor.super_ = superCtor;
              ctor.prototype = Object.create(superCtor.prototype, {
                constructor: {
                  value: ctor,
                  enumerable: false,
                  writable: true,
                  configurable: true
                }
              });
            };
          } else {
            // old school shim for old browsers
            module.exports = function inherits(ctor, superCtor) {
              ctor.super_ = superCtor;
              var TempCtor = function () {
              };
              TempCtor.prototype = superCtor.prototype;
              ctor.prototype = new TempCtor();
              ctor.prototype.constructor = ctor;
            };
          }
        },
        {}
      ],
      7: [
        function (require, module, exports) {
          module.exports = function isBuffer(arg) {
            return arg && typeof arg === 'object' && typeof arg.copy === 'function' && typeof arg.fill === 'function' && typeof arg.readUInt8 === 'function';
          };
        },
        {}
      ],
      8: [
        function (require, module, exports) {
          (function (process, global) {
            // Copyright Joyent, Inc. and other Node contributors.
            //
            // Permission is hereby granted, free of charge, to any person obtaining a
            // copy of this software and associated documentation files (the
            // "Software"), to deal in the Software without restriction, including
            // without limitation the rights to use, copy, modify, merge, publish,
            // distribute, sublicense, and/or sell copies of the Software, and to permit
            // persons to whom the Software is furnished to do so, subject to the
            // following conditions:
            //
            // The above copyright notice and this permission notice shall be included
            // in all copies or substantial portions of the Software.
            //
            // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
            // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
            // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
            // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
            // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
            // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
            // USE OR OTHER DEALINGS IN THE SOFTWARE.
            var formatRegExp = /%[sdj%]/g;
            exports.format = function (f) {
              if (!isString(f)) {
                var objects = [];
                for (var i = 0; i < arguments.length; i++) {
                  objects.push(inspect(arguments[i]));
                }
                return objects.join(' ');
              }
              var i = 1;
              var args = arguments;
              var len = args.length;
              var str = String(f).replace(formatRegExp, function (x) {
                if (x === '%%')
                  return '%';
                if (i >= len)
                  return x;
                switch (x) {
                case '%s':
                  return String(args[i++]);
                case '%d':
                  return Number(args[i++]);
                case '%j':
                  try {
                    return JSON.stringify(args[i++]);
                  } catch (_) {
                    return '[Circular]';
                  }
                default:
                  return x;
                }
              });
              for (var x = args[i]; i < len; x = args[++i]) {
                if (isNull(x) || !isObject(x)) {
                  str += ' ' + x;
                } else {
                  str += ' ' + inspect(x);
                }
              }
              return str;
            };
            // Mark that a method should not be used.
            // Returns a modified function which warns once by default.
            // If --no-deprecation is set, then it is a no-op.
            exports.deprecate = function (fn, msg) {
              // Allow for deprecating things in the process of starting up.
              if (isUndefined(global.process)) {
                return function () {
                  return exports.deprecate(fn, msg).apply(this, arguments);
                };
              }
              if (process.noDeprecation === true) {
                return fn;
              }
              var warned = false;
              function deprecated() {
                if (!warned) {
                  if (process.throwDeprecation) {
                    throw new Error(msg);
                  } else if (process.traceDeprecation) {
                    console.trace(msg);
                  } else {
                    console.error(msg);
                  }
                  warned = true;
                }
                return fn.apply(this, arguments);
              }
              return deprecated;
            };
            var debugs = {};
            var debugEnviron;
            exports.debuglog = function (set) {
              if (isUndefined(debugEnviron))
                debugEnviron = process.env.NODE_DEBUG || '';
              set = set.toUpperCase();
              if (!debugs[set]) {
                if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
                  var pid = process.pid;
                  debugs[set] = function () {
                    var msg = exports.format.apply(exports, arguments);
                    console.error('%s %d: %s', set, pid, msg);
                  };
                } else {
                  debugs[set] = function () {
                  };
                }
              }
              return debugs[set];
            };
            /**
             * Echos the value of a value. Trys to print the value out
             * in the best way possible given the different types.
             *
             * @param {Object} obj The object to print out.
             * @param {Object} opts Optional options object that alters the output.
             */
            /* legacy: obj, showHidden, depth, colors*/
            function inspect(obj, opts) {
              // default options
              var ctx = {
                seen: [],
                stylize: stylizeNoColor
              };
              // legacy...
              if (arguments.length >= 3)
                ctx.depth = arguments[2];
              if (arguments.length >= 4)
                ctx.colors = arguments[3];
              if (isBoolean(opts)) {
                // legacy...
                ctx.showHidden = opts;
              } else if (opts) {
                // got an "options" object
                exports._extend(ctx, opts);
              }
              // set default options
              if (isUndefined(ctx.showHidden))
                ctx.showHidden = false;
              if (isUndefined(ctx.depth))
                ctx.depth = 2;
              if (isUndefined(ctx.colors))
                ctx.colors = false;
              if (isUndefined(ctx.customInspect))
                ctx.customInspect = true;
              if (ctx.colors)
                ctx.stylize = stylizeWithColor;
              return formatValue(ctx, obj, ctx.depth);
            }
            exports.inspect = inspect;
            // http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
            inspect.colors = {
              'bold': [
                1,
                22
              ],
              'italic': [
                3,
                23
              ],
              'underline': [
                4,
                24
              ],
              'inverse': [
                7,
                27
              ],
              'white': [
                37,
                39
              ],
              'grey': [
                90,
                39
              ],
              'black': [
                30,
                39
              ],
              'blue': [
                34,
                39
              ],
              'cyan': [
                36,
                39
              ],
              'green': [
                32,
                39
              ],
              'magenta': [
                35,
                39
              ],
              'red': [
                31,
                39
              ],
              'yellow': [
                33,
                39
              ]
            };
            // Don't use 'blue' not visible on cmd.exe
            inspect.styles = {
              'special': 'cyan',
              'number': 'yellow',
              'boolean': 'yellow',
              'undefined': 'grey',
              'null': 'bold',
              'string': 'green',
              'date': 'magenta',
              // "name": intentionally not styling
              'regexp': 'red'
            };
            function stylizeWithColor(str, styleType) {
              var style = inspect.styles[styleType];
              if (style) {
                return '\x1B[' + inspect.colors[style][0] + 'm' + str + '\x1B[' + inspect.colors[style][1] + 'm';
              } else {
                return str;
              }
            }
            function stylizeNoColor(str, styleType) {
              return str;
            }
            function arrayToHash(array) {
              var hash = {};
              array.forEach(function (val, idx) {
                hash[val] = true;
              });
              return hash;
            }
            function formatValue(ctx, value, recurseTimes) {
              // Provide a hook for user-specified inspect functions.
              // Check that value is an object with an inspect function on it
              if (ctx.customInspect && value && isFunction(value.inspect) && // Filter out the util module, it's inspect function is special
                value.inspect !== exports.inspect && // Also filter out any prototype objects using the circular check.
                !(value.constructor && value.constructor.prototype === value)) {
                var ret = value.inspect(recurseTimes, ctx);
                if (!isString(ret)) {
                  ret = formatValue(ctx, ret, recurseTimes);
                }
                return ret;
              }
              // Primitive types cannot have properties
              var primitive = formatPrimitive(ctx, value);
              if (primitive) {
                return primitive;
              }
              // Look up the keys of the object.
              var keys = Object.keys(value);
              var visibleKeys = arrayToHash(keys);
              if (ctx.showHidden) {
                keys = Object.getOwnPropertyNames(value);
              }
              // IE doesn't make error fields non-enumerable
              // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx
              if (isError(value) && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) {
                return formatError(value);
              }
              // Some type of object without properties can be shortcutted.
              if (keys.length === 0) {
                if (isFunction(value)) {
                  var name = value.name ? ': ' + value.name : '';
                  return ctx.stylize('[Function' + name + ']', 'special');
                }
                if (isRegExp(value)) {
                  return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
                }
                if (isDate(value)) {
                  return ctx.stylize(Date.prototype.toString.call(value), 'date');
                }
                if (isError(value)) {
                  return formatError(value);
                }
              }
              var base = '', array = false, braces = [
                  '{',
                  '}'
                ];
              // Make Array say that they are Array
              if (isArray(value)) {
                array = true;
                braces = [
                  '[',
                  ']'
                ];
              }
              // Make functions say that they are functions
              if (isFunction(value)) {
                var n = value.name ? ': ' + value.name : '';
                base = ' [Function' + n + ']';
              }
              // Make RegExps say that they are RegExps
              if (isRegExp(value)) {
                base = ' ' + RegExp.prototype.toString.call(value);
              }
              // Make dates with properties first say the date
              if (isDate(value)) {
                base = ' ' + Date.prototype.toUTCString.call(value);
              }
              // Make error with message first say the error
              if (isError(value)) {
                base = ' ' + formatError(value);
              }
              if (keys.length === 0 && (!array || value.length == 0)) {
                return braces[0] + base + braces[1];
              }
              if (recurseTimes < 0) {
                if (isRegExp(value)) {
                  return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
                } else {
                  return ctx.stylize('[Object]', 'special');
                }
              }
              ctx.seen.push(value);
              var output;
              if (array) {
                output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
              } else {
                output = keys.map(function (key) {
                  return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
                });
              }
              ctx.seen.pop();
              return reduceToSingleString(output, base, braces);
            }
            function formatPrimitive(ctx, value) {
              if (isUndefined(value))
                return ctx.stylize('undefined', 'undefined');
              if (isString(value)) {
                var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '').replace(/'/g, '\\\'').replace(/\\"/g, '"') + '\'';
                return ctx.stylize(simple, 'string');
              }
              if (isNumber(value))
                return ctx.stylize('' + value, 'number');
              if (isBoolean(value))
                return ctx.stylize('' + value, 'boolean');
              // For some reason typeof null is "object", so special case here.
              if (isNull(value))
                return ctx.stylize('null', 'null');
            }
            function formatError(value) {
              return '[' + Error.prototype.toString.call(value) + ']';
            }
            function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
              var output = [];
              for (var i = 0, l = value.length; i < l; ++i) {
                if (hasOwnProperty(value, String(i))) {
                  output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, String(i), true));
                } else {
                  output.push('');
                }
              }
              keys.forEach(function (key) {
                if (!key.match(/^\d+$/)) {
                  output.push(formatProperty(ctx, value, recurseTimes, visibleKeys, key, true));
                }
              });
              return output;
            }
            function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
              var name, str, desc;
              desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
              if (desc.get) {
                if (desc.set) {
                  str = ctx.stylize('[Getter/Setter]', 'special');
                } else {
                  str = ctx.stylize('[Getter]', 'special');
                }
              } else {
                if (desc.set) {
                  str = ctx.stylize('[Setter]', 'special');
                }
              }
              if (!hasOwnProperty(visibleKeys, key)) {
                name = '[' + key + ']';
              }
              if (!str) {
                if (ctx.seen.indexOf(desc.value) < 0) {
                  if (isNull(recurseTimes)) {
                    str = formatValue(ctx, desc.value, null);
                  } else {
                    str = formatValue(ctx, desc.value, recurseTimes - 1);
                  }
                  if (str.indexOf('\n') > -1) {
                    if (array) {
                      str = str.split('\n').map(function (line) {
                        return '  ' + line;
                      }).join('\n').substr(2);
                    } else {
                      str = '\n' + str.split('\n').map(function (line) {
                        return '   ' + line;
                      }).join('\n');
                    }
                  }
                } else {
                  str = ctx.stylize('[Circular]', 'special');
                }
              }
              if (isUndefined(name)) {
                if (array && key.match(/^\d+$/)) {
                  return str;
                }
                name = JSON.stringify('' + key);
                if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
                  name = name.substr(1, name.length - 2);
                  name = ctx.stylize(name, 'name');
                } else {
                  name = name.replace(/'/g, '\\\'').replace(/\\"/g, '"').replace(/(^"|"$)/g, '\'');
                  name = ctx.stylize(name, 'string');
                }
              }
              return name + ': ' + str;
            }
            function reduceToSingleString(output, base, braces) {
              var numLinesEst = 0;
              var length = output.reduce(function (prev, cur) {
                numLinesEst++;
                if (cur.indexOf('\n') >= 0)
                  numLinesEst++;
                return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
              }, 0);
              if (length > 60) {
                return braces[0] + (base === '' ? '' : base + '\n ') + ' ' + output.join(',\n  ') + ' ' + braces[1];
              }
              return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
            }
            // NOTE: These type checking functions intentionally don't use `instanceof`
            // because it is fragile and can be easily faked with `Object.create()`.
            function isArray(ar) {
              return Array.isArray(ar);
            }
            exports.isArray = isArray;
            function isBoolean(arg) {
              return typeof arg === 'boolean';
            }
            exports.isBoolean = isBoolean;
            function isNull(arg) {
              return arg === null;
            }
            exports.isNull = isNull;
            function isNullOrUndefined(arg) {
              return arg == null;
            }
            exports.isNullOrUndefined = isNullOrUndefined;
            function isNumber(arg) {
              return typeof arg === 'number';
            }
            exports.isNumber = isNumber;
            function isString(arg) {
              return typeof arg === 'string';
            }
            exports.isString = isString;
            function isSymbol(arg) {
              return typeof arg === 'symbol';
            }
            exports.isSymbol = isSymbol;
            function isUndefined(arg) {
              return arg === void 0;
            }
            exports.isUndefined = isUndefined;
            function isRegExp(re) {
              return isObject(re) && objectToString(re) === '[object RegExp]';
            }
            exports.isRegExp = isRegExp;
            function isObject(arg) {
              return typeof arg === 'object' && arg !== null;
            }
            exports.isObject = isObject;
            function isDate(d) {
              return isObject(d) && objectToString(d) === '[object Date]';
            }
            exports.isDate = isDate;
            function isError(e) {
              return isObject(e) && (objectToString(e) === '[object Error]' || e instanceof Error);
            }
            exports.isError = isError;
            function isFunction(arg) {
              return typeof arg === 'function';
            }
            exports.isFunction = isFunction;
            function isPrimitive(arg) {
              return arg === null || typeof arg === 'boolean' || typeof arg === 'number' || typeof arg === 'string' || typeof arg === 'symbol' || // ES6 symbol
              typeof arg === 'undefined';
            }
            exports.isPrimitive = isPrimitive;
            exports.isBuffer = require('./support/isBuffer');
            function objectToString(o) {
              return Object.prototype.toString.call(o);
            }
            function pad(n) {
              return n < 10 ? '0' + n.toString(10) : n.toString(10);
            }
            var months = [
              'Jan',
              'Feb',
              'Mar',
              'Apr',
              'May',
              'Jun',
              'Jul',
              'Aug',
              'Sep',
              'Oct',
              'Nov',
              'Dec'
            ];
            // 26 Feb 16:19:34
            function timestamp() {
              var d = new Date();
              var time = [
                pad(d.getHours()),
                pad(d.getMinutes()),
                pad(d.getSeconds())
              ].join(':');
              return [
                d.getDate(),
                months[d.getMonth()],
                time
              ].join(' ');
            }
            // log is just a thin wrapper to console.log that prepends a timestamp
            exports.log = function () {
              console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
            };
            /**
             * Inherit the prototype methods from one constructor into another.
             *
             * The Function.prototype.inherits from lang.js rewritten as a standalone
             * function (not on Function.prototype). NOTE: If this file is to be loaded
             * during bootstrapping this function needs to be rewritten using some native
             * functions as prototype setup using normal JavaScript does not work as
             * expected during bootstrapping (see mirror.js in r114903).
             *
             * @param {function} ctor Constructor function which needs to inherit the
             *     prototype.
             * @param {function} superCtor Constructor function to inherit prototype from.
             */
            exports.inherits = require('inherits');
            exports._extend = function (origin, add) {
              // Don't do anything if add isn't an object
              if (!add || !isObject(add))
                return origin;
              var keys = Object.keys(add);
              var i = keys.length;
              while (i--) {
                origin[keys[i]] = add[keys[i]];
              }
              return origin;
            };
            function hasOwnProperty(obj, prop) {
              return Object.prototype.hasOwnProperty.call(obj, prop);
            }
          }.call(this, require('_process'), typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : typeof window !== 'undefined' ? window : {}));
        },
        {
          './support/isBuffer': 7,
          '_process': 5,
          'inherits': 6
        }
      ],
      9: [
        function (require, module, exports) {
          'use strict';
          var levels = require('./levels');
          var DEFAULT_FORMAT = ':remote-addr - -' + ' ":method :url HTTP/:http-version"' + ' :status :content-length ":referrer"' + ' ":user-agent"';
          /**
           * Log requests with the given `options` or a `format` string.
           *
           * Options:
           *
           *   - `format`        Format string, see below for tokens
           *   - `level`         A log4js levels instance. Supports also 'auto'
           *
           * Tokens:
           *
           *   - `:req[header]` ex: `:req[Accept]`
           *   - `:res[header]` ex: `:res[Content-Length]`
           *   - `:http-version`
           *   - `:response-time`
           *   - `:remote-addr`
           *   - `:date`
           *   - `:method`
           *   - `:url`
           *   - `:referrer`
           *   - `:user-agent`
           *   - `:status`
           *
           * @param {String|Function|Object} format or options
           * @return {Function}
           * @api public
           */
          function getLogger(logger4js, options) {
            if ('object' == typeof options) {
              options = options || {};
            } else if (options) {
              options = { format: options };
            } else {
              options = {};
            }
            var thislogger = logger4js, level = levels.toLevel(options.level, levels.INFO), fmt = options.format || DEFAULT_FORMAT, nolog = options.nolog ? createNoLogCondition(options.nolog) : null;
            return function (req, res, next) {
              // mount safety
              if (req._logging)
                return next();
              // nologs
              if (nolog && nolog.test(req.originalUrl))
                return next();
              if (thislogger.isLevelEnabled(level) || options.level === 'auto') {
                var start = new Date(), statusCode, writeHead = res.writeHead, url = req.originalUrl;
                // flag as logging
                req._logging = true;
                // proxy for statusCode.
                res.writeHead = function (code, headers) {
                  res.writeHead = writeHead;
                  res.writeHead(code, headers);
                  res.__statusCode = statusCode = code;
                  res.__headers = headers || {};
                  //status code response level handling
                  if (options.level === 'auto') {
                    level = levels.INFO;
                    if (code >= 300)
                      level = levels.WARN;
                    if (code >= 400)
                      level = levels.ERROR;
                  } else {
                    level = levels.toLevel(options.level, levels.INFO);
                  }
                };
                //hook on end request to emit the log entry of the HTTP request.
                res.on('finish', function () {
                  res.responseTime = new Date() - start;
                  //status code response level handling
                  if (res.statusCode && options.level === 'auto') {
                    level = levels.INFO;
                    if (res.statusCode >= 300)
                      level = levels.WARN;
                    if (res.statusCode >= 400)
                      level = levels.ERROR;
                  }
                  if (thislogger.isLevelEnabled(level)) {
                    var combined_tokens = assemble_tokens(req, res, options.tokens || []);
                    if (typeof fmt === 'function') {
                      var line = fmt(req, res, function (str) {
                        return format(str, combined_tokens);
                      });
                      if (line)
                        thislogger.log(level, line);
                    } else {
                      thislogger.log(level, format(fmt, combined_tokens));
                    }
                  }
                });
              }
              //ensure next gets always called
              next();
            };
          }
          /**
           * Adds custom {token, replacement} objects to defaults,
           * overwriting the defaults if any tokens clash
           *
           * @param  {IncomingMessage} req
           * @param  {ServerResponse} res
           * @param  {Array} custom_tokens
           *    [{ token: string-or-regexp, replacement: string-or-replace-function }]
           * @return {Array}
           */
          function assemble_tokens(req, res, custom_tokens) {
            var array_unique_tokens = function (array) {
              var a = array.concat();
              for (var i = 0; i < a.length; ++i) {
                for (var j = i + 1; j < a.length; ++j) {
                  if (a[i].token == a[j].token) {
                    // not === because token can be regexp object
                    a.splice(j--, 1);
                  }
                }
              }
              return a;
            };
            var default_tokens = [];
            default_tokens.push({
              token: ':url',
              replacement: getUrl(req)
            });
            default_tokens.push({
              token: ':protocol',
              replacement: req.protocol
            });
            default_tokens.push({
              token: ':hostname',
              replacement: req.hostname
            });
            default_tokens.push({
              token: ':method',
              replacement: req.method
            });
            default_tokens.push({
              token: ':status',
              replacement: res.__statusCode || res.statusCode
            });
            default_tokens.push({
              token: ':response-time',
              replacement: res.responseTime
            });
            default_tokens.push({
              token: ':date',
              replacement: new Date().toUTCString()
            });
            default_tokens.push({
              token: ':referrer',
              replacement: req.headers.referer || req.headers.referrer || ''
            });
            default_tokens.push({
              token: ':http-version',
              replacement: req.httpVersionMajor + '.' + req.httpVersionMinor
            });
            default_tokens.push({
              token: ':remote-addr',
              replacement: req.headers['x-forwarded-for'] || req.ip || req._remoteAddress || req.socket && (req.socket.remoteAddress || req.socket.socket && req.socket.socket.remoteAddress)
            });
            default_tokens.push({
              token: ':user-agent',
              replacement: req.headers['user-agent']
            });
            default_tokens.push({
              token: ':content-length',
              replacement: res._headers && res._headers['content-length'] || res.__headers && res.__headers['Content-Length'] || '-'
            });
            default_tokens.push({
              token: /:req\[([^\]]+)\]/g,
              replacement: function (_, field) {
                return req.headers[field.toLowerCase()];
              }
            });
            default_tokens.push({
              token: /:res\[([^\]]+)\]/g,
              replacement: function (_, field) {
                return res._headers ? res._headers[field.toLowerCase()] || res.__headers[field] : res.__headers && res.__headers[field];
              }
            });
            return array_unique_tokens(custom_tokens.concat(default_tokens));
          }
          /**
           * Return request url path,
           * adding this function prevents the Cyclomatic Complexity,
           * for the assemble_tokens function at low, to pass the tests.
           *
           * @param  {IncomingMessage} req
           * @return {String}
           * @api private
           */
          function getUrl(req) {
            return req.originalUrl || req.url;
          }
          /**
           * Return formatted log line.
           *
           * @param  {String} str
           * @param  {IncomingMessage} req
           * @param  {ServerResponse} res
           * @return {String}
           * @api private
           */
          function format(str, tokens) {
            for (var i = 0; i < tokens.length; i++) {
              str = str.replace(tokens[i].token, tokens[i].replacement);
            }
            return str;
          }
          /**
           * Return RegExp Object about nolog
           *
           * @param  {String} nolog
           * @return {RegExp}
           * @api private
           *
           * syntax
           *  1. String
           *   1.1 "\\.gif"
           *         NOT LOGGING http://example.com/hoge.gif and http://example.com/hoge.gif?fuga
           *         LOGGING http://example.com/hoge.agif
           *   1.2 in "\\.gif|\\.jpg$"
           *         NOT LOGGING http://example.com/hoge.gif and
           *           http://example.com/hoge.gif?fuga and http://example.com/hoge.jpg?fuga
           *         LOGGING http://example.com/hoge.agif,
           *           http://example.com/hoge.ajpg and http://example.com/hoge.jpg?hoge
           *   1.3 in "\\.(gif|jpe?g|png)$"
           *         NOT LOGGING http://example.com/hoge.gif and http://example.com/hoge.jpeg
           *         LOGGING http://example.com/hoge.gif?uid=2 and http://example.com/hoge.jpg?pid=3
           *  2. RegExp
           *   2.1 in /\.(gif|jpe?g|png)$/
           *         SAME AS 1.3
           *  3. Array
           *   3.1 ["\\.jpg$", "\\.png", "\\.gif"]
           *         SAME AS "\\.jpg|\\.png|\\.gif"
           */
          function createNoLogCondition(nolog) {
            var regexp = null;
            if (nolog) {
              if (nolog instanceof RegExp) {
                regexp = nolog;
              }
              if (typeof nolog === 'string') {
                regexp = new RegExp(nolog);
              }
              if (Array.isArray(nolog)) {
                var regexpsAsStrings = nolog.map(function convertToStrings(o) {
                  return o.source ? o.source : o;
                });
                regexp = new RegExp(regexpsAsStrings.join('|'));
              }
            }
            return regexp;
          }
          exports.connectLogger = getLogger;
        },
        { './levels': 12 }
      ],
      10: [
        function (require, module, exports) {
          'use strict';
          exports.ISO8601_FORMAT = 'yyyy-MM-dd hh:mm:ss.SSS';
          exports.ISO8601_WITH_TZ_OFFSET_FORMAT = 'yyyy-MM-ddThh:mm:ssO';
          exports.DATETIME_FORMAT = 'dd MM yyyy hh:mm:ss.SSS';
          exports.ABSOLUTETIME_FORMAT = 'hh:mm:ss.SSS';
          function padWithZeros(vNumber, width) {
            var numAsString = vNumber + '';
            while (numAsString.length < width) {
              numAsString = '0' + numAsString;
            }
            return numAsString;
          }
          function addZero(vNumber) {
            return padWithZeros(vNumber, 2);
          }
          /**
           * Formats the TimeOffest
           * Thanks to http://www.svendtofte.com/code/date_format/
           * @private
           */
          function offset(timezoneOffset) {
            // Difference to Greenwich time (GMT) in hours
            var os = Math.abs(timezoneOffset);
            var h = String(Math.floor(os / 60));
            var m = String(os % 60);
            if (h.length == 1) {
              h = '0' + h;
            }
            if (m.length == 1) {
              m = '0' + m;
            }
            return timezoneOffset < 0 ? '+' + h + m : '-' + h + m;
          }
          exports.asString = function (date, timezoneOffset) {
            /*jshint -W071 */
            var format = exports.ISO8601_FORMAT;
            if (typeof date === 'string') {
              format = arguments[0];
              date = arguments[1];
              timezoneOffset = arguments[2];
            }
            // make the date independent of the system timezone by working with UTC
            if (timezoneOffset === undefined) {
              timezoneOffset = date.getTimezoneOffset();
            }
            date.setUTCMinutes(date.getUTCMinutes() - timezoneOffset);
            var vDay = addZero(date.getUTCDate());
            var vMonth = addZero(date.getUTCMonth() + 1);
            var vYearLong = addZero(date.getUTCFullYear());
            var vYearShort = addZero(date.getUTCFullYear().toString().substring(2, 4));
            var vYear = format.indexOf('yyyy') > -1 ? vYearLong : vYearShort;
            var vHour = addZero(date.getUTCHours());
            var vMinute = addZero(date.getUTCMinutes());
            var vSecond = addZero(date.getUTCSeconds());
            var vMillisecond = padWithZeros(date.getUTCMilliseconds(), 3);
            var vTimeZone = offset(timezoneOffset);
            date.setUTCMinutes(date.getUTCMinutes() + timezoneOffset);
            var formatted = format.replace(/dd/g, vDay).replace(/MM/g, vMonth).replace(/y{1,4}/g, vYear).replace(/hh/g, vHour).replace(/mm/g, vMinute).replace(/ss/g, vSecond).replace(/SSS/g, vMillisecond).replace(/O/g, vTimeZone);
            return formatted;
          };  /*jshint +W071 */
        },
        {}
      ],
      11: [
        function (require, module, exports) {
          (function (process) {
            'use strict';
            var dateFormat = require('./date_format'), os = require('os'), eol = os.EOL || '\n', util = require('util'), semver = require('semver'), replacementRegExp = /%[sdj]/g, layoutMakers = {
                'messagePassThrough': function () {
                  return messagePassThroughLayout;
                },
                'basic': function () {
                  return basicLayout;
                },
                'colored': function () {
                  return colouredLayout;
                },
                'coloured': function () {
                  return colouredLayout;
                },
                'pattern': function (config) {
                  return patternLayout(config && config.pattern, config && config.tokens);
                },
                'dummy': function () {
                  return dummyLayout;
                }
              }, colours = {
                ALL: 'grey',
                TRACE: 'blue',
                DEBUG: 'cyan',
                INFO: 'green',
                WARN: 'yellow',
                ERROR: 'red',
                FATAL: 'magenta',
                OFF: 'grey'
              };
            function wrapErrorsWithInspect(items) {
              return items.map(function (item) {
                if (item instanceof Error && item.stack) {
                  return {
                    inspect: function () {
                      if (semver.satisfies(process.version, '>=6')) {
                        return util.format(item);
                      } else {
                        return util.format(item) + '\n' + item.stack;
                      }
                    }
                  };
                } else {
                  return item;
                }
              });
            }
            function formatLogData(logData) {
              var data = Array.isArray(logData) ? logData : Array.prototype.slice.call(arguments);
              return util.format.apply(util, wrapErrorsWithInspect(data));
            }
            var styles = {
              //styles
              'bold': [
                1,
                22
              ],
              'italic': [
                3,
                23
              ],
              'underline': [
                4,
                24
              ],
              'inverse': [
                7,
                27
              ],
              //grayscale
              'white': [
                37,
                39
              ],
              'grey': [
                90,
                39
              ],
              'black': [
                90,
                39
              ],
              //colors
              'blue': [
                34,
                39
              ],
              'cyan': [
                36,
                39
              ],
              'green': [
                32,
                39
              ],
              'magenta': [
                35,
                39
              ],
              'red': [
                31,
                39
              ],
              'yellow': [
                33,
                39
              ]
            };
            function colorizeStart(style) {
              return style ? '\x1B[' + styles[style][0] + 'm' : '';
            }
            function colorizeEnd(style) {
              return style ? '\x1B[' + styles[style][1] + 'm' : '';
            }
            /**
             * Taken from masylum's fork (https://github.com/masylum/log4js-node)
             */
            function colorize(str, style) {
              return colorizeStart(style) + str + colorizeEnd(style);
            }
            function timestampLevelAndCategory(loggingEvent, colour, timezoneOffest) {
              var output = colorize(formatLogData('[%s] [%s] %s - ', dateFormat.asString(loggingEvent.startTime, timezoneOffest), loggingEvent.level, loggingEvent.categoryName), colour);
              return output;
            }
            /**
             * BasicLayout is a simple layout for storing the logs. The logs are stored
             * in following format:
             * <pre>
             * [startTime] [logLevel] categoryName - message\n
             * </pre>
             *
             * @author Stephan Strittmatter
             */
            function basicLayout(loggingEvent, timezoneOffset) {
              return timestampLevelAndCategory(loggingEvent, undefined, timezoneOffset) + formatLogData(loggingEvent.data);
            }
            /**
             * colouredLayout - taken from masylum's fork.
             * same as basicLayout, but with colours.
             */
            function colouredLayout(loggingEvent, timezoneOffset) {
              return timestampLevelAndCategory(loggingEvent, colours[loggingEvent.level.toString()], timezoneOffset) + formatLogData(loggingEvent.data);
            }
            function messagePassThroughLayout(loggingEvent) {
              return formatLogData(loggingEvent.data);
            }
            function dummyLayout(loggingEvent) {
              return loggingEvent.data[0];
            }
            /**
             * PatternLayout
             * Format for specifiers is %[padding].[truncation][field]{[format]}
             * e.g. %5.10p - left pad the log level by 5 characters, up to a max of 10
             * Fields can be any of:
             *  - %r time in toLocaleTimeString format
             *  - %p log level
             *  - %c log category
             *  - %h hostname
             *  - %m log data
             *  - %d date in various formats
             *  - %% %
             *  - %n newline
             *  - %z pid
             *  - %x{<tokenname>} add dynamic tokens to your log. Tokens are specified in the tokens parameter
             * You can use %[ and %] to define a colored block.
             *
             * Tokens are specified as simple key:value objects.
             * The key represents the token name whereas the value can be a string or function
             * which is called to extract the value to put in the log message. If token is not
             * found, it doesn't replace the field.
             *
             * A sample token would be: { "pid" : function() { return process.pid; } }
             *
             * Takes a pattern string, array of tokens and returns a layout function.
             * @param {String} Log format pattern String
             * @param {object} map object of different tokens
             * @param {number} timezone offset in minutes
             * @return {Function}
             * @author Stephan Strittmatter
             * @author Jan Schmidle
             */
            function patternLayout(pattern, tokens, timezoneOffset) {
              // jshint maxstatements:22
              var TTCC_CONVERSION_PATTERN = '%r %p %c - %m%n';
              var regex = /%(-?[0-9]+)?(\.?[0-9]+)?([\[\]cdhmnprzxy%])(\{([^\}]+)\})?|([^%]+)/;
              pattern = pattern || TTCC_CONVERSION_PATTERN;
              function categoryName(loggingEvent, specifier) {
                var loggerName = loggingEvent.categoryName;
                if (specifier) {
                  var precision = parseInt(specifier, 10);
                  var loggerNameBits = loggerName.split('.');
                  if (precision < loggerNameBits.length) {
                    loggerName = loggerNameBits.slice(loggerNameBits.length - precision).join('.');
                  }
                }
                return loggerName;
              }
              function formatAsDate(loggingEvent, specifier) {
                var format = dateFormat.ISO8601_FORMAT;
                if (specifier) {
                  format = specifier;
                  // Pick up special cases
                  if (format == 'ISO8601') {
                    format = dateFormat.ISO8601_FORMAT;
                  } else if (format == 'ISO8601_WITH_TZ_OFFSET') {
                    format = dateFormat.ISO8601_WITH_TZ_OFFSET_FORMAT;
                  } else if (format == 'ABSOLUTE') {
                    format = dateFormat.ABSOLUTETIME_FORMAT;
                  } else if (format == 'DATE') {
                    format = dateFormat.DATETIME_FORMAT;
                  }
                }
                // Format the date
                return dateFormat.asString(format, loggingEvent.startTime, timezoneOffset);
              }
              function hostname() {
                return os.hostname().toString();
              }
              function formatMessage(loggingEvent) {
                return formatLogData(loggingEvent.data);
              }
              function endOfLine() {
                return eol;
              }
              function logLevel(loggingEvent) {
                return loggingEvent.level.toString();
              }
              function startTime(loggingEvent) {
                return dateFormat.asString('hh:mm:ss', loggingEvent.startTime, timezoneOffset);
              }
              function startColour(loggingEvent) {
                return colorizeStart(colours[loggingEvent.level.toString()]);
              }
              function endColour(loggingEvent) {
                return colorizeEnd(colours[loggingEvent.level.toString()]);
              }
              function percent() {
                return '%';
              }
              function pid(loggingEvent) {
                if (loggingEvent && loggingEvent.pid) {
                  return loggingEvent.pid;
                } else {
                  return process.pid;
                }
              }
              function clusterInfo(loggingEvent, specifier) {
                if (loggingEvent.cluster && specifier) {
                  return specifier.replace('%m', loggingEvent.cluster.master).replace('%w', loggingEvent.cluster.worker).replace('%i', loggingEvent.cluster.workerId);
                } else if (loggingEvent.cluster) {
                  return loggingEvent.cluster.worker + '@' + loggingEvent.cluster.master;
                } else {
                  return pid();
                }
              }
              function userDefined(loggingEvent, specifier) {
                if (typeof tokens[specifier] !== 'undefined') {
                  if (typeof tokens[specifier] === 'function') {
                    return tokens[specifier](loggingEvent);
                  } else {
                    return tokens[specifier];
                  }
                }
                return null;
              }
              var replacers = {
                'c': categoryName,
                'd': formatAsDate,
                'h': hostname,
                'm': formatMessage,
                'n': endOfLine,
                'p': logLevel,
                'r': startTime,
                '[': startColour,
                ']': endColour,
                'y': clusterInfo,
                'z': pid,
                '%': percent,
                'x': userDefined
              };
              function replaceToken(conversionCharacter, loggingEvent, specifier) {
                return replacers[conversionCharacter](loggingEvent, specifier);
              }
              function truncate(truncation, toTruncate) {
                var len;
                if (truncation) {
                  len = parseInt(truncation.substr(1), 10);
                  return toTruncate.substring(0, len);
                }
                return toTruncate;
              }
              function pad(padding, toPad) {
                var len;
                if (padding) {
                  if (padding.charAt(0) == '-') {
                    len = parseInt(padding.substr(1), 10);
                    // Right pad with spaces
                    while (toPad.length < len) {
                      toPad += ' ';
                    }
                  } else {
                    len = parseInt(padding, 10);
                    // Left pad with spaces
                    while (toPad.length < len) {
                      toPad = ' ' + toPad;
                    }
                  }
                }
                return toPad;
              }
              function truncateAndPad(toTruncAndPad, truncation, padding) {
                var replacement = toTruncAndPad;
                replacement = truncate(truncation, replacement);
                replacement = pad(padding, replacement);
                return replacement;
              }
              return function (loggingEvent) {
                var formattedString = '';
                var result;
                var searchString = pattern;
                while (result = regex.exec(searchString)) {
                  var matchedString = result[0];
                  var padding = result[1];
                  var truncation = result[2];
                  var conversionCharacter = result[3];
                  var specifier = result[5];
                  var text = result[6];
                  // Check if the pattern matched was just normal text
                  if (text) {
                    formattedString += '' + text;
                  } else {
                    // Create a raw replacement string based on the conversion
                    // character and specifier
                    var replacement = replaceToken(conversionCharacter, loggingEvent, specifier);
                    formattedString += truncateAndPad(replacement, truncation, padding);
                  }
                  searchString = searchString.substr(result.index + result[0].length);
                }
                return formattedString;
              };
            }
            module.exports = {
              basicLayout: basicLayout,
              messagePassThroughLayout: messagePassThroughLayout,
              patternLayout: patternLayout,
              colouredLayout: colouredLayout,
              coloredLayout: colouredLayout,
              dummyLayout: dummyLayout,
              addLayout: function (name, serializerGenerator) {
                layoutMakers[name] = serializerGenerator;
              },
              layout: function (name, config) {
                return layoutMakers[name] && layoutMakers[name](config);
              }
            };
          }.call(this, require('_process')));
        },
        {
          './date_format': 10,
          '_process': 5,
          'os': 2,
          'semver': 15,
          'util': 8
        }
      ],
      12: [
        function (require, module, exports) {
          'use strict';
          function Level(level, levelStr) {
            this.level = level;
            this.levelStr = levelStr;
          }
          /**
           * converts given String to corresponding Level
           * @param {String} sArg String value of Level OR Log4js.Level
           * @param {Log4js.Level} defaultLevel default Level, if no String representation
           * @return Level object
           * @type Log4js.Level
           */
          function toLevel(sArg, defaultLevel) {
            if (!sArg) {
              return defaultLevel;
            }
            if (sArg instanceof Level) {
              module.exports[sArg.toString()] = sArg;
              return sArg;
            }
            if (typeof sArg === 'string') {
              return module.exports[sArg.toUpperCase()] || defaultLevel;
            }
            return toLevel(sArg.toString());
          }
          Level.prototype.toString = function () {
            return this.levelStr;
          };
          Level.prototype.isLessThanOrEqualTo = function (otherLevel) {
            if (typeof otherLevel === 'string') {
              otherLevel = toLevel(otherLevel);
            }
            return this.level <= otherLevel.level;
          };
          Level.prototype.isGreaterThanOrEqualTo = function (otherLevel) {
            if (typeof otherLevel === 'string') {
              otherLevel = toLevel(otherLevel);
            }
            return this.level >= otherLevel.level;
          };
          Level.prototype.isEqualTo = function (otherLevel) {
            if (typeof otherLevel === 'string') {
              otherLevel = toLevel(otherLevel);
            }
            return this.level === otherLevel.level;
          };
          module.exports = {
            ALL: new Level(Number.MIN_VALUE, 'ALL'),
            TRACE: new Level(5000, 'TRACE'),
            DEBUG: new Level(10000, 'DEBUG'),
            INFO: new Level(20000, 'INFO'),
            WARN: new Level(30000, 'WARN'),
            ERROR: new Level(40000, 'ERROR'),
            FATAL: new Level(50000, 'FATAL'),
            MARK: new Level(9007199254740992, 'MARK'),
            // 2^53
            OFF: new Level(Number.MAX_VALUE, 'OFF'),
            toLevel: toLevel,
            Level: Level
          };
        },
        {}
      ],
      13: [
        function (require, module, exports) {
          (function (process) {
            'use strict';
            /*
             * 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.
             */
            /**
             * @fileoverview log4js is a library to log in JavaScript in similar manner
             * than in log4j for Java. The API should be nearly the same.
             *
             * <h3>Example:</h3>
             * <pre>
             *  var logging = require('log4js');
             *  //add an appender that logs all messages to stdout.
             *  logging.addAppender(logging.consoleAppender());
             *  //add an appender that logs "some-category" to a file
             *  logging.addAppender(logging.fileAppender("file.log"), "some-category");
             *  //get a logger
             *  var log = logging.getLogger("some-category");
             *  log.setLevel(logging.levels.TRACE); //set the Level
             *
             *  ...
             *
             *  //call the log
             *  log.trace("trace me" );
             * </pre>
             *
             * NOTE: the authors below are the original browser-based log4js authors
             * don't try to contact them about bugs in this version :)
             * @version 1.0
             * @author Stephan Strittmatter - http://jroller.com/page/stritti
             * @author Seth Chisamore - http://www.chisamore.com
             * @since 2005-05-20
             * @static
             * Website: http://log4js.berlios.de
             */
            var events = require('events'), fs = require('fs'), path = require('path'), util = require('util'), layouts = require('./layouts'), levels = require('./levels'), loggerModule = require('./logger'), LoggingEvent = loggerModule.LoggingEvent, Logger = loggerModule.Logger, ALL_CATEGORIES = '[all]', appenders = {}, loggers = {}, appenderMakers = {}, appenderShutdowns = {}, defaultConfig = {
                appenders: [{ type: 'stdout' }],
                replaceConsole: false
              };
            function hasLogger(logger) {
              return loggers.hasOwnProperty(logger);
            }
            levels.forName = function (levelStr, levelVal) {
              var level;
              if (typeof levelStr === 'string' && typeof levelVal === 'number') {
                var levelUpper = levelStr.toUpperCase();
                level = new levels.Level(levelVal, levelUpper);
                loggerModule.addLevelMethods(level);
              }
              return level;
            };
            levels.getLevel = function (levelStr) {
              var level;
              if (typeof levelStr === 'string') {
                var levelUpper = levelStr.toUpperCase();
                level = levels.toLevel(levelStr);
              }
              return level;
            };
            function getBufferedLogger(categoryName) {
              var base_logger = getLogger(categoryName);
              var logger = {};
              logger.temp = [];
              logger.target = base_logger;
              logger.flush = function () {
                for (var i = 0; i < logger.temp.length; i++) {
                  var log = logger.temp[i];
                  logger.target[log.level](log.message);
                  delete logger.temp[i];
                }
              };
              logger.trace = function (message) {
                logger.temp.push({
                  level: 'trace',
                  message: message
                });
              };
              logger.debug = function (message) {
                logger.temp.push({
                  level: 'debug',
                  message: message
                });
              };
              logger.info = function (message) {
                logger.temp.push({
                  level: 'info',
                  message: message
                });
              };
              logger.warn = function (message) {
                logger.temp.push({
                  level: 'warn',
                  message: message
                });
              };
              logger.error = function (message) {
                logger.temp.push({
                  level: 'error',
                  message: message
                });
              };
              logger.fatal = function (message) {
                logger.temp.push({
                  level: 'fatal',
                  message: message
                });
              };
              return logger;
            }
            function normalizeCategory(category) {
              return category + '.';
            }
            function doesLevelEntryContainsLogger(levelCategory, loggerCategory) {
              var normalizedLevelCategory = normalizeCategory(levelCategory);
              var normalizedLoggerCategory = normalizeCategory(loggerCategory);
              return normalizedLoggerCategory.substring(0, normalizedLevelCategory.length) == normalizedLevelCategory;  //jshint ignore:line
            }
            function doesAppenderContainsLogger(appenderCategory, loggerCategory) {
              var normalizedAppenderCategory = normalizeCategory(appenderCategory);
              var normalizedLoggerCategory = normalizeCategory(loggerCategory);
              return normalizedLoggerCategory.substring(0, normalizedAppenderCategory.length) == normalizedAppenderCategory;  //jshint ignore:line
            }
            /**
             * Get a logger instance. Instance is cached on categoryName level.
             * @param  {String} categoryName name of category to log to.
             * @return {Logger} instance of logger for the category
             * @static
             */
            function getLogger(loggerCategoryName) {
              // Use default logger if categoryName is not specified or invalid
              if (typeof loggerCategoryName !== 'string') {
                loggerCategoryName = Logger.DEFAULT_CATEGORY;
              }
              if (!hasLogger(loggerCategoryName)) {
                var level;
                /* jshint -W073 */
                // If there's a "levels" entry in the configuration
                if (levels.config) {
                  // Goes through the categories in the levels configuration entry,
                  // starting with the "higher" ones.
                  var keys = Object.keys(levels.config).sort();
                  for (var idx = 0; idx < keys.length; idx++) {
                    var levelCategory = keys[idx];
                    if (doesLevelEntryContainsLogger(levelCategory, loggerCategoryName)) {
                      // level for the logger
                      level = levels.config[levelCategory];
                    }
                  }
                }
                /* jshint +W073 */
                // Create the logger for this name if it doesn't already exist
                loggers[loggerCategoryName] = new Logger(loggerCategoryName, level);
                /* jshint -W083 */
                var appenderList;
                for (var appenderCategory in appenders) {
                  if (doesAppenderContainsLogger(appenderCategory, loggerCategoryName)) {
                    appenderList = appenders[appenderCategory];
                    appenderList.forEach(function (appender) {
                      loggers[loggerCategoryName].addListener('log', appender);
                    });
                  }
                }
                /* jshint +W083 */
                if (appenders[ALL_CATEGORIES]) {
                  appenderList = appenders[ALL_CATEGORIES];
                  appenderList.forEach(function (appender) {
                    loggers[loggerCategoryName].addListener('log', appender);
                  });
                }
              }
              return loggers[loggerCategoryName];
            }
            /**
             * args are appender, then zero or more categories
             */
            function addAppender() {
              var args = Array.prototype.slice.call(arguments);
              var appender = args.shift();
              if (args.length === 0 || args[0] === undefined) {
                args = [ALL_CATEGORIES];
              }
              //argument may already be an array
              if (Array.isArray(args[0])) {
                args = args[0];
              }
              args.forEach(function (appenderCategory) {
                addAppenderToCategory(appender, appenderCategory);
                if (appenderCategory === ALL_CATEGORIES) {
                  addAppenderToAllLoggers(appender);
                } else {
                  for (var loggerCategory in loggers) {
                    if (doesAppenderContainsLogger(appenderCategory, loggerCategory)) {
                      loggers[loggerCategory].addListener('log', appender);
                    }
                  }
                }
              });
            }
            function addAppenderToAllLoggers(appender) {
              for (var logger in loggers) {
                if (hasLogger(logger)) {
                  loggers[logger].addListener('log', appender);
                }
              }
            }
            function addAppenderToCategory(appender, category) {
              if (!appenders[category]) {
                appenders[category] = [];
              }
              appenders[category].push(appender);
            }
            function clearAppenders() {
              appenders = {};
              for (var logger in loggers) {
                if (hasLogger(logger)) {
                  loggers[logger].removeAllListeners('log');
                }
              }
            }
            function configureAppenders(appenderList, options) {
              clearAppenders();
              if (appenderList) {
                appenderList.forEach(function (appenderConfig) {
                  loadAppender(appenderConfig.type);
                  var appender;
                  appenderConfig.makers = appenderMakers;
                  try {
                    appender = appenderMakers[appenderConfig.type](appenderConfig, options);
                    addAppender(appender, appenderConfig.category);
                  } catch (e) {
                    throw new Error('log4js configuration problem for ' + util.inspect(appenderConfig), e);
                  }
                });
              }
            }
            function configureLevels(_levels) {
              levels.config = _levels;
              // Keep it so we can create loggers later using this cfg
              if (_levels) {
                var keys = Object.keys(levels.config).sort();
                for (var idx in keys) {
                  var category = keys[idx];
                  if (category === ALL_CATEGORIES) {
                    setGlobalLogLevel(_levels[category]);
                  }
                  /* jshint -W073 */
                  for (var loggerCategory in loggers) {
                    if (doesLevelEntryContainsLogger(category, loggerCategory)) {
                      loggers[loggerCategory].setLevel(_levels[category]);
                    }
                  }  /* jshint +W073 */
                }
              }
            }
            function setGlobalLogLevel(level) {
              Logger.prototype.level = levels.toLevel(level, levels.TRACE);
            }
            /**
             * Get the default logger instance.
             * @return {Logger} instance of default logger
             * @static
             */
            function getDefaultLogger() {
              return getLogger(Logger.DEFAULT_CATEGORY);
            }
            var configState = {};
            function loadConfigurationFile(filename) {
              if (filename) {
                return JSON.parse(fs.readFileSync(filename, 'utf8'));
              }
              return undefined;
            }
            function configureOnceOff(config, options) {
              if (config) {
                try {
                  configureLevels(config.levels);
                  configureAppenders(config.appenders, options);
                  if (config.replaceConsole) {
                    replaceConsole();
                  } else {
                    restoreConsole();
                  }
                } catch (e) {
                  throw new Error('Problem reading log4js config ' + util.inspect(config) + '. Error was "' + e.message + '" (' + e.stack + ')');
                }
              }
            }
            function reloadConfiguration(options) {
              var mtime = getMTime(configState.filename);
              if (!mtime)
                return;
              if (configState.lastMTime && mtime.getTime() > configState.lastMTime.getTime()) {
                configureOnceOff(loadConfigurationFile(configState.filename), options);
              }
              configState.lastMTime = mtime;
            }
            function getMTime(filename) {
              var mtime;
              try {
                mtime = fs.statSync(configState.filename).mtime;
              } catch (e) {
                getLogger('log4js').warn('Failed to load configuration file ' + filename);
              }
              return mtime;
            }
            function initReloadConfiguration(filename, options) {
              if (configState.timerId) {
                clearInterval(configState.timerId);
                delete configState.timerId;
              }
              configState.filename = filename;
              configState.lastMTime = getMTime(filename);
              configState.timerId = setInterval(reloadConfiguration, options.reloadSecs * 1000, options);
            }
            function configure(configurationFileOrObject, options) {
              var config = configurationFileOrObject;
              config = config || process.env.LOG4JS_CONFIG;
              options = options || {};
              if (config === undefined || config === null || typeof config === 'string') {
                if (options.reloadSecs) {
                  initReloadConfiguration(config, options);
                }
                config = loadConfigurationFile(config) || defaultConfig;
              } else {
                if (options.reloadSecs) {
                  getLogger('log4js').warn('Ignoring configuration reload parameter for "object" configuration.');
                }
              }
              configureOnceOff(config, options);
            }
            var originalConsoleFunctions = {
              log: console.log,
              debug: console.debug,
              info: console.info,
              warn: console.warn,
              error: console.error
            };
            function replaceConsole(logger) {
              function replaceWith(fn) {
                return function () {
                  fn.apply(logger, arguments);
                };
              }
              logger = logger || getLogger('console');
              [
                'log',
                'debug',
                'info',
                'warn',
                'error'
              ].forEach(function (item) {
                console[item] = replaceWith(item === 'log' ? logger.info : logger[item]);
              });
            }
            function restoreConsole() {
              [
                'log',
                'debug',
                'info',
                'warn',
                'error'
              ].forEach(function (item) {
                console[item] = originalConsoleFunctions[item];
              });
            }
            /**
             * Load an appenderModule based on the provided appender filepath. Will first
             * check if the appender path is a subpath of the log4js "lib/appenders" directory.
             * If not, it will attempt to load the the appender as complete path.
             *
             * @param {string} appender The filepath for the appender.
             * @returns {Object|null} The required appender or null if appender could not be loaded.
             * @private
             */
            function requireAppender(appender) {
              // Make sure ModuleRobot knows about the modules whose names we intend to build dynamically, or
              // else the include (whose name is known only at runtime) won't load.
              var appenders = {
                stdout: include('LYNX.Logger.ThirdParty.appender.stdout'),
                console: include('LYNX.Logger.ThirdParty.appender.console')
              };
              return appenders[appender];
            }
            /**
             * Load an appender. Provided the appender path to be loaded. If appenderModule is defined,
             * it will be used in place of requiring the appender module.
             *
             * @param {string} appender The path to the appender module.
             * @param {Object|void} [appenderModule] The pre-required appender module. When provided,
             * instead of requiring the appender by its path, this object will be used.
             * @returns {void}
             * @private
             */
            function loadAppender(appender, appenderModule) {
              appenderModule = appenderModule || requireAppender(appender);
              if (!appenderModule) {
                throw new Error('Invalid log4js appender: ' + util.inspect(appender));
              }
              module.exports.appenders[appender] = appenderModule.appender.bind(appenderModule);
              if (appenderModule.shutdown) {
                appenderShutdowns[appender] = appenderModule.shutdown.bind(appenderModule);
              }
              appenderMakers[appender] = appenderModule.configure.bind(appenderModule);
            }
            /**
             * Shutdown all log appenders. This will first disable all writing to appenders
             * and then call the shutdown function each appender.
             *
             * @params {Function} cb - The callback to be invoked once all appenders have
             *  shutdown. If an error occurs, the callback will be given the error object
             *  as the first argument.
             * @returns {void}
             */
            function shutdown(cb) {
              // First, disable all writing to appenders. This prevents appenders from
              // not being able to be drained because of run-away log writes.
              loggerModule.disableAllLogWrites();
              //turn off config reloading
              if (configState.timerId) {
                clearInterval(configState.timerId);
              }
              // Call each of the shutdown functions in parallel
              var completed = 0;
              var error;
              var shutdownFcts = [];
              var complete = function (err) {
                error = error || err;
                completed++;
                if (completed >= shutdownFcts.length) {
                  cb(error);
                }
              };
              for (var category in appenderShutdowns) {
                if (appenderShutdowns.hasOwnProperty(category)) {
                  shutdownFcts.push(appenderShutdowns[category]);
                }
              }
              if (!shutdownFcts.length) {
                return cb();
              }
              shutdownFcts.forEach(function (shutdownFct) {
                shutdownFct(complete);
              });
            }
            var exps = {
              getBufferedLogger: getBufferedLogger,
              getLogger: getLogger,
              getDefaultLogger: getDefaultLogger,
              hasLogger: hasLogger,
              addAppender: addAppender,
              loadAppender: loadAppender,
              clearAppenders: clearAppenders,
              configure: configure,
              shutdown: shutdown,
              replaceConsole: replaceConsole,
              restoreConsole: restoreConsole,
              levels: levels,
              setGlobalLogLevel: setGlobalLogLevel,
              layouts: layouts,
              appenders: {},
              appenderMakers: appenderMakers,
              connectLogger: require('./connect-logger').connectLogger
            };
            module.exports = exps;
            exportModule('LYNX.Logger.ThirdParty.log4js', exps);
            //set ourselves up
            configure();
          }.call(this, require('_process')));
        },
        {
          './connect-logger': 9,
          './layouts': 11,
          './levels': 12,
          './logger': 14,
          '_process': 5,
          'events': 3,
          'fs': 1,
          'path': 4,
          'util': 8
        }
      ],
      14: [
        function (require, module, exports) {
          'use strict';
          var levels = require('./levels'), util = require('util'), events = require('events'), DEFAULT_CATEGORY = '[default]';
          var logWritesEnabled = true;
          /**
           * Models a logging event.
           * @constructor
           * @param {String} categoryName name of category
           * @param {Log4js.Level} level level of message
           * @param {Array} data objects to log
           * @param {Log4js.Logger} logger the associated logger
           * @author Seth Chisamore
           */
          function LoggingEvent(categoryName, level, data, logger) {
            this.startTime = new Date();
            this.categoryName = categoryName;
            this.data = data;
            this.level = level;
            this.logger = logger;
          }
          /**
           * Logger to log messages.
           * use {@see Log4js#getLogger(String)} to get an instance.
           * @constructor
           * @param name name of category to log to
           * @author Stephan Strittmatter
           */
          function Logger(name, level) {
            this.category = name || DEFAULT_CATEGORY;
            if (level) {
              this.setLevel(level);
            }
          }
          util.inherits(Logger, events.EventEmitter);
          Logger.DEFAULT_CATEGORY = DEFAULT_CATEGORY;
          Logger.prototype.level = levels.TRACE;
          Logger.prototype.setLevel = function (level) {
            this.level = levels.toLevel(level, this.level || levels.TRACE);
          };
          Logger.prototype.removeLevel = function () {
            delete this.level;
          };
          Logger.prototype.log = function () {
            var logLevel = levels.toLevel(arguments[0], levels.INFO);
            if (!this.isLevelEnabled(logLevel)) {
              return;
            }
            var numArgs = arguments.length - 1;
            var args = new Array(numArgs);
            for (var i = 0; i < numArgs; i++) {
              args[i] = arguments[i + 1];
            }
            this._log(logLevel, args);
          };
          Logger.prototype.isLevelEnabled = function (otherLevel) {
            return this.level.isLessThanOrEqualTo(otherLevel);
          };
          [
            'Trace',
            'Debug',
            'Info',
            'Warn',
            'Error',
            'Fatal',
            'Mark'
          ].forEach(addLevelMethods);
          function addLevelMethods(level) {
            level = levels.toLevel(level);
            var levelStrLower = level.toString().toLowerCase();
            var levelMethod = levelStrLower.replace(/_([a-z])/g, function (g) {
              return g[1].toUpperCase();
            });
            var isLevelMethod = levelMethod[0].toUpperCase() + levelMethod.slice(1);
            Logger.prototype['is' + isLevelMethod + 'Enabled'] = function () {
              return this.isLevelEnabled(level);
            };
            Logger.prototype[levelMethod] = function () {
              if (logWritesEnabled && this.isLevelEnabled(level)) {
                var numArgs = arguments.length;
                var args = Array.prototype.slice.call(arguments);
                this._log(level, args);
              }
            };
          }
          Logger.prototype._log = function (level, data) {
            var loggingEvent = new LoggingEvent(this.category, level, data, this);
            this.emit('log', loggingEvent);
          };
          /**
           * Disable all log writes.
           * @returns {void}
           */
          function disableAllLogWrites() {
            logWritesEnabled = false;
          }
          /**
           * Enable log writes.
           * @returns {void}
           */
          function enableAllLogWrites() {
            logWritesEnabled = true;
          }
          exports.LoggingEvent = LoggingEvent;
          exports.Logger = Logger;
          exports.disableAllLogWrites = disableAllLogWrites;
          exports.enableAllLogWrites = enableAllLogWrites;
          exports.addLevelMethods = addLevelMethods;
        },
        {
          './levels': 12,
          'events': 3,
          'util': 8
        }
      ],
      15: [
        function (require, module, exports) {
          (function (process) {
            exports = module.exports = SemVer;
            // The debug function is excluded entirely from the minified version.
            /* nomin */
            var debug;
            /* nomin */
            if (typeof process === 'object' && /* nomin */
              process.env && /* nomin */
              process.env.NODE_DEBUG && /* nomin */
              /\bsemver\b/i.test(process.env.NODE_DEBUG))
              /* nomin */
              debug = function () {
                /* nomin */
                var args = Array.prototype.slice.call(arguments, 0);
                /* nomin */
                args.unshift('SEMVER');
                /* nomin */
                console.log.apply(console, args);  /* nomin */
              };  /* nomin */
            else
              /* nomin */
              debug = function () {
              };
            // Note: this is the semver.org version of the spec that it implements
            // Not necessarily the package version of this code.
            exports.SEMVER_SPEC_VERSION = '2.0.0';
            var MAX_LENGTH = 256;
            var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;
            // The actual regexps go on exports.re
            var re = exports.re = [];
            var src = exports.src = [];
            var R = 0;
            // The following Regular Expressions can be used for tokenizing,
            // validating, and parsing SemVer version strings.
            // ## Numeric Identifier
            // A single `0`, or a non-zero digit followed by zero or more digits.
            var NUMERICIDENTIFIER = R++;
            src[NUMERICIDENTIFIER] = '0|[1-9]\\d*';
            var NUMERICIDENTIFIERLOOSE = R++;
            src[NUMERICIDENTIFIERLOOSE] = '[0-9]+';
            // ## Non-numeric Identifier
            // Zero or more digits, followed by a letter or hyphen, and then zero or
            // more letters, digits, or hyphens.
            var NONNUMERICIDENTIFIER = R++;
            src[NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*';
            // ## Main Version
            // Three dot-separated numeric identifiers.
            var MAINVERSION = R++;
            src[MAINVERSION] = '(' + src[NUMERICIDENTIFIER] + ')\\.' + '(' + src[NUMERICIDENTIFIER] + ')\\.' + '(' + src[NUMERICIDENTIFIER] + ')';
            var MAINVERSIONLOOSE = R++;
            src[MAINVERSIONLOOSE] = '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + '(' + src[NUMERICIDENTIFIERLOOSE] + ')';
            // ## Pre-release Version Identifier
            // A numeric identifier, or a non-numeric identifier.
            var PRERELEASEIDENTIFIER = R++;
            src[PRERELEASEIDENTIFIER] = '(?:' + src[NUMERICIDENTIFIER] + '|' + src[NONNUMERICIDENTIFIER] + ')';
            var PRERELEASEIDENTIFIERLOOSE = R++;
            src[PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[NUMERICIDENTIFIERLOOSE] + '|' + src[NONNUMERICIDENTIFIER] + ')';
            // ## Pre-release Version
            // Hyphen, followed by one or more dot-separated pre-release version
            // identifiers.
            var PRERELEASE = R++;
            src[PRERELEASE] = '(?:-(' + src[PRERELEASEIDENTIFIER] + '(?:\\.' + src[PRERELEASEIDENTIFIER] + ')*))';
            var PRERELEASELOOSE = R++;
            src[PRERELEASELOOSE] = '(?:-?(' + src[PRERELEASEIDENTIFIERLOOSE] + '(?:\\.' + src[PRERELEASEIDENTIFIERLOOSE] + ')*))';
            // ## Build Metadata Identifier
            // Any combination of digits, letters, or hyphens.
            var BUILDIDENTIFIER = R++;
            src[BUILDIDENTIFIER] = '[0-9A-Za-z-]+';
            // ## Build Metadata
            // Plus sign, followed by one or more period-separated build metadata
            // identifiers.
            var BUILD = R++;
            src[BUILD] = '(?:\\+(' + src[BUILDIDENTIFIER] + '(?:\\.' + src[BUILDIDENTIFIER] + ')*))';
            // ## Full Version String
            // A main version, followed optionally by a pre-release version and
            // build metadata.
            // Note that the only major, minor, patch, and pre-release sections of
            // the version string are capturing groups.  The build metadata is not a
            // capturing group, because it should not ever be used in version
            // comparison.
            var FULL = R++;
            var FULLPLAIN = 'v?' + src[MAINVERSION] + src[PRERELEASE] + '?' + src[BUILD] + '?';
            src[FULL] = '^' + FULLPLAIN + '$';
            // like full, but allows v1.2.3 and =1.2.3, which people do sometimes.
            // also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty
            // common in the npm registry.
            var LOOSEPLAIN = '[v=\\s]*' + src[MAINVERSIONLOOSE] + src[PRERELEASELOOSE] + '?' + src[BUILD] + '?';
            var LOOSE = R++;
            src[LOOSE] = '^' + LOOSEPLAIN + '$';
            var GTLT = R++;
            src[GTLT] = '((?:<|>)?=?)';
            // Something like "2.*" or "1.2.x".
            // Note that "x.x" is a valid xRange identifer, meaning "any version"
            // Only the first item is strictly required.
            var XRANGEIDENTIFIERLOOSE = R++;
            src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + '|x|X|\\*';
            var XRANGEIDENTIFIER = R++;
            src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + '|x|X|\\*';
            var XRANGEPLAIN = R++;
            src[XRANGEPLAIN] = '[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + '(?:' + src[PRERELEASE] + ')?' + src[BUILD] + '?' + ')?)?';
            var XRANGEPLAINLOOSE = R++;
            src[XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + '(?:' + src[PRERELEASELOOSE] + ')?' + src[BUILD] + '?' + ')?)?';
            var XRANGE = R++;
            src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$';
            var XRANGELOOSE = R++;
            src[XRANGELOOSE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAINLOOSE] + '$';
            // Tilde ranges.
            // Meaning is "reasonably at or greater than"
            var LONETILDE = R++;
            src[LONETILDE] = '(?:~>?)';
            var TILDETRIM = R++;
            src[TILDETRIM] = '(\\s*)' + src[LONETILDE] + '\\s+';
            re[TILDETRIM] = new RegExp(src[TILDETRIM], 'g');
            var tildeTrimReplace = '$1~';
            var TILDE = R++;
            src[TILDE] = '^' + src[LONETILDE] + src[XRANGEPLAIN] + '$';
            var TILDELOOSE = R++;
            src[TILDELOOSE] = '^' + src[LONETILDE] + src[XRANGEPLAINLOOSE] + '$';
            // Caret ranges.
            // Meaning is "at least and backwards compatible with"
            var LONECARET = R++;
            src[LONECARET] = '(?:\\^)';
            var CARETTRIM = R++;
            src[CARETTRIM] = '(\\s*)' + src[LONECARET] + '\\s+';
            re[CARETTRIM] = new RegExp(src[CARETTRIM], 'g');
            var caretTrimReplace = '$1^';
            var CARET = R++;
            src[CARET] = '^' + src[LONECARET] + src[XRANGEPLAIN] + '$';
            var CARETLOOSE = R++;
            src[CARETLOOSE] = '^' + src[LONECARET] + src[XRANGEPLAINLOOSE] + '$';
            // A simple gt/lt/eq thing, or just "" to indicate "any version"
            var COMPARATORLOOSE = R++;
            src[COMPARATORLOOSE] = '^' + src[GTLT] + '\\s*(' + LOOSEPLAIN + ')$|^$';
            var COMPARATOR = R++;
            src[COMPARATOR] = '^' + src[GTLT] + '\\s*(' + FULLPLAIN + ')$|^$';
            // An expression to strip any whitespace between the gtlt and the thing
            // it modifies, so that `> 1.2.3` ==> `>1.2.3`
            var COMPARATORTRIM = R++;
            src[COMPARATORTRIM] = '(\\s*)' + src[GTLT] + '\\s*(' + LOOSEPLAIN + '|' + src[XRANGEPLAIN] + ')';
            // this one has to use the /g flag
            re[COMPARATORTRIM] = new RegExp(src[COMPARATORTRIM], 'g');
            var comparatorTrimReplace = '$1$2$3';
            // Something like `1.2.3 - 1.2.4`
            // Note that these all use the loose form, because they'll be
            // checked against either the strict or loose comparator form
            // later.
            var HYPHENRANGE = R++;
            src[HYPHENRANGE] = '^\\s*(' + src[XRANGEPLAIN] + ')' + '\\s+-\\s+' + '(' + src[XRANGEPLAIN] + ')' + '\\s*$';
            var HYPHENRANGELOOSE = R++;
            src[HYPHENRANGELOOSE] = '^\\s*(' + src[XRANGEPLAINLOOSE] + ')' + '\\s+-\\s+' + '(' + src[XRANGEPLAINLOOSE] + ')' + '\\s*$';
            // Star ranges basically just allow anything at all.
            var STAR = R++;
            src[STAR] = '(<|>)?=?\\s*\\*';
            // Compile to actual regexp objects.
            // All are flag-free, unless they were created above with a flag.
            for (var i = 0; i < R; i++) {
              debug(i, src[i]);
              if (!re[i])
                re[i] = new RegExp(src[i]);
            }
            exports.parse = parse;
            function parse(version, loose) {
              if (version instanceof SemVer)
                return version;
              if (typeof version !== 'string')
                return null;
              if (version.length > MAX_LENGTH)
                return null;
              var r = loose ? re[LOOSE] : re[FULL];
              if (!r.test(version))
                return null;
              try {
                return new SemVer(version, loose);
              } catch (er) {
                return null;
              }
            }
            exports.valid = valid;
            function valid(version, loose) {
              var v = parse(version, loose);
              return v ? v.version : null;
            }
            exports.clean = clean;
            function clean(version, loose) {
              var s = parse(version.trim().replace(/^[=v]+/, ''), loose);
              return s ? s.version : null;
            }
            exports.SemVer = SemVer;
            function SemVer(version, loose) {
              if (version instanceof SemVer) {
                if (version.loose === loose)
                  return version;
                else
                  version = version.version;
              } else if (typeof version !== 'string') {
                throw new TypeError('Invalid Version: ' + version);
              }
              if (version.length > MAX_LENGTH)
                throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters');
              if (!(this instanceof SemVer))
                return new SemVer(version, loose);
              debug('SemVer', version, loose);
              this.loose = loose;
              var m = version.trim().match(loose ? re[LOOSE] : re[FULL]);
              if (!m)
                throw new TypeError('Invalid Version: ' + version);
              this.raw = version;
              // these are actually numbers
              this.major = +m[1];
              this.minor = +m[2];
              this.patch = +m[3];
              if (this.major > MAX_SAFE_INTEGER || this.major < 0)
                throw new TypeError('Invalid major version');
              if (this.minor > MAX_SAFE_INTEGER || this.minor < 0)
                throw new TypeError('Invalid minor version');
              if (this.patch > MAX_SAFE_INTEGER || this.patch < 0)
                throw new TypeError('Invalid patch version');
              // numberify any prerelease numeric ids
              if (!m[4])
                this.prerelease = [];
              else
                this.prerelease = m[4].split('.').map(function (id) {
                  if (/^[0-9]+$/.test(id)) {
                    var num = +id;
                    if (num >= 0 && num < MAX_SAFE_INTEGER)
                      return num;
                  }
                  return id;
                });
              this.build = m[5] ? m[5].split('.') : [];
              this.format();
            }
            SemVer.prototype.format = function () {
              this.version = this.major + '.' + this.minor + '.' + this.patch;
              if (this.prerelease.length)
                this.version += '-' + this.prerelease.join('.');
              return this.version;
            };
            SemVer.prototype.toString = function () {
              return this.version;
            };
            SemVer.prototype.compare = function (other) {
              debug('SemVer.compare', this.version, this.loose, other);
              if (!(other instanceof SemVer))
                other = new SemVer(other, this.loose);
              return this.compareMain(other) || this.comparePre(other);
            };
            SemVer.prototype.compareMain = function (other) {
              if (!(other instanceof SemVer))
                other = new SemVer(other, this.loose);
              return compareIdentifiers(this.major, other.major) || compareIdentifiers(this.minor, other.minor) || compareIdentifiers(this.patch, other.patch);
            };
            SemVer.prototype.comparePre = function (other) {
              if (!(other instanceof SemVer))
                other = new SemVer(other, this.loose);
              // NOT having a prerelease is > having one
              if (this.prerelease.length && !other.prerelease.length)
                return -1;
              else if (!this.prerelease.length && other.prerelease.length)
                return 1;
              else if (!this.prerelease.length && !other.prerelease.length)
                return 0;
              var i = 0;
              do {
                var a = this.prerelease[i];
                var b = other.prerelease[i];
                debug('prerelease compare', i, a, b);
                if (a === undefined && b === undefined)
                  return 0;
                else if (b === undefined)
                  return 1;
                else if (a === undefined)
                  return -1;
                else if (a === b)
                  continue;
                else
                  return compareIdentifiers(a, b);
              } while (++i);
            };
            // preminor will bump the version up to the next minor release, and immediately
            // down to pre-release. premajor and prepatch work the same way.
            SemVer.prototype.inc = function (release, identifier) {
              switch (release) {
              case 'premajor':
                this.prerelease.length = 0;
                this.patch = 0;
                this.minor = 0;
                this.major++;
                this.inc('pre', identifier);
                break;
              case 'preminor':
                this.prerelease.length = 0;
                this.patch = 0;
                this.minor++;
                this.inc('pre', identifier);
                break;
              case 'prepatch':
                // If this is already a prerelease, it will bump to the next version
                // drop any prereleases that might already exist, since they are not
                // relevant at this point.
                this.prerelease.length = 0;
                this.inc('patch', identifier);
                this.inc('pre', identifier);
                break;
              // If the input is a non-prerelease version, this acts the same as
              // prepatch.
              case 'prerelease':
                if (this.prerelease.length === 0)
                  this.inc('patch', identifier);
                this.inc('pre', identifier);
                break;
              case 'major':
                // If this is a pre-major version, bump up to the same major version.
                // Otherwise increment major.
                // 1.0.0-5 bumps to 1.0.0
                // 1.1.0 bumps to 2.0.0
                if (this.minor !== 0 || this.patch !== 0 || this.prerelease.length === 0)
                  this.major++;
                this.minor = 0;
                this.patch = 0;
                this.prerelease = [];
                break;
              case 'minor':
                // If this is a pre-minor version, bump up to the same minor version.
                // Otherwise increment minor.
                // 1.2.0-5 bumps to 1.2.0
                // 1.2.1 bumps to 1.3.0
                if (this.patch !== 0 || this.prerelease.length === 0)
                  this.minor++;
                this.patch = 0;
                this.prerelease = [];
                break;
              case 'patch':
                // If this is not a pre-release version, it will increment the patch.
                // If it is a pre-release it will bump up to the same patch version.
                // 1.2.0-5 patches to 1.2.0
                // 1.2.0 patches to 1.2.1
                if (this.prerelease.length === 0)
                  this.patch++;
                this.prerelease = [];
                break;
              // This probably shouldn't be used publicly.
              // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction.
              case 'pre':
                if (this.prerelease.length === 0)
                  this.prerelease = [0];
                else {
                  var i = this.prerelease.length;
                  while (--i >= 0) {
                    if (typeof this.prerelease[i] === 'number') {
                      this.prerelease[i]++;
                      i = -2;
                    }
                  }
                  if (i === -1)
                    // didn't increment anything
                    this.prerelease.push(0);
                }
                if (identifier) {
                  // 1.2.0-beta.1 bumps to 1.2.0-beta.2,
                  // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0
                  if (this.prerelease[0] === identifier) {
                    if (isNaN(this.prerelease[1]))
                      this.prerelease = [
                        identifier,
                        0
                      ];
                  } else
                    this.prerelease = [
                      identifier,
                      0
                    ];
                }
                break;
              default:
                throw new Error('invalid increment argument: ' + release);
              }
              this.format();
              this.raw = this.version;
              return this;
            };
            exports.inc = inc;
            function inc(version, release, loose, identifier) {
              if (typeof loose === 'string') {
                identifier = loose;
                loose = undefined;
              }
              try {
                return new SemVer(version, loose).inc(release, identifier).version;
              } catch (er) {
                return null;
              }
            }
            exports.diff = diff;
            function diff(version1, version2) {
              if (eq(version1, version2)) {
                return null;
              } else {
                var v1 = parse(version1);
                var v2 = parse(version2);
                if (v1.prerelease.length || v2.prerelease.length) {
                  for (var key in v1) {
                    if (key === 'major' || key === 'minor' || key === 'patch') {
                      if (v1[key] !== v2[key]) {
                        return 'pre' + key;
                      }
                    }
                  }
                  return 'prerelease';
                }
                for (var key in v1) {
                  if (key === 'major' || key === 'minor' || key === 'patch') {
                    if (v1[key] !== v2[key]) {
                      return key;
                    }
                  }
                }
              }
            }
            exports.compareIdentifiers = compareIdentifiers;
            var numeric = /^[0-9]+$/;
            function compareIdentifiers(a, b) {
              var anum = numeric.test(a);
              var bnum = numeric.test(b);
              if (anum && bnum) {
                a = +a;
                b = +b;
              }
              return anum && !bnum ? -1 : bnum && !anum ? 1 : a < b ? -1 : a > b ? 1 : 0;
            }
            exports.rcompareIdentifiers = rcompareIdentifiers;
            function rcompareIdentifiers(a, b) {
              return compareIdentifiers(b, a);
            }
            exports.major = major;
            function major(a, loose) {
              return new SemVer(a, loose).major;
            }
            exports.minor = minor;
            function minor(a, loose) {
              return new SemVer(a, loose).minor;
            }
            exports.patch = patch;
            function patch(a, loose) {
              return new SemVer(a, loose).patch;
            }
            exports.compare = compare;
            function compare(a, b, loose) {
              return new SemVer(a, loose).compare(b);
            }
            exports.compareLoose = compareLoose;
            function compareLoose(a, b) {
              return compare(a, b, true);
            }
            exports.rcompare = rcompare;
            function rcompare(a, b, loose) {
              return compare(b, a, loose);
            }
            exports.sort = sort;
            function sort(list, loose) {
              return list.sort(function (a, b) {
                return exports.compare(a, b, loose);
              });
            }
            exports.rsort = rsort;
            function rsort(list, loose) {
              return list.sort(function (a, b) {
                return exports.rcompare(a, b, loose);
              });
            }
            exports.gt = gt;
            function gt(a, b, loose) {
              return compare(a, b, loose) > 0;
            }
            exports.lt = lt;
            function lt(a, b, loose) {
              return compare(a, b, loose) < 0;
            }
            exports.eq = eq;
            function eq(a, b, loose) {
              return compare(a, b, loose) === 0;
            }
            exports.neq = neq;
            function neq(a, b, loose) {
              return compare(a, b, loose) !== 0;
            }
            exports.gte = gte;
            function gte(a, b, loose) {
              return compare(a, b, loose) >= 0;
            }
            exports.lte = lte;
            function lte(a, b, loose) {
              return compare(a, b, loose) <= 0;
            }
            exports.cmp = cmp;
            function cmp(a, op, b, loose) {
              var ret;
              switch (op) {
              case '===':
                if (typeof a === 'object')
                  a = a.version;
                if (typeof b === 'object')
                  b = b.version;
                ret = a === b;
                break;
              case '!==':
                if (typeof a === 'object')
                  a = a.version;
                if (typeof b === 'object')
                  b = b.version;
                ret = a !== b;
                break;
              case '':
              case '=':
              case '==':
                ret = eq(a, b, loose);
                break;
              case '!=':
                ret = neq(a, b, loose);
                break;
              case '>':
                ret = gt(a, b, loose);
                break;
              case '>=':
                ret = gte(a, b, loose);
                break;
              case '<':
                ret = lt(a, b, loose);
                break;
              case '<=':
                ret = lte(a, b, loose);
                break;
              default:
                throw new TypeError('Invalid operator: ' + op);
              }
              return ret;
            }
            exports.Comparator = Comparator;
            function Comparator(comp, loose) {
              if (comp instanceof Comparator) {
                if (comp.loose === loose)
                  return comp;
                else
                  comp = comp.value;
              }
              if (!(this instanceof Comparator))
                return new Comparator(comp, loose);
              debug('comparator', comp, loose);
              this.loose = loose;
              this.parse(comp);
              if (this.semver === ANY)
                this.value = '';
              else
                this.value = this.operator + this.semver.version;
              debug('comp', this);
            }
            var ANY = {};
            Comparator.prototype.parse = function (comp) {
              var r = this.loose ? re[COMPARATORLOOSE] : re[COMPARATOR];
              var m = comp.match(r);
              if (!m)
                throw new TypeError('Invalid comparator: ' + comp);
              this.operator = m[1];
              if (this.operator === '=')
                this.operator = '';
              // if it literally is just '>' or '' then allow anything.
              if (!m[2])
                this.semver = ANY;
              else
                this.semver = new SemVer(m[2], this.loose);
            };
            Comparator.prototype.toString = function () {
              return this.value;
            };
            Comparator.prototype.test = function (version) {
              debug('Comparator.test', version, this.loose);
              if (this.semver === ANY)
                return true;
              if (typeof version === 'string')
                version = new SemVer(version, this.loose);
              return cmp(version, this.operator, this.semver, this.loose);
            };
            exports.Range = Range;
            function Range(range, loose) {
              if (range instanceof Range && range.loose === loose)
                return range;
              if (!(this instanceof Range))
                return new Range(range, loose);
              this.loose = loose;
              // First, split based on boolean or ||
              this.raw = range;
              this.set = range.split(/\s*\|\|\s*/).map(function (range) {
                return this.parseRange(range.trim());
              }, this).filter(function (c) {
                // throw out any that are not relevant for whatever reason
                return c.length;
              });
              if (!this.set.length) {
                throw new TypeError('Invalid SemVer Range: ' + range);
              }
              this.format();
            }
            Range.prototype.format = function () {
              this.range = this.set.map(function (comps) {
                return comps.join(' ').trim();
              }).join('||').trim();
              return this.range;
            };
            Range.prototype.toString = function () {
              return this.range;
            };
            Range.prototype.parseRange = function (range) {
              var loose = this.loose;
              range = range.trim();
              debug('range', range, loose);
              // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`
              var hr = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE];
              range = range.replace(hr, hyphenReplace);
              debug('hyphen replace', range);
              // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
              range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace);
              debug('comparator trim', range, re[COMPARATORTRIM]);
              // `~ 1.2.3` => `~1.2.3`
              range = range.replace(re[TILDETRIM], tildeTrimReplace);
              // `^ 1.2.3` => `^1.2.3`
              range = range.replace(re[CARETTRIM], caretTrimReplace);
              // normalize spaces
              range = range.split(/\s+/).join(' ');
              // At this point, the range is completely trimmed and
              // ready to be split into comparators.
              var compRe = loose ? re[COMPARATORLOOSE] : re[COMPARATOR];
              var set = range.split(' ').map(function (comp) {
                return parseComparator(comp, loose);
              }).join(' ').split(/\s+/);
              if (this.loose) {
                // in loose mode, throw out any that are not valid comparators
                set = set.filter(function (comp) {
                  return !!comp.match(compRe);
                });
              }
              set = set.map(function (comp) {
                return new Comparator(comp, loose);
              });
              return set;
            };
            // Mostly just for testing and legacy API reasons
            exports.toComparators = toComparators;
            function toComparators(range, loose) {
              return new Range(range, loose).set.map(function (comp) {
                return comp.map(function (c) {
                  return c.value;
                }).join(' ').trim().split(' ');
              });
            }
            // comprised of xranges, tildes, stars, and gtlt's at this point.
            // already replaced the hyphen ranges
            // turn into a set of JUST comparators.
            function parseComparator(comp, loose) {
              debug('comp', comp);
              comp = replaceCarets(comp, loose);
              debug('caret', comp);
              comp = replaceTildes(comp, loose);
              debug('tildes', comp);
              comp = replaceXRanges(comp, loose);
              debug('xrange', comp);
              comp = replaceStars(comp, loose);
              debug('stars', comp);
              return comp;
            }
            function isX(id) {
              return !id || id.toLowerCase() === 'x' || id === '*';
            }
            // ~, ~> --> * (any, kinda silly)
            // ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0
            // ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0
            // ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0
            // ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0
            // ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0
            function replaceTildes(comp, loose) {
              return comp.trim().split(/\s+/).map(function (comp) {
                return replaceTilde(comp, loose);
              }).join(' ');
            }
            function replaceTilde(comp, loose) {
              var r = loose ? re[TILDELOOSE] : re[TILDE];
              return comp.replace(r, function (_, M, m, p, pr) {
                debug('tilde', comp, _, M, m, p, pr);
                var ret;
                if (isX(M))
                  ret = '';
                else if (isX(m))
                  ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0';
                else if (isX(p))
                  // ~1.2 == >=1.2.0 <1.3.0
                  ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0';
                else if (pr) {
                  debug('replaceTilde pr', pr);
                  if (pr.charAt(0) !== '-')
                    pr = '-' + pr;
                  ret = '>=' + M + '.' + m + '.' + p + pr + ' <' + M + '.' + (+m + 1) + '.0';
                } else
                  // ~1.2.3 == >=1.2.3 <1.3.0
                  ret = '>=' + M + '.' + m + '.' + p + ' <' + M + '.' + (+m + 1) + '.0';
                debug('tilde return', ret);
                return ret;
              });
            }
            // ^ --> * (any, kinda silly)
            // ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0
            // ^2.0, ^2.0.x --> >=2.0.0 <3.0.0
            // ^1.2, ^1.2.x --> >=1.2.0 <2.0.0
            // ^1.2.3 --> >=1.2.3 <2.0.0
            // ^1.2.0 --> >=1.2.0 <2.0.0
            function replaceCarets(comp, loose) {
              return comp.trim().split(/\s+/).map(function (comp) {
                return replaceCaret(comp, loose);
              }).join(' ');
            }
            function replaceCaret(comp, loose) {
              debug('caret', comp, loose);
              var r = loose ? re[CARETLOOSE] : re[CARET];
              return comp.replace(r, function (_, M, m, p, pr) {
                debug('caret', comp, _, M, m, p, pr);
                var ret;
                if (isX(M))
                  ret = '';
                else if (isX(m))
                  ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0';
                else if (isX(p)) {
                  if (M === '0')
                    ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0';
                  else
                    ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0';
                } else if (pr) {
                  debug('replaceCaret pr', pr);
                  if (pr.charAt(0) !== '-')
                    pr = '-' + pr;
                  if (M === '0') {
                    if (m === '0')
                      ret = '>=' + M + '.' + m + '.' + p + pr + ' <' + M + '.' + m + '.' + (+p + 1);
                    else
                      ret = '>=' + M + '.' + m + '.' + p + pr + ' <' + M + '.' + (+m + 1) + '.0';
                  } else
                    ret = '>=' + M + '.' + m + '.' + p + pr + ' <' + (+M + 1) + '.0.0';
                } else {
                  debug('no pr');
                  if (M === '0') {
                    if (m === '0')
                      ret = '>=' + M + '.' + m + '.' + p + ' <' + M + '.' + m + '.' + (+p + 1);
                    else
                      ret = '>=' + M + '.' + m + '.' + p + ' <' + M + '.' + (+m + 1) + '.0';
                  } else
                    ret = '>=' + M + '.' + m + '.' + p + ' <' + (+M + 1) + '.0.0';
                }
                debug('caret return', ret);
                return ret;
              });
            }
            function replaceXRanges(comp, loose) {
              debug('replaceXRanges', comp, loose);
              return comp.split(/\s+/).map(function (comp) {
                return replaceXRange(comp, loose);
              }).join(' ');
            }
            function replaceXRange(comp, loose) {
              comp = comp.trim();
              var r = loose ? re[XRANGELOOSE] : re[XRANGE];
              return comp.replace(r, function (ret, gtlt, M, m, p, pr) {
                debug('xRange', comp, ret, gtlt, M, m, p, pr);
                var xM = isX(M);
                var xm = xM || isX(m);
                var xp = xm || isX(p);
                var anyX = xp;
                if (gtlt === '=' && anyX)
                  gtlt = '';
                if (xM) {
                  if (gtlt === '>' || gtlt === '<') {
                    // nothing is allowed
                    ret = '<0.0.0';
                  } else {
                    // nothing is forbidden
                    ret = '*';
                  }
                } else if (gtlt && anyX) {
                  // replace X with 0
                  if (xm)
                    m = 0;
                  if (xp)
                    p = 0;
                  if (gtlt === '>') {
                    // >1 => >=2.0.0
                    // >1.2 => >=1.3.0
                    // >1.2.3 => >= 1.2.4
                    gtlt = '>=';
                    if (xm) {
                      M = +M + 1;
                      m = 0;
                      p = 0;
                    } else if (xp) {
                      m = +m + 1;
                      p = 0;
                    }
                  } else if (gtlt === '<=') {
                    // <=0.7.x is actually <0.8.0, since any 0.7.x should
                    // pass.  Similarly, <=7.x is actually <8.0.0, etc.
                    gtlt = '<';
                    if (xm)
                      M = +M + 1;
                    else
                      m = +m + 1;
                  }
                  ret = gtlt + M + '.' + m + '.' + p;
                } else if (xm) {
                  ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0';
                } else if (xp) {
                  ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0';
                }
                debug('xRange return', ret);
                return ret;
              });
            }
            // Because * is AND-ed with everything else in the comparator,
            // and '' means "any version", just remove the *s entirely.
            function replaceStars(comp, loose) {
              debug('replaceStars', comp, loose);
              // Looseness is ignored here.  star is always as loose as it gets!
              return comp.trim().replace(re[STAR], '');
            }
            // This function is passed to string.replace(re[HYPHENRANGE])
            // M, m, patch, prerelease, build
            // 1.2 - 3.4.5 => >=1.2.0 <=3.4.5
            // 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do
            // 1.2 - 3.4 => >=1.2.0 <3.5.0
            function hyphenReplace($0, from, fM, fm, fp, fpr, fb, to, tM, tm, tp, tpr, tb) {
              if (isX(fM))
                from = '';
              else if (isX(fm))
                from = '>=' + fM + '.0.0';
              else if (isX(fp))
                from = '>=' + fM + '.' + fm + '.0';
              else
                from = '>=' + from;
              if (isX(tM))
                to = '';
              else if (isX(tm))
                to = '<' + (+tM + 1) + '.0.0';
              else if (isX(tp))
                to = '<' + tM + '.' + (+tm + 1) + '.0';
              else if (tpr)
                to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr;
              else
                to = '<=' + to;
              return (from + ' ' + to).trim();
            }
            // if ANY of the sets match ALL of its comparators, then pass
            Range.prototype.test = function (version) {
              if (!version)
                return false;
              if (typeof version === 'string')
                version = new SemVer(version, this.loose);
              for (var i = 0; i < this.set.length; i++) {
                if (testSet(this.set[i], version))
                  return true;
              }
              return false;
            };
            function testSet(set, version) {
              for (var i = 0; i < set.length; i++) {
                if (!set[i].test(version))
                  return false;
              }
              if (version.prerelease.length) {
                // Find the set of versions that are allowed to have prereleases
                // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0
                // That should allow `1.2.3-pr.2` to pass.
                // However, `1.2.4-alpha.notready` should NOT be allowed,
                // even though it's within the range set by the comparators.
                for (var i = 0; i < set.length; i++) {
                  debug(set[i].semver);
                  if (set[i].semver === ANY)
                    continue;
                  if (set[i].semver.prerelease.length > 0) {
                    var allowed = set[i].semver;
                    if (allowed.major === version.major && allowed.minor === version.minor && allowed.patch === version.patch)
                      return true;
                  }
                }
                // Version has a -pre, but it's not one of the ones we like.
                return false;
              }
              return true;
            }
            exports.satisfies = satisfies;
            function satisfies(version, range, loose) {
              try {
                range = new Range(range, loose);
              } catch (er) {
                return false;
              }
              return range.test(version);
            }
            exports.maxSatisfying = maxSatisfying;
            function maxSatisfying(versions, range, loose) {
              return versions.filter(function (version) {
                return satisfies(version, range, loose);
              }).sort(function (a, b) {
                return rcompare(a, b, loose);
              })[0] || null;
            }
            exports.minSatisfying = minSatisfying;
            function minSatisfying(versions, range, loose) {
              return versions.filter(function (version) {
                return satisfies(version, range, loose);
              }).sort(function (a, b) {
                return compare(a, b, loose);
              })[0] || null;
            }
            exports.validRange = validRange;
            function validRange(range, loose) {
              try {
                // Return '*' instead of '' so that truthiness works.
                // This will throw if it's invalid anyway
                return new Range(range, loose).range || '*';
              } catch (er) {
                return null;
              }
            }
            // Determine if version is less than all the versions possible in the range
            exports.ltr = ltr;
            function ltr(version, range, loose) {
              return outside(version, range, '<', loose);
            }
            // Determine if version is greater than all the versions possible in the range.
            exports.gtr = gtr;
            function gtr(version, range, loose) {
              return outside(version, range, '>', loose);
            }
            exports.outside = outside;
            function outside(version, range, hilo, loose) {
              version = new SemVer(version, loose);
              range = new Range(range, loose);
              var gtfn, ltefn, ltfn, comp, ecomp;
              switch (hilo) {
              case '>':
                gtfn = gt;
                ltefn = lte;
                ltfn = lt;
                comp = '>';
                ecomp = '>=';
                break;
              case '<':
                gtfn = lt;
                ltefn = gte;
                ltfn = gt;
                comp = '<';
                ecomp = '<=';
                break;
              default:
                throw new TypeError('Must provide a hilo val of "<" or ">"');
              }
              // If it satisifes the range it is not outside
              if (satisfies(version, range, loose)) {
                return false;
              }
              // From now on, variable terms are as if we're in "gtr" mode.
              // but note that everything is flipped for the "ltr" function.
              for (var i = 0; i < range.set.length; ++i) {
                var comparators = range.set[i];
                var high = null;
                var low = null;
                comparators.forEach(function (comparator) {
                  if (comparator.semver === ANY) {
                    comparator = new Comparator('>=0.0.0');
                  }
                  high = high || comparator;
                  low = low || comparator;
                  if (gtfn(comparator.semver, high.semver, loose)) {
                    high = comparator;
                  } else if (ltfn(comparator.semver, low.semver, loose)) {
                    low = comparator;
                  }
                });
                // If the edge version comparator has a operator then our version
                // isn't outside it
                if (high.operator === comp || high.operator === ecomp) {
                  return false;
                }
                // If the lowest version comparator has an operator and our version
                // is less than it then it isn't higher than the range
                if ((!low.operator || low.operator === comp) && ltefn(version, low.semver)) {
                  return false;
                } else if (low.operator === ecomp && ltfn(version, low.semver)) {
                  return false;
                }
              }
              return true;
            }
            exports.prerelease = prerelease;
            function prerelease(version, loose) {
              var parsed = parse(version, loose);
              return parsed && parsed.prerelease.length ? parsed.prerelease : null;
            }
          }.call(this, require('_process')));
        },
        { '_process': 5 }
      ]
    }, {}, [13]));
    (function () {
      var chalk = include('LYNX.Logger.ThirdParty.chalk');
      var log4js = include('LYNX.Logger.ThirdParty.log4js');
      var ModuleLogger = {};
      var isLog4jsInitialized = false;
      var _loggers = {};
      /**
       * A log4js configuration file that emulates the old LYNX logger with colored output for entries
       * formatted like this:
       * 2016-11-16T20:38:46.366Z [module.name] Message
       */
      var DEFAULT_CONSOLE_PRINTER_CONFIG = {
        appenders: [{
            type: 'console',
            layout: {
              type: 'pattern',
              pattern: '%x{colorStart} %x{dateUTC} [%c] %m %x{colorStop}',
              tokens: {
                dateUTC: function (loggingEvent) {
                  return new Date().toISOString();
                },
                colorStart: chalk.supportsColor ? function (loggingEvent) {
                  var style = ModuleLogger.color.styles[loggingEvent.level.levelStr];
                  return style ? style.open : chalk.styles.white.open;
                } : function () {
                  return '';
                },
                colorStop: chalk.supportsColor ? function (loggingEvent) {
                  var style = ModuleLogger.color.styles[loggingEvent.level.levelStr];
                  return style ? style.close : chalk.styles.white.close;
                } : function () {
                  return '';
                }
              }
            }
          }],
        replaceConsole: false,
        levels: { '[all]': 'INFO' }
      };
      /**
       * Fetches the logger for a given module.
       * @param {?string=} in_moduleName The module name. For example, use
       *   'LYNX.HFDM.PropertyGraph.Implementations.Cassandra' to get a logger specific to that module.
       *   Omitting this parameter will yield a global logger that isn't tied to a specific module.
       * @return {log4js.Logger} A log4js logger.
       */
      ModuleLogger.getLogger = function (in_moduleName) {
        _lazyInitialize.call(this);
        var logger = log4js.getLogger(in_moduleName);
        _loggers[in_moduleName] = logger.level.levelStr;
        return logger;
      };
      /**
       * Initializes logging with the specified log4js config.
       * @param {?Object=} in_loggerConfig A log4js configuration file. Defaults to a configuration that
       *   emulates the old LYNX logger with colored output for entries formatted like this:
       *   2016-11-16T20:38:46.366Z [module.name] Message
       */
      ModuleLogger.init = function (in_loggerConfig) {
        in_loggerConfig = in_loggerConfig || DEFAULT_CONSOLE_PRINTER_CONFIG;
        log4js.configure(in_loggerConfig);
      };
      Object.defineProperty(ModuleLogger, 'loggers', {
        /**
         * Fetches a list of all loggers known to the ModuleLogger. Useful to know what modules have
         * enhanced logging capabilities, such as being able to set log levels per module dynamically.
         * @return {Object} A map of module names to their logging level.
         *   Example: {"LYNX.CollaborationServer":"WARN","LYNX.HFDM.PropertyGraph.Implementations.Cassandra":"TRACE"}
         */
        get: function () {
          var loggerNameArray = Object.keys(_loggers);
          for (var i = 0; i < loggerNameArray.length; i++) {
            var loggerName = loggerNameArray[i];
            var logger = log4js.getLogger(loggerName);
            _loggers[loggerName] = logger.level.levelStr;
          }
          // Return a copy
          return JSON.parse(JSON.stringify(_loggers));
        }
      });
      /**
       * Initializes log4js with a config that emulates the ConsolePrinter behaviour, prior to adding
       * support for log4js. This configures the console logger to output lines formatted like this:
       * 2016-11-16T20:38:46.366Z [module.name] Message
       *
       * or, if no module is supplied:
       * 2016-11-16T20:38:46.366Z [] Message
       * @private
       * @this ModuleLogger
       */
      var _lazyInitialize = function () {
        if (isLog4jsInitialized) {
          return;
        }
        ModuleLogger.init();
        isLog4jsInitialized = true;
      };
      /**
       * Exposes the logger's chalk color styles per logging level.
       */
      ModuleLogger.color = {
        styles: {
          TRACE: chalk.styles.gray,
          DEBUG: chalk.styles.green,
          INFO: chalk.styles.cyan,
          WARN: chalk.styles.yellow,
          ERROR: chalk.styles.red,
          FATAL: {
            open: chalk.styles.bold.open + chalk.styles.white.open + chalk.styles.bgRed.open,
            close: chalk.styles.bold.close + chalk.styles.white.close + chalk.styles.bgRed.close
          }
        }
      };
      /**
       * Exposing the available log levels makes it easy to write code like this:
       *
       * if (logger.isLevelEnabled(ModuleLogger.levels.TRACE)) {
       *
       * that doesn't rely on strings.
       */
      ModuleLogger.levels = log4js.levels;
      exportModule('LYNX.Logger.ModuleLogger', ModuleLogger);
    }());
    (function () {
      var _ = include('underscore');
      var ModuleLogger = include('LYNX.Logger.ModuleLogger');
      var logger = ModuleLogger.getLogger('LYNX.CollaborationClient.BearerTokenAdapter');
      /**
       * @param {object} params The Bearer Token Adapter options
       * @param {getBearerTokenFn=} [in_params.getBearerToken] Function that accepts a callback
       *  function that returns an error or the OAuth2 bearer token representing the user.
       */
      var BearerTokenAdapter = function (params) {
        this._getBearerToken = params.getBearerToken;
      };
      /**
       * Add bearer token header
       * @param {object} in_headers The headers to augment with a bearer token.
       * @param {function} in_callback invoked with error or resulting headers
       */
      BearerTokenAdapter.prototype.addBearerToken = function (in_headers, in_callback) {
        var that = this;
        if (!that._getBearerToken) {
          in_callback(undefined, in_headers);
        } else {
          // Invoke user-supplied callback to get the latest bearer token
          that._getBearerToken(function (error, bearerToken) {
            if (error) {
              logger.error('Failed to retrieve bearer token: ' + error.message);
              in_callback(error);
            } else {
              in_callback(undefined, _.extend(in_headers, { Authorization: 'Bearer ' + bearerToken }));
            }
          });
        }
      };
      exportModule('LYNX.CollaborationClient.BearerTokenAdapter', BearerTokenAdapter);
    }());
    (function () {
      var _ = include('underscore');
      var BearerTokenAdapter = include('LYNX.CollaborationClient.BearerTokenAdapter');
      /**
       * Service providing url related methods for the LynxCollaborationServer
       * @param {object} in_params List of parameters
       * @param {string} in_params.serverUrl The url pointing to the collaboration server.
       * @param {getBearerTokenFn=} in_params.getBearerToken Function that accepts a callback
       *  function that should be called with an error or the OAuth2 bearer token representing the user.
       * @param {object|undefined} [in_params.headers] Headers to supply when making requests (used by tests)
       * @constructor
       */
      var UrlService = function (in_params) {
        this._baseURL = in_params.serverUrl;
        this._getBearerToken = in_params.getBearerToken;
        this._headers = in_params.headers || {};
        this._bearerTokenAdapter = new BearerTokenAdapter({ getBearerToken: this._getBearerToken });
      };
      /**
       * Reset serverUrl to new value
       * @param {string} serverUrl The url pointing to the collaboration server.
       */
      UrlService.prototype.resetServerUrl = function (serverUrl) {
        this._baseURL = serverUrl;
      };
      /**
       * Return authenticate REST endpoint parameters
       * @param {object} in_headers Any additional headers to use in the request
       * @param {function} in_callback invoked with error or request parameters
       */
      UrlService.prototype.getAuthenticateParameters = function (in_headers, in_callback) {
        var that = this;
        // Create a new headers object where in_headers supersedes this.headers
        var headers = _.extend({}, that._headers, in_headers);
        that._bearerTokenAdapter.addBearerToken(headers, function (error, headersWithBearerToken) {
          if (error) {
            in_callback(error);
          } else {
            in_callback(undefined, {
              url: that._baseURL + '/authorize',
              method: 'GET',
              json: true,
              headers: headersWithBearerToken
            });
          }
        });
      };
      /**
       * Return room occupancy REST endpoint parameters
       * @param {string} in_roomName The room name to check its occupancy
       * @param {object} queryString Additional options to be appended as querystring in the url
       * @param {function} in_callback invoked with error or request parameters
       */
      UrlService.prototype.getRoomOccupancyParameters = function (in_roomName, queryString, in_callback) {
        var that = this;
        that._bearerTokenAdapter.addBearerToken(that._headers, function (error, headersWithBearerToken) {
          if (error) {
            in_callback(error);
          } else {
            in_callback(undefined, {
              url: that._baseURL + '/rooms/' + in_roomName + '/occupancy',
              method: 'GET',
              qs: queryString,
              json: true,
              headers: headersWithBearerToken
            });
          }
        });
      };
      /**
       * Return room occupancy REST endpoint parameters
       * @param {string} in_roomName The room name to check its occupancy
       * @param {object} queryString Additional options to be appended as querystring in the url
       * @return {Promise} - Resolved request parameters, else rejected Error
       */
      UrlService.prototype.getRoomParameters = function (in_roomName, queryString) {
        var that = this;
        return new Promise(function (resolve, reject) {
          that._bearerTokenAdapter.addBearerToken(that._headers, function (error, headersWithBearerToken) {
            if (error) {
              reject(error);
            } else {
              resolve({
                url: that._baseURL + '/rooms/' + in_roomName,
                method: 'GET',
                qs: queryString,
                json: true,
                headers: headersWithBearerToken
              });
            }
          });
        });
      };
      /**
       * Return all open rooms
       * @param {object} queryString Additional options to be appended as querystring in the url
       * @param {function} in_callback invoked with error or request parameters
       */
      UrlService.prototype.getRoomsParams = function (queryString, in_callback) {
        var that = this;
        that._bearerTokenAdapter.addBearerToken(that._headers, function (error, headersWithBearerToken) {
          if (error) {
            in_callback(error);
          } else {
            in_callback(undefined, {
              url: that._baseURL + '/rooms',
              method: 'GET',
              qs: queryString,
              json: true,
              headers: headersWithBearerToken
            });
          }
        });
      };
      exportModule('LYNX.CollaborationClient.Services.UrlService', UrlService);
    }());
    (function () {
      /**
       * Error messages for the PropertySets library
       * @alias LYNX.Property._constants.MSG
       */
      var MSG = {
        MODIFY_READ_ONLY: 'Error. Trying to modify read only property value.',
        OVERWRITING_ID: 'Id already exists: ',
        REMOVING_NON_EXISTING_ID: 'Removing non existing id: ',
        ID_IS_DIFFERENT: 'Trying to match properties with different IDs.',
        TYPEID_IS_DIFFERENT: 'Trying to match properties with different typeids.',
        DESERIALIZE_ERROR: 'Error deserializing property',
        CONTEXT_IS_DIFFERENT: 'Trying to match properties with different context.',
        STRUCT_IS_DIFFERENT: 'Trying to match properties with different structure.',
        CANNOT_RESOLVE_PATH: 'Cannot resolve path in ',
        NO_PATH_BETWEEN: 'No path between ',
        INVALID_PATH: 'Invalid path in ChangeSet: ',
        ID_CHANGE_FOR_PROPERTY_WITH_PARENT: 'Cannot change the ID of a property that has a parent',
        INVALID_PATH_TOKEN: 'Accessed a child via an invalid path syntax',
        PROPERTY_ALREADY_EXISTS: 'The property already exists in this collection: ',
        INSERTED_EXISTING_ENTRY: 'Inserted an already existing entry.',
        REMOVED_NON_EXISTING_ENTRY: 'Removed a not existing entry.',
        MODIFY_NON_EXISTING_ENTRY: 'Modified a not existing entry.',
        INSERTED_ENTRY_WITH_PARENT: 'Inserted a property which has already another parent into collection.',
        EMPTY_TOKEN: 'Encountered empty token. These have to be escaped.',
        QUOTES_WITHIN_TOKEN: 'Quotes must be at the start and end with a token',
        DOT_AT_START: 'Encountered a dot at the start of the path',
        DOT_AT_END: 'Encountered a dot at the end of the path',
        DOTS_IN_SQUARE_BRACKETS: 'Encountered a dot within a square bracket. These have to be escaped.',
        MISSING_DOT_AT_SEGMENT_START: 'Missing . or [ at segment start.',
        INVALID_END_OF_SQUARE_BRACKETS: 'Square brackets have to be followed either by "." or by "[" or by "*".',
        UNCLOSED_BRACKETS: 'Unclosed brackets at the end of the path',
        CLOSING_BRACKET_WITHOUT_OPENING: 'Encountered closing bracket without corresponding opening one',
        INVALID_ESCAPE_SEQUENCE: 'Encountered an invalid escape sequence',
        UNCLOSED_QUOTATION_MARKS: 'Encountered unclosed quotation marks',
        CANT_INSERT_NON_NAMED_PROPERTIES: 'Set can only contain named properties',
        CANT_BIND: 'Cannot bind. Data appear to not exist.',
        INVALID_CONTAINER: 'Backing store must be DataArray, Array, ArrayBuffer or Object type.',
        CANT_PUSH_TO_SINGLE: 'Single value property, cannot push!',
        ADDED_CHILD_WITHOUT_ID: 'Added child without id.',
        UNKNOWN_CONTEXT: 'Unknown context: ',
        NO_PATHS_FOR_NON_PRIMITIVE_ARRAYS: 'Path resolution is only supported for non primitive type arrays.',
        INVALID_NON_NUMERIC_SEGMENT_IN_PATH: 'Accessed an array via an non numeric index.',
        GET_PATH_SEGMENT_CALLED_FOR_NON_ENTRY: '_getPathSegmentForChildNode has been called for an entry that is not ' + 'an entry of the collection',
        NO_NORMALIZED_CHANGESET: 'deserialize was called with a non-normalized ChangeSet.',
        MISSING_GUID_IN_NORMALIZED_CHANGESET: 'Missing GUID in a normalized ChangeSet with named properties',
        UNKNOWN_ENUM: 'enum value unknown: ',
        UNKNOWN_REPOSITORY: 'Unknown repository for branch: ',
        MISSING_BRANCH: 'Missing branch. Cannot commit.',
        FUNCTION_DEPRECATED: ' function has been deprecated. ',
        NOT_SPECIFIED: 'Function not specified.',
        NOT_IMPLEMENTED: 'Function not implemented.',
        NOT_CONNECTED: 'Not connected to the backend.',
        CANNOT_FETCH_COMMIT: 'Cannot fetch commit. ',
        NO_ROOT_IN_MODIFY: 'Cannot call modify on a repository that has no checked out root, yet.',
        NO_REPOSITORY_CHECKED_OUT: 'No repository has been checked out yet.',
        UNKNOWN_OPERATION: 'Unknown operation.',
        ALREADY_CONNECTED: 'Already connected',
        CONNECTING: 'In the process of connecting',
        FAILED_TO_PERSIST: 'Failed to persist local branch. Server error: ',
        NO_PARENT_COMMIT: 'Parent commit doesn\'t exist',
        TEMPLATE_MISMATCH: 'Template structures do not match for typeid = ',
        TEMPLATE_DOES_NOT_EXIST: 'Template does not exist typeid = ',
        UNVERSIONED_TEMPLATE: 'Templates MUST be versioned.',
        UNVERSIONED_REMOTE_TEMPLATE: 'FATAL - Remote template is not verisoned.',
        INVALID_PATH_IN_REFERENCE: 'References may only contain absolute repository references ' + 'or empty strings',
        DETACHED_HEAD: 'Workspace is on a detached head',
        PROPERTY_NOT_PART_OF_CHECKOUT_VIEW: 'The repository reference property is not part of a CheckOutView and ' + 'therefore, it is not possible to access the referenced repository.',
        REFERENCED_REPOSITORY_NOT_AVAILABLE: 'Repository referenced by a RepositoryReferenceProperty is not yet ' + 'available.',
        SET_PARENT_REPO_INVALID: '_setParentRepository called with non exiting checked out repository ',
        DUPLICATED_COMMIT_UPDATE: 'INTERNAL ERROR: Tried to register a checkout view for a commit GUID ' + 'that already exists. This shouldn\'t happen. The caller of ' + '_updateRepositoryInfo should have taken care of this',
        SETTING_REPOSITORY_REF_PROPERTY: 'The ref member of a repository reference property cannot be directly set.',
        NON_EXISTING_PROPERTY_REPOSITORY_REFERENCE: '[RepositoryReferenceProperty] could not find property: ',
        CHILD_WITHOUT_ID_ADDED: 'A child without an ID cannot be added to a NodeProperty',
        MODIFICATION_OF_REFERENCED_PROPERTY: 'Modifications of referenced repositories are not allowed.',
        REBASE_NEEDED: 'Rebase is needed',
        REMOTE_BRANCH_NOT_TRACKED: 'Remote branch is not tracked',
        FAILED_PUSH: 'Failed to push local commits. Server error: ',
        INSERTING_INTO_MODIFY_CHANGESET: 'Tried to add an insert to a ChangeSet that already contains a modify',
        SERIALIZING_CYCLIC_PROPERTY_TREE: 'Serialization of a PropertyTree with cyclic repository references is ' + 'not possible',
        SERVER_URL_NOT_STRING: 'ServerURL parameter must be a string',
        GETBEARERTOKEN_NOT_FUNCTION: 'getBearerToken parameter must be a function',
        HEADERS_NOT_OBJECT: 'headers parameter must be a object',
        NOT_JOINED: 'Branch not joined'
      };
      var WORKSPACE_STATE = {
        // No changes have been made.
        MERGING: 'merging',
        // There are pending changes.
        PENDING_CHANGES: 'pending_changes',
        // Pending changes conflict.
        CONFLICTED: 'conflicted',
        // In rebase mode.
        REBASING: 'rebasing'
      };
      exportModule('LYNX.Property._constants.WORKSPACE_STATE', WORKSPACE_STATE);
      // Error messages for the property sets library
      exportModule('LYNX.Property._constants.MSG', MSG);
      // The separation character used to separate between properties in the path spec
      exportModule('LYNX.Property._constants.PROPERTY_PATH_DELIMITER', '.');
    }());
    (function () {
      var EventEmitter = include('LYNX.Utils.Events.EventEmitter');
      var RequestUtils = include('LYNX.Utils.RequestUtils');
      var CollaborationMessage = include('LYNX.CollaborationServerAPI.CollaborationMessage');
      var _ = include('underscore');
      var CHANNELS = include('LYNX.CollaborationServerAPI.CONSTANTS').CHANNELS;
      var COLLABORATOR_TYPES = include('LYNX.CollaborationServerAPI.CONSTANTS').COLLABORATOR_TYPES;
      var SocketListener = include('LYNX.CollaborationClient.SocketListener');
      var CollaboratorManager = include('LYNX.CollaborationClient.CollaboratorManager');
      var Room = include('LYNX.CollaborationClient.Room');
      var UrlService = include('LYNX.CollaborationClient.Services.UrlService');
      var generateGUID = include('LYNX.Utils.generateGUID');
      var ModuleLogger = include('LYNX.Logger.ModuleLogger');
      var async = include('LYNX.Utils.ThirdParty.async');
      var MSG = include('LYNX.Property._constants.MSG');
      var logger = ModuleLogger.getLogger('LYNX.CollaborationClient.Clients.BaseCollaborator');
      var promiseRequest = function (requestParams) {
        return new Promise(function (resolve, reject) {
          RequestUtils.requestWithRetries({
            requestParams: requestParams,
            logMessage: '[BaseCollaborator] promiseRequest: error requesting ' + requestParams.url + ' : '
          }, function (error, response) {
            if (error) {
              reject(error);
            } else {
              resolve(response);
            }
          });
        });
      };
      var logError = function (error) {
        if (error) {
          if (error.stack) {
            logger.error(error.stack);
          } else {
            logger.error(error);
          }
        }
      };
      var _parseError = function (in_data) {
        var error;
        if (in_data && in_data.message) {
          error = new Error(in_data.message);
        } else if (in_data) {
          error = new Error('Invalid error: ' + JSON.stringify(in_data));
        }
        return error;
      };
      var STATE = {
        DISCONNECTED: 0,
        AUTHENTICATING: 1,
        CONNECTING: 2,
        CONNECTED: 3,
        SETUP: 4
      };
      var REVERSESTATE = _.invert(STATE);
      /**
       * Class abstracting away the socket.io specifics from the end user
       * @param {object|undefined} in_params List of parameters
       * @param {string} [in_params.id] - A unique ID that the client should identify itself as
       * @param {string} [in_params.friendlyName] - A nice name that might appear in a UI
       * @param {string=} [in_params.type=human] The type of collaborator: human, robot or android
       * @param {string=} [in_params.serverUrl=http://127.0.0.1:3000] The url pointing to the collaboration server.
       *  This is used as the base url to call REST API endpoints.
       * @param {getBearerTokenFn=} [in_params.getBearerToken] Function that accepts a callback
       *  function that should be called with an error or the OAuth2 bearer token representing the user.
       * @param {object|undefined} [in_params.headers] Headers to supply when making requests (used by tests)
       * @constructor
       * @private
       * @alias LYNX.Collaboration.Clients.BaseCollaborator
       */
      var BaseCollaborator = function (in_params) {
        var params = in_params || {};
        if (params.type) {
          console.assert(_.invert(COLLABORATOR_TYPES)[params.type], 'Unknown collaborator type ' + params.type);
        }
        this._type = params.type || COLLABORATOR_TYPES.HUMAN;
        EventEmitter.call(this);
        this._serverUrl = params.serverUrl || 'http://127.0.0.1:3000';
        this._getBearerToken = params.getBearerToken;
        this._headers = params.headers || {};
        this._openSocket = undefined;
        this._socketURL = this._serverUrl;
        this._state = STATE.DISCONNECTED;
        this._urlService = new UrlService({
          serverUrl: this._serverUrl,
          headers: this._headers,
          getBearerToken: this._getBearerToken
        });
        /**
         * @type {CollaboratorManager}
         */
        this._collaboratorManager = new CollaboratorManager();
        this.guid = generateGUID();
        this.id = params.id;
        this.friendlyName = params.friendlyName;
      };
      BaseCollaborator.prototype = Object.create(EventEmitter.prototype);
      BaseCollaborator.prototype.isConnecting = function () {
        return this._state === STATE.AUTHENTICATING || this._state === STATE.CONNECTING;
      };
      BaseCollaborator.prototype.isConnected = function () {
        return this._state >= STATE.CONNECTED;
      };
      /**
       * Called to give the Microservice a chance to prepare, setup, validate any preconditions.
       * At this point the MS receives no data, and is not visible as a collaborator in the scene.
       * Here we're given the room to connect to and we setup the websocket
       * @param {object} in_params
       * @param {object=} in_params.socketOptions
       * @param {object|undefined} [in_params.serverUrl] Override for server url to the collaboration server.
       * @param {object|undefined} [in_params.headers] Headers to supply when making requests (used by tests)
       * @param {function} in_callback
       */
      BaseCollaborator.prototype.connect = function (in_params, in_callback) {
        if (this.isConnected()) {
          in_callback(new Error(MSG.ALREADY_CONNECTED));
          return;
        }
        if (this.isConnecting()) {
          in_callback(new Error(MSG.CONNECTING));
          return;
        }
        this._state = STATE.AUTHENTICATING;
        var params = in_params || {};
        // Warn if the client provides in_params.socketURL
        if (params.socketURL) {
          logger.debug('connect() no longer supports specifying in_params.socketURL. ' + 'Use in_params.serverUrl during ctor instead');
        }
        // If user provides in_params.serverUrl, use it
        if (params.serverUrl) {
          this._serverUrl = params.serverUrl;
          this._urlService.resetServerUrl(this._serverUrl);
        }
        var socketOptions = params.socketOptions || {};
        _.defaults(socketOptions, {
          forceNew: true,
          timeout: 1500
        });
        var that = this;
        // Call the /authorize rest endpoint to get the web socket URL and JWT
        that._urlService.getAuthenticateParameters(params.headers, function (error, requestParams) {
          if (error) {
            in_callback(error);
          } else {
            logger.debug('request GET ' + requestParams.url);
            var callback = in_callback || logError;
            // Retry /authorize request up to 5 times with exponential backoff
            RequestUtils.requestWithRetries({
              requestParams: requestParams,
              logMessage: '[BaseCollaborator] error requesting ' + requestParams.url + ' : '
            }, function (err, body) {
              if (err) {
                callback(err);
              } else {
                that._socketURL = body.socketURL;
                // Add JWT to query parameters and socketPath
                var options = _.extend({
                  query: 'token=' + body.token,
                  path: body.socketPath
                }, socketOptions);
                that._openSocket = that._createWebSocket(that._socketURL, options);
                that._socketListener = new SocketListener(that._openSocket);
                that._state = STATE.CONNECTING;
                // create a websocket for this child
                that._connect(callback);
              }
            });
          }
        });
      };
      /**
       * Get the serialized data of the collaborator
       * @return {object} Serialized object
       */
      BaseCollaborator.prototype.serialize = function () {
        return {
          type: this._type,
          guid: this.guid,
          id: this.id,
          friendlyName: this.friendlyName
        };
      };
      /**
       * Check if the client is setup
       * @return {boolean} Returns true if the client is at a setup state or higher
       */
      BaseCollaborator.prototype.isSetup = function () {
        return this._state >= STATE.SETUP;
      };
      /**
       * Return the human readable state
       * @return {string} The human readable string corresponding to the state
       */
      BaseCollaborator.prototype.getReadableState = function () {
        return REVERSESTATE[this._state];
      };
      /**
       * Create a web socket connection
       * @param {string} in_socketURL
       * @param {object} in_socketOptions
       * @return {object} A Socket.io client object
       */
      BaseCollaborator.prototype._createWebSocket = function (in_socketURL, in_socketOptions) {
        throw new Error('Collaborator must implement this function');
      };
      /**
       * Connect to the backend
       * @param {function} in_callback Called back after joining the room
       */
      BaseCollaborator.prototype._connect = function (in_callback) {
        console.assert(this._openSocket, 'Not connected');
        this._collaboratorManager.setSocket(this._openSocket);
        var that = this;
        this.once('init', function (setupResult) {
          in_callback(null, setupResult);
        });
        this._openSocket.on('error', function (error) {
          logger.error('error');
          if (error.stack) {
            logger.error(error.stack);
          }
          that.trigger('error', that, error);
        });
        var initialized = false;
        this._openSocket.on('connect', function () {
          logger.debug('connected: ' + that._socketURL);
          that._state = STATE.CONNECTED;
          that.trigger('connected', that);
          async.whilst(function () {
            return that._state === STATE.CONNECTED;
          }, function (asyncCallback) {
            that._setup(function (error, setupResult) {
              if (error) {
                logger.error('Failed at SETUP step error:');
                logger.error(error.stack);
              }
              // We continously retry on an error so we don't need to pass it in the callback
              asyncCallback(null, setupResult);
            });
          }, function (error, setupResult) {
            if (!initialized) {
              that.trigger('init', that, setupResult);
              initialized = true;
            } else {
              that._joinAll();
            }
          });
        });
        this._openSocket.on('disconnect', function (reason) {
          logger.debug('disconnect');
          that._state = STATE.DISCONNECTED;
          that.trigger('disconnected', that, reason);
        });
        // Fired upon a connection error.
        this._openSocket.on('connect_error', function (error) {
          logger.error('Trouble connecting: ' + that._socketURL);
          if (error.stack) {
            logger.error(error.stack);
          }
          that.trigger('connect_error', that, error);
        });
        // Fired upon a connection timeout.
        this._openSocket.on('connect_timeout', function () {
          logger.debug('connect_timeout');
          that.trigger('connect_timeout', that);
        });
        // Fired upon an attempt to reconnect.
        this._openSocket.on('reconnecting', function (attemptNumber) {
          that._state = STATE.CONNECTING;
          logger.debug('reconnecting');
        });
        // Fired upon a successful reconnection.
        this._openSocket.on('reconnect', function (attemptNumber) {
          logger.debug('reconnect');
          that.trigger('reconnect', that, attemptNumber);
        });
        // Fired upon a reconnection attempt error.
        this._openSocket.on('reconnect_error', function (error) {
          logger.debug('reconnect_error');
        });
        // Fired upon an attempt to reconnect.
        this._openSocket.on('reconnect_attempt', function (attemptNumber) {
          logger.debug('reconnect_attempt', attemptNumber);
        });
        // Fired when couldn’t reconnect within reconnectionAttempts
        this._openSocket.on('reconnect_failed', function () {
          logger.debug('reconnect_failed: ', that._socketURL);
        });
      };
      /**
       * @param {Function} callback
       */
      BaseCollaborator.prototype._setup = function (callback) {
        console.assert(this._openSocket, 'Not connected');
        if (this._state >= STATE.CONNECTED) {
          var collaborationMessage = new CollaborationMessage({ data: this.serialize() });
          var that = this;
          this._openSocket.emit(CHANNELS.SETUP, collaborationMessage, function (error, response) {
            if (!error) {
              that._state = STATE.SETUP;
            }
            callback(_parseError(error), response);
          });
        } else {
          callback(new Error('Must first connect. State: ' + REVERSESTATE[this._state]));
        }
      };
      /**
       * Called when the microservice is no longer needed.
       * At this point the send api call is still available.
       * @param {function=} callback
       */
      BaseCollaborator.prototype.close = function (callback) {
        logger.debug('Closing.');
        this._collaboratorManager.reset();
        if (this.isConnected()) {
          this.disconnect();
        }
        if (callback) {
          callback();
        }
      };
      /**
       * Disconnect from the backend
       */
      BaseCollaborator.prototype.disconnect = function () {
        if (_.isUndefined(this._openSocket)) {
          throw new Error('Already disconnected');
        }
        this._openSocket.disconnect();
        delete this._openSocket;
      };
      /**
       * Broadcast an event to all subscribers of the specified room, excluding the
       * client that triggers the event
       * @param {string} in_room
       * @param {string} in_event Event handle to emit
       * @param {object} in_data Data to be sent over of the wire
       * @param {function(Error=)} in_callback The function to call when the operation is done.
       */
      BaseCollaborator.prototype.broadcast = function (in_room, in_event, in_data, in_callback) {
        console.assert(_.isString(in_room), 'in_room must be specified');
        console.assert(this._openSocket, 'Not connected');
        var callback = in_callback || logError;
        if (this.hasJoined(in_room)) {
          var collaborationMessage = new CollaborationMessage({
            room: in_room,
            event: in_event,
            data: in_data
          });
          this._openSocket.emit(CHANNELS.BROADCAST, collaborationMessage, function (error, response) {
            callback(_parseError(error), response);
          });
        } else {
          callback(new Error('Must first join channel. State: ' + REVERSESTATE[this._state]));
        }
      };
      /**
       * A message not intended for any participant. The message is only handled by the CollaborationServer
       * @param {string} in_room
       * @param {string} in_event
       * @param {*=} in_data
       * @param {function} in_callback
       */
      BaseCollaborator.prototype.messageCollaborationServer = function (in_room, in_event, in_data, in_callback) {
        console.assert(_.isString(in_room), 'in_room must be specified');
        console.assert(this._openSocket, 'Not connected');
        var callback = in_callback || logError;
        if (this.hasJoined(in_room)) {
          var collaborationMessage = new CollaborationMessage({
            room: in_room,
            event: in_event,
            data: in_data
          });
          this._openSocket.emit(CHANNELS.COLLABORATION_SERVER, collaborationMessage, function (error, response) {
            callback(_parseError(error), response);
          });
        } else {
          callback(new Error('Must first join room. State: ' + REVERSESTATE[this._state]));
        }
      };
      /**
       * Emit an event to all subscribers of the specified room including the
       * client that triggers the event.
       * @param {string} in_room
       * @param {string} in_event Event handle to emit
       * @param {object} in_data Data to be sent over of the wire
       * @param {function(Error=)} in_callback The function to call when the operation is done.
       */
      BaseCollaborator.prototype.emit = function (in_room, in_event, in_data, in_callback) {
        console.assert(_.isString(in_room), 'in_room must be specified');
        console.assert(this._openSocket, 'Not connected');
        var callback = in_callback || logError;
        if (this.hasJoined(in_room)) {
          var collaborationMessage = new CollaborationMessage({
            room: in_room,
            event: in_event,
            data: in_data
          });
          // logger.debug('emit: ', collaborationMessage);
          this._openSocket.emit(CHANNELS.EMIT, collaborationMessage, function (error, response) {
            callback(_parseError(error), response);
          });
        } else {
          callback(new Error('Must first join room. State: ' + REVERSESTATE[this._state]));
        }
      };
      /**
       * Join a specific room
       * @param {string} in_roomName
       * @param {object=} in_options
       * @param {function(Error=)=} in_callback The function to call when the operation is done.
       */
      BaseCollaborator.prototype.join = function (in_roomName, in_options, in_callback) {
        console.assert(_.isString(in_roomName), 'in_roomName must be specified');
        console.assert(this._openSocket, 'Not connected: ' + this._socketURL);
        logger.debug('Joining ', in_roomName);
        var callback = in_callback || logError;
        if (this._state >= STATE.SETUP) {
          var that = this;
          var room = new Room(in_roomName, in_options);
          that._collaboratorManager.addRoom(room);
          var collaborationMessage = new CollaborationMessage({
            room: in_roomName,
            data: in_options
          });
          this._openSocket.emit(CHANNELS.JOIN, collaborationMessage, function (error, response) {
            if (!error) {
              _.each(response.room.collaborators, function (collaborator) {
                room.addCollaborator(collaborator);
              });
            } else {
              that._collaboratorManager.leaveRoom(room.name);
            }
            callback(_parseError(error), response);
          });
        } else {
          callback(new Error('Must first setup. State: ' + REVERSESTATE[this._state]));
        }
      };
      /**
       * Re-join all of the rooms that we were in before being disconnected
       */
      BaseCollaborator.prototype._joinAll = function () {
        var that = this;
        _.each(this._collaboratorManager.getRooms(), function (room) {
          that.join(room.name, room.options);
        });
      };
      /**
       * @return {Array<LYNX.CollaborationClient.Room>} The rooms that we're connected to
       */
      BaseCollaborator.prototype.getRooms = function () {
        return this._collaboratorManager.getRooms();
      };
      /**
       * @param  {string}  room
       * @return {Boolean}
       */
      BaseCollaborator.prototype.hasJoined = function (roomName) {
        return this._collaboratorManager.hasJoined(roomName);
      };
      /**
       * Get the occupancy of a given room
       * @param {string} in_roomName The room to check the occupancy
       * @param {string|undefined} in_collaboratorType The type of collaborator to check against. If this is undefined
       *  then the result will reflect against all collaborators in the room
       * @param {function(Error=,object=)} in_callback Callback invoked with an error or an object containing the
       * occupancy i.e.
       * <pre>
       *  {occupancy: 5}
       * </pre>
       */
      BaseCollaborator.prototype.getRoomOccupancy = function (in_roomName, in_collaboratorType, in_callback) {
        var params = {};
        if (in_collaboratorType) {
          params.collaboratorType = in_collaboratorType;
        }
        this._urlService.getRoomOccupancyParameters(in_roomName, params, function (error, requestParams) {
          if (error) {
            in_callback(error);
          } else {
            RequestUtils.requestWithRetries({
              requestParams: requestParams,
              logMessage: '[BaseCollaborator] getRoomOccupancy: error requesting ' + requestParams.url + ' : '
            }, in_callback);
          }
        });
      };
      /**
       * Get the state of a Room
       * This function uses Promises AND callbacks until all public functions of this class support promises
       * @param {string} in_roomName
       * @param {object=} [in_params]
       * @param {function(Error=,object=)=} [in_callback] Callback invoked with an error or an object containing the Room
       * @return {Promise}
       */
      BaseCollaborator.prototype.getRoom = function (in_roomName, in_params, in_callback) {
        var promise = this._urlService.getRoomParameters(in_roomName, in_params).then(function (requestParams) {
          return promiseRequest(requestParams);
        });
        if (in_callback) {
          promise.then(function (response) {
            in_callback(null, promise);
          }).catch(in_callback);
        }
        return promise;
      };
      /**
       * leave a specific room
       * @param {string} in_roomName
       * @param {string} in_options
       * @param {function(Error=)} in_callback The function to call when the operation is done.
       */
      BaseCollaborator.prototype.leave = function (in_roomName, in_options, in_callback) {
        console.assert(_.isString(in_roomName), 'in_roomName must be specified');
        console.assert(this._openSocket, 'Not connected');
        logger.debug('Leaving ', in_roomName);
        var callback = in_callback || logError;
        var that = this;
        if (this.hasJoined(in_roomName)) {
          var collaborationMessage = new CollaborationMessage({ room: in_roomName });
          this._openSocket.emit(CHANNELS.LEAVE, collaborationMessage, function (error, response) {
            if (!error) {
              that._collaboratorManager.leaveRoom(in_roomName);
            }
            callback(_parseError(error), response);
          });
        } else {
          callback(new Error('Must first join room. State: ' + REVERSESTATE[this._state]));
        }
      };
      /**
       * If for some reason wants to listen to the socket directly
       * Not advisable, may be deprecated some day
       * @return {SocketListener}
       */
      BaseCollaborator.prototype.getSocket = function () {
        console.assert(this._openSocket, 'Not connected');
        return this._socketListener;
      };
      /**
       * Register to an event
       * Events are emitted within a specified room via the broadcast() API
       * @param {string} in_event Event handle to listen on
       * @param {function(Object)} in_callback Callback to be invoked when in_event is triggered
       * @deprecated
       */
      BaseCollaborator.prototype.registerToEvent = function (in_event, in_callback) {
        console.assert(this._openSocket, 'Not connected');
        console.assert(_.isFunction(in_callback));
        logger.debug('Please attach listeners to socket directly');
        this._socketListener.on(in_event, in_callback);
      };
      /**
       * If the Microservice implements receive, receive will be called when there are events on
       * the collaborator bus.
       * Events are the standard PSet events seen here <TODO: link to the pset documentation>
       *
       * @param {function(LYNX.CollaborationServerAPI.CollaborationMessage)} in_callback Callback function invoked with
       * the following object parameter format:
       * {
       *   room: {string} The room associated with the branchGuid
       *   event: {string} The branch update event handle
       *   data: {object} The data passed in to the Client.commit() API
       * }
       */
      BaseCollaborator.prototype.receive = function (in_callback) {
        logger.debug('Collaborator must implement this function');
      };
      /**
       * Unregister to an event
       * @param {string} in_event Event handle to stop listening on
       * @param {function(Object)} in_callback Pointer to the callback that was registered via registerToEvent() API
       * @deprecated
       */
      BaseCollaborator.prototype.unregisterFromEvent = function (in_event, in_callback) {
        console.assert(this._openSocket, 'Not connected');
        console.assert(_.isFunction(in_callback));
        logger.debug('Please remove listeners to socket directly');
        this._socketListener.off(in_event, in_callback);
      };
      BaseCollaborator.prototype.getCollaborator = function (roomName, guid) {
        return this._collaboratorManager.getCollaborator(roomName, guid);
      };
      BaseCollaborator.prototype.getCollaborators = function (roomName) {
        return this._collaboratorManager.getCollaborators(roomName);
      };
      exportModule('LYNX.CollaborationClient.Clients.BaseCollaborator', BaseCollaborator);
    }());
    (function () {
      var BaseCollaborator = include('LYNX.CollaborationClient.Clients.BaseCollaborator');
      var ModuleLogger = include('LYNX.Logger.ModuleLogger');
      var logger = ModuleLogger.getLogger('LYNX.CollaborationClient.Clients.BrowserCollaborator');
      /**
       * @param {object} in_params
       * @constructor
       * @extends {LYNX.Collaboration.Clients.BaseCollaborator}
       */
      var BrowserCollaborator = function (in_params) {
        BaseCollaborator.call(this, in_params);
      };
      BrowserCollaborator.prototype = Object.create(BaseCollaborator.prototype);
      /**
       * @param {string} in_socketURL
       * @param {object} in_socketOptions
       * @return {object} A Socket.io client object
       */
      BrowserCollaborator.prototype._createWebSocket = function (in_socketURL, in_socketOptions) {
        logger.info('Connecting to ' + in_socketURL);
        return window.io(in_socketURL, in_socketOptions);
      };
      exportModule('LYNX.CollaborationClient.Clients.BrowserCollaborator', BrowserCollaborator);
    }());
    (function () {
      /**
       * Chronometer used to keep track of the start and end time.
       * @constructor
       */
      var Chronometer = function () {
        this._startTime = undefined;
        this._endTime = undefined;
      };
      /**
       * Start the chronometer
       */
      Chronometer.prototype.start = function () {
        this._endTime = undefined;
        this._startTime = new Date().getTime();
      };
      /**
       * Stop the chronometer
       */
      Chronometer.prototype.stop = function () {
        this._endTime = new Date().getTime();
      };
      /**
       * Get the elapsed time in milliseconds.
       * At this point, the chronomeneter must have been started and stopped
       * @return {number} Time elapsed in milliseconds
       */
      Chronometer.prototype.elapsedMS = function () {
        console.assert(this._startTime !== undefined, 'Chronometer not started');
        console.assert(this._endTime !== undefined, 'Chronometer not stopped');
        return this._endTime - this._startTime;
      };
      /**
       * Get the elapsed time in seconds.
       * At this point, the chronomeneter must have been started and stopped
       * @return {number} Time elapsed in seconds
       */
      Chronometer.prototype.elapsedSec = function () {
        return this.elapsedMS() / 1000;
      };
      exportModule('LYNX.Logger.Lib.Chronometer', Chronometer);
    }());
    (function () {
      var chalk = include('LYNX.Logger.ThirdParty.chalk');
      var ModuleLogger = include('LYNX.Logger.ModuleLogger');
      var LEVELS = ModuleLogger.levels;
      var Chronometer = include('LYNX.Logger.Lib.Chronometer');
      /**
       * A simple class for debug and console output
       * @param {object=} [in_params] Construction parameters
       * @param {boolean=} [in_params.muteLogs=false] mute the logs
       * @param {string=} [in_params.logLevel='INFO'] The log level.
       * @param {string=} [in_params.moduleName=''] name to print before output
       */
      var ConsolePrinter = function (in_params) {
        var params = in_params || {};
        this.setModuleName(params.moduleName ? params.moduleName : '');
        this.setLogLevel(LEVELS[params.logLevel] || LEVELS.INFO);
        this._muteLogs = false;
        if (params.muteLogs) {
          this.muteLogs();
        }
        Object.defineProperty(this, 'logger', {
          /**
           * @return {log4js.Logger} The log4js logger that the console printer instance is using to
           *   output logs.
           */
          get: function () {
            return this._logger;
          }
        });
        this._chronometers = {};
      };
      /**
       * Tests whether or not a log entry will be produced at the given level. Useful to call when
       * producing the log entry itself is an expensive operation.
       * @param {string|log4js.Level} in_level A log level to test.
       * @return {boolean} true if a log entry would be output at the given level, false otherwise.
       */
      ConsolePrinter.prototype.isLevelEnabled = function (in_level) {
        return this._logger.isLevelEnabled(in_level);
      };
      ConsolePrinter.prototype.setModuleName = function (moduleName) {
        this._logger = ModuleLogger.getLogger(moduleName);
        if (!moduleName) {
          // Mimic the ConsolePrinter behaviour where a space is printed if no category is provided.
          this._logger.category = '';
        }
      };
      ConsolePrinter.prototype.setLogLevel = function (logLevel) {
        console.assert(logLevel instanceof LEVELS.Level || LEVELS[logLevel], 'Invalid log level');
        this._logger.setLevel(logLevel);
      };
      ConsolePrinter.prototype.muteLogs = function () {
        // Emulate old this._muteLogs behaviour by setting the log level to ERROR. (Muted logs weren't
        // muted for errors.)
        if (!this._muteLogs) {
          this._logLevelBeforeMute = this._logger.level;
          this._logger.setLevel(LEVELS.ERROR);
          this._muteLogs = true;
        }
      };
      ConsolePrinter.prototype.unmuteLogs = function () {
        if (this._muteLogs) {
          this._logger.setLevel(this._logLevelBeforeMute);
          delete this._logLevelBeforeMute;
          this._muteLogs = false;
        }
      };
      /**
       * Pretty print helpers
       * @param {string} in_str a string
       * @return {string} a string with colors
       */
      var handleTermColors = function (in_str) {
        if (chalk.supportsColor) {
          return in_str;
        } else {
          return chalk.stripColor(in_str);
        }
      };
      ConsolePrinter.prototype._log = function (in_messages, logLevel, color) {
        var messages = in_messages.map(function (in_message) {
          var message = in_message;
          if (message instanceof Error) {
            message = message.stack;
          }
          if (typeof message === 'object' && message !== null) {
            try {
              message = JSON.stringify(message);
            } catch (error) {
              return message;
            }
          }
          if (color) {
            var levelStyle = ModuleLogger.color.styles[logLevel.levelStr];
            return handleTermColors(chalk[color](message)) + levelStyle.open;
          } else {
            return message;
          }
        });
        messages.unshift(logLevel);
        this._logger.log.apply(this._logger, messages);
      };
      ConsolePrinter.prototype.printTrace = function () {
        var level = LEVELS.TRACE;
        if (!this._logger.isLevelEnabled(level)) {
          return;
        }
        var messages = arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments);
        this._log(messages, level);
      };
      ConsolePrinter.prototype.trace = ConsolePrinter.prototype.printTrace;
      /**
       * Print debug information
       * @param {string} in_str the string to print
       */
      ConsolePrinter.prototype.printDebug = function () {
        var level = LEVELS.DEBUG;
        if (!this._logger.isLevelEnabled(level)) {
          return;
        }
        var messages = arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments);
        this._log(messages, level);
      };
      ConsolePrinter.prototype.debug = ConsolePrinter.prototype.printDebug;
      ConsolePrinter.prototype.verbose = ConsolePrinter.prototype.printDebug;
      ConsolePrinter.prototype.printInfo = function () {
        var level = LEVELS.INFO;
        if (!this._logger.isLevelEnabled(level)) {
          return;
        }
        var messages = arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments);
        this._log(messages, level);
      };
      ConsolePrinter.prototype.info = ConsolePrinter.prototype.printInfo;
      ConsolePrinter.prototype.log = ConsolePrinter.prototype.printInfo;
      ConsolePrinter.prototype.printInfoDim = function () {
        var level = LEVELS.INFO;
        if (!this._logger.isLevelEnabled(level)) {
          return;
        }
        var messages = arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments);
        this._log(messages, level, 'dim');
      };
      ConsolePrinter.prototype.infoDim = ConsolePrinter.prototype.printInfoDim;
      ConsolePrinter.prototype.printTitle = function () {
        var level = LEVELS.INFO;
        if (!this._logger.isLevelEnabled(level)) {
          return;
        }
        var messages = arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments);
        this._log(messages, level, 'blue');
      };
      ConsolePrinter.prototype.title = ConsolePrinter.prototype.printTitle;
      ConsolePrinter.prototype.printWarning = function () {
        var level = LEVELS.WARN;
        if (!this._logger.isLevelEnabled(level)) {
          return;
        }
        var messages = arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments);
        this._log(messages, level);
      };
      ConsolePrinter.prototype.warning = ConsolePrinter.prototype.printWarning;
      ConsolePrinter.prototype.warn = ConsolePrinter.prototype.printWarning;
      ConsolePrinter.prototype.printError = function () {
        var level = LEVELS.ERROR;
        if (!this._logger.isLevelEnabled(level)) {
          return;
        }
        var messages = arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments);
        this._log(messages, level);
      };
      ConsolePrinter.prototype.printSevere = ConsolePrinter.prototype.printError;
      ConsolePrinter.prototype.severe = ConsolePrinter.prototype.printError;
      ConsolePrinter.prototype.error = ConsolePrinter.prototype.printError;
      ConsolePrinter.prototype.printFatal = function () {
        var level = LEVELS.FATAL;
        if (!this._logger.isLevelEnabled(level)) {
          return;
        }
        var messages = arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments);
        this._log(messages, level);
      };
      ConsolePrinter.prototype.fatal = ConsolePrinter.prototype.printFatal;
      /**
       * Start a chronometer for the given key
       * @param {string} in_key Key to start a chronometer on
       */
      ConsolePrinter.prototype.startChronometer = function (in_key) {
        if (this._logger.isLevelEnabled(ModuleLogger.levels.TRACE)) {
          console.assert(!this._chronometers[in_key], 'Chronometer already started for the given key = ' + in_key);
          this._chronometers[in_key] = new Chronometer();
          this._chronometers[in_key].start();
        }
      };
      /**
       * Stop a chronometer for the given key.
       * startChronomenter must be called prior to invoking this method.
       * Calling this routine will log a "trace" with the elapsed time in ms
       * @param {string} in_key Key to stop the chronometer for
       */
      ConsolePrinter.prototype.stopChronometer = function (in_key) {
        if (this._logger.isLevelEnabled(ModuleLogger.levels.TRACE)) {
          this._chronometers[in_key].stop();
          this.trace('stopChronometer for key = ' + in_key);
          this.trace('  ELAPSED ' + this._chronometers[in_key].elapsedMS() + ' ms');
          delete this._chronometers[in_key];
        }
      };
      exportModule('LYNX.Logger.ConsolePrinter', ConsolePrinter);
    }());
    (function () {
      var RequestUtils = include('LYNX.Utils.RequestUtils');
      var _ = include('underscore');
      var UrlService = include('LYNX.CollaborationClient.Services.UrlService');
      /**
       * Class providing easy REST endpoint access to server
       * @param {string} serverUrl The url pointing to the collaboration server.
       * @param {getBearerTokenFn=} getBearerToken Function that accepts a callback
       *   function that returns an error or the OAuth2 bearer token representing the user.
       * @param {object|undefined} headers Headers to supply when making requests (used by tests)
       * @constructor
       */
      var CollaborationRestClient = function (serverUrl, getBearerToken, headers) {
        this._urlService = new UrlService({
          serverUrl: serverUrl,
          getBearerToken: getBearerToken,
          headers: headers
        });
      };
      /**
       * Get all rooms
       * @param  {object} queryString A set of query parameters
       * @return {Promise} A Promise that resolves with the result or an error
       */
      CollaborationRestClient.prototype.getRooms = function (queryString) {
        var that = this;
        return new Promise(function (resolve, reject) {
          that._urlService.getRoomsParams(queryString, function (err, roomParams) {
            if (err) {
              reject(err);
            } else {
              RequestUtils.requestWithRetries({
                requestParams: roomParams,
                logMessage: '[CollaborationRestClient] getRooms: error requesting ' + roomParams.url + ' : '
              }, function (error, response) {
                if (error) {
                  reject(error);
                } else {
                  resolve(response);
                }
              });
            }
          });
        });
      };
      /**
       * Get room occupancy
       * @param  {object} params List of parameters
       * @param  {string=} params.roomName The room name to query
       * @param  {string} params.collaboratorType Type of collaborator to query
       * @return {Promise} A Promise that resolves with the result or an error
       */
      CollaborationRestClient.prototype.getRoomOccupancy = function (params) {
        var that = this;
        console.assert(_.isString(params.roomName), 'roomName must be specified');
        var queryParams = {};
        if (params.collaboratorType) {
          queryParams.collaboratorType = params.collaboratorType;
        }
        return new Promise(function (resolve, reject) {
          that._urlService.getRoomOccupancyParameters(params.roomName, queryParams, function (err, occupancyParams) {
            if (err) {
              reject(err);
            } else {
              RequestUtils.requestWithRetries({
                requestParams: occupancyParams,
                logMessage: '[CollaborationRestClient] getRoomOccupancy: error requesting ' + occupancyParams.url + ' : '
              }, function (error, response) {
                if (error) {
                  reject(error);
                } else {
                  resolve(response);
                }
              });
            }
          });
        });
      };
      exportModule('LYNX.CollaborationClient.CollaborationRestClient', CollaborationRestClient);
    }());
    (function () {
      var _ = include('underscore');
      /**
       * Service providing url related methods for the PropertySetsServer
       * @param {object} in_params List of parameters
       * @param {string} in_params.baseUrl The base url of the PropertySetServer
       * @param {getBearerTokenFn=} [in_params.getBearerToken] Function that accepts a callback
       *  function that should be called with an error or the OAuth2 bearer token representing the user.
       * @param {object|undefined} [in_params.headers] Headers to supply when making requests (used by tests)
       * @constructor
       */
      var UrlService = function (in_params) {
        this._baseUrl = in_params.baseUrl;
        this._getBearerToken = in_params.getBearerToken;
        this._headers = in_params.headers || {};
      };
      /**
       * Add bearer token header
       * @param {object} in_headers The headers to augment with a bearer token.
       * @param {function} in_callback invoked with error or resulting headers
       */
      UrlService.prototype.addBearerToken = function (in_headers, in_callback) {
        var that = this;
        if (!that._getBearerToken) {
          in_callback(undefined, in_headers);
        } else {
          // Invoke user-supplied callback to get the latest bearer token
          that._getBearerToken(function (error, bearerToken) {
            if (error) {
              console.log('[UrlService] Failed to retrieve bearer token: ' + error.message);
              in_callback(error);
            } else {
              in_callback(undefined, _.extend(in_headers, { Authorization: 'Bearer ' + bearerToken }));
            }
          });
        }
      };
      /**
       * Return commit REST endpoint parameters
       * @param {object} in_commit Commit declaration
       * @param {object=} [in_options] Additional options
       * @param {object=} [in_options.headers] Objects to be appended as headers in the request
       * @param {object=} [in_options.qs] Parameters appended to the query string
       * @param {function} in_callback invoked with error or request parameters
       */
      UrlService.prototype.getCommitParameters = function (in_commit, in_options, in_callback) {
        var that = this;
        in_options = in_options || {};
        // Create a new headers object where in_options.headers supersedes this.headers
        var headers = _.extend({}, this._headers, in_options.headers);
        that.addBearerToken(headers, function (error, headersWithBearerToken) {
          if (error) {
            in_callback(error);
          } else {
            var api = _.isUndefined(in_commit.commits) ? 'commit' : 'batch_commit';
            in_callback(undefined, {
              //  /branch/:branchGuid/commit
              url: that._baseUrl + '/branch/' + in_commit.branch.guid + '/' + api,
              method: 'POST',
              headers: headersWithBearerToken,
              body: in_commit,
              qs: in_options.qs || {},
              json: true
            });
          }
        });
      };
      /**
       * Return squashed commit history REST endpoint parameters
       * @param {string} in_branchGuid Commit declaration
       * @param {object=} [in_options] Additional options
       * @param {object=} [in_options.headers] Objects to be appended as headers in the request
       * @param {object=} [in_options.qs] Parameters appended to the query string
       * @param {object=} [in_options.commit] the current commit
       * @param {function} in_callback invoked with error or request parameters
       */
      UrlService.prototype.getSquashedCommitHistoryParameters = function (in_branchGuid, in_options, in_callback) {
        var that = this;
        in_options = in_options || {};
        // Create a new headers object where in_options.headers supersedes this.headers
        var headers = _.extend({}, this._headers, in_options.headers);
        that.addBearerToken(headers, function (error, headersWithBearerToken) {
          if (error) {
            in_callback(error);
          } else {
            in_callback(undefined, {
              //  /container
              url: that._baseUrl + '/branch/' + in_branchGuid + '/squashedCommitHistory',
              method: 'POST',
              headers: headersWithBearerToken,
              qs: in_options.qs || {},
              body: in_options,
              json: true
            });
          }
        });
      };
      /**
       * Return create container REST endpoint parameters
       * @param {object=} [in_options] Additional options
       * @param {object=} [in_options.headers] Objects to be appended as headers in the request
       * @param {object=} [in_options.qs] Parameters appended to the query string
       * @param {function} in_callback invoked with error or request parameters
       */
      UrlService.prototype.getCreateContainersParameters = function (in_options, in_callback) {
        var that = this;
        in_options = in_options || {};
        // Create a new headers object where in_options.headers supersedes this.headers
        var headers = _.extend({}, this._headers, in_options.headers);
        that.addBearerToken(headers, function (error, headersWithBearerToken) {
          if (error) {
            in_callback(error);
          } else {
            in_callback(undefined, {
              //  /container
              url: that._baseUrl + '/container',
              method: 'POST',
              headers: headersWithBearerToken,
              qs: in_options.qs || {},
              json: true
            });
          }
        });
      };
      /**
       * Return create branch REST endpoint parameters
       * @param {object} in_branch Branch definition
       * @param {object=} [in_options] Additional options
       * @param {object=} [in_options.headers] Objects to be appended as headers in the request
       * @param {object=} [in_options.qs] Parameters appended to the query string
       * @param {function} in_callback invoked with error or request parameters
       */
      UrlService.prototype.getCreateBranchParameters = function (in_branch, in_options, in_callback) {
        var that = this;
        in_options = in_options || {};
        // For ACM support in Apigee, we need the Container Guid in the URL.  The Property Graph does NOT expect the
        // Container Guid, so remove it from the body payload.
        var containerGuid = in_branch.container.guid;
        delete in_branch.container;
        // Create a new headers object where in_options.headers supersedes this.headers
        var headers = _.extend({}, this._headers, in_options.headers);
        that.addBearerToken(headers, function (error, headersWithBearerToken) {
          if (error) {
            in_callback(error);
          } else {
            in_callback(undefined, {
              //  /container/:containerGuid/branch
              url: that._baseUrl + '/container/' + containerGuid + '/branch',
              method: 'POST',
              headers: headersWithBearerToken,
              body: in_branch,
              qs: in_options.qs || {},
              json: true
            });
          }
        });
      };
      /**
       * Return Share/UnShare resources REST endpoint parameters
       * @param {object} in_params The share parameters
       * @param {object=} [in_options] Additional options
       * @param {object=} [in_options.headers] Objects to be appended as headers in the request
       * @param {object=} [in_options.qs] Parameters appended to the query string
       * @param {function} in_callback invoked with error or request parameters
       */
      UrlService.prototype.getShareRepositoryParameters = function (in_params, in_options, in_callback) {
        var that = this;
        in_options = in_options || {};
        // Create a new headers object where in_options.headers supersedes this.headers
        var headers = _.extend({}, this._headers, in_options.headers);
        that.addBearerToken(headers, function (error, headersWithBearerToken) {
          if (error) {
            in_callback(error);
          } else {
            in_callback(undefined, {
              //  /share
              url: that._baseUrl + '/share',
              headers: headersWithBearerToken,
              body: in_params,
              qs: in_options.qs || {},
              json: true
            });
          }
        });
      };
      /**
       * Return REST endpoint parameters for the squashedCommitHistory endpoint
       *
       * @param {object} in_params The get share repository function parameters
       * @param {object=} in_params.branch Branch to share
       * @param {object=} in_params.commit Branch to share
       * @param {object=} [in_options] Additional options
       * @param {object=} [in_options.headers] Objects to be appended as headers in the request
       * @param {object=} [in_options.qs] Parameters appended to the query string
       * @param {function} in_callback invoked with error or request parameters
       */
      UrlService.prototype.fetchSquashedCommitHistoryParameters = function (in_params, in_options, in_callback) {
        var that = this;
        in_options = in_options || {};
        // Create a new headers object where in_options.headers supersedes this.headers
        var headers = _.extend({}, this._headers, in_options.headers);
        that.addBearerToken(headers, function (error, headersWithBearerToken) {
          if (error) {
            in_callback(error);
          } else {
            in_callback(undefined, {
              //  /share
              url: that._baseUrl + '/branch/' + in_params.branch + '/squashedCommitHistory',
              method: 'POST',
              headers: headersWithBearerToken,
              body: { commit: { guid: in_params.commit } },
              qs: in_options.qs || {},
              json: true
            });
          }
        });
      };
      exportModule('LYNX.PropertySetsServer.Services.UrlService', UrlService);
    }());
    (function () {
      var CONSTANTS = {};
      // List of events
      CONSTANTS.EVENTS = {
        // Channel associated with the branch update event
        BRANCH_UPDATE: 'container:branch:update'
      };
      exportModule('LYNX.PropertySetsServer.CONSTANTS', CONSTANTS);
    }());
    (function () {
      var request = include('LYNX.Utils.ThirdParty.request');
      var generateGUID = include('LYNX.Utils.generateGUID');
      var RequestUtils = include('LYNX.Utils.RequestUtils');
      var UrlService = include('LYNX.PropertySetsServer.Services.UrlService');
      var _ = include('underscore');
      var EVENTS = include('LYNX.PropertySetsServer.CONSTANTS').EVENTS;
      /**
       * The client class housing the PropertySetsServer API
       * @param {object=} in_params CollaborationServer Client parameters
       * @param {string=} [in_params.serverUrl] The url pointing to the PropertySetsServer
       * @param {getBearerTokenFn=} [in_params.getBearerToken] Function that accepts a callback
       *  function that should be called with an error or the OAuth2 bearer token representing the user.
       * @param {object|undefined=} [in_params.headers] Headers to supply when making requests (used by tests)
       * @constructor
       * @alias LYNX.PropertySetsServer.Client
       * @private
       */
      var Client = function (in_params) {
        var params = _.defaults(in_params || {}, { serverUrl: 'http://localhost:3000' });
        this._registeredToUpdates = false;
        this._serverURL = params.serverUrl;
        this._getBearerToken = params.getBearerToken;
        this._headers = params.headers || {};
        this._psetServerUrlService = new UrlService({
          baseUrl: this._serverURL,
          getBearerToken: this._getBearerToken,
          headers: this._headers
        });
      };
      /**
       * Commit a change set to a given branch
       * @param {object} in_params List of parameters
       * @param {object} [in_params.commit] Commit meta data
       *  NOTE: if in_params.commits is specified, this field must be undefined
       * @param {Array<object>} [in_params.commits] Array of commit meta data.
       *  NOTE: if in_params.commit is specified, this field must be undefined
       * @param {string=} in_params.commit.guid (optional) Commit guid
       *  If not passed in, a guid will be generated
       * @param {object} in_params.changeSet Serialized changeset
       * @param {object} in_params.branch The branch information to commit on
       * @param {string=} in_params.branch.guid The branch guid
       * @param {object=} in_params.meta The branch meta data
       * @param {string=} in_params.baseGuid (optional) The base guid of the commit that is applied on.
       *  If not specified, the commit will be appended to the head of the branch.
       *  Most commonly omitted when pushing data (IoT use case)
       * @param {object=} [in_options] Additional options
       * @param {object=} [in_options.headers] Objects to be appended as headers in the request
       * @param {object=} [in_options.qs] Parameters appended to the query string
       * @param {string=} [in_options.api] Api to use. If set to 'rest', it will make a call to REST
       * endpoint.
       * @param {function(?Error)} in_callback Callback invoked with an error
       */
      Client.prototype.commit = function (in_params, in_options, in_callback) {
        var that = this;
        in_options = in_options || {};
        console.assert(in_params.commit && !in_params.commits || !in_params.commit && in_params.commits, 'Only one of the two arguments are allowed (in_params.commit or in_params.commits)');
        if (in_params.commit && !in_params.commit.guid) {
          // Generate a guid in the case where we are submitting a single commit
          // but have not supplied a "guid" for it.
          in_params.commit.guid = generateGUID();
        }
        if (_.isUndefined(this._openSocket) || in_options.api === 'rest') {
          // make restful call to the backend.
          that._psetServerUrlService.getCommitParameters(in_params, in_options, function (error, params) {
            if (error) {
              in_callback(error);
            } else {
              request(params, RequestUtils.handleRequestCallback.bind(that, in_callback));
            }
          });
        } else {
          // push data to the backend
          this.super_.prototype.emit.call(this, in_params.branch.guid, EVENTS.BRANCH_UPDATE, in_params, in_callback);
        }
      };
      /**
       * Share resources (URN's) with given user(s)
       * @param {object} in_params List of parameters
       * @param {Array<string>} in_params.urns The URNs for the resources
       * @param {Array<string>=} in_params.userIds The oxygen user ids which will get policies assigned
       * @param {Array<string>=} in_params.groupIds The oxygen group ids which will get policies assigned
       * @param {Array<string>=} in_params.actions The actions to be added on the URNs to the oxygen ids
       *   Defaults to ['read', 'write', 'delete']
       * @param {object=} [in_options] Additional options
       * @param {object=} [in_options.headers] Objects to be appended as headers in the request
       * @param {object=} [in_options.qs] Parameters appended to the query string
       * @param {function(?Error)} in_callback Callback invoked with an error
       */
      Client.prototype.share = function (in_params, in_options, in_callback) {
        this._share('POST', in_params, in_options, in_callback);
      };
      /**
       * UnShare resources (URN's) with given user(s)
       * @param {object} in_params List of parameters
       * @param {Array<string>} in_params.urns The URNs for the resources
       * @param {Array<string>=} in_params.userIds The oxygen user ids which will get policies assigned
       * @param {Array<string>=} in_params.groupIds The oxygen group ids which will get policies unassigned
       * @param {Array<string>=} in_params.actions The actions to be removed on the URNs to the oxygen ids
       *   Defaults to ['read', 'write', 'delete']
       * @param {object=} [in_options] Additional options
       * @param {object=} [in_options.headers] Objects to be appended as headers in the request
       * @param {object=} [in_options.qs] Parameters appended to the query string
       * @param {function(?Error)} in_callback Callback invoked with an error
       */
      Client.prototype.unshare = function (in_params, in_options, in_callback) {
        this._share('DELETE', in_params, in_options, in_callback);
      };
      /**
       * Share/UnShare resources Implementation
       * @param {string} in_method 'POST' for Share and 'DELETE' for UnShare
       * @param {object} in_params List of parameters
       * @param {Array<string>} in_params.urns The URNs for the resources
       * @param {Array<string>=} in_params.userIds The oxygen user ids which will get policies assigned
       * @param {Array<string>=} in_params.groupIds The oxygen group ids which will get policies assigned/unassigned
       * @param {Array<string>=} in_params.actions The actions to be added/removed on the URNs to the oxygen ids
       *   Defaults to ['read', 'write', 'delete']
       * @param {object=} [in_options] Additional options
       * @param {object=} [in_options.headers] Objects to be appended as headers in the request
       * @param {object=} [in_options.qs] Parameters appended to the query string
       * @param {function(?Error)} in_callback Callback invoked with an error
       */
      Client.prototype._share = function (in_method, in_params, in_options, in_callback) {
        var that = this;
        that._psetServerUrlService.getShareRepositoryParameters(in_params, in_options, function (error, params) {
          if (error) {
            in_callback(error);
          } else {
            params.method = in_method;
            request(params, RequestUtils.handleRequestCallback.bind(that, in_callback));
          }
        });
      };
      /**
       * Create a branch
       * @param {object} in_params List of parameters
       * @param {string} in_params.baseGuid Commit guid to branch off of
       * @param {object} in_params.branch Branch meta data
       * @param {string=} in_params.branch.guid (optional) Branch guid
       *  If not passed in, a guid will be generated
       * @param {string=} in_params.branch.name Branch name
       * @param {object} in_params.container Container meta data
       * @param {string=} in_params.container.guid Container guid
       * @param {object=} [in_options] Additional options
       * @param {object=} [in_options.headers] Objects to be appended as headers in the request
       * @param {object=} [in_options.qs] Parameters appended to the query string
       * @param {function(?Error)} in_callback Callback invoked with an error
       */
      Client.prototype.branch = function (in_params, in_options, in_callback) {
        var that = this;
        that._psetServerUrlService.getCreateBranchParameters(in_params, in_options, function (error, params) {
          if (error) {
            in_callback(error);
          } else {
            request(params, RequestUtils.handleRequestCallback.bind(that, in_callback));
          }
        });
      };
      /**
       * Register for updates coming from the server
       * @param {function(LYNX.CollaborationServerAPI.CollaborationMessage)} in_callback Callback function invoked with
       * the following object parameter format:
       * {
       *   room: {string} The room associated with the branchGuid
       *   event: {string} The branch update event handle
       *   data: {object} The data passed in to the Client.commit() API
       * }
       */
      Client.prototype.registerForUpdates = function (in_callback) {
        if (this._registeredToUpdates) {
          console.warn('Already registered to receiving updates');
          return;
        }
        this.super_.prototype.registerToEvent.call(this, EVENTS.BRANCH_UPDATE, in_callback);
        this._registeredToUpdates = true;
      };
      Client.prototype.receive = Client.prototype.registerForUpdates;
      // For backward compatibility/legacy reasons.
      Client.prototype.registerToUpdates = Client.prototype.registerForUpdates;
      /**
       * Unregister from server updates
       * @param {function} in_callback Reference to the callback that we wish to
       *  unregister from.
       */
      Client.prototype.unregisterFromUpdates = function (in_callback) {
        if (!this._registeredToUpdates) {
          console.warn('Already unregistered from receiving updates');
          return;
        }
        this.super_.prototype.unregisterFromEvent.call(this, EVENTS.BRANCH_UPDATE, in_callback);
        this._registeredToUpdates = false;
      };
      /**
       * Create property containers
       * @param {object=} [in_options] Additional options
       * @param {object=} [in_options.headers] Objects to be appended as headers in the request
       * @param {object=} [in_options.qs] Parameters appended to the query string
       * @param {number=} [in_options.qs.count] Number of containers to create
       * @param {function(?Error=, Array.<object>)} in_callback Callback invoked with an error
       *  or a list of created containers of the form:
       *  {
       *   container: {
       *     guid: {string} The guid associated with the newly created container
       *   },
       *   branch: {
       *     guid: {string} The guid of the branch that the container was initialized with
       *   },
       *   commits: [{
       *     guid: {string} The guid of the initial commit
       *   }]
       *  }
       */
      Client.prototype.createContainers = function (in_options, in_callback) {
        var that = this;
        that._psetServerUrlService.getCreateContainersParameters(in_options, function (error, params) {
          if (error) {
            in_callback(error);
          } else {
            request(params, RequestUtils.handleRequestCallback.bind(that, in_callback));
          }
        });
      };
      /**
       * Fetches a specific commit from the property sets server
       *
       * @param {String} in_commitGUID     - GUID of the commit
       * @param {String} in_branchGUID     - GUID of the branch containing the commit
       * @param {Object} in_options        - Additional options
       * @param {function(?Error=, ?LYNX.PropertyGraph.Nodes.CommitNode=)} in_callback -
       *     Callback function invoked with an error if the commit could not be retrieved
       *     or the newly created commit node
       */
      Client.prototype.fetchCommit = function (in_commitGUID, in_branchGUID, in_options, in_callback) {
        var that = this;
        that._psetServerUrlService.fetchSquashedCommitHistoryParameters({
          commit: in_commitGUID,
          branch: in_branchGUID
        }, in_options, function (error, params) {
          if (error) {
            in_callback(error);
          } else {
            request(params, RequestUtils.handleRequestCallback.bind(that, function (in_error, in_data) {
              if (in_error) {
                in_callback(in_error);
                return;
              }
              var commit = in_data.commits[0];
              var commitInfo = {
                guid: commit.guid,
                meta: { name: '' },
                base: undefined,
                changeSet: commit.changeSet,
                repository: { guid: in_data.container.guid }
              };
              in_callback(null, commitInfo);
            }));
          }
        });
      };
      Client.prototype.joinBranchSession = function (in_branchGuid, in_options, in_callback) {
        in_options = in_options || {};
        in_options.roomType = 'branch';
        this.super_.prototype.join.call(this, in_branchGuid, in_options, in_callback);
      };
      Client.prototype.leaveBranchSession = function (in_branchGuid, in_options, in_callback) {
        this.super_.prototype.leave.call(this, in_branchGuid, in_options, in_callback);
      };
      /**
       * get the the squashed commit history from the property set server
       * @param {object=} [in_params] Additionnal options
       * @param {object=} [in_params.headers] Objects to be appended as headers in the request
       * @param {object=} [in_params.qs] Parameters appended to the query string
       * @param {object=} [in_params.commit] current commit
       * @param {function(?Error=,  ?Array.<object>)} in_callback Callback invoked with an error
       */
      Client.prototype.squashedCommitHistory = function (inBranchGuid, in_params, in_callback) {
        var that = this;
        this._psetServerUrlService.getSquashedCommitHistoryParameters(inBranchGuid, in_params, function (error, params) {
          if (error) {
            in_callback(error);
          } else {
            request(params, RequestUtils.handleRequestCallback.bind(that, in_callback));
          }
        });
      };
      exportModule('LYNX.PropertySetsServer.Client', Client);
    }());
    (function () {
      var BrowserCollaboratorClient = include('LYNX.CollaborationClient.Clients.BrowserCollaborator');
      var PropertySetsServerClient = include('LYNX.PropertySetsServer.Client');
      var _ = include('underscore');
      /**
       * This is the description.
       * @param {object} in_params CollaborationServer Client parameters
       * @param {string=} [in_params.serverUrl] The url pointing to the PropertySetsServer
       * @param {getBearerTokenFn=} [in_params.getBearerToken] Function that accepts a callback
       *  function that should be called with an error or the OAuth2 bearer token representing the user.
       * @param {object|undefined=} [in_params.headers] Headers to supply when making requests (used by tests)
       * @constructor
       * @extends {LYNX.PropertySetsServer.Client}
       * @extends {LYNX.CollaborationClient.Clients.BrowserCollaborator}
       * @alias LYNX.PropertySetsServer.BrowserClient
       * @private
       */
      var PropertySetBrowserClient = function (in_params) {
        PropertySetsServerClient.call(this, in_params);
        BrowserCollaboratorClient.call(this, in_params);
      };
      PropertySetBrowserClient.prototype = Object.create(BrowserCollaboratorClient.prototype);
      // Node convention https://nodejs.org/docs/latest/api/util.html#util_util_inherits_constructor_superconstructor
      PropertySetBrowserClient.prototype.super_ = BrowserCollaboratorClient;
      _.extend(PropertySetBrowserClient.prototype, PropertySetsServerClient.prototype);
      exportModule('LYNX.PropertySetsServer.BrowserClient', PropertySetBrowserClient);
    }());
    (function e(t, n, r) {
      function s(o, u) {
        if (!n[o]) {
          if (!t[o]) {
            var a = typeof require == 'function' && require;
            if (!u && a)
              return a(o, !0);
            if (i)
              return i(o, !0);
            var f = new Error('Cannot find module \'' + o + '\'');
            throw f.code = 'MODULE_NOT_FOUND', f;
          }
          var l = n[o] = { exports: {} };
          t[o][0].call(l.exports, function (e) {
            var n = t[o][1][e];
            return s(n ? n : e);
          }, l, l.exports, e, t, n, r);
        }
        return n[o].exports;
      }
      var i = typeof require == 'function' && require;
      for (var o = 0; o < r.length; o++)
        s(r[o]);
      return s;
    }({
      1: [
        function (require, module, exports) {
          // shim for using process in browser
          var process = module.exports = {};
          // cached from whatever global is present so that test runners that stub it
          // don't break things.  But we need to wrap it in a try catch in case it is
          // wrapped in strict mode code which doesn't define any globals.  It's inside a
          // function because try/catches deoptimize in certain engines.
          var cachedSetTimeout;
          var cachedClearTimeout;
          function defaultSetTimout() {
            throw new Error('setTimeout has not been defined');
          }
          function defaultClearTimeout() {
            throw new Error('clearTimeout has not been defined');
          }
          (function () {
            try {
              if (typeof setTimeout === 'function') {
                cachedSetTimeout = setTimeout;
              } else {
                cachedSetTimeout = defaultSetTimout;
              }
            } catch (e) {
              cachedSetTimeout = defaultSetTimout;
            }
            try {
              if (typeof clearTimeout === 'function') {
                cachedClearTimeout = clearTimeout;
              } else {
                cachedClearTimeout = defaultClearTimeout;
              }
            } catch (e) {
              cachedClearTimeout = defaultClearTimeout;
            }
          }());
          function runTimeout(fun) {
            if (cachedSetTimeout === setTimeout) {
              //normal enviroments in sane situations
              return setTimeout(fun, 0);
            }
            // if setTimeout wasn't available but was latter defined
            if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
              cachedSetTimeout = setTimeout;
              return setTimeout(fun, 0);
            }
            try {
              // when when somebody has screwed with setTimeout but no I.E. maddness
              return cachedSetTimeout(fun, 0);
            } catch (e) {
              try {
                // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
                return cachedSetTimeout.call(null, fun, 0);
              } catch (e) {
                // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
                return cachedSetTimeout.call(this, fun, 0);
              }
            }
          }
          function runClearTimeout(marker) {
            if (cachedClearTimeout === clearTimeout) {
              //normal enviroments in sane situations
              return clearTimeout(marker);
            }
            // if clearTimeout wasn't available but was latter defined
            if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
              cachedClearTimeout = clearTimeout;
              return clearTimeout(marker);
            }
            try {
              // when when somebody has screwed with setTimeout but no I.E. maddness
              return cachedClearTimeout(marker);
            } catch (e) {
              try {
                // When we are in I.E. but the script has been evaled so I.E. doesn't  trust the global object when called normally
                return cachedClearTimeout.call(null, marker);
              } catch (e) {
                // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
                // Some versions of I.E. have different rules for clearTimeout vs setTimeout
                return cachedClearTimeout.call(this, marker);
              }
            }
          }
          var queue = [];
          var draining = false;
          var currentQueue;
          var queueIndex = -1;
          function cleanUpNextTick() {
            if (!draining || !currentQueue) {
              return;
            }
            draining = false;
            if (currentQueue.length) {
              queue = currentQueue.concat(queue);
            } else {
              queueIndex = -1;
            }
            if (queue.length) {
              drainQueue();
            }
          }
          function drainQueue() {
            if (draining) {
              return;
            }
            var timeout = runTimeout(cleanUpNextTick);
            draining = true;
            var len = queue.length;
            while (len) {
              currentQueue = queue;
              queue = [];
              while (++queueIndex < len) {
                if (currentQueue) {
                  currentQueue[queueIndex].run();
                }
              }
              queueIndex = -1;
              len = queue.length;
            }
            currentQueue = null;
            draining = false;
            runClearTimeout(timeout);
          }
          process.nextTick = function (fun) {
            var args = new Array(arguments.length - 1);
            if (arguments.length > 1) {
              for (var i = 1; i < arguments.length; i++) {
                args[i - 1] = arguments[i];
              }
            }
            queue.push(new Item(fun, args));
            if (queue.length === 1 && !draining) {
              runTimeout(drainQueue);
            }
          };
          // v8 likes predictible objects
          function Item(fun, array) {
            this.fun = fun;
            this.array = array;
          }
          Item.prototype.run = function () {
            this.fun.apply(null, this.array);
          };
          process.title = 'browser';
          process.browser = true;
          process.env = {};
          process.argv = [];
          process.version = '';
          // empty string to avoid regexp issues
          process.versions = {};
          function noop() {
          }
          process.on = noop;
          process.addListener = noop;
          process.once = noop;
          process.off = noop;
          process.removeListener = noop;
          process.removeAllListeners = noop;
          process.emit = noop;
          process.binding = function (name) {
            throw new Error('process.binding is not supported');
          };
          process.cwd = function () {
            return '/';
          };
          process.chdir = function (dir) {
            throw new Error('process.chdir is not supported');
          };
          process.umask = function () {
            return 0;
          };
        },
        {}
      ],
      2: [
        function (require, module, exports) {
          (function (process) {
            exports = module.exports = SemVer;
            // The debug function is excluded entirely from the minified version.
            /* nomin */
            var debug;
            /* nomin */
            if (typeof process === 'object' && /* nomin */
              process.env && /* nomin */
              process.env.NODE_DEBUG && /* nomin */
              /\bsemver\b/i.test(process.env.NODE_DEBUG))
              /* nomin */
              debug = function () {
                /* nomin */
                var args = Array.prototype.slice.call(arguments, 0);
                /* nomin */
                args.unshift('SEMVER');
                /* nomin */
                console.log.apply(console, args);  /* nomin */
              };  /* nomin */
            else
              /* nomin */
              debug = function () {
              };
            // Note: this is the semver.org version of the spec that it implements
            // Not necessarily the package version of this code.
            exports.SEMVER_SPEC_VERSION = '2.0.0';
            var MAX_LENGTH = 256;
            var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;
            // The actual regexps go on exports.re
            var re = exports.re = [];
            var src = exports.src = [];
            var R = 0;
            // The following Regular Expressions can be used for tokenizing,
            // validating, and parsing SemVer version strings.
            // ## Numeric Identifier
            // A single `0`, or a non-zero digit followed by zero or more digits.
            var NUMERICIDENTIFIER = R++;
            src[NUMERICIDENTIFIER] = '0|[1-9]\\d*';
            var NUMERICIDENTIFIERLOOSE = R++;
            src[NUMERICIDENTIFIERLOOSE] = '[0-9]+';
            // ## Non-numeric Identifier
            // Zero or more digits, followed by a letter or hyphen, and then zero or
            // more letters, digits, or hyphens.
            var NONNUMERICIDENTIFIER = R++;
            src[NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*';
            // ## Main Version
            // Three dot-separated numeric identifiers.
            var MAINVERSION = R++;
            src[MAINVERSION] = '(' + src[NUMERICIDENTIFIER] + ')\\.' + '(' + src[NUMERICIDENTIFIER] + ')\\.' + '(' + src[NUMERICIDENTIFIER] + ')';
            var MAINVERSIONLOOSE = R++;
            src[MAINVERSIONLOOSE] = '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + '(' + src[NUMERICIDENTIFIERLOOSE] + ')';
            // ## Pre-release Version Identifier
            // A numeric identifier, or a non-numeric identifier.
            var PRERELEASEIDENTIFIER = R++;
            src[PRERELEASEIDENTIFIER] = '(?:' + src[NUMERICIDENTIFIER] + '|' + src[NONNUMERICIDENTIFIER] + ')';
            var PRERELEASEIDENTIFIERLOOSE = R++;
            src[PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[NUMERICIDENTIFIERLOOSE] + '|' + src[NONNUMERICIDENTIFIER] + ')';
            // ## Pre-release Version
            // Hyphen, followed by one or more dot-separated pre-release version
            // identifiers.
            var PRERELEASE = R++;
            src[PRERELEASE] = '(?:-(' + src[PRERELEASEIDENTIFIER] + '(?:\\.' + src[PRERELEASEIDENTIFIER] + ')*))';
            var PRERELEASELOOSE = R++;
            src[PRERELEASELOOSE] = '(?:-?(' + src[PRERELEASEIDENTIFIERLOOSE] + '(?:\\.' + src[PRERELEASEIDENTIFIERLOOSE] + ')*))';
            // ## Build Metadata Identifier
            // Any combination of digits, letters, or hyphens.
            var BUILDIDENTIFIER = R++;
            src[BUILDIDENTIFIER] = '[0-9A-Za-z-]+';
            // ## Build Metadata
            // Plus sign, followed by one or more period-separated build metadata
            // identifiers.
            var BUILD = R++;
            src[BUILD] = '(?:\\+(' + src[BUILDIDENTIFIER] + '(?:\\.' + src[BUILDIDENTIFIER] + ')*))';
            // ## Full Version String
            // A main version, followed optionally by a pre-release version and
            // build metadata.
            // Note that the only major, minor, patch, and pre-release sections of
            // the version string are capturing groups.  The build metadata is not a
            // capturing group, because it should not ever be used in version
            // comparison.
            var FULL = R++;
            var FULLPLAIN = 'v?' + src[MAINVERSION] + src[PRERELEASE] + '?' + src[BUILD] + '?';
            src[FULL] = '^' + FULLPLAIN + '$';
            // like full, but allows v1.2.3 and =1.2.3, which people do sometimes.
            // also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty
            // common in the npm registry.
            var LOOSEPLAIN = '[v=\\s]*' + src[MAINVERSIONLOOSE] + src[PRERELEASELOOSE] + '?' + src[BUILD] + '?';
            var LOOSE = R++;
            src[LOOSE] = '^' + LOOSEPLAIN + '$';
            var GTLT = R++;
            src[GTLT] = '((?:<|>)?=?)';
            // Something like "2.*" or "1.2.x".
            // Note that "x.x" is a valid xRange identifer, meaning "any version"
            // Only the first item is strictly required.
            var XRANGEIDENTIFIERLOOSE = R++;
            src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + '|x|X|\\*';
            var XRANGEIDENTIFIER = R++;
            src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + '|x|X|\\*';
            var XRANGEPLAIN = R++;
            src[XRANGEPLAIN] = '[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + '(?:' + src[PRERELEASE] + ')?' + src[BUILD] + '?' + ')?)?';
            var XRANGEPLAINLOOSE = R++;
            src[XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + '(?:' + src[PRERELEASELOOSE] + ')?' + src[BUILD] + '?' + ')?)?';
            var XRANGE = R++;
            src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$';
            var XRANGELOOSE = R++;
            src[XRANGELOOSE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAINLOOSE] + '$';
            // Tilde ranges.
            // Meaning is "reasonably at or greater than"
            var LONETILDE = R++;
            src[LONETILDE] = '(?:~>?)';
            var TILDETRIM = R++;
            src[TILDETRIM] = '(\\s*)' + src[LONETILDE] + '\\s+';
            re[TILDETRIM] = new RegExp(src[TILDETRIM], 'g');
            var tildeTrimReplace = '$1~';
            var TILDE = R++;
            src[TILDE] = '^' + src[LONETILDE] + src[XRANGEPLAIN] + '$';
            var TILDELOOSE = R++;
            src[TILDELOOSE] = '^' + src[LONETILDE] + src[XRANGEPLAINLOOSE] + '$';
            // Caret ranges.
            // Meaning is "at least and backwards compatible with"
            var LONECARET = R++;
            src[LONECARET] = '(?:\\^)';
            var CARETTRIM = R++;
            src[CARETTRIM] = '(\\s*)' + src[LONECARET] + '\\s+';
            re[CARETTRIM] = new RegExp(src[CARETTRIM], 'g');
            var caretTrimReplace = '$1^';
            var CARET = R++;
            src[CARET] = '^' + src[LONECARET] + src[XRANGEPLAIN] + '$';
            var CARETLOOSE = R++;
            src[CARETLOOSE] = '^' + src[LONECARET] + src[XRANGEPLAINLOOSE] + '$';
            // A simple gt/lt/eq thing, or just "" to indicate "any version"
            var COMPARATORLOOSE = R++;
            src[COMPARATORLOOSE] = '^' + src[GTLT] + '\\s*(' + LOOSEPLAIN + ')$|^$';
            var COMPARATOR = R++;
            src[COMPARATOR] = '^' + src[GTLT] + '\\s*(' + FULLPLAIN + ')$|^$';
            // An expression to strip any whitespace between the gtlt and the thing
            // it modifies, so that `> 1.2.3` ==> `>1.2.3`
            var COMPARATORTRIM = R++;
            src[COMPARATORTRIM] = '(\\s*)' + src[GTLT] + '\\s*(' + LOOSEPLAIN + '|' + src[XRANGEPLAIN] + ')';
            // this one has to use the /g flag
            re[COMPARATORTRIM] = new RegExp(src[COMPARATORTRIM], 'g');
            var comparatorTrimReplace = '$1$2$3';
            // Something like `1.2.3 - 1.2.4`
            // Note that these all use the loose form, because they'll be
            // checked against either the strict or loose comparator form
            // later.
            var HYPHENRANGE = R++;
            src[HYPHENRANGE] = '^\\s*(' + src[XRANGEPLAIN] + ')' + '\\s+-\\s+' + '(' + src[XRANGEPLAIN] + ')' + '\\s*$';
            var HYPHENRANGELOOSE = R++;
            src[HYPHENRANGELOOSE] = '^\\s*(' + src[XRANGEPLAINLOOSE] + ')' + '\\s+-\\s+' + '(' + src[XRANGEPLAINLOOSE] + ')' + '\\s*$';
            // Star ranges basically just allow anything at all.
            var STAR = R++;
            src[STAR] = '(<|>)?=?\\s*\\*';
            // Compile to actual regexp objects.
            // All are flag-free, unless they were created above with a flag.
            for (var i = 0; i < R; i++) {
              debug(i, src[i]);
              if (!re[i])
                re[i] = new RegExp(src[i]);
            }
            exports.parse = parse;
            function parse(version, loose) {
              if (version instanceof SemVer)
                return version;
              if (typeof version !== 'string')
                return null;
              if (version.length > MAX_LENGTH)
                return null;
              var r = loose ? re[LOOSE] : re[FULL];
              if (!r.test(version))
                return null;
              try {
                return new SemVer(version, loose);
              } catch (er) {
                return null;
              }
            }
            exports.valid = valid;
            function valid(version, loose) {
              var v = parse(version, loose);
              return v ? v.version : null;
            }
            exports.clean = clean;
            function clean(version, loose) {
              var s = parse(version.trim().replace(/^[=v]+/, ''), loose);
              return s ? s.version : null;
            }
            exports.SemVer = SemVer;
            exportModule('LYNX.Utils.ThirdParty.semver', SemVer);
            function SemVer(version, loose) {
              if (version instanceof SemVer) {
                if (version.loose === loose)
                  return version;
                else
                  version = version.version;
              } else if (typeof version !== 'string') {
                throw new TypeError('Invalid Version: ' + version);
              }
              if (version.length > MAX_LENGTH)
                throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters');
              if (!(this instanceof SemVer))
                return new SemVer(version, loose);
              debug('SemVer', version, loose);
              this.loose = loose;
              var m = version.trim().match(loose ? re[LOOSE] : re[FULL]);
              if (!m)
                throw new TypeError('Invalid Version: ' + version);
              this.raw = version;
              // these are actually numbers
              this.major = +m[1];
              this.minor = +m[2];
              this.patch = +m[3];
              if (this.major > MAX_SAFE_INTEGER || this.major < 0)
                throw new TypeError('Invalid major version');
              if (this.minor > MAX_SAFE_INTEGER || this.minor < 0)
                throw new TypeError('Invalid minor version');
              if (this.patch > MAX_SAFE_INTEGER || this.patch < 0)
                throw new TypeError('Invalid patch version');
              // numberify any prerelease numeric ids
              if (!m[4])
                this.prerelease = [];
              else
                this.prerelease = m[4].split('.').map(function (id) {
                  if (/^[0-9]+$/.test(id)) {
                    var num = +id;
                    if (num >= 0 && num < MAX_SAFE_INTEGER)
                      return num;
                  }
                  return id;
                });
              this.build = m[5] ? m[5].split('.') : [];
              this.format();
            }
            SemVer.prototype.format = function () {
              this.version = this.major + '.' + this.minor + '.' + this.patch;
              if (this.prerelease.length)
                this.version += '-' + this.prerelease.join('.');
              return this.version;
            };
            SemVer.prototype.toString = function () {
              return this.version;
            };
            SemVer.prototype.compare = function (other) {
              debug('SemVer.compare', this.version, this.loose, other);
              if (!(other instanceof SemVer))
                other = new SemVer(other, this.loose);
              return this.compareMain(other) || this.comparePre(other);
            };
            SemVer.prototype.compareMain = function (other) {
              if (!(other instanceof SemVer))
                other = new SemVer(other, this.loose);
              return compareIdentifiers(this.major, other.major) || compareIdentifiers(this.minor, other.minor) || compareIdentifiers(this.patch, other.patch);
            };
            SemVer.prototype.comparePre = function (other) {
              if (!(other instanceof SemVer))
                other = new SemVer(other, this.loose);
              // NOT having a prerelease is > having one
              if (this.prerelease.length && !other.prerelease.length)
                return -1;
              else if (!this.prerelease.length && other.prerelease.length)
                return 1;
              else if (!this.prerelease.length && !other.prerelease.length)
                return 0;
              var i = 0;
              do {
                var a = this.prerelease[i];
                var b = other.prerelease[i];
                debug('prerelease compare', i, a, b);
                if (a === undefined && b === undefined)
                  return 0;
                else if (b === undefined)
                  return 1;
                else if (a === undefined)
                  return -1;
                else if (a === b)
                  continue;
                else
                  return compareIdentifiers(a, b);
              } while (++i);
            };
            // preminor will bump the version up to the next minor release, and immediately
            // down to pre-release. premajor and prepatch work the same way.
            SemVer.prototype.inc = function (release, identifier) {
              switch (release) {
              case 'premajor':
                this.prerelease.length = 0;
                this.patch = 0;
                this.minor = 0;
                this.major++;
                this.inc('pre', identifier);
                break;
              case 'preminor':
                this.prerelease.length = 0;
                this.patch = 0;
                this.minor++;
                this.inc('pre', identifier);
                break;
              case 'prepatch':
                // If this is already a prerelease, it will bump to the next version
                // drop any prereleases that might already exist, since they are not
                // relevant at this point.
                this.prerelease.length = 0;
                this.inc('patch', identifier);
                this.inc('pre', identifier);
                break;
              // If the input is a non-prerelease version, this acts the same as
              // prepatch.
              case 'prerelease':
                if (this.prerelease.length === 0)
                  this.inc('patch', identifier);
                this.inc('pre', identifier);
                break;
              case 'major':
                // If this is a pre-major version, bump up to the same major version.
                // Otherwise increment major.
                // 1.0.0-5 bumps to 1.0.0
                // 1.1.0 bumps to 2.0.0
                if (this.minor !== 0 || this.patch !== 0 || this.prerelease.length === 0)
                  this.major++;
                this.minor = 0;
                this.patch = 0;
                this.prerelease = [];
                break;
              case 'minor':
                // If this is a pre-minor version, bump up to the same minor version.
                // Otherwise increment minor.
                // 1.2.0-5 bumps to 1.2.0
                // 1.2.1 bumps to 1.3.0
                if (this.patch !== 0 || this.prerelease.length === 0)
                  this.minor++;
                this.patch = 0;
                this.prerelease = [];
                break;
              case 'patch':
                // If this is not a pre-release version, it will increment the patch.
                // If it is a pre-release it will bump up to the same patch version.
                // 1.2.0-5 patches to 1.2.0
                // 1.2.0 patches to 1.2.1
                if (this.prerelease.length === 0)
                  this.patch++;
                this.prerelease = [];
                break;
              // This probably shouldn't be used publicly.
              // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction.
              case 'pre':
                if (this.prerelease.length === 0)
                  this.prerelease = [0];
                else {
                  var i = this.prerelease.length;
                  while (--i >= 0) {
                    if (typeof this.prerelease[i] === 'number') {
                      this.prerelease[i]++;
                      i = -2;
                    }
                  }
                  if (i === -1)
                    // didn't increment anything
                    this.prerelease.push(0);
                }
                if (identifier) {
                  // 1.2.0-beta.1 bumps to 1.2.0-beta.2,
                  // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0
                  if (this.prerelease[0] === identifier) {
                    if (isNaN(this.prerelease[1]))
                      this.prerelease = [
                        identifier,
                        0
                      ];
                  } else
                    this.prerelease = [
                      identifier,
                      0
                    ];
                }
                break;
              default:
                throw new Error('invalid increment argument: ' + release);
              }
              this.format();
              this.raw = this.version;
              return this;
            };
            exports.inc = inc;
            function inc(version, release, loose, identifier) {
              if (typeof loose === 'string') {
                identifier = loose;
                loose = undefined;
              }
              try {
                return new SemVer(version, loose).inc(release, identifier).version;
              } catch (er) {
                return null;
              }
            }
            exports.diff = diff;
            function diff(version1, version2) {
              if (eq(version1, version2)) {
                return null;
              } else {
                var v1 = parse(version1);
                var v2 = parse(version2);
                if (v1.prerelease.length || v2.prerelease.length) {
                  for (var key in v1) {
                    if (key === 'major' || key === 'minor' || key === 'patch') {
                      if (v1[key] !== v2[key]) {
                        return 'pre' + key;
                      }
                    }
                  }
                  return 'prerelease';
                }
                for (var key in v1) {
                  if (key === 'major' || key === 'minor' || key === 'patch') {
                    if (v1[key] !== v2[key]) {
                      return key;
                    }
                  }
                }
              }
            }
            exports.compareIdentifiers = compareIdentifiers;
            var numeric = /^[0-9]+$/;
            function compareIdentifiers(a, b) {
              var anum = numeric.test(a);
              var bnum = numeric.test(b);
              if (anum && bnum) {
                a = +a;
                b = +b;
              }
              return anum && !bnum ? -1 : bnum && !anum ? 1 : a < b ? -1 : a > b ? 1 : 0;
            }
            exports.rcompareIdentifiers = rcompareIdentifiers;
            function rcompareIdentifiers(a, b) {
              return compareIdentifiers(b, a);
            }
            exports.major = major;
            function major(a, loose) {
              return new SemVer(a, loose).major;
            }
            exports.minor = minor;
            function minor(a, loose) {
              return new SemVer(a, loose).minor;
            }
            exports.patch = patch;
            function patch(a, loose) {
              return new SemVer(a, loose).patch;
            }
            exports.compare = compare;
            function compare(a, b, loose) {
              return new SemVer(a, loose).compare(b);
            }
            exports.compareLoose = compareLoose;
            function compareLoose(a, b) {
              return compare(a, b, true);
            }
            exports.rcompare = rcompare;
            function rcompare(a, b, loose) {
              return compare(b, a, loose);
            }
            exports.sort = sort;
            function sort(list, loose) {
              return list.sort(function (a, b) {
                return exports.compare(a, b, loose);
              });
            }
            exports.rsort = rsort;
            function rsort(list, loose) {
              return list.sort(function (a, b) {
                return exports.rcompare(a, b, loose);
              });
            }
            exports.gt = gt;
            function gt(a, b, loose) {
              return compare(a, b, loose) > 0;
            }
            exports.lt = lt;
            function lt(a, b, loose) {
              return compare(a, b, loose) < 0;
            }
            exports.eq = eq;
            function eq(a, b, loose) {
              return compare(a, b, loose) === 0;
            }
            exports.neq = neq;
            function neq(a, b, loose) {
              return compare(a, b, loose) !== 0;
            }
            exports.gte = gte;
            function gte(a, b, loose) {
              return compare(a, b, loose) >= 0;
            }
            exports.lte = lte;
            function lte(a, b, loose) {
              return compare(a, b, loose) <= 0;
            }
            exports.cmp = cmp;
            function cmp(a, op, b, loose) {
              var ret;
              switch (op) {
              case '===':
                if (typeof a === 'object')
                  a = a.version;
                if (typeof b === 'object')
                  b = b.version;
                ret = a === b;
                break;
              case '!==':
                if (typeof a === 'object')
                  a = a.version;
                if (typeof b === 'object')
                  b = b.version;
                ret = a !== b;
                break;
              case '':
              case '=':
              case '==':
                ret = eq(a, b, loose);
                break;
              case '!=':
                ret = neq(a, b, loose);
                break;
              case '>':
                ret = gt(a, b, loose);
                break;
              case '>=':
                ret = gte(a, b, loose);
                break;
              case '<':
                ret = lt(a, b, loose);
                break;
              case '<=':
                ret = lte(a, b, loose);
                break;
              default:
                throw new TypeError('Invalid operator: ' + op);
              }
              return ret;
            }
            exports.Comparator = Comparator;
            function Comparator(comp, loose) {
              if (comp instanceof Comparator) {
                if (comp.loose === loose)
                  return comp;
                else
                  comp = comp.value;
              }
              if (!(this instanceof Comparator))
                return new Comparator(comp, loose);
              debug('comparator', comp, loose);
              this.loose = loose;
              this.parse(comp);
              if (this.semver === ANY)
                this.value = '';
              else
                this.value = this.operator + this.semver.version;
              debug('comp', this);
            }
            var ANY = {};
            Comparator.prototype.parse = function (comp) {
              var r = this.loose ? re[COMPARATORLOOSE] : re[COMPARATOR];
              var m = comp.match(r);
              if (!m)
                throw new TypeError('Invalid comparator: ' + comp);
              this.operator = m[1];
              if (this.operator === '=')
                this.operator = '';
              // if it literally is just '>' or '' then allow anything.
              if (!m[2])
                this.semver = ANY;
              else
                this.semver = new SemVer(m[2], this.loose);
            };
            Comparator.prototype.toString = function () {
              return this.value;
            };
            Comparator.prototype.test = function (version) {
              debug('Comparator.test', version, this.loose);
              if (this.semver === ANY)
                return true;
              if (typeof version === 'string')
                version = new SemVer(version, this.loose);
              return cmp(version, this.operator, this.semver, this.loose);
            };
            exports.Range = Range;
            function Range(range, loose) {
              if (range instanceof Range && range.loose === loose)
                return range;
              if (!(this instanceof Range))
                return new Range(range, loose);
              this.loose = loose;
              // First, split based on boolean or ||
              this.raw = range;
              this.set = range.split(/\s*\|\|\s*/).map(function (range) {
                return this.parseRange(range.trim());
              }, this).filter(function (c) {
                // throw out any that are not relevant for whatever reason
                return c.length;
              });
              if (!this.set.length) {
                throw new TypeError('Invalid SemVer Range: ' + range);
              }
              this.format();
            }
            Range.prototype.format = function () {
              this.range = this.set.map(function (comps) {
                return comps.join(' ').trim();
              }).join('||').trim();
              return this.range;
            };
            Range.prototype.toString = function () {
              return this.range;
            };
            Range.prototype.parseRange = function (range) {
              var loose = this.loose;
              range = range.trim();
              debug('range', range, loose);
              // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`
              var hr = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE];
              range = range.replace(hr, hyphenReplace);
              debug('hyphen replace', range);
              // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
              range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace);
              debug('comparator trim', range, re[COMPARATORTRIM]);
              // `~ 1.2.3` => `~1.2.3`
              range = range.replace(re[TILDETRIM], tildeTrimReplace);
              // `^ 1.2.3` => `^1.2.3`
              range = range.replace(re[CARETTRIM], caretTrimReplace);
              // normalize spaces
              range = range.split(/\s+/).join(' ');
              // At this point, the range is completely trimmed and
              // ready to be split into comparators.
              var compRe = loose ? re[COMPARATORLOOSE] : re[COMPARATOR];
              var set = range.split(' ').map(function (comp) {
                return parseComparator(comp, loose);
              }).join(' ').split(/\s+/);
              if (this.loose) {
                // in loose mode, throw out any that are not valid comparators
                set = set.filter(function (comp) {
                  return !!comp.match(compRe);
                });
              }
              set = set.map(function (comp) {
                return new Comparator(comp, loose);
              });
              return set;
            };
            // Mostly just for testing and legacy API reasons
            exports.toComparators = toComparators;
            function toComparators(range, loose) {
              return new Range(range, loose).set.map(function (comp) {
                return comp.map(function (c) {
                  return c.value;
                }).join(' ').trim().split(' ');
              });
            }
            // comprised of xranges, tildes, stars, and gtlt's at this point.
            // already replaced the hyphen ranges
            // turn into a set of JUST comparators.
            function parseComparator(comp, loose) {
              debug('comp', comp);
              comp = replaceCarets(comp, loose);
              debug('caret', comp);
              comp = replaceTildes(comp, loose);
              debug('tildes', comp);
              comp = replaceXRanges(comp, loose);
              debug('xrange', comp);
              comp = replaceStars(comp, loose);
              debug('stars', comp);
              return comp;
            }
            function isX(id) {
              return !id || id.toLowerCase() === 'x' || id === '*';
            }
            // ~, ~> --> * (any, kinda silly)
            // ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0
            // ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0
            // ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0
            // ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0
            // ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0
            function replaceTildes(comp, loose) {
              return comp.trim().split(/\s+/).map(function (comp) {
                return replaceTilde(comp, loose);
              }).join(' ');
            }
            function replaceTilde(comp, loose) {
              var r = loose ? re[TILDELOOSE] : re[TILDE];
              return comp.replace(r, function (_, M, m, p, pr) {
                debug('tilde', comp, _, M, m, p, pr);
                var ret;
                if (isX(M))
                  ret = '';
                else if (isX(m))
                  ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0';
                else if (isX(p))
                  // ~1.2 == >=1.2.0 <1.3.0
                  ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0';
                else if (pr) {
                  debug('replaceTilde pr', pr);
                  if (pr.charAt(0) !== '-')
                    pr = '-' + pr;
                  ret = '>=' + M + '.' + m + '.' + p + pr + ' <' + M + '.' + (+m + 1) + '.0';
                } else
                  // ~1.2.3 == >=1.2.3 <1.3.0
                  ret = '>=' + M + '.' + m + '.' + p + ' <' + M + '.' + (+m + 1) + '.0';
                debug('tilde return', ret);
                return ret;
              });
            }
            // ^ --> * (any, kinda silly)
            // ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0
            // ^2.0, ^2.0.x --> >=2.0.0 <3.0.0
            // ^1.2, ^1.2.x --> >=1.2.0 <2.0.0
            // ^1.2.3 --> >=1.2.3 <2.0.0
            // ^1.2.0 --> >=1.2.0 <2.0.0
            function replaceCarets(comp, loose) {
              return comp.trim().split(/\s+/).map(function (comp) {
                return replaceCaret(comp, loose);
              }).join(' ');
            }
            function replaceCaret(comp, loose) {
              debug('caret', comp, loose);
              var r = loose ? re[CARETLOOSE] : re[CARET];
              return comp.replace(r, function (_, M, m, p, pr) {
                debug('caret', comp, _, M, m, p, pr);
                var ret;
                if (isX(M))
                  ret = '';
                else if (isX(m))
                  ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0';
                else if (isX(p)) {
                  if (M === '0')
                    ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0';
                  else
                    ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0';
                } else if (pr) {
                  debug('replaceCaret pr', pr);
                  if (pr.charAt(0) !== '-')
                    pr = '-' + pr;
                  if (M === '0') {
                    if (m === '0')
                      ret = '>=' + M + '.' + m + '.' + p + pr + ' <' + M + '.' + m + '.' + (+p + 1);
                    else
                      ret = '>=' + M + '.' + m + '.' + p + pr + ' <' + M + '.' + (+m + 1) + '.0';
                  } else
                    ret = '>=' + M + '.' + m + '.' + p + pr + ' <' + (+M + 1) + '.0.0';
                } else {
                  debug('no pr');
                  if (M === '0') {
                    if (m === '0')
                      ret = '>=' + M + '.' + m + '.' + p + ' <' + M + '.' + m + '.' + (+p + 1);
                    else
                      ret = '>=' + M + '.' + m + '.' + p + ' <' + M + '.' + (+m + 1) + '.0';
                  } else
                    ret = '>=' + M + '.' + m + '.' + p + ' <' + (+M + 1) + '.0.0';
                }
                debug('caret return', ret);
                return ret;
              });
            }
            function replaceXRanges(comp, loose) {
              debug('replaceXRanges', comp, loose);
              return comp.split(/\s+/).map(function (comp) {
                return replaceXRange(comp, loose);
              }).join(' ');
            }
            function replaceXRange(comp, loose) {
              comp = comp.trim();
              var r = loose ? re[XRANGELOOSE] : re[XRANGE];
              return comp.replace(r, function (ret, gtlt, M, m, p, pr) {
                debug('xRange', comp, ret, gtlt, M, m, p, pr);
                var xM = isX(M);
                var xm = xM || isX(m);
                var xp = xm || isX(p);
                var anyX = xp;
                if (gtlt === '=' && anyX)
                  gtlt = '';
                if (xM) {
                  if (gtlt === '>' || gtlt === '<') {
                    // nothing is allowed
                    ret = '<0.0.0';
                  } else {
                    // nothing is forbidden
                    ret = '*';
                  }
                } else if (gtlt && anyX) {
                  // replace X with 0
                  if (xm)
                    m = 0;
                  if (xp)
                    p = 0;
                  if (gtlt === '>') {
                    // >1 => >=2.0.0
                    // >1.2 => >=1.3.0
                    // >1.2.3 => >= 1.2.4
                    gtlt = '>=';
                    if (xm) {
                      M = +M + 1;
                      m = 0;
                      p = 0;
                    } else if (xp) {
                      m = +m + 1;
                      p = 0;
                    }
                  } else if (gtlt === '<=') {
                    // <=0.7.x is actually <0.8.0, since any 0.7.x should
                    // pass.  Similarly, <=7.x is actually <8.0.0, etc.
                    gtlt = '<';
                    if (xm)
                      M = +M + 1;
                    else
                      m = +m + 1;
                  }
                  ret = gtlt + M + '.' + m + '.' + p;
                } else if (xm) {
                  ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0';
                } else if (xp) {
                  ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0';
                }
                debug('xRange return', ret);
                return ret;
              });
            }
            // Because * is AND-ed with everything else in the comparator,
            // and '' means "any version", just remove the *s entirely.
            function replaceStars(comp, loose) {
              debug('replaceStars', comp, loose);
              // Looseness is ignored here.  star is always as loose as it gets!
              return comp.trim().replace(re[STAR], '');
            }
            // This function is passed to string.replace(re[HYPHENRANGE])
            // M, m, patch, prerelease, build
            // 1.2 - 3.4.5 => >=1.2.0 <=3.4.5
            // 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do
            // 1.2 - 3.4 => >=1.2.0 <3.5.0
            function hyphenReplace($0, from, fM, fm, fp, fpr, fb, to, tM, tm, tp, tpr, tb) {
              if (isX(fM))
                from = '';
              else if (isX(fm))
                from = '>=' + fM + '.0.0';
              else if (isX(fp))
                from = '>=' + fM + '.' + fm + '.0';
              else
                from = '>=' + from;
              if (isX(tM))
                to = '';
              else if (isX(tm))
                to = '<' + (+tM + 1) + '.0.0';
              else if (isX(tp))
                to = '<' + tM + '.' + (+tm + 1) + '.0';
              else if (tpr)
                to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr;
              else
                to = '<=' + to;
              return (from + ' ' + to).trim();
            }
            // if ANY of the sets match ALL of its comparators, then pass
            Range.prototype.test = function (version) {
              if (!version)
                return false;
              if (typeof version === 'string')
                version = new SemVer(version, this.loose);
              for (var i = 0; i < this.set.length; i++) {
                if (testSet(this.set[i], version))
                  return true;
              }
              return false;
            };
            function testSet(set, version) {
              for (var i = 0; i < set.length; i++) {
                if (!set[i].test(version))
                  return false;
              }
              if (version.prerelease.length) {
                // Find the set of versions that are allowed to have prereleases
                // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0
                // That should allow `1.2.3-pr.2` to pass.
                // However, `1.2.4-alpha.notready` should NOT be allowed,
                // even though it's within the range set by the comparators.
                for (var i = 0; i < set.length; i++) {
                  debug(set[i].semver);
                  if (set[i].semver === ANY)
                    continue;
                  if (set[i].semver.prerelease.length > 0) {
                    var allowed = set[i].semver;
                    if (allowed.major === version.major && allowed.minor === version.minor && allowed.patch === version.patch)
                      return true;
                  }
                }
                // Version has a -pre, but it's not one of the ones we like.
                return false;
              }
              return true;
            }
            exports.satisfies = satisfies;
            function satisfies(version, range, loose) {
              try {
                range = new Range(range, loose);
              } catch (er) {
                return false;
              }
              return range.test(version);
            }
            exports.maxSatisfying = maxSatisfying;
            function maxSatisfying(versions, range, loose) {
              return versions.filter(function (version) {
                return satisfies(version, range, loose);
              }).sort(function (a, b) {
                return rcompare(a, b, loose);
              })[0] || null;
            }
            exports.minSatisfying = minSatisfying;
            function minSatisfying(versions, range, loose) {
              return versions.filter(function (version) {
                return satisfies(version, range, loose);
              }).sort(function (a, b) {
                return compare(a, b, loose);
              })[0] || null;
            }
            exports.validRange = validRange;
            function validRange(range, loose) {
              try {
                // Return '*' instead of '' so that truthiness works.
                // This will throw if it's invalid anyway
                return new Range(range, loose).range || '*';
              } catch (er) {
                return null;
              }
            }
            // Determine if version is less than all the versions possible in the range
            exports.ltr = ltr;
            function ltr(version, range, loose) {
              return outside(version, range, '<', loose);
            }
            // Determine if version is greater than all the versions possible in the range.
            exports.gtr = gtr;
            function gtr(version, range, loose) {
              return outside(version, range, '>', loose);
            }
            exports.outside = outside;
            function outside(version, range, hilo, loose) {
              version = new SemVer(version, loose);
              range = new Range(range, loose);
              var gtfn, ltefn, ltfn, comp, ecomp;
              switch (hilo) {
              case '>':
                gtfn = gt;
                ltefn = lte;
                ltfn = lt;
                comp = '>';
                ecomp = '>=';
                break;
              case '<':
                gtfn = lt;
                ltefn = gte;
                ltfn = gt;
                comp = '<';
                ecomp = '<=';
                break;
              default:
                throw new TypeError('Must provide a hilo val of "<" or ">"');
              }
              // If it satisifes the range it is not outside
              if (satisfies(version, range, loose)) {
                return false;
              }
              // From now on, variable terms are as if we're in "gtr" mode.
              // but note that everything is flipped for the "ltr" function.
              for (var i = 0; i < range.set.length; ++i) {
                var comparators = range.set[i];
                var high = null;
                var low = null;
                comparators.forEach(function (comparator) {
                  if (comparator.semver === ANY) {
                    comparator = new Comparator('>=0.0.0');
                  }
                  high = high || comparator;
                  low = low || comparator;
                  if (gtfn(comparator.semver, high.semver, loose)) {
                    high = comparator;
                  } else if (ltfn(comparator.semver, low.semver, loose)) {
                    low = comparator;
                  }
                });
                // If the edge version comparator has a operator then our version
                // isn't outside it
                if (high.operator === comp || high.operator === ecomp) {
                  return false;
                }
                // If the lowest version comparator has an operator and our version
                // is less than it then it isn't higher than the range
                if ((!low.operator || low.operator === comp) && ltefn(version, low.semver)) {
                  return false;
                } else if (low.operator === ecomp && ltfn(version, low.semver)) {
                  return false;
                }
              }
              return true;
            }
            exports.prerelease = prerelease;
            function prerelease(version, loose) {
              var parsed = parse(version, loose);
              return parsed && parsed.prerelease.length ? parsed.prerelease : null;
            }
          }.call(this, require('_process')));
        },
        { '_process': 1 }
      ]
    }, {}, [2]));
    !function (e) {
      var toExport;
      if ('object' == typeof exports && 'undefined' != typeof module) {
        module.exports = e();
        toExport = module.exports;
      } else if ('function' == typeof define && define.amd)
        define([], e);
      else {
        var r;
        r = 'undefined' != typeof window ? window : 'undefined' != typeof global ? global : 'undefined' != typeof self ? self : this, r.Ajv = e();
        toExport = r.Ajv;
      }
      exportModule('LYNX.Utils.ThirdParty.ajv', toExport);
    }(function () {
      var e;
      return function e(r, t, a) {
        function s(i, n) {
          if (!t[i]) {
            if (!r[i]) {
              var l = 'function' == typeof require && require;
              if (!n && l)
                return l(i, !0);
              if (o)
                return o(i, !0);
              var c = new Error('Cannot find module \'' + i + '\'');
              throw c.code = 'MODULE_NOT_FOUND', c;
            }
            var h = t[i] = { exports: {} };
            r[i][0].call(h.exports, function (e) {
              var t = r[i][1][e];
              return s(t ? t : e);
            }, h, h.exports, e, r, t, a);
          }
          return t[i].exports;
        }
        for (var o = 'function' == typeof require && require, i = 0; i < a.length; i++)
          s(a[i]);
        return s;
      }({
        1: [
          function (e, r, t) {
            'use strict';
            function a(e, r) {
              r !== !1 && (r = !0);
              var t, s = e.async, o = e.transpile;
              switch (typeof o) {
              case 'string':
                var i = m[o];
                if (!i)
                  throw new Error('bad transpiler: ' + o);
                return e._transpileFunc = i(e, r);
              case 'undefined':
              case 'boolean':
                if ('string' == typeof s) {
                  if (t = p[s], !t)
                    throw new Error('bad async mode: ' + s);
                  return e.transpile = t(e, r);
                }
                for (var n = 0; n < v.length; n++) {
                  var l = v[n];
                  if (a(l, !1))
                    return d.copy(l, e), e.transpile;
                }
                throw new Error('generators, nodent and regenerator are not available');
              case 'function':
                return e._transpileFunc = e.transpile;
              default:
                throw new Error('bad transpiler: ' + o);
              }
            }
            function s(e, r) {
              try {
                return new Function('(function*(){})()')(), !0;
              } catch (e) {
                if (r)
                  throw new Error('generators not supported');
              }
            }
            function o(e, r) {
              try {
                return new Function('(async function(){})()')(), !0;
              } catch (e) {
                if (r)
                  throw new Error('es7 async functions not supported');
              }
            }
            function i(r, t) {
              try {
                if (!u) {
                  var a = 'regenerator';
                  u = e(a), u.runtime();
                }
                return r.async && r.async !== !0 || (r.async = 'es7'), n;
              } catch (e) {
                if (t)
                  throw new Error('regenerator not available');
              }
            }
            function n(e) {
              return u.compile(e).code;
            }
            function l(r, t) {
              try {
                if (!f) {
                  var a = 'nodent';
                  f = e(a)({
                    log: !1,
                    dontInstallRequireHook: !0
                  });
                }
                return 'es7' != r.async && (r.async && r.async !== !0 && console.warn('nodent transpiles only es7 async functions'), r.async = 'es7'), c;
              } catch (e) {
                if (t)
                  throw new Error('nodent not available');
              }
            }
            function c(e) {
              return f.compile(e, '', {
                promises: !0,
                sourcemap: !1
              }).code;
            }
            function h(e, r) {
              function t(e, r, a) {
                function o(a) {
                  function o(a, o) {
                    if (a)
                      return r(a);
                    if (!s._refs[i] && !s._schemas[i])
                      try {
                        s.addSchema(o, i);
                      } catch (e) {
                        return void r(e);
                      }
                    t(e, r);
                  }
                  var i = a.missingSchema;
                  if (s._refs[i] || s._schemas[i])
                    return r(new Error('Schema ' + i + ' is loaded but ' + a.missingRef + ' cannot be resolved'));
                  var n = s._loadingSchemas[i];
                  n ? 'function' == typeof n ? s._loadingSchemas[i] = [
                    n,
                    o
                  ] : n[n.length] = o : (s._loadingSchemas[i] = o, s._opts.loadSchema(i, function (e, r) {
                    var t = s._loadingSchemas[i];
                    if (delete s._loadingSchemas[i], 'function' == typeof t)
                      t(e, r);
                    else
                      for (var a = 0; a < t.length; a++)
                        t[a](e, r);
                  }));
                }
                function i(e, t) {
                  return a ? void setTimeout(function () {
                    r(e, t);
                  }) : r(e, t);
                }
                var n;
                try {
                  n = s.compile(e);
                } catch (e) {
                  return void (e.missingSchema ? o(e) : i(e));
                }
                i(null, n);
              }
              var a, s = this;
              try {
                a = this._addSchema(e);
              } catch (e) {
                return void setTimeout(function () {
                  r(e);
                });
              }
              if (a.validate)
                setTimeout(function () {
                  r(null, a.validate);
                });
              else {
                if ('function' != typeof this._opts.loadSchema)
                  throw new Error('options.loadSchema should be a function');
                t(e, r, !0);
              }
            }
            r.exports = {
              setup: a,
              compile: h
            };
            var u, f, d = e('./compile/util'), p = {
                '*': s,
                'co*': s,
                es7: o
              }, m = {
                nodent: l,
                regenerator: i
              }, v = [
                { async: 'co*' },
                {
                  async: 'es7',
                  transpile: 'nodent'
                },
                {
                  async: 'co*',
                  transpile: 'regenerator'
                }
              ];
          },
          { './compile/util': 11 }
        ],
        2: [
          function (e, r, t) {
            'use strict';
            var a = r.exports = function () {
              this._cache = {};
            };
            a.prototype.put = function (e, r) {
              this._cache[e] = r;
            }, a.prototype.get = function (e) {
              return this._cache[e];
            }, a.prototype.del = function (e) {
              delete this._cache[e];
            }, a.prototype.clear = function () {
              this._cache = {};
            };
          },
          {}
        ],
        3: [
          function (e, r, t) {
            'use strict';
            r.exports = {
              $ref: e('../dotjs/ref'),
              allOf: e('../dotjs/allOf'),
              anyOf: e('../dotjs/anyOf'),
              dependencies: e('../dotjs/dependencies'),
              enum: e('../dotjs/enum'),
              format: e('../dotjs/format'),
              items: e('../dotjs/items'),
              maximum: e('../dotjs/_limit'),
              minimum: e('../dotjs/_limit'),
              maxItems: e('../dotjs/_limitItems'),
              minItems: e('../dotjs/_limitItems'),
              maxLength: e('../dotjs/_limitLength'),
              minLength: e('../dotjs/_limitLength'),
              maxProperties: e('../dotjs/_limitProperties'),
              minProperties: e('../dotjs/_limitProperties'),
              multipleOf: e('../dotjs/multipleOf'),
              not: e('../dotjs/not'),
              oneOf: e('../dotjs/oneOf'),
              pattern: e('../dotjs/pattern'),
              properties: e('../dotjs/properties'),
              required: e('../dotjs/required'),
              uniqueItems: e('../dotjs/uniqueItems'),
              validate: e('../dotjs/validate')
            };
          },
          {
            '../dotjs/_limit': 14,
            '../dotjs/_limitItems': 15,
            '../dotjs/_limitLength': 16,
            '../dotjs/_limitProperties': 17,
            '../dotjs/allOf': 18,
            '../dotjs/anyOf': 19,
            '../dotjs/dependencies': 22,
            '../dotjs/enum': 23,
            '../dotjs/format': 24,
            '../dotjs/items': 25,
            '../dotjs/multipleOf': 26,
            '../dotjs/not': 27,
            '../dotjs/oneOf': 28,
            '../dotjs/pattern': 29,
            '../dotjs/properties': 31,
            '../dotjs/ref': 32,
            '../dotjs/required': 33,
            '../dotjs/uniqueItems': 35,
            '../dotjs/validate': 36
          }
        ],
        4: [
          function (e, r, t) {
            'use strict';
            r.exports = function e(r, t) {
              if (r === t)
                return !0;
              var a, s = Array.isArray(r), o = Array.isArray(t);
              if (s && o) {
                if (r.length != t.length)
                  return !1;
                for (a = 0; a < r.length; a++)
                  if (!e(r[a], t[a]))
                    return !1;
                return !0;
              }
              if (s != o)
                return !1;
              if (r && t && 'object' == typeof r && 'object' == typeof t) {
                var i = Object.keys(r);
                if (i.length !== Object.keys(t).length)
                  return !1;
                var n = r instanceof Date, l = t instanceof Date;
                if (n && l)
                  return r.getTime() == t.getTime();
                if (n != l)
                  return !1;
                var c = r instanceof RegExp, h = t instanceof RegExp;
                if (c && h)
                  return r.toString() == t.toString();
                if (c != h)
                  return !1;
                for (a = 0; a < i.length; a++)
                  if (!Object.prototype.hasOwnProperty.call(t, i[a]))
                    return !1;
                for (a = 0; a < i.length; a++)
                  if (!e(r[i[a]], t[i[a]]))
                    return !1;
                return !0;
              }
              return !1;
            };
          },
          {}
        ],
        5: [
          function (e, r, t) {
            'use strict';
            function a(e) {
              e = 'full' == e ? 'full' : 'fast';
              var r = d.copy(a[e]);
              for (var t in a.compare)
                r[t] = {
                  validate: r[t],
                  compare: a.compare[t]
                };
              return r;
            }
            function s(e) {
              var r = e.match(p);
              if (!r)
                return !1;
              var t = +r[1], a = +r[2];
              return t >= 1 && t <= 12 && a >= 1 && a <= m[t];
            }
            function o(e, r) {
              var t = e.match(v);
              if (!t)
                return !1;
              var a = t[1], s = t[2], o = t[3], i = t[5];
              return a <= 23 && s <= 59 && o <= 59 && (!r || i);
            }
            function i(e) {
              var r = e.split(w);
              return 2 == r.length && s(r[0]) && o(r[1], !0);
            }
            function n(e) {
              return e.length <= 255 && y.test(e);
            }
            function l(e) {
              return j.test(e) && g.test(e);
            }
            function c(e) {
              try {
                return new RegExp(e), !0;
              } catch (e) {
                return !1;
              }
            }
            function h(e, r) {
              if (e && r)
                return e > r ? 1 : e < r ? -1 : e === r ? 0 : void 0;
            }
            function u(e, r) {
              if (e && r && (e = e.match(v), r = r.match(v), e && r))
                return e = e[1] + e[2] + e[3] + (e[4] || ''), r = r[1] + r[2] + r[3] + (r[4] || ''), e > r ? 1 : e < r ? -1 : e === r ? 0 : void 0;
            }
            function f(e, r) {
              if (e && r) {
                e = e.split(w), r = r.split(w);
                var t = h(e[0], r[0]);
                if (void 0 !== t)
                  return t || u(e[1], r[1]);
              }
            }
            var d = e('./util'), p = /^\d\d\d\d-(\d\d)-(\d\d)$/, m = [
                0,
                31,
                29,
                31,
                30,
                31,
                30,
                31,
                31,
                30,
                31,
                30,
                31
              ], v = /^(\d\d):(\d\d):(\d\d)(\.\d+)?(z|[+-]\d\d:\d\d)?$/i, y = /^[0-9a-z](?:(?:[-0-9a-z]{0,61})?[0-9a-z])?(\.[0-9a-z](?:(?:[-0-9a-z]{0,61})?[0-9a-z])?)*$/i, g = /^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'()*+,;=:@]|%[0-9a-f]{2})*)*)(?:\?(?:[a-z0-9\-._~!$&'()*+,;=:@\/?]|%[0-9a-f]{2})*)?(?:\#(?:[a-z0-9\-._~!$&'()*+,;=:@\/?]|%[0-9a-f]{2})*)?$/i, P = /^(?:urn\:uuid\:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i, E = /^(?:\/(?:[^~\/]|~0|~1)*)*$|^\#(?:\/(?:[a-z0-9_\-\.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i, b = /^(?:0|[1-9][0-9]*)(?:\#|(?:\/(?:[^~\/]|~0|~1)*)*)$/;
            r.exports = a, a.fast = {
              date: /^\d\d\d\d-[0-1]\d-[0-3]\d$/,
              time: /^[0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:z|[+-]\d\d:\d\d)?$/i,
              'date-time': /^\d\d\d\d-[0-1]\d-[0-3]\d[t\s][0-2]\d:[0-5]\d:[0-5]\d(?:\.\d+)?(?:z|[+-]\d\d:\d\d)$/i,
              uri: /^(?:[a-z][a-z0-9+-.]*)?(?:\:|\/)\/?[^\s]*$/i,
              email: /^[a-z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i,
              hostname: y,
              ipv4: /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/,
              ipv6: /^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i,
              regex: c,
              uuid: P,
              'json-pointer': E,
              'relative-json-pointer': b
            }, a.full = {
              date: s,
              time: o,
              'date-time': i,
              uri: l,
              email: /^[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&''*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i,
              hostname: n,
              ipv4: /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/,
              ipv6: /^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i,
              regex: c,
              uuid: P,
              'json-pointer': E,
              'relative-json-pointer': b
            }, a.compare = {
              date: h,
              time: u,
              'date-time': f
            };
            var w = /t|\s/i, j = /\/|\:/;
          },
          { './util': 11 }
        ],
        6: [
          function (e, r, t) {
            'use strict';
            function a() {
              if (void 0 === d) {
                var r = 'js-beautify';
                try {
                  d = e(r).js_beautify;
                } catch (e) {
                  d = !1;
                }
              }
            }
            function s(e, r, t, n) {
              function j() {
                var e = N.validate, r = e.apply(null, arguments);
                return j.errors = e.errors, r;
              }
              function S(e, t, o, i) {
                var n = !t || t && t.schema == e;
                if (t.schema != r.schema)
                  return s.call(k, e, t, o, i);
                var v = e.$async === !0;
                v && !q.transpile && y.setup(q);
                var j = g({
                  isTop: !0,
                  schema: e,
                  isRoot: n,
                  baseId: i,
                  root: t,
                  schemaPath: '',
                  errSchemaPath: '#',
                  errorPath: '""',
                  RULES: H,
                  validate: g,
                  util: m,
                  resolve: p,
                  resolveRef: $,
                  usePattern: R,
                  useDefault: I,
                  useCustomRule: A,
                  opts: q,
                  formats: F,
                  self: k
                });
                j = f(D, h) + f(Q, l) + f(V, c) + f(U, u) + j, q.beautify && (a(), d ? j = d(j, q.beautify) : console.error('"npm install js-beautify" to use beautify option'));
                var S, x, _ = q._transpileFunc;
                try {
                  x = v && _ ? _(j) : j;
                  var O = new Function('self', 'RULES', 'formats', 'root', 'refVal', 'defaults', 'customRules', 'co', 'equal', 'ucs2length', 'ValidationError', x);
                  S = O(k, H, F, r, D, V, U, P, b, E, w), D[0] = S;
                } catch (e) {
                  throw console.error('Error compiling schema, function code:', x), e;
                }
                return S.schema = e, S.errors = null, S.refs = L, S.refVal = D, S.root = n ? S : t, v && (S.$async = !0), T && (S.sourceCode = j), q.sourceCode === !0 && (S.source = {
                  patterns: Q,
                  defaults: V
                }), S;
              }
              function $(e, a, o) {
                a = p.url(e, a);
                var i, n, l = L[a];
                if (void 0 !== l)
                  return i = D[l], n = 'refVal[' + l + ']', O(i, n);
                if (!o && r.refs) {
                  var c = r.refs[a];
                  if (void 0 !== c)
                    return i = r.refVal[c], n = x(a, i), O(i, n);
                }
                n = x(a);
                var h = p.call(k, S, r, a);
                if (!h) {
                  var u = t && t[a];
                  u && (h = p.inlineRef(u, q.inlineRefs) ? u : s.call(k, u, r, t, e));
                }
                return h ? (_(a, h), O(h, n)) : void 0;
              }
              function x(e, r) {
                var t = D.length;
                return D[t] = r, L[e] = t, 'refVal' + t;
              }
              function _(e, r) {
                var t = L[e];
                D[t] = r;
              }
              function O(e, r) {
                return 'object' == typeof e ? {
                  code: r,
                  schema: e,
                  inline: !0
                } : {
                  code: r,
                  $async: e && e.$async
                };
              }
              function R(e) {
                var r = C[e];
                return void 0 === r && (r = C[e] = Q.length, Q[r] = e), 'pattern' + r;
              }
              function I(e) {
                switch (typeof e) {
                case 'boolean':
                case 'number':
                  return '' + e;
                case 'string':
                  return m.toQuotedString(e);
                case 'object':
                  if (null === e)
                    return 'null';
                  var r = v(e), t = z[r];
                  return void 0 === t && (t = z[r] = V.length, V[t] = e), 'default' + t;
                }
              }
              function A(e, r, t, a) {
                var s = e.definition.validateSchema;
                if (s && k._opts.validateSchema !== !1) {
                  var o = s(r);
                  if (!o) {
                    var i = 'keyword schema is invalid: ' + k.errorsText(s.errors);
                    if ('log' != k._opts.validateSchema)
                      throw new Error(i);
                    console.error(i);
                  }
                }
                var n, l = e.definition.compile, c = e.definition.inline, h = e.definition.macro;
                l ? n = l.call(k, r, t, a) : h ? (n = h.call(k, r, t, a), q.validateSchema !== !1 && k.validateSchema(n, !0)) : n = c ? c.call(k, a, e.keyword, r, t) : e.definition.validate;
                var u = U.length;
                return U[u] = n, {
                  code: 'customRule' + u,
                  validate: n
                };
              }
              var k = this, q = this._opts, D = [void 0], L = {}, Q = [], C = {}, V = [], z = {}, U = [], T = q.sourceCode !== !1;
              r = r || {
                schema: e,
                refVal: D,
                refs: L
              };
              var M = o.call(this, e, r, n), N = this._compilations[M.index];
              if (M.compiling)
                return N.callValidate = j;
              var F = this._formats, H = this.RULES;
              try {
                var J = S(e, r, t, n);
                N.validate = J;
                var G = N.callValidate;
                return G && (G.schema = J.schema, G.errors = null, G.refs = J.refs, G.refVal = J.refVal, G.root = J.root, G.$async = J.$async, T && (G.sourceCode = J.sourceCode)), J;
              } finally {
                i.call(this, e, r, n);
              }
            }
            function o(e, r, t) {
              var a = n.call(this, e, r, t);
              return a >= 0 ? {
                index: a,
                compiling: !0
              } : (a = this._compilations.length, this._compilations[a] = {
                schema: e,
                root: r,
                baseId: t
              }, {
                index: a,
                compiling: !1
              });
            }
            function i(e, r, t) {
              var a = n.call(this, e, r, t);
              a >= 0 && this._compilations.splice(a, 1);
            }
            function n(e, r, t) {
              for (var a = 0; a < this._compilations.length; a++) {
                var s = this._compilations[a];
                if (s.schema == e && s.root == r && s.baseId == t)
                  return a;
              }
              return -1;
            }
            function l(e, r) {
              return 'var pattern' + e + ' = new RegExp(' + m.toQuotedString(r[e]) + ');';
            }
            function c(e) {
              return 'var default' + e + ' = defaults[' + e + '];';
            }
            function h(e, r) {
              return r[e] ? 'var refVal' + e + ' = refVal[' + e + '];' : '';
            }
            function u(e) {
              return 'var customRule' + e + ' = customRules[' + e + '];';
            }
            function f(e, r) {
              if (!e.length)
                return '';
              for (var t = '', a = 0; a < e.length; a++)
                t += r(a, e);
              return t;
            }
            var d, p = e('./resolve'), m = e('./util'), v = e('json-stable-stringify'), y = e('../async'), g = e('../dotjs/validate'), P = e('co'), E = m.ucs2length, b = e('./equal'), w = e('./validation_error');
            r.exports = s;
          },
          {
            '../async': 1,
            '../dotjs/validate': 36,
            './equal': 4,
            './resolve': 7,
            './util': 11,
            './validation_error': 12,
            co: 47,
            'json-stable-stringify': 48
          }
        ],
        7: [
          function (e, r, t) {
            'use strict';
            function a(e, r, t) {
              var o = this._refs[t];
              if ('string' == typeof o) {
                if (!this._refs[o])
                  return a.call(this, e, r, o);
                o = this._refs[o];
              }
              if (o = o || this._schemas[t], o instanceof g)
                return n(o.schema, this._opts.inlineRefs) ? o.schema : o.validate || this._compile(o);
              var i, l, c, h = s.call(this, r, t);
              return h && (i = h.schema, r = h.root, c = h.baseId), i instanceof g ? l = i.validate || e.call(this, i.schema, r, void 0, c) : i && (l = n(i, this._opts.inlineRefs) ? i : e.call(this, i, r, void 0, c)), l;
            }
            function s(e, r) {
              var t = m.parse(r, !1, !0), a = u(t), s = h(e.schema.id);
              if (a !== s) {
                var n = f(a), l = this._refs[n];
                if ('string' == typeof l)
                  return o.call(this, e, l, t);
                if (l instanceof g)
                  l.validate || this._compile(l), e = l;
                else {
                  if (l = this._schemas[n], !(l instanceof g))
                    return;
                  if (l.validate || this._compile(l), n == f(r))
                    return {
                      schema: l,
                      root: e,
                      baseId: s
                    };
                  e = l;
                }
                if (!e.schema)
                  return;
                s = h(e.schema.id);
              }
              return i.call(this, t, s, e.schema, e);
            }
            function o(e, r, t) {
              var a = s.call(this, e, r);
              if (a) {
                var o = a.schema, n = a.baseId;
                return e = a.root, o.id && (n = d(n, o.id)), i.call(this, t, n, o, e);
              }
            }
            function i(e, r, t, a) {
              if (e.hash = e.hash || '', '#/' == e.hash.slice(0, 2)) {
                for (var o = e.hash.split('/'), i = 1; i < o.length; i++) {
                  var n = o[i];
                  if (n) {
                    if (n = y.unescapeFragment(n), t = t[n], !t)
                      break;
                    if (t.id && !P[n] && (r = d(r, t.id)), t.$ref) {
                      var l = d(r, t.$ref), c = s.call(this, a, l);
                      c && (t = c.schema, a = c.root, r = c.baseId);
                    }
                  }
                }
                return t && t != a.schema ? {
                  schema: t,
                  root: a,
                  baseId: r
                } : void 0;
              }
            }
            function n(e, r) {
              return r !== !1 && (void 0 === r || r === !0 ? l(e) : r ? c(e) <= r : void 0);
            }
            function l(e) {
              var r;
              if (Array.isArray(e)) {
                for (var t = 0; t < e.length; t++)
                  if (r = e[t], 'object' == typeof r && !l(r))
                    return !1;
              } else
                for (var a in e) {
                  if ('$ref' == a)
                    return !1;
                  if (r = e[a], 'object' == typeof r && !l(r))
                    return !1;
                }
              return !0;
            }
            function c(e) {
              var r, t = 0;
              if (Array.isArray(e)) {
                for (var a = 0; a < e.length; a++)
                  if (r = e[a], 'object' == typeof r && (t += c(r)), t == 1 / 0)
                    return 1 / 0;
              } else
                for (var s in e) {
                  if ('$ref' == s)
                    return 1 / 0;
                  if (E[s])
                    t++;
                  else if (r = e[s], 'object' == typeof r && (t += c(r) + 1), t == 1 / 0)
                    return 1 / 0;
                }
              return t;
            }
            function h(e, r) {
              r !== !1 && (e = f(e));
              var t = m.parse(e, !1, !0);
              return u(t);
            }
            function u(e) {
              var r = e.protocol || '//' == e.href.slice(0, 2) ? '//' : '';
              return (e.protocol || '') + r + (e.host || '') + (e.path || '') + '#';
            }
            function f(e) {
              return e ? e.replace(b, '') : '';
            }
            function d(e, r) {
              return r = f(r), m.resolve(e, r);
            }
            function p(e) {
              function r(e, t, s) {
                if (Array.isArray(e))
                  for (var o = 0; o < e.length; o++)
                    r.call(this, e[o], t + '/' + o, s);
                else if (e && 'object' == typeof e) {
                  if ('string' == typeof e.id) {
                    var i = s = s ? m.resolve(s, e.id) : e.id;
                    i = f(i);
                    var n = this._refs[i];
                    if ('string' == typeof n && (n = this._refs[n]), n && n.schema) {
                      if (!v(e, n.schema))
                        throw new Error('id "' + i + '" resolves to more than one schema');
                    } else if (i != f(t))
                      if ('#' == i[0]) {
                        if (a[i] && !v(e, a[i]))
                          throw new Error('id "' + i + '" resolves to more than one schema');
                        a[i] = e;
                      } else
                        this._refs[i] = t;
                  }
                  for (var l in e)
                    r.call(this, e[l], t + '/' + y.escapeFragment(l), s);
                }
              }
              var t = f(e.id), a = {};
              return r.call(this, e, h(t, !1), t), a;
            }
            var m = e('url'), v = e('./equal'), y = e('./util'), g = e('./schema_obj');
            r.exports = a, a.normalizeId = f, a.fullPath = h, a.url = d, a.ids = p, a.inlineRef = n, a.schema = s;
            var P = y.toHash([
                'properties',
                'patternProperties',
                'enum',
                'dependencies',
                'definitions'
              ]), E = y.toHash([
                'type',
                'format',
                'pattern',
                'maxLength',
                'minLength',
                'maxProperties',
                'minProperties',
                'maxItems',
                'minItems',
                'maximum',
                'minimum',
                'uniqueItems',
                'multipleOf',
                'required',
                'enum'
              ]), b = /#\/?$/;
          },
          {
            './equal': 4,
            './schema_obj': 9,
            './util': 11,
            url: 45
          }
        ],
        8: [
          function (e, r, t) {
            'use strict';
            var a = e('./_rules'), s = e('./util').toHash;
            r.exports = function () {
              var e = [
                  {
                    type: 'number',
                    rules: [
                      'maximum',
                      'minimum',
                      'multipleOf'
                    ]
                  },
                  {
                    type: 'string',
                    rules: [
                      'maxLength',
                      'minLength',
                      'pattern',
                      'format'
                    ]
                  },
                  {
                    type: 'array',
                    rules: [
                      'maxItems',
                      'minItems',
                      'uniqueItems',
                      'items'
                    ]
                  },
                  {
                    type: 'object',
                    rules: [
                      'maxProperties',
                      'minProperties',
                      'required',
                      'dependencies',
                      'properties'
                    ]
                  },
                  {
                    rules: [
                      '$ref',
                      'enum',
                      'not',
                      'anyOf',
                      'oneOf',
                      'allOf'
                    ]
                  }
                ], r = [
                  'type',
                  'additionalProperties',
                  'patternProperties'
                ], t = [
                  'additionalItems',
                  '$schema',
                  'id',
                  'title',
                  'description',
                  'default'
                ], o = [
                  'number',
                  'integer',
                  'string',
                  'array',
                  'object',
                  'boolean',
                  'null'
                ];
              return e.all = s(r), e.forEach(function (t) {
                t.rules = t.rules.map(function (t) {
                  r.push(t);
                  var s = e.all[t] = {
                    keyword: t,
                    code: a[t]
                  };
                  return s;
                });
              }), e.keywords = s(r.concat(t)), e.types = s(o), e.custom = {}, e;
            };
          },
          {
            './_rules': 3,
            './util': 11
          }
        ],
        9: [
          function (e, r, t) {
            'use strict';
            function a(e) {
              s.copy(e, this);
            }
            var s = e('./util');
            r.exports = a;
          },
          { './util': 11 }
        ],
        10: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e) {
              for (var r, t = 0, a = e.length, s = 0; s < a;)
                t++, r = e.charCodeAt(s++), r >= 55296 && r <= 56319 && s < a && (r = e.charCodeAt(s), 56320 == (64512 & r) && s++);
              return t;
            };
          },
          {}
        ],
        11: [
          function (e, r, t) {
            'use strict';
            function a(e, r) {
              r = r || {};
              for (var t in e)
                r[t] = e[t];
              return r;
            }
            function s(e, r, t) {
              var a = t ? ' !== ' : ' === ', s = t ? ' || ' : ' && ', o = t ? '!' : '', i = t ? '' : '!';
              switch (e) {
              case 'null':
                return r + a + 'null';
              case 'array':
                return o + 'Array.isArray(' + r + ')';
              case 'object':
                return '(' + o + r + s + 'typeof ' + r + a + '"object"' + s + i + 'Array.isArray(' + r + '))';
              case 'integer':
                return '(typeof ' + r + a + '"number"' + s + i + '(' + r + ' % 1)' + s + r + a + r + ')';
              default:
                return 'typeof ' + r + a + '"' + e + '"';
              }
            }
            function o(e, r) {
              switch (e.length) {
              case 1:
                return s(e[0], r, !0);
              default:
                var t = '', a = n(e);
                a.array && a.object && (t = a.null ? '(' : '(!' + r + ' || ', t += 'typeof ' + r + ' !== "object")', delete a.null, delete a.array, delete a.object), a.number && delete a.integer;
                for (var o in a)
                  t += (t ? ' && ' : '') + s(o, r, !0);
                return t;
              }
            }
            function i(e, r) {
              if (Array.isArray(r)) {
                for (var t = [], a = 0; a < r.length; a++) {
                  var s = r[a];
                  $[s] ? t[t.length] = s : 'array' === e && 'array' === s && (t[t.length] = s);
                }
                if (t.length)
                  return t;
              } else {
                if ($[r])
                  return [r];
                if ('array' === e && 'array' === r)
                  return ['array'];
              }
            }
            function n(e) {
              for (var r = {}, t = 0; t < e.length; t++)
                r[e[t]] = !0;
              return r;
            }
            function l(e) {
              return 'number' == typeof e ? '[' + e + ']' : x.test(e) ? '.' + e : '[\'' + c(e) + '\']';
            }
            function c(e) {
              return e.replace(_, '\\$&').replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/\f/g, '\\f').replace(/\t/g, '\\t');
            }
            function h(e, r) {
              r += '[^0-9]';
              var t = e.match(new RegExp(r, 'g'));
              return t ? t.length : 0;
            }
            function u(e, r, t) {
              return r += '([^0-9])', t = t.replace(/\$/g, '$$$$'), e.replace(new RegExp(r, 'g'), t + '$1');
            }
            function f(e) {
              return e.replace(O, '').replace(R, '').replace(I, 'if (!($1))');
            }
            function d(e, r) {
              var t = e.match(A);
              return t && 2 === t.length ? r ? e.replace(q, '').replace(Q, C) : e.replace(k, '').replace(D, L) : e;
            }
            function p(e, r) {
              for (var t in e)
                if (r[t])
                  return !0;
            }
            function m(e, r, t) {
              for (var a in e)
                if (a != t && r[a])
                  return !0;
            }
            function v(e) {
              return '\'' + c(e) + '\'';
            }
            function y(e, r, t, a) {
              var s = t ? '\'/\' + ' + r + (a ? '' : '.replace(/~/g, \'~0\').replace(/\\//g, \'~1\')') : a ? '\'[\' + ' + r + ' + \']\'' : '\'[\\\'\' + ' + r + ' + \'\\\']\'';
              return E(e, s);
            }
            function g(e, r, t) {
              var a = v(t ? '/' + j(r) : l(r));
              return E(e, a);
            }
            function P(e, r, t) {
              var a, s, o, i;
              if ('' === e)
                return 'rootData';
              if ('/' == e[0]) {
                if (!V.test(e))
                  throw new Error('Invalid JSON-pointer: ' + e);
                s = e, o = 'rootData';
              } else {
                if (i = e.match(z), !i)
                  throw new Error('Invalid JSON-pointer: ' + e);
                if (a = +i[1], s = i[2], '#' == s) {
                  if (a >= r)
                    throw new Error('Cannot access property/index ' + a + ' levels up, current level is ' + r);
                  return t[r - a];
                }
                if (a > r)
                  throw new Error('Cannot access data ' + a + ' levels up, current level is ' + r);
                if (o = 'data' + (r - a || ''), !s)
                  return o;
              }
              for (var n = o, c = s.split('/'), h = 0; h < c.length; h++) {
                var u = c[h];
                u && (o += l(S(u)), n += ' && ' + o);
              }
              return n;
            }
            function E(e, r) {
              return '""' == e ? r : (e + ' + ' + r).replace(/' \+ '/g, '');
            }
            function b(e) {
              return S(decodeURIComponent(e));
            }
            function w(e) {
              return encodeURIComponent(j(e));
            }
            function j(e) {
              return e.replace(/~/g, '~0').replace(/\//g, '~1');
            }
            function S(e) {
              return e.replace(/~1/g, '/').replace(/~0/g, '~');
            }
            r.exports = {
              copy: a,
              checkDataType: s,
              checkDataTypes: o,
              coerceToTypes: i,
              toHash: n,
              getProperty: l,
              escapeQuotes: c,
              ucs2length: e('./ucs2length'),
              varOccurences: h,
              varReplace: u,
              cleanUpCode: f,
              cleanUpVarErrors: d,
              schemaHasRules: p,
              schemaHasRulesExcept: m,
              stableStringify: e('json-stable-stringify'),
              toQuotedString: v,
              getPathExpr: y,
              getPath: g,
              getData: P,
              unescapeFragment: b,
              escapeFragment: w,
              escapeJsonPointer: j
            };
            var $ = n([
                'string',
                'number',
                'integer',
                'boolean',
                'null'
              ]), x = /^[a-z$_][a-z$_0-9]*$/i, _ = /'|\\/g, O = /else\s*{\s*}/g, R = /if\s*\([^)]+\)\s*\{\s*\}(?!\s*else)/g, I = /if\s*\(([^)]+)\)\s*\{\s*\}\s*else(?!\s*if)/g, A = /[^v\.]errors/g, k = /var errors = 0;|var vErrors = null;|validate.errors = vErrors;/g, q = /var errors = 0;|var vErrors = null;/g, D = 'return errors === 0;', L = 'validate.errors = null; return true;', Q = /if \(errors === 0\) return true;\s*else throw new ValidationError\(vErrors\);/, C = 'return true;', V = /^\/(?:[^~]|~0|~1)*$/, z = /^([0-9]+)(#|\/(?:[^~]|~0|~1)*)?$/;
          },
          {
            './ucs2length': 10,
            'json-stable-stringify': 48
          }
        ],
        12: [
          function (e, r, t) {
            'use strict';
            function a(e) {
              this.message = 'validation failed', this.errors = e, this.ajv = this.validation = !0;
            }
            r.exports = a, a.prototype = Object.create(Error.prototype), a.prototype.constructor = a;
          },
          {}
        ],
        13: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t, a = ' ', s = e.level, o = e.dataLevel, i = e.schema[r], n = e.schemaPath + e.util.getProperty(r), l = e.errSchemaPath + '/' + r, c = !e.opts.allErrors, h = 'data' + (o || ''), u = 'valid' + s;
              if (a += 'var ' + u + ' = undefined;', e.opts.format === !1)
                return a += ' ' + u + ' = true; ';
              var f = e.schema.format, d = e.opts.v5 && f.$data, p = '';
              if (d) {
                var m = e.util.getData(f.$data, o, e.dataPathArr), v = 'format' + s, y = 'compare' + s;
                a += ' var ' + v + ' = formats[' + m + '] , ' + y + ' = ' + v + ' && ' + v + '.compare;';
              } else {
                var v = e.formats[f];
                if (!v || !v.compare)
                  return a += '  ' + u + ' = true; ';
                var y = 'formats' + e.util.getProperty(f) + '.compare';
              }
              var g, P = 'formatMaximum' == r, E = 'formatExclusive' + (P ? 'Maximum' : 'Minimum'), b = e.schema[E], w = e.opts.v5 && b && b.$data, j = P ? '<' : '>', S = 'result' + s, $ = e.opts.v5 && i && i.$data;
              if ($ ? (a += ' var schema' + s + ' = ' + e.util.getData(i.$data, o, e.dataPathArr) + '; ', g = 'schema' + s) : g = i, w) {
                var x = e.util.getData(b.$data, o, e.dataPathArr), _ = 'exclusive' + s, O = 'op' + s, R = '\' + ' + O + ' + \'';
                a += ' var schemaExcl' + s + ' = ' + x + '; ', x = 'schemaExcl' + s, a += ' if (typeof ' + x + ' != \'boolean\' && ' + x + ' !== undefined) { ' + u + ' = false; ';
                var t = E, I = I || [];
                I.push(a), a = '', e.createErrors !== !1 ? (a += ' { keyword: \'' + (t || '_formatExclusiveLimit') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(l) + ' , params: {} ', e.opts.messages !== !1 && (a += ' , message: \'' + E + ' should be boolean\' '), e.opts.verbose && (a += ' , schema: validate.schema' + n + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + h + ' '), a += ' } ') : a += ' {} ';
                var A = a;
                a = I.pop(), a += !e.compositeRule && c ? e.async ? ' throw new ValidationError([' + A + ']); ' : ' validate.errors = [' + A + ']; return false; ' : ' var err = ' + A + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', a += ' }  ', c && (p += '}', a += ' else { '), $ && (a += ' if (' + g + ' === undefined) ' + u + ' = true; else if (typeof ' + g + ' != \'string\') ' + u + ' = false; else { ', p += '}'), d && (a += ' if (!' + y + ') ' + u + ' = true; else { ', p += '}'), a += ' var ' + S + ' = ' + y + '(' + h + ',  ', a += $ ? '' + g : '' + e.util.toQuotedString(i), a += ' ); if (' + S + ' === undefined) ' + u + ' = false; var ' + _ + ' = ' + x + ' === true; if (' + u + ' === undefined) { ' + u + ' = ' + _ + ' ? ' + S + ' ' + j + ' 0 : ' + S + ' ' + j + '= 0; } if (!' + u + ') var op' + s + ' = ' + _ + ' ? \'' + j + '\' : \'' + j + '=\';';
              } else {
                var _ = b === !0, R = j;
                _ || (R += '=');
                var O = '\'' + R + '\'';
                $ && (a += ' if (' + g + ' === undefined) ' + u + ' = true; else if (typeof ' + g + ' != \'string\') ' + u + ' = false; else { ', p += '}'), d && (a += ' if (!' + y + ') ' + u + ' = true; else { ', p += '}'), a += ' var ' + S + ' = ' + y + '(' + h + ',  ', a += $ ? '' + g : '' + e.util.toQuotedString(i), a += ' ); if (' + S + ' === undefined) ' + u + ' = false; if (' + u + ' === undefined) ' + u + ' = ' + S + ' ' + j, _ || (a += '='), a += ' 0;';
              }
              a += '' + p + 'if (!' + u + ') { ';
              var t = r, I = I || [];
              I.push(a), a = '', e.createErrors !== !1 ? (a += ' { keyword: \'' + (t || '_formatLimit') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(l) + ' , params: { comparison: ' + O + ', limit:  ', a += $ ? '' + g : '' + e.util.toQuotedString(i), a += ' , exclusive: ' + _ + ' } ', e.opts.messages !== !1 && (a += ' , message: \'should be ' + R + ' "', a += $ ? '\' + ' + g + ' + \'' : '' + e.util.escapeQuotes(i), a += '"\' '), e.opts.verbose && (a += ' , schema:  ', a += $ ? 'validate.schema' + n : '' + e.util.toQuotedString(i), a += '         , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + h + ' '), a += ' } ') : a += ' {} ';
              var A = a;
              return a = I.pop(), a += !e.compositeRule && c ? e.async ? ' throw new ValidationError([' + A + ']); ' : ' validate.errors = [' + A + ']; return false; ' : ' var err = ' + A + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', a += '}';
            };
          },
          {}
        ],
        14: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t, a, s = ' ', o = e.level, i = e.dataLevel, n = e.schema[r], l = e.schemaPath + e.util.getProperty(r), c = e.errSchemaPath + '/' + r, h = !e.opts.allErrors, u = 'data' + (i || ''), f = e.opts.v5 && n && n.$data;
              f ? (s += ' var schema' + o + ' = ' + e.util.getData(n.$data, i, e.dataPathArr) + '; ', a = 'schema' + o) : a = n;
              var d = 'maximum' == r, p = d ? 'exclusiveMaximum' : 'exclusiveMinimum', m = e.schema[p], v = e.opts.v5 && m && m.$data, y = d ? '<' : '>', g = d ? '>' : '<';
              if (v) {
                var P = e.util.getData(m.$data, i, e.dataPathArr), E = 'exclusive' + o, b = 'op' + o, w = '\' + ' + b + ' + \'';
                s += ' var schemaExcl' + o + ' = ' + P + '; ', P = 'schemaExcl' + o, s += ' var exclusive' + o + '; if (typeof ' + P + ' != \'boolean\' && typeof ' + P + ' != \'undefined\') { ';
                var t = p, j = j || [];
                j.push(s), s = '', e.createErrors !== !1 ? (s += ' { keyword: \'' + (t || '_exclusiveLimit') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(c) + ' , params: {} ', e.opts.messages !== !1 && (s += ' , message: \'' + p + ' should be boolean\' '), e.opts.verbose && (s += ' , schema: validate.schema' + l + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + u + ' '), s += ' } ') : s += ' {} ';
                var S = s;
                s = j.pop(), s += !e.compositeRule && h ? e.async ? ' throw new ValidationError([' + S + ']); ' : ' validate.errors = [' + S + ']; return false; ' : ' var err = ' + S + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', s += ' } else if( ', f && (s += ' (' + a + ' !== undefined && typeof ' + a + ' != \'number\') || '), s += ' ((exclusive' + o + ' = ' + P + ' === true) ? ' + u + ' ' + g + '= ' + a + ' : ' + u + ' ' + g + ' ' + a + ') || ' + u + ' !== ' + u + ') { var op' + o + ' = exclusive' + o + ' ? \'' + y + '\' : \'' + y + '=\';';
              } else {
                var E = m === !0, w = y;
                E || (w += '=');
                var b = '\'' + w + '\'';
                s += ' if ( ', f && (s += ' (' + a + ' !== undefined && typeof ' + a + ' != \'number\') || '), s += ' ' + u + ' ' + g, E && (s += '='), s += ' ' + a + ' || ' + u + ' !== ' + u + ') {';
              }
              var t = r, j = j || [];
              j.push(s), s = '', e.createErrors !== !1 ? (s += ' { keyword: \'' + (t || '_limit') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(c) + ' , params: { comparison: ' + b + ', limit: ' + a + ', exclusive: ' + E + ' } ', e.opts.messages !== !1 && (s += ' , message: \'should be ' + w + ' ', s += f ? '\' + ' + a : '' + n + '\''), e.opts.verbose && (s += ' , schema:  ', s += f ? 'validate.schema' + l : '' + n, s += '         , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + u + ' '), s += ' } ') : s += ' {} ';
              var S = s;
              return s = j.pop(), s += !e.compositeRule && h ? e.async ? ' throw new ValidationError([' + S + ']); ' : ' validate.errors = [' + S + ']; return false; ' : ' var err = ' + S + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', s += ' } ', h && (s += ' else { '), s;
            };
          },
          {}
        ],
        15: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t, a, s = ' ', o = e.level, i = e.dataLevel, n = e.schema[r], l = e.schemaPath + e.util.getProperty(r), c = e.errSchemaPath + '/' + r, h = !e.opts.allErrors, u = 'data' + (i || ''), f = e.opts.v5 && n && n.$data;
              f ? (s += ' var schema' + o + ' = ' + e.util.getData(n.$data, i, e.dataPathArr) + '; ', a = 'schema' + o) : a = n;
              var d = 'maxItems' == r ? '>' : '<';
              s += 'if ( ', f && (s += ' (' + a + ' !== undefined && typeof ' + a + ' != \'number\') || '), s += ' ' + u + '.length ' + d + ' ' + a + ') { ';
              var t = r, p = p || [];
              p.push(s), s = '', e.createErrors !== !1 ? (s += ' { keyword: \'' + (t || '_limitItems') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(c) + ' , params: { limit: ' + a + ' } ', e.opts.messages !== !1 && (s += ' , message: \'should NOT have ', s += 'maxItems' == r ? 'more' : 'less', s += ' than ', s += f ? '\' + ' + a + ' + \'' : '' + n, s += ' items\' '), e.opts.verbose && (s += ' , schema:  ', s += f ? 'validate.schema' + l : '' + n, s += '         , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + u + ' '), s += ' } ') : s += ' {} ';
              var m = s;
              return s = p.pop(), s += !e.compositeRule && h ? e.async ? ' throw new ValidationError([' + m + ']); ' : ' validate.errors = [' + m + ']; return false; ' : ' var err = ' + m + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', s += '} ', h && (s += ' else { '), s;
            };
          },
          {}
        ],
        16: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t, a, s = ' ', o = e.level, i = e.dataLevel, n = e.schema[r], l = e.schemaPath + e.util.getProperty(r), c = e.errSchemaPath + '/' + r, h = !e.opts.allErrors, u = 'data' + (i || ''), f = e.opts.v5 && n && n.$data;
              f ? (s += ' var schema' + o + ' = ' + e.util.getData(n.$data, i, e.dataPathArr) + '; ', a = 'schema' + o) : a = n;
              var d = 'maxLength' == r ? '>' : '<';
              s += 'if ( ', f && (s += ' (' + a + ' !== undefined && typeof ' + a + ' != \'number\') || '), s += e.opts.unicode === !1 ? ' ' + u + '.length ' : ' ucs2length(' + u + ') ', s += ' ' + d + ' ' + a + ') { ';
              var t = r, p = p || [];
              p.push(s), s = '', e.createErrors !== !1 ? (s += ' { keyword: \'' + (t || '_limitLength') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(c) + ' , params: { limit: ' + a + ' } ', e.opts.messages !== !1 && (s += ' , message: \'should NOT be ', s += 'maxLength' == r ? 'longer' : 'shorter', s += ' than ', s += f ? '\' + ' + a + ' + \'' : '' + n, s += ' characters\' '), e.opts.verbose && (s += ' , schema:  ', s += f ? 'validate.schema' + l : '' + n, s += '         , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + u + ' '), s += ' } ') : s += ' {} ';
              var m = s;
              return s = p.pop(), s += !e.compositeRule && h ? e.async ? ' throw new ValidationError([' + m + ']); ' : ' validate.errors = [' + m + ']; return false; ' : ' var err = ' + m + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', s += '} ', h && (s += ' else { '), s;
            };
          },
          {}
        ],
        17: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t, a, s = ' ', o = e.level, i = e.dataLevel, n = e.schema[r], l = e.schemaPath + e.util.getProperty(r), c = e.errSchemaPath + '/' + r, h = !e.opts.allErrors, u = 'data' + (i || ''), f = e.opts.v5 && n && n.$data;
              f ? (s += ' var schema' + o + ' = ' + e.util.getData(n.$data, i, e.dataPathArr) + '; ', a = 'schema' + o) : a = n;
              var d = 'maxProperties' == r ? '>' : '<';
              s += 'if ( ', f && (s += ' (' + a + ' !== undefined && typeof ' + a + ' != \'number\') || '), s += ' Object.keys(' + u + ').length ' + d + ' ' + a + ') { ';
              var t = r, p = p || [];
              p.push(s), s = '', e.createErrors !== !1 ? (s += ' { keyword: \'' + (t || '_limitProperties') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(c) + ' , params: { limit: ' + a + ' } ', e.opts.messages !== !1 && (s += ' , message: \'should NOT have ', s += 'maxProperties' == r ? 'more' : 'less', s += ' than ', s += f ? '\' + ' + a + ' + \'' : '' + n, s += ' properties\' '), e.opts.verbose && (s += ' , schema:  ', s += f ? 'validate.schema' + l : '' + n, s += '         , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + u + ' '), s += ' } ') : s += ' {} ';
              var m = s;
              return s = p.pop(), s += !e.compositeRule && h ? e.async ? ' throw new ValidationError([' + m + ']); ' : ' validate.errors = [' + m + ']; return false; ' : ' var err = ' + m + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', s += '} ', h && (s += ' else { '), s;
            };
          },
          {}
        ],
        18: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t = ' ', a = e.schema[r], s = e.schemaPath + e.util.getProperty(r), o = e.errSchemaPath + '/' + r, i = !e.opts.allErrors, n = e.util.copy(e), l = '';
              n.level++;
              var c = 'valid' + n.level, h = n.baseId, u = !0, f = a;
              if (f)
                for (var d, p = -1, m = f.length - 1; p < m;)
                  d = f[p += 1], e.util.schemaHasRules(d, e.RULES.all) && (u = !1, n.schema = d, n.schemaPath = s + '[' + p + ']', n.errSchemaPath = o + '/' + p, t += '  ' + e.validate(n) + ' ', n.baseId = h, i && (t += ' if (' + c + ') { ', l += '}'));
              return i && (t += u ? ' if (true) { ' : ' ' + l.slice(0, -1) + ' '), t = e.util.cleanUpCode(t);
            };
          },
          {}
        ],
        19: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t, a = ' ', s = e.level, o = e.dataLevel, i = e.schema[r], n = e.schemaPath + e.util.getProperty(r), l = e.errSchemaPath + '/' + r, c = !e.opts.allErrors, h = 'data' + (o || ''), u = 'valid' + s, f = 'errs__' + s, d = e.util.copy(e), p = '';
              d.level++;
              var m = 'valid' + d.level, v = i.every(function (r) {
                  return e.util.schemaHasRules(r, e.RULES.all);
                });
              if (v) {
                var y = d.baseId;
                a += ' var ' + f + ' = errors; var ' + u + ' = false;  ';
                var g = e.compositeRule;
                e.compositeRule = d.compositeRule = !0;
                var P = i;
                if (P)
                  for (var E, b = -1, w = P.length - 1; b < w;)
                    E = P[b += 1], d.schema = E, d.schemaPath = n + '[' + b + ']', d.errSchemaPath = l + '/' + b, a += '  ' + e.validate(d) + ' ', d.baseId = y, a += ' ' + u + ' = ' + u + ' || ' + m + '; if (!' + u + ') { ', p += '}';
                e.compositeRule = d.compositeRule = g, a += ' ' + p + ' if (!' + u + ') {  var err =   ', e.createErrors !== !1 ? (a += ' { keyword: \'' + (t || 'anyOf') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(l) + ' , params: {} ', e.opts.messages !== !1 && (a += ' , message: \'should match some schema in anyOf\' '), e.opts.verbose && (a += ' , schema: validate.schema' + n + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + h + ' '), a += ' } ') : a += ' {} ', a += ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } else {  errors = ' + f + '; if (vErrors !== null) { if (' + f + ') vErrors.length = ' + f + '; else vErrors = null; } ', e.opts.allErrors && (a += ' } '), a = e.util.cleanUpCode(a);
              } else
                c && (a += ' if (true) { ');
              return a;
            };
          },
          {}
        ],
        20: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t, a, s = ' ', o = e.level, i = e.dataLevel, n = e.schema[r], l = e.schemaPath + e.util.getProperty(r), c = e.errSchemaPath + '/' + r, h = !e.opts.allErrors, u = 'data' + (i || ''), f = 'valid' + o, d = e.opts.v5 && n && n.$data;
              d ? (s += ' var schema' + o + ' = ' + e.util.getData(n.$data, i, e.dataPathArr) + '; ', a = 'schema' + o) : a = n, d || (s += ' var schema' + o + ' = validate.schema' + l + ';'), s += 'var ' + f + ' = equal(' + u + ', schema' + o + '); if (!' + f + ') {   ';
              var p = p || [];
              p.push(s), s = '', e.createErrors !== !1 ? (s += ' { keyword: \'' + (t || 'constant') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(c) + ' , params: {} ', e.opts.messages !== !1 && (s += ' , message: \'should be equal to constant\' '), e.opts.verbose && (s += ' , schema: validate.schema' + l + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + u + ' '), s += ' } ') : s += ' {} ';
              var m = s;
              return s = p.pop(), s += !e.compositeRule && h ? e.async ? ' throw new ValidationError([' + m + ']); ' : ' validate.errors = [' + m + ']; return false; ' : ' var err = ' + m + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', s += ' }';
            };
          },
          {}
        ],
        21: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t, a, s = ' ', o = e.level, i = e.dataLevel, n = e.schema[r], l = e.schemaPath + e.util.getProperty(r), c = e.errSchemaPath + '/' + r, h = !e.opts.allErrors, u = 'data' + (i || ''), f = 'valid' + o, d = 'errs__' + o, p = e.opts.v5 && n && n.$data;
              p ? (s += ' var schema' + o + ' = ' + e.util.getData(n.$data, i, e.dataPathArr) + '; ', a = 'schema' + o) : a = n;
              var m, v, y, g, P, E = this, b = 'definition' + o, w = E.definition;
              if (p && w.$data) {
                P = 'keywordValidate' + o;
                var j = w.validateSchema;
                s += ' var ' + b + ' = RULES.custom[\'' + r + '\'].definition; var ' + P + ' = ' + b + '.validate;';
              } else
                g = e.useCustomRule(E, n, e.schema, e), a = 'validate.schema' + l, P = g.code, m = w.compile, v = w.inline, y = w.macro;
              var S = P + '.errors', $ = 'i' + o, x = 'ruleErr' + o, _ = w.async;
              if (_ && !e.async)
                throw new Error('async keyword in sync schema');
              if (v || y || (s += '' + S + ' = null;'), s += 'var ' + d + ' = errors;var ' + f + ';', j && (s += ' ' + f + ' = ' + b + '.validateSchema(' + a + '); if (' + f + ') {'), v)
                s += w.statements ? ' ' + g.validate + ' ' : ' ' + f + ' = ' + g.validate + '; ';
              else if (y) {
                var O = e.util.copy(e);
                O.level++;
                var R = 'valid' + O.level;
                O.schema = g.validate, O.schemaPath = '';
                var I = e.compositeRule;
                e.compositeRule = O.compositeRule = !0;
                var A = e.validate(O).replace(/validate\.schema/g, P);
                e.compositeRule = O.compositeRule = I, s += ' ' + A;
              } else {
                var k = k || [];
                k.push(s), s = '', s += '  ' + P + '.call( ', s += e.opts.passContext ? 'this' : 'self', s += m || w.schema === !1 ? ' , ' + u + ' ' : ' , ' + a + ' , ' + u + ' , validate.schema' + e.schemaPath + ' ', s += ' , (dataPath || \'\')', '""' != e.errorPath && (s += ' + ' + e.errorPath);
                var q = i ? 'data' + (i - 1 || '') : 'parentData', D = i ? e.dataPathArr[i] : 'parentDataProperty';
                s += ' , ' + q + ' , ' + D + ' , rootData )  ';
                var L = s;
                s = k.pop(), w.errors === !1 ? (s += ' ' + f + ' = ', _ && (s += '' + e.yieldAwait), s += '' + L + '; ') : _ ? (S = 'customErrors' + o, s += ' var ' + S + ' = null; try { ' + f + ' = ' + e.yieldAwait + L + '; } catch (e) { ' + f + ' = false; if (e instanceof ValidationError) ' + S + ' = e.errors; else throw e; } ') : s += ' ' + S + ' = null; ' + f + ' = ' + L + '; ';
              }
              if (w.modifying && (s += ' ' + u + ' = ' + q + '[' + D + '];'), j && (s += ' }'), w.valid)
                h && (s += ' if (true) { ');
              else {
                s += ' if ( ', void 0 === w.valid ? (s += ' !', s += y ? '' + R : '' + f) : s += ' ' + !w.valid + ' ', s += ') { ', t = E.keyword;
                var k = k || [];
                k.push(s), s = '';
                var k = k || [];
                k.push(s), s = '', e.createErrors !== !1 ? (s += ' { keyword: \'' + (t || 'custom') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(c) + ' , params: { keyword: \'' + E.keyword + '\' } ', e.opts.messages !== !1 && (s += ' , message: \'should pass "' + E.keyword + '" keyword validation\' '), e.opts.verbose && (s += ' , schema: validate.schema' + l + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + u + ' '), s += ' } ') : s += ' {} ';
                var Q = s;
                s = k.pop(), s += !e.compositeRule && h ? e.async ? ' throw new ValidationError([' + Q + ']); ' : ' validate.errors = [' + Q + ']; return false; ' : ' var err = ' + Q + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
                var C = s;
                s = k.pop(), v ? w.errors ? 'full' != w.errors && (s += '  for (var ' + $ + '=' + d + '; ' + $ + '<errors; ' + $ + '++) { var ' + x + ' = vErrors[' + $ + ']; if (' + x + '.dataPath === undefined) ' + x + '.dataPath = (dataPath || \'\') + ' + e.errorPath + '; if (' + x + '.schemaPath === undefined) { ' + x + '.schemaPath = "' + c + '"; } ', e.opts.verbose && (s += ' ' + x + '.schema = ' + a + '; ' + x + '.data = ' + u + '; '), s += ' } ') : w.errors === !1 ? s += ' ' + C + ' ' : (s += ' if (' + d + ' == errors) { ' + C + ' } else {  for (var ' + $ + '=' + d + '; ' + $ + '<errors; ' + $ + '++) { var ' + x + ' = vErrors[' + $ + ']; if (' + x + '.dataPath === undefined) ' + x + '.dataPath = (dataPath || \'\') + ' + e.errorPath + '; if (' + x + '.schemaPath === undefined) { ' + x + '.schemaPath = "' + c + '"; } ', e.opts.verbose && (s += ' ' + x + '.schema = ' + a + '; ' + x + '.data = ' + u + '; '), s += ' } } ') : y ? (s += '   var err =   ', e.createErrors !== !1 ? (s += ' { keyword: \'' + (t || 'custom') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(c) + ' , params: { keyword: \'' + E.keyword + '\' } ', e.opts.messages !== !1 && (s += ' , message: \'should pass "' + E.keyword + '" keyword validation\' '), e.opts.verbose && (s += ' , schema: validate.schema' + l + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + u + ' '), s += ' } ') : s += ' {} ', s += ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', !e.compositeRule && h && (s += e.async ? ' throw new ValidationError(vErrors); ' : ' validate.errors = vErrors; return false; ')) : w.errors === !1 ? s += ' ' + C + ' ' : (s += ' if (Array.isArray(' + S + ')) { if (vErrors === null) vErrors = ' + S + '; else vErrors = vErrors.concat(' + S + '); errors = vErrors.length;  for (var ' + $ + '=' + d + '; ' + $ + '<errors; ' + $ + '++) { var ' + x + ' = vErrors[' + $ + ']; if (' + x + '.dataPath === undefined) ' + x + '.dataPath = (dataPath || \'\') + ' + e.errorPath + ';  ' + x + '.schemaPath = "' + c + '";  ', e.opts.verbose && (s += ' ' + x + '.schema = ' + a + '; ' + x + '.data = ' + u + '; '), s += ' } } else { ' + C + ' } '), s += ' } ', h && (s += ' else { ');
              }
              return s;
            };
          },
          {}
        ],
        22: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t, a = ' ', s = e.level, o = e.dataLevel, i = e.schema[r], n = e.schemaPath + e.util.getProperty(r), l = e.errSchemaPath + '/' + r, c = !e.opts.allErrors, h = 'data' + (o || ''), u = 'errs__' + s, f = e.util.copy(e), d = '';
              f.level++;
              var p = 'valid' + f.level, m = {}, v = {};
              for (E in i) {
                var y = i[E], g = Array.isArray(y) ? v : m;
                g[E] = y;
              }
              a += 'var ' + u + ' = errors;';
              var P = e.errorPath;
              a += 'var missing' + s + ';';
              for (var E in v) {
                if (g = v[E], a += ' if (' + h + e.util.getProperty(E) + ' !== undefined ', c) {
                  a += ' && ( ';
                  var b = g;
                  if (b)
                    for (var w, j = -1, S = b.length - 1; j < S;) {
                      w = b[j += 1], j && (a += ' || ');
                      var $ = e.util.getProperty(w);
                      a += ' ( ' + h + $ + ' === undefined && (missing' + s + ' = ' + e.util.toQuotedString(e.opts.jsonPointers ? w : $) + ') ) ';
                    }
                  a += ')) {  ';
                  var x = 'missing' + s, _ = '\' + ' + x + ' + \'';
                  e.opts._errorDataPathProperty && (e.errorPath = e.opts.jsonPointers ? e.util.getPathExpr(P, x, !0) : P + ' + ' + x);
                  var O = O || [];
                  O.push(a), a = '', e.createErrors !== !1 ? (a += ' { keyword: \'' + (t || 'dependencies') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(l) + ' , params: { property: \'' + e.util.escapeQuotes(E) + '\', missingProperty: \'' + _ + '\', depsCount: ' + g.length + ', deps: \'' + e.util.escapeQuotes(1 == g.length ? g[0] : g.join(', ')) + '\' } ', e.opts.messages !== !1 && (a += ' , message: \'should have ', a += 1 == g.length ? 'property ' + e.util.escapeQuotes(g[0]) : 'properties ' + e.util.escapeQuotes(g.join(', ')), a += ' when property ' + e.util.escapeQuotes(E) + ' is present\' '), e.opts.verbose && (a += ' , schema: validate.schema' + n + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + h + ' '), a += ' } ') : a += ' {} ';
                  var R = a;
                  a = O.pop(), a += !e.compositeRule && c ? e.async ? ' throw new ValidationError([' + R + ']); ' : ' validate.errors = [' + R + ']; return false; ' : ' var err = ' + R + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
                } else {
                  a += ' ) { ';
                  var I = g;
                  if (I)
                    for (var A, k = -1, q = I.length - 1; k < q;) {
                      A = I[k += 1];
                      var $ = e.util.getProperty(A), _ = e.util.escapeQuotes(A);
                      e.opts._errorDataPathProperty && (e.errorPath = e.util.getPath(P, A, e.opts.jsonPointers)), a += ' if (' + h + $ + ' === undefined) {  var err =   ', e.createErrors !== !1 ? (a += ' { keyword: \'' + (t || 'dependencies') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(l) + ' , params: { property: \'' + e.util.escapeQuotes(E) + '\', missingProperty: \'' + _ + '\', depsCount: ' + g.length + ', deps: \'' + e.util.escapeQuotes(1 == g.length ? g[0] : g.join(', ')) + '\' } ', e.opts.messages !== !1 && (a += ' , message: \'should have ', a += 1 == g.length ? 'property ' + e.util.escapeQuotes(g[0]) : 'properties ' + e.util.escapeQuotes(g.join(', ')), a += ' when property ' + e.util.escapeQuotes(E) + ' is present\' '), e.opts.verbose && (a += ' , schema: validate.schema' + n + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + h + ' '), a += ' } ') : a += ' {} ', a += ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } ';
                    }
                }
                a += ' }   ', c && (d += '}', a += ' else { ');
              }
              e.errorPath = P;
              var D = f.baseId;
              for (var E in m) {
                var y = m[E];
                e.util.schemaHasRules(y, e.RULES.all) && (a += ' ' + p + ' = true; if (' + h + '[\'' + E + '\'] !== undefined) { ', f.schema = y, f.schemaPath = n + e.util.getProperty(E), f.errSchemaPath = l + '/' + e.util.escapeFragment(E), a += '  ' + e.validate(f) + ' ', f.baseId = D, a += ' }  ', c && (a += ' if (' + p + ') { ', d += '}'));
              }
              return c && (a += '   ' + d + ' if (' + u + ' == errors) {'), a = e.util.cleanUpCode(a);
            };
          },
          {}
        ],
        23: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t, a, s = ' ', o = e.level, i = e.dataLevel, n = e.schema[r], l = e.schemaPath + e.util.getProperty(r), c = e.errSchemaPath + '/' + r, h = !e.opts.allErrors, u = 'data' + (i || ''), f = 'valid' + o, d = e.opts.v5 && n && n.$data;
              d ? (s += ' var schema' + o + ' = ' + e.util.getData(n.$data, i, e.dataPathArr) + '; ', a = 'schema' + o) : a = n;
              var p = 'i' + o, m = 'schema' + o;
              d || (s += ' var ' + m + ' = validate.schema' + l + ';'), s += 'var ' + f + ';', d && (s += ' if (schema' + o + ' === undefined) ' + f + ' = true; else if (!Array.isArray(schema' + o + ')) ' + f + ' = false; else {'), s += '' + f + ' = false;for (var ' + p + '=0; ' + p + '<' + m + '.length; ' + p + '++) if (equal(' + u + ', ' + m + '[' + p + '])) { ' + f + ' = true; break; }', d && (s += '  }  '), s += ' if (!' + f + ') {   ';
              var v = v || [];
              v.push(s), s = '', e.createErrors !== !1 ? (s += ' { keyword: \'' + (t || 'enum') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(c) + ' , params: { allowedValues: schema' + o + ' } ', e.opts.messages !== !1 && (s += ' , message: \'should be equal to one of the allowed values\' '), e.opts.verbose && (s += ' , schema: validate.schema' + l + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + u + ' '), s += ' } ') : s += ' {} ';
              var y = s;
              return s = v.pop(), s += !e.compositeRule && h ? e.async ? ' throw new ValidationError([' + y + ']); ' : ' validate.errors = [' + y + ']; return false; ' : ' var err = ' + y + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', s += ' }', h && (s += ' else { '), s;
            };
          },
          {}
        ],
        24: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t, a = ' ', s = e.level, o = e.dataLevel, i = e.schema[r], n = e.schemaPath + e.util.getProperty(r), l = e.errSchemaPath + '/' + r, c = !e.opts.allErrors, h = 'data' + (o || '');
              if (e.opts.format === !1)
                return c && (a += ' if (true) { '), a;
              var u, f = e.opts.v5 && i && i.$data;
              f ? (a += ' var schema' + s + ' = ' + e.util.getData(i.$data, o, e.dataPathArr) + '; ', u = 'schema' + s) : u = i;
              var d = e.opts.unknownFormats, p = Array.isArray(d);
              if (f) {
                var m = 'format' + s;
                a += ' var ' + m + ' = formats[' + u + ']; var isObject' + s + ' = typeof ' + m + ' == \'object\' && !(' + m + ' instanceof RegExp) && ' + m + '.validate; if (isObject' + s + ') { ', e.async && (a += ' var async' + s + ' = ' + m + '.async; '), a += ' ' + m + ' = ' + m + '.validate; } if (  ', f && (a += ' (' + u + ' !== undefined && typeof ' + u + ' != \'string\') || '), a += ' (', (d === !0 || p) && (a += ' (' + u + ' && !' + m + ' ', p && (a += ' && self._opts.unknownFormats.indexOf(' + u + ') == -1 '), a += ') || '), a += ' (' + m + ' && !(typeof ' + m + ' == \'function\' ? ', a += e.async ? ' (async' + s + ' ? ' + e.yieldAwait + ' ' + m + '(' + h + ') : ' + m + '(' + h + ')) ' : ' ' + m + '(' + h + ') ', a += ' : ' + m + '.test(' + h + '))))) {';
              } else {
                var m = e.formats[i];
                if (!m) {
                  if (d === !0 || p && d.indexOf(i) == -1)
                    throw new Error('unknown format "' + i + '" is used in schema at path "' + e.errSchemaPath + '"');
                  return p || (console.warn('unknown format "' + i + '" ignored in schema at path "' + e.errSchemaPath + '"'), 'ignore' !== d && console.warn('In the next major version it will throw exception. See option unknownFormats for more information')), c && (a += ' if (true) { '), a;
                }
                var v = 'object' == typeof m && !(m instanceof RegExp) && m.validate;
                if (v) {
                  var y = m.async === !0;
                  m = m.validate;
                }
                if (y) {
                  if (!e.async)
                    throw new Error('async format in sync schema');
                  var g = 'formats' + e.util.getProperty(i) + '.validate';
                  a += ' if (!(' + e.yieldAwait + ' ' + g + '(' + h + '))) { ';
                } else {
                  a += ' if (! ';
                  var g = 'formats' + e.util.getProperty(i);
                  v && (g += '.validate'), a += 'function' == typeof m ? ' ' + g + '(' + h + ') ' : ' ' + g + '.test(' + h + ') ', a += ') { ';
                }
              }
              var P = P || [];
              P.push(a), a = '', e.createErrors !== !1 ? (a += ' { keyword: \'' + (t || 'format') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(l) + ' , params: { format:  ', a += f ? '' + u : '' + e.util.toQuotedString(i), a += '  } ', e.opts.messages !== !1 && (a += ' , message: \'should match format "', a += f ? '\' + ' + u + ' + \'' : '' + e.util.escapeQuotes(i), a += '"\' '), e.opts.verbose && (a += ' , schema:  ', a += f ? 'validate.schema' + n : '' + e.util.toQuotedString(i), a += '         , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + h + ' '), a += ' } ') : a += ' {} ';
              var E = a;
              return a = P.pop(), a += !e.compositeRule && c ? e.async ? ' throw new ValidationError([' + E + ']); ' : ' validate.errors = [' + E + ']; return false; ' : ' var err = ' + E + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', a += ' } ', c && (a += ' else { '), a;
            };
          },
          {}
        ],
        25: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t, a = ' ', s = e.level, o = e.dataLevel, i = e.schema[r], n = e.schemaPath + e.util.getProperty(r), l = e.errSchemaPath + '/' + r, c = !e.opts.allErrors, h = 'data' + (o || ''), u = 'valid' + s, f = 'errs__' + s, d = e.util.copy(e), p = '';
              d.level++;
              var m = 'valid' + d.level, v = 'i' + s, y = d.dataLevel = e.dataLevel + 1, g = 'data' + y, P = e.baseId;
              if (a += 'var ' + f + ' = errors;var ' + u + ';', Array.isArray(i)) {
                var E = e.schema.additionalItems;
                if (E === !1) {
                  a += ' ' + u + ' = ' + h + '.length <= ' + i.length + '; ';
                  var b = l;
                  l = e.errSchemaPath + '/additionalItems', a += '  if (!' + u + ') {   ';
                  var w = w || [];
                  w.push(a), a = '', e.createErrors !== !1 ? (a += ' { keyword: \'' + (t || 'additionalItems') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(l) + ' , params: { limit: ' + i.length + ' } ', e.opts.messages !== !1 && (a += ' , message: \'should NOT have more than ' + i.length + ' items\' '), e.opts.verbose && (a += ' , schema: false , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + h + ' '), a += ' } ') : a += ' {} ';
                  var j = a;
                  a = w.pop(), a += !e.compositeRule && c ? e.async ? ' throw new ValidationError([' + j + ']); ' : ' validate.errors = [' + j + ']; return false; ' : ' var err = ' + j + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', a += ' } ', l = b, c && (p += '}', a += ' else { ');
                }
                var S = i;
                if (S)
                  for (var $, x = -1, _ = S.length - 1; x < _;)
                    if ($ = S[x += 1], e.util.schemaHasRules($, e.RULES.all)) {
                      a += ' ' + m + ' = true; if (' + h + '.length > ' + x + ') { ';
                      var O = h + '[' + x + ']';
                      d.schema = $, d.schemaPath = n + '[' + x + ']', d.errSchemaPath = l + '/' + x, d.errorPath = e.util.getPathExpr(e.errorPath, x, e.opts.jsonPointers, !0), d.dataPathArr[y] = x;
                      var R = e.validate(d);
                      d.baseId = P, a += e.util.varOccurences(R, g) < 2 ? ' ' + e.util.varReplace(R, g, O) + ' ' : ' var ' + g + ' = ' + O + '; ' + R + ' ', a += ' }  ', c && (a += ' if (' + m + ') { ', p += '}');
                    }
                if ('object' == typeof E && e.util.schemaHasRules(E, e.RULES.all)) {
                  d.schema = E, d.schemaPath = e.schemaPath + '.additionalItems', d.errSchemaPath = e.errSchemaPath + '/additionalItems', a += ' ' + m + ' = true; if (' + h + '.length > ' + i.length + ') {  for (var ' + v + ' = ' + i.length + '; ' + v + ' < ' + h + '.length; ' + v + '++) { ', d.errorPath = e.util.getPathExpr(e.errorPath, v, e.opts.jsonPointers, !0);
                  var O = h + '[' + v + ']';
                  d.dataPathArr[y] = v;
                  var R = e.validate(d);
                  d.baseId = P, a += e.util.varOccurences(R, g) < 2 ? ' ' + e.util.varReplace(R, g, O) + ' ' : ' var ' + g + ' = ' + O + '; ' + R + ' ', c && (a += ' if (!' + m + ') break; '), a += ' } }  ', c && (a += ' if (' + m + ') { ', p += '}');
                }
              } else if (e.util.schemaHasRules(i, e.RULES.all)) {
                d.schema = i, d.schemaPath = n, d.errSchemaPath = l, a += '  for (var ' + v + ' = 0; ' + v + ' < ' + h + '.length; ' + v + '++) { ', d.errorPath = e.util.getPathExpr(e.errorPath, v, e.opts.jsonPointers, !0);
                var O = h + '[' + v + ']';
                d.dataPathArr[y] = v;
                var R = e.validate(d);
                d.baseId = P, a += e.util.varOccurences(R, g) < 2 ? ' ' + e.util.varReplace(R, g, O) + ' ' : ' var ' + g + ' = ' + O + '; ' + R + ' ', c && (a += ' if (!' + m + ') break; '), a += ' }  ', c && (a += ' if (' + m + ') { ', p += '}');
              }
              return c && (a += ' ' + p + ' if (' + f + ' == errors) {'), a = e.util.cleanUpCode(a);
            };
          },
          {}
        ],
        26: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t, a, s = ' ', o = e.level, i = e.dataLevel, n = e.schema[r], l = e.schemaPath + e.util.getProperty(r), c = e.errSchemaPath + '/' + r, h = !e.opts.allErrors, u = 'data' + (i || ''), f = e.opts.v5 && n && n.$data;
              f ? (s += ' var schema' + o + ' = ' + e.util.getData(n.$data, i, e.dataPathArr) + '; ', a = 'schema' + o) : a = n, s += 'var division' + o + ';if (', f && (s += ' ' + a + ' !== undefined && ( typeof ' + a + ' != \'number\' || '), s += ' (division' + o + ' = ' + u + ' / ' + a + ', ', s += e.opts.multipleOfPrecision ? ' Math.abs(Math.round(division' + o + ') - division' + o + ') > 1e-' + e.opts.multipleOfPrecision + ' ' : ' division' + o + ' !== parseInt(division' + o + ') ', s += ' ) ', f && (s += '  )  '), s += ' ) {   ';
              var d = d || [];
              d.push(s), s = '', e.createErrors !== !1 ? (s += ' { keyword: \'' + (t || 'multipleOf') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(c) + ' , params: { multipleOf: ' + a + ' } ', e.opts.messages !== !1 && (s += ' , message: \'should be multiple of ', s += f ? '\' + ' + a : '' + n + '\''), e.opts.verbose && (s += ' , schema:  ', s += f ? 'validate.schema' + l : '' + n, s += '         , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + u + ' '), s += ' } ') : s += ' {} ';
              var p = s;
              return s = d.pop(), s += !e.compositeRule && h ? e.async ? ' throw new ValidationError([' + p + ']); ' : ' validate.errors = [' + p + ']; return false; ' : ' var err = ' + p + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', s += '} ', h && (s += ' else { '), s;
            };
          },
          {}
        ],
        27: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t, a = ' ', s = e.level, o = e.dataLevel, i = e.schema[r], n = e.schemaPath + e.util.getProperty(r), l = e.errSchemaPath + '/' + r, c = !e.opts.allErrors, h = 'data' + (o || ''), u = 'errs__' + s, f = e.util.copy(e);
              f.level++;
              var d = 'valid' + f.level;
              if (e.util.schemaHasRules(i, e.RULES.all)) {
                f.schema = i, f.schemaPath = n, f.errSchemaPath = l, a += ' var ' + u + ' = errors;  ';
                var p = e.compositeRule;
                e.compositeRule = f.compositeRule = !0, f.createErrors = !1;
                var m;
                f.opts.allErrors && (m = f.opts.allErrors, f.opts.allErrors = !1), a += ' ' + e.validate(f) + ' ', f.createErrors = !0, m && (f.opts.allErrors = m), e.compositeRule = f.compositeRule = p, a += ' if (' + d + ') {   ';
                var v = v || [];
                v.push(a), a = '', e.createErrors !== !1 ? (a += ' { keyword: \'' + (t || 'not') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(l) + ' , params: {} ', e.opts.messages !== !1 && (a += ' , message: \'should NOT be valid\' '), e.opts.verbose && (a += ' , schema: validate.schema' + n + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + h + ' '), a += ' } ') : a += ' {} ';
                var y = a;
                a = v.pop(), a += !e.compositeRule && c ? e.async ? ' throw new ValidationError([' + y + ']); ' : ' validate.errors = [' + y + ']; return false; ' : ' var err = ' + y + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', a += ' } else {  errors = ' + u + '; if (vErrors !== null) { if (' + u + ') vErrors.length = ' + u + '; else vErrors = null; } ', e.opts.allErrors && (a += ' } ');
              } else
                a += '  var err =   ', e.createErrors !== !1 ? (a += ' { keyword: \'' + (t || 'not') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(l) + ' , params: {} ', e.opts.messages !== !1 && (a += ' , message: \'should NOT be valid\' '), e.opts.verbose && (a += ' , schema: validate.schema' + n + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + h + ' '), a += ' } ') : a += ' {} ', a += ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', c && (a += ' if (false) { ');
              return a;
            };
          },
          {}
        ],
        28: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t, a = ' ', s = e.level, o = e.dataLevel, i = e.schema[r], n = e.schemaPath + e.util.getProperty(r), l = e.errSchemaPath + '/' + r, c = !e.opts.allErrors, h = 'data' + (o || ''), u = 'valid' + s, f = 'errs__' + s, d = e.util.copy(e), p = '';
              d.level++;
              var m = 'valid' + d.level;
              a += 'var ' + f + ' = errors;var prevValid' + s + ' = false;var ' + u + ' = false;';
              var v = d.baseId, y = e.compositeRule;
              e.compositeRule = d.compositeRule = !0;
              var g = i;
              if (g)
                for (var P, E = -1, b = g.length - 1; E < b;)
                  P = g[E += 1], e.util.schemaHasRules(P, e.RULES.all) ? (d.schema = P, d.schemaPath = n + '[' + E + ']', d.errSchemaPath = l + '/' + E, a += '  ' + e.validate(d) + ' ', d.baseId = v) : a += ' var ' + m + ' = true; ', E && (a += ' if (' + m + ' && prevValid' + s + ') ' + u + ' = false; else { ', p += '}'), a += ' if (' + m + ') ' + u + ' = prevValid' + s + ' = true;';
              e.compositeRule = d.compositeRule = y, a += '' + p + 'if (!' + u + ') {   ';
              var w = w || [];
              w.push(a), a = '', e.createErrors !== !1 ? (a += ' { keyword: \'' + (t || 'oneOf') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(l) + ' , params: {} ', e.opts.messages !== !1 && (a += ' , message: \'should match exactly one schema in oneOf\' '), e.opts.verbose && (a += ' , schema: validate.schema' + n + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + h + ' '), a += ' } ') : a += ' {} ';
              var j = a;
              return a = w.pop(), a += !e.compositeRule && c ? e.async ? ' throw new ValidationError([' + j + ']); ' : ' validate.errors = [' + j + ']; return false; ' : ' var err = ' + j + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', a += '} else {  errors = ' + f + '; if (vErrors !== null) { if (' + f + ') vErrors.length = ' + f + '; else vErrors = null; }', e.opts.allErrors && (a += ' } '), a;
            };
          },
          {}
        ],
        29: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t, a, s = ' ', o = e.level, i = e.dataLevel, n = e.schema[r], l = e.schemaPath + e.util.getProperty(r), c = e.errSchemaPath + '/' + r, h = !e.opts.allErrors, u = 'data' + (i || ''), f = e.opts.v5 && n && n.$data;
              f ? (s += ' var schema' + o + ' = ' + e.util.getData(n.$data, i, e.dataPathArr) + '; ', a = 'schema' + o) : a = n;
              var d = f ? '(new RegExp(' + a + '))' : e.usePattern(n);
              s += 'if ( ', f && (s += ' (' + a + ' !== undefined && typeof ' + a + ' != \'string\') || '), s += ' !' + d + '.test(' + u + ') ) {   ';
              var p = p || [];
              p.push(s), s = '', e.createErrors !== !1 ? (s += ' { keyword: \'' + (t || 'pattern') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(c) + ' , params: { pattern:  ', s += f ? '' + a : '' + e.util.toQuotedString(n), s += '  } ', e.opts.messages !== !1 && (s += ' , message: \'should match pattern "', s += f ? '\' + ' + a + ' + \'' : '' + e.util.escapeQuotes(n), s += '"\' '), e.opts.verbose && (s += ' , schema:  ', s += f ? 'validate.schema' + l : '' + e.util.toQuotedString(n), s += '         , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + u + ' '), s += ' } ') : s += ' {} ';
              var m = s;
              return s = p.pop(), s += !e.compositeRule && h ? e.async ? ' throw new ValidationError([' + m + ']); ' : ' validate.errors = [' + m + ']; return false; ' : ' var err = ' + m + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', s += '} ', h && (s += ' else { '), s;
            };
          },
          {}
        ],
        30: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t, a = ' ', s = e.level, o = e.dataLevel, i = e.schema[r], n = e.schemaPath + e.util.getProperty(r), l = e.errSchemaPath + '/' + r, c = !e.opts.allErrors, h = 'data' + (o || ''), u = 'valid' + s, f = 'key' + s, d = 'patternMatched' + s, p = '', m = e.opts.ownProperties;
              a += 'var ' + u + ' = true;';
              var v = i;
              if (v)
                for (var y, g = -1, P = v.length - 1; g < P;) {
                  y = v[g += 1], a += ' var ' + d + ' = false; for (var ' + f + ' in ' + h + ') {  ', m && (a += ' if (!Object.prototype.hasOwnProperty.call(' + h + ', ' + f + ')) continue; '), a += ' ' + d + ' = ' + e.usePattern(y) + '.test(' + f + '); if (' + d + ') break; } ';
                  var E = e.util.escapeQuotes(y);
                  a += ' if (!' + d + ') { ' + u + ' = false;  var err =   ', e.createErrors !== !1 ? (a += ' { keyword: \'' + (t || 'patternRequired') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(l) + ' , params: { missingPattern: \'' + E + '\' } ', e.opts.messages !== !1 && (a += ' , message: \'should have property matching pattern \\\'' + E + '\\\'\' '), e.opts.verbose && (a += ' , schema: validate.schema' + n + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + h + ' '), a += ' } ') : a += ' {} ', a += ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; }   ', c && (p += '}', a += ' else { ');
                }
              return a += '' + p;
            };
          },
          {}
        ],
        31: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t, a = ' ', s = e.level, o = e.dataLevel, i = e.schema[r], n = e.schemaPath + e.util.getProperty(r), l = e.errSchemaPath + '/' + r, c = !e.opts.allErrors, h = 'data' + (o || ''), u = 'valid' + s, f = 'errs__' + s, d = e.util.copy(e), p = '';
              d.level++;
              var m = 'valid' + d.level, v = 'key' + s, y = d.dataLevel = e.dataLevel + 1, g = 'data' + y, P = Object.keys(i || {}), E = e.schema.patternProperties || {}, b = Object.keys(E), w = e.schema.additionalProperties, j = P.length || b.length, S = w === !1, $ = 'object' == typeof w && Object.keys(w).length, x = e.opts.removeAdditional, _ = S || $ || x, O = e.opts.ownProperties, R = e.baseId, I = e.schema.required;
              if (I && (!e.opts.v5 || !I.$data) && I.length < e.opts.loopRequired)
                var A = e.util.toHash(I);
              if (e.opts.v5)
                var k = e.schema.patternGroups || {}, q = Object.keys(k);
              if (a += 'var ' + f + ' = errors;var ' + m + ' = true;', _) {
                if (a += ' for (var ' + v + ' in ' + h + ') {  ', O && (a += ' if (!Object.prototype.hasOwnProperty.call(' + h + ', ' + v + ')) continue; '), j) {
                  if (a += ' var isAdditional' + s + ' = !(false ', P.length)
                    if (P.length > 5)
                      a += ' || validate.schema' + n + '[' + v + '] ';
                    else {
                      var D = P;
                      if (D)
                        for (var L, Q = -1, C = D.length - 1; Q < C;)
                          L = D[Q += 1], a += ' || ' + v + ' == ' + e.util.toQuotedString(L) + ' ';
                    }
                  if (b.length) {
                    var V = b;
                    if (V)
                      for (var z, U = -1, T = V.length - 1; U < T;)
                        z = V[U += 1], a += ' || ' + e.usePattern(z) + '.test(' + v + ') ';
                  }
                  if (e.opts.v5 && q && q.length) {
                    var M = q;
                    if (M)
                      for (var N, U = -1, F = M.length - 1; U < F;)
                        N = M[U += 1], a += ' || ' + e.usePattern(N) + '.test(' + v + ') ';
                  }
                  a += ' ); if (isAdditional' + s + ') { ';
                }
                if ('all' == x)
                  a += ' delete ' + h + '[' + v + ']; ';
                else {
                  var H = e.errorPath, J = '\' + ' + v + ' + \'';
                  if (e.opts._errorDataPathProperty && (e.errorPath = e.util.getPathExpr(e.errorPath, v, e.opts.jsonPointers)), S)
                    if (x)
                      a += ' delete ' + h + '[' + v + ']; ';
                    else {
                      a += ' ' + m + ' = false; ';
                      var G = l;
                      l = e.errSchemaPath + '/additionalProperties';
                      var K = K || [];
                      K.push(a), a = '', e.createErrors !== !1 ? (a += ' { keyword: \'' + (t || 'additionalProperties') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(l) + ' , params: { additionalProperty: \'' + J + '\' } ', e.opts.messages !== !1 && (a += ' , message: \'should NOT have additional properties\' '), e.opts.verbose && (a += ' , schema: false , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + h + ' '), a += ' } ') : a += ' {} ';
                      var B = a;
                      a = K.pop(), a += !e.compositeRule && c ? e.async ? ' throw new ValidationError([' + B + ']); ' : ' validate.errors = [' + B + ']; return false; ' : ' var err = ' + B + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', l = G, c && (a += ' break; ');
                    }
                  else if ($)
                    if ('failing' == x) {
                      a += ' var ' + f + ' = errors;  ';
                      var Z = e.compositeRule;
                      e.compositeRule = d.compositeRule = !0, d.schema = w, d.schemaPath = e.schemaPath + '.additionalProperties', d.errSchemaPath = e.errSchemaPath + '/additionalProperties', d.errorPath = e.opts._errorDataPathProperty ? e.errorPath : e.util.getPathExpr(e.errorPath, v, e.opts.jsonPointers);
                      var Y = h + '[' + v + ']';
                      d.dataPathArr[y] = v;
                      var W = e.validate(d);
                      d.baseId = R, a += e.util.varOccurences(W, g) < 2 ? ' ' + e.util.varReplace(W, g, Y) + ' ' : ' var ' + g + ' = ' + Y + '; ' + W + ' ', a += ' if (!' + m + ') { errors = ' + f + '; if (validate.errors !== null) { if (errors) validate.errors.length = errors; else validate.errors = null; } delete ' + h + '[' + v + ']; }  ', e.compositeRule = d.compositeRule = Z;
                    } else {
                      d.schema = w, d.schemaPath = e.schemaPath + '.additionalProperties', d.errSchemaPath = e.errSchemaPath + '/additionalProperties', d.errorPath = e.opts._errorDataPathProperty ? e.errorPath : e.util.getPathExpr(e.errorPath, v, e.opts.jsonPointers);
                      var Y = h + '[' + v + ']';
                      d.dataPathArr[y] = v;
                      var W = e.validate(d);
                      d.baseId = R, a += e.util.varOccurences(W, g) < 2 ? ' ' + e.util.varReplace(W, g, Y) + ' ' : ' var ' + g + ' = ' + Y + '; ' + W + ' ', c && (a += ' if (!' + m + ') break; ');
                    }
                  e.errorPath = H;
                }
                j && (a += ' } '), a += ' }  ', c && (a += ' if (' + m + ') { ', p += '}');
              }
              var X = e.opts.useDefaults && !e.compositeRule;
              if (P.length) {
                var ee = P;
                if (ee)
                  for (var L, re = -1, te = ee.length - 1; re < te;) {
                    L = ee[re += 1];
                    var ae = i[L];
                    if (e.util.schemaHasRules(ae, e.RULES.all)) {
                      var se = e.util.getProperty(L), Y = h + se, oe = X && void 0 !== ae.default;
                      d.schema = ae, d.schemaPath = n + se, d.errSchemaPath = l + '/' + e.util.escapeFragment(L), d.errorPath = e.util.getPath(e.errorPath, L, e.opts.jsonPointers), d.dataPathArr[y] = e.util.toQuotedString(L);
                      var W = e.validate(d);
                      if (d.baseId = R, e.util.varOccurences(W, g) < 2) {
                        W = e.util.varReplace(W, g, Y);
                        var ie = Y;
                      } else {
                        var ie = g;
                        a += ' var ' + g + ' = ' + Y + '; ';
                      }
                      if (oe)
                        a += ' ' + W + ' ';
                      else {
                        if (A && A[L]) {
                          a += ' if (' + ie + ' === undefined) { ' + m + ' = false; ';
                          var H = e.errorPath, G = l, ne = e.util.escapeQuotes(L);
                          e.opts._errorDataPathProperty && (e.errorPath = e.util.getPath(H, L, e.opts.jsonPointers)), l = e.errSchemaPath + '/required';
                          var K = K || [];
                          K.push(a), a = '', e.createErrors !== !1 ? (a += ' { keyword: \'' + (t || 'required') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(l) + ' , params: { missingProperty: \'' + ne + '\' } ', e.opts.messages !== !1 && (a += ' , message: \'', a += e.opts._errorDataPathProperty ? 'is a required property' : 'should have required property \\\'' + ne + '\\\'', a += '\' '), e.opts.verbose && (a += ' , schema: validate.schema' + n + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + h + ' '), a += ' } ') : a += ' {} ';
                          var B = a;
                          a = K.pop(), a += !e.compositeRule && c ? e.async ? ' throw new ValidationError([' + B + ']); ' : ' validate.errors = [' + B + ']; return false; ' : ' var err = ' + B + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', l = G, e.errorPath = H, a += ' } else { ';
                        } else
                          a += c ? ' if (' + ie + ' === undefined) { ' + m + ' = true; } else { ' : ' if (' + ie + ' !== undefined) { ';
                        a += ' ' + W + ' } ';
                      }
                    }
                    c && (a += ' if (' + m + ') { ', p += '}');
                  }
              }
              var le = b;
              if (le)
                for (var z, ce = -1, he = le.length - 1; ce < he;) {
                  z = le[ce += 1];
                  var ae = E[z];
                  if (e.util.schemaHasRules(ae, e.RULES.all)) {
                    d.schema = ae, d.schemaPath = e.schemaPath + '.patternProperties' + e.util.getProperty(z), d.errSchemaPath = e.errSchemaPath + '/patternProperties/' + e.util.escapeFragment(z), a += ' for (var ' + v + ' in ' + h + ') {  ', O && (a += ' if (!Object.prototype.hasOwnProperty.call(' + h + ', ' + v + ')) continue; '), a += ' if (' + e.usePattern(z) + '.test(' + v + ')) { ', d.errorPath = e.util.getPathExpr(e.errorPath, v, e.opts.jsonPointers);
                    var Y = h + '[' + v + ']';
                    d.dataPathArr[y] = v;
                    var W = e.validate(d);
                    d.baseId = R, a += e.util.varOccurences(W, g) < 2 ? ' ' + e.util.varReplace(W, g, Y) + ' ' : ' var ' + g + ' = ' + Y + '; ' + W + ' ', c && (a += ' if (!' + m + ') break; '), a += ' } ', c && (a += ' else ' + m + ' = true; '), a += ' }  ', c && (a += ' if (' + m + ') { ', p += '}');
                  }
                }
              if (e.opts.v5) {
                var ue = q;
                if (ue)
                  for (var N, fe = -1, de = ue.length - 1; fe < de;) {
                    N = ue[fe += 1];
                    var pe = k[N], ae = pe.schema;
                    if (e.util.schemaHasRules(ae, e.RULES.all)) {
                      d.schema = ae, d.schemaPath = e.schemaPath + '.patternGroups' + e.util.getProperty(N) + '.schema', d.errSchemaPath = e.errSchemaPath + '/patternGroups/' + e.util.escapeFragment(N) + '/schema', a += ' var pgPropCount' + s + ' = 0; for (var ' + v + ' in ' + h + ') {  ', O && (a += ' if (!Object.prototype.hasOwnProperty.call(' + h + ', ' + v + ')) continue; '), a += ' if (' + e.usePattern(N) + '.test(' + v + ')) { pgPropCount' + s + '++; ', d.errorPath = e.util.getPathExpr(e.errorPath, v, e.opts.jsonPointers);
                      var Y = h + '[' + v + ']';
                      d.dataPathArr[y] = v;
                      var W = e.validate(d);
                      d.baseId = R, a += e.util.varOccurences(W, g) < 2 ? ' ' + e.util.varReplace(W, g, Y) + ' ' : ' var ' + g + ' = ' + Y + '; ' + W + ' ', c && (a += ' if (!' + m + ') break; '), a += ' } ', c && (a += ' else ' + m + ' = true; '), a += ' }  ', c && (a += ' if (' + m + ') { ', p += '}');
                      var me = pe.minimum, ve = pe.maximum;
                      if (void 0 !== me || void 0 !== ve) {
                        a += ' var ' + u + ' = true; ';
                        var G = l;
                        if (void 0 !== me) {
                          var ye = me, ge = 'minimum', Pe = 'less';
                          a += ' ' + u + ' = pgPropCount' + s + ' >= ' + me + '; ', l = e.errSchemaPath + '/patternGroups/minimum', a += '  if (!' + u + ') {   ';
                          var K = K || [];
                          K.push(a), a = '', e.createErrors !== !1 ? (a += ' { keyword: \'' + (t || 'patternGroups') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(l) + ' , params: { reason: \'' + ge + '\', limit: ' + ye + ', pattern: \'' + e.util.escapeQuotes(N) + '\' } ', e.opts.messages !== !1 && (a += ' , message: \'should NOT have ' + Pe + ' than ' + ye + ' properties matching pattern "' + e.util.escapeQuotes(N) + '"\' '), e.opts.verbose && (a += ' , schema: validate.schema' + n + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + h + ' '), a += ' } ') : a += ' {} ';
                          var B = a;
                          a = K.pop(), a += !e.compositeRule && c ? e.async ? ' throw new ValidationError([' + B + ']); ' : ' validate.errors = [' + B + ']; return false; ' : ' var err = ' + B + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', a += ' } ', void 0 !== ve && (a += ' else ');
                        }
                        if (void 0 !== ve) {
                          var ye = ve, ge = 'maximum', Pe = 'more';
                          a += ' ' + u + ' = pgPropCount' + s + ' <= ' + ve + '; ', l = e.errSchemaPath + '/patternGroups/maximum', a += '  if (!' + u + ') {   ';
                          var K = K || [];
                          K.push(a), a = '', e.createErrors !== !1 ? (a += ' { keyword: \'' + (t || 'patternGroups') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(l) + ' , params: { reason: \'' + ge + '\', limit: ' + ye + ', pattern: \'' + e.util.escapeQuotes(N) + '\' } ', e.opts.messages !== !1 && (a += ' , message: \'should NOT have ' + Pe + ' than ' + ye + ' properties matching pattern "' + e.util.escapeQuotes(N) + '"\' '), e.opts.verbose && (a += ' , schema: validate.schema' + n + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + h + ' '), a += ' } ') : a += ' {} ';
                          var B = a;
                          a = K.pop(), a += !e.compositeRule && c ? e.async ? ' throw new ValidationError([' + B + ']); ' : ' validate.errors = [' + B + ']; return false; ' : ' var err = ' + B + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', a += ' } ';
                        }
                        l = G, c && (a += ' if (' + u + ') { ', p += '}');
                      }
                    }
                  }
              }
              return c && (a += ' ' + p + ' if (' + f + ' == errors) {'), a = e.util.cleanUpCode(a);
            };
          },
          {}
        ],
        32: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t, a, s, o = ' ', i = e.level, n = e.dataLevel, l = e.schema[r], c = e.errSchemaPath + '/' + r, h = !e.opts.allErrors, u = 'data' + (n || ''), f = 'valid' + i;
              if ('#' == l || '#/' == l)
                e.isRoot ? (a = e.async, s = 'validate') : (a = e.root.schema.$async === !0, s = 'root.refVal[0]');
              else {
                var d = e.resolveRef(e.baseId, l, e.isRoot);
                if (void 0 === d) {
                  var p = 'can\'t resolve reference ' + l + ' from id ' + e.baseId;
                  if ('fail' == e.opts.missingRefs) {
                    console.log(p);
                    var m = m || [];
                    m.push(o), o = '', e.createErrors !== !1 ? (o += ' { keyword: \'' + (t || '$ref') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(c) + ' , params: { ref: \'' + e.util.escapeQuotes(l) + '\' } ', e.opts.messages !== !1 && (o += ' , message: \'can\\\'t resolve reference ' + e.util.escapeQuotes(l) + '\' '), e.opts.verbose && (o += ' , schema: ' + e.util.toQuotedString(l) + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + u + ' '), o += ' } ') : o += ' {} ';
                    var v = o;
                    o = m.pop(), o += !e.compositeRule && h ? e.async ? ' throw new ValidationError([' + v + ']); ' : ' validate.errors = [' + v + ']; return false; ' : ' var err = ' + v + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', h && (o += ' if (false) { ');
                  } else {
                    if ('ignore' != e.opts.missingRefs) {
                      var y = new Error(p);
                      throw y.missingRef = e.resolve.url(e.baseId, l), y.missingSchema = e.resolve.normalizeId(e.resolve.fullPath(y.missingRef)), y;
                    }
                    console.log(p), h && (o += ' if (true) { ');
                  }
                } else if (d.inline) {
                  var g = e.util.copy(e);
                  g.level++;
                  var P = 'valid' + g.level;
                  g.schema = d.schema, g.schemaPath = '', g.errSchemaPath = l;
                  var E = e.validate(g).replace(/validate\.schema/g, d.code);
                  o += ' ' + E + ' ', h && (o += ' if (' + P + ') { ');
                } else
                  a = d.$async === !0, s = d.code;
              }
              if (s) {
                var m = m || [];
                m.push(o), o = '', o += e.opts.passContext ? ' ' + s + '.call(this, ' : ' ' + s + '( ', o += ' ' + u + ', (dataPath || \'\')', '""' != e.errorPath && (o += ' + ' + e.errorPath);
                var b = n ? 'data' + (n - 1 || '') : 'parentData', w = n ? e.dataPathArr[n] : 'parentDataProperty';
                o += ' , ' + b + ' , ' + w + ', rootData)  ';
                var j = o;
                if (o = m.pop(), a) {
                  if (!e.async)
                    throw new Error('async schema referenced by sync schema');
                  o += ' try { ', h && (o += 'var ' + f + ' ='), o += ' ' + e.yieldAwait + ' ' + j + '; } catch (e) { if (!(e instanceof ValidationError)) throw e; if (vErrors === null) vErrors = e.errors; else vErrors = vErrors.concat(e.errors); errors = vErrors.length; } ', h && (o += ' if (' + f + ') { ');
                } else
                  o += ' if (!' + j + ') { if (vErrors === null) vErrors = ' + s + '.errors; else vErrors = vErrors.concat(' + s + '.errors); errors = vErrors.length; } ', h && (o += ' else { ');
              }
              return o;
            };
          },
          {}
        ],
        33: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t, a, s = ' ', o = e.level, i = e.dataLevel, n = e.schema[r], l = e.schemaPath + e.util.getProperty(r), c = e.errSchemaPath + '/' + r, h = !e.opts.allErrors, u = 'data' + (i || ''), f = 'valid' + o, d = e.opts.v5 && n && n.$data;
              d ? (s += ' var schema' + o + ' = ' + e.util.getData(n.$data, i, e.dataPathArr) + '; ', a = 'schema' + o) : a = n;
              var p = 'schema' + o;
              if (!d)
                if (n.length < e.opts.loopRequired && e.schema.properties && Object.keys(e.schema.properties).length) {
                  var m = [], v = n;
                  if (v)
                    for (var y, g = -1, P = v.length - 1; g < P;) {
                      y = v[g += 1];
                      var E = e.schema.properties[y];
                      E && e.util.schemaHasRules(E, e.RULES.all) || (m[m.length] = y);
                    }
                } else
                  var m = n;
              if (d || m.length) {
                var b = e.errorPath, w = d || m.length >= e.opts.loopRequired;
                if (h)
                  if (s += ' var missing' + o + '; ', w) {
                    d || (s += ' var ' + p + ' = validate.schema' + l + '; ');
                    var j = 'i' + o, S = 'schema' + o + '[' + j + ']', $ = '\' + ' + S + ' + \'';
                    e.opts._errorDataPathProperty && (e.errorPath = e.util.getPathExpr(b, S, e.opts.jsonPointers)), s += ' var ' + f + ' = true; ', d && (s += ' if (schema' + o + ' === undefined) ' + f + ' = true; else if (!Array.isArray(schema' + o + ')) ' + f + ' = false; else {'), s += ' for (var ' + j + ' = 0; ' + j + ' < ' + p + '.length; ' + j + '++) { ' + f + ' = ' + u + '[' + p + '[' + j + ']] !== undefined; if (!' + f + ') break; } ', d && (s += '  }  '), s += '  if (!' + f + ') {   ';
                    var x = x || [];
                    x.push(s), s = '', e.createErrors !== !1 ? (s += ' { keyword: \'' + (t || 'required') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(c) + ' , params: { missingProperty: \'' + $ + '\' } ', e.opts.messages !== !1 && (s += ' , message: \'', s += e.opts._errorDataPathProperty ? 'is a required property' : 'should have required property \\\'' + $ + '\\\'', s += '\' '), e.opts.verbose && (s += ' , schema: validate.schema' + l + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + u + ' '), s += ' } ') : s += ' {} ';
                    var _ = s;
                    s = x.pop(), s += !e.compositeRule && h ? e.async ? ' throw new ValidationError([' + _ + ']); ' : ' validate.errors = [' + _ + ']; return false; ' : ' var err = ' + _ + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', s += ' } else { ';
                  } else {
                    s += ' if ( ';
                    var O = m;
                    if (O)
                      for (var R, j = -1, I = O.length - 1; j < I;) {
                        R = O[j += 1], j && (s += ' || ');
                        var A = e.util.getProperty(R);
                        s += ' ( ' + u + A + ' === undefined && (missing' + o + ' = ' + e.util.toQuotedString(e.opts.jsonPointers ? R : A) + ') ) ';
                      }
                    s += ') {  ';
                    var S = 'missing' + o, $ = '\' + ' + S + ' + \'';
                    e.opts._errorDataPathProperty && (e.errorPath = e.opts.jsonPointers ? e.util.getPathExpr(b, S, !0) : b + ' + ' + S);
                    var x = x || [];
                    x.push(s), s = '', e.createErrors !== !1 ? (s += ' { keyword: \'' + (t || 'required') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(c) + ' , params: { missingProperty: \'' + $ + '\' } ', e.opts.messages !== !1 && (s += ' , message: \'', s += e.opts._errorDataPathProperty ? 'is a required property' : 'should have required property \\\'' + $ + '\\\'', s += '\' '), e.opts.verbose && (s += ' , schema: validate.schema' + l + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + u + ' '), s += ' } ') : s += ' {} ';
                    var _ = s;
                    s = x.pop(), s += !e.compositeRule && h ? e.async ? ' throw new ValidationError([' + _ + ']); ' : ' validate.errors = [' + _ + ']; return false; ' : ' var err = ' + _ + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', s += ' } else { ';
                  }
                else if (w) {
                  d || (s += ' var ' + p + ' = validate.schema' + l + '; ');
                  var j = 'i' + o, S = 'schema' + o + '[' + j + ']', $ = '\' + ' + S + ' + \'';
                  e.opts._errorDataPathProperty && (e.errorPath = e.util.getPathExpr(b, S, e.opts.jsonPointers)), d && (s += ' if (' + p + ' && !Array.isArray(' + p + ')) {  var err =   ', e.createErrors !== !1 ? (s += ' { keyword: \'' + (t || 'required') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(c) + ' , params: { missingProperty: \'' + $ + '\' } ', e.opts.messages !== !1 && (s += ' , message: \'', s += e.opts._errorDataPathProperty ? 'is a required property' : 'should have required property \\\'' + $ + '\\\'', s += '\' '), e.opts.verbose && (s += ' , schema: validate.schema' + l + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + u + ' '), s += ' } ') : s += ' {} ', s += ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } else if (' + p + ' !== undefined) { '), s += ' for (var ' + j + ' = 0; ' + j + ' < ' + p + '.length; ' + j + '++) { if (' + u + '[' + p + '[' + j + ']] === undefined) {  var err =   ', e.createErrors !== !1 ? (s += ' { keyword: \'' + (t || 'required') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(c) + ' , params: { missingProperty: \'' + $ + '\' } ', e.opts.messages !== !1 && (s += ' , message: \'', s += e.opts._errorDataPathProperty ? 'is a required property' : 'should have required property \\\'' + $ + '\\\'', s += '\' '), e.opts.verbose && (s += ' , schema: validate.schema' + l + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + u + ' '), s += ' } ') : s += ' {} ', s += ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } } ', d && (s += '  }  ');
                } else {
                  var k = m;
                  if (k)
                    for (var q, D = -1, L = k.length - 1; D < L;) {
                      q = k[D += 1];
                      var A = e.util.getProperty(q), $ = e.util.escapeQuotes(q);
                      e.opts._errorDataPathProperty && (e.errorPath = e.util.getPath(b, q, e.opts.jsonPointers)), s += ' if (' + u + A + ' === undefined) {  var err =   ', e.createErrors !== !1 ? (s += ' { keyword: \'' + (t || 'required') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(c) + ' , params: { missingProperty: \'' + $ + '\' } ', e.opts.messages !== !1 && (s += ' , message: \'', s += e.opts._errorDataPathProperty ? 'is a required property' : 'should have required property \\\'' + $ + '\\\'', s += '\' '), e.opts.verbose && (s += ' , schema: validate.schema' + l + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + u + ' '), s += ' } ') : s += ' {} ', s += ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; } ';
                    }
                }
                e.errorPath = b;
              } else
                h && (s += ' if (true) {');
              return s;
            };
          },
          {}
        ],
        34: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t, a = ' ', s = e.level, o = e.dataLevel, i = e.schema[r], n = e.schemaPath + e.util.getProperty(r), l = e.errSchemaPath + '/' + r, c = !e.opts.allErrors, h = 'data' + (o || ''), u = 'valid' + s, f = 'errs__' + s, d = e.util.copy(e), p = '';
              d.level++;
              var m, v = 'valid' + d.level, y = 'ifPassed' + e.level, g = d.baseId;
              a += 'var ' + y + ';';
              var P = i;
              if (P)
                for (var E, b = -1, w = P.length - 1; b < w;) {
                  if (E = P[b += 1], b && !m && (a += ' if (!' + y + ') { ', p += '}'), E.if && e.util.schemaHasRules(E.if, e.RULES.all)) {
                    a += ' var ' + f + ' = errors;   ';
                    var j = e.compositeRule;
                    if (e.compositeRule = d.compositeRule = !0, d.createErrors = !1, d.schema = E.if, d.schemaPath = n + '[' + b + '].if', d.errSchemaPath = l + '/' + b + '/if', a += '  ' + e.validate(d) + ' ', d.baseId = g, d.createErrors = !0, e.compositeRule = d.compositeRule = j, a += ' ' + y + ' = ' + v + '; if (' + y + ') {  ', 'boolean' == typeof E.then) {
                      if (E.then === !1) {
                        var S = S || [];
                        S.push(a), a = '', e.createErrors !== !1 ? (a += ' { keyword: \'' + (t || 'switch') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(l) + ' , params: { caseIndex: ' + b + ' } ', e.opts.messages !== !1 && (a += ' , message: \'should pass "switch" keyword validation\' '), e.opts.verbose && (a += ' , schema: validate.schema' + n + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + h + ' '), a += ' } ') : a += ' {} ';
                        var $ = a;
                        a = S.pop(), a += !e.compositeRule && c ? e.async ? ' throw new ValidationError([' + $ + ']); ' : ' validate.errors = [' + $ + ']; return false; ' : ' var err = ' + $ + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
                      }
                      a += ' var ' + v + ' = ' + E.then + '; ';
                    } else
                      d.schema = E.then, d.schemaPath = n + '[' + b + '].then', d.errSchemaPath = l + '/' + b + '/then', a += '  ' + e.validate(d) + ' ', d.baseId = g;
                    a += '  } else {  errors = ' + f + '; if (vErrors !== null) { if (' + f + ') vErrors.length = ' + f + '; else vErrors = null; } } ';
                  } else if (a += ' ' + y + ' = true;  ', 'boolean' == typeof E.then) {
                    if (E.then === !1) {
                      var S = S || [];
                      S.push(a), a = '', e.createErrors !== !1 ? (a += ' { keyword: \'' + (t || 'switch') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(l) + ' , params: { caseIndex: ' + b + ' } ', e.opts.messages !== !1 && (a += ' , message: \'should pass "switch" keyword validation\' '), e.opts.verbose && (a += ' , schema: validate.schema' + n + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + h + ' '), a += ' } ') : a += ' {} ';
                      var $ = a;
                      a = S.pop(), a += !e.compositeRule && c ? e.async ? ' throw new ValidationError([' + $ + ']); ' : ' validate.errors = [' + $ + ']; return false; ' : ' var err = ' + $ + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ';
                    }
                    a += ' var ' + v + ' = ' + E.then + '; ';
                  } else
                    d.schema = E.then, d.schemaPath = n + '[' + b + '].then', d.errSchemaPath = l + '/' + b + '/then', a += '  ' + e.validate(d) + ' ', d.baseId = g;
                  m = E.continue;
                }
              return a += '' + p + 'var ' + u + ' = ' + v + '; ', a = e.util.cleanUpCode(a);
            };
          },
          {}
        ],
        35: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              var t, a, s = ' ', o = e.level, i = e.dataLevel, n = e.schema[r], l = e.schemaPath + e.util.getProperty(r), c = e.errSchemaPath + '/' + r, h = !e.opts.allErrors, u = 'data' + (i || ''), f = 'valid' + o, d = e.opts.v5 && n && n.$data;
              if (d ? (s += ' var schema' + o + ' = ' + e.util.getData(n.$data, i, e.dataPathArr) + '; ', a = 'schema' + o) : a = n, (n || d) && e.opts.uniqueItems !== !1) {
                d && (s += ' var ' + f + '; if (' + a + ' === false || ' + a + ' === undefined) ' + f + ' = true; else if (typeof ' + a + ' != \'boolean\') ' + f + ' = false; else { '), s += ' var ' + f + ' = true; if (' + u + '.length > 1) { var i = ' + u + '.length, j; outer: for (;i--;) { for (j = i; j--;) { if (equal(' + u + '[i], ' + u + '[j])) { ' + f + ' = false; break outer; } } } } ', d && (s += '  }  '), s += ' if (!' + f + ') {   ';
                var p = p || [];
                p.push(s), s = '', e.createErrors !== !1 ? (s += ' { keyword: \'' + (t || 'uniqueItems') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(c) + ' , params: { i: i, j: j } ', e.opts.messages !== !1 && (s += ' , message: \'should NOT have duplicate items (items ## \' + j + \' and \' + i + \' are identical)\' '), e.opts.verbose && (s += ' , schema:  ', s += d ? 'validate.schema' + l : '' + n, s += '         , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + u + ' '), s += ' } ') : s += ' {} ';
                var m = s;
                s = p.pop(), s += !e.compositeRule && h ? e.async ? ' throw new ValidationError([' + m + ']); ' : ' validate.errors = [' + m + ']; return false; ' : ' var err = ' + m + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', s += ' } ', h && (s += ' else { ');
              } else
                h && (s += ' if (true) { ');
              return s;
            };
          },
          {}
        ],
        36: [
          function (e, r, t) {
            'use strict';
            r.exports = function (e, r) {
              function t(e) {
                for (var r = 0; r < e.rules.length; r++)
                  if (a(e.rules[r]))
                    return !0;
              }
              function a(r) {
                return void 0 !== e.schema[r.keyword] || 'properties' == r.keyword && (e.schema.additionalProperties === !1 || 'object' == typeof e.schema.additionalProperties || e.schema.patternProperties && Object.keys(e.schema.patternProperties).length || e.opts.v5 && e.schema.patternGroups && Object.keys(e.schema.patternGroups).length);
              }
              var s = '', o = e.schema.$async === !0;
              if (e.isTop) {
                var i = e.isTop, n = e.level = 0, l = e.dataLevel = 0, c = 'data';
                if (e.rootId = e.resolve.fullPath(e.root.schema.id), e.baseId = e.baseId || e.rootId, o) {
                  e.async = !0;
                  var h = 'es7' == e.opts.async;
                  e.yieldAwait = h ? 'await' : 'yield';
                }
                delete e.isTop, e.dataPathArr = [void 0], s += ' var validate = ', o ? h ? s += ' (async function ' : ('co*' == e.opts.async && (s += 'co.wrap'), s += '(function* ') : s += ' (function ', s += ' (data, dataPath, parentData, parentDataProperty, rootData) { \'use strict\'; var vErrors = null; ', s += ' var errors = 0;     ', s += ' if (rootData === undefined) rootData = data;';
              } else {
                var n = e.level, l = e.dataLevel, c = 'data' + (l || '');
                if (e.schema.id && (e.baseId = e.resolve.url(e.baseId, e.schema.id)), o && !e.async)
                  throw new Error('async schema in sync schema');
                s += ' var errs_' + n + ' = errors;';
              }
              var u, f = 'valid' + n, d = !e.opts.allErrors, p = '', m = '', v = e.schema.type, y = Array.isArray(v);
              if (v && e.opts.coerceTypes) {
                var g = e.util.coerceToTypes(e.opts.coerceTypes, v);
                if (g) {
                  var P = e.schemaPath + '.type', E = e.errSchemaPath + '/type', b = y ? 'checkDataTypes' : 'checkDataType';
                  s += ' if (' + e.util[b](v, c, !0) + ') {  ';
                  var w = 'dataType' + n, j = 'coerced' + n;
                  s += ' var ' + w + ' = typeof ' + c + '; ', 'array' == e.opts.coerceTypes && (s += ' if (' + w + ' == \'object\' && Array.isArray(' + c + ')) ' + w + ' = \'array\'; '), s += ' var ' + j + ' = undefined; ';
                  var S = '', $ = g;
                  if ($)
                    for (var x, _ = -1, O = $.length - 1; _ < O;)
                      x = $[_ += 1], _ && (s += ' if (' + j + ' === undefined) { ', S += '}'), 'array' == e.opts.coerceTypes && 'array' != x && (s += ' if (' + w + ' == \'array\' && ' + c + '.length == 1) { ' + j + ' = ' + c + ' = ' + c + '[0]; ' + w + ' = typeof ' + c + ';  } '), 'string' == x ? s += ' if (' + w + ' == \'number\' || ' + w + ' == \'boolean\') ' + j + ' = \'\' + ' + c + '; else if (' + c + ' === null) ' + j + ' = \'\'; ' : 'number' == x || 'integer' == x ? (s += ' if (' + w + ' == \'boolean\' || ' + c + ' === null || (' + w + ' == \'string\' && ' + c + ' && ' + c + ' == +' + c + ' ', 'integer' == x && (s += ' && !(' + c + ' % 1)'), s += ')) ' + j + ' = +' + c + '; ') : 'boolean' == x ? s += ' if (' + c + ' === \'false\' || ' + c + ' === 0 || ' + c + ' === null) ' + j + ' = false; else if (' + c + ' === \'true\' || ' + c + ' === 1) ' + j + ' = true; ' : 'null' == x ? s += ' if (' + c + ' === \'\' || ' + c + ' === 0 || ' + c + ' === false) ' + j + ' = null; ' : 'array' == e.opts.coerceTypes && 'array' == x && (s += ' if (' + w + ' == \'string\' || ' + w + ' == \'number\' || ' + w + ' == \'boolean\' || ' + c + ' == null) ' + j + ' = [' + c + ']; ');
                  s += ' ' + S + ' if (' + j + ' === undefined) {   ';
                  var R = R || [];
                  R.push(s), s = '', e.createErrors !== !1 ? (s += ' { keyword: \'' + (u || 'type') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(E) + ' , params: { type: \'', s += y ? '' + v.join(',') : '' + v, s += '\' } ', e.opts.messages !== !1 && (s += ' , message: \'should be ', s += y ? '' + v.join(',') : '' + v, s += '\' '), e.opts.verbose && (s += ' , schema: validate.schema' + P + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + c + ' '), s += ' } ') : s += ' {} ';
                  var I = s;
                  s = R.pop(), s += !e.compositeRule && d ? e.async ? ' throw new ValidationError([' + I + ']); ' : ' validate.errors = [' + I + ']; return false; ' : ' var err = ' + I + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', s += ' } else {  ';
                  var A = l ? 'data' + (l - 1 || '') : 'parentData', k = l ? e.dataPathArr[l] : 'parentDataProperty';
                  s += ' ' + c + ' = ' + j + '; ', l || (s += 'if (' + A + ' !== undefined)'), s += ' ' + A + '[' + k + '] = ' + j + '; } } ';
                }
              }
              var q;
              if (e.schema.$ref && (q = e.util.schemaHasRulesExcept(e.schema, e.RULES.all, '$ref'))) {
                if ('fail' == e.opts.extendRefs)
                  throw new Error('$ref: validation keywords used in schema at path "' + e.errSchemaPath + '"');
                'ignore' == e.opts.extendRefs ? (q = !1, console.log('$ref: keywords ignored in schema at path "' + e.errSchemaPath + '"')) : e.opts.extendRefs !== !0 && console.log('$ref: all keywords used in schema at path "' + e.errSchemaPath + '". It will change in the next major version, see issue #260. Use option { extendRefs: true } to keep current behaviour');
              }
              if (e.schema.$ref && !q)
                s += ' ' + e.RULES.all.$ref.code(e, '$ref') + ' ', d && (s += ' } if (errors === ', s += i ? '0' : 'errs_' + n, s += ') { ', m += '}');
              else {
                var D = e.RULES;
                if (D)
                  for (var L, Q = -1, C = D.length - 1; Q < C;)
                    if (L = D[Q += 1], t(L)) {
                      if (L.type && (s += ' if (' + e.util.checkDataType(L.type, c) + ') { '), e.opts.useDefaults && !e.compositeRule)
                        if ('object' == L.type && e.schema.properties) {
                          var V = e.schema.properties, z = Object.keys(V), U = z;
                          if (U)
                            for (var T, M = -1, N = U.length - 1; M < N;) {
                              T = U[M += 1];
                              var F = V[T];
                              if (void 0 !== F.default) {
                                var H = c + e.util.getProperty(T);
                                s += '  if (' + H + ' === undefined) ' + H + ' = ', s += 'shared' == e.opts.useDefaults ? ' ' + e.useDefault(F.default) + ' ' : ' ' + JSON.stringify(F.default) + ' ', s += '; ';
                              }
                            }
                        } else if ('array' == L.type && Array.isArray(e.schema.items)) {
                          var J = e.schema.items;
                          if (J)
                            for (var F, _ = -1, G = J.length - 1; _ < G;)
                              if (F = J[_ += 1], void 0 !== F.default) {
                                var H = c + '[' + _ + ']';
                                s += '  if (' + H + ' === undefined) ' + H + ' = ', s += 'shared' == e.opts.useDefaults ? ' ' + e.useDefault(F.default) + ' ' : ' ' + JSON.stringify(F.default) + ' ', s += '; ';
                              }
                        }
                      var K = L.rules;
                      if (K)
                        for (var B, Z = -1, Y = K.length - 1; Z < Y;)
                          B = K[Z += 1], a(B) && (s += ' ' + B.code(e, B.keyword) + ' ', d && (p += '}'));
                      if (d && (s += ' ' + p + ' ', p = ''), L.type && (s += ' } ', v && v === L.type)) {
                        var W = !0;
                        s += ' else { ';
                        var P = e.schemaPath + '.type', E = e.errSchemaPath + '/type', R = R || [];
                        R.push(s), s = '', e.createErrors !== !1 ? (s += ' { keyword: \'' + (u || 'type') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(E) + ' , params: { type: \'', s += y ? '' + v.join(',') : '' + v, s += '\' } ', e.opts.messages !== !1 && (s += ' , message: \'should be ', s += y ? '' + v.join(',') : '' + v, s += '\' '), e.opts.verbose && (s += ' , schema: validate.schema' + P + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + c + ' '), s += ' } ') : s += ' {} ';
                        var I = s;
                        s = R.pop(), s += !e.compositeRule && d ? e.async ? ' throw new ValidationError([' + I + ']); ' : ' validate.errors = [' + I + ']; return false; ' : ' var err = ' + I + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', s += ' } ';
                      }
                      d && (s += ' if (errors === ', s += i ? '0' : 'errs_' + n, s += ') { ', m += '}');
                    }
              }
              if (v && !W && (!e.opts.coerceTypes || !g)) {
                var P = e.schemaPath + '.type', E = e.errSchemaPath + '/type', b = y ? 'checkDataTypes' : 'checkDataType';
                s += ' if (' + e.util[b](v, c, !0) + ') {   ';
                var R = R || [];
                R.push(s), s = '', e.createErrors !== !1 ? (s += ' { keyword: \'' + (u || 'type') + '\' , dataPath: (dataPath || \'\') + ' + e.errorPath + ' , schemaPath: ' + e.util.toQuotedString(E) + ' , params: { type: \'', s += y ? '' + v.join(',') : '' + v, s += '\' } ', e.opts.messages !== !1 && (s += ' , message: \'should be ', s += y ? '' + v.join(',') : '' + v, s += '\' '), e.opts.verbose && (s += ' , schema: validate.schema' + P + ' , parentSchema: validate.schema' + e.schemaPath + ' , data: ' + c + ' '), s += ' } ') : s += ' {} ';
                var I = s;
                s = R.pop(), s += !e.compositeRule && d ? e.async ? ' throw new ValidationError([' + I + ']); ' : ' validate.errors = [' + I + ']; return false; ' : ' var err = ' + I + ';  if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ', s += ' }';
              }
              return d && (s += ' ' + m + ' '), i ? (o ? (s += ' if (errors === 0) return true;           ', s += ' else throw new ValidationError(vErrors); ') : (s += ' validate.errors = vErrors; ', s += ' return errors === 0;       '), s += ' }); return validate;') : s += ' var ' + f + ' = errors === errs_' + n + ';', s = e.util.cleanUpCode(s), i && d && (s = e.util.cleanUpVarErrors(s, o)), s;
            };
          },
          {}
        ],
        37: [
          function (e, r, t) {
            'use strict';
            function a(e, r) {
              function t(e, r, t) {
                for (var a, o = 0; o < s.length; o++) {
                  var i = s[o];
                  if (i.type == r) {
                    a = i;
                    break;
                  }
                }
                a || (a = {
                  type: r,
                  rules: []
                }, s.push(a));
                var l = {
                  keyword: e,
                  definition: t,
                  custom: !0,
                  code: n
                };
                a.rules.push(l), s.custom[e] = l;
              }
              function a(e) {
                if (!s.types[e])
                  throw new Error('Unknown type ' + e);
              }
              var s = this.RULES;
              if (s.keywords[e])
                throw new Error('Keyword ' + e + ' is already defined');
              if (!i.test(e))
                throw new Error('Keyword ' + e + ' is not a valid identifier');
              if (r) {
                if (r.macro && void 0 !== r.valid)
                  throw new Error('"valid" option cannot be used with macro keywords');
                var o = r.type;
                if (Array.isArray(o)) {
                  var l, c = o.length;
                  for (l = 0; l < c; l++)
                    a(o[l]);
                  for (l = 0; l < c; l++)
                    t(e, o[l], r);
                } else
                  o && a(o), t(e, o, r);
                var h = r.$data === !0 && this._opts.v5;
                if (h && !r.validate)
                  throw new Error('$data support: "validate" function is not defined');
                var u = r.metaSchema;
                u && (h && (u = {
                  anyOf: [
                    u,
                    { $ref: 'https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-v5.json#/definitions/$data' }
                  ]
                }), r.validateSchema = this.compile(u, !0));
              }
              s.keywords[e] = s.all[e] = !0;
            }
            function s(e) {
              var r = this.RULES.custom[e];
              return r ? r.definition : this.RULES.keywords[e] || !1;
            }
            function o(e) {
              var r = this.RULES;
              delete r.keywords[e], delete r.all[e], delete r.custom[e];
              for (var t = 0; t < r.length; t++)
                for (var a = r[t].rules, s = 0; s < a.length; s++)
                  if (a[s].keyword == e) {
                    a.splice(s, 1);
                    break;
                  }
            }
            var i = /^[a-z_$][a-z0-9_$\-]*$/i, n = e('./dotjs/custom');
            r.exports = {
              add: a,
              get: s,
              remove: o
            };
          },
          { './dotjs/custom': 21 }
        ],
        38: [
          function (e, r, t) {
            r.exports = {
              id: 'http://json-schema.org/draft-04/schema#',
              $schema: 'http://json-schema.org/draft-04/schema#',
              description: 'Core schema meta-schema',
              definitions: {
                schemaArray: {
                  type: 'array',
                  minItems: 1,
                  items: { $ref: '#' }
                },
                positiveInteger: {
                  type: 'integer',
                  minimum: 0
                },
                positiveIntegerDefault0: {
                  allOf: [
                    { $ref: '#/definitions/positiveInteger' },
                    { default: 0 }
                  ]
                },
                simpleTypes: {
                  enum: [
                    'array',
                    'boolean',
                    'integer',
                    'null',
                    'number',
                    'object',
                    'string'
                  ]
                },
                stringArray: {
                  type: 'array',
                  items: { type: 'string' },
                  minItems: 1,
                  uniqueItems: !0
                }
              },
              type: 'object',
              properties: {
                id: {
                  type: 'string',
                  format: 'uri'
                },
                $schema: {
                  type: 'string',
                  format: 'uri'
                },
                title: { type: 'string' },
                description: { type: 'string' },
                default: {},
                multipleOf: {
                  type: 'number',
                  minimum: 0,
                  exclusiveMinimum: !0
                },
                maximum: { type: 'number' },
                exclusiveMaximum: {
                  type: 'boolean',
                  default: !1
                },
                minimum: { type: 'number' },
                exclusiveMinimum: {
                  type: 'boolean',
                  default: !1
                },
                maxLength: { $ref: '#/definitions/positiveInteger' },
                minLength: { $ref: '#/definitions/positiveIntegerDefault0' },
                pattern: {
                  type: 'string',
                  format: 'regex'
                },
                additionalItems: {
                  anyOf: [
                    { type: 'boolean' },
                    { $ref: '#' }
                  ],
                  default: {}
                },
                items: {
                  anyOf: [
                    { $ref: '#' },
                    { $ref: '#/definitions/schemaArray' }
                  ],
                  default: {}
                },
                maxItems: { $ref: '#/definitions/positiveInteger' },
                minItems: { $ref: '#/definitions/positiveIntegerDefault0' },
                uniqueItems: {
                  type: 'boolean',
                  default: !1
                },
                maxProperties: { $ref: '#/definitions/positiveInteger' },
                minProperties: { $ref: '#/definitions/positiveIntegerDefault0' },
                required: { $ref: '#/definitions/stringArray' },
                additionalProperties: {
                  anyOf: [
                    { type: 'boolean' },
                    { $ref: '#' }
                  ],
                  default: {}
                },
                definitions: {
                  type: 'object',
                  additionalProperties: { $ref: '#' },
                  default: {}
                },
                properties: {
                  type: 'object',
                  additionalProperties: { $ref: '#' },
                  default: {}
                },
                patternProperties: {
                  type: 'object',
                  additionalProperties: { $ref: '#' },
                  default: {}
                },
                dependencies: {
                  type: 'object',
                  additionalProperties: {
                    anyOf: [
                      { $ref: '#' },
                      { $ref: '#/definitions/stringArray' }
                    ]
                  }
                },
                enum: {
                  type: 'array',
                  minItems: 1,
                  uniqueItems: !0
                },
                type: {
                  anyOf: [
                    { $ref: '#/definitions/simpleTypes' },
                    {
                      type: 'array',
                      items: { $ref: '#/definitions/simpleTypes' },
                      minItems: 1,
                      uniqueItems: !0
                    }
                  ]
                },
                allOf: { $ref: '#/definitions/schemaArray' },
                anyOf: { $ref: '#/definitions/schemaArray' },
                oneOf: { $ref: '#/definitions/schemaArray' },
                not: { $ref: '#' }
              },
              dependencies: {
                exclusiveMaximum: ['maximum'],
                exclusiveMinimum: ['minimum']
              },
              default: {}
            };
          },
          {}
        ],
        39: [
          function (e, r, t) {
            r.exports = {
              id: 'https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-v5.json#',
              $schema: 'http://json-schema.org/draft-04/schema#',
              description: 'Core schema meta-schema (v5 proposals)',
              definitions: {
                schemaArray: {
                  type: 'array',
                  minItems: 1,
                  items: { $ref: '#' }
                },
                positiveInteger: {
                  type: 'integer',
                  minimum: 0
                },
                positiveIntegerDefault0: {
                  allOf: [
                    { $ref: '#/definitions/positiveInteger' },
                    { default: 0 }
                  ]
                },
                simpleTypes: {
                  enum: [
                    'array',
                    'boolean',
                    'integer',
                    'null',
                    'number',
                    'object',
                    'string'
                  ]
                },
                stringArray: {
                  type: 'array',
                  items: { type: 'string' },
                  minItems: 1,
                  uniqueItems: !0
                },
                $data: {
                  type: 'object',
                  required: ['$data'],
                  properties: {
                    $data: {
                      type: 'string',
                      anyOf: [
                        { format: 'relative-json-pointer' },
                        { format: 'json-pointer' }
                      ]
                    }
                  },
                  additionalProperties: !1
                }
              },
              type: 'object',
              properties: {
                id: {
                  type: 'string',
                  format: 'uri'
                },
                $schema: {
                  type: 'string',
                  format: 'uri'
                },
                title: { type: 'string' },
                description: { type: 'string' },
                default: {},
                multipleOf: {
                  anyOf: [
                    {
                      type: 'number',
                      minimum: 0,
                      exclusiveMinimum: !0
                    },
                    { $ref: '#/definitions/$data' }
                  ]
                },
                maximum: {
                  anyOf: [
                    { type: 'number' },
                    { $ref: '#/definitions/$data' }
                  ]
                },
                exclusiveMaximum: {
                  anyOf: [
                    {
                      type: 'boolean',
                      default: !1
                    },
                    { $ref: '#/definitions/$data' }
                  ]
                },
                minimum: {
                  anyOf: [
                    { type: 'number' },
                    { $ref: '#/definitions/$data' }
                  ]
                },
                exclusiveMinimum: {
                  anyOf: [
                    {
                      type: 'boolean',
                      default: !1
                    },
                    { $ref: '#/definitions/$data' }
                  ]
                },
                maxLength: {
                  anyOf: [
                    { $ref: '#/definitions/positiveInteger' },
                    { $ref: '#/definitions/$data' }
                  ]
                },
                minLength: {
                  anyOf: [
                    { $ref: '#/definitions/positiveIntegerDefault0' },
                    { $ref: '#/definitions/$data' }
                  ]
                },
                pattern: {
                  anyOf: [
                    {
                      type: 'string',
                      format: 'regex'
                    },
                    { $ref: '#/definitions/$data' }
                  ]
                },
                additionalItems: {
                  anyOf: [
                    { type: 'boolean' },
                    { $ref: '#' },
                    { $ref: '#/definitions/$data' }
                  ],
                  default: {}
                },
                items: {
                  anyOf: [
                    { $ref: '#' },
                    { $ref: '#/definitions/schemaArray' }
                  ],
                  default: {}
                },
                maxItems: {
                  anyOf: [
                    { $ref: '#/definitions/positiveInteger' },
                    { $ref: '#/definitions/$data' }
                  ]
                },
                minItems: {
                  anyOf: [
                    { $ref: '#/definitions/positiveIntegerDefault0' },
                    { $ref: '#/definitions/$data' }
                  ]
                },
                uniqueItems: {
                  anyOf: [
                    {
                      type: 'boolean',
                      default: !1
                    },
                    { $ref: '#/definitions/$data' }
                  ]
                },
                maxProperties: {
                  anyOf: [
                    { $ref: '#/definitions/positiveInteger' },
                    { $ref: '#/definitions/$data' }
                  ]
                },
                minProperties: {
                  anyOf: [
                    { $ref: '#/definitions/positiveIntegerDefault0' },
                    { $ref: '#/definitions/$data' }
                  ]
                },
                required: {
                  anyOf: [
                    { $ref: '#/definitions/stringArray' },
                    { $ref: '#/definitions/$data' }
                  ]
                },
                additionalProperties: {
                  anyOf: [
                    { type: 'boolean' },
                    { $ref: '#' },
                    { $ref: '#/definitions/$data' }
                  ],
                  default: {}
                },
                definitions: {
                  type: 'object',
                  additionalProperties: { $ref: '#' },
                  default: {}
                },
                properties: {
                  type: 'object',
                  additionalProperties: { $ref: '#' },
                  default: {}
                },
                patternProperties: {
                  type: 'object',
                  additionalProperties: { $ref: '#' },
                  default: {}
                },
                dependencies: {
                  type: 'object',
                  additionalProperties: {
                    anyOf: [
                      { $ref: '#' },
                      { $ref: '#/definitions/stringArray' }
                    ]
                  }
                },
                enum: {
                  anyOf: [
                    {
                      type: 'array',
                      minItems: 1,
                      uniqueItems: !0
                    },
                    { $ref: '#/definitions/$data' }
                  ]
                },
                type: {
                  anyOf: [
                    { $ref: '#/definitions/simpleTypes' },
                    {
                      type: 'array',
                      items: { $ref: '#/definitions/simpleTypes' },
                      minItems: 1,
                      uniqueItems: !0
                    }
                  ]
                },
                allOf: { $ref: '#/definitions/schemaArray' },
                anyOf: { $ref: '#/definitions/schemaArray' },
                oneOf: { $ref: '#/definitions/schemaArray' },
                not: { $ref: '#' },
                format: {
                  anyOf: [
                    { type: 'string' },
                    { $ref: '#/definitions/$data' }
                  ]
                },
                formatMaximum: {
                  anyOf: [
                    { type: 'string' },
                    { $ref: '#/definitions/$data' }
                  ]
                },
                formatMinimum: {
                  anyOf: [
                    { type: 'string' },
                    { $ref: '#/definitions/$data' }
                  ]
                },
                formatExclusiveMaximum: {
                  anyOf: [
                    {
                      type: 'boolean',
                      default: !1
                    },
                    { $ref: '#/definitions/$data' }
                  ]
                },
                formatExclusiveMinimum: {
                  anyOf: [
                    {
                      type: 'boolean',
                      default: !1
                    },
                    { $ref: '#/definitions/$data' }
                  ]
                },
                constant: {
                  anyOf: [
                    {},
                    { $ref: '#/definitions/$data' }
                  ]
                },
                contains: { $ref: '#' },
                patternGroups: {
                  type: 'object',
                  additionalProperties: {
                    type: 'object',
                    required: ['schema'],
                    properties: {
                      maximum: {
                        anyOf: [
                          { $ref: '#/definitions/positiveInteger' },
                          { $ref: '#/definitions/$data' }
                        ]
                      },
                      minimum: {
                        anyOf: [
                          { $ref: '#/definitions/positiveIntegerDefault0' },
                          { $ref: '#/definitions/$data' }
                        ]
                      },
                      schema: { $ref: '#' }
                    },
                    additionalProperties: !1
                  },
                  default: {}
                },
                switch: {
                  type: 'array',
                  items: {
                    required: ['then'],
                    properties: {
                      if: { $ref: '#' },
                      then: {
                        anyOf: [
                          { type: 'boolean' },
                          { $ref: '#' }
                        ]
                      },
                      continue: { type: 'boolean' }
                    },
                    additionalProperties: !1,
                    dependencies: { continue: ['if'] }
                  }
                }
              },
              dependencies: {
                exclusiveMaximum: ['maximum'],
                exclusiveMinimum: ['minimum'],
                formatMaximum: ['format'],
                formatMinimum: ['format'],
                formatExclusiveMaximum: ['formatMaximum'],
                formatExclusiveMinimum: ['formatMinimum']
              },
              default: {}
            };
          },
          {}
        ],
        40: [
          function (e, r, t) {
            'use strict';
            function a(r) {
              function t(e, t, s) {
                var o = {
                  inline: s || a[e],
                  statements: !0,
                  errors: 'full'
                };
                t && (o.type = t), r.addKeyword(e, o);
              }
              var a = {
                switch: e('./dotjs/switch'),
                constant: e('./dotjs/constant'),
                _formatLimit: e('./dotjs/_formatLimit'),
                patternRequired: e('./dotjs/patternRequired')
              };
              if (r._opts.meta !== !1) {
                var i = e('./refs/json-schema-v5.json');
                r.addMetaSchema(i, o);
              }
              t('constant'), r.addKeyword('contains', {
                type: 'array',
                macro: s
              }), t('formatMaximum', 'string', a._formatLimit), t('formatMinimum', 'string', a._formatLimit), r.addKeyword('formatExclusiveMaximum'), r.addKeyword('formatExclusiveMinimum'), r.addKeyword('patternGroups'), t('patternRequired', 'object'), t('switch');
            }
            function s(e) {
              return { not: { items: { not: e } } };
            }
            var o = 'https://raw.githubusercontent.com/epoberezkin/ajv/master/lib/refs/json-schema-v5.json';
            r.exports = {
              enable: a,
              META_SCHEMA_ID: o
            };
          },
          {
            './dotjs/_formatLimit': 13,
            './dotjs/constant': 20,
            './dotjs/patternRequired': 30,
            './dotjs/switch': 34,
            './refs/json-schema-v5.json': 39
          }
        ],
        41: [
          function (r, t, a) {
            (function (r) {
              !function (s) {
                function o(e) {
                  throw new RangeError(D[e]);
                }
                function i(e, r) {
                  for (var t = e.length, a = []; t--;)
                    a[t] = r(e[t]);
                  return a;
                }
                function n(e, r) {
                  var t = e.split('@'), a = '';
                  t.length > 1 && (a = t[0] + '@', e = t[1]), e = e.replace(q, '.');
                  var s = e.split('.'), o = i(s, r).join('.');
                  return a + o;
                }
                function l(e) {
                  for (var r, t, a = [], s = 0, o = e.length; s < o;)
                    r = e.charCodeAt(s++), r >= 55296 && r <= 56319 && s < o ? (t = e.charCodeAt(s++), 56320 == (64512 & t) ? a.push(((1023 & r) << 10) + (1023 & t) + 65536) : (a.push(r), s--)) : a.push(r);
                  return a;
                }
                function c(e) {
                  return i(e, function (e) {
                    var r = '';
                    return e > 65535 && (e -= 65536, r += C(e >>> 10 & 1023 | 55296), e = 56320 | 1023 & e), r += C(e);
                  }).join('');
                }
                function h(e) {
                  return e - 48 < 10 ? e - 22 : e - 65 < 26 ? e - 65 : e - 97 < 26 ? e - 97 : j;
                }
                function u(e, r) {
                  return e + 22 + 75 * (e < 26) - ((0 != r) << 5);
                }
                function f(e, r, t) {
                  var a = 0;
                  for (e = t ? Q(e / _) : e >> 1, e += Q(e / r); e > L * $ >> 1; a += j)
                    e = Q(e / L);
                  return Q(a + (L + 1) * e / (e + x));
                }
                function d(e) {
                  var r, t, a, s, i, n, l, u, d, p, m = [], v = e.length, y = 0, g = R, P = O;
                  for (t = e.lastIndexOf(I), t < 0 && (t = 0), a = 0; a < t; ++a)
                    e.charCodeAt(a) >= 128 && o('not-basic'), m.push(e.charCodeAt(a));
                  for (s = t > 0 ? t + 1 : 0; s < v;) {
                    for (i = y, n = 1, l = j; s >= v && o('invalid-input'), u = h(e.charCodeAt(s++)), (u >= j || u > Q((w - y) / n)) && o('overflow'), y += u * n, d = l <= P ? S : l >= P + $ ? $ : l - P, !(u < d); l += j)
                      p = j - d, n > Q(w / p) && o('overflow'), n *= p;
                    r = m.length + 1, P = f(y - i, r, 0 == i), Q(y / r) > w - g && o('overflow'), g += Q(y / r), y %= r, m.splice(y++, 0, g);
                  }
                  return c(m);
                }
                function p(e) {
                  var r, t, a, s, i, n, c, h, d, p, m, v, y, g, P, E = [];
                  for (e = l(e), v = e.length, r = R, t = 0, i = O, n = 0; n < v; ++n)
                    m = e[n], m < 128 && E.push(C(m));
                  for (a = s = E.length, s && E.push(I); a < v;) {
                    for (c = w, n = 0; n < v; ++n)
                      m = e[n], m >= r && m < c && (c = m);
                    for (y = a + 1, c - r > Q((w - t) / y) && o('overflow'), t += (c - r) * y, r = c, n = 0; n < v; ++n)
                      if (m = e[n], m < r && ++t > w && o('overflow'), m == r) {
                        for (h = t, d = j; p = d <= i ? S : d >= i + $ ? $ : d - i, !(h < p); d += j)
                          P = h - p, g = j - p, E.push(C(u(p + P % g, 0))), h = Q(P / g);
                        E.push(C(u(h, 0))), i = f(t, y, a == s), t = 0, ++a;
                      }
                    ++t, ++r;
                  }
                  return E.join('');
                }
                function m(e) {
                  return n(e, function (e) {
                    return A.test(e) ? d(e.slice(4).toLowerCase()) : e;
                  });
                }
                function v(e) {
                  return n(e, function (e) {
                    return k.test(e) ? 'xn--' + p(e) : e;
                  });
                }
                var y = 'object' == typeof a && a && !a.nodeType && a, g = 'object' == typeof t && t && !t.nodeType && t, P = 'object' == typeof r && r;
                P.global !== P && P.window !== P && P.self !== P || (s = P);
                var E, b, w = 2147483647, j = 36, S = 1, $ = 26, x = 38, _ = 700, O = 72, R = 128, I = '-', A = /^xn--/, k = /[^\x20-\x7E]/, q = /[\x2E\u3002\uFF0E\uFF61]/g, D = {
                    overflow: 'Overflow: input needs wider integers to process',
                    'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
                    'invalid-input': 'Invalid input'
                  }, L = j - S, Q = Math.floor, C = String.fromCharCode;
                if (E = {
                    version: '1.4.1',
                    ucs2: {
                      decode: l,
                      encode: c
                    },
                    decode: d,
                    encode: p,
                    toASCII: v,
                    toUnicode: m
                  }, 'function' == typeof e && 'object' == typeof e.amd && e.amd)
                  e('punycode', function () {
                    return E;
                  });
                else if (y && g)
                  if (t.exports == y)
                    g.exports = E;
                  else
                    for (b in E)
                      E.hasOwnProperty(b) && (y[b] = E[b]);
                else
                  s.punycode = E;
              }(this);
            }.call(this, 'undefined' != typeof global ? global : 'undefined' != typeof self ? self : 'undefined' != typeof window ? window : {}));
          },
          {}
        ],
        42: [
          function (e, r, t) {
            'use strict';
            function a(e, r) {
              return Object.prototype.hasOwnProperty.call(e, r);
            }
            r.exports = function (e, r, t, o) {
              r = r || '&', t = t || '=';
              var i = {};
              if ('string' != typeof e || 0 === e.length)
                return i;
              var n = /\+/g;
              e = e.split(r);
              var l = 1000;
              o && 'number' == typeof o.maxKeys && (l = o.maxKeys);
              var c = e.length;
              l > 0 && c > l && (c = l);
              for (var h = 0; h < c; ++h) {
                var u, f, d, p, m = e[h].replace(n, '%20'), v = m.indexOf(t);
                v >= 0 ? (u = m.substr(0, v), f = m.substr(v + 1)) : (u = m, f = ''), d = decodeURIComponent(u), p = decodeURIComponent(f), a(i, d) ? s(i[d]) ? i[d].push(p) : i[d] = [
                  i[d],
                  p
                ] : i[d] = p;
              }
              return i;
            };
            var s = Array.isArray || function (e) {
              return '[object Array]' === Object.prototype.toString.call(e);
            };
          },
          {}
        ],
        43: [
          function (e, r, t) {
            'use strict';
            function a(e, r) {
              if (e.map)
                return e.map(r);
              for (var t = [], a = 0; a < e.length; a++)
                t.push(r(e[a], a));
              return t;
            }
            var s = function (e) {
              switch (typeof e) {
              case 'string':
                return e;
              case 'boolean':
                return e ? 'true' : 'false';
              case 'number':
                return isFinite(e) ? e : '';
              default:
                return '';
              }
            };
            r.exports = function (e, r, t, n) {
              return r = r || '&', t = t || '=', null === e && (e = void 0), 'object' == typeof e ? a(i(e), function (i) {
                var n = encodeURIComponent(s(i)) + t;
                return o(e[i]) ? a(e[i], function (e) {
                  return n + encodeURIComponent(s(e));
                }).join(r) : n + encodeURIComponent(s(e[i]));
              }).join(r) : n ? encodeURIComponent(s(n)) + t + encodeURIComponent(s(e)) : '';
            };
            var o = Array.isArray || function (e) {
                return '[object Array]' === Object.prototype.toString.call(e);
              }, i = Object.keys || function (e) {
                var r = [];
                for (var t in e)
                  Object.prototype.hasOwnProperty.call(e, t) && r.push(t);
                return r;
              };
          },
          {}
        ],
        44: [
          function (e, r, t) {
            'use strict';
            t.decode = t.parse = e('./decode'), t.encode = t.stringify = e('./encode');
          },
          {
            './decode': 42,
            './encode': 43
          }
        ],
        45: [
          function (e, r, t) {
            'use strict';
            function a() {
              this.protocol = null, this.slashes = null, this.auth = null, this.host = null, this.port = null, this.hostname = null, this.hash = null, this.search = null, this.query = null, this.pathname = null, this.path = null, this.href = null;
            }
            function s(e, r, t) {
              if (e && c.isObject(e) && e instanceof a)
                return e;
              var s = new a();
              return s.parse(e, r, t), s;
            }
            function o(e) {
              return c.isString(e) && (e = s(e)), e instanceof a ? e.format() : a.prototype.format.call(e);
            }
            function i(e, r) {
              return s(e, !1, !0).resolve(r);
            }
            function n(e, r) {
              return e ? s(e, !1, !0).resolveObject(r) : r;
            }
            var l = e('punycode'), c = e('./util');
            t.parse = s, t.resolve = i, t.resolveObject = n, t.format = o, t.Url = a;
            var h = /^([a-z0-9.+-]+:)/i, u = /:[0-9]*$/, f = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/, d = [
                '<',
                '>',
                '"',
                '`',
                ' ',
                '\r',
                '\n',
                '\t'
              ], p = [
                '{',
                '}',
                '|',
                '\\',
                '^',
                '`'
              ].concat(d), m = ['\''].concat(p), v = [
                '%',
                '/',
                '?',
                ';',
                '#'
              ].concat(m), y = [
                '/',
                '?',
                '#'
              ], g = 255, P = /^[+a-z0-9A-Z_-]{0,63}$/, E = /^([+a-z0-9A-Z_-]{0,63})(.*)$/, b = {
                javascript: !0,
                'javascript:': !0
              }, w = {
                javascript: !0,
                'javascript:': !0
              }, j = {
                http: !0,
                https: !0,
                ftp: !0,
                gopher: !0,
                file: !0,
                'http:': !0,
                'https:': !0,
                'ftp:': !0,
                'gopher:': !0,
                'file:': !0
              }, S = e('querystring');
            a.prototype.parse = function (e, r, t) {
              if (!c.isString(e))
                throw new TypeError('Parameter \'url\' must be a string, not ' + typeof e);
              var a = e.indexOf('?'), s = a !== -1 && a < e.indexOf('#') ? '?' : '#', o = e.split(s), i = /\\/g;
              o[0] = o[0].replace(i, '/'), e = o.join(s);
              var n = e;
              if (n = n.trim(), !t && 1 === e.split('#').length) {
                var u = f.exec(n);
                if (u)
                  return this.path = n, this.href = n, this.pathname = u[1], u[2] ? (this.search = u[2], this.query = r ? S.parse(this.search.substr(1)) : this.search.substr(1)) : r && (this.search = '', this.query = {}), this;
              }
              var d = h.exec(n);
              if (d) {
                d = d[0];
                var p = d.toLowerCase();
                this.protocol = p, n = n.substr(d.length);
              }
              if (t || d || n.match(/^\/\/[^@\/]+@[^@\/]+/)) {
                var $ = '//' === n.substr(0, 2);
                !$ || d && w[d] || (n = n.substr(2), this.slashes = !0);
              }
              if (!w[d] && ($ || d && !j[d])) {
                for (var x = -1, _ = 0; _ < y.length; _++) {
                  var O = n.indexOf(y[_]);
                  O !== -1 && (x === -1 || O < x) && (x = O);
                }
                var R, I;
                I = x === -1 ? n.lastIndexOf('@') : n.lastIndexOf('@', x), I !== -1 && (R = n.slice(0, I), n = n.slice(I + 1), this.auth = decodeURIComponent(R)), x = -1;
                for (var _ = 0; _ < v.length; _++) {
                  var O = n.indexOf(v[_]);
                  O !== -1 && (x === -1 || O < x) && (x = O);
                }
                x === -1 && (x = n.length), this.host = n.slice(0, x), n = n.slice(x), this.parseHost(), this.hostname = this.hostname || '';
                var A = '[' === this.hostname[0] && ']' === this.hostname[this.hostname.length - 1];
                if (!A)
                  for (var k = this.hostname.split(/\./), _ = 0, q = k.length; _ < q; _++) {
                    var D = k[_];
                    if (D && !D.match(P)) {
                      for (var L = '', Q = 0, C = D.length; Q < C; Q++)
                        L += D.charCodeAt(Q) > 127 ? 'x' : D[Q];
                      if (!L.match(P)) {
                        var V = k.slice(0, _), z = k.slice(_ + 1), U = D.match(E);
                        U && (V.push(U[1]), z.unshift(U[2])), z.length && (n = '/' + z.join('.') + n), this.hostname = V.join('.');
                        break;
                      }
                    }
                  }
                this.hostname = this.hostname.length > g ? '' : this.hostname.toLowerCase(), A || (this.hostname = l.toASCII(this.hostname));
                var T = this.port ? ':' + this.port : '', M = this.hostname || '';
                this.host = M + T, this.href += this.host, A && (this.hostname = this.hostname.substr(1, this.hostname.length - 2), '/' !== n[0] && (n = '/' + n));
              }
              if (!b[p])
                for (var _ = 0, q = m.length; _ < q; _++) {
                  var N = m[_];
                  if (n.indexOf(N) !== -1) {
                    var F = encodeURIComponent(N);
                    F === N && (F = escape(N)), n = n.split(N).join(F);
                  }
                }
              var H = n.indexOf('#');
              H !== -1 && (this.hash = n.substr(H), n = n.slice(0, H));
              var J = n.indexOf('?');
              if (J !== -1 ? (this.search = n.substr(J), this.query = n.substr(J + 1), r && (this.query = S.parse(this.query)), n = n.slice(0, J)) : r && (this.search = '', this.query = {}), n && (this.pathname = n), j[p] && this.hostname && !this.pathname && (this.pathname = '/'), this.pathname || this.search) {
                var T = this.pathname || '', G = this.search || '';
                this.path = T + G;
              }
              return this.href = this.format(), this;
            }, a.prototype.format = function () {
              var e = this.auth || '';
              e && (e = encodeURIComponent(e), e = e.replace(/%3A/i, ':'), e += '@');
              var r = this.protocol || '', t = this.pathname || '', a = this.hash || '', s = !1, o = '';
              this.host ? s = e + this.host : this.hostname && (s = e + (this.hostname.indexOf(':') === -1 ? this.hostname : '[' + this.hostname + ']'), this.port && (s += ':' + this.port)), this.query && c.isObject(this.query) && Object.keys(this.query).length && (o = S.stringify(this.query));
              var i = this.search || o && '?' + o || '';
              return r && ':' !== r.substr(-1) && (r += ':'), this.slashes || (!r || j[r]) && s !== !1 ? (s = '//' + (s || ''), t && '/' !== t.charAt(0) && (t = '/' + t)) : s || (s = ''), a && '#' !== a.charAt(0) && (a = '#' + a), i && '?' !== i.charAt(0) && (i = '?' + i), t = t.replace(/[?#]/g, function (e) {
                return encodeURIComponent(e);
              }), i = i.replace('#', '%23'), r + s + t + i + a;
            }, a.prototype.resolve = function (e) {
              return this.resolveObject(s(e, !1, !0)).format();
            }, a.prototype.resolveObject = function (e) {
              if (c.isString(e)) {
                var r = new a();
                r.parse(e, !1, !0), e = r;
              }
              for (var t = new a(), s = Object.keys(this), o = 0; o < s.length; o++) {
                var i = s[o];
                t[i] = this[i];
              }
              if (t.hash = e.hash, '' === e.href)
                return t.href = t.format(), t;
              if (e.slashes && !e.protocol) {
                for (var n = Object.keys(e), l = 0; l < n.length; l++) {
                  var h = n[l];
                  'protocol' !== h && (t[h] = e[h]);
                }
                return j[t.protocol] && t.hostname && !t.pathname && (t.path = t.pathname = '/'), t.href = t.format(), t;
              }
              if (e.protocol && e.protocol !== t.protocol) {
                if (!j[e.protocol]) {
                  for (var u = Object.keys(e), f = 0; f < u.length; f++) {
                    var d = u[f];
                    t[d] = e[d];
                  }
                  return t.href = t.format(), t;
                }
                if (t.protocol = e.protocol, e.host || w[e.protocol])
                  t.pathname = e.pathname;
                else {
                  for (var p = (e.pathname || '').split('/'); p.length && !(e.host = p.shift()););
                  e.host || (e.host = ''), e.hostname || (e.hostname = ''), '' !== p[0] && p.unshift(''), p.length < 2 && p.unshift(''), t.pathname = p.join('/');
                }
                if (t.search = e.search, t.query = e.query, t.host = e.host || '', t.auth = e.auth, t.hostname = e.hostname || e.host, t.port = e.port, t.pathname || t.search) {
                  var m = t.pathname || '', v = t.search || '';
                  t.path = m + v;
                }
                return t.slashes = t.slashes || e.slashes, t.href = t.format(), t;
              }
              var y = t.pathname && '/' === t.pathname.charAt(0), g = e.host || e.pathname && '/' === e.pathname.charAt(0), P = g || y || t.host && e.pathname, E = P, b = t.pathname && t.pathname.split('/') || [], p = e.pathname && e.pathname.split('/') || [], S = t.protocol && !j[t.protocol];
              if (S && (t.hostname = '', t.port = null, t.host && ('' === b[0] ? b[0] = t.host : b.unshift(t.host)), t.host = '', e.protocol && (e.hostname = null, e.port = null, e.host && ('' === p[0] ? p[0] = e.host : p.unshift(e.host)), e.host = null), P = P && ('' === p[0] || '' === b[0])), g)
                t.host = e.host || '' === e.host ? e.host : t.host, t.hostname = e.hostname || '' === e.hostname ? e.hostname : t.hostname, t.search = e.search, t.query = e.query, b = p;
              else if (p.length)
                b || (b = []), b.pop(), b = b.concat(p), t.search = e.search, t.query = e.query;
              else if (!c.isNullOrUndefined(e.search)) {
                if (S) {
                  t.hostname = t.host = b.shift();
                  var $ = !!(t.host && t.host.indexOf('@') > 0) && t.host.split('@');
                  $ && (t.auth = $.shift(), t.host = t.hostname = $.shift());
                }
                return t.search = e.search, t.query = e.query, c.isNull(t.pathname) && c.isNull(t.search) || (t.path = (t.pathname ? t.pathname : '') + (t.search ? t.search : '')), t.href = t.format(), t;
              }
              if (!b.length)
                return t.pathname = null, t.path = t.search ? '/' + t.search : null, t.href = t.format(), t;
              for (var x = b.slice(-1)[0], _ = (t.host || e.host || b.length > 1) && ('.' === x || '..' === x) || '' === x, O = 0, R = b.length; R >= 0; R--)
                x = b[R], '.' === x ? b.splice(R, 1) : '..' === x ? (b.splice(R, 1), O++) : O && (b.splice(R, 1), O--);
              if (!P && !E)
                for (; O--; O)
                  b.unshift('..');
              !P || '' === b[0] || b[0] && '/' === b[0].charAt(0) || b.unshift(''), _ && '/' !== b.join('/').substr(-1) && b.push('');
              var I = '' === b[0] || b[0] && '/' === b[0].charAt(0);
              if (S) {
                t.hostname = t.host = I ? '' : b.length ? b.shift() : '';
                var $ = !!(t.host && t.host.indexOf('@') > 0) && t.host.split('@');
                $ && (t.auth = $.shift(), t.host = t.hostname = $.shift());
              }
              return P = P || t.host && b.length, P && !I && b.unshift(''), b.length ? t.pathname = b.join('/') : (t.pathname = null, t.path = null), c.isNull(t.pathname) && c.isNull(t.search) || (t.path = (t.pathname ? t.pathname : '') + (t.search ? t.search : '')), t.auth = e.auth || t.auth, t.slashes = t.slashes || e.slashes, t.href = t.format(), t;
            }, a.prototype.parseHost = function () {
              var e = this.host, r = u.exec(e);
              r && (r = r[0], ':' !== r && (this.port = r.substr(1)), e = e.substr(0, e.length - r.length)), e && (this.hostname = e);
            };
          },
          {
            './util': 46,
            punycode: 41,
            querystring: 44
          }
        ],
        46: [
          function (e, r, t) {
            'use strict';
            r.exports = {
              isString: function (e) {
                return 'string' == typeof e;
              },
              isObject: function (e) {
                return 'object' == typeof e && null !== e;
              },
              isNull: function (e) {
                return null === e;
              },
              isNullOrUndefined: function (e) {
                return null == e;
              }
            };
          },
          {}
        ],
        47: [
          function (e, r, t) {
            function a(e) {
              var r = this, t = f.call(arguments, 1);
              return new Promise(function (a, o) {
                function i(r) {
                  var t;
                  try {
                    t = e.next(r);
                  } catch (e) {
                    return o(e);
                  }
                  c(t);
                }
                function n(r) {
                  var t;
                  try {
                    t = e.throw(r);
                  } catch (e) {
                    return o(e);
                  }
                  c(t);
                }
                function c(e) {
                  if (e.done)
                    return a(e.value);
                  var t = s.call(r, e.value);
                  return t && l(t) ? t.then(i, n) : n(new TypeError('You may only yield a function, promise, generator, array, or object, but the following object was passed: "' + String(e.value) + '"'));
                }
                return 'function' == typeof e && (e = e.apply(r, t)), e && 'function' == typeof e.next ? void i() : a(e);
              });
            }
            function s(e) {
              return e ? l(e) ? e : h(e) || c(e) ? a.call(this, e) : 'function' == typeof e ? o.call(this, e) : Array.isArray(e) ? i.call(this, e) : u(e) ? n.call(this, e) : e : e;
            }
            function o(e) {
              var r = this;
              return new Promise(function (t, a) {
                e.call(r, function (e, r) {
                  return e ? a(e) : (arguments.length > 2 && (r = f.call(arguments, 1)), void t(r));
                });
              });
            }
            function i(e) {
              return Promise.all(e.map(s, this));
            }
            function n(e) {
              function r(e, r) {
                t[r] = void 0, o.push(e.then(function (e) {
                  t[r] = e;
                }));
              }
              for (var t = new e.constructor(), a = Object.keys(e), o = [], i = 0; i < a.length; i++) {
                var n = a[i], c = s.call(this, e[n]);
                c && l(c) ? r(c, n) : t[n] = e[n];
              }
              return Promise.all(o).then(function () {
                return t;
              });
            }
            function l(e) {
              return 'function' == typeof e.then;
            }
            function c(e) {
              return 'function' == typeof e.next && 'function' == typeof e.throw;
            }
            function h(e) {
              var r = e.constructor;
              return !!r && ('GeneratorFunction' === r.name || 'GeneratorFunction' === r.displayName || c(r.prototype));
            }
            function u(e) {
              return Object == e.constructor;
            }
            var f = Array.prototype.slice;
            r.exports = a.default = a.co = a, a.wrap = function (e) {
              function r() {
                return a.call(this, e.apply(this, arguments));
              }
              return r.__generatorFunction__ = e, r;
            };
          },
          {}
        ],
        48: [
          function (e, r, t) {
            var a = 'undefined' != typeof JSON ? JSON : e('jsonify');
            r.exports = function (e, r) {
              r || (r = {}), 'function' == typeof r && (r = { cmp: r });
              var t = r.space || '';
              'number' == typeof t && (t = Array(t + 1).join(' '));
              var i = 'boolean' == typeof r.cycles && r.cycles, n = r.replacer || function (e, r) {
                  return r;
                }, l = r.cmp && function (e) {
                  return function (r) {
                    return function (t, a) {
                      var s = {
                          key: t,
                          value: r[t]
                        }, o = {
                          key: a,
                          value: r[a]
                        };
                      return e(s, o);
                    };
                  };
                }(r.cmp), c = [];
              return function e(r, h, u, f) {
                var d = t ? '\n' + new Array(f + 1).join(t) : '', p = t ? ': ' : ':';
                if (u && u.toJSON && 'function' == typeof u.toJSON && (u = u.toJSON()), u = n.call(r, h, u), void 0 !== u) {
                  if ('object' != typeof u || null === u)
                    return a.stringify(u);
                  if (s(u)) {
                    for (var m = [], v = 0; v < u.length; v++) {
                      var y = e(u, v, u[v], f + 1) || a.stringify(null);
                      m.push(d + t + y);
                    }
                    return '[' + m.join(',') + d + ']';
                  }
                  if (c.indexOf(u) !== -1) {
                    if (i)
                      return a.stringify('__cycle__');
                    throw new TypeError('Converting circular structure to JSON');
                  }
                  c.push(u);
                  for (var g = o(u).sort(l && l(u)), m = [], v = 0; v < g.length; v++) {
                    var h = g[v], P = e(u, h, u[h], f + 1);
                    if (P) {
                      var E = a.stringify(h) + p + P;
                      m.push(d + t + E);
                    }
                  }
                  return c.splice(c.indexOf(u), 1), '{' + m.join(',') + d + '}';
                }
              }({ '': e }, '', e, 0);
            };
            var s = Array.isArray || function (e) {
                return '[object Array]' === {}.toString.call(e);
              }, o = Object.keys || function (e) {
                var r = Object.prototype.hasOwnProperty || function () {
                    return !0;
                  }, t = [];
                for (var a in e)
                  r.call(e, a) && t.push(a);
                return t;
              };
          },
          { jsonify: 49 }
        ],
        49: [
          function (e, r, t) {
            t.parse = e('./lib/parse'), t.stringify = e('./lib/stringify');
          },
          {
            './lib/parse': 50,
            './lib/stringify': 51
          }
        ],
        50: [
          function (e, r, t) {
            var a, s, o, i, n = {
                '"': '"',
                '\\': '\\',
                '/': '/',
                b: '\b',
                f: '\f',
                n: '\n',
                r: '\r',
                t: '\t'
              }, l = function (e) {
                throw {
                  name: 'SyntaxError',
                  message: e,
                  at: a,
                  text: o
                };
              }, c = function (e) {
                return e && e !== s && l('Expected \'' + e + '\' instead of \'' + s + '\''), s = o.charAt(a), a += 1, s;
              }, h = function () {
                var e, r = '';
                for ('-' === s && (r = '-', c('-')); s >= '0' && s <= '9';)
                  r += s, c();
                if ('.' === s)
                  for (r += '.'; c() && s >= '0' && s <= '9';)
                    r += s;
                if ('e' === s || 'E' === s)
                  for (r += s, c(), '-' !== s && '+' !== s || (r += s, c()); s >= '0' && s <= '9';)
                    r += s, c();
                return e = +r, isFinite(e) ? e : void l('Bad number');
              }, u = function () {
                var e, r, t, a = '';
                if ('"' === s)
                  for (; c();) {
                    if ('"' === s)
                      return c(), a;
                    if ('\\' === s)
                      if (c(), 'u' === s) {
                        for (t = 0, r = 0; r < 4 && (e = parseInt(c(), 16), isFinite(e)); r += 1)
                          t = 16 * t + e;
                        a += String.fromCharCode(t);
                      } else {
                        if ('string' != typeof n[s])
                          break;
                        a += n[s];
                      }
                    else
                      a += s;
                  }
                l('Bad string');
              }, f = function () {
                for (; s && s <= ' ';)
                  c();
              }, d = function () {
                switch (s) {
                case 't':
                  return c('t'), c('r'), c('u'), c('e'), !0;
                case 'f':
                  return c('f'), c('a'), c('l'), c('s'), c('e'), !1;
                case 'n':
                  return c('n'), c('u'), c('l'), c('l'), null;
                }
                l('Unexpected \'' + s + '\'');
              }, p = function () {
                var e = [];
                if ('[' === s) {
                  if (c('['), f(), ']' === s)
                    return c(']'), e;
                  for (; s;) {
                    if (e.push(i()), f(), ']' === s)
                      return c(']'), e;
                    c(','), f();
                  }
                }
                l('Bad array');
              }, m = function () {
                var e, r = {};
                if ('{' === s) {
                  if (c('{'), f(), '}' === s)
                    return c('}'), r;
                  for (; s;) {
                    if (e = u(), f(), c(':'), Object.hasOwnProperty.call(r, e) && l('Duplicate key "' + e + '"'), r[e] = i(), f(), '}' === s)
                      return c('}'), r;
                    c(','), f();
                  }
                }
                l('Bad object');
              };
            i = function () {
              switch (f(), s) {
              case '{':
                return m();
              case '[':
                return p();
              case '"':
                return u();
              case '-':
                return h();
              default:
                return s >= '0' && s <= '9' ? h() : d();
              }
            }, r.exports = function (e, r) {
              var t;
              return o = e, a = 0, s = ' ', t = i(), f(), s && l('Syntax error'), 'function' == typeof r ? function e(t, a) {
                var s, o, i = t[a];
                if (i && 'object' == typeof i)
                  for (s in i)
                    Object.prototype.hasOwnProperty.call(i, s) && (o = e(i, s), void 0 !== o ? i[s] = o : delete i[s]);
                return r.call(t, a, i);
              }({ '': t }, '') : t;
            };
          },
          {}
        ],
        51: [
          function (e, r, t) {
            function a(e) {
              return l.lastIndex = 0, l.test(e) ? '"' + e.replace(l, function (e) {
                var r = c[e];
                return 'string' == typeof r ? r : '\\u' + ('0000' + e.charCodeAt(0).toString(16)).slice(-4);
              }) + '"' : '"' + e + '"';
            }
            function s(e, r) {
              var t, l, c, h, u, f = o, d = r[e];
              switch (d && 'object' == typeof d && 'function' == typeof d.toJSON && (d = d.toJSON(e)), 'function' == typeof n && (d = n.call(r, e, d)), typeof d) {
              case 'string':
                return a(d);
              case 'number':
                return isFinite(d) ? String(d) : 'null';
              case 'boolean':
              case 'null':
                return String(d);
              case 'object':
                if (!d)
                  return 'null';
                if (o += i, u = [], '[object Array]' === Object.prototype.toString.apply(d)) {
                  for (h = d.length, t = 0; t < h; t += 1)
                    u[t] = s(t, d) || 'null';
                  return c = 0 === u.length ? '[]' : o ? '[\n' + o + u.join(',\n' + o) + '\n' + f + ']' : '[' + u.join(',') + ']', o = f, c;
                }
                if (n && 'object' == typeof n)
                  for (h = n.length, t = 0; t < h; t += 1)
                    l = n[t], 'string' == typeof l && (c = s(l, d), c && u.push(a(l) + (o ? ': ' : ':') + c));
                else
                  for (l in d)
                    Object.prototype.hasOwnProperty.call(d, l) && (c = s(l, d), c && u.push(a(l) + (o ? ': ' : ':') + c));
                return c = 0 === u.length ? '{}' : o ? '{\n' + o + u.join(',\n' + o) + '\n' + f + '}' : '{' + u.join(',') + '}', o = f, c;
              }
            }
            var o, i, n, l = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, c = {
                '\b': '\\b',
                '\t': '\\t',
                '\n': '\\n',
                '\f': '\\f',
                '\r': '\\r',
                '"': '\\"',
                '\\': '\\\\'
              };
            r.exports = function (e, r, t) {
              var a;
              if (o = '', i = '', 'number' == typeof t)
                for (a = 0; a < t; a += 1)
                  i += ' ';
              else
                'string' == typeof t && (i = t);
              if (n = r, r && 'function' != typeof r && ('object' != typeof r || 'number' != typeof r.length))
                throw new Error('JSON.stringify');
              return s('', { '': e });
            };
          },
          {}
        ],
        ajv: [
          function (e, r, t) {
            'use strict';
            function a(e) {
              return y.test(e);
            }
            function Ajv(r) {
              function t(e, r) {
                var t;
                if ('string' == typeof e) {
                  if (t = j(e), !t)
                    throw new Error('no schema with key or ref "' + e + '"');
                } else {
                  var a = O(e);
                  t = a.validate || R(a);
                }
                var s = t(r);
                return t.$async === !0 ? '*' == C._opts.async ? p(s) : s : (C.errors = t.errors, s);
              }
              function m(e, r) {
                var t = O(e, void 0, r);
                return t.validate || R(t);
              }
              function P(e, r, t, a) {
                if (Array.isArray(e))
                  for (var s = 0; s < e.length; s++)
                    P(e[s], void 0, t, a);
                else
                  r = o.normalizeId(r || e.id), L(r), C._schemas[r] = O(e, t, a, !0);
              }
              function E(e, r, t) {
                P(e, r, t, !0);
              }
              function b(e, r) {
                var s = e.$schema || C._opts.defaultMeta || w(), o = C._formats.uri;
                C._formats.uri = 'function' == typeof o ? a : y;
                var i;
                try {
                  i = t(s, e);
                } finally {
                  C._formats.uri = o;
                }
                if (!i && r) {
                  var n = 'schema is invalid: ' + I();
                  if ('log' != C._opts.validateSchema)
                    throw new Error(n);
                  console.error(n);
                }
                return i;
              }
              function w() {
                var e = C._opts.meta;
                return C._opts.defaultMeta = 'object' == typeof e ? e.id || e : C._opts.v5 ? u.META_SCHEMA_ID : v;
              }
              function j(e) {
                var r = $(e);
                switch (typeof r) {
                case 'object':
                  return r.validate || R(r);
                case 'string':
                  return j(r);
                case 'undefined':
                  return S(e);
                }
              }
              function S(e) {
                var r = o.schema.call(C, { schema: {} }, e);
                if (r) {
                  var t = r.schema, a = r.root, i = r.baseId, l = s.call(C, t, a, void 0, i);
                  return C._fragments[e] = new n({
                    ref: e,
                    fragment: !0,
                    schema: t,
                    root: a,
                    baseId: i,
                    validate: l
                  }), l;
                }
              }
              function $(e) {
                return e = o.normalizeId(e), C._schemas[e] || C._refs[e] || C._fragments[e];
              }
              function x(e) {
                if (e instanceof RegExp)
                  return _(C._schemas, e), void _(C._refs, e);
                switch (typeof e) {
                case 'undefined':
                  return _(C._schemas), _(C._refs), void C._cache.clear();
                case 'string':
                  var r = $(e);
                  return r && C._cache.del(r.jsonStr), delete C._schemas[e], void delete C._refs[e];
                case 'object':
                  var t = l(e);
                  C._cache.del(t);
                  var a = e.id;
                  a && (a = o.normalizeId(a), delete C._schemas[a], delete C._refs[a]);
                }
              }
              function _(e, r) {
                for (var t in e) {
                  var a = e[t];
                  a.meta || r && !r.test(t) || (C._cache.del(a.jsonStr), delete e[t]);
                }
              }
              function O(e, r, t, a) {
                if ('object' != typeof e)
                  throw new Error('schema should be object');
                var s = l(e), i = C._cache.get(s);
                if (i)
                  return i;
                a = a || C._opts.addUsedSchema !== !1;
                var c = o.normalizeId(e.id);
                c && a && L(c);
                var h, u = C._opts.validateSchema !== !1 && !r;
                u && !(h = e.id && e.id == e.$schema) && b(e, !0);
                var f = o.ids.call(C, e), d = new n({
                    id: c,
                    schema: e,
                    localRefs: f,
                    jsonStr: s,
                    meta: t
                  });
                return '#' != c[0] && a && (C._refs[c] = d), C._cache.put(s, d), u && h && b(e, !0), d;
              }
              function R(e, r) {
                function t() {
                  var r = e.validate, a = r.apply(null, arguments);
                  return t.errors = r.errors, a;
                }
                if (e.compiling)
                  return e.validate = t, t.schema = e.schema, t.errors = null, t.root = r ? r : t, e.schema.$async === !0 && (t.$async = !0), t;
                e.compiling = !0;
                var a;
                e.meta && (a = C._opts, C._opts = C._metaOpts);
                var o;
                try {
                  o = s.call(C, e.schema, r, e.localRefs);
                } finally {
                  e.compiling = !1, e.meta && (C._opts = a);
                }
                return e.validate = o, e.refs = o.refs, e.refVal = o.refVal, e.root = o.root, o;
              }
              function I(e, r) {
                if (e = e || C.errors, !e)
                  return 'No errors';
                r = r || {};
                for (var t = void 0 === r.separator ? ', ' : r.separator, a = void 0 === r.dataVar ? 'data' : r.dataVar, s = '', o = 0; o < e.length; o++) {
                  var i = e[o];
                  i && (s += a + i.dataPath + ' ' + i.message + t);
                }
                return s.slice(0, -t.length);
              }
              function A(e, r) {
                'string' == typeof r && (r = new RegExp(r)), C._formats[e] = r;
              }
              function k() {
                if (C._opts.meta !== !1) {
                  var r = e('./refs/json-schema-draft-04.json');
                  E(r, v, !0), C._refs['http://json-schema.org/schema'] = v;
                }
              }
              function q() {
                var e = C._opts.schemas;
                if (e)
                  if (Array.isArray(e))
                    P(e);
                  else
                    for (var r in e)
                      P(e[r], r);
              }
              function D() {
                for (var e in C._opts.formats) {
                  var r = C._opts.formats[e];
                  A(e, r);
                }
              }
              function L(e) {
                if (C._schemas[e] || C._refs[e])
                  throw new Error('schema with key or id "' + e + '" already exists');
              }
              function Q() {
                for (var e = f.copy(C._opts), r = 0; r < g.length; r++)
                  delete e[g[r]];
                return e;
              }
              if (!(this instanceof Ajv))
                return new Ajv(r);
              var C = this;
              r = this._opts = f.copy(r) || {}, this._schemas = {}, this._refs = {}, this._fragments = {}, this._formats = c(r.format), this._cache = r.cache || new i(), this._loadingSchemas = {}, this._compilations = [], this.RULES = h(), this.validate = t, this.compile = m, this.addSchema = P, this.addMetaSchema = E, this.validateSchema = b, this.getSchema = j, this.removeSchema = x, this.addFormat = A, this.errorsText = I, this._addSchema = O, this._compile = R, r.loopRequired = r.loopRequired || 1 / 0, (r.async || r.transpile) && d.setup(r), r.beautify === !0 && (r.beautify = { indent_size: 2 }), 'property' == r.errorDataPath && (r._errorDataPathProperty = !0), this._metaOpts = Q(), r.formats && D(), k(), r.v5 && u.enable(this), 'object' == typeof r.meta && E(r.meta), q();
            }
            var s = e('./compile'), o = e('./compile/resolve'), i = e('./cache'), n = e('./compile/schema_obj'), l = e('json-stable-stringify'), c = e('./compile/formats'), h = e('./compile/rules'), u = e('./v5'), f = e('./compile/util'), d = e('./async'), p = e('co');
            r.exports = Ajv, Ajv.prototype.compileAsync = d.compile;
            var m = e('./keyword');
            Ajv.prototype.addKeyword = m.add, Ajv.prototype.getKeyword = m.get, Ajv.prototype.removeKeyword = m.remove, Ajv.ValidationError = e('./compile/validation_error');
            var v = 'http://json-schema.org/draft-04/schema', y = /^(?:(?:[a-z][a-z0-9+-.]*:)?\/\/)?[^\s]*$/i, g = [
                'removeAdditional',
                'useDefaults',
                'coerceTypes'
              ];
          },
          {
            './async': 1,
            './cache': 2,
            './compile': 6,
            './compile/formats': 5,
            './compile/resolve': 7,
            './compile/rules': 8,
            './compile/schema_obj': 9,
            './compile/util': 11,
            './compile/validation_error': 12,
            './keyword': 37,
            './refs/json-schema-draft-04.json': 38,
            './v5': 40,
            co: 47,
            'json-stable-stringify': 48
          }
        ]
      }, {}, [])('ajv');
    });
    (function () {
      var toExport;
      if (typeof module !== 'undefined' && module.exports) {
        toExport = require('traverse');
      } else {
        toExport = function () {
          function Traverse(obj) {
            this.value = obj;
          }
          Traverse.prototype.get = function (ps) {
            var node = this.value;
            for (var i = 0; i < ps.length; i++) {
              var key = ps[i];
              if (!node || !hasOwnProperty.call(node, key)) {
                node = undefined;
                break;
              }
              node = node[key];
            }
            return node;
          };
          Traverse.prototype.has = function (ps) {
            var node = this.value;
            for (var i = 0; i < ps.length; i++) {
              var key = ps[i];
              if (!node || !hasOwnProperty.call(node, key)) {
                return false;
              }
              node = node[key];
            }
            return true;
          };
          Traverse.prototype.set = function (ps, value) {
            var node = this.value;
            for (var i = 0; i < ps.length - 1; i++) {
              var key = ps[i];
              if (!hasOwnProperty.call(node, key))
                node[key] = {};
              node = node[key];
            }
            node[ps[i]] = value;
            return value;
          };
          Traverse.prototype.map = function (cb) {
            return walk(this.value, cb, true);
          };
          Traverse.prototype.forEach = function (cb) {
            this.value = walk(this.value, cb, false);
            return this.value;
          };
          Traverse.prototype.reduce = function (cb, init) {
            var skip = arguments.length === 1;
            var acc = skip ? this.value : init;
            this.forEach(function (x) {
              if (!this.isRoot || !skip) {
                acc = cb.call(this, acc, x);
              }
            });
            return acc;
          };
          Traverse.prototype.paths = function () {
            var acc = [];
            this.forEach(function (x) {
              acc.push(this.path);
            });
            return acc;
          };
          Traverse.prototype.nodes = function () {
            var acc = [];
            this.forEach(function (x) {
              acc.push(this.node);
            });
            return acc;
          };
          Traverse.prototype.clone = function () {
            var parents = [], nodes = [];
            return function clone(src) {
              for (var i = 0; i < parents.length; i++) {
                if (parents[i] === src) {
                  return nodes[i];
                }
              }
              if (typeof src === 'object' && src !== null) {
                var dst = copy(src);
                parents.push(src);
                nodes.push(dst);
                forEach(objectKeys(src), function (key) {
                  dst[key] = clone(src[key]);
                });
                parents.pop();
                nodes.pop();
                return dst;
              } else {
                return src;
              }
            }(this.value);
          };
          function walk(root, cb, immutable) {
            var path = [];
            var parents = [];
            var alive = true;
            return function walker(node_) {
              var node = immutable ? copy(node_) : node_;
              var modifiers = {};
              var keepGoing = true;
              var state = {
                node: node,
                node_: node_,
                path: [].concat(path),
                parent: parents[parents.length - 1],
                parents: parents,
                key: path.slice(-1)[0],
                isRoot: path.length === 0,
                level: path.length,
                circular: null,
                update: function (x, stopHere) {
                  if (!state.isRoot) {
                    state.parent.node[state.key] = x;
                  }
                  state.node = x;
                  if (stopHere)
                    keepGoing = false;
                },
                'delete': function (stopHere) {
                  delete state.parent.node[state.key];
                  if (stopHere)
                    keepGoing = false;
                },
                remove: function (stopHere) {
                  if (isArray(state.parent.node)) {
                    state.parent.node.splice(state.key, 1);
                  } else {
                    delete state.parent.node[state.key];
                  }
                  if (stopHere)
                    keepGoing = false;
                },
                keys: null,
                before: function (f) {
                  modifiers.before = f;
                },
                after: function (f) {
                  modifiers.after = f;
                },
                pre: function (f) {
                  modifiers.pre = f;
                },
                post: function (f) {
                  modifiers.post = f;
                },
                stop: function () {
                  alive = false;
                },
                block: function () {
                  keepGoing = false;
                }
              };
              if (!alive)
                return state;
              function updateState() {
                if (typeof state.node === 'object' && state.node !== null) {
                  if (!state.keys || state.node_ !== state.node) {
                    state.keys = objectKeys(state.node);
                  }
                  state.isLeaf = state.keys.length == 0;
                  for (var i = 0; i < parents.length; i++) {
                    if (parents[i].node_ === node_) {
                      state.circular = parents[i];
                      break;
                    }
                  }
                } else {
                  state.isLeaf = true;
                  state.keys = null;
                }
                state.notLeaf = !state.isLeaf;
                state.notRoot = !state.isRoot;
              }
              updateState();
              // use return values to update if defined
              var ret = cb.call(state, state.node);
              if (ret !== undefined && state.update)
                state.update(ret);
              if (modifiers.before)
                modifiers.before.call(state, state.node);
              if (!keepGoing)
                return state;
              if (typeof state.node == 'object' && state.node !== null && !state.circular) {
                parents.push(state);
                updateState();
                forEach(state.keys, function (key, i) {
                  path.push(key);
                  if (modifiers.pre)
                    modifiers.pre.call(state, state.node[key], key);
                  var child = walker(state.node[key]);
                  if (immutable && hasOwnProperty.call(state.node, key)) {
                    state.node[key] = child.node;
                  }
                  child.isLast = i == state.keys.length - 1;
                  child.isFirst = i == 0;
                  if (modifiers.post)
                    modifiers.post.call(state, child);
                  path.pop();
                });
                parents.pop();
              }
              if (modifiers.after)
                modifiers.after.call(state, state.node);
              return state;
            }(root).node;
          }
          function copy(src) {
            if (typeof src === 'object' && src !== null) {
              var dst;
              if (isArray(src)) {
                dst = [];
              } else if (isDate(src)) {
                dst = new Date(src.getTime ? src.getTime() : src);
              } else if (isRegExp(src)) {
                dst = new RegExp(src);
              } else if (isError(src)) {
                dst = { message: src.message };
              } else if (isBoolean(src)) {
                dst = new Boolean(src);
              } else if (isNumber(src)) {
                dst = new Number(src);
              } else if (isString(src)) {
                dst = new String(src);
              } else if (Object.create && Object.getPrototypeOf) {
                dst = Object.create(Object.getPrototypeOf(src));
              } else if (src.constructor === Object) {
                dst = {};
              } else {
                var proto = src.constructor && src.constructor.prototype || src.__proto__ || {};
                var T = function () {
                };
                T.prototype = proto;
                dst = new T();
              }
              forEach(objectKeys(src), function (key) {
                dst[key] = src[key];
              });
              return dst;
            } else
              return src;
          }
          var objectKeys = Object.keys || function keys(obj) {
            var res = [];
            for (var key in obj)
              res.push(key);
            return res;
          };
          function toS(obj) {
            return Object.prototype.toString.call(obj);
          }
          function isDate(obj) {
            return toS(obj) === '[object Date]';
          }
          function isRegExp(obj) {
            return toS(obj) === '[object RegExp]';
          }
          function isError(obj) {
            return toS(obj) === '[object Error]';
          }
          function isBoolean(obj) {
            return toS(obj) === '[object Boolean]';
          }
          function isNumber(obj) {
            return toS(obj) === '[object Number]';
          }
          function isString(obj) {
            return toS(obj) === '[object String]';
          }
          var isArray = Array.isArray || function isArray(xs) {
            return Object.prototype.toString.call(xs) === '[object Array]';
          };
          var forEach = function (xs, fn) {
            if (xs.forEach)
              return xs.forEach(fn);
            else
              for (var i = 0; i < xs.length; i++) {
                fn(xs[i], i, xs);
              }
          };
          var traverse = function (obj) {
            return new Traverse(obj);
          };
          forEach(objectKeys(Traverse.prototype), function (key) {
            traverse[key] = function (obj) {
              var args = [].slice.call(arguments, 1);
              var t = new Traverse(obj);
              return t[key].apply(t, args);
            };
          });
          var hasOwnProperty = Object.hasOwnProperty || function (obj, key) {
            return key in obj;
          };
          return traverse;
        }();
      }
      exportModule('LYNX.Utils.ThirdParty.traverse', toExport);
    }());
    (function () {
      'use strict';
      // If obj.hasOwnProperty has been overridden, then calling
      // obj.hasOwnProperty(prop) will break.
      // See: https://github.com/joyent/node/issues/1707
      function hasOwnProperty(obj, prop) {
        return Object.prototype.hasOwnProperty.call(obj, prop);
      }
      var querystring = {};
      querystring.parse = querystring.decode = function (qs, sep, eq, options) {
        sep = sep || '&';
        eq = eq || '=';
        var obj = {};
        if (typeof qs !== 'string' || qs.length === 0) {
          return obj;
        }
        var regexp = /\+/g;
        qs = qs.split(sep);
        var maxKeys = 1000;
        if (options && typeof options.maxKeys === 'number') {
          maxKeys = options.maxKeys;
        }
        var len = qs.length;
        // maxKeys <= 0 means that we should not limit keys count
        if (maxKeys > 0 && len > maxKeys) {
          len = maxKeys;
        }
        for (var i = 0; i < len; ++i) {
          var x = qs[i].replace(regexp, '%20'), idx = x.indexOf(eq), kstr, vstr, k, v;
          if (idx >= 0) {
            kstr = x.substr(0, idx);
            vstr = x.substr(idx + 1);
          } else {
            kstr = x;
            vstr = '';
          }
          k = decodeURIComponent(kstr);
          v = decodeURIComponent(vstr);
          if (!hasOwnProperty(obj, k)) {
            obj[k] = v;
          } else if (Array.isArray(obj[k])) {
            obj[k].push(v);
          } else {
            obj[k] = [
              obj[k],
              v
            ];
          }
        }
        return obj;
      };
      var stringifyPrimitive = function (v) {
        switch (typeof v) {
        case 'string':
          return v;
        case 'boolean':
          return v ? 'true' : 'false';
        case 'number':
          return isFinite(v) ? v : '';
        default:
          return '';
        }
      };
      querystring.stringify = querystring.encode = function (obj, sep, eq, name) {
        sep = sep || '&';
        eq = eq || '=';
        if (obj === null) {
          obj = undefined;
        }
        if (typeof obj === 'object') {
          return Object.keys(obj).map(function (k) {
            var ks = encodeURIComponent(stringifyPrimitive(k)) + eq;
            if (Array.isArray(obj[k])) {
              return obj[k].map(function (v) {
                return ks + encodeURIComponent(stringifyPrimitive(v));
              }).join(sep);
            } else {
              return ks + encodeURIComponent(stringifyPrimitive(obj[k]));
            }
          }).join(sep);
        }
        if (!name)
          return '';
        return encodeURIComponent(stringifyPrimitive(name)) + eq + encodeURIComponent(stringifyPrimitive(obj));
      };
      exportModule('LYNX.Utils.ThirdParty.querystring', querystring);
    }());
    (function webpackUniversalModuleDefinition(root, factory) {
      if (typeof exports === 'object' && typeof module === 'object')
        module.exports = factory();
      else if (typeof define === 'function' && define.amd)
        define([], factory);
      else if (typeof exports === 'object')
        exports['io'] = factory();
      else
        root['io'] = factory();
      //----------------------------------------------------------------------------------
      // This section inserted into the original code to export this code to module-robot.
      //----------------------------------------------------------------------------------
      var toExport;
      if (typeof module !== 'undefined' && module.exports) {
        toExport = require('socket.io-client');
      } else {
        toExport = window.io;
      }
      exportModule('LYNX.Utils.ThirdParty.socketIOClient', toExport);  //----------------------------------------------------------------------------------
                                                                       // End section.
                                                                       //----------------------------------------------------------------------------------
    }(this, function () {
      return function (modules) {
        // webpackBootstrap
        // The module cache
        var installedModules = {};
        // The require function
        function __webpack_require__(moduleId) {
          // Check if module is in cache
          if (installedModules[moduleId])
            return installedModules[moduleId].exports;
          // Create a new module (and put it into the cache)
          var module = installedModules[moduleId] = {
            exports: {},
            id: moduleId,
            loaded: false
          };
          // Execute the module function
          modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
          // Flag the module as loaded
          module.loaded = true;
          // Return the exports of the module
          return module.exports;
        }
        // expose the modules object (__webpack_modules__)
        __webpack_require__.m = modules;
        // expose the module cache
        __webpack_require__.c = installedModules;
        // __webpack_public_path__
        __webpack_require__.p = '';
        // Load entry module and return exports
        return __webpack_require__(0);
      }([
        /* 0 */
        /***/
        function (module, exports, __webpack_require__) {
          'use strict';
          var _typeof = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol' ? function (obj) {
            return typeof obj;
          } : function (obj) {
            return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : typeof obj;
          };
          /**
           * Module dependencies.
           */
          var url = __webpack_require__(1);
          var parser = __webpack_require__(7);
          var Manager = __webpack_require__(17);
          var debug = __webpack_require__(3)('socket.io-client');
          /**
           * Module exports.
           */
          module.exports = exports = lookup;
          /**
           * Managers cache.
           */
          var cache = exports.managers = {};
          /**
           * Looks up an existing `Manager` for multiplexing.
           * If the user summons:
           *
           *   `io('http://localhost/a');`
           *   `io('http://localhost/b');`
           *
           * We reuse the existing instance based on same scheme/port/host,
           * and we initialize sockets for each namespace.
           *
           * @api public
           */
          function lookup(uri, opts) {
            if ((typeof uri === 'undefined' ? 'undefined' : _typeof(uri)) === 'object') {
              opts = uri;
              uri = undefined;
            }
            opts = opts || {};
            var parsed = url(uri);
            var source = parsed.source;
            var id = parsed.id;
            var path = parsed.path;
            var sameNamespace = cache[id] && path in cache[id].nsps;
            var newConnection = opts.forceNew || opts['force new connection'] || false === opts.multiplex || sameNamespace;
            var io;
            if (newConnection) {
              debug('ignoring socket cache for %s', source);
              io = Manager(source, opts);
            } else {
              if (!cache[id]) {
                debug('new io instance for %s', source);
                cache[id] = Manager(source, opts);
              }
              io = cache[id];
            }
            if (parsed.query && !opts.query) {
              opts.query = parsed.query;
            } else if (opts && 'object' === _typeof(opts.query)) {
              opts.query = encodeQueryString(opts.query);
            }
            return io.socket(parsed.path, opts);
          }
          /**
           *  Helper method to parse query objects to string.
           * @param {object} query
           * @returns {string}
           */
          function encodeQueryString(obj) {
            var str = [];
            for (var p in obj) {
              if (obj.hasOwnProperty(p)) {
                str.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p]));
              }
            }
            return str.join('&');
          }
          /**
           * Protocol version.
           *
           * @api public
           */
          exports.protocol = parser.protocol;
          /**
           * `connect`.
           *
           * @param {String} uri
           * @api public
           */
          exports.connect = lookup;
          /**
           * Expose constructors for standalone build.
           *
           * @api public
           */
          exports.Manager = __webpack_require__(17);
          exports.Socket = __webpack_require__(44);  /***/
        },
        /* 1 */
        /***/
        function (module, exports, __webpack_require__) {
          /* WEBPACK VAR INJECTION */
          (function (global) {
            'use strict';
            /**
             * Module dependencies.
             */
            var parseuri = __webpack_require__(2);
            var debug = __webpack_require__(3)('socket.io-client:url');
            /**
             * Module exports.
             */
            module.exports = url;
            /**
             * URL parser.
             *
             * @param {String} url
             * @param {Object} An object meant to mimic window.location.
             *                 Defaults to window.location.
             * @api public
             */
            function url(uri, loc) {
              var obj = uri;
              // default to window.location
              loc = loc || global.location;
              if (null == uri)
                uri = loc.protocol + '//' + loc.host;
              // relative path support
              if ('string' === typeof uri) {
                if ('/' === uri.charAt(0)) {
                  if ('/' === uri.charAt(1)) {
                    uri = loc.protocol + uri;
                  } else {
                    uri = loc.host + uri;
                  }
                }
                if (!/^(https?|wss?):\/\//.test(uri)) {
                  debug('protocol-less url %s', uri);
                  if ('undefined' !== typeof loc) {
                    uri = loc.protocol + '//' + uri;
                  } else {
                    uri = 'https://' + uri;
                  }
                }
                // parse
                debug('parse %s', uri);
                obj = parseuri(uri);
              }
              // make sure we treat `localhost:80` and `localhost` equally
              if (!obj.port) {
                if (/^(http|ws)$/.test(obj.protocol)) {
                  obj.port = '80';
                } else if (/^(http|ws)s$/.test(obj.protocol)) {
                  obj.port = '443';
                }
              }
              obj.path = obj.path || '/';
              var ipv6 = obj.host.indexOf(':') !== -1;
              var host = ipv6 ? '[' + obj.host + ']' : obj.host;
              // define unique id
              obj.id = obj.protocol + '://' + host + ':' + obj.port;
              // define href
              obj.href = obj.protocol + '://' + host + (loc && loc.port === obj.port ? '' : ':' + obj.port);
              return obj;
            }  /* WEBPACK VAR INJECTION */
          }.call(exports, function () {
            return this;
          }()));  /***/
        },
        /* 2 */
        /***/
        function (module, exports) {
          /**
           * Parses an URI
           *
           * @author Steven Levithan <stevenlevithan.com> (MIT license)
           * @api private
           */
          var re = /^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/;
          var parts = [
            'source',
            'protocol',
            'authority',
            'userInfo',
            'user',
            'password',
            'host',
            'port',
            'relative',
            'path',
            'directory',
            'file',
            'query',
            'anchor'
          ];
          module.exports = function parseuri(str) {
            var src = str, b = str.indexOf('['), e = str.indexOf(']');
            if (b != -1 && e != -1) {
              str = str.substring(0, b) + str.substring(b, e).replace(/:/g, ';') + str.substring(e, str.length);
            }
            var m = re.exec(str || ''), uri = {}, i = 14;
            while (i--) {
              uri[parts[i]] = m[i] || '';
            }
            if (b != -1 && e != -1) {
              uri.source = src;
              uri.host = uri.host.substring(1, uri.host.length - 1).replace(/;/g, ':');
              uri.authority = uri.authority.replace('[', '').replace(']', '').replace(/;/g, ':');
              uri.ipv6uri = true;
            }
            return uri;
          };  /***/
        },
        /* 3 */
        /***/
        function (module, exports, __webpack_require__) {
          /* WEBPACK VAR INJECTION */
          (function (process) {
            /**
             * This is the web browser implementation of `debug()`.
             *
             * Expose `debug()` as the module.
             */
            exports = module.exports = __webpack_require__(5);
            exports.log = log;
            exports.formatArgs = formatArgs;
            exports.save = save;
            exports.load = load;
            exports.useColors = useColors;
            exports.storage = 'undefined' != typeof chrome && 'undefined' != typeof chrome.storage ? chrome.storage.local : localstorage();
            /**
             * Colors.
             */
            exports.colors = [
              'lightseagreen',
              'forestgreen',
              'goldenrod',
              'dodgerblue',
              'darkorchid',
              'crimson'
            ];
            /**
             * Currently only WebKit-based Web Inspectors, Firefox >= v31,
             * and the Firebug extension (any Firefox version) are known
             * to support "%c" CSS customizations.
             *
             * TODO: add a `localStorage` variable to explicitly enable/disable colors
             */
            function useColors() {
              // is webkit? http://stackoverflow.com/a/16459606/376773
              // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
              return typeof document !== 'undefined' && 'WebkitAppearance' in document.documentElement.style || window.console && (console.firebug || console.exception && console.table) || navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31;
            }
            /**
             * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
             */
            exports.formatters.j = function (v) {
              try {
                return JSON.stringify(v);
              } catch (err) {
                return '[UnexpectedJSONParseError]: ' + err.message;
              }
            };
            /**
             * Colorize log arguments if enabled.
             *
             * @api public
             */
            function formatArgs() {
              var args = arguments;
              var useColors = this.useColors;
              args[0] = (useColors ? '%c' : '') + this.namespace + (useColors ? ' %c' : ' ') + args[0] + (useColors ? '%c ' : ' ') + '+' + exports.humanize(this.diff);
              if (!useColors)
                return args;
              var c = 'color: ' + this.color;
              args = [
                args[0],
                c,
                'color: inherit'
              ].concat(Array.prototype.slice.call(args, 1));
              // the final "%c" is somewhat tricky, because there could be other
              // arguments passed either before or after the %c, so we need to
              // figure out the correct index to insert the CSS into
              var index = 0;
              var lastC = 0;
              args[0].replace(/%[a-z%]/g, function (match) {
                if ('%%' === match)
                  return;
                index++;
                if ('%c' === match) {
                  // we only are interested in the *last* %c
                  // (the user may have provided their own)
                  lastC = index;
                }
              });
              args.splice(lastC, 0, c);
              return args;
            }
            /**
             * Invokes `console.log()` when available.
             * No-op when `console.log` is not a "function".
             *
             * @api public
             */
            function log() {
              // this hackery is required for IE8/9, where
              // the `console.log` function doesn't have 'apply'
              return 'object' === typeof console && console.log && Function.prototype.apply.call(console.log, console, arguments);
            }
            /**
             * Save `namespaces`.
             *
             * @param {String} namespaces
             * @api private
             */
            function save(namespaces) {
              try {
                if (null == namespaces) {
                  exports.storage.removeItem('debug');
                } else {
                  exports.storage.debug = namespaces;
                }
              } catch (e) {
              }
            }
            /**
             * Load `namespaces`.
             *
             * @return {String} returns the previously persisted debug modes
             * @api private
             */
            function load() {
              var r;
              try {
                return exports.storage.debug;
              } catch (e) {
              }
              // If debug isn't set in LS, and we're in Electron, try to load $DEBUG
              if (typeof process !== 'undefined' && 'env' in process) {
                return process.env.DEBUG;
              }
            }
            /**
             * Enable namespaces listed in `localStorage.debug` initially.
             */
            exports.enable(load());
            /**
             * Localstorage attempts to return the localstorage.
             *
             * This is necessary because safari throws
             * when a user disables cookies/localstorage
             * and you attempt to access it.
             *
             * @return {LocalStorage}
             * @api private
             */
            function localstorage() {
              try {
                return window.localStorage;
              } catch (e) {
              }
            }  /* WEBPACK VAR INJECTION */
          }.call(exports, __webpack_require__(4)));  /***/
        },
        /* 4 */
        /***/
        function (module, exports) {
          // shim for using process in browser
          var process = module.exports = {};
          // cached from whatever global is present so that test runners that stub it
          // don't break things.  But we need to wrap it in a try catch in case it is
          // wrapped in strict mode code which doesn't define any globals.  It's inside a
          // function because try/catches deoptimize in certain engines.
          var cachedSetTimeout;
          var cachedClearTimeout;
          function defaultSetTimout() {
            throw new Error('setTimeout has not been defined');
          }
          function defaultClearTimeout() {
            throw new Error('clearTimeout has not been defined');
          }
          (function () {
            try {
              if (typeof setTimeout === 'function') {
                cachedSetTimeout = setTimeout;
              } else {
                cachedSetTimeout = defaultSetTimout;
              }
            } catch (e) {
              cachedSetTimeout = defaultSetTimout;
            }
            try {
              if (typeof clearTimeout === 'function') {
                cachedClearTimeout = clearTimeout;
              } else {
                cachedClearTimeout = defaultClearTimeout;
              }
            } catch (e) {
              cachedClearTimeout = defaultClearTimeout;
            }
          }());
          function runTimeout(fun) {
            if (cachedSetTimeout === setTimeout) {
              //normal enviroments in sane situations
              return setTimeout(fun, 0);
            }
            // if setTimeout wasn't available but was latter defined
            if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
              cachedSetTimeout = setTimeout;
              return setTimeout(fun, 0);
            }
            try {
              // when when somebody has screwed with setTimeout but no I.E. maddness
              return cachedSetTimeout(fun, 0);
            } catch (e) {
              try {
                // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
                return cachedSetTimeout.call(null, fun, 0);
              } catch (e) {
                // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
                return cachedSetTimeout.call(this, fun, 0);
              }
            }
          }
          function runClearTimeout(marker) {
            if (cachedClearTimeout === clearTimeout) {
              //normal enviroments in sane situations
              return clearTimeout(marker);
            }
            // if clearTimeout wasn't available but was latter defined
            if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
              cachedClearTimeout = clearTimeout;
              return clearTimeout(marker);
            }
            try {
              // when when somebody has screwed with setTimeout but no I.E. maddness
              return cachedClearTimeout(marker);
            } catch (e) {
              try {
                // When we are in I.E. but the script has been evaled so I.E. doesn't  trust the global object when called normally
                return cachedClearTimeout.call(null, marker);
              } catch (e) {
                // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
                // Some versions of I.E. have different rules for clearTimeout vs setTimeout
                return cachedClearTimeout.call(this, marker);
              }
            }
          }
          var queue = [];
          var draining = false;
          var currentQueue;
          var queueIndex = -1;
          function cleanUpNextTick() {
            if (!draining || !currentQueue) {
              return;
            }
            draining = false;
            if (currentQueue.length) {
              queue = currentQueue.concat(queue);
            } else {
              queueIndex = -1;
            }
            if (queue.length) {
              drainQueue();
            }
          }
          function drainQueue() {
            if (draining) {
              return;
            }
            var timeout = runTimeout(cleanUpNextTick);
            draining = true;
            var len = queue.length;
            while (len) {
              currentQueue = queue;
              queue = [];
              while (++queueIndex < len) {
                if (currentQueue) {
                  currentQueue[queueIndex].run();
                }
              }
              queueIndex = -1;
              len = queue.length;
            }
            currentQueue = null;
            draining = false;
            runClearTimeout(timeout);
          }
          process.nextTick = function (fun) {
            var args = new Array(arguments.length - 1);
            if (arguments.length > 1) {
              for (var i = 1; i < arguments.length; i++) {
                args[i - 1] = arguments[i];
              }
            }
            queue.push(new Item(fun, args));
            if (queue.length === 1 && !draining) {
              runTimeout(drainQueue);
            }
          };
          // v8 likes predictible objects
          function Item(fun, array) {
            this.fun = fun;
            this.array = array;
          }
          Item.prototype.run = function () {
            this.fun.apply(null, this.array);
          };
          process.title = 'browser';
          process.browser = true;
          process.env = {};
          process.argv = [];
          process.version = '';
          // empty string to avoid regexp issues
          process.versions = {};
          function noop() {
          }
          process.on = noop;
          process.addListener = noop;
          process.once = noop;
          process.off = noop;
          process.removeListener = noop;
          process.removeAllListeners = noop;
          process.emit = noop;
          process.binding = function (name) {
            throw new Error('process.binding is not supported');
          };
          process.cwd = function () {
            return '/';
          };
          process.chdir = function (dir) {
            throw new Error('process.chdir is not supported');
          };
          process.umask = function () {
            return 0;
          };  /***/
        },
        /* 5 */
        /***/
        function (module, exports, __webpack_require__) {
          /**
           * This is the common logic for both the Node.js and web browser
           * implementations of `debug()`.
           *
           * Expose `debug()` as the module.
           */
          exports = module.exports = debug.debug = debug;
          exports.coerce = coerce;
          exports.disable = disable;
          exports.enable = enable;
          exports.enabled = enabled;
          exports.humanize = __webpack_require__(6);
          /**
           * The currently active debug mode names, and names to skip.
           */
          exports.names = [];
          exports.skips = [];
          /**
           * Map of special "%n" handling functions, for the debug "format" argument.
           *
           * Valid key names are a single, lowercased letter, i.e. "n".
           */
          exports.formatters = {};
          /**
           * Previously assigned color.
           */
          var prevColor = 0;
          /**
           * Previous log timestamp.
           */
          var prevTime;
          /**
           * Select a color.
           *
           * @return {Number}
           * @api private
           */
          function selectColor() {
            return exports.colors[prevColor++ % exports.colors.length];
          }
          /**
           * Create a debugger with the given `namespace`.
           *
           * @param {String} namespace
           * @return {Function}
           * @api public
           */
          function debug(namespace) {
            // define the `disabled` version
            function disabled() {
            }
            disabled.enabled = false;
            // define the `enabled` version
            function enabled() {
              var self = enabled;
              // set `diff` timestamp
              var curr = +new Date();
              var ms = curr - (prevTime || curr);
              self.diff = ms;
              self.prev = prevTime;
              self.curr = curr;
              prevTime = curr;
              // add the `color` if not set
              if (null == self.useColors)
                self.useColors = exports.useColors();
              if (null == self.color && self.useColors)
                self.color = selectColor();
              var args = new Array(arguments.length);
              for (var i = 0; i < args.length; i++) {
                args[i] = arguments[i];
              }
              args[0] = exports.coerce(args[0]);
              if ('string' !== typeof args[0]) {
                // anything else let's inspect with %o
                args = ['%o'].concat(args);
              }
              // apply any `formatters` transformations
              var index = 0;
              args[0] = args[0].replace(/%([a-z%])/g, function (match, format) {
                // if we encounter an escaped % then don't increase the array index
                if (match === '%%')
                  return match;
                index++;
                var formatter = exports.formatters[format];
                if ('function' === typeof formatter) {
                  var val = args[index];
                  match = formatter.call(self, val);
                  // now we need to remove `args[index]` since it's inlined in the `format`
                  args.splice(index, 1);
                  index--;
                }
                return match;
              });
              // apply env-specific formatting
              args = exports.formatArgs.apply(self, args);
              var logFn = enabled.log || exports.log || console.log.bind(console);
              logFn.apply(self, args);
            }
            enabled.enabled = true;
            var fn = exports.enabled(namespace) ? enabled : disabled;
            fn.namespace = namespace;
            return fn;
          }
          /**
           * Enables a debug mode by namespaces. This can include modes
           * separated by a colon and wildcards.
           *
           * @param {String} namespaces
           * @api public
           */
          function enable(namespaces) {
            exports.save(namespaces);
            var split = (namespaces || '').split(/[\s,]+/);
            var len = split.length;
            for (var i = 0; i < len; i++) {
              if (!split[i])
                continue;
              // ignore empty strings
              namespaces = split[i].replace(/[\\^$+?.()|[\]{}]/g, '\\$&').replace(/\*/g, '.*?');
              if (namespaces[0] === '-') {
                exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
              } else {
                exports.names.push(new RegExp('^' + namespaces + '$'));
              }
            }
          }
          /**
           * Disable debug output.
           *
           * @api public
           */
          function disable() {
            exports.enable('');
          }
          /**
           * Returns true if the given mode name is enabled, false otherwise.
           *
           * @param {String} name
           * @return {Boolean}
           * @api public
           */
          function enabled(name) {
            var i, len;
            for (i = 0, len = exports.skips.length; i < len; i++) {
              if (exports.skips[i].test(name)) {
                return false;
              }
            }
            for (i = 0, len = exports.names.length; i < len; i++) {
              if (exports.names[i].test(name)) {
                return true;
              }
            }
            return false;
          }
          /**
           * Coerce `val`.
           *
           * @param {Mixed} val
           * @return {Mixed}
           * @api private
           */
          function coerce(val) {
            if (val instanceof Error)
              return val.stack || val.message;
            return val;
          }  /***/
        },
        /* 6 */
        /***/
        function (module, exports) {
          /**
           * Helpers.
           */
          var s = 1000;
          var m = s * 60;
          var h = m * 60;
          var d = h * 24;
          var y = d * 365.25;
          /**
           * Parse or format the given `val`.
           *
           * Options:
           *
           *  - `long` verbose formatting [false]
           *
           * @param {String|Number} val
           * @param {Object} options
           * @throws {Error} throw an error if val is not a non-empty string or a number
           * @return {String|Number}
           * @api public
           */
          module.exports = function (val, options) {
            options = options || {};
            var type = typeof val;
            if (type === 'string' && val.length > 0) {
              return parse(val);
            } else if (type === 'number' && isNaN(val) === false) {
              return options.long ? fmtLong(val) : fmtShort(val);
            }
            throw new Error('val is not a non-empty string or a valid number. val=' + JSON.stringify(val));
          };
          /**
           * Parse the given `str` and return milliseconds.
           *
           * @param {String} str
           * @return {Number}
           * @api private
           */
          function parse(str) {
            str = String(str);
            if (str.length > 10000) {
              return;
            }
            var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str);
            if (!match) {
              return;
            }
            var n = parseFloat(match[1]);
            var type = (match[2] || 'ms').toLowerCase();
            switch (type) {
            case 'years':
            case 'year':
            case 'yrs':
            case 'yr':
            case 'y':
              return n * y;
            case 'days':
            case 'day':
            case 'd':
              return n * d;
            case 'hours':
            case 'hour':
            case 'hrs':
            case 'hr':
            case 'h':
              return n * h;
            case 'minutes':
            case 'minute':
            case 'mins':
            case 'min':
            case 'm':
              return n * m;
            case 'seconds':
            case 'second':
            case 'secs':
            case 'sec':
            case 's':
              return n * s;
            case 'milliseconds':
            case 'millisecond':
            case 'msecs':
            case 'msec':
            case 'ms':
              return n;
            default:
              return undefined;
            }
          }
          /**
           * Short format for `ms`.
           *
           * @param {Number} ms
           * @return {String}
           * @api private
           */
          function fmtShort(ms) {
            if (ms >= d) {
              return Math.round(ms / d) + 'd';
            }
            if (ms >= h) {
              return Math.round(ms / h) + 'h';
            }
            if (ms >= m) {
              return Math.round(ms / m) + 'm';
            }
            if (ms >= s) {
              return Math.round(ms / s) + 's';
            }
            return ms + 'ms';
          }
          /**
           * Long format for `ms`.
           *
           * @param {Number} ms
           * @return {String}
           * @api private
           */
          function fmtLong(ms) {
            return plural(ms, d, 'day') || plural(ms, h, 'hour') || plural(ms, m, 'minute') || plural(ms, s, 'second') || ms + ' ms';
          }
          /**
           * Pluralization helper.
           */
          function plural(ms, n, name) {
            if (ms < n) {
              return;
            }
            if (ms < n * 1.5) {
              return Math.floor(ms / n) + ' ' + name;
            }
            return Math.ceil(ms / n) + ' ' + name + 's';
          }  /***/
        },
        /* 7 */
        /***/
        function (module, exports, __webpack_require__) {
          /**
           * Module dependencies.
           */
          var debug = __webpack_require__(8)('socket.io-parser');
          var json = __webpack_require__(11);
          var Emitter = __webpack_require__(13);
          var binary = __webpack_require__(14);
          var isBuf = __webpack_require__(16);
          /**
           * Protocol version.
           *
           * @api public
           */
          exports.protocol = 4;
          /**
           * Packet types.
           *
           * @api public
           */
          exports.types = [
            'CONNECT',
            'DISCONNECT',
            'EVENT',
            'ACK',
            'ERROR',
            'BINARY_EVENT',
            'BINARY_ACK'
          ];
          /**
           * Packet type `connect`.
           *
           * @api public
           */
          exports.CONNECT = 0;
          /**
           * Packet type `disconnect`.
           *
           * @api public
           */
          exports.DISCONNECT = 1;
          /**
           * Packet type `event`.
           *
           * @api public
           */
          exports.EVENT = 2;
          /**
           * Packet type `ack`.
           *
           * @api public
           */
          exports.ACK = 3;
          /**
           * Packet type `error`.
           *
           * @api public
           */
          exports.ERROR = 4;
          /**
           * Packet type 'binary event'
           *
           * @api public
           */
          exports.BINARY_EVENT = 5;
          /**
           * Packet type `binary ack`. For acks with binary arguments.
           *
           * @api public
           */
          exports.BINARY_ACK = 6;
          /**
           * Encoder constructor.
           *
           * @api public
           */
          exports.Encoder = Encoder;
          /**
           * Decoder constructor.
           *
           * @api public
           */
          exports.Decoder = Decoder;
          /**
           * A socket.io Encoder instance
           *
           * @api public
           */
          function Encoder() {
          }
          /**
           * Encode a packet as a single string if non-binary, or as a
           * buffer sequence, depending on packet type.
           *
           * @param {Object} obj - packet object
           * @param {Function} callback - function to handle encodings (likely engine.write)
           * @return Calls callback with Array of encodings
           * @api public
           */
          Encoder.prototype.encode = function (obj, callback) {
            debug('encoding packet %j', obj);
            if (exports.BINARY_EVENT == obj.type || exports.BINARY_ACK == obj.type) {
              encodeAsBinary(obj, callback);
            } else {
              var encoding = encodeAsString(obj);
              callback([encoding]);
            }
          };
          /**
           * Encode packet as string.
           *
           * @param {Object} packet
           * @return {String} encoded
           * @api private
           */
          function encodeAsString(obj) {
            var str = '';
            var nsp = false;
            // first is type
            str += obj.type;
            // attachments if we have them
            if (exports.BINARY_EVENT == obj.type || exports.BINARY_ACK == obj.type) {
              str += obj.attachments;
              str += '-';
            }
            // if we have a namespace other than `/`
            // we append it followed by a comma `,`
            if (obj.nsp && '/' != obj.nsp) {
              nsp = true;
              str += obj.nsp;
            }
            // immediately followed by the id
            if (null != obj.id) {
              if (nsp) {
                str += ',';
                nsp = false;
              }
              str += obj.id;
            }
            // json data
            if (null != obj.data) {
              if (nsp)
                str += ',';
              str += json.stringify(obj.data);
            }
            debug('encoded %j as %s', obj, str);
            return str;
          }
          /**
           * Encode packet as 'buffer sequence' by removing blobs, and
           * deconstructing packet into object with placeholders and
           * a list of buffers.
           *
           * @param {Object} packet
           * @return {Buffer} encoded
           * @api private
           */
          function encodeAsBinary(obj, callback) {
            function writeEncoding(bloblessData) {
              var deconstruction = binary.deconstructPacket(bloblessData);
              var pack = encodeAsString(deconstruction.packet);
              var buffers = deconstruction.buffers;
              buffers.unshift(pack);
              // add packet info to beginning of data list
              callback(buffers);  // write all the buffers
            }
            binary.removeBlobs(obj, writeEncoding);
          }
          /**
           * A socket.io Decoder instance
           *
           * @return {Object} decoder
           * @api public
           */
          function Decoder() {
            this.reconstructor = null;
          }
          /**
           * Mix in `Emitter` with Decoder.
           */
          Emitter(Decoder.prototype);
          /**
           * Decodes an ecoded packet string into packet JSON.
           *
           * @param {String} obj - encoded packet
           * @return {Object} packet
           * @api public
           */
          Decoder.prototype.add = function (obj) {
            var packet;
            if ('string' == typeof obj) {
              packet = decodeString(obj);
              if (exports.BINARY_EVENT == packet.type || exports.BINARY_ACK == packet.type) {
                // binary packet's json
                this.reconstructor = new BinaryReconstructor(packet);
                // no attachments, labeled binary but no binary data to follow
                if (this.reconstructor.reconPack.attachments === 0) {
                  this.emit('decoded', packet);
                }
              } else {
                // non-binary full packet
                this.emit('decoded', packet);
              }
            } else if (isBuf(obj) || obj.base64) {
              // raw binary data
              if (!this.reconstructor) {
                throw new Error('got binary data when not reconstructing a packet');
              } else {
                packet = this.reconstructor.takeBinaryData(obj);
                if (packet) {
                  // received final buffer
                  this.reconstructor = null;
                  this.emit('decoded', packet);
                }
              }
            } else {
              throw new Error('Unknown type: ' + obj);
            }
          };
          /**
           * Decode a packet String (JSON data)
           *
           * @param {String} str
           * @return {Object} packet
           * @api private
           */
          function decodeString(str) {
            var p = {};
            var i = 0;
            // look up type
            p.type = Number(str.charAt(0));
            if (null == exports.types[p.type])
              return error();
            // look up attachments if type binary
            if (exports.BINARY_EVENT == p.type || exports.BINARY_ACK == p.type) {
              var buf = '';
              while (str.charAt(++i) != '-') {
                buf += str.charAt(i);
                if (i == str.length)
                  break;
              }
              if (buf != Number(buf) || str.charAt(i) != '-') {
                throw new Error('Illegal attachments');
              }
              p.attachments = Number(buf);
            }
            // look up namespace (if any)
            if ('/' == str.charAt(i + 1)) {
              p.nsp = '';
              while (++i) {
                var c = str.charAt(i);
                if (',' == c)
                  break;
                p.nsp += c;
                if (i == str.length)
                  break;
              }
            } else {
              p.nsp = '/';
            }
            // look up id
            var next = str.charAt(i + 1);
            if ('' !== next && Number(next) == next) {
              p.id = '';
              while (++i) {
                var c = str.charAt(i);
                if (null == c || Number(c) != c) {
                  --i;
                  break;
                }
                p.id += str.charAt(i);
                if (i == str.length)
                  break;
              }
              p.id = Number(p.id);
            }
            // look up json data
            if (str.charAt(++i)) {
              p = tryParse(p, str.substr(i));
            }
            debug('decoded %s as %j', str, p);
            return p;
          }
          function tryParse(p, str) {
            try {
              p.data = json.parse(str);
            } catch (e) {
              return error();
            }
            return p;
          }
          ;
          /**
           * Deallocates a parser's resources
           *
           * @api public
           */
          Decoder.prototype.destroy = function () {
            if (this.reconstructor) {
              this.reconstructor.finishedReconstruction();
            }
          };
          /**
           * A manager of a binary event's 'buffer sequence'. Should
           * be constructed whenever a packet of type BINARY_EVENT is
           * decoded.
           *
           * @param {Object} packet
           * @return {BinaryReconstructor} initialized reconstructor
           * @api private
           */
          function BinaryReconstructor(packet) {
            this.reconPack = packet;
            this.buffers = [];
          }
          /**
           * Method to be called when binary data received from connection
           * after a BINARY_EVENT packet.
           *
           * @param {Buffer | ArrayBuffer} binData - the raw binary data received
           * @return {null | Object} returns null if more binary data is expected or
           *   a reconstructed packet object if all buffers have been received.
           * @api private
           */
          BinaryReconstructor.prototype.takeBinaryData = function (binData) {
            this.buffers.push(binData);
            if (this.buffers.length == this.reconPack.attachments) {
              // done with buffer list
              var packet = binary.reconstructPacket(this.reconPack, this.buffers);
              this.finishedReconstruction();
              return packet;
            }
            return null;
          };
          /**
           * Cleans up binary packet reconstruction variables.
           *
           * @api private
           */
          BinaryReconstructor.prototype.finishedReconstruction = function () {
            this.reconPack = null;
            this.buffers = [];
          };
          function error(data) {
            return {
              type: exports.ERROR,
              data: 'parser error'
            };
          }  /***/
        },
        /* 8 */
        /***/
        function (module, exports, __webpack_require__) {
          /**
           * This is the web browser implementation of `debug()`.
           *
           * Expose `debug()` as the module.
           */
          exports = module.exports = __webpack_require__(9);
          exports.log = log;
          exports.formatArgs = formatArgs;
          exports.save = save;
          exports.load = load;
          exports.useColors = useColors;
          exports.storage = 'undefined' != typeof chrome && 'undefined' != typeof chrome.storage ? chrome.storage.local : localstorage();
          /**
           * Colors.
           */
          exports.colors = [
            'lightseagreen',
            'forestgreen',
            'goldenrod',
            'dodgerblue',
            'darkorchid',
            'crimson'
          ];
          /**
           * Currently only WebKit-based Web Inspectors, Firefox >= v31,
           * and the Firebug extension (any Firefox version) are known
           * to support "%c" CSS customizations.
           *
           * TODO: add a `localStorage` variable to explicitly enable/disable colors
           */
          function useColors() {
            // is webkit? http://stackoverflow.com/a/16459606/376773
            return 'WebkitAppearance' in document.documentElement.style || window.console && (console.firebug || console.exception && console.table) || navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31;
          }
          /**
           * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
           */
          exports.formatters.j = function (v) {
            return JSON.stringify(v);
          };
          /**
           * Colorize log arguments if enabled.
           *
           * @api public
           */
          function formatArgs() {
            var args = arguments;
            var useColors = this.useColors;
            args[0] = (useColors ? '%c' : '') + this.namespace + (useColors ? ' %c' : ' ') + args[0] + (useColors ? '%c ' : ' ') + '+' + exports.humanize(this.diff);
            if (!useColors)
              return args;
            var c = 'color: ' + this.color;
            args = [
              args[0],
              c,
              'color: inherit'
            ].concat(Array.prototype.slice.call(args, 1));
            // the final "%c" is somewhat tricky, because there could be other
            // arguments passed either before or after the %c, so we need to
            // figure out the correct index to insert the CSS into
            var index = 0;
            var lastC = 0;
            args[0].replace(/%[a-z%]/g, function (match) {
              if ('%%' === match)
                return;
              index++;
              if ('%c' === match) {
                // we only are interested in the *last* %c
                // (the user may have provided their own)
                lastC = index;
              }
            });
            args.splice(lastC, 0, c);
            return args;
          }
          /**
           * Invokes `console.log()` when available.
           * No-op when `console.log` is not a "function".
           *
           * @api public
           */
          function log() {
            // this hackery is required for IE8/9, where
            // the `console.log` function doesn't have 'apply'
            return 'object' === typeof console && console.log && Function.prototype.apply.call(console.log, console, arguments);
          }
          /**
           * Save `namespaces`.
           *
           * @param {String} namespaces
           * @api private
           */
          function save(namespaces) {
            try {
              if (null == namespaces) {
                exports.storage.removeItem('debug');
              } else {
                exports.storage.debug = namespaces;
              }
            } catch (e) {
            }
          }
          /**
           * Load `namespaces`.
           *
           * @return {String} returns the previously persisted debug modes
           * @api private
           */
          function load() {
            var r;
            try {
              r = exports.storage.debug;
            } catch (e) {
            }
            return r;
          }
          /**
           * Enable namespaces listed in `localStorage.debug` initially.
           */
          exports.enable(load());
          /**
           * Localstorage attempts to return the localstorage.
           *
           * This is necessary because safari throws
           * when a user disables cookies/localstorage
           * and you attempt to access it.
           *
           * @return {LocalStorage}
           * @api private
           */
          function localstorage() {
            try {
              return window.localStorage;
            } catch (e) {
            }
          }  /***/
        },
        /* 9 */
        /***/
        function (module, exports, __webpack_require__) {
          /**
           * This is the common logic for both the Node.js and web browser
           * implementations of `debug()`.
           *
           * Expose `debug()` as the module.
           */
          exports = module.exports = debug;
          exports.coerce = coerce;
          exports.disable = disable;
          exports.enable = enable;
          exports.enabled = enabled;
          exports.humanize = __webpack_require__(10);
          /**
           * The currently active debug mode names, and names to skip.
           */
          exports.names = [];
          exports.skips = [];
          /**
           * Map of special "%n" handling functions, for the debug "format" argument.
           *
           * Valid key names are a single, lowercased letter, i.e. "n".
           */
          exports.formatters = {};
          /**
           * Previously assigned color.
           */
          var prevColor = 0;
          /**
           * Previous log timestamp.
           */
          var prevTime;
          /**
           * Select a color.
           *
           * @return {Number}
           * @api private
           */
          function selectColor() {
            return exports.colors[prevColor++ % exports.colors.length];
          }
          /**
           * Create a debugger with the given `namespace`.
           *
           * @param {String} namespace
           * @return {Function}
           * @api public
           */
          function debug(namespace) {
            // define the `disabled` version
            function disabled() {
            }
            disabled.enabled = false;
            // define the `enabled` version
            function enabled() {
              var self = enabled;
              // set `diff` timestamp
              var curr = +new Date();
              var ms = curr - (prevTime || curr);
              self.diff = ms;
              self.prev = prevTime;
              self.curr = curr;
              prevTime = curr;
              // add the `color` if not set
              if (null == self.useColors)
                self.useColors = exports.useColors();
              if (null == self.color && self.useColors)
                self.color = selectColor();
              var args = Array.prototype.slice.call(arguments);
              args[0] = exports.coerce(args[0]);
              if ('string' !== typeof args[0]) {
                // anything else let's inspect with %o
                args = ['%o'].concat(args);
              }
              // apply any `formatters` transformations
              var index = 0;
              args[0] = args[0].replace(/%([a-z%])/g, function (match, format) {
                // if we encounter an escaped % then don't increase the array index
                if (match === '%%')
                  return match;
                index++;
                var formatter = exports.formatters[format];
                if ('function' === typeof formatter) {
                  var val = args[index];
                  match = formatter.call(self, val);
                  // now we need to remove `args[index]` since it's inlined in the `format`
                  args.splice(index, 1);
                  index--;
                }
                return match;
              });
              if ('function' === typeof exports.formatArgs) {
                args = exports.formatArgs.apply(self, args);
              }
              var logFn = enabled.log || exports.log || console.log.bind(console);
              logFn.apply(self, args);
            }
            enabled.enabled = true;
            var fn = exports.enabled(namespace) ? enabled : disabled;
            fn.namespace = namespace;
            return fn;
          }
          /**
           * Enables a debug mode by namespaces. This can include modes
           * separated by a colon and wildcards.
           *
           * @param {String} namespaces
           * @api public
           */
          function enable(namespaces) {
            exports.save(namespaces);
            var split = (namespaces || '').split(/[\s,]+/);
            var len = split.length;
            for (var i = 0; i < len; i++) {
              if (!split[i])
                continue;
              // ignore empty strings
              namespaces = split[i].replace(/\*/g, '.*?');
              if (namespaces[0] === '-') {
                exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
              } else {
                exports.names.push(new RegExp('^' + namespaces + '$'));
              }
            }
          }
          /**
           * Disable debug output.
           *
           * @api public
           */
          function disable() {
            exports.enable('');
          }
          /**
           * Returns true if the given mode name is enabled, false otherwise.
           *
           * @param {String} name
           * @return {Boolean}
           * @api public
           */
          function enabled(name) {
            var i, len;
            for (i = 0, len = exports.skips.length; i < len; i++) {
              if (exports.skips[i].test(name)) {
                return false;
              }
            }
            for (i = 0, len = exports.names.length; i < len; i++) {
              if (exports.names[i].test(name)) {
                return true;
              }
            }
            return false;
          }
          /**
           * Coerce `val`.
           *
           * @param {Mixed} val
           * @return {Mixed}
           * @api private
           */
          function coerce(val) {
            if (val instanceof Error)
              return val.stack || val.message;
            return val;
          }  /***/
        },
        /* 10 */
        /***/
        function (module, exports) {
          /**
           * Helpers.
           */
          var s = 1000;
          var m = s * 60;
          var h = m * 60;
          var d = h * 24;
          var y = d * 365.25;
          /**
           * Parse or format the given `val`.
           *
           * Options:
           *
           *  - `long` verbose formatting [false]
           *
           * @param {String|Number} val
           * @param {Object} options
           * @return {String|Number}
           * @api public
           */
          module.exports = function (val, options) {
            options = options || {};
            if ('string' == typeof val)
              return parse(val);
            return options.long ? long(val) : short(val);
          };
          /**
           * Parse the given `str` and return milliseconds.
           *
           * @param {String} str
           * @return {Number}
           * @api private
           */
          function parse(str) {
            str = '' + str;
            if (str.length > 10000)
              return;
            var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(str);
            if (!match)
              return;
            var n = parseFloat(match[1]);
            var type = (match[2] || 'ms').toLowerCase();
            switch (type) {
            case 'years':
            case 'year':
            case 'yrs':
            case 'yr':
            case 'y':
              return n * y;
            case 'days':
            case 'day':
            case 'd':
              return n * d;
            case 'hours':
            case 'hour':
            case 'hrs':
            case 'hr':
            case 'h':
              return n * h;
            case 'minutes':
            case 'minute':
            case 'mins':
            case 'min':
            case 'm':
              return n * m;
            case 'seconds':
            case 'second':
            case 'secs':
            case 'sec':
            case 's':
              return n * s;
            case 'milliseconds':
            case 'millisecond':
            case 'msecs':
            case 'msec':
            case 'ms':
              return n;
            }
          }
          /**
           * Short format for `ms`.
           *
           * @param {Number} ms
           * @return {String}
           * @api private
           */
          function short(ms) {
            if (ms >= d)
              return Math.round(ms / d) + 'd';
            if (ms >= h)
              return Math.round(ms / h) + 'h';
            if (ms >= m)
              return Math.round(ms / m) + 'm';
            if (ms >= s)
              return Math.round(ms / s) + 's';
            return ms + 'ms';
          }
          /**
           * Long format for `ms`.
           *
           * @param {Number} ms
           * @return {String}
           * @api private
           */
          function long(ms) {
            return plural(ms, d, 'day') || plural(ms, h, 'hour') || plural(ms, m, 'minute') || plural(ms, s, 'second') || ms + ' ms';
          }
          /**
           * Pluralization helper.
           */
          function plural(ms, n, name) {
            if (ms < n)
              return;
            if (ms < n * 1.5)
              return Math.floor(ms / n) + ' ' + name;
            return Math.ceil(ms / n) + ' ' + name + 's';
          }  /***/
        },
        /* 11 */
        /***/
        function (module, exports, __webpack_require__) {
          /* WEBPACK VAR INJECTION */
          (function (module, global) {
            /*** IMPORTS FROM imports-loader ***/
            var define = false;
            /*! JSON v3.3.2 | http://bestiejs.github.io/json3 | Copyright 2012-2014, Kit Cambridge | http://kit.mit-license.org */
            ;
            (function () {
              // Detect the `define` function exposed by asynchronous module loaders. The
              // strict `define` check is necessary for compatibility with `r.js`.
              var isLoader = typeof define === 'function' && define.amd;
              // A set of types used to distinguish objects from primitives.
              var objectTypes = {
                'function': true,
                'object': true
              };
              // Detect the `exports` object exposed by CommonJS implementations.
              var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;
              // Use the `global` object exposed by Node (including Browserify via
              // `insert-module-globals`), Narwhal, and Ringo as the default context,
              // and the `window` object in browsers. Rhino exports a `global` function
              // instead.
              var root = objectTypes[typeof window] && window || this, freeGlobal = freeExports && objectTypes[typeof module] && module && !module.nodeType && typeof global == 'object' && global;
              if (freeGlobal && (freeGlobal['global'] === freeGlobal || freeGlobal['window'] === freeGlobal || freeGlobal['self'] === freeGlobal)) {
                root = freeGlobal;
              }
              // Public: Initializes JSON 3 using the given `context` object, attaching the
              // `stringify` and `parse` functions to the specified `exports` object.
              function runInContext(context, exports) {
                context || (context = root['Object']());
                exports || (exports = root['Object']());
                // Native constructor aliases.
                var Number = context['Number'] || root['Number'], String = context['String'] || root['String'], Object = context['Object'] || root['Object'], Date = context['Date'] || root['Date'], SyntaxError = context['SyntaxError'] || root['SyntaxError'], TypeError = context['TypeError'] || root['TypeError'], Math = context['Math'] || root['Math'], nativeJSON = context['JSON'] || root['JSON'];
                // Delegate to the native `stringify` and `parse` implementations.
                if (typeof nativeJSON == 'object' && nativeJSON) {
                  exports.stringify = nativeJSON.stringify;
                  exports.parse = nativeJSON.parse;
                }
                // Convenience aliases.
                var objectProto = Object.prototype, getClass = objectProto.toString, isProperty, forEach, undef;
                // Test the `Date#getUTC*` methods. Based on work by @Yaffle.
                var isExtended = new Date(-3509827334573292);
                try {
                  // The `getUTCFullYear`, `Month`, and `Date` methods return nonsensical
                  // results for certain dates in Opera >= 10.53.
                  isExtended = isExtended.getUTCFullYear() == -109252 && isExtended.getUTCMonth() === 0 && isExtended.getUTCDate() === 1 && // Safari < 2.0.2 stores the internal millisecond time value correctly,
                  // but clips the values returned by the date methods to the range of
                  // signed 32-bit integers ([-2 ** 31, 2 ** 31 - 1]).
                  isExtended.getUTCHours() == 10 && isExtended.getUTCMinutes() == 37 && isExtended.getUTCSeconds() == 6 && isExtended.getUTCMilliseconds() == 708;
                } catch (exception) {
                }
                // Internal: Determines whether the native `JSON.stringify` and `parse`
                // implementations are spec-compliant. Based on work by Ken Snyder.
                function has(name) {
                  if (has[name] !== undef) {
                    // Return cached feature test result.
                    return has[name];
                  }
                  var isSupported;
                  if (name == 'bug-string-char-index') {
                    // IE <= 7 doesn't support accessing string characters using square
                    // bracket notation. IE 8 only supports this for primitives.
                    isSupported = 'a'[0] != 'a';
                  } else if (name == 'json') {
                    // Indicates whether both `JSON.stringify` and `JSON.parse` are
                    // supported.
                    isSupported = has('json-stringify') && has('json-parse');
                  } else {
                    var value, serialized = '{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}';
                    // Test `JSON.stringify`.
                    if (name == 'json-stringify') {
                      var stringify = exports.stringify, stringifySupported = typeof stringify == 'function' && isExtended;
                      if (stringifySupported) {
                        // A test function object with a custom `toJSON` method.
                        (value = function () {
                          return 1;
                        }).toJSON = value;
                        try {
                          stringifySupported = // Firefox 3.1b1 and b2 serialize string, number, and boolean
                          // primitives as object literals.
                          stringify(0) === '0' && // FF 3.1b1, b2, and JSON 2 serialize wrapped primitives as object
                          // literals.
                          stringify(new Number()) === '0' && stringify(new String()) == '""' && // FF 3.1b1, 2 throw an error if the value is `null`, `undefined`, or
                          // does not define a canonical JSON representation (this applies to
                          // objects with `toJSON` properties as well, *unless* they are nested
                          // within an object or array).
                          stringify(getClass) === undef && // IE 8 serializes `undefined` as `"undefined"`. Safari <= 5.1.7 and
                          // FF 3.1b3 pass this test.
                          stringify(undef) === undef && // Safari <= 5.1.7 and FF 3.1b3 throw `Error`s and `TypeError`s,
                          // respectively, if the value is omitted entirely.
                          stringify() === undef && // FF 3.1b1, 2 throw an error if the given value is not a number,
                          // string, array, object, Boolean, or `null` literal. This applies to
                          // objects with custom `toJSON` methods as well, unless they are nested
                          // inside object or array literals. YUI 3.0.0b1 ignores custom `toJSON`
                          // methods entirely.
                          stringify(value) === '1' && stringify([value]) == '[1]' && // Prototype <= 1.6.1 serializes `[undefined]` as `"[]"` instead of
                          // `"[null]"`.
                          stringify([undef]) == '[null]' && // YUI 3.0.0b1 fails to serialize `null` literals.
                          stringify(null) == 'null' && // FF 3.1b1, 2 halts serialization if an array contains a function:
                          // `[1, true, getClass, 1]` serializes as "[1,true,],". FF 3.1b3
                          // elides non-JSON values from objects and arrays, unless they
                          // define custom `toJSON` methods.
                          stringify([
                            undef,
                            getClass,
                            null
                          ]) == '[null,null,null]' && // Simple serialization test. FF 3.1b1 uses Unicode escape sequences
                          // where character escape codes are expected (e.g., `\b` => `\u0008`).
                          stringify({
                            'a': [
                              value,
                              true,
                              false,
                              null,
                              '\0\b\n\f\r\t'
                            ]
                          }) == serialized && // FF 3.1b1 and b2 ignore the `filter` and `width` arguments.
                          stringify(null, value) === '1' && stringify([
                            1,
                            2
                          ], null, 1) == '[\n 1,\n 2\n]' && // JSON 2, Prototype <= 1.7, and older WebKit builds incorrectly
                          // serialize extended years.
                          stringify(new Date(-8640000000000000)) == '"-271821-04-20T00:00:00.000Z"' && // The milliseconds are optional in ES 5, but required in 5.1.
                          stringify(new Date(8640000000000000)) == '"+275760-09-13T00:00:00.000Z"' && // Firefox <= 11.0 incorrectly serializes years prior to 0 as negative
                          // four-digit years instead of six-digit years. Credits: @Yaffle.
                          stringify(new Date(-62198755200000)) == '"-000001-01-01T00:00:00.000Z"' && // Safari <= 5.1.5 and Opera >= 10.53 incorrectly serialize millisecond
                          // values less than 1000. Credits: @Yaffle.
                          stringify(new Date(-1)) == '"1969-12-31T23:59:59.999Z"';
                        } catch (exception) {
                          stringifySupported = false;
                        }
                      }
                      isSupported = stringifySupported;
                    }
                    // Test `JSON.parse`.
                    if (name == 'json-parse') {
                      var parse = exports.parse;
                      if (typeof parse == 'function') {
                        try {
                          // FF 3.1b1, b2 will throw an exception if a bare literal is provided.
                          // Conforming implementations should also coerce the initial argument to
                          // a string prior to parsing.
                          if (parse('0') === 0 && !parse(false)) {
                            // Simple parsing test.
                            value = parse(serialized);
                            var parseSupported = value['a'].length == 5 && value['a'][0] === 1;
                            if (parseSupported) {
                              try {
                                // Safari <= 5.1.2 and FF 3.1b1 allow unescaped tabs in strings.
                                parseSupported = !parse('"\t"');
                              } catch (exception) {
                              }
                              if (parseSupported) {
                                try {
                                  // FF 4.0 and 4.0.1 allow leading `+` signs and leading
                                  // decimal points. FF 4.0, 4.0.1, and IE 9-10 also allow
                                  // certain octal literals.
                                  parseSupported = parse('01') !== 1;
                                } catch (exception) {
                                }
                              }
                              if (parseSupported) {
                                try {
                                  // FF 4.0, 4.0.1, and Rhino 1.7R3-R4 allow trailing decimal
                                  // points. These environments, along with FF 3.1b1 and 2,
                                  // also allow trailing commas in JSON objects and arrays.
                                  parseSupported = parse('1.') !== 1;
                                } catch (exception) {
                                }
                              }
                            }
                          }
                        } catch (exception) {
                          parseSupported = false;
                        }
                      }
                      isSupported = parseSupported;
                    }
                  }
                  return has[name] = !!isSupported;
                }
                if (!has('json')) {
                  // Common `[[Class]]` name aliases.
                  var functionClass = '[object Function]', dateClass = '[object Date]', numberClass = '[object Number]', stringClass = '[object String]', arrayClass = '[object Array]', booleanClass = '[object Boolean]';
                  // Detect incomplete support for accessing string characters by index.
                  var charIndexBuggy = has('bug-string-char-index');
                  // Define additional utility methods if the `Date` methods are buggy.
                  if (!isExtended) {
                    var floor = Math.floor;
                    // A mapping between the months of the year and the number of days between
                    // January 1st and the first of the respective month.
                    var Months = [
                      0,
                      31,
                      59,
                      90,
                      120,
                      151,
                      181,
                      212,
                      243,
                      273,
                      304,
                      334
                    ];
                    // Internal: Calculates the number of days between the Unix epoch and the
                    // first day of the given month.
                    var getDay = function (year, month) {
                      return Months[month] + 365 * (year - 1970) + floor((year - 1969 + (month = +(month > 1))) / 4) - floor((year - 1901 + month) / 100) + floor((year - 1601 + month) / 400);
                    };
                  }
                  // Internal: Determines if a property is a direct property of the given
                  // object. Delegates to the native `Object#hasOwnProperty` method.
                  if (!(isProperty = objectProto.hasOwnProperty)) {
                    isProperty = function (property) {
                      var members = {}, constructor;
                      if ((members.__proto__ = null, members.__proto__ = {
                          // The *proto* property cannot be set multiple times in recent
                          // versions of Firefox and SeaMonkey.
                          'toString': 1
                        }, members).toString != getClass) {
                        // Safari <= 2.0.3 doesn't implement `Object#hasOwnProperty`, but
                        // supports the mutable *proto* property.
                        isProperty = function (property) {
                          // Capture and break the object's prototype chain (see section 8.6.2
                          // of the ES 5.1 spec). The parenthesized expression prevents an
                          // unsafe transformation by the Closure Compiler.
                          var original = this.__proto__, result = property in (this.__proto__ = null, this);
                          // Restore the original prototype chain.
                          this.__proto__ = original;
                          return result;
                        };
                      } else {
                        // Capture a reference to the top-level `Object` constructor.
                        constructor = members.constructor;
                        // Use the `constructor` property to simulate `Object#hasOwnProperty` in
                        // other environments.
                        isProperty = function (property) {
                          var parent = (this.constructor || constructor).prototype;
                          return property in this && !(property in parent && this[property] === parent[property]);
                        };
                      }
                      members = null;
                      return isProperty.call(this, property);
                    };
                  }
                  // Internal: Normalizes the `for...in` iteration algorithm across
                  // environments. Each enumerated key is yielded to a `callback` function.
                  forEach = function (object, callback) {
                    var size = 0, Properties, members, property;
                    // Tests for bugs in the current environment's `for...in` algorithm. The
                    // `valueOf` property inherits the non-enumerable flag from
                    // `Object.prototype` in older versions of IE, Netscape, and Mozilla.
                    (Properties = function () {
                      this.valueOf = 0;
                    }).prototype.valueOf = 0;
                    // Iterate over a new instance of the `Properties` class.
                    members = new Properties();
                    for (property in members) {
                      // Ignore all properties inherited from `Object.prototype`.
                      if (isProperty.call(members, property)) {
                        size++;
                      }
                    }
                    Properties = members = null;
                    // Normalize the iteration algorithm.
                    if (!size) {
                      // A list of non-enumerable properties inherited from `Object.prototype`.
                      members = [
                        'valueOf',
                        'toString',
                        'toLocaleString',
                        'propertyIsEnumerable',
                        'isPrototypeOf',
                        'hasOwnProperty',
                        'constructor'
                      ];
                      // IE <= 8, Mozilla 1.0, and Netscape 6.2 ignore shadowed non-enumerable
                      // properties.
                      forEach = function (object, callback) {
                        var isFunction = getClass.call(object) == functionClass, property, length;
                        var hasProperty = !isFunction && typeof object.constructor != 'function' && objectTypes[typeof object.hasOwnProperty] && object.hasOwnProperty || isProperty;
                        for (property in object) {
                          // Gecko <= 1.0 enumerates the `prototype` property of functions under
                          // certain conditions; IE does not.
                          if (!(isFunction && property == 'prototype') && hasProperty.call(object, property)) {
                            callback(property);
                          }
                        }
                        // Manually invoke the callback for each non-enumerable property.
                        for (length = members.length; property = members[--length]; hasProperty.call(object, property) && callback(property));
                      };
                    } else if (size == 2) {
                      // Safari <= 2.0.4 enumerates shadowed properties twice.
                      forEach = function (object, callback) {
                        // Create a set of iterated properties.
                        var members = {}, isFunction = getClass.call(object) == functionClass, property;
                        for (property in object) {
                          // Store each property name to prevent double enumeration. The
                          // `prototype` property of functions is not enumerated due to cross-
                          // environment inconsistencies.
                          if (!(isFunction && property == 'prototype') && !isProperty.call(members, property) && (members[property] = 1) && isProperty.call(object, property)) {
                            callback(property);
                          }
                        }
                      };
                    } else {
                      // No bugs detected; use the standard `for...in` algorithm.
                      forEach = function (object, callback) {
                        var isFunction = getClass.call(object) == functionClass, property, isConstructor;
                        for (property in object) {
                          if (!(isFunction && property == 'prototype') && isProperty.call(object, property) && !(isConstructor = property === 'constructor')) {
                            callback(property);
                          }
                        }
                        // Manually invoke the callback for the `constructor` property due to
                        // cross-environment inconsistencies.
                        if (isConstructor || isProperty.call(object, property = 'constructor')) {
                          callback(property);
                        }
                      };
                    }
                    return forEach(object, callback);
                  };
                  // Public: Serializes a JavaScript `value` as a JSON string. The optional
                  // `filter` argument may specify either a function that alters how object and
                  // array members are serialized, or an array of strings and numbers that
                  // indicates which properties should be serialized. The optional `width`
                  // argument may be either a string or number that specifies the indentation
                  // level of the output.
                  if (!has('json-stringify')) {
                    // Internal: A map of control characters and their escaped equivalents.
                    var Escapes = {
                      92: '\\\\',
                      34: '\\"',
                      8: '\\b',
                      12: '\\f',
                      10: '\\n',
                      13: '\\r',
                      9: '\\t'
                    };
                    // Internal: Converts `value` into a zero-padded string such that its
                    // length is at least equal to `width`. The `width` must be <= 6.
                    var leadingZeroes = '000000';
                    var toPaddedString = function (width, value) {
                      // The `|| 0` expression is necessary to work around a bug in
                      // Opera <= 7.54u2 where `0 == -0`, but `String(-0) !== "0"`.
                      return (leadingZeroes + (value || 0)).slice(-width);
                    };
                    // Internal: Double-quotes a string `value`, replacing all ASCII control
                    // characters (characters with code unit values between 0 and 31) with
                    // their escaped equivalents. This is an implementation of the
                    // `Quote(value)` operation defined in ES 5.1 section 15.12.3.
                    var unicodePrefix = '\\u00';
                    var quote = function (value) {
                      var result = '"', index = 0, length = value.length, useCharIndex = !charIndexBuggy || length > 10;
                      var symbols = useCharIndex && (charIndexBuggy ? value.split('') : value);
                      for (; index < length; index++) {
                        var charCode = value.charCodeAt(index);
                        // If the character is a control character, append its Unicode or
                        // shorthand escape sequence; otherwise, append the character as-is.
                        switch (charCode) {
                        case 8:
                        case 9:
                        case 10:
                        case 12:
                        case 13:
                        case 34:
                        case 92:
                          result += Escapes[charCode];
                          break;
                        default:
                          if (charCode < 32) {
                            result += unicodePrefix + toPaddedString(2, charCode.toString(16));
                            break;
                          }
                          result += useCharIndex ? symbols[index] : value.charAt(index);
                        }
                      }
                      return result + '"';
                    };
                    // Internal: Recursively serializes an object. Implements the
                    // `Str(key, holder)`, `JO(value)`, and `JA(value)` operations.
                    var serialize = function (property, object, callback, properties, whitespace, indentation, stack) {
                      var value, className, year, month, date, time, hours, minutes, seconds, milliseconds, results, element, index, length, prefix, result;
                      try {
                        // Necessary for host object support.
                        value = object[property];
                      } catch (exception) {
                      }
                      if (typeof value == 'object' && value) {
                        className = getClass.call(value);
                        if (className == dateClass && !isProperty.call(value, 'toJSON')) {
                          if (value > -1 / 0 && value < 1 / 0) {
                            // Dates are serialized according to the `Date#toJSON` method
                            // specified in ES 5.1 section 15.9.5.44. See section 15.9.1.15
                            // for the ISO 8601 date time string format.
                            if (getDay) {
                              // Manually compute the year, month, date, hours, minutes,
                              // seconds, and milliseconds if the `getUTC*` methods are
                              // buggy. Adapted from @Yaffle's `date-shim` project.
                              date = floor(value / 86400000);
                              for (year = floor(date / 365.2425) + 1970 - 1; getDay(year + 1, 0) <= date; year++);
                              for (month = floor((date - getDay(year, 0)) / 30.42); getDay(year, month + 1) <= date; month++);
                              date = 1 + date - getDay(year, month);
                              // The `time` value specifies the time within the day (see ES
                              // 5.1 section 15.9.1.2). The formula `(A % B + B) % B` is used
                              // to compute `A modulo B`, as the `%` operator does not
                              // correspond to the `modulo` operation for negative numbers.
                              time = (value % 86400000 + 86400000) % 86400000;
                              // The hours, minutes, seconds, and milliseconds are obtained by
                              // decomposing the time within the day. See section 15.9.1.10.
                              hours = floor(time / 3600000) % 24;
                              minutes = floor(time / 60000) % 60;
                              seconds = floor(time / 1000) % 60;
                              milliseconds = time % 1000;
                            } else {
                              year = value.getUTCFullYear();
                              month = value.getUTCMonth();
                              date = value.getUTCDate();
                              hours = value.getUTCHours();
                              minutes = value.getUTCMinutes();
                              seconds = value.getUTCSeconds();
                              milliseconds = value.getUTCMilliseconds();
                            }
                            // Serialize extended years correctly.
                            value = (year <= 0 || year >= 10000 ? (year < 0 ? '-' : '+') + toPaddedString(6, year < 0 ? -year : year) : toPaddedString(4, year)) + '-' + toPaddedString(2, month + 1) + '-' + toPaddedString(2, date) + // Months, dates, hours, minutes, and seconds should have two
                            // digits; milliseconds should have three.
                            'T' + toPaddedString(2, hours) + ':' + toPaddedString(2, minutes) + ':' + toPaddedString(2, seconds) + // Milliseconds are optional in ES 5.0, but required in 5.1.
                            '.' + toPaddedString(3, milliseconds) + 'Z';
                          } else {
                            value = null;
                          }
                        } else if (typeof value.toJSON == 'function' && (className != numberClass && className != stringClass && className != arrayClass || isProperty.call(value, 'toJSON'))) {
                          // Prototype <= 1.6.1 adds non-standard `toJSON` methods to the
                          // `Number`, `String`, `Date`, and `Array` prototypes. JSON 3
                          // ignores all `toJSON` methods on these objects unless they are
                          // defined directly on an instance.
                          value = value.toJSON(property);
                        }
                      }
                      if (callback) {
                        // If a replacement function was provided, call it to obtain the value
                        // for serialization.
                        value = callback.call(object, property, value);
                      }
                      if (value === null) {
                        return 'null';
                      }
                      className = getClass.call(value);
                      if (className == booleanClass) {
                        // Booleans are represented literally.
                        return '' + value;
                      } else if (className == numberClass) {
                        // JSON numbers must be finite. `Infinity` and `NaN` are serialized as
                        // `"null"`.
                        return value > -1 / 0 && value < 1 / 0 ? '' + value : 'null';
                      } else if (className == stringClass) {
                        // Strings are double-quoted and escaped.
                        return quote('' + value);
                      }
                      // Recursively serialize objects and arrays.
                      if (typeof value == 'object') {
                        // Check for cyclic structures. This is a linear search; performance
                        // is inversely proportional to the number of unique nested objects.
                        for (length = stack.length; length--;) {
                          if (stack[length] === value) {
                            // Cyclic structures cannot be serialized by `JSON.stringify`.
                            throw TypeError();
                          }
                        }
                        // Add the object to the stack of traversed objects.
                        stack.push(value);
                        results = [];
                        // Save the current indentation level and indent one additional level.
                        prefix = indentation;
                        indentation += whitespace;
                        if (className == arrayClass) {
                          // Recursively serialize array elements.
                          for (index = 0, length = value.length; index < length; index++) {
                            element = serialize(index, value, callback, properties, whitespace, indentation, stack);
                            results.push(element === undef ? 'null' : element);
                          }
                          result = results.length ? whitespace ? '[\n' + indentation + results.join(',\n' + indentation) + '\n' + prefix + ']' : '[' + results.join(',') + ']' : '[]';
                        } else {
                          // Recursively serialize object members. Members are selected from
                          // either a user-specified list of property names, or the object
                          // itself.
                          forEach(properties || value, function (property) {
                            var element = serialize(property, value, callback, properties, whitespace, indentation, stack);
                            if (element !== undef) {
                              // According to ES 5.1 section 15.12.3: "If `gap` {whitespace}
                              // is not the empty string, let `member` {quote(property) + ":"}
                              // be the concatenation of `member` and the `space` character."
                              // The "`space` character" refers to the literal space
                              // character, not the `space` {width} argument provided to
                              // `JSON.stringify`.
                              results.push(quote(property) + ':' + (whitespace ? ' ' : '') + element);
                            }
                          });
                          result = results.length ? whitespace ? '{\n' + indentation + results.join(',\n' + indentation) + '\n' + prefix + '}' : '{' + results.join(',') + '}' : '{}';
                        }
                        // Remove the object from the traversed object stack.
                        stack.pop();
                        return result;
                      }
                    };
                    // Public: `JSON.stringify`. See ES 5.1 section 15.12.3.
                    exports.stringify = function (source, filter, width) {
                      var whitespace, callback, properties, className;
                      if (objectTypes[typeof filter] && filter) {
                        if ((className = getClass.call(filter)) == functionClass) {
                          callback = filter;
                        } else if (className == arrayClass) {
                          // Convert the property names array into a makeshift set.
                          properties = {};
                          for (var index = 0, length = filter.length, value; index < length; value = filter[index++], (className = getClass.call(value), className == stringClass || className == numberClass) && (properties[value] = 1));
                        }
                      }
                      if (width) {
                        if ((className = getClass.call(width)) == numberClass) {
                          // Convert the `width` to an integer and create a string containing
                          // `width` number of space characters.
                          if ((width -= width % 1) > 0) {
                            for (whitespace = '', width > 10 && (width = 10); whitespace.length < width; whitespace += ' ');
                          }
                        } else if (className == stringClass) {
                          whitespace = width.length <= 10 ? width : width.slice(0, 10);
                        }
                      }
                      // Opera <= 7.54u2 discards the values associated with empty string keys
                      // (`""`) only if they are used directly within an object member list
                      // (e.g., `!("" in { "": 1})`).
                      return serialize('', (value = {}, value[''] = source, value), callback, properties, whitespace, '', []);
                    };
                  }
                  // Public: Parses a JSON source string.
                  if (!has('json-parse')) {
                    var fromCharCode = String.fromCharCode;
                    // Internal: A map of escaped control characters and their unescaped
                    // equivalents.
                    var Unescapes = {
                      92: '\\',
                      34: '"',
                      47: '/',
                      98: '\b',
                      116: '\t',
                      110: '\n',
                      102: '\f',
                      114: '\r'
                    };
                    // Internal: Stores the parser state.
                    var Index, Source;
                    // Internal: Resets the parser state and throws a `SyntaxError`.
                    var abort = function () {
                      Index = Source = null;
                      throw SyntaxError();
                    };
                    // Internal: Returns the next token, or `"$"` if the parser has reached
                    // the end of the source string. A token may be a string, number, `null`
                    // literal, or Boolean literal.
                    var lex = function () {
                      var source = Source, length = source.length, value, begin, position, isSigned, charCode;
                      while (Index < length) {
                        charCode = source.charCodeAt(Index);
                        switch (charCode) {
                        case 9:
                        case 10:
                        case 13:
                        case 32:
                          // Skip whitespace tokens, including tabs, carriage returns, line
                          // feeds, and space characters.
                          Index++;
                          break;
                        case 123:
                        case 125:
                        case 91:
                        case 93:
                        case 58:
                        case 44:
                          // Parse a punctuator token (`{`, `}`, `[`, `]`, `:`, or `,`) at
                          // the current position.
                          value = charIndexBuggy ? source.charAt(Index) : source[Index];
                          Index++;
                          return value;
                        case 34:
                          // `"` delimits a JSON string; advance to the next character and
                          // begin parsing the string. String tokens are prefixed with the
                          // sentinel `@` character to distinguish them from punctuators and
                          // end-of-string tokens.
                          for (value = '@', Index++; Index < length;) {
                            charCode = source.charCodeAt(Index);
                            if (charCode < 32) {
                              // Unescaped ASCII control characters (those with a code unit
                              // less than the space character) are not permitted.
                              abort();
                            } else if (charCode == 92) {
                              // A reverse solidus (`\`) marks the beginning of an escaped
                              // control character (including `"`, `\`, and `/`) or Unicode
                              // escape sequence.
                              charCode = source.charCodeAt(++Index);
                              switch (charCode) {
                              case 92:
                              case 34:
                              case 47:
                              case 98:
                              case 116:
                              case 110:
                              case 102:
                              case 114:
                                // Revive escaped control characters.
                                value += Unescapes[charCode];
                                Index++;
                                break;
                              case 117:
                                // `\u` marks the beginning of a Unicode escape sequence.
                                // Advance to the first character and validate the
                                // four-digit code point.
                                begin = ++Index;
                                for (position = Index + 4; Index < position; Index++) {
                                  charCode = source.charCodeAt(Index);
                                  // A valid sequence comprises four hexdigits (case-
                                  // insensitive) that form a single hexadecimal value.
                                  if (!(charCode >= 48 && charCode <= 57 || charCode >= 97 && charCode <= 102 || charCode >= 65 && charCode <= 70)) {
                                    // Invalid Unicode escape sequence.
                                    abort();
                                  }
                                }
                                // Revive the escaped character.
                                value += fromCharCode('0x' + source.slice(begin, Index));
                                break;
                              default:
                                // Invalid escape sequence.
                                abort();
                              }
                            } else {
                              if (charCode == 34) {
                                // An unescaped double-quote character marks the end of the
                                // string.
                                break;
                              }
                              charCode = source.charCodeAt(Index);
                              begin = Index;
                              // Optimize for the common case where a string is valid.
                              while (charCode >= 32 && charCode != 92 && charCode != 34) {
                                charCode = source.charCodeAt(++Index);
                              }
                              // Append the string as-is.
                              value += source.slice(begin, Index);
                            }
                          }
                          if (source.charCodeAt(Index) == 34) {
                            // Advance to the next character and return the revived string.
                            Index++;
                            return value;
                          }
                          // Unterminated string.
                          abort();
                        default:
                          // Parse numbers and literals.
                          begin = Index;
                          // Advance past the negative sign, if one is specified.
                          if (charCode == 45) {
                            isSigned = true;
                            charCode = source.charCodeAt(++Index);
                          }
                          // Parse an integer or floating-point value.
                          if (charCode >= 48 && charCode <= 57) {
                            // Leading zeroes are interpreted as octal literals.
                            if (charCode == 48 && (charCode = source.charCodeAt(Index + 1), charCode >= 48 && charCode <= 57)) {
                              // Illegal octal literal.
                              abort();
                            }
                            isSigned = false;
                            // Parse the integer component.
                            for (; Index < length && (charCode = source.charCodeAt(Index), charCode >= 48 && charCode <= 57); Index++);
                            // Floats cannot contain a leading decimal point; however, this
                            // case is already accounted for by the parser.
                            if (source.charCodeAt(Index) == 46) {
                              position = ++Index;
                              // Parse the decimal component.
                              for (; position < length && (charCode = source.charCodeAt(position), charCode >= 48 && charCode <= 57); position++);
                              if (position == Index) {
                                // Illegal trailing decimal.
                                abort();
                              }
                              Index = position;
                            }
                            // Parse exponents. The `e` denoting the exponent is
                            // case-insensitive.
                            charCode = source.charCodeAt(Index);
                            if (charCode == 101 || charCode == 69) {
                              charCode = source.charCodeAt(++Index);
                              // Skip past the sign following the exponent, if one is
                              // specified.
                              if (charCode == 43 || charCode == 45) {
                                Index++;
                              }
                              // Parse the exponential component.
                              for (position = Index; position < length && (charCode = source.charCodeAt(position), charCode >= 48 && charCode <= 57); position++);
                              if (position == Index) {
                                // Illegal empty exponent.
                                abort();
                              }
                              Index = position;
                            }
                            // Coerce the parsed value to a JavaScript number.
                            return +source.slice(begin, Index);
                          }
                          // A negative sign may only precede numbers.
                          if (isSigned) {
                            abort();
                          }
                          // `true`, `false`, and `null` literals.
                          if (source.slice(Index, Index + 4) == 'true') {
                            Index += 4;
                            return true;
                          } else if (source.slice(Index, Index + 5) == 'false') {
                            Index += 5;
                            return false;
                          } else if (source.slice(Index, Index + 4) == 'null') {
                            Index += 4;
                            return null;
                          }
                          // Unrecognized token.
                          abort();
                        }
                      }
                      // Return the sentinel `$` character if the parser has reached the end
                      // of the source string.
                      return '$';
                    };
                    // Internal: Parses a JSON `value` token.
                    var get = function (value) {
                      var results, hasMembers;
                      if (value == '$') {
                        // Unexpected end of input.
                        abort();
                      }
                      if (typeof value == 'string') {
                        if ((charIndexBuggy ? value.charAt(0) : value[0]) == '@') {
                          // Remove the sentinel `@` character.
                          return value.slice(1);
                        }
                        // Parse object and array literals.
                        if (value == '[') {
                          // Parses a JSON array, returning a new JavaScript array.
                          results = [];
                          for (;; hasMembers || (hasMembers = true)) {
                            value = lex();
                            // A closing square bracket marks the end of the array literal.
                            if (value == ']') {
                              break;
                            }
                            // If the array literal contains elements, the current token
                            // should be a comma separating the previous element from the
                            // next.
                            if (hasMembers) {
                              if (value == ',') {
                                value = lex();
                                if (value == ']') {
                                  // Unexpected trailing `,` in array literal.
                                  abort();
                                }
                              } else {
                                // A `,` must separate each array element.
                                abort();
                              }
                            }
                            // Elisions and leading commas are not permitted.
                            if (value == ',') {
                              abort();
                            }
                            results.push(get(value));
                          }
                          return results;
                        } else if (value == '{') {
                          // Parses a JSON object, returning a new JavaScript object.
                          results = {};
                          for (;; hasMembers || (hasMembers = true)) {
                            value = lex();
                            // A closing curly brace marks the end of the object literal.
                            if (value == '}') {
                              break;
                            }
                            // If the object literal contains members, the current token
                            // should be a comma separator.
                            if (hasMembers) {
                              if (value == ',') {
                                value = lex();
                                if (value == '}') {
                                  // Unexpected trailing `,` in object literal.
                                  abort();
                                }
                              } else {
                                // A `,` must separate each object member.
                                abort();
                              }
                            }
                            // Leading commas are not permitted, object property names must be
                            // double-quoted strings, and a `:` must separate each property
                            // name and value.
                            if (value == ',' || typeof value != 'string' || (charIndexBuggy ? value.charAt(0) : value[0]) != '@' || lex() != ':') {
                              abort();
                            }
                            results[value.slice(1)] = get(lex());
                          }
                          return results;
                        }
                        // Unexpected token encountered.
                        abort();
                      }
                      return value;
                    };
                    // Internal: Updates a traversed object member.
                    var update = function (source, property, callback) {
                      var element = walk(source, property, callback);
                      if (element === undef) {
                        delete source[property];
                      } else {
                        source[property] = element;
                      }
                    };
                    // Internal: Recursively traverses a parsed JSON object, invoking the
                    // `callback` function for each value. This is an implementation of the
                    // `Walk(holder, name)` operation defined in ES 5.1 section 15.12.2.
                    var walk = function (source, property, callback) {
                      var value = source[property], length;
                      if (typeof value == 'object' && value) {
                        // `forEach` can't be used to traverse an array in Opera <= 8.54
                        // because its `Object#hasOwnProperty` implementation returns `false`
                        // for array indices (e.g., `![1, 2, 3].hasOwnProperty("0")`).
                        if (getClass.call(value) == arrayClass) {
                          for (length = value.length; length--;) {
                            update(value, length, callback);
                          }
                        } else {
                          forEach(value, function (property) {
                            update(value, property, callback);
                          });
                        }
                      }
                      return callback.call(source, property, value);
                    };
                    // Public: `JSON.parse`. See ES 5.1 section 15.12.2.
                    exports.parse = function (source, callback) {
                      var result, value;
                      Index = 0;
                      Source = '' + source;
                      result = get(lex());
                      // If a JSON string contains multiple tokens, it is invalid.
                      if (lex() != '$') {
                        abort();
                      }
                      // Reset the parser state.
                      Index = Source = null;
                      return callback && getClass.call(callback) == functionClass ? walk((value = {}, value[''] = result, value), '', callback) : result;
                    };
                  }
                }
                exports['runInContext'] = runInContext;
                return exports;
              }
              if (freeExports && !isLoader) {
                // Export for CommonJS environments.
                runInContext(root, freeExports);
              } else {
                // Export for web browsers and JavaScript engines.
                var nativeJSON = root.JSON, previousJSON = root['JSON3'], isRestored = false;
                var JSON3 = runInContext(root, root['JSON3'] = {
                  // Public: Restores the original value of the global `JSON` object and
                  // returns a reference to the `JSON3` object.
                  'noConflict': function () {
                    if (!isRestored) {
                      isRestored = true;
                      root.JSON = nativeJSON;
                      root['JSON3'] = previousJSON;
                      nativeJSON = previousJSON = null;
                    }
                    return JSON3;
                  }
                });
                root.JSON = {
                  'parse': JSON3.parse,
                  'stringify': JSON3.stringify
                };
              }
              // Export for asynchronous module loaders.
              if (isLoader) {
                define(function () {
                  return JSON3;
                });
              }
            }.call(this));  /* WEBPACK VAR INJECTION */
          }.call(exports, __webpack_require__(12)(module), function () {
            return this;
          }()));  /***/
        },
        /* 12 */
        /***/
        function (module, exports) {
          module.exports = function (module) {
            if (!module.webpackPolyfill) {
              module.deprecate = function () {
              };
              module.paths = [];
              // module.parent = undefined by default
              module.children = [];
              module.webpackPolyfill = 1;
            }
            return module;
          }  /***/;
        },
        /* 13 */
        /***/
        function (module, exports) {
          /**
           * Expose `Emitter`.
           */
          module.exports = Emitter;
          /**
           * Initialize a new `Emitter`.
           *
           * @api public
           */
          function Emitter(obj) {
            if (obj)
              return mixin(obj);
          }
          ;
          /**
           * Mixin the emitter properties.
           *
           * @param {Object} obj
           * @return {Object}
           * @api private
           */
          function mixin(obj) {
            for (var key in Emitter.prototype) {
              obj[key] = Emitter.prototype[key];
            }
            return obj;
          }
          /**
           * Listen on the given `event` with `fn`.
           *
           * @param {String} event
           * @param {Function} fn
           * @return {Emitter}
           * @api public
           */
          Emitter.prototype.on = Emitter.prototype.addEventListener = function (event, fn) {
            this._callbacks = this._callbacks || {};
            (this._callbacks[event] = this._callbacks[event] || []).push(fn);
            return this;
          };
          /**
           * Adds an `event` listener that will be invoked a single
           * time then automatically removed.
           *
           * @param {String} event
           * @param {Function} fn
           * @return {Emitter}
           * @api public
           */
          Emitter.prototype.once = function (event, fn) {
            var self = this;
            this._callbacks = this._callbacks || {};
            function on() {
              self.off(event, on);
              fn.apply(this, arguments);
            }
            on.fn = fn;
            this.on(event, on);
            return this;
          };
          /**
           * Remove the given callback for `event` or all
           * registered callbacks.
           *
           * @param {String} event
           * @param {Function} fn
           * @return {Emitter}
           * @api public
           */
          Emitter.prototype.off = Emitter.prototype.removeListener = Emitter.prototype.removeAllListeners = Emitter.prototype.removeEventListener = function (event, fn) {
            this._callbacks = this._callbacks || {};
            // all
            if (0 == arguments.length) {
              this._callbacks = {};
              return this;
            }
            // specific event
            var callbacks = this._callbacks[event];
            if (!callbacks)
              return this;
            // remove all handlers
            if (1 == arguments.length) {
              delete this._callbacks[event];
              return this;
            }
            // remove specific handler
            var cb;
            for (var i = 0; i < callbacks.length; i++) {
              cb = callbacks[i];
              if (cb === fn || cb.fn === fn) {
                callbacks.splice(i, 1);
                break;
              }
            }
            return this;
          };
          /**
           * Emit `event` with the given args.
           *
           * @param {String} event
           * @param {Mixed} ...
           * @return {Emitter}
           */
          Emitter.prototype.emit = function (event) {
            this._callbacks = this._callbacks || {};
            var args = [].slice.call(arguments, 1), callbacks = this._callbacks[event];
            if (callbacks) {
              callbacks = callbacks.slice(0);
              for (var i = 0, len = callbacks.length; i < len; ++i) {
                callbacks[i].apply(this, args);
              }
            }
            return this;
          };
          /**
           * Return array of callbacks for `event`.
           *
           * @param {String} event
           * @return {Array}
           * @api public
           */
          Emitter.prototype.listeners = function (event) {
            this._callbacks = this._callbacks || {};
            return this._callbacks[event] || [];
          };
          /**
           * Check if this emitter has `event` handlers.
           *
           * @param {String} event
           * @return {Boolean}
           * @api public
           */
          Emitter.prototype.hasListeners = function (event) {
            return !!this.listeners(event).length;
          };  /***/
        },
        /* 14 */
        /***/
        function (module, exports, __webpack_require__) {
          /* WEBPACK VAR INJECTION */
          (function (global) {
            /*global Blob,File*/
            /**
             * Module requirements
             */
            var isArray = __webpack_require__(15);
            var isBuf = __webpack_require__(16);
            /**
             * Replaces every Buffer | ArrayBuffer in packet with a numbered placeholder.
             * Anything with blobs or files should be fed through removeBlobs before coming
             * here.
             *
             * @param {Object} packet - socket.io event packet
             * @return {Object} with deconstructed packet and list of buffers
             * @api public
             */
            exports.deconstructPacket = function (packet) {
              var buffers = [];
              var packetData = packet.data;
              function _deconstructPacket(data) {
                if (!data)
                  return data;
                if (isBuf(data)) {
                  var placeholder = {
                    _placeholder: true,
                    num: buffers.length
                  };
                  buffers.push(data);
                  return placeholder;
                } else if (isArray(data)) {
                  var newData = new Array(data.length);
                  for (var i = 0; i < data.length; i++) {
                    newData[i] = _deconstructPacket(data[i]);
                  }
                  return newData;
                } else if ('object' == typeof data && !(data instanceof Date)) {
                  var newData = {};
                  for (var key in data) {
                    newData[key] = _deconstructPacket(data[key]);
                  }
                  return newData;
                }
                return data;
              }
              var pack = packet;
              pack.data = _deconstructPacket(packetData);
              pack.attachments = buffers.length;
              // number of binary 'attachments'
              return {
                packet: pack,
                buffers: buffers
              };
            };
            /**
             * Reconstructs a binary packet from its placeholder packet and buffers
             *
             * @param {Object} packet - event packet with placeholders
             * @param {Array} buffers - binary buffers to put in placeholder positions
             * @return {Object} reconstructed packet
             * @api public
             */
            exports.reconstructPacket = function (packet, buffers) {
              var curPlaceHolder = 0;
              function _reconstructPacket(data) {
                if (data && data._placeholder) {
                  var buf = buffers[data.num];
                  // appropriate buffer (should be natural order anyway)
                  return buf;
                } else if (isArray(data)) {
                  for (var i = 0; i < data.length; i++) {
                    data[i] = _reconstructPacket(data[i]);
                  }
                  return data;
                } else if (data && 'object' == typeof data) {
                  for (var key in data) {
                    data[key] = _reconstructPacket(data[key]);
                  }
                  return data;
                }
                return data;
              }
              packet.data = _reconstructPacket(packet.data);
              packet.attachments = undefined;
              // no longer useful
              return packet;
            };
            /**
             * Asynchronously removes Blobs or Files from data via
             * FileReader's readAsArrayBuffer method. Used before encoding
             * data as msgpack. Calls callback with the blobless data.
             *
             * @param {Object} data
             * @param {Function} callback
             * @api private
             */
            exports.removeBlobs = function (data, callback) {
              function _removeBlobs(obj, curKey, containingObject) {
                if (!obj)
                  return obj;
                // convert any blob
                if (global.Blob && obj instanceof Blob || global.File && obj instanceof File) {
                  pendingBlobs++;
                  // async filereader
                  var fileReader = new FileReader();
                  fileReader.onload = function () {
                    // this.result == arraybuffer
                    if (containingObject) {
                      containingObject[curKey] = this.result;
                    } else {
                      bloblessData = this.result;
                    }
                    // if nothing pending its callback time
                    if (!--pendingBlobs) {
                      callback(bloblessData);
                    }
                  };
                  fileReader.readAsArrayBuffer(obj);  // blob -> arraybuffer
                } else if (isArray(obj)) {
                  // handle array
                  for (var i = 0; i < obj.length; i++) {
                    _removeBlobs(obj[i], i, obj);
                  }
                } else if (obj && 'object' == typeof obj && !isBuf(obj)) {
                  // and object
                  for (var key in obj) {
                    _removeBlobs(obj[key], key, obj);
                  }
                }
              }
              var pendingBlobs = 0;
              var bloblessData = data;
              _removeBlobs(bloblessData);
              if (!pendingBlobs) {
                callback(bloblessData);
              }
            };  /* WEBPACK VAR INJECTION */
          }.call(exports, function () {
            return this;
          }()));  /***/
        },
        /* 15 */
        /***/
        function (module, exports) {
          module.exports = Array.isArray || function (arr) {
            return Object.prototype.toString.call(arr) == '[object Array]';
          };  /***/
        },
        /* 16 */
        /***/
        function (module, exports) {
          /* WEBPACK VAR INJECTION */
          (function (global) {
            module.exports = isBuf;
            /**
             * Returns true if obj is a buffer or an arraybuffer.
             *
             * @api private
             */
            function isBuf(obj) {
              return global.Buffer && global.Buffer.isBuffer(obj) || global.ArrayBuffer && obj instanceof ArrayBuffer;
            }  /* WEBPACK VAR INJECTION */
          }.call(exports, function () {
            return this;
          }()));  /***/
        },
        /* 17 */
        /***/
        function (module, exports, __webpack_require__) {
          'use strict';
          var _typeof = typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol' ? function (obj) {
            return typeof obj;
          } : function (obj) {
            return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : typeof obj;
          };
          /**
           * Module dependencies.
           */
          var eio = __webpack_require__(18);
          var Socket = __webpack_require__(44);
          var Emitter = __webpack_require__(35);
          var parser = __webpack_require__(7);
          var on = __webpack_require__(46);
          var bind = __webpack_require__(47);
          var debug = __webpack_require__(3)('socket.io-client:manager');
          var indexOf = __webpack_require__(42);
          var Backoff = __webpack_require__(48);
          /**
           * IE6+ hasOwnProperty
           */
          var has = Object.prototype.hasOwnProperty;
          /**
           * Module exports
           */
          module.exports = Manager;
          /**
           * `Manager` constructor.
           *
           * @param {String} engine instance or engine uri/opts
           * @param {Object} options
           * @api public
           */
          function Manager(uri, opts) {
            if (!(this instanceof Manager))
              return new Manager(uri, opts);
            if (uri && 'object' === (typeof uri === 'undefined' ? 'undefined' : _typeof(uri))) {
              opts = uri;
              uri = undefined;
            }
            opts = opts || {};
            opts.path = opts.path || '/socket.io';
            this.nsps = {};
            this.subs = [];
            this.opts = opts;
            this.reconnection(opts.reconnection !== false);
            this.reconnectionAttempts(opts.reconnectionAttempts || Infinity);
            this.reconnectionDelay(opts.reconnectionDelay || 1000);
            this.reconnectionDelayMax(opts.reconnectionDelayMax || 5000);
            this.randomizationFactor(opts.randomizationFactor || 0.5);
            this.backoff = new Backoff({
              min: this.reconnectionDelay(),
              max: this.reconnectionDelayMax(),
              jitter: this.randomizationFactor()
            });
            this.timeout(null == opts.timeout ? 20000 : opts.timeout);
            this.readyState = 'closed';
            this.uri = uri;
            this.connecting = [];
            this.lastPing = null;
            this.encoding = false;
            this.packetBuffer = [];
            this.encoder = new parser.Encoder();
            this.decoder = new parser.Decoder();
            this.autoConnect = opts.autoConnect !== false;
            if (this.autoConnect)
              this.open();
          }
          /**
           * Propagate given event to sockets and emit on `this`
           *
           * @api private
           */
          Manager.prototype.emitAll = function () {
            this.emit.apply(this, arguments);
            for (var nsp in this.nsps) {
              if (has.call(this.nsps, nsp)) {
                this.nsps[nsp].emit.apply(this.nsps[nsp], arguments);
              }
            }
          };
          /**
           * Update `socket.id` of all sockets
           *
           * @api private
           */
          Manager.prototype.updateSocketIds = function () {
            for (var nsp in this.nsps) {
              if (has.call(this.nsps, nsp)) {
                this.nsps[nsp].id = this.engine.id;
              }
            }
          };
          /**
           * Mix in `Emitter`.
           */
          Emitter(Manager.prototype);
          /**
           * Sets the `reconnection` config.
           *
           * @param {Boolean} true/false if it should automatically reconnect
           * @return {Manager} self or value
           * @api public
           */
          Manager.prototype.reconnection = function (v) {
            if (!arguments.length)
              return this._reconnection;
            this._reconnection = !!v;
            return this;
          };
          /**
           * Sets the reconnection attempts config.
           *
           * @param {Number} max reconnection attempts before giving up
           * @return {Manager} self or value
           * @api public
           */
          Manager.prototype.reconnectionAttempts = function (v) {
            if (!arguments.length)
              return this._reconnectionAttempts;
            this._reconnectionAttempts = v;
            return this;
          };
          /**
           * Sets the delay between reconnections.
           *
           * @param {Number} delay
           * @return {Manager} self or value
           * @api public
           */
          Manager.prototype.reconnectionDelay = function (v) {
            if (!arguments.length)
              return this._reconnectionDelay;
            this._reconnectionDelay = v;
            this.backoff && this.backoff.setMin(v);
            return this;
          };
          Manager.prototype.randomizationFactor = function (v) {
            if (!arguments.length)
              return this._randomizationFactor;
            this._randomizationFactor = v;
            this.backoff && this.backoff.setJitter(v);
            return this;
          };
          /**
           * Sets the maximum delay between reconnections.
           *
           * @param {Number} delay
           * @return {Manager} self or value
           * @api public
           */
          Manager.prototype.reconnectionDelayMax = function (v) {
            if (!arguments.length)
              return this._reconnectionDelayMax;
            this._reconnectionDelayMax = v;
            this.backoff && this.backoff.setMax(v);
            return this;
          };
          /**
           * Sets the connection timeout. `false` to disable
           *
           * @return {Manager} self or value
           * @api public
           */
          Manager.prototype.timeout = function (v) {
            if (!arguments.length)
              return this._timeout;
            this._timeout = v;
            return this;
          };
          /**
           * Starts trying to reconnect if reconnection is enabled and we have not
           * started reconnecting yet
           *
           * @api private
           */
          Manager.prototype.maybeReconnectOnOpen = function () {
            // Only try to reconnect if it's the first time we're connecting
            if (!this.reconnecting && this._reconnection && this.backoff.attempts === 0) {
              // keeps reconnection from firing twice for the same reconnection loop
              this.reconnect();
            }
          };
          /**
           * Sets the current transport `socket`.
           *
           * @param {Function} optional, callback
           * @return {Manager} self
           * @api public
           */
          Manager.prototype.open = Manager.prototype.connect = function (fn, opts) {
            debug('readyState %s', this.readyState);
            if (~this.readyState.indexOf('open'))
              return this;
            debug('opening %s', this.uri);
            this.engine = eio(this.uri, this.opts);
            var socket = this.engine;
            var self = this;
            this.readyState = 'opening';
            this.skipReconnect = false;
            // emit `open`
            var openSub = on(socket, 'open', function () {
              self.onopen();
              fn && fn();
            });
            // emit `connect_error`
            var errorSub = on(socket, 'error', function (data) {
              debug('connect_error');
              self.cleanup();
              self.readyState = 'closed';
              self.emitAll('connect_error', data);
              if (fn) {
                var err = new Error('Connection error');
                err.data = data;
                fn(err);
              } else {
                // Only do this if there is no fn to handle the error
                self.maybeReconnectOnOpen();
              }
            });
            // emit `connect_timeout`
            if (false !== this._timeout) {
              var timeout = this._timeout;
              debug('connect attempt will timeout after %d', timeout);
              // set timer
              var timer = setTimeout(function () {
                debug('connect attempt timed out after %d', timeout);
                openSub.destroy();
                socket.close();
                socket.emit('error', 'timeout');
                self.emitAll('connect_timeout', timeout);
              }, timeout);
              this.subs.push({
                destroy: function destroy() {
                  clearTimeout(timer);
                }
              });
            }
            this.subs.push(openSub);
            this.subs.push(errorSub);
            return this;
          };
          /**
           * Called upon transport open.
           *
           * @api private
           */
          Manager.prototype.onopen = function () {
            debug('open');
            // clear old subs
            this.cleanup();
            // mark as open
            this.readyState = 'open';
            this.emit('open');
            // add new subs
            var socket = this.engine;
            this.subs.push(on(socket, 'data', bind(this, 'ondata')));
            this.subs.push(on(socket, 'ping', bind(this, 'onping')));
            this.subs.push(on(socket, 'pong', bind(this, 'onpong')));
            this.subs.push(on(socket, 'error', bind(this, 'onerror')));
            this.subs.push(on(socket, 'close', bind(this, 'onclose')));
            this.subs.push(on(this.decoder, 'decoded', bind(this, 'ondecoded')));
          };
          /**
           * Called upon a ping.
           *
           * @api private
           */
          Manager.prototype.onping = function () {
            this.lastPing = new Date();
            this.emitAll('ping');
          };
          /**
           * Called upon a packet.
           *
           * @api private
           */
          Manager.prototype.onpong = function () {
            this.emitAll('pong', new Date() - this.lastPing);
          };
          /**
           * Called with data.
           *
           * @api private
           */
          Manager.prototype.ondata = function (data) {
            this.decoder.add(data);
          };
          /**
           * Called when parser fully decodes a packet.
           *
           * @api private
           */
          Manager.prototype.ondecoded = function (packet) {
            this.emit('packet', packet);
          };
          /**
           * Called upon socket error.
           *
           * @api private
           */
          Manager.prototype.onerror = function (err) {
            debug('error', err);
            this.emitAll('error', err);
          };
          /**
           * Creates a new socket for the given `nsp`.
           *
           * @return {Socket}
           * @api public
           */
          Manager.prototype.socket = function (nsp, opts) {
            var socket = this.nsps[nsp];
            if (!socket) {
              socket = new Socket(this, nsp, opts);
              this.nsps[nsp] = socket;
              var self = this;
              socket.on('connecting', onConnecting);
              socket.on('connect', function () {
                socket.id = self.engine.id;
              });
              if (this.autoConnect) {
                // manually call here since connecting evnet is fired before listening
                onConnecting();
              }
            }
            function onConnecting() {
              if (!~indexOf(self.connecting, socket)) {
                self.connecting.push(socket);
              }
            }
            return socket;
          };
          /**
           * Called upon a socket close.
           *
           * @param {Socket} socket
           */
          Manager.prototype.destroy = function (socket) {
            var index = indexOf(this.connecting, socket);
            if (~index)
              this.connecting.splice(index, 1);
            if (this.connecting.length)
              return;
            this.close();
          };
          /**
           * Writes a packet.
           *
           * @param {Object} packet
           * @api private
           */
          Manager.prototype.packet = function (packet) {
            debug('writing packet %j', packet);
            var self = this;
            if (packet.query && packet.type === 0)
              packet.nsp += '?' + packet.query;
            if (!self.encoding) {
              // encode, then write to engine with result
              self.encoding = true;
              this.encoder.encode(packet, function (encodedPackets) {
                for (var i = 0; i < encodedPackets.length; i++) {
                  self.engine.write(encodedPackets[i], packet.options);
                }
                self.encoding = false;
                self.processPacketQueue();
              });
            } else {
              // add packet to the queue
              self.packetBuffer.push(packet);
            }
          };
          /**
           * If packet buffer is non-empty, begins encoding the
           * next packet in line.
           *
           * @api private
           */
          Manager.prototype.processPacketQueue = function () {
            if (this.packetBuffer.length > 0 && !this.encoding) {
              var pack = this.packetBuffer.shift();
              this.packet(pack);
            }
          };
          /**
           * Clean up transport subscriptions and packet buffer.
           *
           * @api private
           */
          Manager.prototype.cleanup = function () {
            debug('cleanup');
            var subsLength = this.subs.length;
            for (var i = 0; i < subsLength; i++) {
              var sub = this.subs.shift();
              sub.destroy();
            }
            this.packetBuffer = [];
            this.encoding = false;
            this.lastPing = null;
            this.decoder.destroy();
          };
          /**
           * Close the current socket.
           *
           * @api private
           */
          Manager.prototype.close = Manager.prototype.disconnect = function () {
            debug('disconnect');
            this.skipReconnect = true;
            this.reconnecting = false;
            if ('opening' === this.readyState) {
              // `onclose` will not fire because
              // an open event never happened
              this.cleanup();
            }
            this.backoff.reset();
            this.readyState = 'closed';
            if (this.engine)
              this.engine.close();
          };
          /**
           * Called upon engine close.
           *
           * @api private
           */
          Manager.prototype.onclose = function (reason) {
            debug('onclose');
            this.cleanup();
            this.backoff.reset();
            this.readyState = 'closed';
            this.emit('close', reason);
            if (this._reconnection && !this.skipReconnect) {
              this.reconnect();
            }
          };
          /**
           * Attempt a reconnection.
           *
           * @api private
           */
          Manager.prototype.reconnect = function () {
            if (this.reconnecting || this.skipReconnect)
              return this;
            var self = this;
            if (this.backoff.attempts >= this._reconnectionAttempts) {
              debug('reconnect failed');
              this.backoff.reset();
              this.emitAll('reconnect_failed');
              this.reconnecting = false;
            } else {
              var delay = this.backoff.duration();
              debug('will wait %dms before reconnect attempt', delay);
              this.reconnecting = true;
              var timer = setTimeout(function () {
                if (self.skipReconnect)
                  return;
                debug('attempting reconnect');
                self.emitAll('reconnect_attempt', self.backoff.attempts);
                self.emitAll('reconnecting', self.backoff.attempts);
                // check again for the case socket closed in above events
                if (self.skipReconnect)
                  return;
                self.open(function (err) {
                  if (err) {
                    debug('reconnect attempt error');
                    self.reconnecting = false;
                    self.reconnect();
                    self.emitAll('reconnect_error', err.data);
                  } else {
                    debug('reconnect success');
                    self.onreconnect();
                  }
                });
              }, delay);
              this.subs.push({
                destroy: function destroy() {
                  clearTimeout(timer);
                }
              });
            }
          };
          /**
           * Called upon successful reconnect.
           *
           * @api private
           */
          Manager.prototype.onreconnect = function () {
            var attempt = this.backoff.attempts;
            this.reconnecting = false;
            this.backoff.reset();
            this.updateSocketIds();
            this.emitAll('reconnect', attempt);
          };  /***/
        },
        /* 18 */
        /***/
        function (module, exports, __webpack_require__) {
          module.exports = __webpack_require__(19);  /***/
        },
        /* 19 */
        /***/
        function (module, exports, __webpack_require__) {
          module.exports = __webpack_require__(20);
          /**
           * Exports parser
           *
           * @api public
           *
           */
          module.exports.parser = __webpack_require__(27);  /***/
        },
        /* 20 */
        /***/
        function (module, exports, __webpack_require__) {
          /* WEBPACK VAR INJECTION */
          (function (global) {
            /**
             * Module dependencies.
             */
            var transports = __webpack_require__(21);
            var Emitter = __webpack_require__(35);
            var debug = __webpack_require__(3)('engine.io-client:socket');
            var index = __webpack_require__(42);
            var parser = __webpack_require__(27);
            var parseuri = __webpack_require__(2);
            var parsejson = __webpack_require__(43);
            var parseqs = __webpack_require__(36);
            /**
             * Module exports.
             */
            module.exports = Socket;
            /**
             * Socket constructor.
             *
             * @param {String|Object} uri or options
             * @param {Object} options
             * @api public
             */
            function Socket(uri, opts) {
              if (!(this instanceof Socket))
                return new Socket(uri, opts);
              opts = opts || {};
              if (uri && 'object' === typeof uri) {
                opts = uri;
                uri = null;
              }
              if (uri) {
                uri = parseuri(uri);
                opts.hostname = uri.host;
                opts.secure = uri.protocol === 'https' || uri.protocol === 'wss';
                opts.port = uri.port;
                if (uri.query)
                  opts.query = uri.query;
              } else if (opts.host) {
                opts.hostname = parseuri(opts.host).host;
              }
              this.secure = null != opts.secure ? opts.secure : global.location && 'https:' === location.protocol;
              if (opts.hostname && !opts.port) {
                // if no port is specified manually, use the protocol default
                opts.port = this.secure ? '443' : '80';
              }
              this.agent = opts.agent || false;
              this.hostname = opts.hostname || (global.location ? location.hostname : 'localhost');
              this.port = opts.port || (global.location && location.port ? location.port : this.secure ? 443 : 80);
              this.query = opts.query || {};
              if ('string' === typeof this.query)
                this.query = parseqs.decode(this.query);
              this.upgrade = false !== opts.upgrade;
              this.path = (opts.path || '/engine.io').replace(/\/$/, '') + '/';
              this.forceJSONP = !!opts.forceJSONP;
              this.jsonp = false !== opts.jsonp;
              this.forceBase64 = !!opts.forceBase64;
              this.enablesXDR = !!opts.enablesXDR;
              this.timestampParam = opts.timestampParam || 't';
              this.timestampRequests = opts.timestampRequests;
              this.transports = opts.transports || [
                'polling',
                'websocket'
              ];
              this.readyState = '';
              this.writeBuffer = [];
              this.prevBufferLen = 0;
              this.policyPort = opts.policyPort || 843;
              this.rememberUpgrade = opts.rememberUpgrade || false;
              this.binaryType = null;
              this.onlyBinaryUpgrades = opts.onlyBinaryUpgrades;
              this.perMessageDeflate = false !== opts.perMessageDeflate ? opts.perMessageDeflate || {} : false;
              if (true === this.perMessageDeflate)
                this.perMessageDeflate = {};
              if (this.perMessageDeflate && null == this.perMessageDeflate.threshold) {
                this.perMessageDeflate.threshold = 1024;
              }
              // SSL options for Node.js client
              this.pfx = opts.pfx || null;
              this.key = opts.key || null;
              this.passphrase = opts.passphrase || null;
              this.cert = opts.cert || null;
              this.ca = opts.ca || null;
              this.ciphers = opts.ciphers || null;
              this.rejectUnauthorized = opts.rejectUnauthorized === undefined ? null : opts.rejectUnauthorized;
              this.forceNode = !!opts.forceNode;
              // other options for Node.js client
              var freeGlobal = typeof global === 'object' && global;
              if (freeGlobal.global === freeGlobal) {
                if (opts.extraHeaders && Object.keys(opts.extraHeaders).length > 0) {
                  this.extraHeaders = opts.extraHeaders;
                }
                if (opts.localAddress) {
                  this.localAddress = opts.localAddress;
                }
              }
              // set on handshake
              this.id = null;
              this.upgrades = null;
              this.pingInterval = null;
              this.pingTimeout = null;
              // set on heartbeat
              this.pingIntervalTimer = null;
              this.pingTimeoutTimer = null;
              this.open();
            }
            Socket.priorWebsocketSuccess = false;
            /**
             * Mix in `Emitter`.
             */
            Emitter(Socket.prototype);
            /**
             * Protocol version.
             *
             * @api public
             */
            Socket.protocol = parser.protocol;
            // this is an int
            /**
             * Expose deps for legacy compatibility
             * and standalone browser access.
             */
            Socket.Socket = Socket;
            Socket.Transport = __webpack_require__(26);
            Socket.transports = __webpack_require__(21);
            Socket.parser = __webpack_require__(27);
            /**
             * Creates transport of the given type.
             *
             * @param {String} transport name
             * @return {Transport}
             * @api private
             */
            Socket.prototype.createTransport = function (name) {
              debug('creating transport "%s"', name);
              var query = clone(this.query);
              // append engine.io protocol identifier
              query.EIO = parser.protocol;
              // transport name
              query.transport = name;
              // session id if we already have one
              if (this.id)
                query.sid = this.id;
              var transport = new transports[name]({
                agent: this.agent,
                hostname: this.hostname,
                port: this.port,
                secure: this.secure,
                path: this.path,
                query: query,
                forceJSONP: this.forceJSONP,
                jsonp: this.jsonp,
                forceBase64: this.forceBase64,
                enablesXDR: this.enablesXDR,
                timestampRequests: this.timestampRequests,
                timestampParam: this.timestampParam,
                policyPort: this.policyPort,
                socket: this,
                pfx: this.pfx,
                key: this.key,
                passphrase: this.passphrase,
                cert: this.cert,
                ca: this.ca,
                ciphers: this.ciphers,
                rejectUnauthorized: this.rejectUnauthorized,
                perMessageDeflate: this.perMessageDeflate,
                extraHeaders: this.extraHeaders,
                forceNode: this.forceNode,
                localAddress: this.localAddress
              });
              return transport;
            };
            function clone(obj) {
              var o = {};
              for (var i in obj) {
                if (obj.hasOwnProperty(i)) {
                  o[i] = obj[i];
                }
              }
              return o;
            }
            /**
             * Initializes transport to use and starts probe.
             *
             * @api private
             */
            Socket.prototype.open = function () {
              var transport;
              if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') !== -1) {
                transport = 'websocket';
              } else if (0 === this.transports.length) {
                // Emit error on next tick so it can be listened to
                var self = this;
                setTimeout(function () {
                  self.emit('error', 'No transports available');
                }, 0);
                return;
              } else {
                transport = this.transports[0];
              }
              this.readyState = 'opening';
              // Retry with the next transport if the transport is disabled (jsonp: false)
              try {
                transport = this.createTransport(transport);
              } catch (e) {
                this.transports.shift();
                this.open();
                return;
              }
              transport.open();
              this.setTransport(transport);
            };
            /**
             * Sets the current transport. Disables the existing one (if any).
             *
             * @api private
             */
            Socket.prototype.setTransport = function (transport) {
              debug('setting transport %s', transport.name);
              var self = this;
              if (this.transport) {
                debug('clearing existing transport %s', this.transport.name);
                this.transport.removeAllListeners();
              }
              // set up transport
              this.transport = transport;
              // set up transport listeners
              transport.on('drain', function () {
                self.onDrain();
              }).on('packet', function (packet) {
                self.onPacket(packet);
              }).on('error', function (e) {
                self.onError(e);
              }).on('close', function () {
                self.onClose('transport close');
              });
            };
            /**
             * Probes a transport.
             *
             * @param {String} transport name
             * @api private
             */
            Socket.prototype.probe = function (name) {
              debug('probing transport "%s"', name);
              var transport = this.createTransport(name, { probe: 1 });
              var failed = false;
              var self = this;
              Socket.priorWebsocketSuccess = false;
              function onTransportOpen() {
                if (self.onlyBinaryUpgrades) {
                  var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary;
                  failed = failed || upgradeLosesBinary;
                }
                if (failed)
                  return;
                debug('probe transport "%s" opened', name);
                transport.send([{
                    type: 'ping',
                    data: 'probe'
                  }]);
                transport.once('packet', function (msg) {
                  if (failed)
                    return;
                  if ('pong' === msg.type && 'probe' === msg.data) {
                    debug('probe transport "%s" pong', name);
                    self.upgrading = true;
                    self.emit('upgrading', transport);
                    if (!transport)
                      return;
                    Socket.priorWebsocketSuccess = 'websocket' === transport.name;
                    debug('pausing current transport "%s"', self.transport.name);
                    self.transport.pause(function () {
                      if (failed)
                        return;
                      if ('closed' === self.readyState)
                        return;
                      debug('changing transport and sending upgrade packet');
                      cleanup();
                      self.setTransport(transport);
                      transport.send([{ type: 'upgrade' }]);
                      self.emit('upgrade', transport);
                      transport = null;
                      self.upgrading = false;
                      self.flush();
                    });
                  } else {
                    debug('probe transport "%s" failed', name);
                    var err = new Error('probe error');
                    err.transport = transport.name;
                    self.emit('upgradeError', err);
                  }
                });
              }
              function freezeTransport() {
                if (failed)
                  return;
                // Any callback called by transport should be ignored since now
                failed = true;
                cleanup();
                transport.close();
                transport = null;
              }
              // Handle any error that happens while probing
              function onerror(err) {
                var error = new Error('probe error: ' + err);
                error.transport = transport.name;
                freezeTransport();
                debug('probe transport "%s" failed because of error: %s', name, err);
                self.emit('upgradeError', error);
              }
              function onTransportClose() {
                onerror('transport closed');
              }
              // When the socket is closed while we're probing
              function onclose() {
                onerror('socket closed');
              }
              // When the socket is upgraded while we're probing
              function onupgrade(to) {
                if (transport && to.name !== transport.name) {
                  debug('"%s" works - aborting "%s"', to.name, transport.name);
                  freezeTransport();
                }
              }
              // Remove all listeners on the transport and on self
              function cleanup() {
                transport.removeListener('open', onTransportOpen);
                transport.removeListener('error', onerror);
                transport.removeListener('close', onTransportClose);
                self.removeListener('close', onclose);
                self.removeListener('upgrading', onupgrade);
              }
              transport.once('open', onTransportOpen);
              transport.once('error', onerror);
              transport.once('close', onTransportClose);
              this.once('close', onclose);
              this.once('upgrading', onupgrade);
              transport.open();
            };
            /**
             * Called when connection is deemed open.
             *
             * @api public
             */
            Socket.prototype.onOpen = function () {
              debug('socket open');
              this.readyState = 'open';
              Socket.priorWebsocketSuccess = 'websocket' === this.transport.name;
              this.emit('open');
              this.flush();
              // we check for `readyState` in case an `open`
              // listener already closed the socket
              if ('open' === this.readyState && this.upgrade && this.transport.pause) {
                debug('starting upgrade probes');
                for (var i = 0, l = this.upgrades.length; i < l; i++) {
                  this.probe(this.upgrades[i]);
                }
              }
            };
            /**
             * Handles a packet.
             *
             * @api private
             */
            Socket.prototype.onPacket = function (packet) {
              if ('opening' === this.readyState || 'open' === this.readyState || 'closing' === this.readyState) {
                debug('socket receive: type "%s", data "%s"', packet.type, packet.data);
                this.emit('packet', packet);
                // Socket is live - any packet counts
                this.emit('heartbeat');
                switch (packet.type) {
                case 'open':
                  this.onHandshake(parsejson(packet.data));
                  break;
                case 'pong':
                  this.setPing();
                  this.emit('pong');
                  break;
                case 'error':
                  var err = new Error('server error');
                  err.code = packet.data;
                  this.onError(err);
                  break;
                case 'message':
                  this.emit('data', packet.data);
                  this.emit('message', packet.data);
                  break;
                }
              } else {
                debug('packet received with socket readyState "%s"', this.readyState);
              }
            };
            /**
             * Called upon handshake completion.
             *
             * @param {Object} handshake obj
             * @api private
             */
            Socket.prototype.onHandshake = function (data) {
              this.emit('handshake', data);
              this.id = data.sid;
              this.transport.query.sid = data.sid;
              this.upgrades = this.filterUpgrades(data.upgrades);
              this.pingInterval = data.pingInterval;
              this.pingTimeout = data.pingTimeout;
              this.onOpen();
              // In case open handler closes socket
              if ('closed' === this.readyState)
                return;
              this.setPing();
              // Prolong liveness of socket on heartbeat
              this.removeListener('heartbeat', this.onHeartbeat);
              this.on('heartbeat', this.onHeartbeat);
            };
            /**
             * Resets ping timeout.
             *
             * @api private
             */
            Socket.prototype.onHeartbeat = function (timeout) {
              clearTimeout(this.pingTimeoutTimer);
              var self = this;
              self.pingTimeoutTimer = setTimeout(function () {
                if ('closed' === self.readyState)
                  return;
                self.onClose('ping timeout');
              }, timeout || self.pingInterval + self.pingTimeout);
            };
            /**
             * Pings server every `this.pingInterval` and expects response
             * within `this.pingTimeout` or closes connection.
             *
             * @api private
             */
            Socket.prototype.setPing = function () {
              var self = this;
              clearTimeout(self.pingIntervalTimer);
              self.pingIntervalTimer = setTimeout(function () {
                debug('writing ping packet - expecting pong within %sms', self.pingTimeout);
                self.ping();
                self.onHeartbeat(self.pingTimeout);
              }, self.pingInterval);
            };
            /**
            * Sends a ping packet.
            *
            * @api private
            */
            Socket.prototype.ping = function () {
              var self = this;
              this.sendPacket('ping', function () {
                self.emit('ping');
              });
            };
            /**
             * Called on `drain` event
             *
             * @api private
             */
            Socket.prototype.onDrain = function () {
              this.writeBuffer.splice(0, this.prevBufferLen);
              // setting prevBufferLen = 0 is very important
              // for example, when upgrading, upgrade packet is sent over,
              // and a nonzero prevBufferLen could cause problems on `drain`
              this.prevBufferLen = 0;
              if (0 === this.writeBuffer.length) {
                this.emit('drain');
              } else {
                this.flush();
              }
            };
            /**
             * Flush write buffers.
             *
             * @api private
             */
            Socket.prototype.flush = function () {
              if ('closed' !== this.readyState && this.transport.writable && !this.upgrading && this.writeBuffer.length) {
                debug('flushing %d packets in socket', this.writeBuffer.length);
                this.transport.send(this.writeBuffer);
                // keep track of current length of writeBuffer
                // splice writeBuffer and callbackBuffer on `drain`
                this.prevBufferLen = this.writeBuffer.length;
                this.emit('flush');
              }
            };
            /**
             * Sends a message.
             *
             * @param {String} message.
             * @param {Function} callback function.
             * @param {Object} options.
             * @return {Socket} for chaining.
             * @api public
             */
            Socket.prototype.write = Socket.prototype.send = function (msg, options, fn) {
              this.sendPacket('message', msg, options, fn);
              return this;
            };
            /**
             * Sends a packet.
             *
             * @param {String} packet type.
             * @param {String} data.
             * @param {Object} options.
             * @param {Function} callback function.
             * @api private
             */
            Socket.prototype.sendPacket = function (type, data, options, fn) {
              if ('function' === typeof data) {
                fn = data;
                data = undefined;
              }
              if ('function' === typeof options) {
                fn = options;
                options = null;
              }
              if ('closing' === this.readyState || 'closed' === this.readyState) {
                return;
              }
              options = options || {};
              options.compress = false !== options.compress;
              var packet = {
                type: type,
                data: data,
                options: options
              };
              this.emit('packetCreate', packet);
              this.writeBuffer.push(packet);
              if (fn)
                this.once('flush', fn);
              this.flush();
            };
            /**
             * Closes the connection.
             *
             * @api private
             */
            Socket.prototype.close = function () {
              if ('opening' === this.readyState || 'open' === this.readyState) {
                this.readyState = 'closing';
                var self = this;
                if (this.writeBuffer.length) {
                  this.once('drain', function () {
                    if (this.upgrading) {
                      waitForUpgrade();
                    } else {
                      close();
                    }
                  });
                } else if (this.upgrading) {
                  waitForUpgrade();
                } else {
                  close();
                }
              }
              function close() {
                self.onClose('forced close');
                debug('socket closing - telling transport to close');
                self.transport.close();
              }
              function cleanupAndClose() {
                self.removeListener('upgrade', cleanupAndClose);
                self.removeListener('upgradeError', cleanupAndClose);
                close();
              }
              function waitForUpgrade() {
                // wait for upgrade to finish since we can't send packets while pausing a transport
                self.once('upgrade', cleanupAndClose);
                self.once('upgradeError', cleanupAndClose);
              }
              return this;
            };
            /**
             * Called upon transport error
             *
             * @api private
             */
            Socket.prototype.onError = function (err) {
              debug('socket error %j', err);
              Socket.priorWebsocketSuccess = false;
              this.emit('error', err);
              this.onClose('transport error', err);
            };
            /**
             * Called upon transport close.
             *
             * @api private
             */
            Socket.prototype.onClose = function (reason, desc) {
              if ('opening' === this.readyState || 'open' === this.readyState || 'closing' === this.readyState) {
                debug('socket close with reason: "%s"', reason);
                var self = this;
                // clear timers
                clearTimeout(this.pingIntervalTimer);
                clearTimeout(this.pingTimeoutTimer);
                // stop event from firing again for transport
                this.transport.removeAllListeners('close');
                // ensure transport won't stay open
                this.transport.close();
                // ignore further transport communication
                this.transport.removeAllListeners();
                // set ready state
                this.readyState = 'closed';
                // clear session id
                this.id = null;
                // emit close event
                this.emit('close', reason, desc);
                // clean buffers after, so users can still
                // grab the buffers on `close` event
                self.writeBuffer = [];
                self.prevBufferLen = 0;
              }
            };
            /**
             * Filters upgrades, returning only those matching client transports.
             *
             * @param {Array} server upgrades
             * @api private
             *
             */
            Socket.prototype.filterUpgrades = function (upgrades) {
              var filteredUpgrades = [];
              for (var i = 0, j = upgrades.length; i < j; i++) {
                if (~index(this.transports, upgrades[i]))
                  filteredUpgrades.push(upgrades[i]);
              }
              return filteredUpgrades;
            };  /* WEBPACK VAR INJECTION */
          }.call(exports, function () {
            return this;
          }()));  /***/
        },
        /* 21 */
        /***/
        function (module, exports, __webpack_require__) {
          /* WEBPACK VAR INJECTION */
          (function (global) {
            /**
             * Module dependencies
             */
            var XMLHttpRequest = __webpack_require__(22);
            var XHR = __webpack_require__(24);
            var JSONP = __webpack_require__(39);
            var websocket = __webpack_require__(40);
            /**
             * Export transports.
             */
            exports.polling = polling;
            exports.websocket = websocket;
            /**
             * Polling transport polymorphic constructor.
             * Decides on xhr vs jsonp based on feature detection.
             *
             * @api private
             */
            function polling(opts) {
              var xhr;
              var xd = false;
              var xs = false;
              var jsonp = false !== opts.jsonp;
              if (global.location) {
                var isSSL = 'https:' === location.protocol;
                var port = location.port;
                // some user agents have empty `location.port`
                if (!port) {
                  port = isSSL ? 443 : 80;
                }
                xd = opts.hostname !== location.hostname || port !== opts.port;
                xs = opts.secure !== isSSL;
              }
              opts.xdomain = xd;
              opts.xscheme = xs;
              xhr = new XMLHttpRequest(opts);
              if ('open' in xhr && !opts.forceJSONP) {
                return new XHR(opts);
              } else {
                if (!jsonp)
                  throw new Error('JSONP disabled');
                return new JSONP(opts);
              }
            }  /* WEBPACK VAR INJECTION */
          }.call(exports, function () {
            return this;
          }()));  /***/
        },
        /* 22 */
        /***/
        function (module, exports, __webpack_require__) {
          /* WEBPACK VAR INJECTION */
          (function (global) {
            // browser shim for xmlhttprequest module
            var hasCORS = __webpack_require__(23);
            module.exports = function (opts) {
              var xdomain = opts.xdomain;
              // scheme must be same when usign XDomainRequest
              // http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx
              var xscheme = opts.xscheme;
              // XDomainRequest has a flow of not sending cookie, therefore it should be disabled as a default.
              // https://github.com/Automattic/engine.io-client/pull/217
              var enablesXDR = opts.enablesXDR;
              // XMLHttpRequest can be disabled on IE
              try {
                if ('undefined' !== typeof XMLHttpRequest && (!xdomain || hasCORS)) {
                  return new XMLHttpRequest();
                }
              } catch (e) {
              }
              // Use XDomainRequest for IE8 if enablesXDR is true
              // because loading bar keeps flashing when using jsonp-polling
              // https://github.com/yujiosaka/socke.io-ie8-loading-example
              try {
                if ('undefined' !== typeof XDomainRequest && !xscheme && enablesXDR) {
                  return new XDomainRequest();
                }
              } catch (e) {
              }
              if (!xdomain) {
                try {
                  return new global[(['Active'].concat('Object').join('X'))]('Microsoft.XMLHTTP');
                } catch (e) {
                }
              }
            };  /* WEBPACK VAR INJECTION */
          }.call(exports, function () {
            return this;
          }()));  /***/
        },
        /* 23 */
        /***/
        function (module, exports) {
          /**
           * Module exports.
           *
           * Logic borrowed from Modernizr:
           *
           *   - https://github.com/Modernizr/Modernizr/blob/master/feature-detects/cors.js
           */
          try {
            module.exports = typeof XMLHttpRequest !== 'undefined' && 'withCredentials' in new XMLHttpRequest();
          } catch (err) {
            // if XMLHttp support is disabled in IE then it will throw
            // when trying to create
            module.exports = false;
          }  /***/
        },
        /* 24 */
        /***/
        function (module, exports, __webpack_require__) {
          /* WEBPACK VAR INJECTION */
          (function (global) {
            /**
             * Module requirements.
             */
            var XMLHttpRequest = __webpack_require__(22);
            var Polling = __webpack_require__(25);
            var Emitter = __webpack_require__(35);
            var inherit = __webpack_require__(37);
            var debug = __webpack_require__(3)('engine.io-client:polling-xhr');
            /**
             * Module exports.
             */
            module.exports = XHR;
            module.exports.Request = Request;
            /**
             * Empty function
             */
            function empty() {
            }
            /**
             * XHR Polling constructor.
             *
             * @param {Object} opts
             * @api public
             */
            function XHR(opts) {
              Polling.call(this, opts);
              this.requestTimeout = opts.requestTimeout;
              if (global.location) {
                var isSSL = 'https:' === location.protocol;
                var port = location.port;
                // some user agents have empty `location.port`
                if (!port) {
                  port = isSSL ? 443 : 80;
                }
                this.xd = opts.hostname !== global.location.hostname || port !== opts.port;
                this.xs = opts.secure !== isSSL;
              } else {
                this.extraHeaders = opts.extraHeaders;
              }
            }
            /**
             * Inherits from Polling.
             */
            inherit(XHR, Polling);
            /**
             * XHR supports binary
             */
            XHR.prototype.supportsBinary = true;
            /**
             * Creates a request.
             *
             * @param {String} method
             * @api private
             */
            XHR.prototype.request = function (opts) {
              opts = opts || {};
              opts.uri = this.uri();
              opts.xd = this.xd;
              opts.xs = this.xs;
              opts.agent = this.agent || false;
              opts.supportsBinary = this.supportsBinary;
              opts.enablesXDR = this.enablesXDR;
              // SSL options for Node.js client
              opts.pfx = this.pfx;
              opts.key = this.key;
              opts.passphrase = this.passphrase;
              opts.cert = this.cert;
              opts.ca = this.ca;
              opts.ciphers = this.ciphers;
              opts.rejectUnauthorized = this.rejectUnauthorized;
              opts.requestTimeout = this.requestTimeout;
              // other options for Node.js client
              opts.extraHeaders = this.extraHeaders;
              return new Request(opts);
            };
            /**
             * Sends data.
             *
             * @param {String} data to send.
             * @param {Function} called upon flush.
             * @api private
             */
            XHR.prototype.doWrite = function (data, fn) {
              var isBinary = typeof data !== 'string' && data !== undefined;
              var req = this.request({
                method: 'POST',
                data: data,
                isBinary: isBinary
              });
              var self = this;
              req.on('success', fn);
              req.on('error', function (err) {
                self.onError('xhr post error', err);
              });
              this.sendXhr = req;
            };
            /**
             * Starts a poll cycle.
             *
             * @api private
             */
            XHR.prototype.doPoll = function () {
              debug('xhr poll');
              var req = this.request();
              var self = this;
              req.on('data', function (data) {
                self.onData(data);
              });
              req.on('error', function (err) {
                self.onError('xhr poll error', err);
              });
              this.pollXhr = req;
            };
            /**
             * Request constructor
             *
             * @param {Object} options
             * @api public
             */
            function Request(opts) {
              this.method = opts.method || 'GET';
              this.uri = opts.uri;
              this.xd = !!opts.xd;
              this.xs = !!opts.xs;
              this.async = false !== opts.async;
              this.data = undefined !== opts.data ? opts.data : null;
              this.agent = opts.agent;
              this.isBinary = opts.isBinary;
              this.supportsBinary = opts.supportsBinary;
              this.enablesXDR = opts.enablesXDR;
              this.requestTimeout = opts.requestTimeout;
              // SSL options for Node.js client
              this.pfx = opts.pfx;
              this.key = opts.key;
              this.passphrase = opts.passphrase;
              this.cert = opts.cert;
              this.ca = opts.ca;
              this.ciphers = opts.ciphers;
              this.rejectUnauthorized = opts.rejectUnauthorized;
              // other options for Node.js client
              this.extraHeaders = opts.extraHeaders;
              this.create();
            }
            /**
             * Mix in `Emitter`.
             */
            Emitter(Request.prototype);
            /**
             * Creates the XHR object and sends the request.
             *
             * @api private
             */
            Request.prototype.create = function () {
              var opts = {
                agent: this.agent,
                xdomain: this.xd,
                xscheme: this.xs,
                enablesXDR: this.enablesXDR
              };
              // SSL options for Node.js client
              opts.pfx = this.pfx;
              opts.key = this.key;
              opts.passphrase = this.passphrase;
              opts.cert = this.cert;
              opts.ca = this.ca;
              opts.ciphers = this.ciphers;
              opts.rejectUnauthorized = this.rejectUnauthorized;
              var xhr = this.xhr = new XMLHttpRequest(opts);
              var self = this;
              try {
                debug('xhr open %s: %s', this.method, this.uri);
                xhr.open(this.method, this.uri, this.async);
                try {
                  if (this.extraHeaders) {
                    xhr.setDisableHeaderCheck(true);
                    for (var i in this.extraHeaders) {
                      if (this.extraHeaders.hasOwnProperty(i)) {
                        xhr.setRequestHeader(i, this.extraHeaders[i]);
                      }
                    }
                  }
                } catch (e) {
                }
                if (this.supportsBinary) {
                  // This has to be done after open because Firefox is stupid
                  // http://stackoverflow.com/questions/13216903/get-binary-data-with-xmlhttprequest-in-a-firefox-extension
                  xhr.responseType = 'arraybuffer';
                }
                if ('POST' === this.method) {
                  try {
                    if (this.isBinary) {
                      xhr.setRequestHeader('Content-type', 'application/octet-stream');
                    } else {
                      xhr.setRequestHeader('Content-type', 'text/plain;charset=UTF-8');
                    }
                  } catch (e) {
                  }
                }
                try {
                  xhr.setRequestHeader('Accept', '*/*');
                } catch (e) {
                }
                // ie6 check
                if ('withCredentials' in xhr) {
                  xhr.withCredentials = true;
                }
                if (this.requestTimeout) {
                  xhr.timeout = this.requestTimeout;
                }
                if (this.hasXDR()) {
                  xhr.onload = function () {
                    self.onLoad();
                  };
                  xhr.onerror = function () {
                    self.onError(xhr.responseText);
                  };
                } else {
                  xhr.onreadystatechange = function () {
                    if (4 !== xhr.readyState)
                      return;
                    if (200 === xhr.status || 1223 === xhr.status) {
                      self.onLoad();
                    } else {
                      // make sure the `error` event handler that's user-set
                      // does not throw in the same tick and gets caught here
                      setTimeout(function () {
                        self.onError(xhr.status);
                      }, 0);
                    }
                  };
                }
                debug('xhr data %s', this.data);
                xhr.send(this.data);
              } catch (e) {
                // Need to defer since .create() is called directly fhrom the constructor
                // and thus the 'error' event can only be only bound *after* this exception
                // occurs.  Therefore, also, we cannot throw here at all.
                setTimeout(function () {
                  self.onError(e);
                }, 0);
                return;
              }
              if (global.document) {
                this.index = Request.requestsCount++;
                Request.requests[this.index] = this;
              }
            };
            /**
             * Called upon successful response.
             *
             * @api private
             */
            Request.prototype.onSuccess = function () {
              this.emit('success');
              this.cleanup();
            };
            /**
             * Called if we have data.
             *
             * @api private
             */
            Request.prototype.onData = function (data) {
              this.emit('data', data);
              this.onSuccess();
            };
            /**
             * Called upon error.
             *
             * @api private
             */
            Request.prototype.onError = function (err) {
              this.emit('error', err);
              this.cleanup(true);
            };
            /**
             * Cleans up house.
             *
             * @api private
             */
            Request.prototype.cleanup = function (fromError) {
              if ('undefined' === typeof this.xhr || null === this.xhr) {
                return;
              }
              // xmlhttprequest
              if (this.hasXDR()) {
                this.xhr.onload = this.xhr.onerror = empty;
              } else {
                this.xhr.onreadystatechange = empty;
              }
              if (fromError) {
                try {
                  this.xhr.abort();
                } catch (e) {
                }
              }
              if (global.document) {
                delete Request.requests[this.index];
              }
              this.xhr = null;
            };
            /**
             * Called upon load.
             *
             * @api private
             */
            Request.prototype.onLoad = function () {
              var data;
              try {
                var contentType;
                try {
                  contentType = this.xhr.getResponseHeader('Content-Type').split(';')[0];
                } catch (e) {
                }
                if (contentType === 'application/octet-stream') {
                  data = this.xhr.response || this.xhr.responseText;
                } else {
                  if (!this.supportsBinary) {
                    data = this.xhr.responseText;
                  } else {
                    try {
                      data = String.fromCharCode.apply(null, new Uint8Array(this.xhr.response));
                    } catch (e) {
                      var ui8Arr = new Uint8Array(this.xhr.response);
                      var dataArray = [];
                      for (var idx = 0, length = ui8Arr.length; idx < length; idx++) {
                        dataArray.push(ui8Arr[idx]);
                      }
                      data = String.fromCharCode.apply(null, dataArray);
                    }
                  }
                }
              } catch (e) {
                this.onError(e);
              }
              if (null != data) {
                this.onData(data);
              }
            };
            /**
             * Check if it has XDomainRequest.
             *
             * @api private
             */
            Request.prototype.hasXDR = function () {
              return 'undefined' !== typeof global.XDomainRequest && !this.xs && this.enablesXDR;
            };
            /**
             * Aborts the request.
             *
             * @api public
             */
            Request.prototype.abort = function () {
              this.cleanup();
            };
            /**
             * Aborts pending requests when unloading the window. This is needed to prevent
             * memory leaks (e.g. when using IE) and to ensure that no spurious error is
             * emitted.
             */
            Request.requestsCount = 0;
            Request.requests = {};
            if (global.document) {
              if (global.attachEvent) {
                global.attachEvent('onunload', unloadHandler);
              } else if (global.addEventListener) {
                global.addEventListener('beforeunload', unloadHandler, false);
              }
            }
            function unloadHandler() {
              for (var i in Request.requests) {
                if (Request.requests.hasOwnProperty(i)) {
                  Request.requests[i].abort();
                }
              }
            }  /* WEBPACK VAR INJECTION */
          }.call(exports, function () {
            return this;
          }()));  /***/
        },
        /* 25 */
        /***/
        function (module, exports, __webpack_require__) {
          /**
           * Module dependencies.
           */
          var Transport = __webpack_require__(26);
          var parseqs = __webpack_require__(36);
          var parser = __webpack_require__(27);
          var inherit = __webpack_require__(37);
          var yeast = __webpack_require__(38);
          var debug = __webpack_require__(3)('engine.io-client:polling');
          /**
           * Module exports.
           */
          module.exports = Polling;
          /**
           * Is XHR2 supported?
           */
          var hasXHR2 = function () {
            var XMLHttpRequest = __webpack_require__(22);
            var xhr = new XMLHttpRequest({ xdomain: false });
            return null != xhr.responseType;
          }();
          /**
           * Polling interface.
           *
           * @param {Object} opts
           * @api private
           */
          function Polling(opts) {
            var forceBase64 = opts && opts.forceBase64;
            if (!hasXHR2 || forceBase64) {
              this.supportsBinary = false;
            }
            Transport.call(this, opts);
          }
          /**
           * Inherits from Transport.
           */
          inherit(Polling, Transport);
          /**
           * Transport name.
           */
          Polling.prototype.name = 'polling';
          /**
           * Opens the socket (triggers polling). We write a PING message to determine
           * when the transport is open.
           *
           * @api private
           */
          Polling.prototype.doOpen = function () {
            this.poll();
          };
          /**
           * Pauses polling.
           *
           * @param {Function} callback upon buffers are flushed and transport is paused
           * @api private
           */
          Polling.prototype.pause = function (onPause) {
            var self = this;
            this.readyState = 'pausing';
            function pause() {
              debug('paused');
              self.readyState = 'paused';
              onPause();
            }
            if (this.polling || !this.writable) {
              var total = 0;
              if (this.polling) {
                debug('we are currently polling - waiting to pause');
                total++;
                this.once('pollComplete', function () {
                  debug('pre-pause polling complete');
                  --total || pause();
                });
              }
              if (!this.writable) {
                debug('we are currently writing - waiting to pause');
                total++;
                this.once('drain', function () {
                  debug('pre-pause writing complete');
                  --total || pause();
                });
              }
            } else {
              pause();
            }
          };
          /**
           * Starts polling cycle.
           *
           * @api public
           */
          Polling.prototype.poll = function () {
            debug('polling');
            this.polling = true;
            this.doPoll();
            this.emit('poll');
          };
          /**
           * Overloads onData to detect payloads.
           *
           * @api private
           */
          Polling.prototype.onData = function (data) {
            var self = this;
            debug('polling got data %s', data);
            var callback = function (packet, index, total) {
              // if its the first message we consider the transport open
              if ('opening' === self.readyState) {
                self.onOpen();
              }
              // if its a close packet, we close the ongoing requests
              if ('close' === packet.type) {
                self.onClose();
                return false;
              }
              // otherwise bypass onData and handle the message
              self.onPacket(packet);
            };
            // decode payload
            parser.decodePayload(data, this.socket.binaryType, callback);
            // if an event did not trigger closing
            if ('closed' !== this.readyState) {
              // if we got data we're not polling
              this.polling = false;
              this.emit('pollComplete');
              if ('open' === this.readyState) {
                this.poll();
              } else {
                debug('ignoring poll - transport state "%s"', this.readyState);
              }
            }
          };
          /**
           * For polling, send a close packet.
           *
           * @api private
           */
          Polling.prototype.doClose = function () {
            var self = this;
            function close() {
              debug('writing close packet');
              self.write([{ type: 'close' }]);
            }
            if ('open' === this.readyState) {
              debug('transport open - closing');
              close();
            } else {
              // in case we're trying to close while
              // handshaking is in progress (GH-164)
              debug('transport not open - deferring close');
              this.once('open', close);
            }
          };
          /**
           * Writes a packets payload.
           *
           * @param {Array} data packets
           * @param {Function} drain callback
           * @api private
           */
          Polling.prototype.write = function (packets) {
            var self = this;
            this.writable = false;
            var callbackfn = function () {
              self.writable = true;
              self.emit('drain');
            };
            parser.encodePayload(packets, this.supportsBinary, function (data) {
              self.doWrite(data, callbackfn);
            });
          };
          /**
           * Generates uri for connection.
           *
           * @api private
           */
          Polling.prototype.uri = function () {
            var query = this.query || {};
            var schema = this.secure ? 'https' : 'http';
            var port = '';
            // cache busting is forced
            if (false !== this.timestampRequests) {
              query[this.timestampParam] = yeast();
            }
            if (!this.supportsBinary && !query.sid) {
              query.b64 = 1;
            }
            query = parseqs.encode(query);
            // avoid port if default for schema
            if (this.port && ('https' === schema && Number(this.port) !== 443 || 'http' === schema && Number(this.port) !== 80)) {
              port = ':' + this.port;
            }
            // prepend ? to query
            if (query.length) {
              query = '?' + query;
            }
            var ipv6 = this.hostname.indexOf(':') !== -1;
            return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path + query;
          };  /***/
        },
        /* 26 */
        /***/
        function (module, exports, __webpack_require__) {
          /**
           * Module dependencies.
           */
          var parser = __webpack_require__(27);
          var Emitter = __webpack_require__(35);
          /**
           * Module exports.
           */
          module.exports = Transport;
          /**
           * Transport abstract constructor.
           *
           * @param {Object} options.
           * @api private
           */
          function Transport(opts) {
            this.path = opts.path;
            this.hostname = opts.hostname;
            this.port = opts.port;
            this.secure = opts.secure;
            this.query = opts.query;
            this.timestampParam = opts.timestampParam;
            this.timestampRequests = opts.timestampRequests;
            this.readyState = '';
            this.agent = opts.agent || false;
            this.socket = opts.socket;
            this.enablesXDR = opts.enablesXDR;
            // SSL options for Node.js client
            this.pfx = opts.pfx;
            this.key = opts.key;
            this.passphrase = opts.passphrase;
            this.cert = opts.cert;
            this.ca = opts.ca;
            this.ciphers = opts.ciphers;
            this.rejectUnauthorized = opts.rejectUnauthorized;
            this.forceNode = opts.forceNode;
            // other options for Node.js client
            this.extraHeaders = opts.extraHeaders;
            this.localAddress = opts.localAddress;
          }
          /**
           * Mix in `Emitter`.
           */
          Emitter(Transport.prototype);
          /**
           * Emits an error.
           *
           * @param {String} str
           * @return {Transport} for chaining
           * @api public
           */
          Transport.prototype.onError = function (msg, desc) {
            var err = new Error(msg);
            err.type = 'TransportError';
            err.description = desc;
            this.emit('error', err);
            return this;
          };
          /**
           * Opens the transport.
           *
           * @api public
           */
          Transport.prototype.open = function () {
            if ('closed' === this.readyState || '' === this.readyState) {
              this.readyState = 'opening';
              this.doOpen();
            }
            return this;
          };
          /**
           * Closes the transport.
           *
           * @api private
           */
          Transport.prototype.close = function () {
            if ('opening' === this.readyState || 'open' === this.readyState) {
              this.doClose();
              this.onClose();
            }
            return this;
          };
          /**
           * Sends multiple packets.
           *
           * @param {Array} packets
           * @api private
           */
          Transport.prototype.send = function (packets) {
            if ('open' === this.readyState) {
              this.write(packets);
            } else {
              throw new Error('Transport not open');
            }
          };
          /**
           * Called upon open
           *
           * @api private
           */
          Transport.prototype.onOpen = function () {
            this.readyState = 'open';
            this.writable = true;
            this.emit('open');
          };
          /**
           * Called with data.
           *
           * @param {String} data
           * @api private
           */
          Transport.prototype.onData = function (data) {
            var packet = parser.decodePacket(data, this.socket.binaryType);
            this.onPacket(packet);
          };
          /**
           * Called with a decoded packet.
           */
          Transport.prototype.onPacket = function (packet) {
            this.emit('packet', packet);
          };
          /**
           * Called upon close.
           *
           * @api private
           */
          Transport.prototype.onClose = function () {
            this.readyState = 'closed';
            this.emit('close');
          };  /***/
        },
        /* 27 */
        /***/
        function (module, exports, __webpack_require__) {
          /* WEBPACK VAR INJECTION */
          (function (global) {
            /**
             * Module dependencies.
             */
            var keys = __webpack_require__(28);
            var hasBinary = __webpack_require__(29);
            var sliceBuffer = __webpack_require__(30);
            var after = __webpack_require__(31);
            var utf8 = __webpack_require__(32);
            var base64encoder;
            if (global && global.ArrayBuffer) {
              base64encoder = __webpack_require__(33);
            }
            /**
             * Check if we are running an android browser. That requires us to use
             * ArrayBuffer with polling transports...
             *
             * http://ghinda.net/jpeg-blob-ajax-android/
             */
            var isAndroid = typeof navigator !== 'undefined' && /Android/i.test(navigator.userAgent);
            /**
             * Check if we are running in PhantomJS.
             * Uploading a Blob with PhantomJS does not work correctly, as reported here:
             * https://github.com/ariya/phantomjs/issues/11395
             * @type boolean
             */
            var isPhantomJS = typeof navigator !== 'undefined' && /PhantomJS/i.test(navigator.userAgent);
            /**
             * When true, avoids using Blobs to encode payloads.
             * @type boolean
             */
            var dontSendBlobs = isAndroid || isPhantomJS;
            /**
             * Current protocol version.
             */
            exports.protocol = 3;
            /**
             * Packet types.
             */
            var packets = exports.packets = {
              open: 0  // non-ws
    ,
              close: 1  // non-ws
    ,
              ping: 2,
              pong: 3,
              message: 4,
              upgrade: 5,
              noop: 6
            };
            var packetslist = keys(packets);
            /**
             * Premade error packet.
             */
            var err = {
              type: 'error',
              data: 'parser error'
            };
            /**
             * Create a blob api even for blob builder when vendor prefixes exist
             */
            var Blob = __webpack_require__(34);
            /**
             * Encodes a packet.
             *
             *     <packet type id> [ <data> ]
             *
             * Example:
             *
             *     5hello world
             *     3
             *     4
             *
             * Binary is encoded in an identical principle
             *
             * @api private
             */
            exports.encodePacket = function (packet, supportsBinary, utf8encode, callback) {
              if ('function' == typeof supportsBinary) {
                callback = supportsBinary;
                supportsBinary = false;
              }
              if ('function' == typeof utf8encode) {
                callback = utf8encode;
                utf8encode = null;
              }
              var data = packet.data === undefined ? undefined : packet.data.buffer || packet.data;
              if (global.ArrayBuffer && data instanceof ArrayBuffer) {
                return encodeArrayBuffer(packet, supportsBinary, callback);
              } else if (Blob && data instanceof global.Blob) {
                return encodeBlob(packet, supportsBinary, callback);
              }
              // might be an object with { base64: true, data: dataAsBase64String }
              if (data && data.base64) {
                return encodeBase64Object(packet, callback);
              }
              // Sending data as a utf-8 string
              var encoded = packets[packet.type];
              // data fragment is optional
              if (undefined !== packet.data) {
                encoded += utf8encode ? utf8.encode(String(packet.data)) : String(packet.data);
              }
              return callback('' + encoded);
            };
            function encodeBase64Object(packet, callback) {
              // packet data is an object { base64: true, data: dataAsBase64String }
              var message = 'b' + exports.packets[packet.type] + packet.data.data;
              return callback(message);
            }
            /**
             * Encode packet helpers for binary types
             */
            function encodeArrayBuffer(packet, supportsBinary, callback) {
              if (!supportsBinary) {
                return exports.encodeBase64Packet(packet, callback);
              }
              var data = packet.data;
              var contentArray = new Uint8Array(data);
              var resultBuffer = new Uint8Array(1 + data.byteLength);
              resultBuffer[0] = packets[packet.type];
              for (var i = 0; i < contentArray.length; i++) {
                resultBuffer[i + 1] = contentArray[i];
              }
              return callback(resultBuffer.buffer);
            }
            function encodeBlobAsArrayBuffer(packet, supportsBinary, callback) {
              if (!supportsBinary) {
                return exports.encodeBase64Packet(packet, callback);
              }
              var fr = new FileReader();
              fr.onload = function () {
                packet.data = fr.result;
                exports.encodePacket(packet, supportsBinary, true, callback);
              };
              return fr.readAsArrayBuffer(packet.data);
            }
            function encodeBlob(packet, supportsBinary, callback) {
              if (!supportsBinary) {
                return exports.encodeBase64Packet(packet, callback);
              }
              if (dontSendBlobs) {
                return encodeBlobAsArrayBuffer(packet, supportsBinary, callback);
              }
              var length = new Uint8Array(1);
              length[0] = packets[packet.type];
              var blob = new Blob([
                length.buffer,
                packet.data
              ]);
              return callback(blob);
            }
            /**
             * Encodes a packet with binary data in a base64 string
             *
             * @param {Object} packet, has `type` and `data`
             * @return {String} base64 encoded message
             */
            exports.encodeBase64Packet = function (packet, callback) {
              var message = 'b' + exports.packets[packet.type];
              if (Blob && packet.data instanceof global.Blob) {
                var fr = new FileReader();
                fr.onload = function () {
                  var b64 = fr.result.split(',')[1];
                  callback(message + b64);
                };
                return fr.readAsDataURL(packet.data);
              }
              var b64data;
              try {
                b64data = String.fromCharCode.apply(null, new Uint8Array(packet.data));
              } catch (e) {
                // iPhone Safari doesn't let you apply with typed arrays
                var typed = new Uint8Array(packet.data);
                var basic = new Array(typed.length);
                for (var i = 0; i < typed.length; i++) {
                  basic[i] = typed[i];
                }
                b64data = String.fromCharCode.apply(null, basic);
              }
              message += global.btoa(b64data);
              return callback(message);
            };
            /**
             * Decodes a packet. Changes format to Blob if requested.
             *
             * @return {Object} with `type` and `data` (if any)
             * @api private
             */
            exports.decodePacket = function (data, binaryType, utf8decode) {
              if (data === undefined) {
                return err;
              }
              // String data
              if (typeof data == 'string') {
                if (data.charAt(0) == 'b') {
                  return exports.decodeBase64Packet(data.substr(1), binaryType);
                }
                if (utf8decode) {
                  data = tryDecode(data);
                  if (data === false) {
                    return err;
                  }
                }
                var type = data.charAt(0);
                if (Number(type) != type || !packetslist[type]) {
                  return err;
                }
                if (data.length > 1) {
                  return {
                    type: packetslist[type],
                    data: data.substring(1)
                  };
                } else {
                  return { type: packetslist[type] };
                }
              }
              var asArray = new Uint8Array(data);
              var type = asArray[0];
              var rest = sliceBuffer(data, 1);
              if (Blob && binaryType === 'blob') {
                rest = new Blob([rest]);
              }
              return {
                type: packetslist[type],
                data: rest
              };
            };
            function tryDecode(data) {
              try {
                data = utf8.decode(data);
              } catch (e) {
                return false;
              }
              return data;
            }
            /**
             * Decodes a packet encoded in a base64 string
             *
             * @param {String} base64 encoded message
             * @return {Object} with `type` and `data` (if any)
             */
            exports.decodeBase64Packet = function (msg, binaryType) {
              var type = packetslist[msg.charAt(0)];
              if (!base64encoder) {
                return {
                  type: type,
                  data: {
                    base64: true,
                    data: msg.substr(1)
                  }
                };
              }
              var data = base64encoder.decode(msg.substr(1));
              if (binaryType === 'blob' && Blob) {
                data = new Blob([data]);
              }
              return {
                type: type,
                data: data
              };
            };
            /**
             * Encodes multiple messages (payload).
             *
             *     <length>:data
             *
             * Example:
             *
             *     11:hello world2:hi
             *
             * If any contents are binary, they will be encoded as base64 strings. Base64
             * encoded strings are marked with a b before the length specifier
             *
             * @param {Array} packets
             * @api private
             */
            exports.encodePayload = function (packets, supportsBinary, callback) {
              if (typeof supportsBinary == 'function') {
                callback = supportsBinary;
                supportsBinary = null;
              }
              var isBinary = hasBinary(packets);
              if (supportsBinary && isBinary) {
                if (Blob && !dontSendBlobs) {
                  return exports.encodePayloadAsBlob(packets, callback);
                }
                return exports.encodePayloadAsArrayBuffer(packets, callback);
              }
              if (!packets.length) {
                return callback('0:');
              }
              function setLengthHeader(message) {
                return message.length + ':' + message;
              }
              function encodeOne(packet, doneCallback) {
                exports.encodePacket(packet, !isBinary ? false : supportsBinary, true, function (message) {
                  doneCallback(null, setLengthHeader(message));
                });
              }
              map(packets, encodeOne, function (err, results) {
                return callback(results.join(''));
              });
            };
            /**
             * Async array map using after
             */
            function map(ary, each, done) {
              var result = new Array(ary.length);
              var next = after(ary.length, done);
              var eachWithIndex = function (i, el, cb) {
                each(el, function (error, msg) {
                  result[i] = msg;
                  cb(error, result);
                });
              };
              for (var i = 0; i < ary.length; i++) {
                eachWithIndex(i, ary[i], next);
              }
            }
            /*
             * Decodes data when a payload is maybe expected. Possible binary contents are
             * decoded from their base64 representation
             *
             * @param {String} data, callback method
             * @api public
             */
            exports.decodePayload = function (data, binaryType, callback) {
              if (typeof data != 'string') {
                return exports.decodePayloadAsBinary(data, binaryType, callback);
              }
              if (typeof binaryType === 'function') {
                callback = binaryType;
                binaryType = null;
              }
              var packet;
              if (data == '') {
                // parser error - ignoring payload
                return callback(err, 0, 1);
              }
              var length = '', n, msg;
              for (var i = 0, l = data.length; i < l; i++) {
                var chr = data.charAt(i);
                if (':' != chr) {
                  length += chr;
                } else {
                  if ('' == length || length != (n = Number(length))) {
                    // parser error - ignoring payload
                    return callback(err, 0, 1);
                  }
                  msg = data.substr(i + 1, n);
                  if (length != msg.length) {
                    // parser error - ignoring payload
                    return callback(err, 0, 1);
                  }
                  if (msg.length) {
                    packet = exports.decodePacket(msg, binaryType, true);
                    if (err.type == packet.type && err.data == packet.data) {
                      // parser error in individual packet - ignoring payload
                      return callback(err, 0, 1);
                    }
                    var ret = callback(packet, i + n, l);
                    if (false === ret)
                      return;
                  }
                  // advance cursor
                  i += n;
                  length = '';
                }
              }
              if (length != '') {
                // parser error - ignoring payload
                return callback(err, 0, 1);
              }
            };
            /**
             * Encodes multiple messages (payload) as binary.
             *
             * <1 = binary, 0 = string><number from 0-9><number from 0-9>[...]<number
             * 255><data>
             *
             * Example:
             * 1 3 255 1 2 3, if the binary contents are interpreted as 8 bit integers
             *
             * @param {Array} packets
             * @return {ArrayBuffer} encoded payload
             * @api private
             */
            exports.encodePayloadAsArrayBuffer = function (packets, callback) {
              if (!packets.length) {
                return callback(new ArrayBuffer(0));
              }
              function encodeOne(packet, doneCallback) {
                exports.encodePacket(packet, true, true, function (data) {
                  return doneCallback(null, data);
                });
              }
              map(packets, encodeOne, function (err, encodedPackets) {
                var totalLength = encodedPackets.reduce(function (acc, p) {
                  var len;
                  if (typeof p === 'string') {
                    len = p.length;
                  } else {
                    len = p.byteLength;
                  }
                  return acc + len.toString().length + len + 2;  // string/binary identifier + separator = 2
                }, 0);
                var resultArray = new Uint8Array(totalLength);
                var bufferIndex = 0;
                encodedPackets.forEach(function (p) {
                  var isString = typeof p === 'string';
                  var ab = p;
                  if (isString) {
                    var view = new Uint8Array(p.length);
                    for (var i = 0; i < p.length; i++) {
                      view[i] = p.charCodeAt(i);
                    }
                    ab = view.buffer;
                  }
                  if (isString) {
                    // not true binary
                    resultArray[bufferIndex++] = 0;
                  } else {
                    // true binary
                    resultArray[bufferIndex++] = 1;
                  }
                  var lenStr = ab.byteLength.toString();
                  for (var i = 0; i < lenStr.length; i++) {
                    resultArray[bufferIndex++] = parseInt(lenStr[i]);
                  }
                  resultArray[bufferIndex++] = 255;
                  var view = new Uint8Array(ab);
                  for (var i = 0; i < view.length; i++) {
                    resultArray[bufferIndex++] = view[i];
                  }
                });
                return callback(resultArray.buffer);
              });
            };
            /**
             * Encode as Blob
             */
            exports.encodePayloadAsBlob = function (packets, callback) {
              function encodeOne(packet, doneCallback) {
                exports.encodePacket(packet, true, true, function (encoded) {
                  var binaryIdentifier = new Uint8Array(1);
                  binaryIdentifier[0] = 1;
                  if (typeof encoded === 'string') {
                    var view = new Uint8Array(encoded.length);
                    for (var i = 0; i < encoded.length; i++) {
                      view[i] = encoded.charCodeAt(i);
                    }
                    encoded = view.buffer;
                    binaryIdentifier[0] = 0;
                  }
                  var len = encoded instanceof ArrayBuffer ? encoded.byteLength : encoded.size;
                  var lenStr = len.toString();
                  var lengthAry = new Uint8Array(lenStr.length + 1);
                  for (var i = 0; i < lenStr.length; i++) {
                    lengthAry[i] = parseInt(lenStr[i]);
                  }
                  lengthAry[lenStr.length] = 255;
                  if (Blob) {
                    var blob = new Blob([
                      binaryIdentifier.buffer,
                      lengthAry.buffer,
                      encoded
                    ]);
                    doneCallback(null, blob);
                  }
                });
              }
              map(packets, encodeOne, function (err, results) {
                return callback(new Blob(results));
              });
            };
            /*
             * Decodes data when a payload is maybe expected. Strings are decoded by
             * interpreting each byte as a key code for entries marked to start with 0. See
             * description of encodePayloadAsBinary
             *
             * @param {ArrayBuffer} data, callback method
             * @api public
             */
            exports.decodePayloadAsBinary = function (data, binaryType, callback) {
              if (typeof binaryType === 'function') {
                callback = binaryType;
                binaryType = null;
              }
              var bufferTail = data;
              var buffers = [];
              var numberTooLong = false;
              while (bufferTail.byteLength > 0) {
                var tailArray = new Uint8Array(bufferTail);
                var isString = tailArray[0] === 0;
                var msgLength = '';
                for (var i = 1;; i++) {
                  if (tailArray[i] == 255)
                    break;
                  if (msgLength.length > 310) {
                    numberTooLong = true;
                    break;
                  }
                  msgLength += tailArray[i];
                }
                if (numberTooLong)
                  return callback(err, 0, 1);
                bufferTail = sliceBuffer(bufferTail, 2 + msgLength.length);
                msgLength = parseInt(msgLength);
                var msg = sliceBuffer(bufferTail, 0, msgLength);
                if (isString) {
                  try {
                    msg = String.fromCharCode.apply(null, new Uint8Array(msg));
                  } catch (e) {
                    // iPhone Safari doesn't let you apply to typed arrays
                    var typed = new Uint8Array(msg);
                    msg = '';
                    for (var i = 0; i < typed.length; i++) {
                      msg += String.fromCharCode(typed[i]);
                    }
                  }
                }
                buffers.push(msg);
                bufferTail = sliceBuffer(bufferTail, msgLength);
              }
              var total = buffers.length;
              buffers.forEach(function (buffer, i) {
                callback(exports.decodePacket(buffer, binaryType, true), i, total);
              });
            };  /* WEBPACK VAR INJECTION */
          }.call(exports, function () {
            return this;
          }()));  /***/
        },
        /* 28 */
        /***/
        function (module, exports) {
          /**
           * Gets the keys for an object.
           *
           * @return {Array} keys
           * @api private
           */
          module.exports = Object.keys || function keys(obj) {
            var arr = [];
            var has = Object.prototype.hasOwnProperty;
            for (var i in obj) {
              if (has.call(obj, i)) {
                arr.push(i);
              }
            }
            return arr;
          };  /***/
        },
        /* 29 */
        /***/
        function (module, exports, __webpack_require__) {
          /* WEBPACK VAR INJECTION */
          (function (global) {
            /*
             * Module requirements.
             */
            var isArray = __webpack_require__(15);
            /**
             * Module exports.
             */
            module.exports = hasBinary;
            /**
             * Checks for binary data.
             *
             * Right now only Buffer and ArrayBuffer are supported..
             *
             * @param {Object} anything
             * @api public
             */
            function hasBinary(data) {
              function _hasBinary(obj) {
                if (!obj)
                  return false;
                if (global.Buffer && global.Buffer.isBuffer && global.Buffer.isBuffer(obj) || global.ArrayBuffer && obj instanceof ArrayBuffer || global.Blob && obj instanceof Blob || global.File && obj instanceof File) {
                  return true;
                }
                if (isArray(obj)) {
                  for (var i = 0; i < obj.length; i++) {
                    if (_hasBinary(obj[i])) {
                      return true;
                    }
                  }
                } else if (obj && 'object' == typeof obj) {
                  // see: https://github.com/Automattic/has-binary/pull/4
                  if (obj.toJSON && 'function' == typeof obj.toJSON) {
                    obj = obj.toJSON();
                  }
                  for (var key in obj) {
                    if (Object.prototype.hasOwnProperty.call(obj, key) && _hasBinary(obj[key])) {
                      return true;
                    }
                  }
                }
                return false;
              }
              return _hasBinary(data);
            }  /* WEBPACK VAR INJECTION */
          }.call(exports, function () {
            return this;
          }()));  /***/
        },
        /* 30 */
        /***/
        function (module, exports) {
          /**
           * An abstraction for slicing an arraybuffer even when
           * ArrayBuffer.prototype.slice is not supported
           *
           * @api public
           */
          module.exports = function (arraybuffer, start, end) {
            var bytes = arraybuffer.byteLength;
            start = start || 0;
            end = end || bytes;
            if (arraybuffer.slice) {
              return arraybuffer.slice(start, end);
            }
            if (start < 0) {
              start += bytes;
            }
            if (end < 0) {
              end += bytes;
            }
            if (end > bytes) {
              end = bytes;
            }
            if (start >= bytes || start >= end || bytes === 0) {
              return new ArrayBuffer(0);
            }
            var abv = new Uint8Array(arraybuffer);
            var result = new Uint8Array(end - start);
            for (var i = start, ii = 0; i < end; i++, ii++) {
              result[ii] = abv[i];
            }
            return result.buffer;
          };  /***/
        },
        /* 31 */
        /***/
        function (module, exports) {
          module.exports = after;
          function after(count, callback, err_cb) {
            var bail = false;
            err_cb = err_cb || noop;
            proxy.count = count;
            return count === 0 ? callback() : proxy;
            function proxy(err, result) {
              if (proxy.count <= 0) {
                throw new Error('after called too many times');
              }
              --proxy.count;
              // after first error, rest are passed to err_cb
              if (err) {
                bail = true;
                callback(err);
                // future error callbacks will go to error handler
                callback = err_cb;
              } else if (proxy.count === 0 && !bail) {
                callback(null, result);
              }
            }
          }
          function noop() {
          }  /***/
        },
        /* 32 */
        /***/
        function (module, exports, __webpack_require__) {
          var __WEBPACK_AMD_DEFINE_RESULT__;
          /* WEBPACK VAR INJECTION */
          (function (module, global) {
            /*! https://mths.be/wtf8 v1.0.0 by @mathias */
            ;
            (function (root) {
              // Detect free variables `exports`
              var freeExports = typeof exports == 'object' && exports;
              // Detect free variable `module`
              var freeModule = typeof module == 'object' && module && module.exports == freeExports && module;
              // Detect free variable `global`, from Node.js or Browserified code,
              // and use it as `root`
              var freeGlobal = typeof global == 'object' && global;
              if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) {
                root = freeGlobal;
              }
              /*--------------------------------------------------------------------------*/
              var stringFromCharCode = String.fromCharCode;
              // Taken from https://mths.be/punycode
              function ucs2decode(string) {
                var output = [];
                var counter = 0;
                var length = string.length;
                var value;
                var extra;
                while (counter < length) {
                  value = string.charCodeAt(counter++);
                  if (value >= 55296 && value <= 56319 && counter < length) {
                    // high surrogate, and there is a next character
                    extra = string.charCodeAt(counter++);
                    if ((extra & 64512) == 56320) {
                      // low surrogate
                      output.push(((value & 1023) << 10) + (extra & 1023) + 65536);
                    } else {
                      // unmatched surrogate; only append this code unit, in case the next
                      // code unit is the high surrogate of a surrogate pair
                      output.push(value);
                      counter--;
                    }
                  } else {
                    output.push(value);
                  }
                }
                return output;
              }
              // Taken from https://mths.be/punycode
              function ucs2encode(array) {
                var length = array.length;
                var index = -1;
                var value;
                var output = '';
                while (++index < length) {
                  value = array[index];
                  if (value > 65535) {
                    value -= 65536;
                    output += stringFromCharCode(value >>> 10 & 1023 | 55296);
                    value = 56320 | value & 1023;
                  }
                  output += stringFromCharCode(value);
                }
                return output;
              }
              /*--------------------------------------------------------------------------*/
              function createByte(codePoint, shift) {
                return stringFromCharCode(codePoint >> shift & 63 | 128);
              }
              function encodeCodePoint(codePoint) {
                if ((codePoint & 4294967168) == 0) {
                  // 1-byte sequence
                  return stringFromCharCode(codePoint);
                }
                var symbol = '';
                if ((codePoint & 4294965248) == 0) {
                  // 2-byte sequence
                  symbol = stringFromCharCode(codePoint >> 6 & 31 | 192);
                } else if ((codePoint & 4294901760) == 0) {
                  // 3-byte sequence
                  symbol = stringFromCharCode(codePoint >> 12 & 15 | 224);
                  symbol += createByte(codePoint, 6);
                } else if ((codePoint & 4292870144) == 0) {
                  // 4-byte sequence
                  symbol = stringFromCharCode(codePoint >> 18 & 7 | 240);
                  symbol += createByte(codePoint, 12);
                  symbol += createByte(codePoint, 6);
                }
                symbol += stringFromCharCode(codePoint & 63 | 128);
                return symbol;
              }
              function wtf8encode(string) {
                var codePoints = ucs2decode(string);
                var length = codePoints.length;
                var index = -1;
                var codePoint;
                var byteString = '';
                while (++index < length) {
                  codePoint = codePoints[index];
                  byteString += encodeCodePoint(codePoint);
                }
                return byteString;
              }
              /*--------------------------------------------------------------------------*/
              function readContinuationByte() {
                if (byteIndex >= byteCount) {
                  throw Error('Invalid byte index');
                }
                var continuationByte = byteArray[byteIndex] & 255;
                byteIndex++;
                if ((continuationByte & 192) == 128) {
                  return continuationByte & 63;
                }
                // If we end up here, it’s not a continuation byte.
                throw Error('Invalid continuation byte');
              }
              function decodeSymbol() {
                var byte1;
                var byte2;
                var byte3;
                var byte4;
                var codePoint;
                if (byteIndex > byteCount) {
                  throw Error('Invalid byte index');
                }
                if (byteIndex == byteCount) {
                  return false;
                }
                // Read the first byte.
                byte1 = byteArray[byteIndex] & 255;
                byteIndex++;
                // 1-byte sequence (no continuation bytes)
                if ((byte1 & 128) == 0) {
                  return byte1;
                }
                // 2-byte sequence
                if ((byte1 & 224) == 192) {
                  var byte2 = readContinuationByte();
                  codePoint = (byte1 & 31) << 6 | byte2;
                  if (codePoint >= 128) {
                    return codePoint;
                  } else {
                    throw Error('Invalid continuation byte');
                  }
                }
                // 3-byte sequence (may include unpaired surrogates)
                if ((byte1 & 240) == 224) {
                  byte2 = readContinuationByte();
                  byte3 = readContinuationByte();
                  codePoint = (byte1 & 15) << 12 | byte2 << 6 | byte3;
                  if (codePoint >= 2048) {
                    return codePoint;
                  } else {
                    throw Error('Invalid continuation byte');
                  }
                }
                // 4-byte sequence
                if ((byte1 & 248) == 240) {
                  byte2 = readContinuationByte();
                  byte3 = readContinuationByte();
                  byte4 = readContinuationByte();
                  codePoint = (byte1 & 15) << 18 | byte2 << 12 | byte3 << 6 | byte4;
                  if (codePoint >= 65536 && codePoint <= 1114111) {
                    return codePoint;
                  }
                }
                throw Error('Invalid WTF-8 detected');
              }
              var byteArray;
              var byteCount;
              var byteIndex;
              function wtf8decode(byteString) {
                byteArray = ucs2decode(byteString);
                byteCount = byteArray.length;
                byteIndex = 0;
                var codePoints = [];
                var tmp;
                while ((tmp = decodeSymbol()) !== false) {
                  codePoints.push(tmp);
                }
                return ucs2encode(codePoints);
              }
              /*--------------------------------------------------------------------------*/
              var wtf8 = {
                'version': '1.0.0',
                'encode': wtf8encode,
                'decode': wtf8decode
              };
              // Some AMD build optimizers, like r.js, check for specific condition patterns
              // like the following:
              if (true) {
                !(__WEBPACK_AMD_DEFINE_RESULT__ = function () {
                  return wtf8;
                }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
              } else if (freeExports && !freeExports.nodeType) {
                if (freeModule) {
                  // in Node.js or RingoJS v0.8.0+
                  freeModule.exports = wtf8;
                } else {
                  // in Narwhal or RingoJS v0.7.0-
                  var object = {};
                  var hasOwnProperty = object.hasOwnProperty;
                  for (var key in wtf8) {
                    hasOwnProperty.call(wtf8, key) && (freeExports[key] = wtf8[key]);
                  }
                }
              } else {
                // in Rhino or a web browser
                root.wtf8 = wtf8;
              }
            }(this));  /* WEBPACK VAR INJECTION */
          }.call(exports, __webpack_require__(12)(module), function () {
            return this;
          }()));  /***/
        },
        /* 33 */
        /***/
        function (module, exports) {
          /*
           * base64-arraybuffer
           * https://github.com/niklasvh/base64-arraybuffer
           *
           * Copyright (c) 2012 Niklas von Hertzen
           * Licensed under the MIT license.
           */
          (function () {
            'use strict';
            var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
            // Use a lookup table to find the index.
            var lookup = new Uint8Array(256);
            for (var i = 0; i < chars.length; i++) {
              lookup[chars.charCodeAt(i)] = i;
            }
            exports.encode = function (arraybuffer) {
              var bytes = new Uint8Array(arraybuffer), i, len = bytes.length, base64 = '';
              for (i = 0; i < len; i += 3) {
                base64 += chars[bytes[i] >> 2];
                base64 += chars[(bytes[i] & 3) << 4 | bytes[i + 1] >> 4];
                base64 += chars[(bytes[i + 1] & 15) << 2 | bytes[i + 2] >> 6];
                base64 += chars[bytes[i + 2] & 63];
              }
              if (len % 3 === 2) {
                base64 = base64.substring(0, base64.length - 1) + '=';
              } else if (len % 3 === 1) {
                base64 = base64.substring(0, base64.length - 2) + '==';
              }
              return base64;
            };
            exports.decode = function (base64) {
              var bufferLength = base64.length * 0.75, len = base64.length, i, p = 0, encoded1, encoded2, encoded3, encoded4;
              if (base64[base64.length - 1] === '=') {
                bufferLength--;
                if (base64[base64.length - 2] === '=') {
                  bufferLength--;
                }
              }
              var arraybuffer = new ArrayBuffer(bufferLength), bytes = new Uint8Array(arraybuffer);
              for (i = 0; i < len; i += 4) {
                encoded1 = lookup[base64.charCodeAt(i)];
                encoded2 = lookup[base64.charCodeAt(i + 1)];
                encoded3 = lookup[base64.charCodeAt(i + 2)];
                encoded4 = lookup[base64.charCodeAt(i + 3)];
                bytes[p++] = encoded1 << 2 | encoded2 >> 4;
                bytes[p++] = (encoded2 & 15) << 4 | encoded3 >> 2;
                bytes[p++] = (encoded3 & 3) << 6 | encoded4 & 63;
              }
              return arraybuffer;
            };
          }());  /***/
        },
        /* 34 */
        /***/
        function (module, exports) {
          /* WEBPACK VAR INJECTION */
          (function (global) {
            /**
             * Create a blob builder even when vendor prefixes exist
             */
            var BlobBuilder = global.BlobBuilder || global.WebKitBlobBuilder || global.MSBlobBuilder || global.MozBlobBuilder;
            /**
             * Check if Blob constructor is supported
             */
            var blobSupported = function () {
              try {
                var a = new Blob(['hi']);
                return a.size === 2;
              } catch (e) {
                return false;
              }
            }();
            /**
             * Check if Blob constructor supports ArrayBufferViews
             * Fails in Safari 6, so we need to map to ArrayBuffers there.
             */
            var blobSupportsArrayBufferView = blobSupported && function () {
              try {
                var b = new Blob([new Uint8Array([
                    1,
                    2
                  ])]);
                return b.size === 2;
              } catch (e) {
                return false;
              }
            }();
            /**
             * Check if BlobBuilder is supported
             */
            var blobBuilderSupported = BlobBuilder && BlobBuilder.prototype.append && BlobBuilder.prototype.getBlob;
            /**
             * Helper function that maps ArrayBufferViews to ArrayBuffers
             * Used by BlobBuilder constructor and old browsers that didn't
             * support it in the Blob constructor.
             */
            function mapArrayBufferViews(ary) {
              for (var i = 0; i < ary.length; i++) {
                var chunk = ary[i];
                if (chunk.buffer instanceof ArrayBuffer) {
                  var buf = chunk.buffer;
                  // if this is a subarray, make a copy so we only
                  // include the subarray region from the underlying buffer
                  if (chunk.byteLength !== buf.byteLength) {
                    var copy = new Uint8Array(chunk.byteLength);
                    copy.set(new Uint8Array(buf, chunk.byteOffset, chunk.byteLength));
                    buf = copy.buffer;
                  }
                  ary[i] = buf;
                }
              }
            }
            function BlobBuilderConstructor(ary, options) {
              options = options || {};
              var bb = new BlobBuilder();
              mapArrayBufferViews(ary);
              for (var i = 0; i < ary.length; i++) {
                bb.append(ary[i]);
              }
              return options.type ? bb.getBlob(options.type) : bb.getBlob();
            }
            ;
            function BlobConstructor(ary, options) {
              mapArrayBufferViews(ary);
              return new Blob(ary, options || {});
            }
            ;
            module.exports = function () {
              if (blobSupported) {
                return blobSupportsArrayBufferView ? global.Blob : BlobConstructor;
              } else if (blobBuilderSupported) {
                return BlobBuilderConstructor;
              } else {
                return undefined;
              }
            }();  /* WEBPACK VAR INJECTION */
          }.call(exports, function () {
            return this;
          }()));  /***/
        },
        /* 35 */
        /***/
        function (module, exports, __webpack_require__) {
          /**
           * Expose `Emitter`.
           */
          if (true) {
            module.exports = Emitter;
          }
          /**
           * Initialize a new `Emitter`.
           *
           * @api public
           */
          function Emitter(obj) {
            if (obj)
              return mixin(obj);
          }
          ;
          /**
           * Mixin the emitter properties.
           *
           * @param {Object} obj
           * @return {Object}
           * @api private
           */
          function mixin(obj) {
            for (var key in Emitter.prototype) {
              obj[key] = Emitter.prototype[key];
            }
            return obj;
          }
          /**
           * Listen on the given `event` with `fn`.
           *
           * @param {String} event
           * @param {Function} fn
           * @return {Emitter}
           * @api public
           */
          Emitter.prototype.on = Emitter.prototype.addEventListener = function (event, fn) {
            this._callbacks = this._callbacks || {};
            (this._callbacks['$' + event] = this._callbacks['$' + event] || []).push(fn);
            return this;
          };
          /**
           * Adds an `event` listener that will be invoked a single
           * time then automatically removed.
           *
           * @param {String} event
           * @param {Function} fn
           * @return {Emitter}
           * @api public
           */
          Emitter.prototype.once = function (event, fn) {
            function on() {
              this.off(event, on);
              fn.apply(this, arguments);
            }
            on.fn = fn;
            this.on(event, on);
            return this;
          };
          /**
           * Remove the given callback for `event` or all
           * registered callbacks.
           *
           * @param {String} event
           * @param {Function} fn
           * @return {Emitter}
           * @api public
           */
          Emitter.prototype.off = Emitter.prototype.removeListener = Emitter.prototype.removeAllListeners = Emitter.prototype.removeEventListener = function (event, fn) {
            this._callbacks = this._callbacks || {};
            // all
            if (0 == arguments.length) {
              this._callbacks = {};
              return this;
            }
            // specific event
            var callbacks = this._callbacks['$' + event];
            if (!callbacks)
              return this;
            // remove all handlers
            if (1 == arguments.length) {
              delete this._callbacks['$' + event];
              return this;
            }
            // remove specific handler
            var cb;
            for (var i = 0; i < callbacks.length; i++) {
              cb = callbacks[i];
              if (cb === fn || cb.fn === fn) {
                callbacks.splice(i, 1);
                break;
              }
            }
            return this;
          };
          /**
           * Emit `event` with the given args.
           *
           * @param {String} event
           * @param {Mixed} ...
           * @return {Emitter}
           */
          Emitter.prototype.emit = function (event) {
            this._callbacks = this._callbacks || {};
            var args = [].slice.call(arguments, 1), callbacks = this._callbacks['$' + event];
            if (callbacks) {
              callbacks = callbacks.slice(0);
              for (var i = 0, len = callbacks.length; i < len; ++i) {
                callbacks[i].apply(this, args);
              }
            }
            return this;
          };
          /**
           * Return array of callbacks for `event`.
           *
           * @param {String} event
           * @return {Array}
           * @api public
           */
          Emitter.prototype.listeners = function (event) {
            this._callbacks = this._callbacks || {};
            return this._callbacks['$' + event] || [];
          };
          /**
           * Check if this emitter has `event` handlers.
           *
           * @param {String} event
           * @return {Boolean}
           * @api public
           */
          Emitter.prototype.hasListeners = function (event) {
            return !!this.listeners(event).length;
          };  /***/
        },
        /* 36 */
        /***/
        function (module, exports) {
          /**
           * Compiles a querystring
           * Returns string representation of the object
           *
           * @param {Object}
           * @api private
           */
          exports.encode = function (obj) {
            var str = '';
            for (var i in obj) {
              if (obj.hasOwnProperty(i)) {
                if (str.length)
                  str += '&';
                str += encodeURIComponent(i) + '=' + encodeURIComponent(obj[i]);
              }
            }
            return str;
          };
          /**
           * Parses a simple querystring into an object
           *
           * @param {String} qs
           * @api private
           */
          exports.decode = function (qs) {
            var qry = {};
            var pairs = qs.split('&');
            for (var i = 0, l = pairs.length; i < l; i++) {
              var pair = pairs[i].split('=');
              qry[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
            }
            return qry;
          };  /***/
        },
        /* 37 */
        /***/
        function (module, exports) {
          module.exports = function (a, b) {
            var fn = function () {
            };
            fn.prototype = b.prototype;
            a.prototype = new fn();
            a.prototype.constructor = a;
          };  /***/
        },
        /* 38 */
        /***/
        function (module, exports) {
          'use strict';
          var alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_'.split(''), length = 64, map = {}, seed = 0, i = 0, prev;
          /**
           * Return a string representing the specified number.
           *
           * @param {Number} num The number to convert.
           * @returns {String} The string representation of the number.
           * @api public
           */
          function encode(num) {
            var encoded = '';
            do {
              encoded = alphabet[num % length] + encoded;
              num = Math.floor(num / length);
            } while (num > 0);
            return encoded;
          }
          /**
           * Return the integer value specified by the given string.
           *
           * @param {String} str The string to convert.
           * @returns {Number} The integer value represented by the string.
           * @api public
           */
          function decode(str) {
            var decoded = 0;
            for (i = 0; i < str.length; i++) {
              decoded = decoded * length + map[str.charAt(i)];
            }
            return decoded;
          }
          /**
           * Yeast: A tiny growing id generator.
           *
           * @returns {String} A unique id.
           * @api public
           */
          function yeast() {
            var now = encode(+new Date());
            if (now !== prev)
              return seed = 0, prev = now;
            return now + '.' + encode(seed++);
          }
          //
          // Map each character to its index.
          //
          for (; i < length; i++)
            map[alphabet[i]] = i;
          //
          // Expose the `yeast`, `encode` and `decode` functions.
          //
          yeast.encode = encode;
          yeast.decode = decode;
          module.exports = yeast;  /***/
        },
        /* 39 */
        /***/
        function (module, exports, __webpack_require__) {
          /* WEBPACK VAR INJECTION */
          (function (global) {
            /**
             * Module requirements.
             */
            var Polling = __webpack_require__(25);
            var inherit = __webpack_require__(37);
            /**
             * Module exports.
             */
            module.exports = JSONPPolling;
            /**
             * Cached regular expressions.
             */
            var rNewline = /\n/g;
            var rEscapedNewline = /\\n/g;
            /**
             * Global JSONP callbacks.
             */
            var callbacks;
            /**
             * Noop.
             */
            function empty() {
            }
            /**
             * JSONP Polling constructor.
             *
             * @param {Object} opts.
             * @api public
             */
            function JSONPPolling(opts) {
              Polling.call(this, opts);
              this.query = this.query || {};
              // define global callbacks array if not present
              // we do this here (lazily) to avoid unneeded global pollution
              if (!callbacks) {
                // we need to consider multiple engines in the same page
                if (!global.___eio)
                  global.___eio = [];
                callbacks = global.___eio;
              }
              // callback identifier
              this.index = callbacks.length;
              // add callback to jsonp global
              var self = this;
              callbacks.push(function (msg) {
                self.onData(msg);
              });
              // append to query string
              this.query.j = this.index;
              // prevent spurious errors from being emitted when the window is unloaded
              if (global.document && global.addEventListener) {
                global.addEventListener('beforeunload', function () {
                  if (self.script)
                    self.script.onerror = empty;
                }, false);
              }
            }
            /**
             * Inherits from Polling.
             */
            inherit(JSONPPolling, Polling);
            /*
             * JSONP only supports binary as base64 encoded strings
             */
            JSONPPolling.prototype.supportsBinary = false;
            /**
             * Closes the socket.
             *
             * @api private
             */
            JSONPPolling.prototype.doClose = function () {
              if (this.script) {
                this.script.parentNode.removeChild(this.script);
                this.script = null;
              }
              if (this.form) {
                this.form.parentNode.removeChild(this.form);
                this.form = null;
                this.iframe = null;
              }
              Polling.prototype.doClose.call(this);
            };
            /**
             * Starts a poll cycle.
             *
             * @api private
             */
            JSONPPolling.prototype.doPoll = function () {
              var self = this;
              var script = document.createElement('script');
              if (this.script) {
                this.script.parentNode.removeChild(this.script);
                this.script = null;
              }
              script.async = true;
              script.src = this.uri();
              script.onerror = function (e) {
                self.onError('jsonp poll error', e);
              };
              var insertAt = document.getElementsByTagName('script')[0];
              if (insertAt) {
                insertAt.parentNode.insertBefore(script, insertAt);
              } else {
                (document.head || document.body).appendChild(script);
              }
              this.script = script;
              var isUAgecko = 'undefined' !== typeof navigator && /gecko/i.test(navigator.userAgent);
              if (isUAgecko) {
                setTimeout(function () {
                  var iframe = document.createElement('iframe');
                  document.body.appendChild(iframe);
                  document.body.removeChild(iframe);
                }, 100);
              }
            };
            /**
             * Writes with a hidden iframe.
             *
             * @param {String} data to send
             * @param {Function} called upon flush.
             * @api private
             */
            JSONPPolling.prototype.doWrite = function (data, fn) {
              var self = this;
              if (!this.form) {
                var form = document.createElement('form');
                var area = document.createElement('textarea');
                var id = this.iframeId = 'eio_iframe_' + this.index;
                var iframe;
                form.className = 'socketio';
                form.style.position = 'absolute';
                form.style.top = '-1000px';
                form.style.left = '-1000px';
                form.target = id;
                form.method = 'POST';
                form.setAttribute('accept-charset', 'utf-8');
                area.name = 'd';
                form.appendChild(area);
                document.body.appendChild(form);
                this.form = form;
                this.area = area;
              }
              this.form.action = this.uri();
              function complete() {
                initIframe();
                fn();
              }
              function initIframe() {
                if (self.iframe) {
                  try {
                    self.form.removeChild(self.iframe);
                  } catch (e) {
                    self.onError('jsonp polling iframe removal error', e);
                  }
                }
                try {
                  // ie6 dynamic iframes with target="" support (thanks Chris Lambacher)
                  var html = '<iframe src="javascript:0" name="' + self.iframeId + '">';
                  iframe = document.createElement(html);
                } catch (e) {
                  iframe = document.createElement('iframe');
                  iframe.name = self.iframeId;
                  iframe.src = 'javascript:0';
                }
                iframe.id = self.iframeId;
                self.form.appendChild(iframe);
                self.iframe = iframe;
              }
              initIframe();
              // escape \n to prevent it from being converted into \r\n by some UAs
              // double escaping is required for escaped new lines because unescaping of new lines can be done safely on server-side
              data = data.replace(rEscapedNewline, '\\\n');
              this.area.value = data.replace(rNewline, '\\n');
              try {
                this.form.submit();
              } catch (e) {
              }
              if (this.iframe.attachEvent) {
                this.iframe.onreadystatechange = function () {
                  if (self.iframe.readyState === 'complete') {
                    complete();
                  }
                };
              } else {
                this.iframe.onload = complete;
              }
            };  /* WEBPACK VAR INJECTION */
          }.call(exports, function () {
            return this;
          }()));  /***/
        },
        /* 40 */
        /***/
        function (module, exports, __webpack_require__) {
          /* WEBPACK VAR INJECTION */
          (function (global) {
            /**
             * Module dependencies.
             */
            var Transport = __webpack_require__(26);
            var parser = __webpack_require__(27);
            var parseqs = __webpack_require__(36);
            var inherit = __webpack_require__(37);
            var yeast = __webpack_require__(38);
            var debug = __webpack_require__(3)('engine.io-client:websocket');
            var BrowserWebSocket = global.WebSocket || global.MozWebSocket;
            var NodeWebSocket;
            if (typeof window === 'undefined') {
              try {
                NodeWebSocket = __webpack_require__(41);
              } catch (e) {
              }
            }
            /**
             * Get either the `WebSocket` or `MozWebSocket` globals
             * in the browser or try to resolve WebSocket-compatible
             * interface exposed by `ws` for Node-like environment.
             */
            var WebSocket = BrowserWebSocket;
            if (!WebSocket && typeof window === 'undefined') {
              WebSocket = NodeWebSocket;
            }
            /**
             * Module exports.
             */
            module.exports = WS;
            /**
             * WebSocket transport constructor.
             *
             * @api {Object} connection options
             * @api public
             */
            function WS(opts) {
              var forceBase64 = opts && opts.forceBase64;
              if (forceBase64) {
                this.supportsBinary = false;
              }
              this.perMessageDeflate = opts.perMessageDeflate;
              this.usingBrowserWebSocket = BrowserWebSocket && !opts.forceNode;
              if (!this.usingBrowserWebSocket) {
                WebSocket = NodeWebSocket;
              }
              Transport.call(this, opts);
            }
            /**
             * Inherits from Transport.
             */
            inherit(WS, Transport);
            /**
             * Transport name.
             *
             * @api public
             */
            WS.prototype.name = 'websocket';
            /*
             * WebSockets support binary
             */
            WS.prototype.supportsBinary = true;
            /**
             * Opens socket.
             *
             * @api private
             */
            WS.prototype.doOpen = function () {
              if (!this.check()) {
                // let probe timeout
                return;
              }
              var uri = this.uri();
              var protocols = void 0;
              var opts = {
                agent: this.agent,
                perMessageDeflate: this.perMessageDeflate
              };
              // SSL options for Node.js client
              opts.pfx = this.pfx;
              opts.key = this.key;
              opts.passphrase = this.passphrase;
              opts.cert = this.cert;
              opts.ca = this.ca;
              opts.ciphers = this.ciphers;
              opts.rejectUnauthorized = this.rejectUnauthorized;
              if (this.extraHeaders) {
                opts.headers = this.extraHeaders;
              }
              if (this.localAddress) {
                opts.localAddress = this.localAddress;
              }
              try {
                this.ws = this.usingBrowserWebSocket ? new WebSocket(uri) : new WebSocket(uri, protocols, opts);
              } catch (err) {
                return this.emit('error', err);
              }
              if (this.ws.binaryType === undefined) {
                this.supportsBinary = false;
              }
              if (this.ws.supports && this.ws.supports.binary) {
                this.supportsBinary = true;
                this.ws.binaryType = 'nodebuffer';
              } else {
                this.ws.binaryType = 'arraybuffer';
              }
              this.addEventListeners();
            };
            /**
             * Adds event listeners to the socket
             *
             * @api private
             */
            WS.prototype.addEventListeners = function () {
              var self = this;
              this.ws.onopen = function () {
                self.onOpen();
              };
              this.ws.onclose = function () {
                self.onClose();
              };
              this.ws.onmessage = function (ev) {
                self.onData(ev.data);
              };
              this.ws.onerror = function (e) {
                self.onError('websocket error', e);
              };
            };
            /**
             * Writes data to socket.
             *
             * @param {Array} array of packets.
             * @api private
             */
            WS.prototype.write = function (packets) {
              var self = this;
              this.writable = false;
              // encodePacket efficient as it uses WS framing
              // no need for encodePayload
              var total = packets.length;
              for (var i = 0, l = total; i < l; i++) {
                (function (packet) {
                  parser.encodePacket(packet, self.supportsBinary, function (data) {
                    if (!self.usingBrowserWebSocket) {
                      // always create a new object (GH-437)
                      var opts = {};
                      if (packet.options) {
                        opts.compress = packet.options.compress;
                      }
                      if (self.perMessageDeflate) {
                        var len = 'string' === typeof data ? global.Buffer.byteLength(data) : data.length;
                        if (len < self.perMessageDeflate.threshold) {
                          opts.compress = false;
                        }
                      }
                    }
                    // Sometimes the websocket has already been closed but the browser didn't
                    // have a chance of informing us about it yet, in that case send will
                    // throw an error
                    try {
                      if (self.usingBrowserWebSocket) {
                        // TypeError is thrown when passing the second argument on Safari
                        self.ws.send(data);
                      } else {
                        self.ws.send(data, opts);
                      }
                    } catch (e) {
                      debug('websocket closed before onclose event');
                    }
                    --total || done();
                  });
                }(packets[i]));
              }
              function done() {
                self.emit('flush');
                // fake drain
                // defer to next tick to allow Socket to clear writeBuffer
                setTimeout(function () {
                  self.writable = true;
                  self.emit('drain');
                }, 0);
              }
            };
            /**
             * Called upon close
             *
             * @api private
             */
            WS.prototype.onClose = function () {
              Transport.prototype.onClose.call(this);
            };
            /**
             * Closes socket.
             *
             * @api private
             */
            WS.prototype.doClose = function () {
              if (typeof this.ws !== 'undefined') {
                this.ws.close();
              }
            };
            /**
             * Generates uri for connection.
             *
             * @api private
             */
            WS.prototype.uri = function () {
              var query = this.query || {};
              var schema = this.secure ? 'wss' : 'ws';
              var port = '';
              // avoid port if default for schema
              if (this.port && ('wss' === schema && Number(this.port) !== 443 || 'ws' === schema && Number(this.port) !== 80)) {
                port = ':' + this.port;
              }
              // append timestamp to URI
              if (this.timestampRequests) {
                query[this.timestampParam] = yeast();
              }
              // communicate binary support capabilities
              if (!this.supportsBinary) {
                query.b64 = 1;
              }
              query = parseqs.encode(query);
              // prepend ? to query
              if (query.length) {
                query = '?' + query;
              }
              var ipv6 = this.hostname.indexOf(':') !== -1;
              return schema + '://' + (ipv6 ? '[' + this.hostname + ']' : this.hostname) + port + this.path + query;
            };
            /**
             * Feature detection for WebSocket.
             *
             * @return {Boolean} whether this transport is available.
             * @api public
             */
            WS.prototype.check = function () {
              return !!WebSocket && !('__initialize' in WebSocket && this.name === WS.prototype.name);
            };  /* WEBPACK VAR INJECTION */
          }.call(exports, function () {
            return this;
          }()));  /***/
        },
        /* 41 */
        /***/
        function (module, exports) {
        },
        /* 42 */
        /***/
        function (module, exports) {
          var indexOf = [].indexOf;
          module.exports = function (arr, obj) {
            if (indexOf)
              return arr.indexOf(obj);
            for (var i = 0; i < arr.length; ++i) {
              if (arr[i] === obj)
                return i;
            }
            return -1;
          };  /***/
        },
        /* 43 */
        /***/
        function (module, exports) {
          /* WEBPACK VAR INJECTION */
          (function (global) {
            /**
             * JSON parse.
             *
             * @see Based on jQuery#parseJSON (MIT) and JSON2
             * @api private
             */
            var rvalidchars = /^[\],:{}\s]*$/;
            var rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
            var rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
            var rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g;
            var rtrimLeft = /^\s+/;
            var rtrimRight = /\s+$/;
            module.exports = function parsejson(data) {
              if ('string' != typeof data || !data) {
                return null;
              }
              data = data.replace(rtrimLeft, '').replace(rtrimRight, '');
              // Attempt to parse using the native JSON parser first
              if (global.JSON && JSON.parse) {
                return JSON.parse(data);
              }
              if (rvalidchars.test(data.replace(rvalidescape, '@').replace(rvalidtokens, ']').replace(rvalidbraces, ''))) {
                return new Function('return ' + data)();
              }
            };  /* WEBPACK VAR INJECTION */
          }.call(exports, function () {
            return this;
          }()));  /***/
        },
        /* 44 */
        /***/
        function (module, exports, __webpack_require__) {
          'use strict';
          /**
           * Module dependencies.
           */
          var parser = __webpack_require__(7);
          var Emitter = __webpack_require__(35);
          var toArray = __webpack_require__(45);
          var on = __webpack_require__(46);
          var bind = __webpack_require__(47);
          var debug = __webpack_require__(3)('socket.io-client:socket');
          var hasBin = __webpack_require__(29);
          /**
           * Module exports.
           */
          module.exports = exports = Socket;
          /**
           * Internal events (blacklisted).
           * These events can't be emitted by the user.
           *
           * @api private
           */
          var events = {
            connect: 1,
            connect_error: 1,
            connect_timeout: 1,
            connecting: 1,
            disconnect: 1,
            error: 1,
            reconnect: 1,
            reconnect_attempt: 1,
            reconnect_failed: 1,
            reconnect_error: 1,
            reconnecting: 1,
            ping: 1,
            pong: 1
          };
          /**
           * Shortcut to `Emitter#emit`.
           */
          var emit = Emitter.prototype.emit;
          /**
           * `Socket` constructor.
           *
           * @api public
           */
          function Socket(io, nsp, opts) {
            this.io = io;
            this.nsp = nsp;
            this.json = this;
            // compat
            this.ids = 0;
            this.acks = {};
            this.receiveBuffer = [];
            this.sendBuffer = [];
            this.connected = false;
            this.disconnected = true;
            if (opts && opts.query) {
              this.query = opts.query;
            }
            if (this.io.autoConnect)
              this.open();
          }
          /**
           * Mix in `Emitter`.
           */
          Emitter(Socket.prototype);
          /**
           * Subscribe to open, close and packet events
           *
           * @api private
           */
          Socket.prototype.subEvents = function () {
            if (this.subs)
              return;
            var io = this.io;
            this.subs = [
              on(io, 'open', bind(this, 'onopen')),
              on(io, 'packet', bind(this, 'onpacket')),
              on(io, 'close', bind(this, 'onclose'))
            ];
          };
          /**
           * "Opens" the socket.
           *
           * @api public
           */
          Socket.prototype.open = Socket.prototype.connect = function () {
            if (this.connected)
              return this;
            this.subEvents();
            this.io.open();
            // ensure open
            if ('open' === this.io.readyState)
              this.onopen();
            this.emit('connecting');
            return this;
          };
          /**
           * Sends a `message` event.
           *
           * @return {Socket} self
           * @api public
           */
          Socket.prototype.send = function () {
            var args = toArray(arguments);
            args.unshift('message');
            this.emit.apply(this, args);
            return this;
          };
          /**
           * Override `emit`.
           * If the event is in `events`, it's emitted normally.
           *
           * @param {String} event name
           * @return {Socket} self
           * @api public
           */
          Socket.prototype.emit = function (ev) {
            if (events.hasOwnProperty(ev)) {
              emit.apply(this, arguments);
              return this;
            }
            var args = toArray(arguments);
            var parserType = parser.EVENT;
            // default
            if (hasBin(args)) {
              parserType = parser.BINARY_EVENT;
            }
            // binary
            var packet = {
              type: parserType,
              data: args
            };
            packet.options = {};
            packet.options.compress = !this.flags || false !== this.flags.compress;
            // event ack callback
            if ('function' === typeof args[args.length - 1]) {
              debug('emitting packet with ack id %d', this.ids);
              this.acks[this.ids] = args.pop();
              packet.id = this.ids++;
            }
            if (this.connected) {
              this.packet(packet);
            } else {
              this.sendBuffer.push(packet);
            }
            delete this.flags;
            return this;
          };
          /**
           * Sends a packet.
           *
           * @param {Object} packet
           * @api private
           */
          Socket.prototype.packet = function (packet) {
            packet.nsp = this.nsp;
            this.io.packet(packet);
          };
          /**
           * Called upon engine `open`.
           *
           * @api private
           */
          Socket.prototype.onopen = function () {
            debug('transport is open - connecting');
            // write connect packet if necessary
            if ('/' !== this.nsp) {
              if (this.query) {
                this.packet({
                  type: parser.CONNECT,
                  query: this.query
                });
              } else {
                this.packet({ type: parser.CONNECT });
              }
            }
          };
          /**
           * Called upon engine `close`.
           *
           * @param {String} reason
           * @api private
           */
          Socket.prototype.onclose = function (reason) {
            debug('close (%s)', reason);
            this.connected = false;
            this.disconnected = true;
            delete this.id;
            this.emit('disconnect', reason);
          };
          /**
           * Called with socket packet.
           *
           * @param {Object} packet
           * @api private
           */
          Socket.prototype.onpacket = function (packet) {
            if (packet.nsp !== this.nsp)
              return;
            switch (packet.type) {
            case parser.CONNECT:
              this.onconnect();
              break;
            case parser.EVENT:
              this.onevent(packet);
              break;
            case parser.BINARY_EVENT:
              this.onevent(packet);
              break;
            case parser.ACK:
              this.onack(packet);
              break;
            case parser.BINARY_ACK:
              this.onack(packet);
              break;
            case parser.DISCONNECT:
              this.ondisconnect();
              break;
            case parser.ERROR:
              this.emit('error', packet.data);
              break;
            }
          };
          /**
           * Called upon a server event.
           *
           * @param {Object} packet
           * @api private
           */
          Socket.prototype.onevent = function (packet) {
            var args = packet.data || [];
            debug('emitting event %j', args);
            if (null != packet.id) {
              debug('attaching ack callback to event');
              args.push(this.ack(packet.id));
            }
            if (this.connected) {
              emit.apply(this, args);
            } else {
              this.receiveBuffer.push(args);
            }
          };
          /**
           * Produces an ack callback to emit with an event.
           *
           * @api private
           */
          Socket.prototype.ack = function (id) {
            var self = this;
            var sent = false;
            return function () {
              // prevent double callbacks
              if (sent)
                return;
              sent = true;
              var args = toArray(arguments);
              debug('sending ack %j', args);
              var type = hasBin(args) ? parser.BINARY_ACK : parser.ACK;
              self.packet({
                type: type,
                id: id,
                data: args
              });
            };
          };
          /**
           * Called upon a server acknowlegement.
           *
           * @param {Object} packet
           * @api private
           */
          Socket.prototype.onack = function (packet) {
            var ack = this.acks[packet.id];
            if ('function' === typeof ack) {
              debug('calling ack %s with %j', packet.id, packet.data);
              ack.apply(this, packet.data);
              delete this.acks[packet.id];
            } else {
              debug('bad ack %s', packet.id);
            }
          };
          /**
           * Called upon server connect.
           *
           * @api private
           */
          Socket.prototype.onconnect = function () {
            this.connected = true;
            this.disconnected = false;
            this.emit('connect');
            this.emitBuffered();
          };
          /**
           * Emit buffered events (received and emitted).
           *
           * @api private
           */
          Socket.prototype.emitBuffered = function () {
            var i;
            for (i = 0; i < this.receiveBuffer.length; i++) {
              emit.apply(this, this.receiveBuffer[i]);
            }
            this.receiveBuffer = [];
            for (i = 0; i < this.sendBuffer.length; i++) {
              this.packet(this.sendBuffer[i]);
            }
            this.sendBuffer = [];
          };
          /**
           * Called upon server disconnect.
           *
           * @api private
           */
          Socket.prototype.ondisconnect = function () {
            debug('server disconnect (%s)', this.nsp);
            this.destroy();
            this.onclose('io server disconnect');
          };
          /**
           * Called upon forced client/server side disconnections,
           * this method ensures the manager stops tracking us and
           * that reconnections don't get triggered for this.
           *
           * @api private.
           */
          Socket.prototype.destroy = function () {
            if (this.subs) {
              // clean subscriptions to avoid reconnections
              for (var i = 0; i < this.subs.length; i++) {
                this.subs[i].destroy();
              }
              this.subs = null;
            }
            this.io.destroy(this);
          };
          /**
           * Disconnects the socket manually.
           *
           * @return {Socket} self
           * @api public
           */
          Socket.prototype.close = Socket.prototype.disconnect = function () {
            if (this.connected) {
              debug('performing disconnect (%s)', this.nsp);
              this.packet({ type: parser.DISCONNECT });
            }
            // remove socket from pool
            this.destroy();
            if (this.connected) {
              // fire events
              this.onclose('io client disconnect');
            }
            return this;
          };
          /**
           * Sets the compress flag.
           *
           * @param {Boolean} if `true`, compresses the sending data
           * @return {Socket} self
           * @api public
           */
          Socket.prototype.compress = function (compress) {
            this.flags = this.flags || {};
            this.flags.compress = compress;
            return this;
          };  /***/
        },
        /* 45 */
        /***/
        function (module, exports) {
          module.exports = toArray;
          function toArray(list, index) {
            var array = [];
            index = index || 0;
            for (var i = index || 0; i < list.length; i++) {
              array[i - index] = list[i];
            }
            return array;
          }  /***/
        },
        /* 46 */
        /***/
        function (module, exports) {
          'use strict';
          /**
           * Module exports.
           */
          module.exports = on;
          /**
           * Helper for subscriptions.
           *
           * @param {Object|EventEmitter} obj with `Emitter` mixin or `EventEmitter`
           * @param {String} event name
           * @param {Function} callback
           * @api public
           */
          function on(obj, ev, fn) {
            obj.on(ev, fn);
            return {
              destroy: function destroy() {
                obj.removeListener(ev, fn);
              }
            };
          }  /***/
        },
        /* 47 */
        /***/
        function (module, exports) {
          /**
           * Slice reference.
           */
          var slice = [].slice;
          /**
           * Bind `obj` to `fn`.
           *
           * @param {Object} obj
           * @param {Function|String} fn or string
           * @return {Function}
           * @api public
           */
          module.exports = function (obj, fn) {
            if ('string' == typeof fn)
              fn = obj[fn];
            if ('function' != typeof fn)
              throw new Error('bind() requires a function');
            var args = slice.call(arguments, 2);
            return function () {
              return fn.apply(obj, args.concat(slice.call(arguments)));
            };
          };  /***/
        },
        /* 48 */
        /***/
        function (module, exports) {
          /**
           * Expose `Backoff`.
           */
          module.exports = Backoff;
          /**
           * Initialize backoff timer with `opts`.
           *
           * - `min` initial timeout in milliseconds [100]
           * - `max` max timeout [10000]
           * - `jitter` [0]
           * - `factor` [2]
           *
           * @param {Object} opts
           * @api public
           */
          function Backoff(opts) {
            opts = opts || {};
            this.ms = opts.min || 100;
            this.max = opts.max || 10000;
            this.factor = opts.factor || 2;
            this.jitter = opts.jitter > 0 && opts.jitter <= 1 ? opts.jitter : 0;
            this.attempts = 0;
          }
          /**
           * Return the backoff duration.
           *
           * @return {Number}
           * @api public
           */
          Backoff.prototype.duration = function () {
            var ms = this.ms * Math.pow(this.factor, this.attempts++);
            if (this.jitter) {
              var rand = Math.random();
              var deviation = Math.floor(rand * this.jitter * ms);
              ms = (Math.floor(rand * 10) & 1) == 0 ? ms - deviation : ms + deviation;
            }
            return Math.min(ms, this.max) | 0;
          };
          /**
           * Reset the number of attempts.
           *
           * @api public
           */
          Backoff.prototype.reset = function () {
            this.attempts = 0;
          };
          /**
           * Set the minimum duration
           *
           * @api public
           */
          Backoff.prototype.setMin = function (min) {
            this.ms = min;
          };
          /**
           * Set the maximum duration
           *
           * @api public
           */
          Backoff.prototype.setMax = function (max) {
            this.max = max;
          };
          /**
           * Set the jitter
           *
           * @api public
           */
          Backoff.prototype.setJitter = function (jitter) {
            this.jitter = jitter;
          };  /***/
        }
      ]);
    }));
    ;
    (function () {
      var _ = include('underscore');
      var MSGS = {
        TYPE_MISMATCH: 'Type does not match this collection type',
        KEY_ALREADY_EXISTS: 'Collection key already exists. ',
        KEY_DOES_NOT_EXIST: 'Collection key does not exist in this collection.',
        MUST_GIVE_KEY: 'Collection missing key.',
        MUST_GIVE_VALUE: 'Collection missing value.',
        KEY_NOT_VALID: 'Key must be of type String or Number'
      };
      /**
       * @classdesc Use in_type to make this a typed Collection.
       * @param {string=} in_name a friendly name to describe this collection. If undefined
       * the collection will have a default "Untitled Collection" assigned to its name.
       * @param {function=} in_type optional parameter pointing to the constructor
       * of a type this Collection will host.
       * @constructor
       *
       * @alias LYNX.Utils.Datastructures.Collection
       */
      var Collection = function (in_name, in_type) {
        in_name = in_name || 'Untitled Collection';
        this._type = in_type;
        this._name = in_name;
        this._items = {};
        /**
         * A ordered arrray of keys
         * @type {Array<String|Number>}
         */
        this._order = [];
        this._firstItem = undefined;
        // Pass-thru binding handles
        this.onAdd = function (in_key, in_value) {
        };
        this.onRemove = function (in_key, in_value) {
        };
        this.onClear = function (in_items) {
        };
        var that = this;
        Object.defineProperty(this, 'items', {
          get: function () {
            return that._items;
          }
        });
      };
      Collection.prototype.constructor = Collection;
      /**
       * @param {Number|String|CONSTANTS.TUID} in_key Key to store the value under
       * @param {object} in_value Value to store in the collection
       * @return {object} Return the value passed in
       */
      Collection.prototype.add = function (in_key, in_value) {
        this._checkType(in_value);
        this._checkKey(in_key);
        this._checkIsNewKey(in_key);
        this._items[in_key] = in_value;
        this._order.push(in_key);
        this.onAdd(in_key, in_value);
        return in_value;
      };
      /**
       * Bulk add items.
       * @param {object} in_items List of key-value pairs to be stored in the collection
       * @return {object} this collection after add
       */
      Collection.prototype.bulkAdd = function (in_items) {
        var that = this;
        _.each(in_items, function (item, key) {
          that.add(key, item);
        });
        return this;
      };
      /**
       * Bulk remove items.
       * @param {object} in_items List of key-value items to be removed
       * @return {object} this collection after add
       */
      Collection.prototype.bulkRemove = function (in_items) {
        var that = this;
        _.each(in_items, function (item, key) {
          that.remove(key);
        });
        return this;
      };
      /**
       * Test if this collection is empty
       * @return {Boolean} true if empty, false otherwise
       * */
      Collection.prototype.isEmpty = function () {
        return _.isEmpty(this._items);
      };
      /**
       * Return the first item in the collection, null if empty
       * @return {object|undefined} - first item, or undefined if empty
       * */
      Collection.prototype.getFirstItem = function () {
        return this._items[this._order[0]];
      };
      /**
       * Return the last item in the collection, null if empty
       * @return {object|undefined} - last item, or undefined if empty
       * */
      Collection.prototype.getLastItem = function () {
        return this._items[_.last(this._order)];
      };
      /**
       * @return {function|undefined} Returns the type of collection (Array, etc.)
       */
      Collection.prototype.getType = function () {
        return this._type;
      };
      /**
       * @return {string} Returns the name of the collection
       */
      Collection.prototype.getName = function () {
        return this._name;
      };
      /**
       * Join another collection to this one
       * @param {Collection} in_collection Collection to join
       * @return {Collection} Return this collection after it has been joined
       */
      Collection.prototype.joinInPlace = function (in_collection) {
        if (in_collection.getType() !== this.getType()) {
          throw new Error('Input object type doesn\'t match this collection\'s type');
        }
        return this.bulkAdd(in_collection.items);
      };
      /**
       * Filter out by function
       * @param {function(string, *)} in_filterFunction with arguments key and item
       * @return {Collection} New filtered collection
       */
      Collection.prototype.filter = function (in_filterFunction) {
        var rtn = new Collection();
        var keeper;
        var filterCb = function (in_key, in_item) {
          keeper = in_filterFunction(in_key, in_item);
          if (keeper) {
            rtn.add(in_key, in_item);
          }
        };
        this.iterate(filterCb);
        return rtn;
      };
      /**
       * Filter out all keys NOT matching the in_filterKey
       * @param {*|Array} in_filterKey a single key or an array of keys, if the
       * item matches any of the keys it will be filtered in.
       * @return {Collection} New filtered collection with all the items
       * matching at least one key.
       */
      Collection.prototype.filterByKey = function (in_filterKey) {
        var rtn = new Collection();
        var filterCb;
        if (_.isArray(in_filterKey)) {
          filterCb = function (in_key, in_item) {
            if (in_filterKey.indexOf(in_key) >= 0) {
              rtn.add(in_key, in_item);
            }
          };
        } else {
          // if in_filterKey is an array
          filterCb = function (in_key, in_item) {
            if (in_key === in_filterKey) {
              rtn.add(in_key, in_item);
            }
          };
        }
        this.iterate(filterCb);
        return rtn;
      };
      /**
       * Filter out all keys NOT matching the in_filterValue
       * @param {object} in_filterValue Value to filter on
       * @return {Collection} Return a filtered collection
       */
      Collection.prototype.filterByValue = function (in_filterValue) {
        var rtn = new Collection();
        var filterCb = function (in_key, in_item) {
          if (in_item === in_filterValue) {
            rtn.add(in_key, in_item);
          }
        };
        this.iterate(filterCb);
        return rtn;
      };
      /**
       * Remove an item from this Collection. This method returns a Boolean indicating
       * the success or failure of the removal. This is practical because if we were
       * to throw an error when the key doesn't exist, it would require additional
       * checks by the caller to make sure this key exists prior to removal. Which
       * would make the attempt of removal more verbose and also costly because the
       * caller would have to keep a list – somewhere else – of the things he can
       * and cannot remove.
       *
       * @param {String|Number} in_key the key we wish to remove
       * @return {Boolean} true if the key exists and was removed, false otherwise.
       */
      Collection.prototype.remove = function (in_key) {
        if (!this.has(in_key)) {
          return false;
        }
        var remember = this._items[in_key];
        delete this._items[in_key];
        this._order.splice(this._order.indexOf(in_key), 1);
        this.onRemove(in_key, remember);
        return true;
      };
      /**
       * Return the number of items in this Collection
       * @return {Number} the number of items in the collection
       */
      Collection.prototype.getCount = function () {
        return this._order.length;
      };
      /**
       * Returns this collection as an ordered Array
       * @return {Array} Array including the values
       */
      Collection.prototype.getAsArray = function () {
        var rtnArr = new Array(this.getCount());
        for (var i = 0; i < this._order.length; i++) {
          rtnArr[i] = this._items[this._order[i]];
        }
        return rtnArr;
      };
      /**
       * @param {string|number|CONSTANTS.GUID} in_key the key we are looking for
       * @return {boolean} true if the item exists
       */
      Collection.prototype.has = function (in_key) {
        return this._items.hasOwnProperty(in_key);
      };
      /**
       * Return an item associated with the given key
       * @param {string|number|CONSTANTS.GUID} in_key the key for the item in this
       * Collection
       * @return {*} The item
       */
      Collection.prototype.item = function (in_key) {
        return this._items[in_key];
      };
      /**
       * Checks if in_value's type is equal to this Collection type. If this collection
       * has no type set, the check will pass.
       *
       * @param {*} in_value A value that is equal to the type managed by this collection.
       * @return {boolean} Return true if the type is a valid type for this
       * collection, throw otherwise.
       * @private
       */
      Collection.prototype._checkType = function (in_value) {
        if (this._type && !(in_value instanceof this._type)) {
          throw new Error(MSGS.TYPE_MISMATCH);
        } else {
          return true;
        }
      };
      /**
       * Checks if this is a new key in the collection. Throw if already exists.
       * @param {Number|String} in_key The key to check against
       * @return {Boolean} true if key is new
       * @private
       */
      Collection.prototype._checkIsNewKey = function (in_key) {
        if (this.has(in_key)) {
          throw new Error(MSGS.KEY_ALREADY_EXISTS + in_key);
        }
        return true;
      };
      /**
       * Checks if the key exists in the collection. Throw if not.
       * @param {Number|String} in_key the key to check against
       * @return {Boolean} true if key exists
       * @private
       */
      Collection.prototype._checkKeyExists = function (in_key) {
        if (!this.has(in_key)) {
          throw new Error(MSGS.KEY_DOES_NOT_EXIST, in_key);
        }
        return true;
      };
      /**
       * Checks in_key validity. Throw if it's invalid, true otherwise.
       * @param {Number|String} in_key Key to check against
       * @return {Boolean} true if the key is valid
       * @private
       */
      Collection.prototype._checkKey = function (in_key) {
        if (!_.isNumber(in_key) && !_.isString(in_key)) {
          throw new Error(MSGS.KEY_NOT_VALID);
        }
        return true;
      };
      /**
       * Set an existing key to a value. If key doesn't exist this call will throw
       * an error.
       * @param {String|Number} in_key the key we want to modify
       * @param {*} in_value the value we are to set at this key
       * @return {*} returns the value passed in
       */
      Collection.prototype.set = function (in_key, in_value) {
        this._checkType(in_value);
        this._checkKey(in_key);
        this._checkKeyExists(in_key);
        this._items[in_key] = in_value;
        return in_value;
      };
      /**
       * Iterate over this collection and run the callback with passing the key and
       * item in the iteration loop.
       * @param {function(in_key, in_item)} in_callback a function that we will call on each item
       * of this collection.
       */
      Collection.prototype.iterate = function (in_callback) {
        var its = this._items;
        _.each(its, function (item, key) {
          in_callback(key, item);
        });
      };
      /**
       * @return {object} Return an object containing the items of this collection
       */
      Collection.prototype.getItems = function () {
        var result = {};
        _.each(this._items, function (item, key) {
          result[key] = item;
        });
        return result;
      };
      /**
       * Return the list of keys
       * @return {Array} List of keys
       */
      Collection.prototype.getKeys = function () {
        return Object.keys(this._items);
      };
      /**
       * Method used to get the first element in the collection along with its key.
       * @return {object} {item, key}
       */
      Collection.prototype.peak = function () {
        return {
          item: this._items[this._order[0]],
          key: this._order[0]
        };
      };
      /**
       * Clear this collection
       * @return {Collection} this collection
       */
      Collection.prototype.clear = function () {
        if (_.isEmpty(this._items)) {
          return this;
        }
        this.onClear(this._items);
        // Best to just iterate through and remove everything, so that OnRemove
        // handlers are called.
        var that = this;
        _.each(this.getKeys(), function (key) {
          that.remove(key);
        });
        return this;
      };
      /**
       * Copy the items of in_collection to this collection.
       * @param {Collection} in_collection the collection we want to
       * copy from.
       * @return {Collection} new Collection
       */
      Collection.prototype.clone = function () {
        var newCol = new Collection(this._name, this._type);
        newCol.bulkAdd(this._items);
        return newCol;
      };
      /**
       * Copy the items of in_collection to this collection.
       * @param {Collection} in_collection the collection we want to
       * copy from.
       */
      Collection.prototype.copy = function (in_collection) {
        this.clear();
        var its = in_collection.items;
        var that = this;
        _.each(its, function (item, key) {
          that.add(key, item);
        });
      };
      Object.defineProperty(Collection.prototype, 'values', {
        get: function () {
          return this.getAsArray();
        }
      });
      Object.defineProperty(Collection.prototype, 'keys', {
        get: function () {
          return Object.keys(this._items);
        }
      });
      exportModule('LYNX.Utils.Datastructures.Collection', Collection);
    }());
    (function () {
      var Collection = include('LYNX.Utils.Datastructures.Collection');
      var _ = include('underscore');
      /**
       * A sorted collection class.
       * @param {string=} in_name a friendly name to describe this collection. If undefined
       * the collection will have a default "Untitled Collection" assigned to its name.
       * @param {function=} in_type optional parameter pointing to the constructor
       * of a type this Collection will host.
       * @constructor
       * @alias LYNX.Utils.Datastructures.SortedCollection
       * @extends LYNX.Utils.Datastructures.Collection
       */
      var SortedCollection = function (in_name, in_type) {
        Collection.call(this, in_name, in_type);
        this._comparisonFunction = undefined;
        this._sortedKeys = [];
      };
      SortedCollection.prototype = Object.create(Collection.prototype);
      /**
       * Set the comparison function. By default the keys will be sorted wrt their ASCII code.
       * @param {function(string, string)} in_fn The function to compare two entries.
       *  the return value of this function must convey to the following cases:
       *  - if a > b, then the return value must be greater than 0
       *  - if a < b, then the return value must be less than 0
       *  - if a == b, then the return value must be 0
       */
      SortedCollection.prototype.setComparisonFunction = function (in_fn) {
        console.assert(_.isFunction(in_fn), 'Must provide a function');
        this._comparisonFunction = in_fn;
      };
      /**
       * Add an item to the collection. Sort the list of keys in an ascending order.
       * @param {string|number} in_key Key to store the value under
       * @param {object} in_value Value to store in the collection
       * @return {object} Return the value passed in
       */
      SortedCollection.prototype.add = function (in_key, in_value) {
        var toReturn = Collection.prototype.add.call(this, in_key, in_value);
        this._sortedKeys.push(in_key.toString());
        this._sortedKeys.sort(this._comparisonFunction);
        return toReturn;
      };
      /**
       * Remove an item from this Collection. This method returns a Boolean indicating
       * the success or failure of the removal. This is practical because if we were
       * to throw an error when the key doesn't exist, it would require additional
       * checks by the caller to make sure this key exists prior to removal. Which
       * would make the attempt of removal more verbose and also costly because the
       * caller would have to keep a list – somewhere else – of the things he can
       * and cannot remove.
       *
       * @param {string|number} in_key the key we wish to remove
       * @return {boolean} true if the key exists and was removed, false otherwise.
       */
      SortedCollection.prototype.remove = function (in_key) {
        var toReturn = Collection.prototype.remove.call(this, in_key);
        this._sortedKeys = _.without(this._sortedKeys, in_key);
        return toReturn;
      };
      /**
       * Copy the items of in_collection to this collection.
       * @return {LYNX.Utils.Datastructures.SortedCollection} cloned SortedCollection
       */
      SortedCollection.prototype.clone = function () {
        var newCol = new SortedCollection(this._name, this._type);
        newCol.setComparisonFunction(this._comparisonFunction);
        newCol.bulkAdd(this._items);
        return newCol;
      };
      /**
       * Internal function use to search (binary search) for the nearest index that
       * the given key would be inserted.
       * i.e. given the array [10, 20, 30, 40, 50] the index that 35 should be inserted at is 3
       * @param {Array<*>} in_array - Target array
       * @param {string} in_key - Key to check against
       * @return {number} The index at which the key would be inserted in
       * @private
       */
      SortedCollection.prototype._binarySearchNearestIndex = function (in_array, in_key) {
        if (!this._comparisonFunction) {
          return _.sortedIndex(in_array, in_key);
        }
        var middleIndex = Math.floor((in_array.length - 1) / 2);
        if (middleIndex < 0) {
          return 0;
        }
        if (this._comparisonFunction(in_array[middleIndex], in_key) > 0) {
          if (in_array.length === 1) {
            return 0;
          }
          return this._binarySearchNearestIndex(in_array.slice(0, middleIndex), in_key);
        }
        if (this._comparisonFunction(in_array[middleIndex], in_key) < 0) {
          if (in_array.length === 1) {
            return 1;
          }
          return middleIndex + 1 + this._binarySearchNearestIndex(in_array.slice(middleIndex + 1, in_array.length), in_key);
        }
        return middleIndex;
      };
      /**
       * Return the nearest next item to the given key i.e.
       * For the given list of keys ['1.0.1', '2.0.0', '2.2.0', '7.0.1'] the nearest next item to 6.0.1 is
       * the item mapped by '7.0.1'
       * @param {string|number} in_key The key to check against in order to get the nearest next item
       * @return {*|undefined} The nearest next item
       */
      SortedCollection.prototype.getNearestNextItem = function (in_key) {
        var closestNextIndex = this._binarySearchNearestIndex(this._sortedKeys, in_key.toString());
        if (closestNextIndex === this.getCount()) {
          return undefined;
        } else {
          return this.item(this._sortedKeys[closestNextIndex]);
        }
      };
      /**
       * Return the nearest previous item to the given key i.e.
       * For the given list of keys ['1.0.1', '2.0.0', '2.2.0', '7.0.1'] the nearest previous item to 6.0.1 is
       * the item mapped by '7.0.1'
       * @param {string|number} in_key The key to check against in order to get the nearest previous item
       * @return {*|undefined} The nearest previous item
       */
      SortedCollection.prototype.getNearestPreviousItem = function (in_key) {
        var closestPreviousIndex = this._binarySearchNearestIndex(this._sortedKeys, in_key.toString());
        if (closestPreviousIndex === 0) {
          return undefined;
        } else {
          return this.item(this._sortedKeys[closestPreviousIndex - 1]);
        }
      };
      exportModule('LYNX.Utils.Datastructures.SortedCollection', SortedCollection);
    }());
    (function () {
      /**
       * A typed data container that is persistable, high-performance, and can be used
       * as a backing store for collaborative property sets.
       *
       * @param {function} in_bufferConstructor - This is the constructor to be used to
       *    setup the internal buffer of the DataArray.
       * @param {number} in_size - The initial size with which to allocate the array.
       * @constructor
       * @alias LYNX.Utils.Datastructures.DataArrays
       */
      var BaseDataArray = function (in_bufferConstructor, in_size) {
        // The 'eslint-...' is used to disable the rule that requires
        // constructors to start with a capitalized letter.
        this._buffer = new in_bufferConstructor(in_size);
        // eslint-disable-line new-cap
        this._bufferConstructor = in_bufferConstructor;
        this._size = in_size;
      };
      /**
       * Get the value at an index. If no index is passed, return zeroth item.
       * @param {Number} in_idx the specific item in the data array.
       * @return {*} the value at that index
       */
      BaseDataArray.prototype.getValue = function (in_idx) {
        in_idx = in_idx === undefined ? 0 : in_idx;
        if (in_idx >= this._size || in_idx < 0) {
          throw new Error('Trying to access out of bounds!');
        }
        return this._buffer[in_idx];
      };
      /**
       * Return a range of values in the array.
       * @param {Number} in_idxStart the starting index
       * @param {Number} in_idxEnd the end index
       * @return {Int16Array|Uint16Array|Float64Array|Float32Array|Int8Array|Int32Array|*} the array of values in the range
       */
      BaseDataArray.prototype.getValueRange = function (in_idxStart, in_idxEnd) {
        if (in_idxStart >= this._size || in_idxEnd > this._size || in_idxStart < 0 || in_idxEnd < 0) {
          throw new Error('Trying to access out of bounds!');
        }
        return this._buffer.subarray(in_idxStart, in_idxEnd);
      };
      /**
       * Return the serialized form of Data Arrays.
       * @return {Array<*>} An object containing an array of the values.
       */
      BaseDataArray.prototype.serialize = function () {
        // Copy over the data to a standard Javascript array.
        var valArray = new Array(this._size);
        for (var i = 0; i < this._size; i++) {
          valArray[i] = this._buffer[i];
        }
        return valArray;
      };
      /**
       * Deserialize data from a serialized representation
       * @param {Array} in_serialized the serialized representation
       */
      BaseDataArray.prototype.deserialize = function (in_serialized) {
        var values = in_serialized;
        var length = in_serialized.length;
        if (length !== this._size) {
          this.resize(length);
          this._size = length;
        }
        var i;
        for (i = 0; i < length; i++) {
          this._buffer[i] = values[i];
        }
      };
      /**
       * Set value at an index.
       * @param {Number} in_idx the index
       * @param {*} in_value the value we want to set at index
       */
      BaseDataArray.prototype.setValue = function (in_idx, in_value) {
        if (in_idx < this._buffer.length) {
          this._buffer[in_idx] = in_value;
        } else {
          console.error('DataArray setValue in_idx is out of bounds.');
        }
      };
      /**
       * creates a copy of a typed array with removed elements
       * @param {TypedArray} in_arr the input array (won't be modified)
       * @param {number} in_offset starting index of range that will be removed
       * @param {number} in_deleteCount number of removed elements
       * @return {TypedArray} a copy of the input array without the selected range
       * @private
       */
      BaseDataArray.prototype._removeElementsFromArray = function (in_arr, in_offset, in_deleteCount) {
        // TODO: this function can be optimized
        var newSize = this._size - in_deleteCount;
        var splicedArray = new in_arr.constructor(newSize);
        splicedArray.set(in_arr.subarray(0, in_offset));
        splicedArray.set(in_arr.subarray(in_offset + in_deleteCount, this._size), in_offset);
        return splicedArray;
      };
      /**
       * remove a range of elements from the array
       * @param {number} in_offset start of the range
       * @param {number} in_deleteCount number of elements to be removed
       */
      BaseDataArray.prototype.removeRange = function (in_offset, in_deleteCount) {
        if (in_offset + in_deleteCount < this._buffer.length + 1) {
          this._buffer = this._removeElementsFromArray(this._buffer, in_offset, in_deleteCount);
          this._size = this._size - in_deleteCount;
        } else {
          console.error('DataArray removeRange in_offset + in_deleteCount is out of bounds.');
        }
      };
      /**
       * copy an array with elements inserted into the copy
       * @param {TypedArray} in_arr the input array (won't be modified)
       * @param {number} in_offset the index where the new elements will be inserted
       * @param {Array} in_addedArray the array with the elements that will be added
       * @return {TypedArray} the combined array
       * @private
       */
      BaseDataArray.prototype._insert = function (in_arr, in_offset, in_addedArray) {
        // TODO: this function can be optimized
        var newSize = this._size + in_addedArray.length;
        var insertedArray = new in_arr.constructor(newSize);
        insertedArray.set(in_arr.subarray(0, in_offset));
        insertedArray.set(in_addedArray, in_offset);
        insertedArray.set(in_arr.subarray(in_offset, this._size), in_offset + in_addedArray.length);
        return insertedArray;
      };
      /**
       * insert the content of an array into the DataArray
       * @param {number} in_offset the target index
       * @param {Array} in_array the array to be inserted
       */
      BaseDataArray.prototype.insertRange = function (in_offset, in_array) {
        this._buffer = this._insert(this._buffer, in_offset, in_array);
        this._size = this._size + in_array.length;
      };
      /**
       * Set this array values to be equal to in_array values
       * @param {Number=} in_offset An optional offset in this array to begin start
       *                  setting this arrays values to in_array values.
       * @param {*} in_array the input array
       */
      BaseDataArray.prototype.set = function (in_offset, in_array) {
        if (in_array instanceof ArrayBuffer || in_array instanceof Array || in_array instanceof this.getBufferCtor()) {
          this._buffer.set(in_array, in_offset);
        } else if (in_array instanceof BaseDataArray) {
          this._buffer.set(in_array.getBuffer(), in_offset);
        } else {
          console.error('DataArray set() must be called with Array, TypedArray or DataArray');
        }
      };
      /**
       * insert a value at the end of the array, creates a new element at the end and sets the value
       * @param {*} in_value the new value
       */
      BaseDataArray.prototype.push = function (in_value) {
        // Adjust the buffer if necessary
        var bufferLength = this._buffer.length;
        if (this._size > bufferLength - 1) {
          this._alloc(this._size * 2 || 1);  // grow by a factor of 2x
        }
        // set the value and update size
        this.setValue(this._size, in_value);
        this._size++;
      };
      /**
       * get direct access to the data (for performance reasons)
       * this should be uses read only
       * @return {TypedArray} the (read only) raw data
       */
      BaseDataArray.prototype.getBuffer = function () {
        return this._buffer;
      };
      /**
       * get the constructor of the underlying TypedArray
       * @return {Function} the constructor for the data buffer
       */
      BaseDataArray.prototype.getBufferCtor = function () {
        return this._bufferConstructor;
      };
      /**
       * apply a given function to all elements of the array
       * @param {Function} in_fn the function that will be applied to every element
       */
      BaseDataArray.prototype.iterate = function (in_fn) {
        var l = this._size;
        for (var i = 0; i < l; i++) {
          in_fn(this._buffer[i]);
        }
      };
      /**
       * get a resized buffer copy
       * @param {Function} in_bufferCtor the constructor for the returned buffer
       * @param {TypedArray} in_buffer the input buffer (won't be modified)
       * @param {number} in_newSize the target size
       * @return {TypedArray} the buffer with the new size
       */
      var resizeBuffer = function (in_bufferCtor, in_buffer, in_newSize) {
        // target buffer with the desired new size
        // The 'eslint-...' is used to disable the rule that requires
        // constructors to start with a capitalized letter.
        var newBuffer = new in_bufferCtor(in_newSize);
        // eslint-disable-line new-cap
        var oldSize = in_buffer.length;
        var oldBuffer = in_buffer;
        var isShrinking = oldSize > in_newSize;
        newBuffer.set(isShrinking ? oldBuffer.subarray(0, in_newSize) : oldBuffer);
        return newBuffer;
      };
      /**
       * allocate memory for the array (for performance reasons, you can allocate more space than the current length,
       * which makes pushes to the array less expensive later)
       * @param {number} in_size the target allocated space
       * @return {BaseDataArray} the DataArray itself
       * @private
       */
      BaseDataArray.prototype._alloc = function (in_size) {
        this._buffer = resizeBuffer(this._bufferConstructor, this._buffer, in_size);
        return this;
      };
      /**
       * change the size of the array
       * @param {number} in_size the target size
       * @return {BaseDataArray} the DataArray itself
       */
      BaseDataArray.prototype.resize = function (in_size) {
        // this can be costly!!!
        this._alloc(in_size);
        this._size = in_size;
        return this;
      };
      BaseDataArray.prototype.copy = function () {
        // and this!
        var newBuffer = new this._bufferConstructor(this._size);
        // buffer with the desired new size
        newBuffer.set(this._buffer);
        return newBuffer;
      };
      Object.defineProperty(BaseDataArray.prototype, 'length', {
        get: function () {
          return this._size;
        }
      });
      var Int8DataArray = function (in_size) {
        BaseDataArray.call(this, this.getBufferCtor(), in_size);
      };
      Int8DataArray.prototype = Object.create(BaseDataArray.prototype);
      Int8DataArray.prototype.getBufferCtor = function () {
        return Int8Array;
      };
      var Int16DataArray = function (in_size) {
        BaseDataArray.call(this, this.getBufferCtor(), in_size);
      };
      Int16DataArray.prototype = Object.create(BaseDataArray.prototype);
      Int16DataArray.prototype.getBufferCtor = function () {
        return Int16Array;
      };
      var Int32DataArray = function (in_size) {
        BaseDataArray.call(this, this.getBufferCtor(), in_size);
      };
      Int32DataArray.prototype = Object.create(BaseDataArray.prototype);
      Int32DataArray.prototype.getBufferCtor = function () {
        return Int32Array;
      };
      var Uint8DataArray = function (in_size) {
        BaseDataArray.call(this, this.getBufferCtor(), in_size);
      };
      Uint8DataArray.prototype = Object.create(BaseDataArray.prototype);
      Uint8DataArray.prototype.getBufferCtor = function () {
        return Uint8Array;
      };
      var Uint16DataArray = function (in_size) {
        BaseDataArray.call(this, this.getBufferCtor(), in_size);
      };
      Uint16DataArray.prototype = Object.create(BaseDataArray.prototype);
      Uint16DataArray.prototype.getBufferCtor = function () {
        return Uint16Array;
      };
      var Uint32DataArray = function (in_size) {
        BaseDataArray.call(this, this.getBufferCtor(), in_size);
      };
      Uint32DataArray.prototype = Object.create(BaseDataArray.prototype);
      Uint32DataArray.prototype.getBufferCtor = function () {
        return Uint32Array;
      };
      var Float32DataArray = function (in_size) {
        BaseDataArray.call(this, this.getBufferCtor(), in_size);
      };
      Float32DataArray.prototype = Object.create(BaseDataArray.prototype);
      Float32DataArray.prototype.getBufferCtor = function () {
        return Float32Array;
      };
      var Float64DataArray = function (in_size) {
        BaseDataArray.call(this, this.getBufferCtor(), in_size);
      };
      Float64DataArray.prototype = Object.create(BaseDataArray.prototype);
      Float64DataArray.prototype.getBufferCtor = function () {
        return Float64Array;
      };
      /**
       * A data container that can contain every native type
       *
       * @param {number} in_size - The initial size with which to allocate the array.
       * @constructor
       * @alias LYNX.Utils.Datastructures.DataArrays.UniversalDataArray
       */
      var UniversalDataArray = function (in_size) {
        BaseDataArray.call(this, this.getBufferCtor(), in_size);
      };
      UniversalDataArray.prototype = Object.create(BaseDataArray.prototype);
      UniversalDataArray.prototype.getBufferCtor = function () {
        return Array;
      };
      /**
       * helper function to write array values into another array at a given offset
       * @param {Array} array target array
       * @param {Array} values the values we need to write
       * @param {number} offset starting index in target array
       */
      var array_set = function (array, values, offset) {
        offset = offset || 0;
        var index = 0;
        values.forEach(function (value) {
          array[index + offset] = value;
          index++;
        });
      };
      /**
       * insert the content of an array into the DataArray
       * @param {number} in_offset the target index
       * @param {Array} in_array the array to be inserted
       */
      UniversalDataArray.prototype.insertRange = function (in_offset, in_array) {
        this._buffer.splice.apply(this._buffer, [
          in_offset,
          0
        ].concat(in_array));
        this._size = this._size + in_array.length;
      };
      /**
       * remove a range of elements from the array
       * @param {number} in_offset start of the range
       * @param {number} in_deleteCount number of elements to be removed
       */
      UniversalDataArray.prototype.removeRange = function (in_offset, in_deleteCount) {
        if (in_offset + in_deleteCount < this._buffer.length + 1) {
          this._buffer.splice(in_offset, in_deleteCount);
          this._size -= in_deleteCount;
        } else {
          throw Error('DataArray removeRange in_offset + in_deleteCount is out of bounds.');
        }
      };
      /**
       * Set this array values to be equal to in_array values
       * @param {Number=} in_offset An optional offset in this array to begin start
       *                  setting this arrays values to in_array values.
       * @param {*} in_array the input array
       */
      UniversalDataArray.prototype.set = function (in_offset, in_array) {
        if (in_array instanceof ArrayBuffer || in_array instanceof Array || in_array instanceof this.getBufferCtor()) {
          array_set(this._buffer, in_array, in_offset);
        } else if (in_array instanceof BaseDataArray) {
          array_set(this._buffer, in_array.getBuffer(), in_offset);
        } else {
          console.error('DataArray set() must be called with Array, TypedArray or DataArray');
        }
      };
      /**
       * Return a range of values in the array.
       * @param {Number} in_idxStart the starting index
       * @param {Number} in_idxEnd the end index - this offset is exclusive
       * @return {Array} the array of values in the range
       */
      UniversalDataArray.prototype.getValueRange = function (in_idxStart, in_idxEnd) {
        if (in_idxStart >= this._size || in_idxEnd > this._size || in_idxStart < 0 || in_idxEnd < 0) {
          throw new Error('Trying to access out of bounds!');
        }
        return this._buffer.slice(in_idxStart, in_idxEnd);
      };
      /**
       * change the size of a javascript array and keep the content, if possible. Keeps the input buffer.
       * @param {Array} in_buffer input buffer - not changed
       * @param {number} in_newSize target size
       * @return {Array} an Array of the new size
       */
      var resizeBufferArray = function (in_buffer, in_newSize) {
        // target buffer with the desired new size
        var newBuffer = new Array(in_newSize);
        var oldSize = in_buffer.length;
        var oldBuffer = in_buffer;
        var isShrinking = oldSize > in_newSize;
        array_set(newBuffer, isShrinking ? oldBuffer.slice(0, in_newSize) : oldBuffer);
        return newBuffer;
      };
      /**
       * allocate memory for the array (for performance reasons, you can allocate more space than the current length,
       * which makes pushes to the array less expensive later)
       * @param {number} in_size the target allocated space
       * @return {BaseDataArray} the DataArray itself
       * @private
       */
      UniversalDataArray.prototype._alloc = function (in_size) {
        this._buffer = resizeBufferArray(this._buffer, in_size);
        return this;
      };
      /**
       * A data container that contains a string
       *
       * @constructor
       * @alias LYNX.Utils.Datastructures.DataArrays.StringDataArray
       */
      var StringDataArray = function () {
        BaseDataArray.call(this, this.getBufferCtor(), '');
        this._size = 0;
        this._buffer = '';
      };
      StringDataArray.prototype = Object.create(BaseDataArray.prototype);
      StringDataArray.prototype.getBufferCtor = function () {
        return String;
      };
      /**
       * insert the content of a string into the StringDataArray
       * @param {number} in_offset the target index
       * @param {string} in_string the string to be inserted
       */
      StringDataArray.prototype.insertRange = function (in_offset, in_string) {
        this._buffer = this._buffer.substr(0, in_offset) + in_string + this._buffer.substr(in_offset);
        this._size = this._size + in_string.length;
      };
      /**
       * remove a range of elements from the string
       * @param {number} in_offset start of the range
       * @param {number} in_deleteCount number of elements to be removed
       */
      StringDataArray.prototype.removeRange = function (in_offset, in_deleteCount) {
        if (in_offset + in_deleteCount < this._buffer.length + 1) {
          this._buffer = this._buffer.substr(0, in_offset) + this._buffer.substr(in_offset + in_deleteCount);
          this._size -= in_deleteCount;
        } else {
          throw Error('DataArray removeRange in_offset + in_deleteCount is out of bounds.');
        }
      };
      /**
       * Set this array values to be equal to in_string values
       * @param {Number=} in_offset The offset in this array to begin start
       *                  setting this arrays values to in_string values.
       * @param {string} in_string the input string
       */
      StringDataArray.prototype.set = function (in_offset, in_string) {
        this._buffer = this._buffer.substr(0, in_offset) + in_string + this._buffer.substr(in_offset + in_string.length);
      };
      /**
       * Return a range of characters in the string.
       * @param {Number} in_idxStart the starting index
       * @param {Number} in_idxEnd the end index - this offset is exclusive
       * @return {String} the characters in the range
       */
      StringDataArray.prototype.getValueRange = function (in_idxStart, in_idxEnd) {
        if (in_idxStart >= this._size || in_idxEnd > this._size || in_idxStart < 0 || in_idxEnd < 0) {
          throw new Error('Trying to access out of bounds!');
        }
        return this._buffer.slice(in_idxStart, in_idxEnd);
      };
      Object.defineProperty(StringDataArray.prototype, 'length', {
        get: function () {
          return this._buffer.length;
        }
      });
      exportModule('LYNX.Utils.Datastructures.DataArrays', {
        BaseDataArray: BaseDataArray,
        Float32DataArray: Float32DataArray,
        Float64DataArray: Float64DataArray,
        Int8DataArray: Int8DataArray,
        Int16DataArray: Int16DataArray,
        Int32DataArray: Int32DataArray,
        Uint8DataArray: Uint8DataArray,
        Uint16DataArray: Uint16DataArray,
        Uint32DataArray: Uint32DataArray,
        UniversalDataArray: UniversalDataArray,
        StringDataArray: StringDataArray
      });
    }());
    (function () {
      /**
       * A data representation class for 64 bit integer types.
       * This is necessary since js doesn't support 64bit
       * integers natively yet.
       *
       * Please note this class is immutable.
       * There are and there won't be set functions!
       * (see javascript String class)
       *
       * @param {number} in_low  - lower 32 bit
       * @param {number} in_high - higher 32 bit
       * @constructor
       * @protected
       * @alias LYNX.Utils.Datastructures.Integer64
       */
      var Integer64 = function (in_low, in_high) {
        this._low = in_low || 0;
        this._high = in_high || 0;
      };
      /**
       * @return {number} the higher 32 bit integer part
      */
      Integer64.prototype.getHigh = function () {
        return this._high;
      };
      /**
       * @return {number} the lower 32 bit integer part
      */
      Integer64.prototype.getLow = function () {
        return this._low;
      };
      /**
       * A data representation class for the signed 64 bit integer type
       *
       * @param {number} in_low  - lower 32 bit
       * @param {number} in_high - higher 32 bit
       * @constructor
       * @extends LYNX.Utils.Datastructures.Integer64
       * @alias LYNX.Utils.Datastructures.Int64
       */
      var Int64 = function (in_low, in_high) {
        Integer64.call(this, in_low, in_high);
      };
      Int64.prototype = Object.create(Integer64.prototype);
      /**
       * @return {Int64} in_other - the  copy
       */
      Int64.prototype.clone = function () {
        return new Int64(this._low, this._high);
      };
      /**
       * A data representation class for the unsigned 64 bit integer type
       *
       * @param {number} in_low  - lower 32 bit
       * @param {number} in_high - higher 32 bit
       * @constructor
       * @extends LYNX.Utils.Datastructures.Integer64
       * @alias LYNX.Utils.Datastructures.Uint64
       */
      var Uint64 = function (in_low, in_high) {
        Integer64.call(this, in_low, in_high);
      };
      Uint64.prototype = Object.create(Integer64.prototype);
      /**
       * @return {Uint64} in_other - the  copy
       */
      Uint64.prototype.clone = function () {
        return new Uint64(this._low, this._high);
      };
      exportModule('LYNX.Utils.Datastructures.Integer64', Integer64);
      exportModule('LYNX.Utils.Datastructures.Int64', Int64);
      exportModule('LYNX.Utils.Datastructures.Uint64', Uint64);
    }());
    (function () {
      /**
       * @namespace LYNX.Utils.Datastructures
       * @alias LYNX.Utils.Datastructures
       * Namespace with data structures that are commonly needed in many algorithms
       */
      /**
       * Node to be used by the List declared below. The intention is that your class will inherit
       * from Node, or you add your data to the Node, depending on the usage of the list.
       * The only parameters that are reserved in a Node are '__next' and '__prev' plus
       * the member functions.
       *
       * @alias LYNX.Utils.Datastructures.List.Node
       * @constructor
       */
      var Node = function () {
        this.__next = undefined;
        this.__prev = undefined;
      };
      /**
       * Return the next node. May be end()
       * @return {LYNX.Utils.Datastructures.List.Node} The next node in the list
       */
      Node.prototype.next = function () {
        if (!this.__next) {
          throw new Error('Calling "next" on end()');
        }
        return this.__next;
      };
      /**
       * Return the previous node. May be rend() of the list.
       * @return {LYNX.Utils.Datastructures.List.Node} The previous node in the list
       */
      Node.prototype.previous = function () {
        if (!this.__prev) {
          throw new Error('Calling "previous" on rend()');
        }
        return this.__prev;
      };
      /**
       * List datastructure, holding LYNX.Utils.Datastructures.List.Node(s)
       *
       * @constructor
       * @alias LYNX.Utils.Datastructures.List
       */
      var List = function () {
        this._head = new Node();
        this._head.SENTINEL = true;
        this._tail = new Node();
        this._tail.SENTINEL = true;
        this._head.__next = this._tail;
        this._tail.__prev = this._head;
        this._count = 0;
      };
      /**
       * Return the first entry. May be end() (i.e., empty list)
       * @return {LYNX.Utils.Datastructures.List.Node} - The first node in the list
       */
      List.prototype.begin = function () {
        return this._head.next();
      };
      /**
       * End of the list. Not a valid entity.
       *
       * @return {LYNX.Utils.Datastructures.List.Node} - The end of the list
       */
      List.prototype.end = function () {
        return this._tail;
      };
      /**
       * Return the first entry, visiting the list in reverse. May be rend() (i.e., empty list)
       * @return {LYNX.Utils.Datastructures.List.Node} - The last node in the list
       */
      List.prototype.rbegin = function () {
        return this._tail.previous();
      };
      /**
       * End of the list. Not a valid entity.
       *
       * @return {LYNX.Utils.Datastructures.List.Node} - the head node of the list
       */
      List.prototype.rend = function () {
        return this._head;
      };
      /**
       * Return whether the list is empty or not.
       * @return {boolean} Is the list empty?
       */
      List.prototype.empty = function () {
        return this._head.__next === this._tail;
      };
      /**
       * Return the number of items in the list
       * @return {number} Number of entries in list
       */
      List.prototype.count = function () {
        return this._count;
      };
      /**
       * Visit every item of the list, passing the node to the provided function.
       * If the function returns true, iteration aborts.
       *
       * @param {function(LYNX.Utils.Datastructures.List.Node) : true|undefined} in_callback -
       *     function to call for each node.
       */
      List.prototype.iterate = function (in_callback) {
        for (var i = this.begin(); i !== this.end(); i = i.next()) {
          if (in_callback(i)) {
            break;
          }
        }
      };
      /**
       * Insert a node in the list
       *
       * @param {LYNX.Utils.Datastructures.List.Node} in_node - the node to insert
       * @param {LYNX.Utils.Datastructures.List.Node=} in_before - the node to insert before, end() by default
       */
      List.prototype.insert = function (in_node, in_before) {
        if (in_node.__next || in_node.__prev) {
          throw new Error('Inserting an already inserted item');
        }
        in_before = in_before || this.end();
        in_node.__prev = in_before.__prev;
        in_node.__next = in_before;
        in_node.__next.__prev = in_node;
        in_node.__prev.__next = in_node;
        this._count++;
      };
      /**
       * Remove the node from the list.
       *
       * @param {LYNX.Utils.Datastructures.List.Node} in_node the node to remove.
       * @return {LYNX.Utils.Datastructures.List.Node} the next node, or end() if there is none
       */
      List.prototype.remove = function (in_node) {
        if (!in_node.__next || !in_node.__prev) {
          throw new Error('Removing end()');
        }
        in_node.__next.__prev = in_node.__prev;
        in_node.__prev.__next = in_node.__next;
        var result = in_node.__next;
        in_node.__next = in_node.__prev = undefined;
        this._count--;
        return result;
      };
      /**
       * Push the node onto the end of the list.
       *
       * @param {LYNX.Utils.Datastructures.List.Node} in_node - The node to add
       */
      List.prototype.push = function (in_node) {
        this.insert(in_node);
      };
      /**
       * Pop the node from the end of the list.
       * Fails if the list is empty.
       *
       * @return {LYNX.Utils.Datastructures.List.Node} The removed node
       */
      List.prototype.pop = function () {
        if (this.empty()) {
          throw new Error('popping an empty list');
        }
        var node = this.end().previous();
        this.remove(node);
        return node;
      };
      /**
       * Validate the linked list is in a good state
       * @param {Boolean} [in_throw=false] - if true, on error an exception is thrown.
       * @return {Boolean} true if ok
       */
      List.prototype._testIntegrity = function (in_throw) {
        if (!this._head.SENTINEL || !this._tail.SENTINEL) {
          if (in_throw) {
            throw new Error('Sentinels corrupt');
          }
          return false;
        }
        if (this.begin().__prev !== this._head || this.end() !== this._tail) {
          if (in_throw) {
            throw new Error('Head and tail corrupt');
          }
          return false;
        }
        var last = this._head;
        for (var i = this.begin(); i !== this.end(); i = i.next()) {
          if (i.__prev !== last || last.__next !== i) {
            if (in_throw) {
              throw new Error('List chaining broken');
            }
            return false;
          }
          last = i;
        }
        if (last !== this._tail.__prev || last.__next !== this._tail) {
          if (in_throw) {
            throw new Error('List chaining broken');
          }
          return false;
        }
        return true;
      };
      List.Node = Node;
      exportModule('LYNX.Utils.Datastructures.List', List);
    }());
    (function () {
      var List = include('LYNX.Utils.Datastructures.List');
      var _ = include('underscore');
      /**
       * The LRUCache maintains a list of items, and the order in which they were recently used.
       * @alias LYNX.Utils.Datastructures.LRUCache
       * @constructor
       */
      var LRUCache = function () {
        this._order = new List();
        this._items = {};
        this._count = 0;
      };
      /**
       * @return {number} The number of items in the cache
       */
      LRUCache.prototype.count = function () {
        return this._count;
      };
      /**
       * Trim in_count least-recently-used entry(ies) from the cache
       *
       * @param {number} [in_count=1] - the number of entities to trim
       * @param {function} [in_trimFunction] - A callback function to call for each trimmed entry
       */
      LRUCache.prototype.trim = function (in_count, in_trimFunction) {
        if (_.isUndefined(in_count)) {
          in_count = 1;
        }
        var n = Math.min(in_count, this._count);
        while (n-- > 0) {
          var node = this._order.pop();
          if (in_trimFunction) {
            in_trimFunction(node._key, node._data);
          }
          delete this._items[node._key];
          this._count--;
        }
      };
      /**
       * Trim to have in_count least-recently-used entry(ies) in the cache
       *
       * @param {number} in_count - the goal number of entities to trim
       * @param {function} [in_trimFunction] - A callback function to call for each trimmed entry
       */
      LRUCache.prototype.trimTo = function (in_count, in_trimFunction) {
        var n = this._count - in_count;
        if (n >= 0) {
          this.trim(n, in_trimFunction);
        }
      };
      /**
       * Empty the cache
       */
      LRUCache.prototype.clear = function () {
        this.trimTo(0);
      };
      /**
       * Return whether there is data in the cache for this key
       * Does NOT affect the lru status.
       *
       * @param {String|Number} in_key - the key for the data.
       * @return {Boolean} true iff there is an entry in the cache.
       */
      LRUCache.prototype.has = function (in_key) {
        return this._items[in_key] !== undefined;
      };
      /**
       * Insert the data into the cache, with the provided key.
       * If the entry already exists, the data is updated.
       * The entry becomes the most recently used item.
       *
       * @param {String|Number} in_key - the key for the data.
       * @param {*} in_data - the new data
       */
      LRUCache.prototype.insert = function (in_key, in_data) {
        var node = this._items[in_key];
        if (node) {
          this._order.remove(node);
        } else {
          node = new List.Node();
          node._key = in_key;
          this._items[in_key] = node;
          this._count++;
        }
        // Set or freshen the data.
        node._data = in_data;
        // Put at the front of the list (most recently used)
        this._order.insert(node, this._order.begin());
      };
      /**
       * Update the data associated with the key without affecting the lru status.
       * Fails if the key doesn't exist (use insert then)
       *
       * @param {String|Number} in_key - the key for the data.
       * @param {*} in_data - the new associated data
       */
      LRUCache.prototype.poke = function (in_key, in_data) {
        var node = this._items[in_key];
        console.assert(node, 'poke used on an invalid key');
        if (node) {
          node._data = in_data;
        }
      };
      /**
       * Get the data of the provided key, and update the lru status.
       * @param {String|Number} in_key - the key for the data.
       * @return {*} The entry at the key
       */
      LRUCache.prototype.get = function (in_key) {
        var node = this._items[in_key];
        if (node) {
          // Move to the head of the list.
          this._order.remove(node);
          this._order.insert(node, this._order.begin());
          return node._data;
        } else {
          return undefined;
        }
      };
      /**
       * Get the data of the provided key without affecting the lru status.
       *
       * @param {String|Number} in_key - the key for the data.
       * @return {*} the associated data
       */
      LRUCache.prototype.peek = function (in_key) {
        var node = this._items[in_key];
        if (node) {
          return node._data;
        } else {
          return undefined;
        }
      };
      /**
       * Remove the entry if it is in the cache, and return the data.
       *
       * @param {String|Number} in_key - the key for the data.
       * @return {*} the data of the removed entry
       */
      LRUCache.prototype.remove = function (in_key) {
        var node = this._items[in_key];
        if (node) {
          this._order.remove(node);
          delete this._items[in_key];
          this._count--;
          return node._data;
        } else {
          // Not found
          return undefined;
        }
      };
      exportModule('LYNX.Utils.Datastructures.LRUCache', LRUCache);
    }());
    (function () {
      var HierarchicalMap = function () {
        this._hierarchy = {};
      };
      /**
       * Insert data for the provided key set.
       *
       * @param {*} in_value - value to store for the key 'chain'
       * @param {String|Number} [arguments] - the keys to insert it under. Will stop at the first undefined one.
       */
      HierarchicalMap.prototype.insert = function (in_value) {
        var table = this._hierarchy;
        for (var i = 1; i < arguments.length && arguments[i]; ++i) {
          var key = arguments[i];
          table.subTable = table.subTable || {};
          table = table.subTable[key] = table.subTable[key] || {};
        }
        table.value = { data: in_value };
      };
      /**
       * Clear the value stored with the key path.
       */
      HierarchicalMap.prototype.remove = function () {
        var table = this._hierarchy;
        for (var i = 0; i < arguments.length && arguments[i] && table && table.subTable; ++i) {
          table = table.subTable[arguments[i]];
        }
        if (table) {
          delete table.value;
        }
      };
      /**
       * Look up data for the given key path.
       * @return {*} the value along the path closest to the given path, if it exists.
       */
      HierarchicalMap.prototype.get = function () {
        var table = this._hierarchy;
        var lastValueSeen = this._hierarchy.value ? this._hierarchy.value.data : undefined;
        for (var i = 0; i < arguments.length && arguments[i] && table && table.subTable; ++i) {
          table = table.subTable[arguments[i]];
          if (table && table.value) {
            lastValueSeen = table.value.data;
          }
        }
        return lastValueSeen;
      };
      exportModule('LYNX.Utils.Datastructures.HierarchicalMap', HierarchicalMap);
    }());
    (function () {
      /**
       * A Sequence is a node in a doubly-linked list.
       *
       * @param {*} load - the load on this node, i.e. the thing that it 'carries'.
       *
       * @constructor
       * @alias LYNX.Utils.Datastructures.Sequence
       */
      var Sequence = function (load) {
        this._load = load;
        // As a convention, we'll take previous to closer to head, and next to be
        // closer to tail.
        this._prev = null;
        this._next = null;
      };
      // We alias all of the older prototype methods for compatibility with old
      // code.
      /**
       * Unwrap a Sequence to reveal its load.
       *
       * @return {*} the load of this sequence.
       */
      Sequence.prototype.getLoad = function () {
        return this._load;
      };
      /**
       * @return {Sequence} the next Sequence, possibly null.
       */
      Sequence.prototype.getNext = function () {
        return this._next;
      };
      /**
       * @return {Sequence} the previous Sequence, possibly null.
       */
      Sequence.prototype.getPrevious = function () {
        return this._prev;
      };
      /**
       * Insert a Sequence immediately after this.
       * @param {Sequence} next - The Sequence instance to insert after this one.
       * @return {Sequence} the next Sequence.
       */
      Sequence.prototype.insertAfterThis = function (next) {
        this._next = next;
        next._prev = this;
        return next;
      };
      /**
       * Remove the item from the sequence
       */
      Sequence.prototype.removeFromSequence = function () {
        if (this._prev) {
          this._prev._next = this._next;
        }
        if (this._next) {
          this._next._prev = this._prev;
        }
        this._prev = this._next = null;
      };
      /**
       * Append something to the end of this Sequence.
       *
       * @param {*} something - the thing to append.
       *
       * @return {Sequence} this Sequence instance.
       */
      Sequence.prototype.append = function (something) {
        var i = this;
        if (this._load === undefined) {
          this._load = something;
        } else {
          while (i._next !== null) {
            i = i._next;
          }
          i._next = new Sequence(something);
        }
        return this;
      };
      Sequence.prototype.Append = function () {
        console.warn('Sequence.Append : this is deprecated! Use Sequence.append instead.');
        return Sequence.prototype.append.apply(this, arguments);
      };
      /**
       * Prepend something to this Sequence.
       *
       * @param {*} something - the thing to prepend.
       *
       * @return {Sequence} the new Sequence (which has _next -> this)
       */
      Sequence.prototype.prepend = function (something) {
        var newSequence = new Sequence(something);
        newSequence._next = this;
        if (this._prev) {
          this._prev._next = newSequence;
        }
        this._prev = newSequence;
        return newSequence;
      };
      Sequence.prototype.Prepend = function () {
        console.warn('Sequence.Prepend : this is deprecated! Use Sequence.prepend instead.');
        return Sequence.prototype.prepend.apply(this, arguments);
      };
      /**
       * Pop this element from the Sequence. ONLY use this on the head of a
       * Sequence (with this._prev === null).
       *
       * @return {Sequence} the next Sequence.
       */
      Sequence.prototype.pop = function () {
        this._next._prev = null;
        return this._next;
      };
      Sequence.prototype.Pop = function () {
        console.warn('Sequence.Pop : this is deprecated! Use Sequence.pop instead.');
        return Sequence.prototype.pop.apply(this, arguments);
      };
      /**
       * Return the index of an element (a load) in this sequence.
       *
       * @param {*} item - get the index of this item.
       * @param {number} i_ - do not specify. It's for internal use.
       *
       * @return {number} the index of item (0-indexed) or -1 if not present.
       */
      Sequence.prototype.indexOf = function (item, i_) {
        if (i_ === undefined) {
          i_ = 0;
        }
        if (this._load === item) {
          return i_;
        } else if (this._next) {
          return this._next.indexOf(item, i_++);
        }
        return -1;
      };
      /**
       * Left fold this Sequence. Extra arguments are passed along.
       *
       * @param {*} base - the base value.
       * @param {function} arbitraryFunction - the binary combinator.
       * @return {*} Whatever was returned by the last call to arbitraryFunction
       */
      Sequence.prototype.foldl = function (base, arbitraryFunction) {
        var args = Array.prototype.slice.call(arguments, 0);
        var curr = this;
        args[0] = base;
        do {
          args[1] = curr._load;
          args[0] = arbitraryFunction.apply(curr, args);
          curr = curr._next;
        } while (curr);
        return args[0];
      };
      Sequence.prototype.Foldl = function () {
        console.warn('Sequence.Foldl : this is deprecated! Use Sequence.foldl instead.');
        return Sequence.prototype.foldl.apply(this, arguments);
      };
      /**
       * Not-so-userfriendly map definition. Used by the method map.
       *
       * @param {function} arbitraryFunction - A function to call for each _load in the sequence.
       * @param {*} args extra arguments passed along each call to the arbitraty function.
       */
      Sequence.prototype._map = function (arbitraryFunction, args) {
        // Treat a node with an undefined load as an empty sequence.
        if (this._load === undefined) {
          return;
        }
        args[0] = this._load;
        arbitraryFunction.apply(this, args);
        if (this._next) {
          this._next._map(arbitraryFunction, args);
        }
      };
      /**
       * map a function over this Sequence. Extra arguments are passed along.
       *
       * @param {function} arbitraryFunction - function map.
       */
      Sequence.prototype.map = function (arbitraryFunction) {
        var args = Array.prototype.slice.call(arguments, 0);
        this._map(arbitraryFunction, args);
      };
      Sequence.prototype.Map = function () {
        console.warn('Sequence.Map : this is deprecated! Use Sequence.map instead.');
        return Sequence.prototype.map.apply(this, arguments);
      };
      Sequence.prototype.last = function () {
        var c = this;
        while (c._next) {
          c = c._next;
        }
        return c;
      };
      Sequence.prototype.Last = function () {
        console.warn('Sequence.Last : this is deprecated! Use Sequence.last instead.');
        return Sequence.prototype.last.apply(this, arguments);
      };
      // Helper for toArray
      var accumulator = function (thing, filter, array) {
        var result = filter(thing);
        if (result !== undefined) {
          array.push(result);
        }
      };
      var identity = function (x) {
        return x;
      };
      /**
       * Transform this Sequence to a typical JavaScript array.
       *
       * @param {function} [filter_] : optional function which maps an element in the
       *     sequence to some other thing. If its result is defined, then that result
       *     will be appended to the array.
       *     If you do not specify this, we use identity, and so toArray behaves
       *     exactly as you would expect.
       * @return {Array} An array with elements of the sequence filtered according to
       *   the given filter_ function.
       */
      Sequence.prototype.toArray = function (filter_) {
        var array = [];
        filter_ = filter_ || identity;
        this.map(accumulator, filter_, array);
        return array;
      };
      exportModule('LYNX.Utils.Datastructures.Sequence', Sequence);
    }());
    (function () {
      var _ = include('underscore');
      /**
       * Deep copy an object.
       *
       * @param {*}             in_obj               - the object to copy.
       * @param {Object|Array} [in_target_]          - copy into this object.
       * @param {string}       [in_key_]             - key in in_target_ at which to place
       *                                               the copied object.
       * @param {boolean}      [in_preserve_ = false] - do not overwrite structs / arrays in
       *                                               in an existing object
       *
       * @return {*} in_target_ if specified, new object containing copy of @obj
       * otherwise.
       * @alias LYNX.Utils.deepCopy
       */
      function deepCopy(in_obj, in_target_, in_key_, in_preserve_) {
        in_preserve_ = in_preserve_ === undefined ? false : in_preserve_;
        var target, copyMembers;
        var copyDirectlyIntoKey = false;
        if (in_target_ && (in_key_ === undefined || in_key_ && in_target_.hasOwnProperty(in_key_) && _.isObject(in_target_[in_key_]) && in_preserve_)) {
          // If no key is given, we directly copy into the
          // target object. Additionally, when a key is given, the member already exists
          // in the target object and preserve is set, we also keep the target object.
          // This is only possible if the target is already an
          // object.
          target = in_key_ ? in_target_[in_key_] : in_target_;
          if (_.isFunction(in_obj) || _.isDate(in_obj) || _.isRegExp(in_obj) || _.isObject(in_obj) !== _.isObject(target) || _.isArray(in_obj) !== _.isArray(target) || !_.isObject(target)) {
            console.error('Copying into incompatible target object.');
            return undefined;
          }
          copyMembers = true;
          copyDirectlyIntoKey = true;
        } else {
          // If no existing target object, we create a new object.
          // literals are directly assigned to the target
          if (!in_obj || !_.isObject(in_obj) || _.isFunction(in_obj)) {
            target = in_obj;  // Special case handlers for dates and regexps
                              // (https://github.com/jashkenas/underscore/pull/595)
          } else if (_.isDate(in_obj)) {
            target = new Date(in_obj.getTime());
          } else if (_.isRegExp(in_obj)) {
            target = new RegExp(in_obj.source, in_obj.toString().replace(/.*\//, ''));  // For objects and arrays we create a new object/array
          } else if (_.isObject(in_obj)) {
            target = _.isArray(in_obj) ? [] : {};
            copyMembers = true;
          } else {
            console.error('It seems I have forgotten a case in deep copy: please implement!');
          }
        }
        // If this is either an object or an array, we have to do a deep copy
        // and recursively copy all members.
        if (copyMembers) {
          var keys = _.keys(in_obj);
          var l = keys.length;
          for (var i = 0; i < l; i++) {
            var key = keys[i];
            deepCopy(in_obj[key], target, key, in_preserve_);
          }
        }
        if (!in_key_) {
          // If you don't give a path, then we return the original or copied object
          return target;
        } else if (!copyDirectlyIntoKey) {
          // If you give a path, we use the path as a key.
          in_target_[in_key_] = target;
        }
        return in_target_;
      }
      exportModule('LYNX.Utils.deepCopy', deepCopy);
    }());
    (function () {
      var UrnUtils = {};
      var isString = function (arg) {
        return (typeof arg === 'string' || arg instanceof String) && arg !== null;
      };
      /**
      * @param {string} repoGuid - The guid of the repository
      * @return {string} The URN for the repository
      */
      UrnUtils.repoUrn = function (repoGuid) {
        if (!isString(repoGuid)) {
          throw new TypeError('Repository guid must be a string');
        }
        return 'urn:adsk.lynx:repository:' + repoGuid;
      };
      /**
      * @param {string} branchGuid - The guid of the branch
      * @return {string} The URN for the branch
      */
      UrnUtils.branchUrn = function (branchGuid) {
        if (!isString(branchGuid)) {
          throw new TypeError('Branch guid must be a string');
        }
        return 'urn:adsk.lynx:branch:' + branchGuid;
      };
      /**
       * @typedef {object} Commit
       * @property {string} commitGuid
       * @property {string=} branchGuid
       */
      /**
       * @param {string|Commit} commit - The guid of the commit, or a { commitGuid: '', branchGuid: '' } object
       * @param {string=} branchGuid - the guid of the branch that is the commit's context
       * @return {string} The URN for the commit
       */
      UrnUtils.commitUrn = function (commit, branchGuid) {
        var commitGuid;
        if (!commit) {
          throw new TypeError('Commit must be a string or commit object');
        }
        if (isString(commit)) {
          commitGuid = commit;
        } else {
          if (!commit.commitGuid) {
            throw new TypeError('Commit must be a string or commit object');
          }
          branchGuid = commit.branchGuid;
          commitGuid = commit.commitGuid;
        }
        if (!isString(commitGuid)) {
          throw new TypeError('Commit guid must be a string or commit object');
        }
        if (branchGuid && !isString(branchGuid)) {
          throw new TypeError('Branch guid must be a string');
        }
        if (branchGuid) {
          return 'urn:adsk.lynx:commit:' + commitGuid + ':branch:' + branchGuid;
        } else {
          return 'urn:adsk.lynx:commit:' + commitGuid;
        }
      };
      /**
      * @param {string} maybeUrn - A string that might be in the format of a Lynx URN
      * @return {boolean} true if it looks like one (i.e. starts with 'urn:adsk.lynx:'); false otherwise
      */
      UrnUtils.isLynxUrn = function (maybeUrn) {
        // No memory allocs here. Look for the prefix, and
        // make sure there's at least one additional colon afterward.
        return isString(maybeUrn) && maybeUrn.startsWith('urn:adsk.lynx:') && maybeUrn.lastIndexOf(':') > 13;
      };
      /**
      * @param {string} maybeUrn - A string that might be in the format of a Lynx Repository URN
      * @return {boolean} true if it looks like one (i.e. starts with 'urn:adsk.lynx:repository:'); false otherwise
      */
      UrnUtils.isRepoUrn = function (maybeUrn) {
        return isString(maybeUrn) && maybeUrn.startsWith('urn:adsk.lynx:repository:') && maybeUrn.lastIndexOf(':') === 24;
      };
      /**
      * @param {string} maybeUrn - A string that might be in the format of a Lynx Branch URN
      * @return {boolean} true if it looks like one (i.e. starts with 'urn:adsk.lynx:branch:'); false otherwise
      */
      UrnUtils.isBranchUrn = function (maybeUrn) {
        return isString(maybeUrn) && maybeUrn.startsWith('urn:adsk.lynx:branch:') && maybeUrn.lastIndexOf(':') === 20;
      };
      /**
      * @param {string} maybeUrn - A string that might be in the format of a Lynx Commit URN
      * @return {boolean} true if it looks like one (i.e. starts with 'urn:adsk.lynx:commit:'); false otherwise
      */
      UrnUtils.isCommitUrn = function (maybeUrn) {
        if (!isString(maybeUrn)) {
          return false;
        }
        if (!maybeUrn.startsWith('urn:adsk.lynx:commit:')) {
          return false;
        }
        var fields = maybeUrn.split(':');
        if (fields.length !== 4 && fields.length !== 6) {
          return false;
        }
        return true;
      };
      /**
       * @param {string} urn - A string in the form of a Lynx commit URN
       * @return {Commit} - The branch and commit guids in an object
       */
      UrnUtils.commitToGuids = function (urn) {
        if (!this.isCommitUrn(urn)) {
          throw new TypeError('Urn must be a valid commit urn');
        }
        var urnPieces = urn.split(':');
        if (urnPieces.length !== 6 && urnPieces.length !== 4) {
          throw new TypeError('Urn must be a valid commit urn');
        }
        return {
          commitGuid: urnPieces[3],
          branchGuid: urnPieces.length === 6 ? urnPieces[5] : undefined
        };
      };
      /**
      * @param {string} urn - A valid Lynx URN
      * @return {string} The guid from the URN, or '' if it is not valid
      */
      UrnUtils.toGuid = function (urn) {
        if (this.isLynxUrn(urn)) {
          var urnPieces = urn.split(':');
          if (urnPieces.length === 4 || this.isCommitUrn(urn))
            return urnPieces[3];
        }
        return '';
      };
      exportModule('LYNX.Utils.UrnUtils', UrnUtils);
    }());
    (function () {
      /**
       * @namespace LYNX.Utils.Strings
       * @alias LYNX.Utils.Strings
       *
       * Helper functions for string processing
       */
      /**
       * Merges two strings with a separator. If one of the two is empty no separator will be added.
       * No duplicated separators will be joined
       *
       * @param {String} in_string1           - The first string to join
       * @param {String} in_string2           - The second string to join
       * @param {String} [in_separator = '/'] - The path separator
       *
       * @return {String} The joined path
       * @alias LYNX.Utils.Strings.joinPaths
       */
      var joinPaths = function (in_string1, in_string2, in_separator) {
        if (in_separator === undefined) {
          in_separator = '/';
        }
        in_string1 = in_string1 || '';
        in_string2 = in_string2 || '';
        if (!in_string1 || !in_string2 || in_string1.substr(-in_separator.length) === in_separator || in_string2.substr(0, in_separator.length) === in_separator) {
          in_separator = '';
        }
        return in_string1 + in_separator + in_string2;
      };
      exportModule('LYNX.Utils.Strings.joinPaths', joinPaths);
    }());
    (function () {
      var _ = include('underscore');
      /**
       * Inserts an object into a nested Object hierarchy. If an entry already exists, it will be overwritten.
       *
       * @param  {Object}     in_object   - The object in which we search for the entry
       * @param  {...number}  in_path     - The path within the hierarchy
       * @param  {*}          in_newEntry - The new entry to insert
       *
       * @return {boolean} Has there already been an entry?
       * @alias LYNX.Utils.NestObjectHelpers.insertInNestedObjects
       */
      var insertInNestedObjects = function (in_object) {
        var currentObject = in_object;
        // Insert all intermediate steps as needed
        for (var j = 1; j < arguments.length - 2; j++) {
          // Make sure the entry exits
          currentObject[arguments[j]] = currentObject[arguments[j]] || {};
          currentObject = currentObject[arguments[j]];
        }
        // Insert the new entry
        var result = currentObject[arguments[arguments.length - 2]] !== undefined;
        currentObject[arguments[arguments.length - 2]] = arguments[arguments.length - 1];
        return result;
      };
      /**
       * Checks, whether an entry exists under the given path in a nested Object hierarchy
       *
       * @param  {Object}     in_object  - The object in which we search for the entry
       * @param  {...number}  in_path    - The path within the hierarchy
       *
       * @return {boolean} Did an entry exist under the given path in a hierarchy
       * @alias LYNX.Utils.NestObjectHelpers.existsInNestedObjects
       */
      var existsInNestedObjects = function (in_object) {
        var currentObject = in_object;
        // traverse all intermediate steps as needed
        for (var j = 1; j < arguments.length; j++) {
          currentObject = currentObject[arguments[j]];
          if (currentObject === undefined) {
            return false;
          }
        }
        return true;
      };
      /**
       * Returns an entry from a nested hierarchy of objects
       *
       * @param  {Object}     in_object  - The object in which we search for the entry
       * @param  {...number}  path       - The path within the hierarchy
       *
       * @return {boolean} Did an entry exist under the given path in a hierarchy
       * @alias LYNX.Utils.NestObjectHelpers.getInNestedObjects
       */
      var getInNestedObjects = function (in_object) {
        var currentObject = in_object;
        // traverse all intermediate steps as needed
        for (var j = 1; j < arguments.length; j++) {
          currentObject = currentObject[arguments[j]];
          if (currentObject === undefined) {
            return undefined;
          }
        }
        return currentObject;
      };
      /**
       * Returns an entry from a nested hierarchy of objects. If it does not yet exist, the given default value is inserted.
       *
       * @param {Object}     in_object  - The object in which we search for the entry
       * @param {...number}  in_path     - The path within the hierarchy
       * @param {*}          in_default - The default value to insert into the object, if no entry is found
       *
       * @return {boolean} Did an entry exist under the given path in a hierarchy
       * @alias LYNX.Utils.NestObjectHelpers.getInNestedObjects
       */
      var getOrInsertDefaultInNestedObjects = function (in_object) {
        var currentObject = in_object;
        // traverse all intermediate steps as needed
        for (var j = 1; j < arguments.length - 1; j++) {
          var nextObject = currentObject[arguments[j]];
          if (nextObject === undefined) {
            insertInNestedObjects.apply(this, [currentObject].concat(Array.from(arguments).slice(j)));
            return arguments[arguments.length - 1];
          } else {
            currentObject = nextObject;
          }
        }
        return currentObject;
      };
      /**
       * Deletes an entry from a nested hierarchy of objects.
       * It will also delete all no longer needed levels of the hierarchy above the deleted entry
       *
       * @param {Object}     in_object  - The object in which we search for the entry
       * @param {...number}  in_path    - The path within the hierarchy
       * @alias LYNX.Utils.NestObjectHelpers.deleteInNestedObjects
       */
      var deleteInNestedObjects = function (in_object) {
        var currentObject = in_object;
        // traverse all intermediate steps as needed
        var objectList = [];
        for (var j = 1; j < arguments.length - 1; j++) {
          objectList.push(currentObject);
          currentObject = currentObject[arguments[j]];
          if (currentObject === undefined) {
            break;
          }
        }
        // Delete the entry
        if (currentObject) {
          delete currentObject[arguments[arguments.length - 1]];
          objectList.push(currentObject);
        }
        // Go backwards and remove no longer needed entries
        for (var j = objectList.length - 1; j > 0; j--) {
          if (_.isEmpty(objectList[j])) {
            delete objectList[j - 1][arguments[j]];
          }
        }
      };
      /**
       * Traverses a hierarchy of nested objects and invokes the callback function for each entry
       *
       *
       * @param {Object}   in_object                - The nested object hierarchy to traverse
       * @param {Number}   in_levels                - The number of levels to descend in the hierarchy
       * @param {Boolean}  in_invokeForHigherLevels - If this is set to true, the callback will also be invoked in
       *                                              cases where there were not in_levels many levels present in the
       *                                              hierarchy.
       * @param {function} in_callback              - Callback that will be invoked with the keys of all nested levels as
       *                                              parameters, followed by the value at that level. If not all levels
       *                                              were existent in the hierarchy, it will be passed undefined parameters
       *                                              to fill up to in_levels keys.
       * @alias LYNX.Utils.NestObjectHelpers.traverseNestedObjects
       */
      var traverseNestedObjects = function (in_object, in_levels, in_invokeForHigherLevels, in_callback) {
        // We use a stack based traversal to avoid too many recursions
        var argumentStack = [];
        var objectStack = [in_object];
        var keyStack = [_.keys(in_object)];
        var currentObject = in_object;
        var currentKeys = keyStack[0];
        var level = 1;
        while (currentObject !== undefined) {
          // Do we still have keys in the currently processed object?
          if (!_.isEmpty(currentKeys)) {
            // Get the next key from the stack
            var nextKey = currentKeys.pop();
            var nextObject = currentObject[nextKey];
            argumentStack.push(nextKey);
            // If the object stored under that key is either not an object, or we have reached the maximum recursion
            // depth, we will invoke the callback
            if (!_.isObject(nextObject) || level === in_levels) {
              // Store the stack length, to restore the stack later
              var stackLength = argumentStack.length;
              // Only invoke the callback, if we either reached the requested recursion depth, or calling was allowed
              // for higher levels
              if (in_invokeForHigherLevels || level === in_levels) {
                // Put additional undefined entries on the arguments list if necessary
                for (var i = argumentStack.length; i < in_levels; i++) {
                  argumentStack[i] = undefined;
                }
                // Push the actual content as last entry on the list of arguments
                argumentStack.push(nextObject);
                // Invoke the callback
                in_callback.apply(this, argumentStack);
              }
              // Restore the arguments stack to its length before invoking the callback
              argumentStack.length = stackLength - 1;
            } else {
              // We have an object and are not at the requested recursion depth. In that case
              // we continue the traversal at the next level, by pushing the corresponding object
              // onto the processing stack
              objectStack.push(nextObject);
              currentKeys = _.keys(nextObject);
              keyStack.push(currentKeys);
              currentObject = nextObject;
              level++;
            }
          } else {
            // We have finished processing the object at the current tip of the stack, so we remove it
            argumentStack.pop();
            objectStack.pop();
            keyStack.pop();
            if (!_.isEmpty(objectStack)) {
              // If there are still objects on the stack, we continue with those
              currentObject = objectStack[objectStack.length - 1];
              currentKeys = keyStack[keyStack.length - 1];
              level--;
            } else {
              // Otherwise, we have to stop the traversal
              break;
            }
          }
        }
      };
      exportModule('LYNX.Utils.NestObjectHelpers.insertInNestedObjects', insertInNestedObjects);
      exportModule('LYNX.Utils.NestObjectHelpers.existsInNestedObjects', existsInNestedObjects);
      exportModule('LYNX.Utils.NestObjectHelpers.getInNestedObjects', getInNestedObjects);
      exportModule('LYNX.Utils.NestObjectHelpers.getOrInsertDefaultInNestedObjects', getOrInsertDefaultInNestedObjects);
      exportModule('LYNX.Utils.NestObjectHelpers.deleteInNestedObjects', deleteInNestedObjects);
      exportModule('LYNX.Utils.NestObjectHelpers.traverseNestedObjects', traverseNestedObjects);
    }());
    (function () {
      exportModule('LYNX.Utils.emptyFunction', function () {
      });
    }());
    (function () {
      var globalObject, isBrowser;
      if (typeof window === 'undefined') {
        globalObject = global;
        isBrowser = false;
      } else {
        globalObject = window;
        isBrowser = true;
      }
      exportModule('LYNX.Utils.Environment', {
        global: globalObject,
        isBrowser: isBrowser
      });
    }());
    (function () {
      var isBrowser = include('LYNX.Utils.Environment').isBrowser;
      var pad = 0;
      var COLORS = {
        RED: isBrowser ? '#FCC' : 'red',
        BLUE: isBrowser ? '#11F' : 'blue',
        GREEN: isBrowser ? '#2F2' : 'green',
        SKY: isBrowser ? '#2AF' : 'sky',
        CYAN: isBrowser ? '#CFF' : 'cyan',
        WHITE: isBrowser ? '#FFF' : 'white',
        BLACK: isBrowser ? '#000' : 'black',
        ORANGE: isBrowser ? '#FC0' : 'orange',
        GRAY: isBrowser ? '#666' : 'gray',
        LIGHT_GRAY: isBrowser ? '#CCCCCC' : 'whitesmoke',
        DARK_GREEN: isBrowser ? '#1C1' : 'darkgreen'
      };
      var FAIL_COLOR = COLORS.RED;
      var PROGRESS_COLOR = COLORS.SKY;
      var WHITE = COLORS.WHITE;
      var BLACK = COLORS.BLACK;
      var GRAY = COLORS.GRAY;
      var colorize;
      if (isBrowser) {
        colorize = function (in_str) {
          return '%c' + in_str;
        };
      } else {
        colorize = function (in_str) {
          return in_str;
        };
      }
      /**
       * A function to process CSS styles or console printing.
       * TODO abstract browser.
       * @param {Object} in_style the CSS style.
       * For example { color : red, background : blue }.
       * @return {string} the processed output
       */
      function processStyle(in_style) {
        if (isBrowser) {
          var str = '';
          var keys = Object.keys(in_style);
          var length = keys.length;
          for (var i = 0; i < length; i++) {
            str += keys[i] + ':' + in_style[keys[i]] + ';';
          }
          return str;
        }
        // If we are not in the browser, there will be
        // fewer things we can do, but we should still do them...
        // Unfortunately it is not that easy. Modules for browser are either nasty
        // (mutating String!), or difficult to integrate...
        // TODO implement
        /*
         if( in_style['font-weight'] ){
         }
         */
        return '';
      }
      /**
       * Process the string from an array of arguments
       * @param {Array} in_arr - Elements to stringify and add to the string.
       * @return {string} the concatenated string
       */
      var processString = function (in_arr) {
        var str = '' + leftPad(pad);
        for (var i = 0; i < in_arr.length; i++) {
          str += ' ' + in_arr[i].toString();
        }
        return str;
      };
      /**
       * Print an error to console
       */
      var error = function () {
        console.error(colorize(processString(arguments)), processStyle({
          background: FAIL_COLOR,
          color: BLACK
        }));
      };
      /**
       * Print a failure message to console
       */
      var fail = function () {
        console.log(colorize(processString(arguments)), processStyle({
          background: FAIL_COLOR,
          color: BLACK
        }));
      };
      /**
       * Print a success message to console
       */
      var success = function () {
        console.log(colorize(processString(arguments)), processStyle({
          background: COLORS.DARK_GREEN,
          color: WHITE,
          'font-weight': 'bold'
        }));
      };
      /**
       * Print a progress to console
       */
      var progress = function () {
        console.log(colorize(processString(arguments)), processStyle({
          background: WHITE,
          color: PROGRESS_COLOR
        }));
      };
      /**
       * Print a warning to console
       */
      var warn = function () {
        console.log(colorize(processString(arguments)), processStyle({
          background: WHITE,
          color: COLORS.ORANGE
        }));
      };
      /**
       * Print info to console
       */
      var info = function () {
        console.log(colorize(processString(arguments)), processStyle({
          background: COLORS.LIGHT_GRAY,
          color: GRAY,
          'font-weight': 'bold'
        }));
      };
      /**
       * Pad in the terminal with in_size units (blank spaces)
       * @param {Number=} in_size the size of the padding.
       */
      var padIn = function (in_size) {
        pad += in_size || 1;
      };
      /**
       * Pad out the terminal with in_size units (blank spaces)
       * @param {Number=} in_size the size of the padding.
       */
      var padOut = function (in_size) {
        pad -= in_size || 1;
      };
      /**
       * Begin a group - this is only supported in a few browsers, and not all
       * in the terminal.
       * TODO add support to output that does not support this feature.
       */
      var group = function () {
        if (isBrowser) {
          console.group(processString(arguments));
        } else {
          console.log('-----');
          padIn(10);
          console.log(processString(arguments));
        }
      };
      /**
       * Close the last opened group
       */
      var groupEnd = function () {
        if (isBrowser) {
          console.groupEnd();
        } else {
          padOut(10);
          console.log('-----');
        }
      };
      /**
       * Print a title to console
       */
      var title = function () {
        console.log(colorize(processString(arguments)), processStyle({
          background: WHITE,
          color: COLORS.GRAY,
          'font-style': 'italic',
          'font-weight': 'bold'
        }));
      };
      /**
       * Helper unction to add space (padding) to a string
       * @param {Number} count the number of blank spaces to add
       * @return {string} the output spaces
       */
      function leftPad(count) {
        var str = '';
        for (var i = 0; i < count; i++) {
          str += ' ';
        }
        return str;
      }
      /**
       * Alternative way to generate msgs when we nee to generate a closure for the
       * message, giving the ability to produce a message functions that can be fired.
       */
      var generatorFn = function (in_cb) {
        return function () {
          var args = arguments;
          return function () {
            in_cb(args);
          };
        };
      };
      var makeError = generatorFn(error);
      var makeFail = generatorFn(fail);
      var makeProgress = generatorFn(progress);
      var makeInfo = generatorFn(info);
      var makeWarn = generatorFn(warn);
      exportModule('LYNX.Utils.PrettyPrint', {
        fail: fail,
        group: group,
        groupEnd: groupEnd,
        progress: progress,
        success: success,
        warn: warn,
        info: info,
        error: error,
        title: title,
        padIn: padIn,
        padOut: padOut,
        makeError: makeError,
        makeFail: makeFail,
        makeProgress: makeProgress,
        makeInfo: makeInfo,
        makeWarn: makeWarn
      });
    }());
    (function () {
      var EventEmitter = include('LYNX.Utils.Events.EventEmitter');
      var TypedObject = function (in_params, in_typeid) {
        if (!in_typeid) {
          console.error('TypedObject must have typeid');
          return;
        }
        EventEmitter.call(this, in_params);
        this._typeid = in_typeid;
        in_params = in_params ? in_params : {};
        this._id = in_params.id;
      };
      TypedObject.prototype = Object.create(EventEmitter.prototype);
      TypedObject.prototype.getTypeid = function () {
        return this._typeid;
      };
      TypedObject.prototype.getId = function () {
        return this._id;
      };
      exportModule('LYNX.Utils.TypedObject', TypedObject);
    }());
    (function () {
      /**
       * The Edge definition class
       * @param {NodePort} in_inputPort the start Node
       * @param {NodePort} in_outputPort the end Node
       * @class
       * @alias LYNX.Utils.Graph.Edge
       * @deprecated - this code is legacy and will soon be replaced, do not use
       */
      var Edge = function (in_startPort, in_endPort) {
        this._flip = in_endPort.isInput();
        this._visited = false;
        this._startPort = in_startPort;
        this._endPort = in_endPort;
      };
      Edge.prototype.setVisited = function () {
        this._visited = true;
      };
      Edge.prototype.wasVisited = function () {
        return this._visited;
      };
      Edge.prototype.clearVisited = function () {
        this._visited = false;
      };
      Edge.prototype.endPort = function () {
        return !this._flip ? this._endPort : this._startPort;
      };
      Edge.prototype.startPort = function () {
        return !this._flip ? this._startPort : this._endPort;
      };
      exportModule('LYNX.Utils.Graph.Edge', Edge);
    }());
    (function () {
      var Collection = include('LYNX.Utils.Datastructures.Collection');
      var TypedObject = include('LYNX.Utils.TypedObject');
      var Edge = include('LYNX.Utils.Graph.Edge');
      var portIdx = 0;
      /**
       * The type of a port
       *
       * @alias LYNX.Utils.Graph.Constants.PORT_TYPE
       */
      var PORT_TYPE = {
        INPUT: 0,
        OUTPUT: 1
      };
      /**
       * A port object
       * @param in_node
       * @param in_id
       * @alias LYNX.Utils.Graph.NodePort
       * @deprecated - this code is legacy and will soon be replaced, do not use
       *
       */
      var NodePort = function (in_params) {
        TypedObject.call(this, in_params, 'PortID');
        this._portType = in_params.PORT_TYPE === PORT_TYPE.INPUT || in_params.PORT_TYPE === PORT_TYPE.OUTPUT ? in_params.PORT_TYPE : console.error('Must specify PORT_TYPE.INPUT or PORT_TYPE.OUTPUT');
        this._belongsTo = in_params.node || console.error('NodePort must belong to a node!');
        this._idx = portIdx++;
        this._connectedPorts = new Collection('connected ports');
        this._isDirty = false;
        this._visited = false;
        this._context = 'single';
        this._dataRef = undefined;
      };
      NodePort.prototype = Object.create(TypedObject.prototype);
      NodePort.prototype.getData = function () {
        if (this.isInput() && this.isConnected()) {
          return this.getConnected()[0].endPort().getData();
        }
        if (this.isOutput()) {
          return this._dataRef;
        }
        return undefined;
      };
      NodePort.prototype.setData = function (in_dataRef) {
        this._dataRef = in_dataRef;
        return in_dataRef;
      };
      NodePort.prototype.getId = function () {
        return this._id;
      };
      NodePort.prototype.getIdx = function () {
        return this._idx;
      };
      NodePort.prototype.isConnected = function () {
        return this._connectedPorts.getCount() > 0;
      };
      NodePort.prototype.isInput = function () {
        return this._portType === PORT_TYPE.INPUT;
      };
      NodePort.prototype.isOutput = function () {
        return this._portType === PORT_TYPE.OUTPUT;
      };
      NodePort.prototype.belongsTo = function () {
        return this._belongsTo;
      };
      NodePort.prototype.isDirty = function () {
        return this._isDirty;
      };
      NodePort.prototype.setDirty = function () {
        this._isDirty = true;
      };
      NodePort.prototype.setClean = function () {
        this._isDirty = false;
      };
      NodePort.prototype.setVisited = function () {
        this._visited = true;
      };
      NodePort.prototype.wasVisited = function () {
        return this._visited;
      };
      NodePort.prototype.clearVisited = function () {
        this._visited = false;
      };
      NodePort.prototype.disconnect = function (in_id) {
        this._connectedPorts.remove(in_id);
      };
      NodePort.prototype.getConnected = function () {
        return this._connectedPorts.getAsArray();
      };
      NodePort.prototype.validateTypeMatch = function (in_port) {
        return in_port.getTypeid() === this.getTypeid();
      };
      NodePort.prototype.connect = function (in_port) {
        if (in_port.belongsTo() === this._belongsTo) {
          console.error('Can\'t connect to self node ports!');
        } else {
          if (this.validateTypeMatch(in_port)) {
            var edge = new Edge(this, in_port);
            this._connectedPorts.add(in_port.getIdx(), edge);
            in_port._connectedPorts.add(this.getIdx(), edge);
          } else {
            console.error('Can\'t connect type mismatch', in_port.getTypeid(), this.getTypeid());
          }
        }
      };
      exportModule('LYNX.Utils.Graph.NodePort', NodePort);
      exportModule('LYNX.Utils.Graph.Constants.PORT_TYPE', PORT_TYPE);
    }());
    (function () {
      var Collection = include('LYNX.Utils.Datastructures.Collection');
      var TypedObject = include('LYNX.Utils.TypedObject');
      var NodePort = include('LYNX.Utils.Graph.NodePort');
      var EMPTY_CALLBACK = include('LYNX.Utils.emptyFunction');
      var _ = include('underscore');
      var nodeIdx = 0;
      /**
       * The Node definition class
       * @class
       * @param {String} in_params.id      the identifier for this node.
       * @param {String} in_params.typeid  typeid of this node
       * @alias LYNX.Utils.Graph.Node
       * @deprecated - this code is legacy and will soon be replaced, do not use
       */
      var Node = function (in_params) {
        TypedObject.call(this, in_params, in_params && in_params.typeid !== undefined ? in_params.typeid : 'NodeID');
        this._idx = nodeIdx++;
        this._data = {};
        // @type Object
        this._inputs = new Collection('inputs', NodePort);
        this._outputs = new Collection('outputs', NodePort);
        this._visited = false;
        // @type Boolean
        this._isDirty = false;
      };
      /**
       * @constructor
       * @type {Function}
       */
      Node.prototype = Object.create(TypedObject.prototype);
      /**
       * Check if the node has an input with in_id
       * @param {String} in_id the identifier we are looking for.
       * @return {boolean} true if the input exists, false otherwise.
       */
      Node.prototype.hasInput = function (in_id) {
        return this._inputs.has(in_id);
      };
      Node.prototype.isDirty = function () {
        return this._isDirty;
      };
      Node.prototype.setDirty = function () {
        this._isDirty = true;
      };
      Node.prototype.setClean = function () {
        this._isDirty = false;
      };
      /**
       * Check if the node has an input with in_id
       * @param {String} in_id the identifier we are looking for.
       * @return {boolean} true if the input exists, false otherwise.
       */
      Node.prototype.hasOutput = function (in_id) {
        return this._outputs.has(in_id);
      };
      Node.prototype.evaluate = EMPTY_CALLBACK;
      /**
       * A unique idx in this scope
       * @return {String} this node identifier
       */
      Node.prototype.getIdx = function () {
        return this._idx;
      };
      /**
       * Sets the visited flag to true on this node
       */
      Node.prototype.setVisited = function () {
        this._visited = true;
      };
      /**
       * Check if this node was visited
       * @return {Boolean} true if it was visited since last clearVisited
       */
      Node.prototype.wasVisited = function () {
        return this._visited;
      };
      /**
       * TODO this doesn't take a port filter, it simply go through all of them
       * Make so that we can specify which data types or port identifiers we want to
       * recurse through
       * @return {Array}
       */
      Node.prototype.getAllPathsFromRoot = function () {
        var allPaths = [];
        var activePath = [];
        this.recurseThroughAllPaths(function (in_node, in_port, in_connection, in_connectedNode) {
          var path = {};
          path.node = in_node;
          path.input = in_port.getId();
          if (in_connection) {
            path.output = in_connection.getId();
            path.connectedTo = in_connectedNode.getMetadata('name');
          }
          activePath.push(path);
          if (in_node.isRoot()) {
            allPaths.push(activePath.reverse());
            // when we reach root we start a new path
            activePath = [];
          }
        });
        return allPaths;
      };
      /**
       * TODO this will simply recurse through all possible paths (connected ports)
       * TODO specify what kinds of types of connections we want to retrieve paths for
       * @param in_path
       * @param Callback in_function with parameters ( input and connection )
       * @return {*|Array}
       */
      Node.prototype.recurseThroughAllPaths = function (in_function) {
        var inports = this._inputs.getAsArray();
        var connected;
        for (var i = 0; i < inports.length; i++) {
          if (this.isRoot()) {
            in_function(this, inports[i]);
          }
          connected = inports[i].getConnected();
          for (var j = 0; j < connected.length; j++) {
            in_function(this, inports[i], connected[i].endPort(), connected[i].endPort().belongsTo());
            connected[j].endPort().belongsTo().recurseThroughAllPaths(in_function);
          }
        }
      };
      // Helper functions
      var portIteratorClearFn = function (in_key, in_port) {
        in_port.clearVisited();
        if (in_port.isConnected()) {
          var conn = in_port.getConnected();
          for (var i = 0; i < conn.length; i++) {
            conn[i].clearVisited();
          }
        }
      };
      /**
       * Clear the visited flag.
       */
      Node.prototype.clearVisited = function () {
        this._visited = false;
        this._inputs.iterate(portIteratorClearFn);
        this._outputs.iterate(portIteratorClearFn);
      };
      /**
       * Add a port to this node
       * @return {NodePort}
       */
      Node.prototype.addPort = function (in_id, in_port) {
        if (in_port.isInput()) {
          this._inputs.add(in_id, in_port);
        } else if (in_port.isOutput()) {
          this._outputs.add(in_id, in_port);
        } else {
          console.error('Is this even a port?');
        }
        return in_port;
      };
      /**
       * Get the output NodeList
       * @return {EdgeList}
       */
      Node.prototype.getOutputs = function () {
        return this._outputs.getItems();
      };
      /**
       * Get the inputs NodeList
       * @return {EdgeList}
       */
      Node.prototype.getOutput = function (in_id) {
        if (this._outputs.has(in_id)) {
          return this._outputs.item(in_id);
        } else {
          console.error('No such output!', in_id, this);
        }
        return undefined;
      };
      /**
       * Get the inputs NodeList
       * @return {EdgeList}
       */
      Node.prototype.getInputs = function () {
        return this._inputs.getItems();
      };
      /**
       * Get the specified input with key in_id
       * @param {String} in_id
       * @return {Edge} the node with this id, or undefined if none exists.
       */
      Node.prototype.getInput = function (in_id) {
        if (this._inputs.has(in_id)) {
          return this._inputs.item(in_id);
        } else {
          console.error('No such input!', in_id, this);
        }
        return undefined;
      };
      /**
       * Get metadata on this node with a key
       * @param {String} in_key
       * @return {*}
       */
      Node.prototype.getMetadata = function (in_key) {
        return this._data[in_key];
      };
      /**
       * Set metadata on this node with a key and a value
       * @param {String} in_key the key
       * @param {*} in_value the value
       * @return {*} returns the set value
       * @private
       */
      Node.prototype._setMetadata = function (in_key, in_value) {
        this._data[in_key] = in_value;
        return in_value;
      };
      /**
       * Copies the meta data from another node
       * Note: This only uses a shallow copy.
       *
       * @param {LYNX.Utils.Graph.Node} in_otherNode - The node from which the meta data is copied
       */
      Node.prototype.copyMetaDataFromNode = function (in_otherNode) {
        this._data = _.clone(in_otherNode);
      };
      Node.prototype.isRoot = function () {
        return false;
      };
      /**
       * The path of a module
       * @param {NodePath} in_modulePath
       * @param {function} in_callback the callback we will
       */
      Node.prototype.expand = function (in_modulePath, in_callback) {
        var node = this;
        var invalidPath = false;
        in_modulePath.iterate(function (in_str) {
          if (!invalidPath) {
            if (node.hasInput(in_str)) {
              node = node.getInput(in_str).endPort();
              in_callback(invalidPath, node);
            } else {
              invalidPath = true;
              in_callback(invalidPath, undefined);
              throw new Error('Invalid path: ' + in_modulePath.getAsString() + ' stops at ' + in_str);
            }
          }
        });
      };
      exportModule('LYNX.Utils.Graph.Node', Node);
    }());
    (function () {
      var print = include('LYNX.Utils.PrettyPrint');
      var Collection = include('LYNX.Utils.Datastructures.Collection');
      var TypedObject = include('LYNX.Utils.TypedObject');
      var Node = include('LYNX.Utils.Graph.Node');
      var _ = include('underscore');
      var leftSpace = function leftSpace(count) {
        var str = '';
        for (var i = 0; i < count; i++) {
          str += ' ';
        }
        return str;
      };
      /**
       * A simple BaseGraph data structure to hold our nodes.
       * @constructor
       * @class
       * @alias LYNX.Utils.Graph
       * @deprecated - this code is legacy and will soon be replaced, do not use
       */
      var Graph = function (in_params) {
        TypedObject.call(this, in_params, 'GraphID');
        this._initialize(in_params);
      };
      /**
       * @constructor
       * @type {Function}
       */
      Graph.prototype = Object.create(TypedObject.prototype);
      Graph.prototype.setRoot = function (io_rootNode) {
        this._root = io_rootNode;
        io_rootNode.isRoot = function () {
          return true;
        };
        return this._root;
      };
      Graph.prototype._initialize = function (in_params) {
        if (this._root) {
          delete this._root;
        }
        if (this._nodes) {
          delete this._nodes;
        }
        this._root = new Node();
        // this is a rooted graph
        this._nodes = new Collection('nodes', Node);
        this._nodes.add('root', this._root);
        return this;
      };
      /**
       * BaseGraph traversal routine
       * @param {Node} in_node the starting node
       * @param {Function} cb the function to call at each node
       */
      Graph.prototype.traverse = function (in_node, cb) {
        var depth = 0;
        /**
         * Recursive depth first search
         * @param in_dfsnode
         * @param dfscb
         */
        function dfs(in_dfsnode, dfscb) {
          print.info(leftSpace(depth) + '|_' + in_dfsnode.getMetadata('name') + ':' + in_dfsnode.getId());
          depth++;
          depth += in_dfsnode.getId().length;
          var outputs = in_dfsnode.getOutputs();
          var outputKeys = Object.keys(outputs);
          for (var i = 0; i < outputKeys.length; i++) {
            var currentOutPut = outputs[outputKeys[i]];
            if (currentOutPut.isConnected() && !currentOutPut.wasVisited()) {
              print.info(leftSpace(depth) + ' > ' + i);
              var connections = currentOutPut.getConnected();
              for (var j = 0; j < connections.length; j++) {
                print.info(leftSpace(depth) + ' > ' + j);
                if (!connections[j].wasVisited()) {
                  connections[j].setVisited();
                  dfs(connections[j].startPort().belongsTo(), dfscb);
                }
              }
              // after lopping over all connections we are good to mark this
              // port as visited
              currentOutPut.setVisited();
            }
          }
          in_dfsnode.setVisited();
          dfscb(in_dfsnode);
          depth--;
          depth -= in_dfsnode.getId().length;
        }
        this.cleanAllVisitedFlags();
        dfs(in_node, cb);
      };
      /**
       * Add nodes to our BaseGraph
       * TODO check for cycles
       * @param {Node} in_node
       */
      Graph.prototype.add = function (in_uid, in_node) {
        this._nodes.add(in_uid, in_node);
        this.trigger('onAdd', this, [
          in_uid,
          in_node
        ]);
      };
      Graph.prototype.getNodeByUid = function (in_uid) {
        return this._nodes.item(in_uid);
      };
      /**
       * Remove a node
       * @param {Node} in_node
       */
      Graph.prototype.remove = function (in_uid) {
        var node = this._nodes.item(in_uid);
        this._nodes.remove(in_uid);
        this.trigger('onRemove', this, [node]);
      };
      /**
       * Get the root of this BaseGraph.
       * @return {Node}
       */
      Graph.prototype.getRoot = function () {
        return this._root;
      };
      /**
       * Method to clear the visited flag
       */
      Graph.prototype.cleanAllVisitedFlags = function () {
        var nodes = this._nodes.getAsArray();
        for (var i = 0; i < nodes.length; i++) {
          nodes[i].clearVisited();
        }
      };
      /**
       * Method to expand the graph from the root and towards a path (the in_modulePath).
       * @param {NodePath} in_modulePath the path we want to expand.
       * @return {Node} the last node in the path.
       */
      Graph.prototype.expandFromRoot = function (in_modulePath) {
        var graph = this;
        var root = graph.getRoot();
        var modulePath = in_modulePath;
        if (modulePath.getAsString() === root.getId()) {
          return root;
        } else {
          // we want to expand from root node, therefore we drop any potential
          // redundant 'root.' in the path.
          if (modulePath.startsWith('root')) {
            modulePath = modulePath.right('root');
          }
          var lastNodeInPath = root;
          // Expand the namespace to the last node in the graph
          root.expand(modulePath, function (err, in_node) {
            if (!err) {
              lastNodeInPath = in_node;
            }
          });
          // If the last node expanded is not really the last segment
          // of our modulePath, this would mean we have no resolution
          // for this path from root.
          // At this point in time the path is not defined. There is no guarantee
          // this path won't become defined at some point... therefore all we know
          // is an undefined path from graph._root.
          if (modulePath.right(lastNodeInPath.getId()).getLength() > 0) {
            return undefined;
          } else {
            // At the end of our modulePath, there was a node. So we return it!
            return lastNodeInPath;
          }
        }
      };
      /**
       * Find the lowest common ancestors between two nodes A and B. That means, we find those nodes, which are parents
       * of A and B, but are themselves not parent of another LCA.
       *
       * This algorithms starts a parallel breadth first search from both passed nodes. When a node has been reached from
       * both start points, it is marked as a candidate for the lowest common ancestor. Since we are only searching for
       * nodes which are not parent of another ancestor, we have to continue the traversal and mark all parents of the
       * ancestors as stale (which means, they can be removed from the list of LCAs).
       *
       * TODO: When can we stop the traversal? If we have multiple LCAs, the traversal is possibly continued up to the
       * root of the graph, since we cannot be sure, that one of the nodes is still a parent of the other nodes.
       * If only one LCA is left and all nodes are marked as stale, we stop the traversal, since we can be sure in that
       * case that the node is the actual LCA.
       *
       * @param {LYNX.Utils.Graph.Node}                 in_a -
       *     The first node to start the search from
       * @param {LYNX.Utils.Graph.Node}                 in_b -
       *     The second node to start the search from
       * @param {Object}                               in_options
       * @param {Boolean}                              [in_options.returnMultiple=false] -
       *     Should multiple ancestors be returned? If set to false, only the first one is returned. There can be
       *     cases, where multiple ancestors exist, in those cases the returned one is selected arbitrary.
       * @param {Array<{pathFromA: Array, pathFromB: Array}>} [in_options.out_paths]  -
       *     Returns an array with the paths along which in_a and in_b are connected to each of the common ancestors
       *     (in the order they are returned)
       *
       * @return {LYNX.Utils.Graph.Node|undefined|Array<LYNX.Utils.Graph.Node>} The lowest common ancestor (or an array
       *     of ancestors, if in_returnMultiple is true), or undefined if none could be found
       */
      Graph.prototype.lowestCommonAncestor = function (in_a, in_b, in_options) {
        in_options = in_options || {};
        // Stores the nodes that have already been visited for each of the two start nodes
        var visitedNodes = [
          {},
          {}
        ];
        // Nodes which have been marked as 'stale': this means that they are themselves ancestor of a common parent
        var staleNodes = {};
        // Keeps track of the nodes from which a node could be reached to extract the path at the end
        var previousNodes = {};
        // The queue with the nodes that have still to be processed
        var processingQueue = [
          {
            node: in_a,
            inputIndex: 0,
            stale: false
          },
          {
            node: in_b,
            inputIndex: 1,
            stale: false
          }
        ];
        var lowestCommonAncestors = {};
        var allStale = false;
        while (!_.isEmpty(processingQueue)) {
          // Check whether all entries on the processing queue
          // are stale
          if (!allStale) {
            // Once all are stale, this will never change again
            allStale = true;
            for (var i = 0; i < processingQueue.length; i++) {
              if (!processingQueue[i].stale && !staleNodes[processingQueue[i].node.getIdx()]) {
                allStale = false;
                break;
              }
            }
          }
          // If the full processing queue is stale, we can stop the traversal
          // if we only have one common ancestor. If there is more than one
          // ancestor we have to continue, since we have to check, whether one is
          // the parent of the other
          if (allStale && _.keys(lowestCommonAncestors).length === 1) {
            break;
          }
          var entry = processingQueue.pop();
          var id = entry.node.getIdx();
          var otherIndex = 1 - entry.inputIndex;
          var stale = entry.stale || staleNodes[entry.node.getIdx()];
          if (stale) {
            staleNodes[id] = true;
            // Remove the entry from the dictionary with
            // lowestCommonAncestors
            delete lowestCommonAncestors[id];
          }
          // Check, whether this node has already been visited
          // by a path starting from the other starting point
          if (visitedNodes[otherIndex][id] !== undefined && !stale) {
            // We have found a common ancestor
            lowestCommonAncestors[id] = entry.node;
            // All parents that can be reached from this node on are stale
            stale = true;
          }
          if (!visitedNodes[entry.inputIndex][id]) {
            // If the node has not yet been visited enqueue all
            // input ports
            var inports = entry.node.getInputs();
            var inportNames = _.keys(inports);
            for (var i = 0; i < inportNames.length; i++) {
              var connected = inports[inportNames[i]].getConnected();
              for (var j = 0; j < connected.length; j++) {
                var connectedNode = connected[j].endPort().belongsTo();
                // Store the path information to reach this node
                if (in_options.out_paths) {
                  previousNodes[connectedNode.getIdx()] = previousNodes[connectedNode.getIdx()] || [];
                  // We don't overwrite an existing entry, since this indicates that the node has already
                  // been reached via a shorter path
                  if (previousNodes[connectedNode.getIdx()][entry.inputIndex] === undefined) {
                    previousNodes[connectedNode.getIdx()][entry.inputIndex] = {
                      node: entry.node,
                      link: connected[j]
                    };
                  }
                }
                var queueEntry = {
                  node: connectedNode,
                  inputIndex: entry.inputIndex,
                  stale: stale
                };
                // If this node is stale and it has already been visited from the other starting
                // point, we jump the queue to catch up
                if (stale && visitedNodes[1 - queueEntry.inputIndex][queueEntry.node.getIdx()]) {
                  processingQueue.push(queueEntry);
                } else {
                  processingQueue.unshift(queueEntry);
                }
                if (stale) {
                  staleNodes[connectedNode.getIdx()] = true;
                }
              }
            }
          }
          // Mark the node as visited
          visitedNodes[entry.inputIndex][id] = true;
        }
        // Get the lowest common ancestors from the dictionary
        var lowestCommonAncestors = _.values(lowestCommonAncestors);
        // Extract the path information for both input nodes
        if (lowestCommonAncestors.length > 0 && in_options.out_paths) {
          for (var i = 0; i < (in_options.returnMultiple ? lowestCommonAncestors.length : 1); i++) {
            in_options.out_paths.push({
              pathFromA: [],
              pathFromB: []
            });
            // Extract both paths separately
            for (var j = 0; j < 2; j++) {
              var path = j === 0 ? in_options.out_paths[i].pathFromA : in_options.out_paths[i].pathFromB;
              path.push({ node: lowestCommonAncestors[i] });
              if (previousNodes[lowestCommonAncestors[i].getIdx()]) {
                var pathSegmentEntry = previousNodes[lowestCommonAncestors[i].getIdx()][j];
                while (pathSegmentEntry) {
                  path[path.length - 1].output = pathSegmentEntry.link.startPort().getId();
                  path.push({
                    node: pathSegmentEntry.node,
                    input: pathSegmentEntry.link.endPort().getId(),
                    link: pathSegmentEntry.link
                  });
                  pathSegmentEntry = previousNodes[pathSegmentEntry.node.getIdx()] && previousNodes[pathSegmentEntry.node.getIdx()][j];
                }
              }
            }
          }
        }
        // If the calling application only requests one common ancestor, we take the first one
        return in_options.returnMultiple ? lowestCommonAncestors : lowestCommonAncestors[0];
      };
      /**
       * Flush this graph. Effectively getting rid of all nodes and it's root.
       */
      Graph.prototype.flush = function () {
        return this._initialize();
      };
      exportModule('LYNX.Utils.Graph.Graph', Graph);
    }());
    (function () {
      var NODE_PATH_DELIMITER = '.';
      var MSG = { MODULE_DOES_NOT_EXIST: 'Module doesn\'t exist ' };
      /**
       * NodePath is a formal definition of paths in a node graph. A path is an
       * ordered list (array) where each segment stores metainformation about the
       * particular node.
       * @param {Array<String>} in_pathArray an array of strings where each
       * item represents one segment of the path.
       * @constructor
       * @alias LYNX.Utils.Graph.NodePath
       * @deprecated - this code is legacy and will soon be replaced, do not use
       */
      var NodePath = function (in_pathArray) {
        this._path = in_pathArray.slice();  // cheap copy input array
      };
      /**
       * Clone this ModulePath
       * @return {NodePath}
       */
      NodePath.prototype.clone = function () {
        return new NodePath(this.getAsArray());
      };
      /**
       * A simple iterator
       * @param in_callback Wil be called at each segment of the path.
       */
      NodePath.prototype.iterate = function (in_callback) {
        var l = this._path.length;
        for (var i = 0; i < l; i++) {
          try {
            in_callback(this._path[i]);
          } catch (e) {
            break;
          }
        }
      };
      /**
       * Tests if the path starts with in_str
       * @param {String} in_str the test string
       * @return {Boolean} true if path starts with in_str, false otherwise
       */
      NodePath.prototype.startsWith = function (in_str) {
        return this._path[0] === in_str;
      };
      /**
       * A handy function to accesss this module path as an array.
       * @return {Array<String>} this path as an array
       */
      NodePath.prototype.getAsArray = function () {
        return this._path.slice();
      };
      /**
       * Join another ModulePath to this module path. This operation is going to
       * mutate this path.
       * @param {NodePath} in_modulePath the ModulePath we want to join to this.
       */
      NodePath.prototype.join = function (in_modulePath) {
        this._path = this._path.concat(in_modulePath.getAsArray());
      };
      /**
       * Method to get the number of segments in this ModulePath.
       * @return {Number} Returns the number of segments.
       */
      NodePath.prototype.getLength = function () {
        return this._path.length;
      };
      /**
       * Get this ModulePath as a String
       * @return {string} return a string from this path.
       */
      NodePath.prototype.getAsString = function () {
        var str = '';
        for (var i = 0; i < this._path.length; i++) {
          str += this._path[i];
          if (i !== this._path.length - 1) {
            str += NODE_PATH_DELIMITER;
          }
        }
        return str;
      };
      NodePath.prototype.toString = NodePath.prototype.getAsString;
      /**
       * Gets the last position of this path.
       * returns {String}
       */
      NodePath.prototype.last = function () {
        return this._path[this._path.length - 1];
      };
      /**
       * Gets the path to the right of in_str – whereby the input string is
       * assumed to be one of the segments of this path.
       * @param {String} in_str
       * @return {NodePath} a new ModulePath that symbolizes the right portion
       * of this path after the in_str.
       */
      NodePath.prototype.right = function (in_str) {
        var idx = this._path.indexOf(in_str);
        if (idx < 0) {
          throw new Error(MSG.MODULE_DOES_NOT_EXIST + this.getAsString());
        } else {
          return new NodePath(this._path.slice(idx + 1));
        }
      };
      exportModule('LYNX.Utils.Graph.NodePath', NodePath);
    }());
    (function () {
      /**
       * Constructor for the Apigee library
       *
       * @constructor
       */
      var ApigeeLib = {};
      // For matching, make headers lower-case
      ApigeeLib.authorization = 'authorization';
      ApigeeLib.xAdsTokenData = 'x-ads-token-data';
      /**
       * Construct a header with this UserId (OxygenId), as expected from Apigee (for tests)
       *
       * @param {string} userId - UserId
       * @return {object} - Header with userId set
       */
      ApigeeLib.withUserId = function (userId) {
        var result = {};
        result[ApigeeLib.xAdsTokenData] = JSON.stringify({ access_token: { userid: userId } });
        return result;
      };
      /**
       * Construct a header with this ClientId, as expected from Apigee (for tests)
       *
       * @param {string} clientId - ClientId
       * @return {object} - Header with clientId set
       * */
      ApigeeLib.withClientId = function (clientId) {
        var result = {};
        result[ApigeeLib.xAdsTokenData] = JSON.stringify({ access_token: { client_id: clientId } });
        return result;
      };
      exportModule('LYNX.Utils.ApigeeLib', ApigeeLib);
    }());
    (function () {
      var _ = include('underscore');
      var EventEmitter = include('LYNX.Utils.Events.EventEmitter');
      var generateGUID = include('LYNX.Utils.generateGUID');
      var STOREKEY = '__attributes';
      var BINDSTATEKEY = '__bindstate';
      // Define private functions vars (to avoid linting warnings).
      var _resetBindings, _setterName, _getterName, _isName, _changedKey, _eventKey, _addAttributes, _lazyInit, _validateKey, _prepareBindings, _get, _set, _camelize, _clearBinding, _setBoundValue, _addBindings, _collectContexts, _addGetterAndSetters, _addGetterAndSetter, _clearBindings, _getPathComponents, _createSetterWithOverride, _getAttribute, _setAttribute, _addAttributeListener, _removeAttributeListener, _splitAttributesAsPathComponents;
      // Storage for installed plugins
      var _plugins = [];
      // The default plugin (for attributes)
      var _attribute_plugin;
      /**
       * The Binding namespace
       * @type {Object}
       * @alias {LYNX.Utils.DataBinding}
       */
      var Binding = {};
      /**
       * @typedef {Object} Binding~PluginSpec
       * Plugin specification
       * @global
       * @property {string} name The plugin name
       * @property {string} separator The plugin separator (must be asingle character)
       * @property {function} getter  The getter function, takes a single string as a parameter and must return a value
       * @property {function} setter  The setter function, takes a string (the path to set the value to)
       *                              and the value as parameters
       * @property {function} bind    The binding function, takes a string (the path to listen)
       *                              and a function as parameters.
       *                              The function expects to be called each time the value changes.
       *                              No parameters are expected.
       * @property (function) unbind  The unbind function, takes a string (the path to unbind) and
       *                              the function that was passed to the bind call.
       */
      /**
       * @typedef {Object} Binding~Attribute
       * An attribute specification. This is a simple object with, for each property,
       * a default value specified
       * @global
       */
      /**
      * @typedef {Object} Binding~ComputedAttribute
      * An attribute calculated from other attributes
      * @global
      * @property {Array.<string>}  from  List of attributes to calculate the value from
      * @property {function}  evaluate  Evaluation function. Takes a list of values (one per attribute)
      *                                 and must return a calculated value.
      */
      /**
       * @typedef {Object} Binding~DataBinding
       * A binding specification.
       * @global
       * @property {string|Array.<string>} from The from expression of the binding. If an array is provided
       *                                        then multiple values are used to calculated the result.
       *                                        A transform member *must* be provided, and twoWay must not be true.
       * @property {string} to The to expression of the binding
       * @property {Binding~Transform=} transform The transformation function of the binding
       * @property {boolean} twoWay If true, then the binding is two ways (reciprocal)
       * @property {boolean} debug  If true, logs to the console informations regarding the binding process.
       */
      /**
      * @typedef {function} Binding~Transform
      * A binding transformation function.
      * @global
      * @param {*|Array.<*>} value The value to transform. An array is passed if the 'from' expression of the
      *                            binding is an array itself.
      * @property {string} direction  The transformation direction. Can be either 'to' or 'from'
      */
      /**
      * @typedef {Object} Binding~PathComponent
      * A path item
      * @private
      * @property {string} path The partial path for that component
      * @property {Binding~PluginSpec} plugin The plugin to use
      */
      /**
       * @typedef {Object} Binding~Context
       * A binding context
       * @private
       * @property  {boolean} validContext true if an attribute was found
       * @property  {function} fct A intermediate function, used to have a placeholder to the bind calls.
       * @property  {function} action The function to call if the value changes
       * @property  {object} obj The intermediate object
       * @property  {string} path The partial subpath of that context
       */
      /**
       * Add observable attributes to a javascript object or its prototype.
       * @param  {function|object} in_obj A constructor function or an instance.
       * @param {Binding~Attribute} in_attributes An attribute list definition
       *                            Each attribute has a default value set.
       *                            Example: {
       *                            	strAttribute1: 'defaultValue1',
       *                            	boolAttribute2: false,
       *                            	numberAttr3: 25
       *                            }
       */
      Binding.addAttributes = function (in_obj, in_attributes) {
        if (_.isFunction(in_obj)) {
          console.assert(_.isObject(in_obj.prototype));
          _addAttributes.call(in_obj.prototype, in_attributes);
        } else if (_.isObject(in_obj)) {
          _addAttributes.call(in_obj, in_attributes);
          _resetBindings.call(in_obj);
        } else {
          console.error('Cannot add attributes to object', in_obj, ': invalid type', typeof in_obj(), 'accepted types are object or function');
        }
      };
      /**
       * Add data bindings to a javascript object or its prototype.
       * @param  {function|object} in_obj A constructor function or an instance.
       * @param {Array.<Binding~DataBinding>} in_bindings A list of data bindings
       *                            Example: [{
       *                            	from: 'value1',
       *                            	to: 'value2'
       *                            }]
       */
      Binding.addDataBindings = function (in_obj, in_bindings) {
        if (_.isFunction(in_obj)) {
          // Bindings are added to a prototype. Let's save them into
          // tHe function prototype (into the __bindings member)
          in_obj.prototype.__bindings = in_obj.prototype.__bindings || [];
          _addBindings(in_obj.prototype.__bindings, in_bindings);
          in_obj.prototype.resetDataBindings = _resetBindings;
          in_obj.prototype.clearDataBindings = _clearBindings;
        } else if (_.isObject(in_obj)) {
          // We are adding bindings to a specific object. Let's add them to
          // that instance bindings
          _lazyInit.call(in_obj);
          _addBindings(in_obj[BINDSTATEKEY].bindings, in_bindings);
          in_obj.resetDataBindings = _resetBindings;
          in_obj.clearDataBindings = _clearBindings;
          // Reset newly added bindings only
          _resetBindings.call(in_obj, in_bindings);
        } else {
          console.error('Cannot add bindings to object', in_obj, ': invalid type', typeof in_obj, 'accepted types are object or function');
        }
      };
      /**
       * Add computed attributes to a javascript object or its prototype.
       * A computed attribute is really a mix of attribute and binding
       * @param  {function|object} in_obj A constructor function or an instance.
       * @param {Object.<Binding~ComputedAttribute>} in_computed A computed attribute list definition
       *                            Example: {
       *                            	strAttribute1: {
       *                            		from: ['value1', 'value2']},
       *                            	  evaluate: function(v1, v2) {return v1+v2;}
       *                              }
       *                            }
       */
      Binding.addComputedAttributes = function (in_obj, in_computed) {
        var newAttributes = _.object(_.keys(in_computed), []);
        Binding.addAttributes(in_obj, newAttributes);
        var bindings = _.keys(in_computed).map(function (key) {
          return {
            from: in_computed[key].from,
            to: key,
            transform: in_computed[key].evaluate
          };
        });
        Binding.addDataBindings(in_obj, bindings);
      };
      /**
       * Registers a listening function for changes to a given attributes
       * @param  {object} in_obj           An object to listen to
       * @param  {string} in_attributeName The attribute name to listen
       * @param  {function} in_fct         The callback function
       */
      Binding.addAttributeListener = function (in_obj, in_attributeName, in_fct) {
        _lazyInit.call(in_obj);
        EventEmitter.prototype.addListener.call(in_obj[BINDSTATEKEY], in_attributeName, in_fct);
      };
      /**
       * Unregisters a attribute listening function
       * @param  {object} in_obj           An object to listen to
       * @param  {string} in_attributeName The attribute to stop listening
       * @param  {function} in_fct         The callback function that was passed to addAttributeListener.
       */
      Binding.removeAttributeListener = function (in_obj, in_attributeName, in_fct) {
        _lazyInit.call(this);
        EventEmitter.prototype.removeListener.call(in_obj[BINDSTATEKEY], in_attributeName, in_fct);
      };
      /**
       * A special return value for get or transform calls. When a getter or a transform
       * returns this value, then any further operator stops (f.e. binding assignation is not
       * performed)
       * @type {Object}
       */
      Binding.noValue = {};
      /**
       * Add attributes to a javascript object
       * @param  {object} in_attributes list of attributes
       * @private
       */
      _addAttributes = function (in_attributes) {
        _addGetterAndSetters.call(this, in_attributes);
      };
      /**
       * Splits a path to path components
       * @param  {string} in_path The path to Splits
       * @return {Array.<Binding~PathComponent>} The splitted path
       */
      _getPathComponents = function (in_path) {
        var result = [];
        if (_plugins.length > 0) {
          // There are plugins to consider.
          // The path how the following form
          // [attribute[.attribute]][{separator}pluginExpression][{separator}pluginExpression]
          var pluginSeparators = _.pluck(_plugins, 'separator');
          // Create a regexp for the split operation
          var regexp = '(\\' + pluginSeparators.join('|\\') + ')';
          var split = in_path.split(new RegExp(regexp));
          var idx = 0;
          if (split[0] === '') {
            // The regexp returns '' as the first element of the split if the first
            // character is a separator.
            idx++;
          } else {
            // If the first element of the path is not a plugin separator, then we assume it
            // to be a path segment for an attribute
            result = _splitAttributesAsPathComponents(split[idx++]);
          }
          while (idx < split.length) {
            var sep = split[idx++];
            var expr = split[idx++];
            var plugins = _.where(_plugins, { separator: sep });
            if (plugins.length > 0) {
              result.push({
                path: expr,
                plugin: plugins[0]
              });
            }
          }
        } else {
          result = _splitAttributesAsPathComponents(in_path);
        }
        return result;
      };
      /**
       * This function is used below and splits the provided path in a sequence
       * of pathComponents using the default plugin.
       * @param {string} in_path The path to splits
       * @return {Array.<Binding~PathComponent>} The splitted path as attribute path components
       */
      _splitAttributesAsPathComponents = function (in_path) {
        return in_path.split('.').map(function (c) {
          return {
            path: c,
            plugin: _attribute_plugin
          };
        });
      };
      /**
       * Add bindings to an object
       * @param {Array.<Binding~DataBinding>} in_databinding An array (possibly empty of data bindings)
       * @param {object} in_bindings List of bindings to add
       * @private
       */
      _addBindings = function (in_databinding, in_bindings) {
        console.assert(_.isArray(in_databinding));
        if (in_bindings && in_bindings.length) {
          _prepareBindings(in_bindings);
          [].push.apply(in_databinding, in_bindings);
        }
      };
      /**
       * Add attributes to an existing constructor function
       * @param  {object} in_object The object to which attributes and bindings must be added.
       * @param  {object} in_def -
       * @param {object} in_def.attributes The type attributes
       * @param {object} in_def.databindings The type bindings
       * @param {object} in_def.computed The type computed properties
       * @return {object} The provided object, with its updated methods.
       */
      Binding.build = function (in_object, in_def) {
        Binding.addAttributes(in_object, in_def.attributes);
        Binding.addComputedAttributes(in_object, in_def.computed);
        Binding.addDataBindings(in_object, in_def.databindings);
        return in_object;
      };
      /**
       * Constructor of an ad-hoc attributed object
       * @param  {object} in_def -
       * @param {object} in_def.attributes The type attributes
       * @param {object} in_def.databindings The type bindings
       * @param {object} in_def.computed The type computed properties
       */
      Binding.Instance = function (in_def) {
        Binding.addAttributes(this, in_def.attributes);
        Binding.addComputedAttributes(this, in_def.computed);
        Binding.addDataBindings(this, in_def.databindings);
      };
      /**
       * Adds a plugin to the plugin list
       * @param  {@pluginSpec} in_pluginSpec The pluginSpecification
       * @return {boolean}           true if the plugin was correctly added
       */
      Binding.addPlugin = function (in_pluginSpec) {
        if (_.where(_plugins, { name: in_pluginSpec.name }).length) {
          console.warn('A plugin with name ', in_pluginSpec.name, 'is already registered');
          return false;
        }
        if (_.where(_plugins, { separator: in_pluginSpec.separator }).length) {
          console.warn('A plugin with separator ', in_pluginSpec.separator, 'is already registered');
          return false;
        }
        _plugins.push(in_pluginSpec);
        return true;
      };
      /**
       * Performs the initialization of the internal data structures.
       * @return {boolean} True if the object is initialize, false if it was already the case.
       * @private
       */
      _lazyInit = function () {
        console.assert(_.isObject(this));
        var wasBound = true;
        if (this[STOREKEY] === undefined) {
          this[STOREKEY] = {};
        }
        if (this[BINDSTATEKEY] === undefined) {
          wasBound = false;
          this[BINDSTATEKEY] = {
            listeners: EventEmitter.prototype.listeners,
            // Required by the eventEmitter implementation
            bindings: _.clone(this.__bindings || [])
          };
        }
        return wasBound;
      };
      /**
       * Adds getters and setters of the provided list of attributes
       * @param  {object} in_attributes List of attributes
       */
      _addGetterAndSetters = function (in_attributes) {
        console.assert(_.isObject(this));
        _.each(in_attributes, function (value, key) {
          _addGetterAndSetter(this, key, value);
        }, this);
      };
      /**
       * Creates a setter function, which overrides a provided getter/setter pair.
       * @param  {string} in_key      The attribute name
       * @param  {function} in_previousGetter  The previous getter function
       * @param  {function} in_previousSetter  The previous setter function
       * @return  {function} The new setter
       */
      _createSetterWithOverride = function (in_key, in_previousGetter, in_previousSetter) {
        return function (in_value) {
          var previous = in_previousGetter.call(this);
          var current = in_value;
          if (previous !== in_value) {
            var keyChanged = _changedKey(in_key);
            var keyEvent = _eventKey(in_key);
            var keyValidate = _validateKey(in_key);
            var validationHandler = this[keyValidate];
            // The validationHandler may return false, in which case the change is refused.
            if (validationHandler) {
              if (validationHandler.call(this, in_value, previous) === false) {
                return previous;
              }
            }
            in_previousSetter.call(this, in_value);
            current = in_previousGetter.call(this);
            var changeHandler = this[keyChanged];
            if (changeHandler) {
              changeHandler.call(this, current, previous);
            }
            if (this[BINDSTATEKEY]) {
              EventEmitter.prototype.trigger.call(this[BINDSTATEKEY], keyEvent, this, [
                current,
                previous
              ]);
            }
          }
          return current;
        };
      };
      /**
       * Add a single getter/setter to an object
       * @param  {object} in_obj   The object to add the getter and setter to
       * @param  {string} in_key   The attribute name
       * @param  {*} in_value The default attribute value
       * @return {object}          The original object, for chaining
       */
      _addGetterAndSetter = function (in_obj, in_key, in_value) {
        var _setter = _setterName(in_key);
        var _previousSetter = in_obj[_setter];
        var _getter = _getterName(in_key);
        var _is = _isName(in_key);
        var _previousGetter = in_obj[_getter];
        var _previousIs = in_obj[_is];
        if (_previousIs || _previousGetter || _previousSetter) {
          if (!_previousSetter) {
            console.error('Detected existing getter but no setter. This is an error.' + 'Piggyback on existing accessors disabled. Please implement propert setter on attribute', in_key);
            _previousGetter = undefined;
          }
          if (_previousGetter && _previousIs && _previousGetter !== _previousIs) {
            console.warn('Syntax ambiguity : ' + _getterName(in_key) + ' and ' + _isName(in_key) + ' exist and differ.' + ' This is semantically ambiguous.');
          }
          if (!_previousGetter && !_previousIs) {
            console.warn('Found previous setter with no getter for attribute.', in_key, '. The original setter will be shadowed.');
          }
        }
        if ((_previousIs || _previousGetter) && _previousSetter) {
          // Both previous setter and getter found. Only the setter is overriden,
          // but called during the process.
          console.log('Found previous getter and setter for attribute', in_key, '. The default value', in_value, 'will be ignored.');
          in_obj[_getter] = in_obj[_is] = _previousIs || _previousGetter;
          in_obj[_setter] = _createSetterWithOverride(in_key, in_obj[_getter], _previousSetter);
        } else {
          // No previous getter, though a setter may already exist. We have to shadow
          // the internal value and maintain a copy of it.
          // This is also the general case where no previous getter and setter exist.
          in_obj[_getter] = function () {
            _lazyInit.call(this);
            var store = this[STOREKEY];
            // Returns the default value if nothing has been set yet.
            if (!_.has(store, in_key)) {
              return in_value;
            }
            return store[in_key];
          };
          // If this is a boolean value, then let's add a is{AttributeName} getter
          if (_.isBoolean(in_value)) {
            in_obj[_is] = in_obj[_getter];
          }
          var inplaceSetter = function (value) {
            _lazyInit.call(this);
            if (_previousSetter) {
              _previousSetter.apply(this, arguments);
            }
            var store = this[STOREKEY];
            store[in_key] = value;
          };
          in_obj[_setter] = _createSetterWithOverride(in_key, in_obj[_getter], inplaceSetter);
        }
        return in_obj;
      };
      /**
       * Returns the event string for a given attribute name
       * @param  {string} in_key The attribute name
       * @return {string}     The event name
       * @private
       */
      _eventKey = function (in_key) {
        return in_key;
      };
      /**
       * Returns the change handle string for a given attribute name
       * @param  {string} in_key The attribute name
       * @return {string}     The change handler  name
       * @private
       */
      _changedKey = function (in_key) {
        return in_key + 'Changed';
      };
      /**
       * Returns the validate handler string for a given attribute name
       * @param  {string} in_key The attribute name
       * @return {string}     The validate handler name
       * @private
       */
      _validateKey = function (in_key) {
        return in_key + 'Validate';
      };
      /**
       * Returns the setter function name for a given attribute name
       * @param  {string} in_key The attribute name
       * @return {string}     The setter function name
       * @private
       */
      _setterName = function (in_key) {
        return 'set' + _camelize(in_key);
      };
      /**
       * Returns the getter function name for a given attribute name
       * @param  {string} in_key The attribute name
       * @return {string}     The getter function name
       * @private
       */
      _getterName = function (in_key) {
        return 'get' + _camelize(in_key);
      };
      /**
       * Returns the boolean getter function for a given attribute name
       * @param  {string} in_key The attribute name
       * @return {string}     The boolean getter function name
       * @private
       */
      _isName = function (in_key) {
        return 'is' + _camelize(in_key);
      };
      /**
       * Gets the value of an attribute
       * @param {string} in_name   The attribute name
       * @return {*} value  The value
       */
      _getAttribute = function (in_name) {
        var getter = this[_getterName(in_name)];
        var attributeValue;
        if (getter) {
          attributeValue = getter.call(this);
        } else {
          attributeValue = Binding.noValue;
        }
        return attributeValue;
      };
      /**
       * Sets the value of an attribute
       * @param {string} in_name   The attribute name
       * @param {*} in_value      The attribute value
       * @return {*} value  The value
       */
      _setAttribute = function (in_name, in_value) {
        console.assert(in_value !== Binding.noValue);
        var setter = this[_setterName(in_name)];
        if (setter) {
          setter.call(this, in_value);
        } else {
          console.warn('Trying to set value to non-attribute named', in_name);
        }
        return in_value;
      };
      /**
       * Retrieves a value from a given object, based on a path.
       * If path is a string, then the list of path components is extracted from it, and queried.
       * If path is an array, then it is assumed that it is a list of path components
       * @param  {string|Array.<Binding~PathComponent>} in_path   A path (either as a string or a set of components)
       * @return {*}           The attribute value, or Binding.noValue if no value can be returned
       */
      _get = function (in_path) {
        var pathItems = _.isArray(in_path) ? in_path.slice() : _getPathComponents(in_path);
        var objectInPath = this;
        var result = objectInPath;
        _.each(pathItems, function (in_pathItem) {
          if (objectInPath) {
            var value = in_pathItem.plugin.getter.call(result, in_pathItem.path);
            if (value === Binding.noValue) {
              result = value;
              objectInPath = undefined;
            } else {
              result = objectInPath = value;
            }
          } else {
            // The object is not found. The path cannot be followed till the end.
            result = Binding.noValue;
          }
        });
        return result;
      };
      /**
       * Sets the value of an attribute at the designated path
       * @param {string} in_path   The path (in the form a.b.c.d)
       * @param {*} in_value  The value to set
       * @return {object} The modified object.
       */
      _set = function (in_path, in_value) {
        var pathItems = _getPathComponents(in_path);
        var final = pathItems.pop();
        var parent = _get.call(this, pathItems);
        if (parent && parent !== Binding.noValue) {
          try {
            final.plugin.setter.call(parent, final.path, in_value);
          } catch (e) {
            console.warn('Could not set value to attribute at path ', final.path, ':', e.message);
          }
        }
        return this;
      };
      /**
       * Prepares the data bindings (assigns a guid).
       * @param {Array.<binding>} in_bindings The attributed object data bindings.
       * @this object
       * @private
       */
      _prepareBindings = function (in_bindings) {
        _.each(in_bindings, function (binding) {
          if (binding.guid === undefined) {
            binding.guid = generateGUID();
          }
        });
      };
      /**
       * Remove all the binding listeners
       * @param {Array.<binding>} [in_bindings] The bindings to clear.
       * @this object
       */
      _clearBindings = function (in_bindings) {
        var bindings = in_bindings || this[BINDSTATEKEY].bindings;
        _.each(bindings, function (binding) {
          _clearBinding.call(this, binding);
        }, this);
      };
      /**
       * Remove the listeners of a single binding
       * @param  {object} in_binding The binding definition for which listeners must be removed
       */
      _clearBinding = function (in_binding) {
        var bindInstance = this[BINDSTATEKEY][in_binding.guid];
        if (bindInstance) {
          var listeners = bindInstance.installedListeners;
          listeners.forEach(function (listener) {
            listener.pathComponent.plugin.unbind.call(listener.obj, listener.pathComponent.path, listener.fct, listener);
          });
          bindInstance.installedListeners = [];
        }
      };
      /**
       * Internal 'bind' _set , that sets the value of an attribute after a transform, and makes sure
       * that if the binding is twoWay, the set value  does not trigger a new binding
       * execution in the opposite direction.
       * @param  {object} in_object       Attributed object on which the binding is installed
       * @param  {Array.<Array.<Contexts>>}         in_pathContexts List of path strings to set the value from.
       * @param  {object} in_bindInstance The object carrying the binding state
       * @param  {object} in_binding      The binding specification
       * @param  {string} in_direction    'from'|'to' The binding execution direction
       * @param  {*} in_newValue        The value to set
       * @private
       */
      _setBoundValue = function (in_object, in_pathContexts, in_bindInstance, in_binding, in_direction, in_newValue) {
        if (in_bindInstance.active) {
          // The binding is being executed. Either there is a cycle, or we have a twoWay binding
          // TODO: Detect both cases and trigger a warning in the former case.
          if (in_binding.debug || Binding.debug) {
            console.log('Binding - skipping bidirectional reset for binding ', in_binding);
          }
          return;
        }
        in_bindInstance.active = true;
        if (in_binding.debug || Binding.debug) {
          console.log('Binding - set value', in_newValue, 'direction', in_direction, 'target', in_binding[in_direction]);
        }
        var values;
        var _directGet = function (contexts) {
          var lastContext = contexts[contexts.length - 1];
          return lastContext.validContext ? lastContext.pathComponent.plugin.getter.call(lastContext.obj, lastContext.pathComponent.path) : Binding.noValue;
        };
        if (in_pathContexts.length === 1) {
          // only one value, optimize by taking the provided value
          if (in_newValue === Binding.noValue) {
            // no value provided, get it from the source
            var v = _directGet(in_pathContexts[0]);
            if (v === Binding.noValue) {
              // Abort, there is no value to get
              in_bindInstance.active = false;
              return;
            }
            values = v;
          } else {
            values = in_newValue;
          }
        } else {
          // Multiple values (f.e. computed attributes)
          values = [];
          for (var i = 0; i < in_pathContexts.length; i++) {
            var component = in_pathContexts[i];
            var res = _directGet(component);
            if (res === Binding.noValue) {
              // Abort, there is no value to get
              in_bindInstance.active = false;
              return;
            }
            values.push(res);
          }
        }
        if (in_binding.transform) {
          values = in_binding.transform.call(in_object, values, in_direction);
        }
        _set.call(in_object, in_binding[in_direction], values);
        in_bindInstance.active = false;
      };
      /**
       * Resets the bindings for a given object. Can be called at setup or after a clearBindings call.
       * @param {Array.<object>} [in_bindingsList] The list of bindings to reset
       */
      _resetBindings = function (in_bindingsList) {
        _lazyInit.call(this);
        var that = this;
        // Clear existing bindings (if empty clears all)
        _clearBindings.call(this, in_bindingsList);
        // install new bindings
        var bindings = in_bindingsList || this[BINDSTATEKEY].bindings;
        _.each(bindings, function (binding) {
          var bindInstance = that[BINDSTATEKEY][binding.guid] = {
            installedListeners: [],
            active: false
          };
          var isTwoWay = !!binding.twoWay;
          console.assert(!isTwoWay || !_.isArray(binding.from), 'Cannot have two-way bindings with multiple from values');
          var toPathComponents = _getPathComponents(binding.to);
          // this is the context, pushed on the listeners stack
          var _resetBinding = function () {
            if (binding.debug || Binding.debug) {
              var pathComponent = this.pathComponent;
              console.log('Resetting bindings for', binding, pathComponent ? 'Because ' + pathComponent.path + ' changed' : '');
            }
            _clearBinding.call(that, binding);
            var contexts = [];
            // Listen for changes in the 'to' path
            var toClone = toPathComponents.slice();
            if (!isTwoWay) {
              // One way binding from -> to, therefore
              // the last item is not listened to.
              toClone.pop();
            }
            var debug = Binding.debug || binding.debug;
            var toContexts = _collectContexts(that, toClone, _resetBinding, debug);
            // 'from' contexts can be multiple
            var multipleFrom = _.isArray(binding.from);
            var fromExpressions = multipleFrom ? binding.from : [binding.from];
            // Collect context for each path expression in the 'from' array.
            var fromContextPaths = fromExpressions.map(function (from) {
              return _collectContexts(that, _getPathComponents(from), _resetBinding, debug);
            });
            contexts = _.flatten(fromContextPaths).concat(toContexts);
            // Listen for changes in the 'from' and 'to' paths
            _.forEach(contexts, function (context) {
              if (context.validContext) {
                context.bindInstance = bindInstance;
                context.pathComponent.plugin.bind.call(context.obj, context.pathComponent.path, context.fct, context);
                bindInstance.installedListeners.push(context);
              }
            });
            // Create an assignation function, that is assigns a value to the 'to' target.
            // This function is called when the leaf value changes in the 'from' source.
            var assignationFct = _.partial(_setBoundValue, that, fromContextPaths, bindInstance, binding, 'to');
            _.each(fromContextPaths, function (fromContexts) {
              if (fromContexts.length) {
                var fromContext = fromContexts[fromContexts.length - 1];
                if (fromContext.validContext) {
                  // The last item in the 'from' path is processed differently: Its value is copied
                  // to the 'to' side of the bind
                  fromContext.action = assignationFct;
                }
              }
            });
            if (isTwoWay) {
              // In case of two way bindings, the last context in the 'to' path is also
              // listened to, and when a change occurs, its value is copied in the 'from' side
              // of the binding.
              var toContext = toContexts[toContexts.length - 1];
              if (toContext.validContext) {
                // The last item in the 'from' path is processed differently: Its value is copied
                // to the 'from' side of the bind
                toContext.action = _.partial(_setBoundValue, that, [toContexts], bindInstance, binding, 'from');
              }
            }
            // initial bind execution
            assignationFct.call(that, Binding.noValue);
          };
          _resetBinding();
        });
      };
      /**
       * Collects contexts to listen along a path as far as possible
       * @param  {object} in_object       The object on which listening context must be collected
       * @param  {Array.<Binding~PathComponent>} in_pathComponents The path to follow
       * @param  {function} in_callback   The callback to add to the context
       * @param  {boolean}  in_debug      If true, thens traces are displayed while the contexts are collected
       * @return {Array.<Binding~BindContext>}  A list of contexts
       */
      _collectContexts = function (in_object, in_pathComponents, in_callback, in_debug) {
        var objectInPath = in_object;
        var contexts = [];
        _.forEach(in_pathComponents, function (pathComponent) {
          if (!objectInPath) {
            if (in_debug) {
              console.log('Attribute in path not found :', pathComponent, 'path was', pathComponent.path);
            }
            contexts.push({ validContext: false });
            return;  // Nothing to bind to
          }
          console.assert(objectInPath);
          if (in_debug) {
            console.log('Bindings to attribute :', pathComponent, 'in path', pathComponent.path);
          }
          var context = {
            validContext: true,
            fct: function () {
              context.action.apply(this, arguments);
            },
            obj: objectInPath,
            pathComponent: pathComponent,
            action: undefined
          };
          context.action = _.bind(in_callback, context);
          contexts.push(context);
          var nextObjectGetter = pathComponent.plugin.getter;
          objectInPath = nextObjectGetter ? nextObjectGetter.call(objectInPath, pathComponent.path) : undefined;
        });
        return contexts;
      };
      _addAttributeListener = function () {
        _lazyInit.call(this);
        EventEmitter.prototype.addListener.apply(this[BINDSTATEKEY], arguments);
      };
      _removeAttributeListener = function () {
        _lazyInit.call(this);
        EventEmitter.prototype.removeListener.apply(this[BINDSTATEKEY], arguments);
      };
      /**
       * Utility function, used to camelize the attribute name
       * @param  {string} in_string String to camelize
       * @return {string}        Camelized stirng
       * @private
       */
      _camelize = function (in_string) {
        console.assert(in_string !== undefined, 'Invalid string');
        if (in_string.length) {
          return in_string[0].toUpperCase() + in_string.slice(1);
        }
        return '';
      };
      // Must be defined here to make sure that all the related functions are initialized.
      _attribute_plugin = {
        name: 'attributes',
        getter: _getAttribute,
        setter: _setAttribute,
        bind: _addAttributeListener,
        unbind: _removeAttributeListener,
        separator: ''
      };
      // MRized
      exportModule('LYNX.Utils.DataBinding', Binding);
    }());
    (function () {
      'use strict';
      /**
       * Create a new DeferredPromise.
       * @constructor
       */
      var DeferredPromise = function () {
        var lResolve, lReject;
        var p = new Promise(function (resolve, reject) {
          lResolve = resolve;
          lReject = reject;
        });
        p._deferredPromiseResolve = lResolve;
        p._deferredPromiseReject = lReject;
        p.getCb = _getCb;
        p.resolve = _resolve;
        p.reject = _reject;
        return p;
      };
      DeferredPromise.prototype = Object.create(Promise.prototype);
      DeferredPromise.prototype.constructor = Promise;
      /**
       * Fetches a node style callback that fulfills the promise when called.
       * @return {Function} A node style callback that fulfills the promise when called.
       */
      var _getCb = function () {
        var that = this;
        return function (error, result) {
          if (error) {
            return that.reject(error);
          }
          return that.resolve(result);
        };
      };
      /**
       * Resolves the promise.
       * @param {*} in_result The promise result.
       */
      var _resolve = function (in_result) {
        this._deferredPromiseResolve(in_result);
      };
      /**
       * Rejects the promise.
       * @param {*} in_error The error.
       */
      var _reject = function (in_error) {
        this._deferredPromiseReject(in_error);
      };
      exportModule('LYNX.Utils.DeferredPromise', DeferredPromise);
    }());
    (function () {
      var guidToUint32x4 = include('LYNX.Utils.guidToUint32x4');
      var uint32x4ToGUID = include('LYNX.Utils.uint32x4ToGUID');
      /**
       * Helper class to store the array buffers needed for the hash computation.
       *
       * TODO: should we cache these classes to avoid unnecessary reallocation
       *       of these buffers. Is it worth the effort?
       * @constructor
       */
      var HashBuffer = function () {
        // This is the internal array buffer we are going to use for conversions
        this.hashBuffer = new ArrayBuffer(32);
        this.dataView = new DataView(this.hashBuffer);
        this.Uint8HashArray = new Uint8Array(this.hashBuffer, 0, 32);
        this.Uint32HashArray = new Uint32Array(this.hashBuffer, 0, 8);
        this.pos = 0;
      };
      /**
       * Shifts the buffers forward by 16 bytes
       */
      HashBuffer.prototype.shiftBuffers = function () {
        this.Uint32HashArray[0] = this.Uint32HashArray[4];
        this.Uint32HashArray[1] = this.Uint32HashArray[5];
        this.Uint32HashArray[2] = this.Uint32HashArray[6];
        this.Uint32HashArray[3] = this.Uint32HashArray[7];
        this.Uint32HashArray[4] = 0;
        this.Uint32HashArray[5] = 0;
        this.Uint32HashArray[6] = 0;
        this.Uint32HashArray[7] = 0;
        this.pos -= 16;
      };
      // If available, we use the browser imul function, otherwise we emulate it
      // Emulation code is from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul
      // License: public domain, according to https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses:
      //   Code samples added on or after August 20, 2010 are in the public domain.
      //   No licensing notice is necessary, but if you need one, you can use:
      //   "Any copyright is dedicated to the Public Domain.
      //    http://creativecommons.org/publicdomain/zero/1.0/".
      var imul = Math.imul || function (a, b) {
        var ah = a >>> 16 & 65535;
        var al = a & 65535;
        var bh = b >>> 16 & 65535;
        var bl = b & 65535;
        // the shift by 0 fixes the sign on the high part
        // the final |0 converts the unsigned value into a signed value
        return al * bl + (ah * bl + al * bh << 16 >>> 0) | 0;
      };
      // Helper functions for the hash computation
      /**
       * ROT function required for the hash computation
       *
       * @param {number} x - 32 bit integer
       * @param {number} r - amount to shift
       * @return {number} Rotated 32-bit integer.
       */
      var rotl32 = function (x, r) {
        return (x << r >>> 0 | x >>> 32 - r) >>> 0;
      };
      /**
       * Mixing function for the finalization
       * @param {number} h - 32 bit integer
       * @return {number} The scrambled 32-bit integer.
       */
      var fmix32 = function (h) {
        h = (h ^ h >>> 16) >>> 0;
        h = imul(h, 2246822507) >>> 0;
        h = (h ^ h >>> 13) >>> 0;
        h = imul(h, 3266489909) >>> 0;
        h = (h ^ h >>> 16) >>> 0;
        return h;
      };
      // Constants
      var c1 = 597399067;
      var c2 = 2869860233;
      var c3 = 951274213;
      var c4 = 2716044179;
      /**
       * Helper class which computes a Murmur3 hash for binary data. The user
       * can successively push data in different binary types into the hash
       * computation function and will then get a hash computed over these fields.
       *
       * NOTE: By default JavaScript uses big-endian byte order. So the hashes
       *       generated by this class might correspond to those generated natively
       *       on a little-endian architecture.
       *
       * @param {number} in_seed - Seed value used for the hash computation
       * @constructor
       * @alias LYNX.Utils.HashCalculator
       */
      var HashCalculator = function (in_seed) {
        // Initialize the hash computation with the seed value
        this._h1 = in_seed;
        this._h2 = in_seed;
        this._h3 = in_seed;
        this._h4 = in_seed;
        this.length = 0;
        this.finalized = false;
        this.hashBuffer = new HashBuffer();
      };
      /**
       * Pushes a single unsigned integer with 8 bits into the hash computation
       *
       * @param {number} in_value - The value to push
       */
      HashCalculator.prototype.pushUint8 = function (in_value) {
        this.hashBuffer.dataView.setUint8(this.hashBuffer.pos, in_value);
        this.hashBuffer.pos++;
        if (this.hashBuffer.pos >= 16) {
          this._computeHashFor16ByteBlock();
        }
      };
      /**
       * Pushes a single unsigned integer with 16 bits into the hash computation
       *
       * @param {number} in_value - The value to push
       */
      HashCalculator.prototype.pushUint16 = function (in_value) {
        this.hashBuffer.dataView.setUint16(this.hashBuffer.pos, in_value);
        this.hashBuffer.pos += 2;
        if (this.hashBuffer.pos >= 16) {
          this._computeHashFor16ByteBlock();
        }
      };
      /**
       * Pushes a single unsigned integer with 32 bits into the hash computation
       *
       * @param {number} in_value - The value to push
       */
      HashCalculator.prototype.pushUint32 = function (in_value) {
        this.hashBuffer.dataView.setUint32(this.hashBuffer.pos, in_value);
        this.hashBuffer.pos += 4;
        if (this.hashBuffer.pos >= 16) {
          this._computeHashFor16ByteBlock();
        }
      };
      /**
       * Pushes a single signed integer with 8 bits into the hash computation
       *
       * @param {number} in_value - The value to push
       */
      HashCalculator.prototype.pushInt8 = function (in_value) {
        this.hashBuffer.dataView.setInt8(this.hashBuffer.pos, in_value);
        this.hashBuffer.pos++;
        if (this.hashBuffer.pos >= 16) {
          this._computeHashFor16ByteBlock();
        }
      };
      /**
       * Pushes a single signed integer with 16 bits into the hash computation
       *
       * @param {number} in_value - The value to push
       */
      HashCalculator.prototype.pushInt16 = function (in_value) {
        this.hashBuffer.dataView.setInt16(this.hashBuffer.pos, in_value);
        this.hashBuffer.pos += 2;
        if (this.hashBuffer.pos >= 16) {
          this._computeHashFor16ByteBlock();
        }
      };
      /**
       * Pushes a single signed integer with 32 bits into the hash computation
       *
       * @param {number} in_value - The value to push
       */
      HashCalculator.prototype.pushInt32 = function (in_value) {
        this.hashBuffer.dataView.setInt32(this.hashBuffer.pos, in_value);
        this.hashBuffer.pos += 4;
        if (this.hashBuffer.pos >= 16) {
          this._computeHashFor16ByteBlock();
        }
      };
      /**
       * Pushes a single float with 32 bits into the hash computation
       *
       * @param {number} in_value - The value to push
       */
      HashCalculator.prototype.pushFloat32 = function (in_value) {
        this.hashBuffer.dataView.setFloat32(this.hashBuffer.pos, in_value);
        this.hashBuffer.pos += 4;
        if (this.hashBuffer.pos >= 16) {
          this._computeHashFor16ByteBlock();
        }
      };
      /**
       * Pushes a single float with 64 bits into the hash computation
       *
       * @param {number} in_value - The value to push
       */
      HashCalculator.prototype.pushFloat64 = function (in_value) {
        this.hashBuffer.dataView.setFloat64(this.hashBuffer.pos, in_value);
        this.hashBuffer.pos += 8;
        if (this.hashBuffer.pos >= 16) {
          this._computeHashFor16ByteBlock();
        }
      };
      /**
       * Pushes a GUID into the hash computation
       *
       * @param {string} in_guid - The value to push
       */
      HashCalculator.prototype.pushGUID = function (in_guid) {
        // Parse the GUID
        var array = guidToUint32x4(in_guid);
        // Add it as a uint32 values
        this.pushArray(array, HashCalculator.Type.Uint32);
      };
      /**
       * Pushes a string into the hash computation
       *
       * @param {string} in_string - The value to push
       */
      HashCalculator.prototype.pushString = function (in_string) {
        // Push each character individually. We have to use uin16 here, as
        // JavaScript strings are UTF16
        for (var i = 0; i < in_string.length; i++) {
          this.pushUint16(in_string.charCodeAt(i));
        }
      };
      /**
       * The type to use when pushing values
       * @enum
       */
      HashCalculator.Type = {};
      /** Signed integer with 8 bits */
      HashCalculator.Type.Int8 = 0;
      /** Signed integer with 16 bits */
      HashCalculator.Type.Int16 = 1;
      /** Signed integer with 32 bits */
      HashCalculator.Type.Int32 = 2;
      /** Unsigned integer with 8 bits */
      HashCalculator.Type.Uint8 = 3;
      /** Unsigned integer with 16 bits */
      HashCalculator.Type.Uint16 = 4;
      /** Unsigned integer with 32 bits */
      HashCalculator.Type.Uint32 = 5;
      /** Floating point value with 32 bits */
      HashCalculator.Type.Float32 = 6;
      /** Floating point value with 64 bits */
      HashCalculator.Type.Float64 = 7;
      var _pushFunctions = [
        HashCalculator.prototype.pushInt8,
        HashCalculator.prototype.pushInt16,
        HashCalculator.prototype.pushInt32,
        HashCalculator.prototype.pushUint8,
        HashCalculator.prototype.pushUint16,
        HashCalculator.prototype.pushUint32,
        HashCalculator.prototype.pushFloat32,
        HashCalculator.prototype.pushFloat64
      ];
      /**
       * Pushes an array into the hash computation
       *
       * @param {number| TypedArray}             array  - The array to include in the hash computation
       * @param {HashCalculator.Type} type - The type to use to compute the hash
       * @param {number}                       [count] - The number of entries to include in the computation.
       *                                                 If not supplied the whole array will be used
       */
      HashCalculator.prototype.pushArray = function (array, type, count) {
        var pushFun = _pushFunctions[type];
        count = count !== undefined ? count : array.length;
        for (var i = 0; i < count; i++) {
          pushFun.call(this, array[i]);
        }
      };
      /**
       * @return {string} the computed Hash
       * Note: after calling this function you no longer may push more data!
       */
      HashCalculator.prototype.getHash = function () {
        if (!this.finalized) {
          this._finalizeHash();
        }
        // Convert the hash to a string
        return uint32x4ToGUID([
          this._h1,
          this._h2,
          this._h3,
          this._h4
        ]);
      };
      /**
       * Computes the murmur3 hash for a block of 16 bytes
       * @private
       */
      HashCalculator.prototype._computeHashFor16ByteBlock = function () {
        var k1 = this.hashBuffer.Uint32HashArray[0];
        var k2 = this.hashBuffer.Uint32HashArray[1];
        var k3 = this.hashBuffer.Uint32HashArray[2];
        var k4 = this.hashBuffer.Uint32HashArray[3];
        this.hashBuffer.shiftBuffers();
        k1 = imul(k1, c1) >>> 0;
        k1 = rotl32(k1, 15);
        k1 = imul(k1, c2) >>> 0;
        this._h1 = (this._h1 ^ k1) >>> 0;
        this._h1 = rotl32(this._h1, 19);
        this._h1 = this._h1 + this._h2 >>> 0;
        this._h1 = (imul(this._h1, 5) >>> 0) + 1444728091 >>> 0;
        k2 = imul(k2, c2) >>> 0;
        k2 = rotl32(k2, 16);
        k2 = imul(k2, c3) >>> 0;
        this._h2 = (this._h2 ^ k2) >>> 0;
        this._h2 = rotl32(this._h2, 17);
        this._h2 = this._h2 + this._h3 >>> 0;
        this._h2 = (imul(this._h2, 5) >>> 0) + 197830471 >>> 0;
        k3 = imul(k3, c3) >>> 0;
        k3 = rotl32(k3, 17);
        k3 = imul(k3, c4) >>> 0;
        this._h3 = (this._h3 ^ k3) >>> 0;
        this._h3 = rotl32(this._h3, 15);
        this._h3 = this._h3 + this._h4 >>> 0;
        this._h3 = (imul(this._h3, 5) >>> 0) + 2530024501 >>> 0;
        k4 = imul(k4, c4) >>> 0;
        k4 = rotl32(k4, 18);
        k4 = imul(k4, c1) >>> 0;
        this._h4 = (this._h4 ^ k4) >>> 0;
        this._h4 = rotl32(this._h4, 13);
        this._h4 = this._h4 + this._h1 >>> 0;
        this._h4 = (imul(this._h4, 5) >>> 0) + 850148119 >>> 0;
        this.length += 16;
      };
      /**
       * Finalizes the hash computation
       * @private
       */
      HashCalculator.prototype._finalizeHash = function () {
        var k1 = 0;
        var k2 = 0;
        var k3 = 0;
        var k4 = 0;
        // Compute the hash for the remaining bytes in the buffer
        var buffer = this.hashBuffer.Uint8HashArray;
        /* eslint-disable no-fallthrough */
        // Allows cases to fallthrough without complaints.
        switch (this.hashBuffer.pos) {
        case 15:
          k4 = (k4 ^ buffer[14] << 16) >>> 0;
        case 14:
          k4 = (k4 ^ buffer[13] << 8) >>> 0;
        case 13:
          k4 = (k4 ^ buffer[12] << 0) >>> 0;
          k4 = imul(k4, c4) >>> 0;
          k4 = rotl32(k4, 18);
          k4 = imul(k4, c1) >>> 0;
          this._h4 = (this._h4 ^ k4) >>> 0;
        case 12:
          k3 = (k3 ^ buffer[11] << 24) >>> 0;
        case 11:
          k3 = (k3 ^ buffer[10] << 16) >>> 0;
        case 10:
          k3 = (k3 ^ buffer[9] << 8) >>> 0;
        case 9:
          k3 = (k3 ^ buffer[8] << 0) >>> 0;
          k3 = imul(k3, c3) >>> 0;
          k3 = rotl32(k3, 17);
          k3 = imul(k3, c4) >>> 0;
          this._h3 = (this._h3 ^ k3) >>> 0;
        case 8:
          k2 = (k2 ^ buffer[7] << 24) >>> 0;
        case 7:
          k2 = (k2 ^ buffer[6] << 16) >>> 0;
        case 6:
          k2 = (k2 ^ buffer[5] << 8) >>> 0;
        case 5:
          k2 = (k2 ^ buffer[4] << 0) >>> 0;
          k2 = imul(k2, c2) >>> 0;
          k2 = rotl32(k2, 16);
          k2 = imul(k2, c3) >>> 0;
          this._h2 = (this._h2 ^ k2) >>> 0;
        case 4:
          k1 = (k1 ^ buffer[3] << 24) >>> 0;
        case 3:
          k1 = (k1 ^ buffer[2] << 16) >>> 0;
        case 2:
          k1 = (k1 ^ buffer[1] << 8) >>> 0;
        case 1:
          k1 = (k1 ^ buffer[0] << 0) >>> 0;
          k1 = imul(k1, c1) >>> 0;
          k1 = rotl32(k1, 15);
          k1 = imul(k1, c2) >>> 0;
          this._h1 = (this._h1 ^ k1) >>> 0;
          break;
        default:
          throw new Error('_finalizeHash: We should never get into the default case.');
        }
        /* eslint-enable no-fallthrough */
        // Perform the finalization
        var len = this.length + this.hashBuffer.pos;
        this._h1 = (this._h1 ^ len) >>> 0;
        this._h2 = (this._h2 ^ len) >>> 0;
        this._h3 = (this._h3 ^ len) >>> 0;
        this._h4 = (this._h4 ^ len) >>> 0;
        this._h1 = this._h1 + this._h2 >>> 0;
        this._h1 = this._h1 + this._h3 >>> 0;
        this._h1 = this._h1 + this._h4 >>> 0;
        this._h2 = this._h2 + this._h1 >>> 0;
        this._h3 = this._h3 + this._h1 >>> 0;
        this._h4 = this._h4 + this._h1 >>> 0;
        this._h1 = fmix32(this._h1);
        this._h2 = fmix32(this._h2);
        this._h3 = fmix32(this._h3);
        this._h4 = fmix32(this._h4);
        this._h1 = this._h1 + this._h2 >>> 0;
        this._h1 = this._h1 + this._h3 >>> 0;
        this._h1 = this._h1 + this._h4 >>> 0;
        this._h2 = this._h2 + this._h1 >>> 0;
        this._h3 = this._h3 + this._h1 >>> 0;
        this._h4 = this._h4 + this._h1 >>> 0;
        this.finalized = true;
      };
      /**
       * Computes a hash for a 32 bit unsigned int value (using the FNV-1a algorithm
       * see http://www.isthe.com/chongo/tech/comp/fnv/)
       *
       * @param {number} in_value - 32 bit unsigned integer for which the hash is computed
       * @param {number} in_seed - result from a previous hash to chain the hash computation
       * @return {number} the computed hash key
       */
      var UINT_32HASH_PRIME = 16777619;
      var UINT_32HASH_OFFSET = 2166136261;
      var fNV1aHashUint32 = function (in_value, in_seed) {
        var result = UINT_32HASH_OFFSET;
        if (in_seed) {
          result = (result ^ in_seed) * UINT_32HASH_PRIME >>> 0;
        }
        result = ((result ^ in_value & 255) >>> 0) * UINT_32HASH_PRIME >>> 0;
        result = ((result ^ in_value >> 8 & 255) >>> 0) * UINT_32HASH_PRIME >>> 0;
        result = ((result ^ in_value >> 16 & 255) >>> 0) * UINT_32HASH_PRIME >>> 0;
        result = ((result ^ in_value >> 24 & 255) >>> 0) * UINT_32HASH_PRIME >>> 0;
        return result;
      };
      /**
       * Compute a new 32-bit hash from a previous 32-bit hash and a new 32-bit "delta hash".
       * This function can be also be used for (slow) high quality hash combine of 32-bit numbers.
       *
       * @param {number} in_newDeltaHash - the new hasH
       * @param {number} in_previousHash - the previous hash
       * @return {number} the computed hash key
       * @alias LYNX.Utils.incrementalHashUint32
       */
      var incrementalHashUint32 = function (in_newDeltaHash, in_previousHash) {
        return fNV1aHashUint32(in_newDeltaHash, in_previousHash);
      };
      /**
       * Compute a new hash array from a previous hash and a new "delta hash".
       * All hashes are assumed to be 4 x 32-bit uint arrays.
       * This function can be also be used for (slow) high quality hash combines.
       *
       * @param {Uint32Array | Array.<number>}  in_newDeltaHash - the new hash array
       * @param {Uint32Array | Array.<number>}  in_previousHash - the previous hash array
       * @param {Uint32Array | Array.<number>} io_result - the computed hash key is returned
       *                                                   through this parameter if specified.
       * @return {Uint32Array | Array.<number>}  the computed hash key
       * @alias LYNX.Utils.incrementalHash4xUint32
       */
      var incrementalHash4xUint32 = function (in_newDeltaHash, in_previousHash, io_result) {
        var accumulated = io_result;
        if (accumulated === undefined) {
          accumulated = new Uint32Array(4);
        }
        accumulated[0] = fNV1aHashUint32(in_newDeltaHash[0], in_previousHash[0]);
        accumulated[0] = fNV1aHashUint32(in_newDeltaHash[1], accumulated[0]);
        accumulated[0] = fNV1aHashUint32(in_newDeltaHash[2], accumulated[0]);
        accumulated[0] = fNV1aHashUint32(in_newDeltaHash[3], accumulated[0]);
        accumulated[1] = fNV1aHashUint32(in_newDeltaHash[0], in_previousHash[1]);
        accumulated[1] = fNV1aHashUint32(in_newDeltaHash[1], accumulated[1]);
        accumulated[1] = fNV1aHashUint32(in_newDeltaHash[2], accumulated[1]);
        accumulated[1] = fNV1aHashUint32(in_newDeltaHash[3], accumulated[1]);
        accumulated[2] = fNV1aHashUint32(in_newDeltaHash[0], in_previousHash[2]);
        accumulated[2] = fNV1aHashUint32(in_newDeltaHash[1], accumulated[2]);
        accumulated[2] = fNV1aHashUint32(in_newDeltaHash[2], accumulated[2]);
        accumulated[2] = fNV1aHashUint32(in_newDeltaHash[3], accumulated[2]);
        accumulated[3] = fNV1aHashUint32(in_newDeltaHash[0], in_previousHash[3]);
        accumulated[3] = fNV1aHashUint32(in_newDeltaHash[1], accumulated[3]);
        accumulated[3] = fNV1aHashUint32(in_newDeltaHash[2], accumulated[3]);
        accumulated[3] = fNV1aHashUint32(in_newDeltaHash[3], accumulated[3]);
        return accumulated;
      };
      /**
       * Performs a hash combination operation on the two supplied Uint32 arrays of length 4 (using
       * a variant of the algorithm from boost::hash_combine
       *
       * @param {Uint32Array | Array.<number>} in_array1 - first hash array
       * @param {Uint32Array | Array.<number>} in_array2 - second hash array
       * @param {Uint32Array | Array.<number>} [io_result] optional array to write to
       *
       * @return {Uint32Array | Array.<number>}
       * @alias LYNX.Utils.hashCombine4xUint32
       */
      var hashCombine4xUint32 = function (in_array1, in_array2, io_result) {
        var accumulated = io_result;
        if (accumulated === undefined) {
          accumulated = new Uint32Array(in_array2);
        } else {
          accumulated[0] = in_array2[0];
          accumulated[1] = in_array2[1];
          accumulated[2] = in_array2[2];
          accumulated[3] = in_array2[3];
        }
        accumulated[0] += 2654435769;
        accumulated[1] += 104395303;
        accumulated[2] += 452930477;
        accumulated[3] += 982451653;
        accumulated[0] += in_array1[3] << 6;
        accumulated[1] += in_array1[0] << 6;
        accumulated[2] += in_array1[1] << 6;
        accumulated[3] += in_array1[2] << 6;
        accumulated[0] += in_array1[2] >> 2;
        accumulated[1] += in_array1[3] >> 2;
        accumulated[2] += in_array1[0] >> 2;
        accumulated[3] += in_array1[1] >> 2;
        accumulated[0] = (accumulated[0] ^ in_array1[1]) * UINT_32HASH_PRIME >>> 0;
        accumulated[1] = (accumulated[1] ^ in_array1[2]) * UINT_32HASH_PRIME >>> 0;
        accumulated[2] = (accumulated[2] ^ in_array1[3]) * UINT_32HASH_PRIME >>> 0;
        accumulated[3] = (accumulated[3] ^ in_array1[0]) * UINT_32HASH_PRIME >>> 0;
        return accumulated;
      };
      exportModule('LYNX.Utils.incrementalHashUint32', incrementalHashUint32);
      exportModule('LYNX.Utils.incrementalHash4xUint32', incrementalHash4xUint32);
      exportModule('LYNX.Utils.hashCombine4xUint32', hashCombine4xUint32);
      exportModule('LYNX.Utils.HashCalculator', HashCalculator);
    }());
    (function () {
      var hashCombine4xUint32 = include('LYNX.Utils.hashCombine4xUint32');
      var guidToUint32x4 = include('LYNX.Utils.guidToUint32x4');
      /**
       * Random number generator that creates a deterministic sequence of random numbers based on an initial seed GUID
       *
       * Warning: This is a very straight forward implementation based on the hashCombine4xUint32 function. It probably
       *          doesn't produce very high quality random numbers (do not use this for cryptography!) and it is not very
       *          efficient.
       *
       * @param {string} in_initialGUID - The initial seed GUID which is used to initialize the random number generator
       */
      var DeterministicRandomGenerator = function (in_initialGUID) {
        // Initialize the internal state from the given initial guid
        this._guid1 = guidToUint32x4(in_initialGUID);
        this._guid2 = guidToUint32x4(in_initialGUID);
        this._result = new Uint32Array(4);
      };
      /**
       * Creates a floating point random number
       *
       * @param {number=} [in_max=1.0] If supplied the returned number will be 0 <= number < in_max. If none is given
       *                               in_max = 1 is assumed
       * @return {number} The random number
       */
      DeterministicRandomGenerator.prototype.random = function (in_max) {
        var randomInteger = this.irandom();
        if (in_max === undefined) {
          return randomInteger / 4294967296;
        } else {
          return randomInteger / 4294967296 * in_max;
        }
      };
      /**
       * Creates an integer point random number
       *
       * @param {number=} [in_max=4294967296] If supplied the returned number will be 0 <= number < in_max. If none is given
       *                                      in_max = 14294967296 (2^32) is assumed
       * @return {number} The random number
       */
      DeterministicRandomGenerator.prototype.irandom = function (in_max) {
        // Create a new hash
        hashCombine4xUint32(this._guid1, this._guid2, this._result);
        // Permute the hashes
        for (var i = 0; i < 4; i++) {
          this._guid1[i] = this._guid2[i];
          this._guid2[i] = this._result[i];
        }
        if (in_max === undefined) {
          return this._guid1[0];
        } else {
          return this._guid1[0] % in_max;
        }
      };
      exportModule('LYNX.Utils.DeterministicRandomGenerator', DeterministicRandomGenerator);
    }());
    (function () {
      /**
       * Extends the Error.prototype with a toJSON function that serializes all the error properties.
       */
      var extendErrorToJSON = function () {
        if (!('toJSON' in Error.prototype)) {
          Object.defineProperty(Error.prototype, 'toJSON', {
            value: function () {
              var alt = {};
              Object.getOwnPropertyNames(this).forEach(function (key) {
                alt[key] = this[key];
              }, this);
              return alt;
            },
            configurable: true,
            writable: true
          });
        }
      };
      exportModule('LYNX.Utils.extendErrorToJSON', extendErrorToJSON);
    }());
    (function () {
      var async = include('LYNX.Utils.ThirdParty.async');
      var request = include('LYNX.Utils.ThirdParty.request');
      /**
       * Requests multiple files via HTTP
       *
       * @param {String}        in_method     - The HTTP method to use (e.g. GET)
       * @param {Array<String>} in_urlList    - The list of URLs to fetch
       * @param {Number}        in_flightSize - The number of maximal number of simultaneous HTTP requests
       * @param {function( (Error|null) , Object<String, {status:Number, response:String}>, Array)} in_callback -
       *     Callback that is invoked when the operation has been completed. The second parameter is an object that maps
       *     the URLs to the responses and status codes. If an error occurred, the list of failed URLs is passed as third
       *     parameter
       */
      var httpRequestMany = function (in_method, in_urlList, in_flightSize, in_callback) {
        var results = {};
        var failedRequests = [];
        async.eachLimit(in_urlList, in_flightSize, function (in_url, in_internalCallback) {
          request({
            method: in_method,
            url: in_url
          }, function (in_error, in_response, in_result) {
            if (in_error) {
              failedRequests.push(in_url);
            }
            results[in_url] = {
              response: in_result,
              status: in_response.status
            };
            in_internalCallback();
          });
        }, function (in_err) {
          if (failedRequests.length > 0) {
            in_callback(new Error('Some requests failed'), results, failedRequests);
          } else {
            in_callback(null, results);
          }
        });
      };
      exportModule('LYNX.Utils.Http.httpRequestMany', httpRequestMany);
    }());
    (function () {
      'use strict';
      var async = include('LYNX.Utils.ThirdParty.async');
      var DeferredPromise = include('LYNX.Utils.DeferredPromise');
      var PromiseUtils = {};
      /**
       * Calls a node style callback upon promise fulfillment or rejection.
       * @param {Promise} in_promise A promise.
       * @param {Function} in_cb A node style callback.
       * @return {Promise} A promise that invokes in_cb upon being fulfilled or rejected.
       */
      PromiseUtils.chainCallback = function (in_promise, in_cb) {
        return in_promise.then(in_cb.bind(null, null)).catch(in_cb);
      };
      /**
       * A convenience function that returns a promise that performs an asynchronous loop before being
       * fulfilled.
       * @param {Function} in_testFn A function that is called before each execution of in_iterateeFn.
       *   It must return 'true' for in_iterateeFn to be called. in_iterateeFn stops being called (and
       *   the returned promise is fulfilled) when in_testFn returns 'false'.
       * @param {Function} in_iterateeFn A function to invoke each time in_testFn returns 'true'. It
       *   takes a node style callback as a parameter that must be invoked by in_iterateeFn when it has
       *   finished processing.
       * @return {Promise} A promise that is fulfilled with the value passed to the callback argument of
       *   in_iterateeFn, after in_testFn returns false. Ex.: If in_iterateeFn is this function:
       *   var fn = function(cb) {
       *     // Do some async processing, and then:
       *     cb(null, 'foo');
       *   }
       *   Then the promise is fulfilled with 'foo'.
       *   Similarly, if the callback is invoked with an error, the promise is rejected with the
       *   supplied error:
       *   var fn = function(cb) {
       *     // Do some async processing, and then fail:
       *     cb(new Error('Failed!'));
       *   }
       */
      PromiseUtils.while = function (in_testFn, in_iterateeFn) {
        var deferredPromise = new DeferredPromise();
        async.whilst(in_testFn, in_iterateeFn, deferredPromise.getCb());
        return deferredPromise;
      };
      exportModule('LYNX.Utils.PromiseUtils', PromiseUtils);
    }());
    (function () {
      var _ = include('underscore');
      /**
       * Make a shallow copy of an array.
       *
       * @param {Array} in_array : array to copy.
       *
       * @return {Array} the copied array.
       */
      function arrayShallowCopy(in_array) {
        // We use a loop rather than slice because this is apparently faster
        // http://jsperf.com/slice-vs-copy
        var length = in_array.length;
        var ret = new Array(length);
        for (var i = 0; i < length; ++i) {
          ret[i] = in_array[i];
        }
        return ret;
      }
      /**
       * Make a shallow copy of an object. Object is assumed to be a plain
       * old data; prototype will not be copied.
       *
       * @param {object} in_object : object to copy.
       *
       * @return {object} the copied object.
       */
      function objectShallowCopy(in_object) {
        var ret = {};
        var keys = _.keys(in_object);
        var l = keys.length;
        for (var i = 0; i < l; i++) {
          var key = keys[i];
          ret[key] = in_object[key];
        }
        return ret;
      }
      /**
       * Perform a shallow copy on the incoming data, whether it is an array, object or simple type.
       *
       * @param {object} in_data - data to copy
       * @return {object} the copied data.
       * @alias LYNX.Utils.shallowCopy
       */
      function shallowCopy(in_data) {
        if (_.isArray(in_data)) {
          return arrayShallowCopy(in_data);
        } else if (_.isDate(in_data)) {
          return new Date(in_data.getTime());
        } else if (_.isRegExp(in_data)) {
          return new RegExp(in_data.source, in_data.toString().replace(/.*\//, ''));
        } else if (_.isFunction(in_data)) {
          return in_data;
        } else if (_.isObject(in_data)) {
          return objectShallowCopy(in_data);
        }
        return in_data;
      }
      exportModule('LYNX.Utils.shallowCopy', shallowCopy);
    }());
    (function () {
      var _ = include('underscore');
      var querystring = include('LYNX.Utils.ThirdParty.querystring');
      var UrlUtils = {};
      /**
       * Method used to parse the query parameters and return
       * them in a hashmap.
       * @param {String} in_queryString - queryString to parse
       * @return {object} Querystring parameters as key-value pairs
       */
      UrlUtils.deserializeQueryString = function (in_queryString) {
        var URLParts = in_queryString.split('?');
        var queryString;
        // Passed something like 'example.com?foo=bar' OR '?foo=bar'
        if (URLParts.length > 1) {
          queryString = URLParts[1];
        } else {
          // passed something like 'foo=bar'
          queryString = URLParts[0];
        }
        var splittedParams = queryString.split('&');
        return _.reduce(splittedParams, function (parsedParams, param) {
          param = param.split('=');
          parsedParams[param[0]] = param[1];
          return parsedParams;
        }, {});
      };
      /**
      * Transform {foo: 'bar', hello: 'world'} => ?foo=bar&hello=world
      * @private
      * @param {Object} in_queryObject - object to serialize into a queryString
      * @return {String} - queryString representation of the object
      */
      var _objectToQueryParamsString = function (in_queryObject) {
        in_queryObject = in_queryObject || {};
        return _.isEmpty(in_queryObject) ? '' : '?' + querystring.stringify(in_queryObject);
      };
      /**
       * Method used to add parameters to an existing url
       * @param {String} in_url - base url to add parameters to
       * @param {Object} in_params - parameters to add
       * @return {String} Newly generated url
       */
      UrlUtils.addParameters = function (in_url, in_params) {
        // Split off an anchor in the URL
        var splittedURL = in_url.split('#');
        in_url = splittedURL[0];
        var hash = splittedURL[1] !== undefined ? '#' + splittedURL[1] : '';
        var paramsString = _objectToQueryParamsString(in_params);
        if (in_url.indexOf('?') !== -1) {
          paramsString = paramsString.replace('?', '&');
        }
        return in_url + paramsString + hash;
      };
      exportModule('LYNX.Utils.UrlUtils', UrlUtils);
    }());
    (function () {
      var _ = include('underscore');
      /**
       * Instantiates an OperationError, which mimics the {@link #Error} class with added properties
       * meant for reporting the result of operations.
       * @param {string} message The error message.
       * @param {?string} operation The operation name.
       * @param {?number} statusCode The operation result as a numerical status code.
       * @param {?number} flags Flags that characterize the error. See {@link OperationError#FLAGS}.
       * @constructor
       * @alias LYNX.Utils.HTTPError
       */
      var OperationError = function (message, operation, statusCode, flags) {
        this.name = 'OperationError';
        this.message = message;
        this.operation = operation;
        this.statusCode = statusCode;
        this.flags = flags || 0;
        this.stack = new Error().stack;
      };
      OperationError.prototype = Object.create(Error.prototype);
      OperationError.prototype.constructor = OperationError;
      /**
       * Flags that may be set on the error instance.
       * @type {{TRANSIENT: number}}
       */
      OperationError.FLAGS = {
        /**
         * A transient error results from an operation that could succeed if retried.
         */
        TRANSIENT: 1
      };
      /**
       * @return {string} A string representation of the error flags.
       * @private
       * @this OperationError
       */
      var _flagsToString = function () {
        var that = this;
        var flagArray = [];
        _.mapObject(OperationError.FLAGS, function (flagValue, flagName) {
          if (_isFlagSet.call(that, flagValue)) {
            flagArray.push(flagName);
          }
        });
        return this.flags + ' [' + flagArray.join(',') + ']';
      };
      /**
       * Checks if a flag is set on the error instance.
       * @param {number} flag A flag value from {@link OperationError#FLAGS}.
       * @return {boolean} True if the flag is set on the error instance, false otherwise.
       * @private
       * @this OperationError
       */
      var _isFlagSet = function (flag) {
        return (this.flags & flag) === flag;
      };
      /**
       * @return {boolean} True if the error results from an operation that could succeed if retried,
       *   false otherwise.
       */
      OperationError.prototype.isTransient = function () {
        return _isFlagSet.call(this, OperationError.FLAGS.TRANSIENT);
      };
      /**
       * Returns a string representing the HTTPError object
       * @return {string} a string representing the HTTPError object
       */
      OperationError.prototype.toString = function () {
        var extendedFieldsArray = [];
        if (this.operation) {
          extendedFieldsArray.push(this.operation);
        }
        if (this.statusCode) {
          extendedFieldsArray.push(this.statusCode);
        }
        if (this.flags) {
          extendedFieldsArray.push(_flagsToString.call(this));
        }
        var msg = this.name;
        if (extendedFieldsArray.length > 0) {
          msg += '[' + extendedFieldsArray.join(', ') + ']';
        }
        msg += ': ' + this.message;
        return msg;
      };
      exportModule('LYNX.Utils.OperationError', OperationError);
    }());
    (function () {
      /**
       * @namespace LYNX.TemplateSchema
       * @alias LYNX.TemplateSchema
       * Namespace containing all schema-related data for property set validation
       */
      var primitiveTypes = [
        'Float32',
        'Float64',
        'Int8',
        'Uint8',
        'Int16',
        'Uint16',
        'Int32',
        'Uint32',
        'Bool',
        'String',
        'Reference',
        'Enum',
        'Int64',
        'Uint64'
      ];
      var reservedTypes = [
        'BaseProperty',
        'NamedProperty',
        'NodeProperty',
        'NamedNodeProperty',
        'RepositoryReferenceProperty'
      ];
      var templateSchema = {
        '$schema': 'http://json-schema.org/schema#',
        'title': 'Property set template schema',
        'id': 'http://www.autodesk.com/schemas/ps/template-schema.json',
        'definitions': {
          'annotation': {
            'type': 'object',
            'properties': {
              'description': { 'type': 'string' },
              'doc': {
                'type': 'string',
                'format': 'uri'
              }
            }
          },
          'primitive-typeid': { 'enum': primitiveTypes },
          'versioned-typeid': {
            'type': 'string',
            'pattern': '^[a-zA-Z0-9\\.]+:[a-zA-Z0-9\\.]+-\\d+\\.\\d+\\.\\d+$'
          },
          'typed-reference-typeid': {
            'type': 'string',
            'pattern': '^Reference<([a-zA-Z0-9\\.]+:[a-zA-Z0-9\\.]+(-\\d+\\.\\d+\\.\\d+)?|' + primitiveTypes.join('|') + '|' + reservedTypes.join('|') + ')>$'
          },
          'reserved-typeid': { 'enum': reservedTypes },
          'typeid': {
            'oneOf': [
              { '$ref': '#/definitions/primitive-typeid' },
              { '$ref': '#/definitions/versioned-typeid' },
              { '$ref': '#/definitions/typed-reference-typeid' },
              { '$ref': '#/definitions/reserved-typeid' }
            ]
          },
          'properties': {
            'type': 'array',
            'minItems': 1,
            'items': { '$ref': '#/definitions/property-item' }
          },
          'property-item': {
            'type': 'object',
            'properties': {
              'context': {
                'enum': [
                  'single',
                  'array',
                  'map',
                  'set'
                ]
              },
              'typeid': { '$ref': '#/definitions/typeid' },
              'id': { 'type': 'string' },
              'properties': { '$ref': '#/definitions/properties' },
              'annotation': { '$ref': '#/definitions/annotation' },
              'length': {
                'type': 'integer',
                'multipleOf': 1,
                'minimum': 0
              },
              'optional': { 'type': 'boolean' }
            },
            'required': ['id'],
            'switch': [
              {
                'if': { 'properties': { 'typeid': { 'constant': 'Enum' } } },
                'then': { 'properties': { 'properties': { 'type': 'array' } } },
                'continue': false
              },
              {
                'then': {
                  'anyOf': [
                    {
                      'properties': {
                        'properties': { 'type': 'array' },
                        'typeid': { 'not': { 'type': 'string' } }
                      }
                    },
                    {
                      'properties': {
                        'typeid': { 'type': 'string' },
                        'properties': { 'not': { 'type': 'array' } }
                      }
                    }
                  ]
                }
              }
            ]
          }
        },
        'type': 'object',
        'minProperties': 1,
        'properties': {
          'typeid': { '$ref': '#/definitions/versioned-typeid' },
          'properties': { '$ref': '#/definitions/properties' },
          'inherits': {
            'oneOf': [
              { '$ref': '#/definitions/typeid' },
              {
                'type': 'array',
                'items': { '$ref': '#/definitions/typeid' }
              }
            ]
          },
          'annotation': { '$ref': '#/definitions/annotation' }
        },
        'required': ['typeid']
      };
      exportModule('LYNX.TemplateSchema', templateSchema);
    }());
    (function () {
      var TypeIdHelper = {};
      var templateSchemaJson = include('LYNX.TemplateSchema');
      /**
       * Checks whether the given type is a primitive type.
       *
       * @param {string} in_typeid - The typeid we want to check
       * @return {boolean} Is this a base type?
       */
      TypeIdHelper.isPrimitiveType = function (in_typeid) {
        if (in_typeid === undefined || in_typeid === '')
          return false;
        var primitiveTypes = templateSchemaJson.definitions['primitive-typeid']['enum'];
        return in_typeid.substr(0, 5) === 'enum<' || in_typeid.substr(0, 10) === 'Reference<' || primitiveTypes.indexOf(in_typeid) >= 0;
      };
      /**
       * Checks whether the given type is a reserved type.
       *
       * @param {string} in_typeid - The typeid we want to check
       * @return {boolean} Is this a reserved type?
       */
      TypeIdHelper.isReservedType = function (in_typeid) {
        var reservedTypes = templateSchemaJson.definitions['reserved-typeid']['enum'];
        return reservedTypes.indexOf(in_typeid) >= 0;
      };
      /**
       * Extract the version number from the given typeid
       * @param {string} in_typeid The typeid to check against
       * @return {Array} Array of size two
       */
      TypeIdHelper.extractVersion = function (in_typeid) {
        var splitTypeId = in_typeid.split('-');
        return {
          version: splitTypeId[1],
          typeidWithoutVersion: splitTypeId[0]
        };
      };
      /**
       * Extracts the context from a typeid
       *
       * @param {string} in_typeid - The typeid to process
       * @return {{typeid: string, context:string, isEnum:boolean}} Returns the typeid without context, the context and
       *     if we have an enum type
       */
      TypeIdHelper.extractContext = function (in_typeid) {
        var bracketIndex = in_typeid.indexOf('<');
        if (bracketIndex !== -1 && in_typeid[in_typeid.length - 1] === '>') {
          var typeid = in_typeid.substr(bracketIndex + 1, in_typeid.length - bracketIndex - 2);
          var context = in_typeid.substr(0, bracketIndex);
          // Special case to handle collections without a typeid (e.g. "map<>", which should
          // be able to support all property types
          if (typeid === '') {
            typeid = context !== 'set' ? 'BaseProperty' : 'NamedProperty';
          }
          // Special case to handle enums (e.g. array<enum<myType>>)
          var isEnum = false;
          if (context === 'enum' || typeid.substr(0, 5) === 'enum<') {
            isEnum = true;
            if (context === 'enum') {
              context = 'single';
            } else {
              // remove the `enum<...>` tag to get the raw typeid
              typeid = typeid.substr(5, typeid.length - 6);
            }
          }
          if (context === 'Reference') {
            typeid = 'Reference<' + typeid + '>';
            context = 'single';
          }
          return {
            typeid: typeid,
            context: context,
            isEnum: isEnum
          };
        } else {
          return {
            typeid: in_typeid,
            context: 'single',
            isEnum: false
          };
        }
      };
      /**
       * Creates a collection typeid string from the
       * typeid and the context.
       *
       * @param {string} in_typeid  - the typeid in the collection
       * @param {string} in_context - the context
       * @param {bool}  in_enum    - set to true, if the type should get an enum tag
       *
       * @return {string} The combined typeid string
       */
      TypeIdHelper.createSerializationTypeId = function (in_typeid, in_context, in_enum) {
        // in_enum
        if (in_typeid === 'BaseProperty') {
          // Special case for BaseProperties. These get represented as a collection
          // typeid without a child typeid. E.g. map<> instead of map<BaseProperty>
          return in_context + '<>';
        } else {
          if (in_enum) {
            if (in_context === '' || in_context === 'single') {
              return 'enum<' + in_typeid + '>';
            } else {
              return in_context + '<enum<' + in_typeid + '>>';
            }
          } else {
            return in_context + '<' + in_typeid + '>';
          }
        }
      };
      /**
       * Checks, whether the supplied typeid is a reference property type id
       *
       * @param {string} in_typeid - The typeid to check
       * @return {boolean} Is this a reference property typeid?
       */
      TypeIdHelper.isReferenceTypeId = function (in_typeid) {
        // in_enum
        return in_typeid === 'Reference' || in_typeid.substr(0, 10) === 'Reference<' && in_typeid.substr(-1) === '>';
      };
      /**
       * Returns the type of the properties a reference points to
       *
       * @param {string} in_typeid - The typeid to process
       * @return {string} The type of the referenced property
       */
      TypeIdHelper.extractReferenceTargetTypeIdFromReference = function (in_typeid) {
        // in_enum
        if (in_typeid.substr(0, 10) === 'Reference<') {
          // Extract the type from the TypeID
          return in_typeid.substr(10, in_typeid.length - 11);
        } else {
          // This is a typeless reference, we allow all types
          return 'BaseProperty';
        }
      };
      exportModule('LYNX.Property.TypeIdHelper', TypeIdHelper);
    }());
    (function () {
      var _ = include('underscore');
      var TypeIdHelper = include('LYNX.Property.TypeIdHelper');
      var deepCopy = include('LYNX.Utils.deepCopy');
      /**
       * Constructor for creating a PropertyTemplate based on the given parameters.
       * @param {object} in_params List of parameters
       * @param {string} in_params.id id of the property
       * @param {string} in_params.name Name of the property
       * @param {string} in_params.typeid The type identifier
       * @param {number=} [in_params.length=1] The length of the property. Only valid if
       *   the property is an array, otherwise the length defaults to 1
       * @param {string} in_params.context The type of property this template represents
       *   i.e. array, hash, etc.
       * @param {Array.<object>} in_params.properties List of property templates that
       *   are used to define children properties
       * @param {Array.<string>} in_params.inherits List of property template typeids that this
       *   PropertyTemplate inherits from
       *
       * @constructor
       * @alias LYNX.Property.PropertyTemplate
       */
      var PropertyTemplate = function (in_params) {
        in_params = in_params || {};
        /** The identifier of the property */
        this.id = in_params.id;
        /** The type identifier of the property */
        this.typeid = in_params.typeid;
        /** Size of the property (if this is an array) */
        if (in_params.context === 'array') {
          if (in_params.length !== undefined) {
            this.length = in_params.length;
          } else if (in_params.size !== undefined) {
            console.warn('Deprecated: use length in property declarations.');
            this.length = in_params.size;
          } else {
            this.length = 0;
          }
        } else {
          this.length = 1;
        }
        console.assert(_.isNumber(this.length), 'length must be a number');
        /** The context of the property */
        this.context = in_params.context;
        /** Array with sub-properties */
        this.properties = in_params.properties;
        /** The annotation object */
        this.annotation = in_params.annotation || {};
        /** Typeids of properties this property inherits from */
        this.inherits = _.isString(in_params.inherits) ? [in_params.inherits] : in_params.inherits;
        if (_.contains(this.inherits, 'Enum')) {
          this._enumDictionary = this._parseEnums(this.properties);
        }
        // check for inlined enums and parse them:
        this._digestNestedInlineEnumProperties(this);
        this._serializedParams = in_params;
      };
      PropertyTemplate.prototype.hasNestedProperties = function () {
        return this.properties && this.properties.length > 0;
      };
      /**
       * intenlal function to recursivly traverse a property template and create dictionaries for found inline enums
       * @param {{}} in_currentPropertyLevel the current level in the template hierarchie
       */
      PropertyTemplate.prototype._digestNestedInlineEnumProperties = function (in_currentPropertyLevel) {
        if (in_currentPropertyLevel.properties) {
          for (var i = 0; i < in_currentPropertyLevel.properties.length; i++) {
            if (in_currentPropertyLevel.properties[i].typeid === 'Enum') {
              var dictionary = this._parseEnums(in_currentPropertyLevel.properties[i].properties);
              in_currentPropertyLevel.properties[i]._enumDictionary = dictionary;
            } else if (in_currentPropertyLevel.properties[i].properties) {
              // call self
              this._digestNestedInlineEnumProperties(in_currentPropertyLevel.properties[i]);
            }
          }
        }
      };
      /**
       * read the enum types list of a template and create a dictionary [value->enum] and [enum->value] for it
       * to efficiently lookup enums/values when setting/getting them from the property
       * @param {Array} in_enumProperties - the list of enums and their values and annotations
       * @return {{}} a dictionary [value->enum] and [enum->value]
       */
      PropertyTemplate.prototype._parseEnums = function (in_enumProperties) {
        var enumDictionary = {
          enumEntriesById: {},
          enumEntriesByValue: {}
        };
        for (var i = 0; i < in_enumProperties.length; i++) {
          var enumEntry = in_enumProperties[i];
          console.assert(enumEntry.id, 'Enum: typeid missing');
          console.assert(!_.isNaN(enumEntry.value), 'Enum: value NaN');
          enumDictionary.enumEntriesById[enumEntry.id] = {
            value: enumEntry.value,
            annotation: enumEntry.annotation
          };
          enumDictionary.enumEntriesByValue[enumEntry.value] = {
            id: enumEntry.id,
            annotation: enumEntry.annotation
          };
        }
        return enumDictionary;
      };
      /**
       * Clones the PropertyTemplate
       *
       * @return {LYNX.Property.PropertyTemplate} The cloned template
       */
      PropertyTemplate.prototype.clone = function () {
        return new PropertyTemplate(this);
      };
      /**
       * Method used to check whether the template is versioned.
       * A versioned template is of the form `xxxx-1.0.0`
       * @return {boolean} Returns true if the template is versioned, false otherwise
       */
      PropertyTemplate.prototype.isVersioned = function () {
        var splitTypeId = TypeIdHelper.extractVersion(this.typeid);
        if (!splitTypeId.version) {
          return false;
        }
        var version = splitTypeId.version;
        return /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/.test(version);
      };
      /**
       * Return the version number of the template.
       * @return {undefined|string} The version string is returned.
       * If the template is not versioned, a warning message is printed
       * and an undefined value is returned
       */
      PropertyTemplate.prototype.getVersion = function () {
        if (this.isVersioned()) {
          var splitTypeId = TypeIdHelper.extractVersion(this.typeid);
          var version = splitTypeId.version;
          return version;
        } else {
          console.warn('Template is not versioned', this.typeid);
          return undefined;
        }
      };
      /**
       * Return the serialized parameters passed in the constructor
       * @return {object} Serialized parameters
       */
      PropertyTemplate.prototype.serialize = function () {
        return deepCopy(this._serializedParams);
      };
      /**
       * Return the typeid of the template without the version number
       * i.e. autodesk.core:color
       * @return {string} The typeid without the version is returned.
       * If the template is not versioned, the typeid is return.
       */
      PropertyTemplate.prototype.getTypeidWithoutVersion = function () {
        if (this.isVersioned()) {
          var splitTypeId = TypeIdHelper.extractVersion(this.typeid);
          return splitTypeId.typeidWithoutVersion;
        } else {
          return this.typeid;
        }
      };
      exportModule('LYNX.Property.PropertyTemplate', PropertyTemplate);
    }());
    (function () {
      var PROPERTY_PATH_DELIMITER = include('LYNX.Property._constants.PROPERTY_PATH_DELIMITER');
      var MSG = include('LYNX.Property._constants.MSG');
      /**
       * @namespace
       * @alias     LYNX.Property.PathHelper
       *
       * Helper functions for string processing
      */
      var PathHelper = {};
      /**
       * @enum Object
       * Type of the token in the path string
       */
      PathHelper.TOKEN_TYPES = {
        /** A normal path segment, separated via . */
        PATH_SEGMENT_TOKEN: 0,
        /** An array path segment, separated via [ ] */
        ARRAY_TOKEN: 1,
        /** A / at the beginning of the path */
        PATH_ROOT_TOKEN: 2,
        /** A * that indicates a dereferencing operation */
        DEREFERENCE_TOKEN: 3,
        /** A ../ that indicates one step above the current path */
        RAISE_LEVEL_TOKEN: 4
      };
      /**
       * Tokenizes a path string
       *
       * @param {string}                               in_path   - The path string to divide into tokens
       * @param {LYNX.Property.PathHelper.TOKEN_TYPES} out_types - The types of the tokens
       *
       * @return {Array.<string>} the tokens from the path string
       */
      PathHelper.tokenizePathString = function (in_path, out_types) {
        // eslint-disable-line complexity
        var tokens = [];
        var currentToken = '';
        if (out_types) {
          // Make sure out_types is empty
          out_types.splice(0, out_types.length);
        }
        // Handle a / at the beginning of the path by adding a special token for it
        var path_start = 0;
        if (in_path[0] === '/') {
          tokens.push('/');
          if (out_types) {
            out_types.push(PathHelper.TOKEN_TYPES.PATH_ROOT_TOKEN);
          }
          path_start = 1;
        } else if (in_path.substr(0, 3) === '../') {
          // Handle relative paths by extracting the number steps above
          var extractLevel = function (current_path) {
            if (current_path.substr(0, 3) === '../') {
              if (out_types) {
                out_types.push(PathHelper.TOKEN_TYPES.RAISE_LEVEL_TOKEN);
              }
              tokens.push('../');
              extractLevel(current_path.substr(3));
              path_start = path_start + 3;
            }
          };
          extractLevel(in_path);
        }
        var inSquareBrackets = false;
        var tokenStarted = false;
        var lastTokenWasQuoted = false;
        // We are in a context where an empty token is valid
        var atStartToken = false;
        var allowSegmentStart = true;
        var storeNextToken = function (tokenType) {
          // Make sure, this is not an empty token (E.g. a .. or a [] )
          if (!tokenStarted) {
            if (!atStartToken) {
              throw new Error(MSG.EMPTY_TOKEN);
            } else {
              return;
            }
          }
          // Store the token
          tokens.push(currentToken);
          currentToken = '';
          tokenStarted = false;
          atStartToken = false;
          lastTokenWasQuoted = false;
          allowSegmentStart = false;
          if (out_types) {
            out_types.push(tokenType);
          }
        };
        for (var i = path_start; i < in_path.length; i++) {
          var character = in_path[i];
          if (character === '"') {
            // If we encounter a quotation mark, we start parsing the
            // quoted section
            if (!tokenStarted) {
              var endFound = false;
              // Read the quoted token
              for (i++; i < in_path.length; i++) {
                if (in_path[i] === '"') {
                  // We have found the end of the quoted token
                  endFound = true;
                  break;
                } else if (in_path[i] === '\\') {
                  // Read an escaped symbol
                  if (in_path.length > i + 1) {
                    if (in_path[i + 1] === '\\') {
                      currentToken += '\\';
                      i++;
                    } else if (in_path[i + 1] === '"') {
                      currentToken += '"';
                      i++;
                    } else {
                      throw new Error(MSG.INVALID_ESCAPE_SEQUENCE);
                    }
                  } else {
                    throw new Error(MSG.INVALID_ESCAPE_SEQUENCE);
                  }
                } else {
                  // Everything else is just added to the token
                  currentToken += in_path[i];
                }
              }
              if (!endFound) {
                throw new Error(MSG.UNCLOSED_QUOTATION_MARKS);
              }
              lastTokenWasQuoted = true;
              tokenStarted = true;
            } else {
              throw new Error(MSG.QUOTES_WITHIN_TOKEN);
            }
          } else if (!inSquareBrackets) {
            if (character === PROPERTY_PATH_DELIMITER) {
              // A dot symbols starts a new token
              storeNextToken(PathHelper.TOKEN_TYPES.PATH_SEGMENT_TOKEN);
              allowSegmentStart = true;
            } else if (character === '[') {
              // An opening square bracket starts a new token
              if (tokenStarted) {
                storeNextToken(PathHelper.TOKEN_TYPES.PATH_SEGMENT_TOKEN);
              }
              // And sets the state to inSquareBrackets
              inSquareBrackets = true;
            } else if (character === ']') {
              throw new Error(MSG.CLOSING_BRACKET_WITHOUT_OPENING);
            } else if (character === '*') {
              // Store the last token
              if (tokenStarted) {
                storeNextToken(PathHelper.TOKEN_TYPES.PATH_SEGMENT_TOKEN);
              }
              // Create a new dereference token
              tokens.push('*');
              if (out_types) {
                out_types.push(PathHelper.TOKEN_TYPES.DEREFERENCE_TOKEN);
              }
              // Reset the token started flag
              tokenStarted = false;
              atStartToken = true;
              allowSegmentStart = false;
            } else {
              if (!tokenStarted && !allowSegmentStart && !inSquareBrackets) {
                throw new Error(MSG.MISSING_DOT_AT_SEGMENT_START);
              }
              currentToken += character;
              // We have started parsing the token
              tokenStarted = true;
              // When a symbols appears after a closing quotation mark, we have an error
              if (lastTokenWasQuoted) {
                throw new Error(MSG.QUOTES_WITHIN_TOKEN);
              }
            }
          } else {
            if (character === ']') {
              // A closing square bracket starts a new token
              storeNextToken(PathHelper.TOKEN_TYPES.ARRAY_TOKEN);
              // We now have to check the next character,
              // as only the combinations '][' and '].' are
              // valid
              if (in_path.length > i + 1) {
                // We only have to check this at the end of the string
                if (in_path[i + 1] === PROPERTY_PATH_DELIMITER) {
                  // We are no longer in square brackets
                  inSquareBrackets = false;
                  allowSegmentStart = true;
                  i++;
                } else if (in_path[i + 1] === '[') {
                  // We remain in square brackets
                  // so inSquareBrackets remains true;
                  i++;
                } else if (in_path[i + 1] === '*') {
                  // We leave the square brackets
                  inSquareBrackets = false;
                } else {
                  throw new Error(MSG.INVALID_END_OF_SQUARE_BRACKETS);
                }
              } else {
                inSquareBrackets = false;
                tokenStarted = false;
              }
            } else if (character === PROPERTY_PATH_DELIMITER) {
              throw new Error(MSG.DOTS_IN_SQUARE_BRACKETS);
            } else {
              currentToken += character;
              // We have started parsing the token
              tokenStarted = true;
              // When a symbols appears after a closing quotation mark, we have an error
              if (lastTokenWasQuoted) {
                throw new Error(MSG.QUOTES_WITHIN_TOKEN);
              }
            }
          }
        }
        // At the end of the path we have to distinguish a few error cases
        if (inSquareBrackets) {
          // There was a un-closed bracket at the end
          throw new Error(MSG.UNCLOSED_BRACKETS);
        } else if (in_path[in_path.length - 1] === PROPERTY_PATH_DELIMITER) {
          // A path ended with a PROPERTY_PATH_DELIMITER
          throw new Error(MSG.DOT_AT_END);
        } else if (tokenStarted) {
          // There was a valid, not yet ended token
          storeNextToken(PathHelper.TOKEN_TYPES.PATH_SEGMENT_TOKEN);
        }
        return tokens;
      };
      /**
       * Creates a quoted string for a path seqment to make sure it parses correctly
       *
       * @param {string} in_pathSegment   - The path string to put in quotes
       *
       * @return {string} quoted path string
       */
      PathHelper.quotePathSegment = function (in_pathSegment) {
        // WARNING: I use RegExps here, as the normal replace
        //          function only replaces the first occurrence
        // First we escape escape symbols
        in_pathSegment = in_pathSegment.replace(/\\/g, '\\\\');
        // Then we escape quotes
        in_pathSegment = in_pathSegment.replace(/"/g, '\\"');
        // And finally, we put the string into quotation marks
        return '"' + in_pathSegment + '"';
      };
      /**
       * Adds quotation marks to a path string if they are needed
       *
       * @param {string} in_pathSegment   - The path string to put in quotes
       *
       * @return {string} quoted path string
       */
      PathHelper.quotePathSegmentIfNeeded = function (in_pathSegment) {
        if (in_pathSegment.indexOf(PROPERTY_PATH_DELIMITER) !== -1 || in_pathSegment.indexOf('"') !== -1 || in_pathSegment.indexOf('\\') !== -1 || in_pathSegment.indexOf('/') !== -1 || in_pathSegment.indexOf('*') !== -1 || in_pathSegment.indexOf('[') !== -1 || in_pathSegment.indexOf(']') !== -1 || in_pathSegment.length === 0) {
          return PathHelper.quotePathSegment(in_pathSegment);
        } else {
          return in_pathSegment;
        }
      };
      /**
       * This function checks, whether the supplied path is a valid repository absolute path.
       *
       * It has to be either an empty string, or a path starting with a /
       *
       * @param {String} in_path - The path to check
       */
      PathHelper.checkValidRepositoryAbsolutePath = function (in_path) {
        if (in_path !== '' && // either an empty reference
          in_path[0] !== '/') {
          // or an absolute path starting with /
          throw new Error(MSG.INVALID_PATH_IN_REFERENCE);
        }
      };
      exportModule('LYNX.Property.PathHelper', PathHelper);
    }());
    (function () {
      var _ = include('underscore');
      var Property = include('LYNX.Property', [
        'ChangeSet',
        'ValueProperty',
        'NodeProperty',
        'StringProperty',
        'RepositoryReferenceProperty'
      ]);
      var PathHelper = include('LYNX.Property.PathHelper');
      var MSG = include('LYNX.Property._constants.MSG');
      var BREAK_TRAVERSAL = 'BREAK';
      var PROPERTY_PATH_DELIMITER = include('LYNX.Property._constants.PROPERTY_PATH_DELIMITER');
      // Constant that indicates that the caches are still disabled (only used internally in this file)
      // We arbitrarily selected a value that evaluates to true and is different from true to distinguish the case
      // where the cache is still disabled
      var _CACHES_INITIALLY_DISABLED = 2;
      /**
       * Used to indicate the state of a property. These flags can be connected via OR.
       * @enum {number}
       * @alias LYNX.Property.BaseProperty.MODIFIED_STATE_FLAGS
       */
      var MODIFIED_STATE_FLAGS = {
        /** No changes to this property at the moment */
        CLEAN: 0,
        /** The property is marked as changed in the currently pending ChangeSet */
        PENDING_CHANGE: 1,
        /** The property has been modified and the result has not yet been reported to the application for scene updates */
        DIRTY: 2
      };
      /**
       * A Parameter in great generality.
       * @param {object} in_params List of parameters
       * @param {string} in_params.id id of the property
       * @param {string=} [in_params.typeid='BaseProperty'] The type unique identifier
       * @param {number=} [in_params.length=1] The length of the property. Only valid if
       *   the property is an array, otherwise the length defaults to 1
       * @param {string=} [in_params.context='single]' The type of property this template represents
       *   i.e. array, hash, etc.
       * @param {Array.<object>} in_params.properties List of property templates that
       *   are used to define children properties -- UNUSED PARAMETER ??
       * @param {Array.<string>} in_params.inherits List of property template typeids that this
       *   PropertyTemplate inherits from -- UNUSED PARAMETER ??
       * @abstract
       * @constructor
       * @alias LYNX.Property.BaseProperty
       */
      var BaseProperty = function (in_params) {
        // Pre-conditions
        console.assert(in_params, 'Object expected as parameters to BaseProperty constructor');
        in_params = in_params || {};
        this._id = in_params.id;
        this._typeid = in_params.typeid || 'BaseProperty';
        this._context = in_params.context || 'single';
        this._parent = undefined;
        this._children = {};
        // internal management
        this._dirty = MODIFIED_STATE_FLAGS.CLEAN;
        //     this._dirtyQueue = [];
        this._childCount = 0;
        this._length = in_params.length;
        this._structChange = _CACHES_INITIALLY_DISABLED;
        // Caches are disabled until all static members have been added
        this._flattenedRepresentation = undefined;
        this._absolutePath = undefined;
        this._cachedPaths = undefined;
        this._rootRef = undefined;
      };
      BaseProperty.MODIFIED_STATE_FLAGS = MODIFIED_STATE_FLAGS;
      BaseProperty._CACHES_INITIALLY_DISABLED = _CACHES_INITIALLY_DISABLED;
      /**
       * @return {string} - The typeid of this property
       */
      BaseProperty.prototype.getTypeid = function () {
        return this._typeid;
      };
      /**
       * @return {string} - The context of this property
       */
      BaseProperty.prototype.getContext = function () {
        return this._context;
      };
      /**
       * Get the scope to which this property belongs to.
       * @return {string|undefined} The guid representing the scope in which the
       * property belongs to
       * @private
       */
      BaseProperty.prototype._getScope = function () {
        if (this._parent) {
          return this.getRoot()._getScope();
        } else {
          return undefined;
        }
      };
      /**
       * Returns the full property type identifier for the ChangeSet including the enum type id
       * @param  {boolean} in_hideCollection - if true the collection type (if applicable) will be omitted
       *                since that is not aplicable here, this param is ignored
       * @return {string} The typeid
       * @private
       */
      BaseProperty.prototype._getTypeidForChangeSet = function (in_hideCollection) {
        return this._typeid;
      };
      /**
       * Updates the parent for the property
       *
       * @param {LYNX.Property.BaseProperty} in_property - The parent property
       * @private
       */
      BaseProperty.prototype._setParent = function (in_property) {
        if (this._parent !== in_property) {
          this._parent = in_property;
          // We have to free the cache in all sub-properties,
          // since they include references to the root
          this._freeCache();
        }
      };
      /**
       * Is this property the root?
       *
       * TODO: What exactly is the semantic we want to have here? Root of the repository? Or root of the CheckOutView?
       *
       * @return {boolean} Is this a root?
       */
      BaseProperty.prototype.isRoot = function () {
        // This checks, whether this is the root of a CheckOutView
        // (all other properties should have a parent property)
        return this._parent === undefined;
      };
      /**
       * Is this property a leaf node with regard to flattening?
       *
       * TODO: Which semantics should flattening have? It stops at primitive types and collections?
       *
       * @return {boolean} Is it a leaf with regard to flattening?
       */
      BaseProperty.prototype._isFlattenLeaf = function () {
        return false;
      };
      /**
       * Get the parent of this property
       *
       * @return {LYNX.Property.BaseProperty|undefined} The parent of this property (or undefined if none existed)
       */
      BaseProperty.prototype.getParent = function () {
        return this._parent;
      };
      /**
       * @return {boolean} Is this a dynamic property (only properties inheriting from NodeProperty are)
       */
      BaseProperty.prototype.isDynamic = function () {
        return false;
      };
      /**
       * Sets the property as dirty. This will flag it as changed in the ChangeSet and at the same time mark it as
       * dirty with an unreported change.
       *
       * @param {boolean} [in_reportToView = true] - By default, the dirtying will always be reported to the checkout view
       *                                             and trigger a modified event there. When batching updates, this
       *                                             can be prevented via this flag.
       * @param {LYNX.Property.BaseProperty} [in_callingChild] - The child which is dirtying its parent
       * @private
       */
      BaseProperty.prototype._setDirty = function (in_reportToView, in_callingChild) {
        // We only update the flags upwards in the tree, when the corresponding nodes are not already flagged as
        // both dirty and changed
        if (this._dirty !== (MODIFIED_STATE_FLAGS.DIRTY | MODIFIED_STATE_FLAGS.PENDING_CHANGE)) {
          // only dirty once until clean.
          this._dirty = MODIFIED_STATE_FLAGS.DIRTY | MODIFIED_STATE_FLAGS.PENDING_CHANGE;
          // Report dirtiness upwards in the hierarchy
          if (this._parent) {
            this._parent._setDirty(in_reportToView, this);
          } else if (in_reportToView === true || in_reportToView === undefined) {
            this._reportDirtinessToView();
          }
        } else if (in_reportToView === true || in_reportToView === undefined) {
          this._reportDirtinessToView();
        }
      };
      /**
       * Dirties this node and all of its children
       *
       * @param {boolean} [in_reportToView = true] - By default, the dirtying will always be reported to the checkout view
       *                                             and trigger a modified event there. When batching updates, this
       *                                             can be prevented via this flag.
       * @private
       */
      BaseProperty.prototype._setDirtyTree = function (in_reportToView) {
        this.traverseDown(function (node) {
          // Set all nodes to dirty, but prevent recursive updates up to the repository for the individual changes
          node._setDirty(false);
        });
        // Now make one report
        if (in_reportToView) {
          this._reportDirtinessToView();
        }
      };
      /**
       * Helper function, which reports the fact that a property has been dirtied to the checkout view
       * @private
       */
      BaseProperty.prototype._reportDirtinessToView = function () {
        // Get the root of the property hierarchy
        var currentNode = this;
        while (currentNode._parent) {
          currentNode = currentNode._parent;
        }
        // Report the dirtiness to the checkout view
        if (currentNode._checkedOutRepositoryInfo && currentNode._isDirty(BaseProperty.MODIFIED_STATE_FLAGS.DIRTY)) {
          currentNode._checkedOutRepositoryInfo._propertyDirtied();
        }
      };
      /**
       * Modifies the property according to the given changeset
       *
       * @param {LYNX.Property.SerializedChangeSet} in_changeSet - The changeset to apply
       */
      BaseProperty.prototype.applyChangeset = function (in_changeSet) {
        this._checkIsNotReadOnly();
        // We just forward the call to the internal function
        this._applyChangeset(in_changeSet, true);
      };
      /**
       * Modifies the property according to the given changeset
       *
       * Internal function.
       *
       * @param {LYNX.Property.SerializedChangeSet} in_changeSet - The changeset to apply
       * @param {boolean} [in_reportToView = true] - By default, the dirtying will always be reported to the checkout view
       *                                             and trigger a modified event there. When batching updates, this
       *                                             can be prevented via this flag.
       */
      BaseProperty.prototype._applyChangeset = function (in_changeSet, in_reportToView) {
        var typeids = _.keys(in_changeSet);
        for (var i = 0; i < typeids.length; i++) {
          var typeid = typeids[i];
          if (Property.ChangeSet.isReservedKeyword(typeid)) {
            continue;  // Ignore the special keys
          }
          var paths = _.keys(in_changeSet[typeid]);
          for (var j = 0; j < paths.length; j++) {
            var path = paths[j];
            var property = this.resolvePath(path);
            if (property) {
              property._applyChangeset(in_changeSet[typeid][path], false);
            } else {
              throw new Error(MSG.INVALID_PATH + path);
            }
          }
        }
        if (in_reportToView) {
          this._reportDirtinessToView();
        }
      };
      /**
       * Removes the dirtiness flag from this property
       * @param {LYNX.Property.BaseProperty.MODIFIED_STATE_FLAGS} [in_flags] - The flags to clean, if none are supplied all
       *                                                                       will be removed
       * @private
       */
      BaseProperty.prototype._cleanDirty = function (in_flags) {
        this._dirty = in_flags === undefined ? MODIFIED_STATE_FLAGS.CLEAN : this._dirty & ~in_flags;
      };
      /**
       * Removes the dirtiness flag from this property and recursively from all of its children
       *
       * @param {LYNX.Property.BaseProperty.MODIFIED_STATE_FLAGS} [in_flags] - The flags to clean, if none are supplied all
       *                                                                       will be removed
       */
      BaseProperty.prototype.cleanDirty = function (in_flags) {
        var dirtyChildren = this._getDirtyChildren(in_flags);
        var child;
        for (var i = 0; i < dirtyChildren.length; i++) {
          child = this.get(dirtyChildren[i]);
          child.cleanDirty(in_flags);
          child._cleanDirty(in_flags);
        }
        // after all paths are clean, we are also clean!
        this._cleanDirty(in_flags);
      };
      /**
       * Indicates that the property has been modified and a corresponding modified call has not yet been sent to the
       * application for runtime scene updates.
       *
       * @param {LYNX.Property.BaseProperty.MODIFIED_STATE_FLAGS} [in_dirtinessType] -
       *     The type of dirtiness to check for. By default this is DIRTY
       * @return {boolean} Is the property dirty?
       * @private
       */
      BaseProperty.prototype._isDirty = function (in_dirtinessType) {
        in_dirtinessType = in_dirtinessType === undefined ? MODIFIED_STATE_FLAGS.DIRTY : in_dirtinessType;
        return !!(this._dirty & in_dirtinessType);
      };
      /**
       * Indicates that the property has been modified and a corresponding modified call has not yet been sent to the
       * application for runtime scene updates.
       *
       * @return {boolean} Is the property dirty?
       */
      BaseProperty.prototype.isDirty = function () {
        return this._isDirty();
      };
      /**
       * The property has pending changes in the current ChangeSet.
       * @return {boolean} Has the property pending changes?
       */
      BaseProperty.prototype.hasPendingChanges = function () {
        return !!(this._dirty & MODIFIED_STATE_FLAGS.PENDING_CHANGE);
      };
      /**
       * Returns the ChangeSet of all sub-properties
       *
       * @return {LYNX.Property.ChangeSet} The serialized changes
       */
      BaseProperty.prototype.getPendingChanges = function () {
        var serialized = this._serialize(true, false, BaseProperty.MODIFIED_STATE_FLAGS.PENDING_CHANGE);
        return new Property.ChangeSet(serialized);
      };
      /**
       * Get the id of this property
       *
       * @return {string|undefined} The id of the property
       */
      BaseProperty.prototype.getId = function () {
        return this._id;
      };
      /**
       * Returns the path segment for a child
       *
       * @param {LYNX.Property.BaseProperty} in_childNode - The child for which the path is returned
       *
       * @return {string} The path segment to resolve the child property under this property
       * @protected
       */
      BaseProperty.prototype._getPathSegmentForChildNode = function (in_childNode) {
        return PROPERTY_PATH_DELIMITER + PathHelper.quotePathSegmentIfNeeded(in_childNode.getId());
      };
      /**
       * Resolves a direct child node based on the given path segment
       *
       * @param {String} in_segment                                   - The path segment to resolve
       * @param {LYNX.Property.PathHelper.TOKEN_TYPES} in_segmentType - The type of segment in the tokenized path
       * @param {Object} out_disableCache                            - The member disableCache will be set to true
       *                                                                in this object, if this path must not be cached
       *
       * @return {LYNX.Property.BaseProperty|undefined} The child property that has been resolved
       * @protected
       */
      BaseProperty.prototype._resolvePathSegment = function (in_segment, in_segmentType, out_disableCache) {
        // Base Properties only support paths separated via dots
        if (in_segmentType !== PathHelper.TOKEN_TYPES.PATH_SEGMENT_TOKEN) {
          throw new Error(MSG.INVALID_PATH_TOKEN);
        }
        if (this.has(in_segment)) {
          return this.get(in_segment);
        } else {
          return undefined;
        }
      };
      /**
       * Set the id of this property
       *
       * @param {string} in_id - The id for this property
       *
       * @return {string} the new id
       * @private
       */
      BaseProperty.prototype._setId = function (in_id) {
        if (!_.isString(in_id) && !_.isNumber(in_id)) {
          throw new Error('id should be a string or a number');
        }
        if (this._parent !== undefined) {
          throw new Error(MSG.ID_CHANGE_FOR_PROPERTY_WITH_PARENT);
        }
        this._id = String(in_id);
        // flush caches
        this._markStructChanged();
        this._setDirty();
        return in_id;
      };
      /**
       * Returns the number of children this node has
       * @return {number} The number of children
       * @private
       */
      BaseProperty.prototype._getChildrenCount = function () {
        // TODO: Is keeping track of this in a separate member
        //       worth the memory it takes?
        return this._childCount;
      };
      /**
       * Return a clone of this property
       * @return {BaseProperty} The cloned property
       */
      BaseProperty.prototype.clone = function () {
        var LYNX = include('LYNX', ['Property']);
        var clone = LYNX.Property.PropertyFactory._createProperty(this._getTypeidForChangeSet(), this._getScope());
        // TODO: this is not very efficient. Clone should be overriden
        // by the child classes
        clone.deserialize(this._serialize());
        clone.cleanDirty(BaseProperty.MODIFIED_STATE_FLAGS.PENDING_CHANGE | BaseProperty.MODIFIED_STATE_FLAGS.DIRTY);
        return clone;
      };
      /**
       * Returns the name of all the sub-properties of this property.
       *
       * @return {Array.<string>} An array of all the property ids
       */
      BaseProperty.prototype.getIds = function () {
        return Object.keys(this._children);
      };
      /**
       * Returns the sub-property having the given name in this property.
       *
       * @param  {string} in_id the ID of the property
       * @return {LYNX.Property.BaseProperty} The property you seek
       */
      BaseProperty.prototype.get = function (in_id) {
        return this._children[in_id];
      };
      /**
       * Checks whether a property with the given name exists
       *
       * @param {string} in_id - Name of the property
       * @return {boolean} Does the property exist?
       */
      BaseProperty.prototype.has = function (in_id) {
        return this._children[in_id] !== undefined;
      };
      /**
       * Get all sub-properties of the current property.
       * Caller MUST NOT modify the properties.
       *
       * @return {Object.<LYNX.Property.BaseProperty>} An object containing all the properties
       */
      BaseProperty.prototype.getEntriesReadOnly = function () {
        /* Note that the implementation is voluntarily generic so that derived classes
         should not have to redefine this function. */
        var res = {};
        var ids = this.getIds();
        for (var i = 0; i < ids.length; i++) {
          res[ids[i]] = this.get(ids[i]);
        }
        return res;
      };
      /**
       * Append a child property
       *
       * This is an internal function, called by the PropertyFactory when instantiating a template and internally by the
       * NodeProperty. Adding children dynamically by the user is only allowed in the NodeProperty.
       *
       * @param {LYNX.Property.BaseProperty} in_property the property to append
       * @protected
       */
      BaseProperty.prototype._append = function (in_property) {
        var id = in_property.getId();
        if (this._children[id] === undefined) {
          this._children[id] = in_property;
          in_property._setParent(this);
          ++this._childCount;
        } else {
          throw new Error(MSG.OVERWRITING_ID + id);
        }
        this._markStructChanged();
      };
      /**
       * Remove a child property
       *
       * This is an internal function, called internally by NodeProperty. Removing children dynamically by the user is
       * only allowed in the NodeProperty.
       *
       * @param {String} in_id - the id of the property to remove
       * @protected
       */
      BaseProperty.prototype._remove = function (in_id) {
        if (this._children[in_id] !== undefined) {
          this._children[in_id]._setParent(undefined);
          delete this._children[in_id];
          --this._childCount;
        } else {
          throw new Error(MSG.REMOVING_NON_EXISTING_ID + in_id);
        }
        this._markStructChanged();
      };
      /**
       * Get a flattened, tree like representation of this object and all of it's
       * descendants. The flattening will stop at primitive properties and collections.
       *
       * For non-leaf nodes, it is possible to access the corresponding node object itself via the
       * propertyNode member of the flattened representation (warning, this will overwrite a
       * property of this name).
       * TODO: Do we want to have this feature or is it to dangerous?
       *
       * @return {Object} the flat representation
       * @private
       */
      BaseProperty.prototype._flatten = function () {
        if (!this._structChange) {
          return this._flattenedRepresentation;
        } else {
          this._flattenedRepresentation = {};
          var keys = Object.keys(this._children);
          for (var i = 0; i < keys.length; i++) {
            var key = keys[i];
            var child = this._children[key];
            if (!child._isFlattenLeaf()) {
              this._flattenedRepresentation[key] = child._flatten();
            } else {
              this._flattenedRepresentation[key] = child;
            }
          }
          this._flattenedRepresentation.propertyNode = this;
          this._structChange = false;
          return this._flattenedRepresentation;
        }
      };
      /**
       * Sets the _structChange member to true in this node and all parents. This will
       * invalidate caches which are higher in the hierarchy. It should be called when
       * this node, or one of its children is changed
       *
       * @private
       */
      BaseProperty.prototype._markStructChanged = function () {
        this._structChange = true;
        // This might be more radical than necessary. Should we use
        // a fine grained strategy that only removes the affected paths?
        this._cachedPaths = undefined;
        this._flattenedRepresentation = undefined;
        if (this._parent) {
          this._parent._markStructChanged();
        }
      };
      /**
       * Repeatedly calls back the given function with human-readable string representations
       * of the property and of its sub-properties. By default it logs to the console.
       *
       * @param {function} [printFct=console.log] - Function to call for printing each property
       */
      BaseProperty.prototype.prettyPrint = function (printFct) {
        if (typeof printFct !== 'function') {
          printFct = console.log;
        }
        this._prettyPrint('', '', printFct);
      };
      /**
       * Repeatedly calls back the given function with human-readable string
       * representations of the property and of its sub-properties.
       * @param {string} indent - Leading spaces to create the tree representation
       * @param {string} externalId - Name of the current property at the upper level.
       *                              Used for arrays.
       * @param {function} printFct - Function to call for printing each property
       */
      BaseProperty.prototype._prettyPrint = function (indent, externalId, printFct) {
        var context = '';
        switch (this._context) {
        case 'map':
          context = 'Map of ';
          break;
        case 'set':
          context = 'Set of ';
          break;
        default:
          break;
        }
        printFct(indent + externalId + this.getId() + ' (' + context + this.getTypeid() + '):');
        this._prettyPrintChildren(indent, printFct);
      };
      /**
       * Repeatedly calls back the given function with human-readable string
       * representations of the property's sub-properties.
       * @param {string} indent - Leading spaces to create the tree representation
       * @param {function} printFct - Function to call for printing each property
       */
      BaseProperty.prototype._prettyPrintChildren = function (indent, printFct) {
        indent += '  ';
        var ids = this.getIds();
        for (var i = 0; i < ids.length; i++) {
          this.get(ids[i])._prettyPrint(indent, '', printFct);
        }
      };
      /**
       * Deprecated/not implemented serialization to JSON
       * TODO: Do we want to have a toJSON method besides the serialize function? Which format should the result have?
       * see LYNXDEV-124
       * @private
       * @deprecated
       */
      BaseProperty.prototype.toJSON = function () {
        throw new Error('Deprecated/Not implemented');
      };
      /**
       * Expand a path returning the value at the end.
       *
       * @param {string} in_path the path
       * @return {LYNX.Property.BaseProperty|undefined} resolved path
       */
      BaseProperty.prototype.resolvePath = function (in_path) {
        var cached;
        if (this._cachedPaths) {
          cached = this._cachedPaths[in_path];
          if (cached) {
            return cached;
          }
        } else {
          // We only create the cache map here because most
          // properties never get to be the 'root' path resolution
          // entry point. Creating it in the constructor would make that
          // most properties end up with a empty {} for no reason.
          this._cachedPaths = {};
        }
        var node = this;
        // Tokenize the path string
        var tokenTypes = [];
        var pathArr = PathHelper.tokenizePathString(in_path, tokenTypes);
        // Return to the repository root, if the path starts with a root token (a / )
        var iterationStart = 0;
        if (pathArr.length > 0) {
          if (tokenTypes[0] === PathHelper.TOKEN_TYPES.PATH_ROOT_TOKEN) {
            node = this.getRoot();
            iterationStart = 1;
          } else if (tokenTypes[0] === PathHelper.TOKEN_TYPES.RAISE_LEVEL_TOKEN) {
            for (var j = 0; j < pathArr.length; j++) {
              if (tokenTypes[j] === PathHelper.TOKEN_TYPES.RAISE_LEVEL_TOKEN) {
                var parent = node.getParent();
                if (parent) {
                  node = parent;
                } else {
                  return undefined;
                }
                iterationStart++;
              }
            }
          }
        }
        var disableCache = {};
        for (var i = iterationStart; i < pathArr.length && node; i++) {
          node = node._resolvePathSegment(pathArr[i], tokenTypes[i], disableCache);
        }
        if (node && disableCache.disableCache !== true) {
          this._cachedPaths[in_path] = node;
        }
        return node;
      };
      /**
       * Recursively free all caches in this node and all child nodes
       *
       * TODO: This doesn't work for reference properties yet (LYNXDEV-730)
       * @protected
       *
       * @param {boolean} [in_children = true] - This clears the caches for all children below this node.
       *                                         _markStructChanged instead clears the caches in all
       *                                         parent nodes
       */
      BaseProperty.prototype._freeCache = function (in_children) {
        // The caches
        if (this._structChange === _CACHES_INITIALLY_DISABLED) {
          return;
        }
        // Clean all caches (this is probably more than
        // strictly necessary)
        this._flattenedRepresentation = undefined;
        this._cachedPaths = {};
        this._absolutePath = undefined;
        this._rootRef = undefined;
        this._structChange = true;
        // do so recursively for all children too
        if (in_children || in_children === undefined) {
          this.traverseDown(function (in_node) {
            // Clear the cache on the children (not recursive, since the traverse method
            //                                  already traverses recursively)
            in_node._freeCache(false);
          });
        }
      };
      /**
       * Returns the path from the given ancestor node to this node
       *
       * @param {LYNX.Property.BaseProperty} in_ancestor - The ancestor node from which the
       *     path is computed
       *
       * @return {string} The path between the ancestor node and this node (calling returnPath with
       *     this path on the ancestor node will return this node)
       */
      BaseProperty.prototype.getRelativePath = function (in_ancestor) {
        var path = [];
        var foundAncestor = undefined;
        if (in_ancestor === this) {
          foundAncestor = BREAK_TRAVERSAL;
        } else {
          if (this.getParent()) {
            path.push(this.getParent()._getPathSegmentForChildNode(this));
            foundAncestor = this.traverseUp(function (in_node) {
              // break where we meet the relative reference
              if (in_node === in_ancestor) {
                return BREAK_TRAVERSAL;
              }
              if (in_node.getParent()) {
                path.push(in_node.getParent()._getPathSegmentForChildNode(in_node));
              }
              return undefined;
            });
          }
        }
        if (foundAncestor === BREAK_TRAVERSAL) {
          var result = path.reverse().join('');
          // We don't use a PROPERTY_PATH_DELIMITER at the start of the path
          if (result[0] === PROPERTY_PATH_DELIMITER) {
            result = result.substr(1);
          }
          return result;
        } else {
          throw new Error(MSG.NO_PATH_BETWEEN + in_ancestor.getAbsolutePath() + ' and ' + this.getAbsolutePath());
        }
      };
      /**
       * Returns the path from the root to this node (including a slash at the beginning)
       *
       * TODO: Relative to which root is this path? Root of the CheckoutView? Of the repository?
       *
       * @return {string} The path from the root
       */
      BaseProperty.prototype.getAbsolutePath = function () {
        if (this._absolutePath === undefined) {
          var path = this.isRoot() ? [] : [this.getParent()._getPathSegmentForChildNode(this)];
          this.traverseUp(function (in_node) {
            if (in_node.getParent()) {
              path.push(in_node.getParent()._getPathSegmentForChildNode(in_node));
            }
          });
          this._absolutePath = path.reverse().join('');
          // We don't use the property path separator at the start of the path
          if (this._absolutePath[0] === PROPERTY_PATH_DELIMITER) {
            this._absolutePath = this._absolutePath.substr(1);
          }
          this._absolutePath = '/' + this._absolutePath;
        }
        return this._absolutePath;
      };
      /**
       * Traverses the property hierarchy upwards until the a node without parent is reached
       *
       * @param {Function} in_callback - Callback to invoke for each of the parents. The traversal can be stopped
       *                                 by returning BaseProperty.BREAK_TRAVERSAL
       * @return {string|undefined} Returns BaseProperty.BREAK_TRAVERSAL, if the traversal didn't reach the root,
       *                            otherwise undefined
       */
      BaseProperty.prototype.traverseUp = function (in_callback) {
        if (this._parent) {
          var result = in_callback(this._parent);
          if (result !== BREAK_TRAVERSAL) {
            return this._parent.traverseUp(in_callback);
          } else {
            return BREAK_TRAVERSAL;
          }
        }
        return undefined;
      };
      /**
       * Traverses the property hierarchy downwards until all child properties are reached
       *
       * @param {Function} in_callback - Callback to invoke for each property. The traversal can be stopped
       *                                 by returning BaseProperty.BREAK_TRAVERSAL
       * @return {string|undefined} Returns BaseProperty.BREAK_TRAVERSAL if the traversal has been interrupted,
       *                            otherwise undefined
       */
      BaseProperty.prototype.traverseDown = function (in_callback) {
        return this._traverse(in_callback, '');
      };
      /**
       * @type {string} Constant to stop the traversal in traverseUp and traverseDown functions
       */
      BaseProperty.BREAK_TRAVERSAL = BREAK_TRAVERSAL;
      /**
       * Returns all children which are dirty (this only returns direct children, it does not travers recursively)
       *
       * @param {LYNX.Property.BaseProperty.MODIFIED_STATE_FLAGS} in_flags - Which types of dirtiness are we looking for?
       *                                                                     If none is given, all types are regarded as
       *                                                                     dirty
       * @return {Array.<String>} The list of keys identifying the dirty children
       * @private
       */
      BaseProperty.prototype._getDirtyChildren = function (in_flags) {
        var flags = in_flags === undefined ? ~MODIFIED_STATE_FLAGS.CLEAN : in_flags;
        var rtn = [];
        var childKeys = _.keys(this._children);
        for (var i = 0; i < childKeys.length; i++) {
          if ((this._children[childKeys[i]]._dirty & flags) !== 0) {
            rtn.push(childKeys[i]);
          }
        }
        return rtn;
      };
      /**
       * Returns the root of the property hierarchy
       * TODO: Which root should this return? CheckoutView or repository? Should we use the caching?
       * @return {LYNX.Property.NodeProperty} The root property
       */
      BaseProperty.prototype.getRoot = function () {
        if (this._rootRef) {
          return this._rootRef;
        } else {
          var root;
          if (!this._parent) {
            root = this;
          } else {
            root = this._parent.getRoot();
          }
          this._rootRef = root;
          return root;
        }
      };
      /**
       * Traverses all children in the child hierarchy
       * TODO: How should this behave for collections?
       *
       * @param {function} in_callback             - Callback to invoke for every child
       * @param {string} in_pathFromTraversalStart - Path from the root of the traversal to this node
       * @return {string|undefined} Returns BaseProperty.BREAK_TRAVERSAL if the traversal has been interrupted,
       *                            otherwise undefined
       * @private
       */
      BaseProperty.prototype._traverse = function (in_callback, in_pathFromTraversalStart) {
        if (in_pathFromTraversalStart) {
          in_pathFromTraversalStart += PROPERTY_PATH_DELIMITER;
        }
        var childKeys, child, childPath, result, i;
        childKeys = Object.keys(this._children);
        for (i = 0; i < childKeys.length; i++) {
          child = this._children[childKeys[i]];
          childPath = in_pathFromTraversalStart + PathHelper.quotePathSegmentIfNeeded(child.getId());
          result = in_callback(child, childPath);
          if (result !== BREAK_TRAVERSAL) {
            result = child._traverse(in_callback, childPath);
            if (result !== BREAK_TRAVERSAL) {
              continue;
            }
          }
          return BREAK_TRAVERSAL;
        }
        return undefined;
      };
      /**
       * Traverses all static properties (properties declared in the template and not added dynamically) in the
       * hierarchy below this node
       *
       * @param {function} in_callback               - Callback to invoke for every property
       * @param {string?}  in_pathFromTraversalStart - Path from the root of the traversal to this node
       * @protected
       */
      BaseProperty.prototype._traverseStaticProperties = function (in_callback, in_pathFromTraversalStart) {
        in_pathFromTraversalStart = in_pathFromTraversalStart || '';
        var propertyKeys = _.keys(this._children);
        for (var i = 0; i < propertyKeys.length; i++) {
          var property = this._children[propertyKeys[i]];
          var childPath = in_pathFromTraversalStart + (in_pathFromTraversalStart.length !== 0 ? PROPERTY_PATH_DELIMITER : '') + PathHelper.quotePathSegmentIfNeeded(property.getId());
          // We only recursively traverse BaseProperties, since these are used to define the hierarchy within
          // one template
          if (property.getTypeid() === 'BaseProperty') {
            property._traverseStaticProperties(in_callback, childPath);
          }
          in_callback(property, childPath);
        }
      };
      /**
       * Sets the property to the state in the given normalized changeset
       *
       * @param {LYNX.Property.SerializedChangeSet} in_serializedObj - The serialized changeset to apply to this node. This
       *     has to be an normalized change-set (only containing insertions and property assignments. Deletes and Modify
       *     must not appear)
       * @return {LYNX.Property.SerializedChangeSet} ChangeSet with the changes that actually were performed during the
       *     deserialization
       */
      BaseProperty.prototype.deserialize = function (in_serializedObj) {
        this._checkIsNotReadOnly();
        return this._deserialize(in_serializedObj, true);
      };
      /**
       * Sets the property to the state in the given normalized changeset
       *
       * @param {LYNX.Property.SerializedChangeSet} in_serializedObj - The serialized changeset to apply to this node. This
       *     has to be an normalized change-set (only containing additions and property assignments. Deletes and Modify
       *     must not appear)
       * @param {boolean} [in_reportToView = true] - By default, the dirtying will always be reported to the checkout view
       *                                             and trigger a modified event there. When batching updates, this
       *                                             can be prevented via this flag.
       * @return {LYNX.Property.SerializedChangeSet} ChangeSet with the changes that actually were performed during the
       *     deserialization
       */
      BaseProperty.prototype._deserialize = function (in_serializedObj, in_reportToView) {
        var changeSet = {};
        // Traverse all properties of this template
        this._traverseStaticProperties(function (in_node, in_pathFromTraversalStart) {
          // We do not deserialize base properties, since the traverseStatic function
          // already traverses recursively
          if (in_node.getTypeid() === 'BaseProperty') {
            return;
          }
          var typeid = in_node._getTypeidForChangeSet();
          // Get the ChangeSet
          // If there is a ChangeSet in the serialized object, we use that as the
          // target ChangeSet, otherwise we use an empty ChangeSet (since properties with
          // empty Sub-ChangeSets are removed from the parent ChangeSet, we have to
          // explicitly use an empty ChangeSet for those)
          var propertyChangeSet = {};
          if (in_serializedObj[typeid] !== undefined && in_serializedObj[typeid][in_pathFromTraversalStart] !== undefined) {
            propertyChangeSet = in_serializedObj[typeid][in_pathFromTraversalStart];
          }
          // Deserialize the ChangeSet into the property
          var changes = in_node._deserialize(propertyChangeSet, false);
          // And track the performed modification in the result
          if (!Property.ChangeSet.isEmptyChangeSet(changes)) {
            changeSet[typeid] = changeSet[typeid] || {};
            changeSet[typeid][in_pathFromTraversalStart] = changes;
          }
        });
        // Finally report the dirtiness to the view (we postponed this above)
        if (in_reportToView) {
          this._reportDirtinessToView();
        }
        return changeSet;
      };
      /**
       * Serialize the property
       *
       * @param {boolean} in_dirtyOnly -
       *     Only include dirty entries in the serialization
       * @param {boolean} in_includeRootTypeid -
       *     Include the typeid of the root of the hierarchy
       * @param {LYNX.Property.BaseProperty.MODIFIED_STATE_FLAGS} [in_dirtinessType] -
       *     The type of dirtiness to use when reporting dirty changes. By default this is
       *     PENDING_CHANGE
       * @param {boolean} [in_includeReferencedRepositories=false] - If this is set to true, the serialize
       *     function will descend into referenced repositories. WARNING: if there are loops in the references
       *     this can result in an infinite loop
       *
       * @return {Object} The serialized representation of this property
       * @private
       */
      BaseProperty.prototype._serialize = function (in_dirtyOnly, in_includeRootTypeid, in_dirtinessType, in_includeReferencedRepositories) {
        var serializedChildren = {};
        var childrenType;
        in_dirtyOnly = in_dirtyOnly || false;
        in_dirtinessType = in_dirtinessType === undefined ? MODIFIED_STATE_FLAGS.PENDING_CHANGE : in_dirtinessType;
        this._traverseStaticProperties(function (in_node, in_pathFromTraversalStart) {
          if (in_dirtyOnly && !in_node._isDirty(in_dirtinessType)) {
            return;
          }
          childrenType = in_node._getTypeidForChangeSet();
          if (childrenType !== 'BaseProperty') {
            // we don't want to keep BaseProperties
            // as they mostly behave as 'paths' to
            // a ValueProperty.
            var serialized = in_node._serialize(in_dirtyOnly, false, in_dirtinessType, in_includeReferencedRepositories);
            // Add the root typeid if requested
            if (!Property.ChangeSet.isEmptyChangeSet(serialized)) {
              if (!serializedChildren[childrenType]) {
                serializedChildren[childrenType] = {};
              }
              serializedChildren[childrenType][in_pathFromTraversalStart] = serialized;
            }
          }
        });
        if (in_includeRootTypeid) {
          serializedChildren['typeid'] = this._getTypeidForChangeSet();
        }
        return serializedChildren;
      };
      /**
       * Serialize the property
       *
       * @param {Object} in_options -
       *     Options for the serialization
       * @param {boolean} [in_options.dirtyOnly=false] -
       *     Only include dirty entries in the serialization
       * @param {boolean} [in_options.includeRootTypeid=false] -
       *     Include the typeid of the root of the hierarchy
       * @param {LYNX.Property.BaseProperty.MODIFIED_STATE_FLAGS} [in_options.dirtinessType=PENDING_CHANGE] -
       *     The type of dirtiness to use when reporting dirty changes.
       * @param {boolean} [in_options.includeReferencedRepositories=false] - If this is set to true, the serialize
       *     function will descend into referenced repositories. WARNING: if there are loops in the references
       *     this can result in an infinite loop
       *
       * @return {Object} The serialized representation of this property
       */
      BaseProperty.prototype.serialize = function (in_options) {
        var opts = {
          'dirtyOnly': false,
          'includeRootTypeid': false,
          'dirtinessType': MODIFIED_STATE_FLAGS.PENDING_CHANGE,
          'includeReferencedRepositories': false
        };
        if (in_options !== undefined) {
          if (typeof in_options !== 'object')
            throw new Error('Argument of serialize() should be an object.');
          Object.assign(opts, in_options);
        }
        return this._serialize(opts.dirtyOnly, opts.includeRootTypeid, opts.dirtinessType, opts.includeReferencedRepositories);
      };
      /**
       * Given an object that mirrors a PSet Template, assign the properties
       * eg.
       * <pre>
       * Templates = {
       *   properties: [
       *     { id: 'foo', typeid: 'String' },
       *     { id: 'bar', properties: [{id: 'baz', typeid: 'Uint32'}] }
       *   ]
       * }
       * </pre>
       *
       * TODO: Warning, the semantics of this function are undefined and will change in the future
       * It's also partially unimplemented for many cases (see LYNXDEV-835)
       *
       * You would update the values like
       * `baseProperty._setValues({foo: 'hello', bar: {baz: 1}});`
       * @param {object} in_properties The properties you would like to assign
       * @private
       */
      BaseProperty.prototype._setValues = function (in_properties) {
        var that = this;
        _.each(in_properties, function (propertyValue, propertyKey) {
          var property = that.resolvePath(propertyKey);
          if (property instanceof Property.ValueProperty || property instanceof Property.StringProperty) {
            property.set(propertyValue);
          } else if (property instanceof BaseProperty && _.isObject(propertyValue)) {
            property._setValues(propertyValue);
          } else if (property instanceof BaseProperty) {
            console.warn('[BaseProperty] The property: ' + propertyKey + ' represents a nested PropertySet');
          } else if (property === undefined) {
            console.warn('[BaseProperty] could not find property: ' + propertyKey);
          }
        });
      };
      /**
       * Indicate that all static members have been added to the property
       *
       * This function is invoked by the PropertyFactory once all static members have been added to the template
       * @protected
       */
      BaseProperty.prototype._signalAllStaticMembersHaveBeenAdded = function () {
        // All static members have been added, so we enabled the cache here
        this._structChange = true;
      };
      /**
       * Tests whether this property may be modified
       */
      BaseProperty.prototype._checkIsNotReadOnly = function () {
        var root = this.getRoot();
        if (root && root._getCheckedOutRepositoryInfo) {
          var repositoryInfo = root._getCheckedOutRepositoryInfo();
          if (repositoryInfo && repositoryInfo._isReadOnly()) {
            throw new Error(MSG.MODIFICATION_OF_REFERENCED_PROPERTY);
          }
        }
      };
      Object.defineProperty(BaseProperty.prototype, '_properties', {
        get: function () {
          if (this._structChange) {
            this._flatten();
          }
          return this._flattenedRepresentation;
        }
      });
      exportModule('LYNX.Property.BaseProperty', BaseProperty);
    }());
    (function () {
      /**
       * Instantiates a ValidationResultBuilder
       * @param {string} in_typeid A template typeid.
       * @constructor
       */
      var ValidationResultBuilder = function (in_typeid) {
        this._result = {
          isValid: true,
          errors: [],
          warnings: [],
          resolvedTypes: [],
          unresolvedTypes: []
        };
        if (in_typeid) {
          this._result.typeid = in_typeid;
        }
        /**
         * Fetches the validation result. Example: {
         *   isValid: false,
         *   errors: ['Something went wrong. Validation failed.'],
         *   warnings: ['A non-fatal warning'],
         *   typeid: 'SomeNamespace:PointID-1.0.0'
         * }
         */
        Object.defineProperty(this, 'result', {
          get: function () {
            return this._result;
          }
        });
      };
      /**
       * Add a validation error.
       * @param {Error} in_error An Error instance.
       */
      ValidationResultBuilder.prototype.addError = function (in_error) {
        this._result.isValid = false;
        this._result.errors.push(in_error);
      };
      /**
       * Add a validation warning.
       * @param {string} in_msg A warning description.
       */
      ValidationResultBuilder.prototype.addWarning = function (in_msg) {
        this._result.warnings.push(in_msg);
      };
      /**
       * Fecthes the boolean validation result.
       * @return {boolean} True if validation produced no error, false otherwise. Warnings don't affect
       *   this value.
       */
      ValidationResultBuilder.prototype.isValid = function () {
        return this._result.isValid;
      };
      exportModule('LYNX.Property.ValidationResultBuilder', ValidationResultBuilder);
    }());
    (function () {
      var ajv = include('LYNX.Utils.ThirdParty.ajv');
      var deepCopy = include('LYNX.Utils.deepCopy');
      var semver = include('LYNX.Utils.ThirdParty.semver');
      var traverse = include('LYNX.Utils.ThirdParty.traverse');
      var TemplateSchema = include('LYNX.TemplateSchema');
      var TypeIdHelper = include('LYNX.Property.TypeIdHelper');
      var ValidationResultBuilder = include('LYNX.Property.ValidationResultBuilder');
      var ajvFactory = ajv({
        allErrors: true,
        v5: true
      });
      var _syntaxValidator = ajvFactory.compile(TemplateSchema);
      var _ = include('underscore');
      /**
       * Message strings. String names that end in '_1' should prefix additional contextual info.
       */
      var MESSAGES = {
        CHANGE_LEVEL_TOO_LOW_1: 'Template mutation requires a higher version change level: ',
        INVALID_VERSION_1: 'Invalid template version in \'typeid\' attribute: ',
        MISSING_TYPE_ID: 'Template is missing the mandatory \'typeid\' attribute.',
        MISSING_VERSION: 'Missing template version in \'typeid\' attribute.',
        MODIFIED_TEMPLATE_1: 'Template has changed at path: ',
        MODIFIED_TEMPLATE_SAME_VERSION_1: 'Template has changed but its version was not increased. Path: ',
        NO_TEMPLATE: 'Template cannot be null or undefined.',
        VERSION_REGRESSION_1: 'New template version is older than the previously registered version: '
      };
      /**
       * A weighted enumeration of semver change types. Higher values are more important.
       * PATCH: Annotation and comment changes.
       * MINOR: Added properties.
       * MAJOR: Everything else, including deleting properties.
       */
      var CHANGE_LEVEL = {
        'patch': 0,
        // '1.0.0' -> '1.0.1'
        'minor': 1,
        // '1.0.0' -> '1.1.0'
        'major': 2,
        // '1.5.2' -> '2.0.0'
        'prerelease': 0,
        // '1.0.0-alpha.1' -> '1.0.0'
        'prepatch': 0,
        // '1.0.0-alpha.1' -> '1.0.1'
        'preminor': 1,
        // '1.0.0-alpha.1' -> '1.1.0'
        'premajor': 2  // '1.0.0-alpha.1' -> '2.0.0'
      };
      var _extractTypeid = function (typeidOrReference) {
        // Take Reference<strong-type-id> and return strong-type-id
        var reference = 'Reference<';
        var result = typeidOrReference || '';
        var isReference = result.indexOf(reference) === 0;
        if (isReference) {
          result = typeidOrReference.substring(reference.length, typeidOrReference.length - 1);
        }
        return result;
      };
      /**
       * Given a typeid string, fetches the semver 'x.y.z' version string.
       * @param {string} in_typeid A PropertySet typeid. For example: 'TeamLeoValidation2:ColorID-1.0.0'.
       * @return {string|null} The semver 'x.y.z' version string, or null if in_typeid is not a valid
       *   PropertySet typeid.
       * @private
       * @this TemplateValidator
       */
      var _getSemverFromTypeId = function (in_typeid) {
        var semverRegex = /.*-(.*)$/g;
        var match = semverRegex.exec(in_typeid);
        return match ? match[1] : null;
      };
      /**
       * Fetches the type name of a javascript entity.
       * @param {*} in_obj A javascript entity.
       * @return {string} The type name for in_obj.
       * @private
       * @this TemplateValidator
       */
      var _getType = function (in_obj) {
        return Object.prototype.toString.call(in_obj).slice(8, -1);
      };
      /**
       * An object deep compare with special handling for pset property arrays.
       * pset property arrays are allowed to be out of order as long as elements can be matched with
       * their id.
       * @param {*} in_source The source entity to test for deep equality.
       * @param {*} in_target The target entity to test for deep equality.
       * @return {Object} {isEqual: false, path: 'foo.properties[1].x'}
       *   isEqual: true if in_source and in_target property sets are equal, even if the individual
       *     property arrays differ but contain the same out of order elements.
       *   path: path to the property that is not equal.
       * @private
       * @this TemplateValidator
       */
      var _psetDeepEquals = function (in_source, in_target) {
        var idPath = [];
        if (in_source && in_source.typeid) {
          idPath.push('<' + in_source.typeid + '>');
        }
        /**
         * Create the _psetDeepEquals result.
         * @param {boolean} isEqual Whether or not a PropertySet result is being constructed for
         *   PropertySets that are deeply equal.
         * @return {{isEqual: boolean, path: string}} An object that indicates whether or not the source
         *   and target PropertySets are deeply equal. If they're not, it also contains a path to the
         *   property that is not equal.
         * @private
         */
        var _getPSetDeepEqualsResult = function (isEqual) {
          return {
            isEqual: isEqual,
            path: isEqual ? undefined : idPath.join('')
          };
        };
        /**
         * Performs a recursive, depth first deep equal test against two PropertySets.
         * @param {*} source The source entity to test for deep equality.
         * @param {*} target The target entity to test for deep equality.
         * @param {?string} id The current path element being compared.
         * @return {Object} The result of _getPSetDeepEqualsResult
         * @private
         * @this TemplateValidator
         */
        var _depthFirstDeepEquals = function (source, target, id) {
          var result = _getPSetDeepEqualsResult(true);
          if (id) {
            if (typeof id === 'number') {
              idPath.push('[' + id + ']');
            } else {
              idPath.push('.' + id);
            }
          }
          if (_.isArray(source)) {
            if (!_.isArray(target)) {
              return _getPSetDeepEqualsResult(false);
            }
            if (source.length !== target.length) {
              return _getPSetDeepEqualsResult(false);
            }
            if (source.length === 0) {
              return _getPSetDeepEqualsResult(true);
            }
            // See if we're comparing arrays of objects (like properties) or simple arrays of strings
            // like inheritance lists.
            var isPropertyArray = _.every(source, function (entry) {
              return _.isObject(entry) && !_.isUndefined(entry.id);
            });
            if (isPropertyArray) {
              var targetMap = {};
              _.each(target, function (element) {
                targetMap[element.id] = element;
              });
              for (var i = 0; i < source.length && result.isEqual; i++) {
                var sourceId = source[i].id;
                result = _depthFirstDeepEquals.call(this, source[i], targetMap[sourceId], sourceId);
                idPath.pop();
              }
            } else {
              // Element order matters
              for (var i = 0; i < source.length && result.isEqual; i++) {
                result = _depthFirstDeepEquals.call(this, source[i], target[i], i);
                idPath.pop();
              }
            }
          } else if (_.isObject(source)) {
            if (!_.isObject(target)) {
              return _getPSetDeepEqualsResult(false);
            }
            var keysSource = _.keys(source);
            var keysTarget = _.keys(target);
            if (keysSource.length !== keysTarget.length) {
              return _getPSetDeepEqualsResult(false);
            }
            for (var i = 0; i < keysSource.length && result.isEqual; i++) {
              var keyName = keysSource[i];
              var id = keyName === 'properties' ? undefined : keyName;
              result = _depthFirstDeepEquals.call(this, source[keyName], target[keyName], id);
              if (id) {
                idPath.pop();
              }
            }
          } else {
            result = _getPSetDeepEqualsResult(_getType.call(this, source) === _getType.call(this, target) && source === target);
          }
          return result;
        };
        return _depthFirstDeepEquals.call(this, in_source, in_target);
      };
      /**
       * Fetches the non semver part of a typeid string.
       * @param {string} in_typeid A PropertySet typeid. For example: 'TeamLeoValidation2:ColorID-1.0.0'.
       * @return {string|null} The typeid, without a semver.
       * @private
       * @this TemplateValidator
       */
      var _stripSemverFromTypeId = function (in_typeid) {
        var semverRegex = /(.*)-.*$/g;
        var match = semverRegex.exec(in_typeid);
        return match ? match[1] : null;
      };
      var _unresolvedTypes = function (in_template) {
        var first = true;
        var that = this;
        var accSet = traverse(in_template).reduce(function (acc, x) {
          if (first) {
            acc = {};
            first = false;
          }
          if (_.isObject(x) && _.has(x, 'typeid')) {
            var extractedTypeid = _extractTypeid.call(that, x.typeid);
            if (!TypeIdHelper.isPrimitiveType(extractedTypeid))
              acc[extractedTypeid] = '';
          }
          return acc;
        });
        return _.keys(accSet);
      };
      /**
       * Performs basic template validation.
       * @param {Object} in_template The template object to validate.
       * @private
       * @this TemplateValidator
       */
      var _validateBasic = function (in_template) {
        if (!in_template) {
          this._resultBuilder.addError(new Error(MESSAGES.NO_TEMPLATE));
        } else if (!in_template.typeid) {
          this._resultBuilder.addError(new Error(MESSAGES.MISSING_TYPE_ID));
        }
      };
      /**
       * Validations performed when the version increases between consecutive LYNX templates.
       * For example: 1.1.3 -> 2.0.0
       * This function checks the change level (PATCH, MINOR, MAJOR) and analyses the template content
       * to emit warnings if the change level should be higher, given the content that changed.
       * This function assumes that: in_versionPrevious < in_version
       * @param {Object} in_template The latest template object.
       * @param {Object} in_templatePrevious The previous template object.
       * @param {string} in_version The latest template version. Ex.: '2.0.0'.
       * @param {string} in_versionPrevious The previous template version. Ex.: '1.1.3'.
       * @private
       * @this TemplateValidator
       */
      var _validatePositiveIncrement = function (in_template, in_templatePrevious, in_version, in_versionPrevious) {
        console.assert(semver.gt(in_version, in_versionPrevious), 'LYNX.Property.TemplateValidator._validatePositiveIncrement called on non incremental ' + 'template versions');
        var versionDiff = semver.diff(in_version, in_versionPrevious);
        if (CHANGE_LEVEL[versionDiff] >= CHANGE_LEVEL['major']) {
          // No need to warn about change levels since they're already declared to be major.
          return;
        }
        if (semver.major(in_version) === 0) {
          // Unstable version doesn't produce any warning.
          return;
        }
        var idPath = ['<' + in_template.typeid + '>'];
        var _depthFirstCompare = function (id, sourceObj, targetObj) {
          if (id === 'annotation') {
            // Here, we know that the version has increased (patch, prepatch or prerelease), so
            // there's no need to check inside comments for changes.
            return;
          }
          if (id) {
            idPath.push(id);
          }
          if (_.isUndefined(sourceObj) !== _.isUndefined(targetObj)) {
            var minimumLevel;
            var mutation;
            if (_.isUndefined(targetObj)) {
              // An element has been deleted.
              minimumLevel = 'major';
              mutation = 'delete';
            } else {
              // An element has been added
              minimumLevel = 'minor';
              mutation = 'add';
            }
            if (CHANGE_LEVEL[versionDiff] < CHANGE_LEVEL[minimumLevel]) {
              // Violates rule 6 (warning).
              this._resultBuilder.addWarning(MESSAGES.CHANGE_LEVEL_TOO_LOW_1 + JSON.stringify({
                mutation: mutation,
                id: idPath.join('.'),
                level: {
                  expected: minimumLevel,
                  actual: versionDiff
                },
                version: {
                  current: in_version,
                  previous: in_versionPrevious
                }
              }));
            }
          } else {
            var sourceObjType = _getType.call(this, sourceObj);
            var targetObjType = _getType.call(this, targetObj);
            if (sourceObjType !== targetObjType) {
              this._resultBuilder.addWarning(MESSAGES.CHANGE_LEVEL_TOO_LOW_1 + JSON.stringify({
                mutation: 'change',
                id: idPath.join('.'),
                type: {
                  current: targetObjType,
                  previous: sourceObjType
                },
                level: {
                  expected: 'major',
                  actual: versionDiff
                },
                version: {
                  current: in_version,
                  previous: in_versionPrevious
                }
              }));
            }
            if (_.isArray(sourceObj)) {
              var targetMap = {};
              _.each(targetObj, function (element) {
                targetMap[element.id] = element;
              });
              for (var i = 0; i < sourceObj.length; i++) {
                var element = sourceObj[i];
                _depthFirstCompare.call(this, element.id, element, targetMap[element.id]);
                delete targetMap[element.id];
              }
              if (!_.isEmpty(targetMap)) {
                // Added array element.
                var minimumLevel = 'minor';
                if (CHANGE_LEVEL[versionDiff] < CHANGE_LEVEL[minimumLevel]) {
                  // Violates rule 5 (warning)
                  idPath.push(_.keys(targetMap)[0]);
                  this._resultBuilder.addWarning(MESSAGES.CHANGE_LEVEL_TOO_LOW_1 + JSON.stringify({
                    mutation: 'add',
                    id: idPath.join('.'),
                    level: {
                      expected: minimumLevel,
                      actual: versionDiff
                    },
                    version: {
                      current: in_version,
                      previous: in_versionPrevious
                    }
                  }));
                  idPath.pop();
                }
              }
            } else if (_.isObject(sourceObj)) {
              var keysSource = _.keys(sourceObj);
              var targetMap = {};
              _.mapObject(targetObj, function (val, key) {
                targetMap[key] = val;
              });
              for (var i = 0; i < keysSource.length; i++) {
                var valueSource = sourceObj[keysSource[i]];
                var valueTarget = targetObj[keysSource[i]];
                _depthFirstCompare.call(this, keysSource[i] === 'properties' ? undefined : keysSource[i], valueSource, valueTarget);
                delete targetMap[keysSource[i]];
              }
              var remainingKeys = Object.keys(targetMap);
              if (!_.isEmpty(remainingKeys)) {
                // Added new keys to the target. This is a MINOR change, unless they new key is a
                // comment, in which case this is a PATCH level change.
                var minimumLevel = remainingKeys.length === 1 && remainingKeys[0] === 'annotation' ? 'patch' : 'minor';
                if (CHANGE_LEVEL[versionDiff] < CHANGE_LEVEL[minimumLevel]) {
                  // Violates rule 5 (warning)
                  idPath.push(remainingKeys[0]);
                  this._resultBuilder.addWarning(MESSAGES.CHANGE_LEVEL_TOO_LOW_1 + JSON.stringify({
                    mutation: 'add',
                    id: idPath.join('.'),
                    level: {
                      expected: minimumLevel,
                      actual: versionDiff
                    },
                    version: {
                      current: in_version,
                      previous: in_versionPrevious
                    }
                  }));
                  idPath.pop();
                }
              }
            } else {
              if (idPath.length === 2 && id === 'typeid') {
                // This is the root property typeid. Ignore the version component.
                sourceObj = _stripSemverFromTypeId.call(this, valueSource);
                targetObj = _stripSemverFromTypeId.call(this, valueTarget);
              }
              if (sourceObj !== targetObj) {
                var minimumLevel = id === 'value' ? 'minor' : 'major';
                if (CHANGE_LEVEL[versionDiff] < CHANGE_LEVEL[minimumLevel]) {
                  this._resultBuilder.addWarning(MESSAGES.CHANGE_LEVEL_TOO_LOW_1 + JSON.stringify({
                    mutation: 'change',
                    id: idPath.join('.'),
                    level: {
                      expected: minimumLevel,
                      actual: versionDiff
                    },
                    value: {
                      current: targetObj,
                      previous: sourceObj
                    },
                    version: {
                      current: in_version,
                      previous: in_versionPrevious
                    }
                  }));
                }
              }
            }
          }
          if (id) {
            idPath.pop();
          }
        };
        _depthFirstCompare.call(this, in_templatePrevious.id, in_templatePrevious, in_template);
      };
      /**
       * Validations performed when the version between consecutive LYNX templates doesn't change.
       * For example: 1.1.3 -> 1.1.3.
       * Templates whose version didn't change should have identical content.
       * @param {Object} in_template The latest template object.
       * @param {Object} in_templatePrevious The previous template object.
       * @private
       * @this TemplateValidator
       */
      var _validateSameVersion = function (in_template, in_templatePrevious) {
        var result = _psetDeepEquals.call(this, in_templatePrevious, in_template);
        if (!result.isEqual) {
          // Violates rule 3a.
          this._resultBuilder.addError(new Error(MESSAGES.MODIFIED_TEMPLATE_SAME_VERSION_1 + result.path));
        }
      };
      /**
       * Validate a template
       * Check that the template is syntactically correct as well as semantically correct.
       * @param {object} in_template The template to check against
       * Return {object|undefined} map of key-value pairs
       *  where the path of the invalid property is the key and the value is the error message
       *  i.e.
       *  <pre>
       *    {
       *      'isValid': true or false,
       *      'typeid': 'The typeid of the object being parsed',
       *      'unresolvedTypes': [ 'An array', 'of strong typeids', 'that were found',
       *        'in the document', 'but not resolved from the local cache' ],
       *      'resolvedTypes': [ 'Array of', 'strong types resolved', 'during template parsing'],
       *      'errors': [ 'Array of', 'objects describing', 'syntax errors in the template' ]
       *      ...
       *    }
       *  </pre>
       */
      var _validateSemanticAndSyntax = function (in_template) {
        _validateSyntax.call(this, in_template);  // TODO: _validateSemantic
      };
      /**
       * Validates that the semver part of a template's typeid is valid.
       * @param {Object} in_template The template object to validate.
       * @return {string} The semver string. For example: '1.0.0'.
       * @private
       * @this TemplateValidator
       */
      var _validateSemverFormat = function (in_template) {
        var templateVersion = _getSemverFromTypeId.call(this, in_template.typeid);
        if (!templateVersion) {
          this._resultBuilder.addError(new Error(MESSAGES.MISSING_VERSION));
        } else if (semver.valid(templateVersion) !== templateVersion) {
          this._resultBuilder.addError(new Error(MESSAGES.INVALID_VERSION_1 + templateVersion));
        }
        return templateVersion;
      };
      /**
       * Skip semver validation. Verify that the content is the same for both templates, while ignoring
       * the root 'typeid' property.
       * @param {Object} in_template The latest template object.
       * @param {Object} in_templatePrevious The previous template object.
       * @private
       * @this TemplateValidator
       */
      var _validateSkipSemver = function (in_template, in_templatePrevious) {
        // Skipping the semver validation. Ignore the root typeid field.
        var result = _psetDeepEquals.call(this, in_template, in_templatePrevious);
        if (!result.isEqual) {
          // Violates rule 3a.
          this._resultBuilder.addError(new Error(MESSAGES.MODIFIED_TEMPLATE_1 + result.path));
        }
      };
      /**
       * Validate just the syntax of a template
       * Check that the template is well-formed, according to the schema.
       * @param {object} in_template The template to check against
       */
      var _validateSyntax = function (in_template) {
        var that = this;
        var result = this._resultBuilder.result;
        result.isValid = _syntaxValidator(in_template);
        if (!result.isValid) {
          console.assert(!_.isEmpty(_syntaxValidator.errors), 'ajv validation failed but produced no error');
        }
        if (_syntaxValidator.errors) {
          _.each(_syntaxValidator.errors, function (error) {
            // Deep-copy for thread-safety.
            that._resultBuilder.addError(deepCopy(error));
          });
        }
        result.unresolvedTypes = _unresolvedTypes.call(this, in_template);
      };
      /**
       * Instantiates a new TemplateValidator.
       * @param {?Object} in_params Input parameters.
       * @param {?boolean=} in_params.skipSemver When set to true, {@link #validate} only checks the
       *   supplied templates' content and fails the validation if they're not identical. Defaults to
       *   false.
       * @constructor
       */
      var TemplateValidator = function (in_params) {
        this._skipSemver = in_params ? !!in_params.skipSemver : false;
      };
      TemplateValidator._MESSAGES = MESSAGES;
      TemplateValidator.Utils = {};
      TemplateValidator.Utils.psetDeepEquals = function (in_source, in_target) {
        return _psetDeepEquals.call(this, in_source, in_target).isEqual;
      };
      /**
       * Validates that all templates conform to the following mandatory rules:
       * 1. Must have a typeid attribute.
       * 2. typeid must end in a valid semver string.
       * 3. When both in_template (B) and in_templatePrevious (A) are supplied:
       *    3a. Semver is identical only if content is identical.
       *    3b. B's semver >= A's semver
       * Additionally, the following soft rules will produce warnings when violated:
       * 4. PATCH revision should be increased when _only_ the template description changes.
       * 5. Adding one or more template attributes is a MINOR change.
       * 6. Removing one or more template attributes is a MAJOR change.
       * @param {Object} in_template The latest template version, as a JSON object.
       * @param {?Object} in_templatePrevious The previous template version, as a JSON object. Optional.
       * @return {Object} The validation results. Example: {
       *   isValid: false,
       *   errors: ['Something went wrong. Validation failed.'],
       *   warnings: ['A non-fatal warning'],
       *   typeid: 'SomeNamespace:PointID-1.0.0'
       * }
       * It's possible for 'isValid' to be true while 'warnings' contains one or more messages.
       */
      TemplateValidator.prototype.validate = function (in_template, in_templatePrevious) {
        this._resultBuilder = new ValidationResultBuilder(in_template ? in_template.typeid : '');
        _validateBasic.call(this, in_template);
        if (in_templatePrevious) {
          _validateBasic.call(this, in_templatePrevious);
        }
        // Basic validation (such as input params) must pass before the real validation can begin.
        if (!this._resultBuilder.isValid()) {
          return this._resultBuilder.result;
        }
        _validateSemanticAndSyntax.call(this, in_template);
        if (!this._resultBuilder.isValid()) {
          return this._resultBuilder.result;
        }
        if (in_templatePrevious) {
          _validateSemanticAndSyntax.call(this, in_templatePrevious);
          if (!this._resultBuilder.isValid()) {
            // Here the previous template is not valid. Make sure the typeid in the returned info is
            // the root of the template that failed validation.
            this._resultBuilder.result.typeid = in_templatePrevious.typeid;
            return this._resultBuilder.result;
          }
        }
        if (this._skipSemver && in_templatePrevious) {
          _validateSkipSemver.call(this, in_template, in_templatePrevious);
          return this._resultBuilder.result;
        }
        // semver format validation
        var version = _validateSemverFormat.call(this, in_template);
        var versionPrevious = in_templatePrevious ? _validateSemverFormat.call(this, in_templatePrevious) : null;
        // semver format validation must pass.
        if (!this._resultBuilder.isValid()) {
          return this._resultBuilder.result;
        }
        if (in_templatePrevious) {
          // Validate that the semver change is valid.
          switch (semver.compare(version, versionPrevious)) {
          case 0:
            _validateSameVersion.call(this, in_template, in_templatePrevious);
            break;
          case 1:
            // newVersion is greater
            _validatePositiveIncrement.call(this, in_template, in_templatePrevious, version, versionPrevious);
            break;
          default:
          case -1:
            // previousVersion is greater. Violates rule 3b.
            this._resultBuilder.addError(new Error(MESSAGES.VERSION_REGRESSION_1 + JSON.stringify({
              current: version,
              previous: versionPrevious
            })));
            break;
          }
        }
        return this._resultBuilder.result;
      };
      exportModule('LYNX.Property.TemplateValidator', TemplateValidator);
    }());
    (function () {
      var _ = include('underscore');
      var deepCopy = include('LYNX.Utils.deepCopy');
      var joinPaths = include('LYNX.Utils.Strings.joinPaths');
      var TypeIdHelper = include('LYNX.Property.TypeIdHelper');
      var PROPERTY_PATH_DELIMITER = include('LYNX.Property._constants.PROPERTY_PATH_DELIMITER');
      var MSG = include('LYNX.Property._constants.MSG');
      var TemplateValidator = include('LYNX.Property.TemplateValidator');
      var Property = include('LYNX.Property', ['Utils']);
      var ArrayOperations = include('LYNX.Property.ChangeSetOperations.ArrayOperations', ['ArrayChangeSetIterator']);
      // we need to include the ArrayOperations since its side effect is extending the ChangeSetOperations
      include('LYNX.Property.ChangeSetOperations.ArrayOperations', ['ChangeSetArrayFunctions']);
      include('LYNX.Property.ChangeSetOperations.IndexedCollectionOperations', ['ChangeSetIndexedCollectionFunctions']);
      /**
       * @typedef {Object} LYNX.Property.SerializedChangeSet
       * The plain serialization data structure used to encode a ChangeSet.
       */
      /**
       * The ChangeSet object encapsulates a serialiezd changeset and provides functionality to merge and swap change sets
       *
       * @param {LYNX.Property.SerializedChangeSet|string|LYNX.Property.ChangeSet} [in_changes] - The serialized changes
       *                                                                  to store in this change set
       *                                                                  If a string is supplied, we assume it to be a
       *                                                                  serialized JSON representation of the change set.
       *                                                                  If none is supplied, an empty changeset will be
       *                                                                  created
       * @constructor
       * @alias LYNX.Property.ChangeSet
       */
      var ChangeSet = function (in_changes) {
        if (!in_changes) {
          this._changes = {};
        } else if (_.isString(in_changes)) {
          this._changes = JSON.parse(in_changes);
        } else if (in_changes instanceof ChangeSet) {
          this._changes = deepCopy(in_changes._changes);
        } else {
          this._changes = in_changes;
        }
      };
      /**
       * @enum {number}
       * Different types of conflicts that can occur during a rebase operation
       */
      ChangeSet.ConflictType = {
        /** We had two incompatible ChangeSets, they probably were with respect to different base commits */
        INVALID_CHANGESET_BASE: 1,
        /** A value was changed in both ChangeSets */
        COLLIDING_SET: 2,
        /** A deleted child node was modified */
        ENTRY_MODIFIED_AFTER_REMOVE: 3,
        /** A child was modified after it had been removed and added.
         *
         * The modification can no longer be applied, since the affected object has changed and thus
         * the ChangeSet is no longer compatible.
         */
        ENTRY_MODIFICATION_AFTER_REMOVE_INSERT: 4,
        /** An entry with the same key was inserted into the collection */
        INSERTED_ENTRY_WITH_SAME_KEY: 5,
        /** A property was removed after a modify, this should mostly be safe, be we report it for completeness sake */
        REMOVE_AFTER_MODIFY: 6,
        // Templates do not match from one commit to another
        MISMATCH_TEMPLATES: 7,
        // Tried to insert inside a removed array range
        INSERT_IN_REMOVED_RANGE: 8
      };
      /**
       * @typedef {Object} LYNX.Property.ChangeSet.ConflictInfo
       * @property {string|undefined}                               path      -
       *     Path to the position where the conflict occurred. If the conflicting change is of type
       *     MISMATCH_TEMPLATES then the path will be undefined.
       * @property {LYNX.Property.ChangeSet.ConflictType}           type      -
       *     Type of the conflict
       * @property {LYNX.Property.SerializedChangeSet|undefined}    conflictingChange -
       *     The ChangeSet whose application failed. Depending on the context, this can have different meanings:
       *
       *       * ENTRY_MODIFIED_AFTER_REMOVE            - The modification that was not applied as the child no longer
       *                                                  existed
       *       * COLLIDING_SET                          - The original value of the property that was overwritten by this
       *                                                  change
       *       * ENTRY_MODIFICATION_AFTER_REMOVE_INSERT - The modification that was not applied due to the conflict
       *       * INSERTED_ENTRY_WITH_SAME_KEY           - The normalized property set of the entry that could not be
       *                                                  inserted
       *       * REMOVE_AFTER_MODIFY                    - The original change that gets removed
       *       * MISMATCH_TEMPLATES                     - A changeset with an insertTemplates entry that only
       *                                                  contains the conflicting templates.
       */
      /**
       * Creates a string representation of the change set
       * @return {string} JSON encoding of the changes in this change set
       */
      ChangeSet.prototype.toString = function () {
        return JSON.stringify(this._changes);
      };
      /**
       * Returns the serialized changes.
       *
       * @return {LYNX.Property.SerializedChangeSet} The serialized changeset
       */
      ChangeSet.prototype.getSerializedChangeSet = function () {
        return this._changes;
      };
      /**
       * Clones the ChangeSet
       *
       * @return {LYNX.Property.ChangeSet} The cloned ChangeSet
       */
      ChangeSet.prototype.clone = function () {
        return new ChangeSet(deepCopy(this._changes));
      };
      /**
       * Updates this ChangeSet. The result will be the concatenation of the two ChangeSets. First the changes in this
       * ChangeSet are executed, then the changes in the supplied ChangeSet are applied afterwards. The result will be
       * stored in this ChangeSet. This function assumes, that the second ChangeSet is relative to the state after
       * application of the first ChangeSet.
       *
       * @param {LYNX.Property.ChangeSet|LYNX.Property.SerializedChangeSet} in_changeSet - The changeset to apply
       */
      ChangeSet.prototype.applyChangeSet = function (in_changeSet) {
        var changes = in_changeSet;
        if (in_changeSet instanceof ChangeSet) {
          changes = in_changeSet.getSerializedChangeSet();
        }
        this._performApplyAfterOnProperty(this._changes, changes);
      };
      /**
       * Applies a changeset to a given property (recursively). The ChangeSet is assumed to be relative to the same
       * property root and it will be applied behind the base ChangeSet (assuming that the changes are relative to the
       * state after the base ChangeSet has been applied. It will change the base ChangeSet.)
       *
       * @param {LYNX.Property.SerializedChangeSet} io_basePropertyChanges    - The ChangeSet describing the initial state
       * @param {LYNX.Property.SerializedChangeSet} in_appliedPropertyChanges - The ChangeSet to apply to this state
       * @private
       */
      ChangeSet.prototype._performApplyAfterOnProperty = function (io_basePropertyChanges, in_appliedPropertyChanges) {
        // Apply dynamic property operations
        if (in_appliedPropertyChanges['insert'] || in_appliedPropertyChanges['modify'] || in_appliedPropertyChanges['remove']) {
          this._performApplyAfterOnPropertyIndexedCollection(io_basePropertyChanges, in_appliedPropertyChanges, 'NodeProperty');  // TODO: recursively propagate the typeid?
        }
        if (!_.isEmpty(in_appliedPropertyChanges.insertTemplates)) {
          io_basePropertyChanges.insertTemplates = io_basePropertyChanges.insertTemplates || {};
          _.extend(io_basePropertyChanges.insertTemplates, in_appliedPropertyChanges.insertTemplates);
        }
        // Apply ChangeSet to the properties
        var modifiedTypeids = _.keys(in_appliedPropertyChanges);
        for (var i = 0; i < modifiedTypeids.length; i++) {
          var typeid = modifiedTypeids[i];
          // The reserved keywords have already been handled above
          if (ChangeSet.isReservedKeyword(typeid)) {
            continue;
          }
          io_basePropertyChanges[typeid] = io_basePropertyChanges[typeid] || {};
          var baseChanges = io_basePropertyChanges[typeid];
          var changedKeys = _.keys(in_appliedPropertyChanges[typeid]);
          for (var j = 0; j < changedKeys.length; j++) {
            this._performApplyAfterOnPropertyWithTypeid(changedKeys[j], baseChanges, in_appliedPropertyChanges[typeid], typeid, true);
          }
          // Remove the type when it no longer contains any changed keys
          if (_.isEmpty(io_basePropertyChanges[typeid])) {
            delete io_basePropertyChanges[typeid];
          }
        }
      };
      /**
       * Helper function used to apply a new value to a given ChangeSet.
       * It is used to handle setting a primitive value, which might either be represented
       * via a literal or an object with a member value.
       * applies in_appliedValue to the io_baseChanges at the given in_baseKey
       * @param  {LYNX.Property.SerializedChangeSet} io_baseChanges - base changes (modified)
       * @param  {string} in_baseKey - key
       * @param  {LYNX.Property.SerializedChangeSet} in_appliedValue - applied changes to be applied
       * @private
       */
      ChangeSet.prototype._applyValue = function (io_baseChanges, in_baseKey, in_appliedValue) {
        var newValue = in_appliedValue.hasOwnProperty('value') ? in_appliedValue.value : in_appliedValue;
        if (io_baseChanges[in_baseKey] !== undefined && io_baseChanges[in_baseKey].hasOwnProperty('value')) {
          io_baseChanges[in_baseKey].value = newValue;
        } else {
          io_baseChanges[in_baseKey] = newValue;
        }
      };
      /**
       * Decides based on the given Typeid which applyAfter operation to perform.
       * Note: This function is not directly called on the ChangeSet but on the object containing it together with a key
       *       since it needs to be able to overwrite this entry
       *
       * @param {String}  in_changedKey             - The key of the entry in the object
       * @param {Object}  in_baseChanges            - The object containing the state before the applyAfter
       * @param {Object}  in_appliedPropertyChanges - The object containing the ChangeSet with the modification
       * @param {String}  in_typeid                 - The typeid of the property to modify
       * @param {Boolean} in_removeEmpty            - Should empty ChangeSets be removed?
       * @private
       */
      ChangeSet.prototype._performApplyAfterOnPropertyWithTypeid = function (in_changedKey, in_baseChanges, in_appliedPropertyChanges, in_typeid, in_removeEmpty) {
        var splitTypeid = TypeIdHelper.extractContext(in_typeid);
        if (splitTypeid.context === 'set' || splitTypeid.context === 'map') {
          in_baseChanges[in_changedKey] = in_baseChanges[in_changedKey] || {};
          this._performApplyAfterOnPropertyIndexedCollection(in_baseChanges[in_changedKey], in_appliedPropertyChanges[in_changedKey], splitTypeid.typeid);
          // Remove the key, when it no longer contains a changeset
          if (in_removeEmpty && _.isEmpty(in_baseChanges[in_changedKey])) {
            delete in_baseChanges[in_changedKey];
          }
        } else if (splitTypeid.context === 'array' || splitTypeid.typeid === 'String') {
          in_baseChanges[in_changedKey] = in_baseChanges[in_changedKey] || {};
          var baseIsSetChange = false;
          if (splitTypeid.typeid === 'String' && (_.isString(in_baseChanges[in_changedKey]) || in_baseChanges[in_changedKey].hasOwnProperty('value'))) {
            // we need to convert the format to allow the application of the changes
            // since _performApplyAfterOnPropertyArray only understands insert/modify/remove commands
            if (in_baseChanges[in_changedKey].hasOwnProperty('value')) {
              in_baseChanges[in_changedKey] = {
                insert: [[
                    0,
                    in_baseChanges[in_changedKey].value
                  ]]
              };
            } else {
              in_baseChanges[in_changedKey] = {
                insert: [[
                    0,
                    in_baseChanges[in_changedKey]
                  ]]
              };
            }
            baseIsSetChange = true;
          }
          var appliedChanges = in_appliedPropertyChanges[in_changedKey];
          if (_.isObject(appliedChanges) && appliedChanges.hasOwnProperty('value')) {
            appliedChanges = appliedChanges.value;
          }
          if (splitTypeid.typeid === 'String' && _.isString(appliedChanges)) {
            // we've got a 'set' command and just overwrite the changes
            in_baseChanges[in_changedKey] = appliedChanges;
          } else {
            // we have incremental changes (or a standard array)
            this._performApplyAfterOnPropertyArray(in_baseChanges[in_changedKey], in_appliedPropertyChanges[in_changedKey], splitTypeid.typeid);
            if (baseIsSetChange) {
              // we have to convert back to a string, if it had been converted before
              if (_.isEmpty(in_baseChanges[in_changedKey])) {
                in_baseChanges[in_changedKey] = '';
              } else {
                in_baseChanges[in_changedKey] = in_baseChanges[in_changedKey].insert[0][1];
              }
            }
          }
          // Remove the key, when it no longer contains a changeset
          if (in_removeEmpty && ChangeSet.isEmptyChangeSet(in_baseChanges[in_changedKey])) {
            delete in_baseChanges[in_changedKey];
          }
        } else if (splitTypeid.isEnum) {
          // Enum types can simply be overwritten
          this._applyValue(in_baseChanges, in_changedKey, in_appliedPropertyChanges[in_changedKey]);
        } else if (splitTypeid.context === 'single') {
          if (TypeIdHelper.isPrimitiveType(splitTypeid.typeid)) {
            // Primitive types can simply be overwritten, however we have an exception for
            // 64 bit integers (until javascript natively supports them)
            if (splitTypeid.typeid === 'Int64' || splitTypeid.typeid === 'Uint64') {
              var appliedVal = in_appliedPropertyChanges[in_changedKey];
              if (appliedVal.hasOwnProperty('value')) {
                appliedVal = appliedVal.value;
              }
              this._applyValue(in_baseChanges, in_changedKey, appliedVal.slice());
            } else {
              this._applyValue(in_baseChanges, in_changedKey, in_appliedPropertyChanges[in_changedKey]);
            }
          } else {
            if (in_baseChanges[in_changedKey]) {
              // Otherwise we have to continue the merging recursively
              this._performApplyAfterOnProperty(in_baseChanges[in_changedKey], in_appliedPropertyChanges[in_changedKey]);
            } else {
              // If the key doesn't exist, yet, we can just copy it
              in_baseChanges[in_changedKey] = deepCopy(in_appliedPropertyChanges[in_changedKey]);
            }
          }
        } else {
          throw new Error(MSG.UNKNOWN_CONTEXT + splitTypeid.context);
        }
      };
      /**
       * Rebases a given ChangeSet behind the current ChangeSet.
       *
       * This function takes a ChangeSet which is assumed to be relative to the same base state as the ChangeSet stored in
       * this class and transforms it in such a way that it can be applied after this ChangeSet. The function will modify
       * the supplied ChangeSet
       *
       * @param {LYNX.Property.SerializedChangeSet}     io_changeSet   -
       *     The ChangeSet that is rebased behind the state obtained by application of this ChangeSet
       * @param {Array.<LYNX.Property.ChangeSet.ConflictInfo>} out_conflicts
       *     A list of paths that resulted in conflicts together with the type of the conflict
       *
       * @return {LYNX.Property.SerializedChangeSet} The rebased ChangeSet (the same object as io_changeSet, it will be
       *     modified in place)
       */
      ChangeSet.prototype._rebaseChangeSet = function (io_changeSet, out_conflicts) {
        // We actually only pass this request to the recursive internal function
        return this._rebaseChangeSetForProperty(this._changes, io_changeSet, '', out_conflicts);
      };
      /**
       * Internal helper function that performs a rebase on a single property
       *
       * @param {LYNX.Property.SerializedChangeSet} in_ownPropertyChangeSet -
       *     The ChangeSet for the property stored in this class
       * @param {LYNX.Property.SerializedChangeSet} io_rebasePropertyChangeSet -
       *     The ChangeSet for the property to be rebased
       * @param {string} in_basePath -
       *     Base path to get to the property processed by this function
       * @param {Array.<LYNX.Property.ChangeSet.ConflictInfo>} out_conflicts -
       *     A list of paths that resulted in conflicts together with the type of the conflict
       *
       * @return  {LYNX.Property.SerializedChangeSet} The rebased ChangeSet for this property
       * @private
       */
      ChangeSet.prototype._rebaseChangeSetForProperty = function (in_ownPropertyChangeSet, io_rebasePropertyChangeSet, in_basePath, out_conflicts) {
        // Process the children in this ChangeSet
        if ((in_ownPropertyChangeSet['insert'] || in_ownPropertyChangeSet['modify'] || in_ownPropertyChangeSet['remove']) && (io_rebasePropertyChangeSet['insert'] || io_rebasePropertyChangeSet['modify'] || io_rebasePropertyChangeSet['remove'])) {
          this._rebaseIndexedCollectionChangeSetForProperty(in_ownPropertyChangeSet, io_rebasePropertyChangeSet, in_basePath, 'NodeProperty', // TODO: recursively propagate the typeid?
          false, // don't use square brackets (use dots instead)
          out_conflicts);
        }
        if (!_.isEmpty(io_rebasePropertyChangeSet.insertTemplates)) {
          var typeids = _.keys(io_rebasePropertyChangeSet.insertTemplates);
          var templateMismatchChangeSet = { insertTemplates: {} };
          var templateMismatchConflict = {
            type: ChangeSet.ConflictType.MISMATCH_TEMPLATES,
            conflictingChange: templateMismatchChangeSet
          };
          _.each(typeids, function (typeid) {
            var template = io_rebasePropertyChangeSet.insertTemplates[typeid];
            if (in_ownPropertyChangeSet.insertTemplates && in_ownPropertyChangeSet.insertTemplates[typeid]) {
              var isEqual = TemplateValidator.Utils.psetDeepEquals(template, in_ownPropertyChangeSet.insertTemplates[template.typeid]);
              if (!isEqual) {
                templateMismatchChangeSet.insertTemplates[typeid] = in_ownPropertyChangeSet.insertTemplates[template.typeid];
                // TODO: Remove this warning message once we offer a conflict resolution API
                console.warn(MSG.TEMPLATE_MISMATCH + typeid);
              }
              delete io_rebasePropertyChangeSet.insertTemplates[typeid];
            }
          });
          // Remove insertTemplates key if it is empty
          if (_.isEmpty(io_rebasePropertyChangeSet.insertTemplates)) {
            delete io_rebasePropertyChangeSet.insertTemplates;
          }
          if (!_.isEmpty(templateMismatchConflict.conflictingChange.insertTemplates)) {
            out_conflicts.push(templateMismatchConflict);
          }
        }
        // Check for collisions in the property assignments
        var changedTypeids = _.keys(in_ownPropertyChangeSet);
        // We currently do not yet have any
        var changeSet = {};
        for (var i = 0; i < changedTypeids.length; i++) {
          var typeid = changedTypeids[i];
          // The reserved keywords have already been handled above and changes which are not present in
          // the other ChangeSet can be ignored
          if (ChangeSet.isReservedKeyword(typeid) || !io_rebasePropertyChangeSet[typeid]) {
            continue;
          }
          // Check, whether we have a collision in a path update
          var paths = _.keys(in_ownPropertyChangeSet[typeid]);
          for (var j = 0; j < paths.length; j++) {
            if (io_rebasePropertyChangeSet[typeid][paths[j]]) {
              in_ownPropertyChangeSet[typeid] = in_ownPropertyChangeSet[typeid] || {};
              var newPath = joinPaths(in_basePath, paths[j], PROPERTY_PATH_DELIMITER);
              // Perform the rebase operation on the ChangeSet for this entry
              var setConflict = this._rebaseChangeSetForPropertyEntryWithTypeid(paths[j], in_ownPropertyChangeSet[typeid], io_rebasePropertyChangeSet[typeid], typeid, newPath, true, out_conflicts);
              // If there has been a non-recursive set collision we handle it here separately
              if (setConflict) {
                // If we have two writes to primitive types, this is a conflict
                changeSet[typeid] = changeSet[typeid] || {};
                // Store the change. Note: We make a deep copy here, as this is a reference into our
                // own internal ChangeSet and we want to be sure, nobody changes our internal data-structures
                changeSet[typeid][paths[j]] = deepCopy(in_ownPropertyChangeSet[typeid][paths[j]]);
              }
              // Remove the typeid, when it no longer contains any keys
              if (_.isEmpty(io_rebasePropertyChangeSet[typeid])) {
                delete io_rebasePropertyChangeSet[typeid];
              }
            }
          }
        }
        // If there were conflicts in the set operations, report them
        if (!_.isEmpty(changeSet)) {
          var conflict = {
            path: in_basePath,
            type: ChangeSet.ConflictType.COLLIDING_SET,
            conflictingChange: changeSet
          };
          out_conflicts.push(conflict);
        }
        return io_rebasePropertyChangeSet;
      };
      /**
       * Decides based on the given Typeid which rebase operation to perform
       * Note: This function is not directly called on the ChangeSet but on the object containing it together with a key
       *       since it needs to be able to overwrite this entry
       *
       * @param {string} in_key                          - The key of the entry in the object
       * @param {Object} in_ownPropertyChangeSet         - The object containing the ChangeSet for the property
       *                                                   stored in this class
       * @param {Object} io_rebasePropertyChangeSet      - The object containing the ChangeSet for the property to
       *                                                   be rebased
       * @param {string} in_typeid                       - The typeid of the property to rebase
       * @param {string} in_basePath                     - Base path to get to the property processed by this function
       * @param {Boolean} in_removeEmpty                 - Should empty ChangeSets be removed?
       * @param {Array.<LYNX.Property.ChangeSet.ConflictInfo>} out_conflicts - A list of paths that resulted in
       *                                                   conflicts together with the type of the conflict
       *
       * @return {boolean} Has there been a simple set collision? Those have to be handled separately
       *                   TODO: We should unify the handling of set collisions
       * @private
       */
      ChangeSet.prototype._rebaseChangeSetForPropertyEntryWithTypeid = function (in_key, in_ownPropertyChangeSet, io_rebasePropertyChangeSet, in_typeid, in_basePath, in_removeEmpty, out_conflicts) {
        var splitTypeid = TypeIdHelper.extractContext(in_typeid);
        if (splitTypeid.context === 'set' || splitTypeid.context === 'map') {
          this._rebaseIndexedCollectionChangeSetForProperty(in_ownPropertyChangeSet[in_key], io_rebasePropertyChangeSet[in_key], in_basePath, splitTypeid.typeid, true, // use square brackets
          out_conflicts);
          // Remove the key, when it no longer contains a changeset
          if (in_removeEmpty && _.isEmpty(io_rebasePropertyChangeSet[in_key])) {
            delete io_rebasePropertyChangeSet[in_key];
          }
        } else if (splitTypeid.context === 'array') {
          this._rebaseArrayChangeSetForProperty(in_ownPropertyChangeSet[in_key], io_rebasePropertyChangeSet[in_key], in_basePath, out_conflicts, splitTypeid.typeid);
          // Remove the key, when it no longer contains a changeset
          if (in_removeEmpty && _.isEmpty(io_rebasePropertyChangeSet[in_key])) {
            delete io_rebasePropertyChangeSet[in_key];
          }
        } else if (splitTypeid.typeid === 'String') {
          this._rebaseChangeSetForString(in_ownPropertyChangeSet[in_key], io_rebasePropertyChangeSet, in_key, in_basePath, out_conflicts);
          // Remove the key, when it no longer contains a changeset
          if (in_removeEmpty && _.isEmpty(io_rebasePropertyChangeSet[in_key])) {
            delete io_rebasePropertyChangeSet[in_key];
          }
        } else if (splitTypeid.context === 'single') {
          // We only can have a conflict when the path exists in both ChangeSets
          if (in_ownPropertyChangeSet[in_key] !== undefined) {
            if (TypeIdHelper.isPrimitiveType(splitTypeid.typeid) || splitTypeid.isEnum) {
              return true;
            } else {
              // Otherwise, we have to continue recursively
              // Make sure the paths exist
              in_ownPropertyChangeSet[in_key] = in_ownPropertyChangeSet[in_key] || {};
              // And then perform the recursive rebase
              this._rebaseChangeSetForProperty(in_ownPropertyChangeSet[in_key], io_rebasePropertyChangeSet[in_key], in_basePath, out_conflicts);
            }
          }
        } else {
          throw new Error(MSG.UNKNOWN_CONTEXT + splitTypeid.context);
        }
        return false;
      };
      /**
       * Helper function which checks, whether a given serialized changeSet is an empty changeSet
       *
       * @param {LYNX.Property.SerializedChangeSet} in_changeSet - The changeset to test
       * @return {boolean} Is it an empty changeset?
       */
      ChangeSet.isEmptyChangeSet = function (in_changeSet) {
        return in_changeSet === undefined || _.isObject(in_changeSet) && _.isEmpty(in_changeSet);
      };
      /**
       * Checks, whether the given key from a ChangeSet is not a typeid, but one of the
       * reserved keywords
       *
       * @param {string} in_key - The key to check
       * @return {boolean} - Is this a reserved keyword
       */
      ChangeSet.isReservedKeyword = function (in_key) {
        return in_key === 'insert' || in_key === 'remove' || in_key === 'modify' || in_key === '.children' || // To be removed
        in_key === 'typeid' || in_key === 'insertTemplates';
      };
      /**
       * recursive helper function for ChangeSet.prototype._toReversibleChangeSet
       * which converts a irreversible changeset to a reversible changeset
       * or uptates the former state of a reversible changeset
       * @param  {{LYNX.Utils.TraversalContext}} in_context the traversal context
       */
      // eslint-disable-next-line complexity
      ChangeSet.prototype._recursivelyBuildReversibleChangeSet = function (in_context) {
        var opType = in_context.getOperationType();
        if (opType === 'remove' || opType === 'modify') {
          var type = in_context.getTypeid();
          var splitType = in_context.getSplitTypeID();
          var oldState = in_context.getWorkspace().oldState;
          var oldValue;
          var nestedChangeset = in_context.getNestedChangeSet();
          if (TypeIdHelper.isPrimitiveType(type)) {
            // get old state in_oldSerializedState
            var old = Property.Utils.getChangesByPath(in_context.getFullPath(), null, oldState);
            if (old.insert) {
              oldValue = old.insert[in_context.getFullPath()];
            } else if (old.modify) {
              oldValue = old.modify[in_context.getFullPath()];
            } else {
              console.error('toReversibleChangeSet: oldValue not found!');
              return;
            }
            // store it in reversibleChangeSet
            if (type === 'String' && !_.isString(nestedChangeset)) {
              // String is a special case
              var oldString;
              if (_.isString(oldValue)) {
                oldString = oldValue;
              }
              if (nestedChangeset.modify) {
                for (var i = 0; i < nestedChangeset.modify.length; i++) {
                  var entry = nestedChangeset.modify[i];
                  var entryOffset = entry[0];
                  var entryLength = entry[1].length;
                  entry[2] = oldString.slice(entryOffset, entryOffset + entryLength);
                }
              }
              if (nestedChangeset.remove) {
                for (var i = 0; i < nestedChangeset.remove.length; i++) {
                  var entry = nestedChangeset.remove[i];
                  var entryOffset = entry[0];
                  var oldEntries = [];
                  var removeRangeLength = entry[1];
                  if (_.isString(removeRangeLength)) {
                    removeRangeLength = entry[1].length;
                  }
                  entry[1] = oldString.slice(entryOffset, entryOffset + removeRangeLength);
                }
              }
            } else {
              if (nestedChangeset.hasOwnProperty('value')) {
                nestedChangeset.oldValue = oldValue;
              } else {
                var newChangeSet = {
                  value: nestedChangeset,
                  oldValue: oldValue
                };
                in_context.replaceNestedChangeSet(newChangeSet);
              }
            }
          } else if (splitType.context === 'array') {
            var old = Property.Utils.getChangesByPath(in_context.getFullPath(), null, oldState);
            if (old.insert) {
              oldValue = old.insert[in_context.getFullPath()].insert[0][1];
            } else if (old.modify) {
              oldValue = old.modify[in_context.getFullPath()].insert[0][1];
            } else {
              console.error('toReversibleChangeSet: oldValue not found!');
              return;
            }
            var nestedChangeset = in_context.getNestedChangeSet();
            if (nestedChangeset.modify) {
              for (var i = 0; i < nestedChangeset.modify.length; i++) {
                var entry = nestedChangeset.modify[i];
                var entryOffset = entry[0];
                var oldEntries = [];
                for (var j = 0; j < entry[1].length; j++) {
                  oldEntries.push(deepCopy(oldValue[entryOffset + j]));
                }
                entry[2] = oldEntries;
              }
            }
            if (nestedChangeset.remove) {
              for (var i = 0; i < nestedChangeset.remove.length; i++) {
                var entry = nestedChangeset.remove[i];
                var entryOffset = entry[0];
                var oldEntries = [];
                var removeRangeLength = entry[1];
                if (_.isArray(removeRangeLength)) {
                  removeRangeLength = entry[1].length;
                }
                for (var j = 0; j < removeRangeLength; j++) {
                  oldEntries.push(deepCopy(oldValue[entryOffset + j]));
                }
                entry[1] = oldEntries;
              }
            }
          } else if (splitType.context === 'map' || (nestedChangeset['insert'] || nestedChangeset['modify'] || nestedChangeset['remove'])) {
            var oldValue;
            if (nestedChangeset.modify || nestedChangeset.remove) {
              var oldSubSet = Property.Utils.getChangesByPath(in_context.getFullPath(), null, oldState);
              if (oldSubSet.modify && oldSubSet.modify.hasOwnProperty(in_context.getFullPath())) {
                oldValue = oldSubSet.modify[in_context.getFullPath()].insert;
              } else if (oldSubSet.modify && in_context.getFullPath() === '') {
                oldValue = oldSubSet.modify.insert;
              } else if (oldSubSet.insert && oldSubSet.insert.hasOwnProperty(in_context.getFullPath())) {
                oldValue = oldSubSet.insert[in_context.getFullPath()].insert;
              } else {
                console.error('toReversibleChangeSet: oldValue not found: ', in_context.getFullPath());
              }
            }
            if (TypeIdHelper.isPrimitiveType(splitType.typeid)) {
              if (nestedChangeset.modify) {
                var modifiedKeys = Object.keys(nestedChangeset.modify);
                for (var i = 0; i < modifiedKeys.length; i++) {
                  var entry = nestedChangeset.modify[modifiedKeys[i]];
                  if (entry.value) {
                    entry = entry.value;
                  }
                  nestedChangeset.modify[modifiedKeys[i]] = {
                    value: entry,
                    oldValue: deepCopy(oldValue[modifiedKeys[i]])
                  };
                }
              }
              var newRemove = {};
              if (nestedChangeset.remove) {
                var removedKeys = nestedChangeset.remove;
                if (!_.isArray(removedKeys)) {
                  removedKeys = Object.keys(removedKeys);
                }
                for (var i = 0; i < removedKeys.length; i++) {
                  newRemove[removedKeys[i]] = deepCopy(oldValue[removedKeys[i]]);
                }
                nestedChangeset.remove = newRemove;
              }
            } else {
              var nestedChangeset = in_context.getNestedChangeSet();
              if (nestedChangeset.modify) {
              }
              var newRemove = {};
              if (nestedChangeset.remove) {
                if (_.isArray(nestedChangeset.remove)) {
                  var removedKeys = nestedChangeset.remove;
                  for (var i = 0; i < removedKeys.length; i++) {
                    var searchedKey = removedKeys[i];
                    // search for this key in the old keys:
                    var oldTypeKeys = Object.keys(oldValue);
                    for (var k = 0; k < oldTypeKeys.length; k++) {
                      if (oldValue[oldTypeKeys[k]].hasOwnProperty(searchedKey)) {
                        var entry = oldValue[oldTypeKeys[k]][searchedKey];
                        if (!newRemove[oldTypeKeys[k]]) {
                          newRemove[oldTypeKeys[k]] = {};
                        }
                        newRemove[oldTypeKeys[k]][removedKeys[i]] = deepCopy(entry);
                      }
                    }
                  }
                  nestedChangeset.remove = newRemove;
                } else {
                  // we already have a reversibleChangeSet and need to update the oldValues
                  var removedTypes = Object.keys(nestedChangeset.remove);
                  for (var t = 0; t < removedTypes.length; t++) {
                    var removedKeys = Object.keys(nestedChangeset.remove[removedTypes[t]]);
                    for (var i = 0; i < removedKeys.length; i++) {
                      var searchedKey = removedKeys[i];
                      var entry = oldValue[removedTypes[t]][searchedKey];
                      nestedChangeset.remove[removedTypes[t]][removedKeys[i]] = entry;
                    }
                  }
                }
              }
            }
          }
        }
      };
      /**
       * Converts an irreversible changeset to a reversible changeset
       * or updates the former state of a reversible changeset
       * WARNING: This function is still experimental and needs more testing
       * and it's set to private for now. It will be converted to a public API function
       * in a later release.
       * @param  {LYNX.Property.SerializedChangeSet} in_oldSerializedState the old state
       * @private
       */
      ChangeSet.prototype._toReversibleChangeSet = function (in_oldSerializedState) {
        if (!in_oldSerializedState) {
          throw new Error('ChangeSet.toReversibleChangeSet: in_oldSerializedState must be provided');
        }
        var workspace = { oldState: in_oldSerializedState };
        Property.Utils.traverseChangeSetRecursively(this._changes, this._recursivelyBuildReversibleChangeSet, workspace);
      };
      /**
       * Converts a reversible changeset to an irreversible changeset
       * WARNING: This function is still experimental and needs more testing
       * and it's set to private for now. It will be converted to a public API function
       * in a later release.
       * @private
       */
      ChangeSet.prototype._stripReversibleChangeSet = function () {
        // eslint-disable-next-line complexity
        Property.Utils.traverseChangeSetRecursively(this._changes, function (in_context) {
          var opType = in_context.getOperationType();
          if (opType === 'remove' || opType === 'modify') {
            var type = in_context.getTypeid();
            if (!type) {
              return;
            }
            var splitType = in_context.getSplitTypeID();
            if (TypeIdHelper.isPrimitiveType(type)) {
              // remove old state
              var nestedChangeset = in_context.getNestedChangeSet();
              if (type === 'String' && !_.isString(nestedChangeset)) {
                // String is a special case
                if (nestedChangeset.modify) {
                  for (var i = 0; i < nestedChangeset.modify.length; i++) {
                    var entry = nestedChangeset.modify[i];
                    entry.splice(2, 1);
                  }
                }
                if (nestedChangeset.remove) {
                  for (var i = 0; i < nestedChangeset.remove.length; i++) {
                    var entry = nestedChangeset.remove[i];
                    var removeRangeLength = entry[1];
                    if (_.isString(removeRangeLength)) {
                      removeRangeLength = entry[1].length;
                    }
                    entry[1] = removeRangeLength;
                  }
                }
                if (nestedChangeset.hasOwnProperty('value')) {
                  in_context.replaceNestedChangeSet(nestedChangeset.value);
                }
              } else if (nestedChangeset.hasOwnProperty('value')) {
                in_context.replaceNestedChangeSet(nestedChangeset.value);
              }
            } else if (splitType.context === 'array') {
              var nestedChangeset = in_context.getNestedChangeSet();
              if (nestedChangeset.modify) {
                for (var i = 0; i < nestedChangeset.modify.length; i++) {
                  var entry = nestedChangeset.modify[i];
                  entry.splice(2, 1);
                }
              }
              if (nestedChangeset.remove) {
                for (var i = 0; i < nestedChangeset.remove.length; i++) {
                  var entry = nestedChangeset.remove[i];
                  var removeRangeLength = entry[1];
                  if (_.isArray(removeRangeLength)) {
                    removeRangeLength = entry[1].length;
                  }
                  entry[1] = removeRangeLength;
                }
              }
            } else if (splitType.context === 'map') {
              var nestedChangeset = in_context.getNestedChangeSet();
              if (TypeIdHelper.isPrimitiveType(splitType.typeid)) {
                if (nestedChangeset.modify) {
                  var modifiedKeys = Object.keys(nestedChangeset.modify);
                  for (var i = 0; i < modifiedKeys.length; i++) {
                    var entry = nestedChangeset.modify[modifiedKeys[i]];
                    if (entry.value) {
                      entry = entry.value;
                    }
                    nestedChangeset.modify[modifiedKeys[i]] = entry;
                  }
                }
                var newRemove = [];
                if (nestedChangeset.remove) {
                  var removedKeys = nestedChangeset.remove;
                  if (!_.isArray(removedKeys)) {
                    removedKeys = Object.keys(removedKeys);
                    nestedChangeset.remove = removedKeys;
                  }
                }
              } else {
                var nestedChangeset = in_context.getNestedChangeSet();
                if (nestedChangeset.modify) {
                }
                var newRemove = {};
                if (nestedChangeset.remove) {
                  if (!_.isArray(nestedChangeset.remove)) {
                    // we have a reversibleChangeSet and need to convert
                    var newRemove = [];
                    var removedTypes = Object.keys(nestedChangeset.remove);
                    for (var t = 0; t < removedTypes.length; t++) {
                      var removedKeys = Object.keys(nestedChangeset.remove[removedTypes[t]]);
                      for (var i = 0; i < removedKeys.length; i++) {
                        newRemove.push(removedKeys[i]);
                      }
                    }
                    nestedChangeset.remove = newRemove;
                  }
                }
              }
            }
          }
        });
      };
      /**
       * recursive helper function for ChangeSet.prototype._toInverseChangeSet
       * @param  {{LYNX.Utils.TraversalContext}} in_context the traversal context
       */
      ChangeSet.prototype._recursivelyInvertReversibleChangeset = function (in_context) {
        if (in_context.getOperationType() === 'remove' || in_context.getOperationType() === 'modify') {
          var type = in_context.getTypeid();
          var splitType = in_context.getSplitTypeID();
          var nestedChangeset = in_context.getNestedChangeSet();
          if (TypeIdHelper.isPrimitiveType(type) && type !== 'String' || type === 'String' && _.isString(nestedChangeset.oldValue)) {
            // switch oldValue and value
            var tmp = nestedChangeset.oldValue;
            nestedChangeset.oldValue = nestedChangeset.value;
            nestedChangeset.value = tmp;
          } else if (type === 'String' && !_.isString(nestedChangeset.oldValue) || splitType.context === 'array') {
            // String and Arrays need special treatment:
            var ArrayChangeSetIterator = ArrayOperations.ArrayChangeSetIterator;
            var arrayIterator = new ArrayChangeSetIterator(nestedChangeset);
            var resultChangeset = {};
            if (nestedChangeset.modify) {
              resultChangeset.modify = [];
            }
            if (nestedChangeset.insert) {
              resultChangeset.remove = [];
            }
            if (nestedChangeset.remove) {
              resultChangeset.insert = [];
            }
            // Successively invert the changes from the changeSet
            while (!arrayIterator.atEnd()) {
              switch (arrayIterator.type) {
              case ArrayChangeSetIterator.types.INSERT:
                // Handle inserts
                resultChangeset.remove.push([
                  arrayIterator.operation[0] + arrayIterator.offset,
                  arrayIterator.operation[1]
                ]);
                break;
              case ArrayChangeSetIterator.types.REMOVE:
                // Handle removes
                resultChangeset.insert.push([
                  arrayIterator.operation[0] + arrayIterator.offset,
                  arrayIterator.operation[1]
                ]);
                break;
              case ArrayChangeSetIterator.types.MODIFY:
                // Handle modifies
                resultChangeset.modify.push([
                  arrayIterator.operation[0] + arrayIterator.offset,
                  arrayIterator.operation[2],
                  arrayIterator.operation[1]
                ]);
                break;
              default:
                console.error('applyChangeset: ' + MSG.UNKNOWN_OPERATION);
              }
              arrayIterator.next();
            }
            in_context.replaceNestedChangeSet(resultChangeset);
          } else if (splitType.context === 'map') {
            if (nestedChangeset.modify) {
              if (TypeIdHelper.isPrimitiveType(splitType.typeid)) {
                var modifiedKeys = Object.keys(nestedChangeset.modify);
                for (var i = 0; i < modifiedKeys.length; i++) {
                  var entry = nestedChangeset.modify[modifiedKeys[i]];
                  var tmp = entry.value;
                  entry.value = entry.oldValue;
                  entry.oldValue = tmp;
                }
              }
            }
            var oldInsert = nestedChangeset.insert;
            if (nestedChangeset.remove) {
              nestedChangeset.insert = nestedChangeset.remove;
              nestedChangeset.remove = undefined;
              delete nestedChangeset.remove;
            }
            if (oldInsert) {
              nestedChangeset.remove = deepCopy(oldInsert);
            }
          }
        }
      };
      /**
       * Inverts a reversible ChangeSets
       * WARNING: This function is still experimental and needs more testing
       * and it's set to private for now. It will be converted to a public API function
       * in a later release
       * @private
       */
      ChangeSet.prototype._toInverseChangeSet = function () {
        Property.Utils.traverseChangeSetRecursively(this._changes, this._recursivelyInvertReversibleChangeset);
      };
      exportModule('LYNX.Property.ChangeSet', ChangeSet);
    }());
    (function () {
      var ChangeSet = include('LYNX.Property.ChangeSet');
      var TypeIdHelper = include('LYNX.Property.TypeIdHelper');
      var deepCopy = include('LYNX.Utils.deepCopy');
      var MSG = include('LYNX.Property._constants.MSG');
      var _ = include('underscore');
      /**
       * @namespace LYNX.Property.ChangeSetOperations
       * @alias LYNX.Property.ChangeSetOperations
       * Helper functions and classes to perform operations on ChangeSets
       */
      /**
       * @namespace LYNX.Property.ChangeSetOperations.ArrayOperations
       * @alias LYNX.Property.ChangeSetOperations.ArrayOperations
       * Helper functions and classes to perform operations on array ChangeSets
       */
      /**
       * Iterator class which iterates over an array ChangeSet. It will successively return the operations ordered by their
       * position within the array. Additionally, it will keep track of the modifications to the array indices caused
       * by the previous operations.
       *
       * @param {LYNX.Property.SerializedChangeSet} in_changeSet - The ChangeSet to iterate over (this has to be an array
       *                                                           ChangeSet)
       * @alias LYNX.Property.ChangeSetOperations.ArrayOperations.ArrayChangeSetIterator
       * @constructor
       * @private
       */
      var ArrayChangeSetIterator = function (in_changeSet) {
        this._changeSet = in_changeSet;
        // if we need to chop overlapping modifies internally, so we have to copy them
        // we do this lazy and only if really needed
        this._copiedModifies = in_changeSet.modify;
        this._currentIndices = {
          insert: 0,
          remove: 0,
          modify: 0
        };
        this._currentOffset = 0;
        this._lastOperationIndex = -1;
        this._lastOperationOffset = 0;
        this._atEnd = false;
        this.type = ArrayChangeSetIterator.types.NOP;
        this.operation = undefined;
        this.offset = 0;
        // go to the first element
        this.next();
      };
      /**
       * Returns the next operation in the ChangeSet
       * @return {boolean} true, if there are operations left
       */
      ArrayChangeSetIterator.prototype.next = function () {
        // Find the smallest index in the operations lists
        var type = undefined, currentIndex = Infinity;
        // Process the current remove entry
        if (this._changeSet.remove && this._currentIndices.remove < this._changeSet.remove.length) {
          type = ArrayChangeSetIterator.types.REMOVE;
          currentIndex = this._changeSet.remove[this._currentIndices.remove][0];
        }
        // Process the current insert entry (we prefer remove over insert, since this prevents the array from growing more
        // than necessary)
        if (this._changeSet.insert && this._currentIndices.insert < this._changeSet.insert.length && this._changeSet.insert[this._currentIndices.insert][0] < currentIndex) {
          type = ArrayChangeSetIterator.types.INSERT;
          currentIndex = this._changeSet.insert[this._currentIndices.insert][0];
        }
        // Process the current modify entry
        if (this._copiedModifies && this._currentIndices.modify < this._copiedModifies.length && this._copiedModifies[this._currentIndices.modify][0] < currentIndex) {
          type = ArrayChangeSetIterator.types.MODIFY;
        }
        if (this._lastOperationIndex !== currentIndex) {
          this._currentOffset += this._lastOperationOffset;
          this._lastOperationIndex = currentIndex;
          this._lastOperationOffset = 0;
        }
        // We have found nothing, so we are at the end of the ChangeSet
        if (type === undefined) {
          this.type = ArrayChangeSetIterator.types.NOP;
          this.operation = undefined;
          this.offset = this._currentOffset;
          this._atEnd = true;
          return false;
        }
        // Determine the return value and update the internal indices and offsets depending on the next operation
        switch (type) {
        case ArrayChangeSetIterator.types.INSERT:
          // Define the return value
          this.type = ArrayChangeSetIterator.types.INSERT;
          this.operation = this._changeSet.insert[this._currentIndices.insert];
          this.offset = this._currentOffset;
          // Update the current offset. For an insert we have to increase it by the number of the inserted elements
          this._lastOperationOffset += this.operation[1].length;
          // Shift the internal index
          this._currentIndices.insert++;
          break;
        case ArrayChangeSetIterator.types.REMOVE:
          // Define the return value
          this.type = ArrayChangeSetIterator.types.REMOVE;
          this.operation = this._changeSet.remove[this._currentIndices.remove];
          this.offset = this._currentOffset;
          // Update the current offset. For a remove we have to decrement it by the number of the removed elements
          var removedElements = _.isNumber(this.operation[1]) ? this.operation[1] : this.operation[1].length;
          this._lastOperationOffset -= removedElements;
          // Shift the internal index
          this._currentIndices.remove++;
          break;
        case ArrayChangeSetIterator.types.MODIFY: {
            this.type = ArrayChangeSetIterator.types.MODIFY;
            this.offset = this._currentOffset;
            // check, if the modify's range overlaps with coming insert changes:
            var nextModify = this._copiedModifies[this._currentIndices.modify];
            var modifyEnd = nextModify[0] + nextModify[1].length;
            if (this._changeSet.insert && this._currentIndices.insert < this._changeSet.insert.length && this._changeSet.insert[this._currentIndices.insert][0] < modifyEnd) {
              // we have an overlap and need to cut the modify
              var insertPosition = this._changeSet.insert[this._currentIndices.insert][0];
              // if we haven't copied the change set's modifies yet, we need to do that now
              if (this._copiedModifies === this._changeSet.modify) {
                this._copiedModifies = this._copyModifies(this._changeSet.modify);
                // now we need to update nextModify!
                nextModify = this._copiedModifies[this._currentIndices.modify];
              }
              // use modify only up to insert's position
              // build a partial modify and cut the remaining one:
              var partialModify = [
                nextModify[0],
                0
              ];
              if (_.isString(nextModify[1])) {
                partialModify[1] = nextModify[1].substr(0, insertPosition - nextModify[0]);
                nextModify[1] = nextModify[1].substr(insertPosition - nextModify[0]);
              } else {
                partialModify[1] = nextModify[1].splice(0, insertPosition - nextModify[0]);
              }
              nextModify[0] = insertPosition;
              // use the whole modify
              this.operation = partialModify;
            } else {
              // use the whole modify
              this.operation = nextModify;
              // Shift the internal index
              this._currentIndices.modify++;
            }
            break;
          }
        default:
          throw new Error('ArrayChangeSetIterator: ' + MSG.UNKNOWN_OPERATION);
        }
        this._atEnd = false;
        return true;
      };
      /**
       * @return {boolean} true, if there are no more operations left
       */
      ArrayChangeSetIterator.prototype.atEnd = function () {
        return this._atEnd;
      };
      ArrayChangeSetIterator.prototype._copyModifies = function (in_modifies) {
        if (!in_modifies || in_modifies.length === 0) {
          return undefined;
        }
        var result = [];
        for (var i = 0; i < in_modifies.length; i++) {
          result.push([
            in_modifies[i][0],
            in_modifies[i][1].slice()
          ]);
        }
        return result;
      };
      /**
       * @enum number
       * The operations that can be performed on the array
       */
      ArrayChangeSetIterator.types = {
        INSERT: 0,
        REMOVE: 1,
        MODIFY: 2,
        MOVE: 3,
        // reserved, not implemented yet
        NOP: 4  // no op (e.g. when a remove neutralized an insert in a merge
      };
      /**
       * @enum number
       * The range combinations of two change sets (A and B)
       * This can either be complete operations, parts of complete operations or overlapping segments
       */
      var ArrayChangeSetRangeType = {
        completeA: 0,
        // a complete operation of change set A
        completeB: 1,
        // a complete operation of change set B
        partOfA: 2,
        // a partial operation of change set A
        partOfB: 3,
        // a partial operation of change set B
        completeApartOfB: 4,
        // a complete operation of change set A overlapping with a partial operation of change set B
        completeBpartOfA: 5,
        // a complete operation of change set B overlapping with a partial operation of change set A
        completeAcompleteB: 6  // a complete operation of change set A overlapping a complete operation of change set B
      };
      /**
       * compute a range for an operation of the current change set
       * @param {{type: LYNX.Property.ChangeSetOperations.ArrayOperations.ArrayChangeSetIterator.types,
       *     operation: number[], offset: number}} io_operation input
       * @param {number} in_aOffset the offset that needs to be added to transform the operation
       * @param {{begin:Number, end: Number, op:Number[], flag:ArrayChangeSetRangeType}} io_resultingRange
       * the computed range
       */
      var getRangeForCurrentStateOperation = function (io_operation, in_aOffset, io_resultingRange) {
        if (!io_operation) {
          return;
        }
        if (io_operation.type === ArrayChangeSetIterator.types.NOP) {
          var dummyOp = { offset: in_aOffset };
          io_resultingRange.begin = undefined;
          io_resultingRange.end = undefined;
          io_resultingRange.op = dummyOp;
          io_resultingRange.flag = ArrayChangeSetRangeType.completeA;
          return;
        }
        io_operation.operation[0] += in_aOffset;
        switch (io_operation.type) {
        case ArrayChangeSetIterator.types.INSERT:
          io_resultingRange.begin = io_operation.operation[0];
          io_resultingRange.end = io_operation.operation[0] + io_operation.operation[1].length;
          io_resultingRange.op = io_operation;
          io_resultingRange.flag = ArrayChangeSetRangeType.completeA;
          return;
        case ArrayChangeSetIterator.types.REMOVE:
          io_resultingRange.begin = io_operation.operation[0];
          io_resultingRange.end = io_operation.operation[0];
          io_resultingRange.op = io_operation;
          io_resultingRange.flag = ArrayChangeSetRangeType.completeA;
          return;
        case ArrayChangeSetIterator.types.MODIFY:
          io_resultingRange.begin = io_operation.operation[0];
          io_resultingRange.end = io_operation.operation[0] + io_operation.operation[1].length;
          io_resultingRange.op = io_operation;
          io_resultingRange.flag = ArrayChangeSetRangeType.completeA;
          return;
        default:
          throw new Error('getRangeForCurrentStateOperation: ' + MSG.UNKNOWN_OPERATION);
        }
      };
      /**
       * computes the impact range for a given operation of the applied change set
       * @param {{type: LYNX.Property.ChangeSetOperations.ArrayOperations.ArrayChangeSetIterator.types,
       *     operation: number[], offset: number}} in_operation the op
       * @param {{begin:Number, end: Number, op:Number[], flag:ArrayChangeSetRangeType}} io_resultingRange
       *     the computed range
       * @param {ArrayChangeSetRangeType} in_flag the flag for the resulting range, default is 'complete B'
       */
      var getRangeForAppliedOperation = function (in_operation, io_resultingRange, in_flag) {
        if (!in_operation || in_operation.type === ArrayChangeSetIterator.types.NOP) {
          io_resultingRange.begin = undefined;
          io_resultingRange.end = undefined;
          io_resultingRange.op = undefined;
          io_resultingRange.flag = undefined;
          return;
        }
        if (!io_resultingRange.op) {
          io_resultingRange.op = {};
        }
        io_resultingRange.op.type = in_operation.type;
        io_resultingRange.op.offset = in_operation.offset;
        if (!io_resultingRange.op.operation) {
          io_resultingRange.op.operation = [];
        }
        io_resultingRange.op.operation[0] = in_operation.operation[0];
        io_resultingRange.begin = in_operation.operation[0];
        io_resultingRange.op._absoluteBegin = in_operation.operation[0];
        if (in_flag !== undefined) {
          io_resultingRange.flag = in_flag;
        } else {
          io_resultingRange.flag = ArrayChangeSetRangeType.completeB;
        }
        switch (in_operation.type) {
        case ArrayChangeSetIterator.types.INSERT:
          io_resultingRange.end = in_operation.operation[0];
          io_resultingRange.op.operation[1] = in_operation.operation[1].slice();
          return;
        case ArrayChangeSetIterator.types.REMOVE:
          var removedElements = _.isNumber(in_operation.operation[1]) ? in_operation.operation[1] : in_operation.operation[1].length;
          io_resultingRange.end = in_operation.operation[0] + removedElements;
          io_resultingRange.op.operation[1] = removedElements;
          return;
        case ArrayChangeSetIterator.types.MODIFY:
          io_resultingRange.end = in_operation.operation[0] + in_operation.operation[1].length;
          io_resultingRange.op.operation[1] = in_operation.operation[1].slice();
          return;
        default:
          throw new Error('getRangeForCurrentStateOperation: ' + MSG.UNKNOWN_OPERATION);
        }
      };
      /**
       * cut overlapping ranges in non-overlapping and completely overlapping segments
       * ranges of length 0 just cut lengthy ranges
       * @param {{begin:Number, end: Number, op:Number[], flag:ArrayChangeSetRangeType}} io_rangeA input A
       * @param {{begin:Number, end: Number, op:Number[], flag:ArrayChangeSetRangeType}} io_rangeB input B
       * @param {{begin:Number, end: Number, op:Number[], flag:ArrayChangeSetRangeType}} io_resultingSegment the
       * resulting overlapping segment
       * @param {boolean} in_rebasing is this function called for rebasing - we have to implement two different
       *     behaviors of this function: one for squashing and one for rebasing, because an insert-insert
       *     operation in squashing should be separte segments, while for rebasing, we need one segment
       *     for both inserts to be able to report a conflict.
       * overlapping range or
       * (partial) A or B
       */
      var splitOverlapping = function (io_rangeA, io_rangeB, io_resultingSegment, in_rebasing) {
        if (io_rangeA.begin === undefined) {
          io_resultingSegment.begin = io_rangeB.begin;
          io_resultingSegment.end = io_rangeB.end;
          io_resultingSegment.op = io_rangeB.op;
          io_resultingSegment.flag = io_rangeB.flag;
          return;
        }
        if (io_rangeB.begin === undefined) {
          io_resultingSegment.begin = io_rangeA.begin;
          io_resultingSegment.end = io_rangeA.end;
          io_resultingSegment.op = io_rangeA.op;
          io_resultingSegment.flag = io_rangeA.flag;
          return;
        }
        if (io_rangeA.end < io_rangeB.begin || (!in_rebasing || io_rangeA.op.type === ArrayChangeSetIterator.types.REMOVE) && io_rangeA.end === io_rangeB.begin) {
          io_resultingSegment.begin = io_rangeA.begin;
          io_resultingSegment.end = io_rangeA.end;
          io_resultingSegment.op = io_rangeA.op;
          io_resultingSegment.flag = io_rangeA.flag;
          return;
        }
        if (io_rangeB.end < io_rangeA.begin || !in_rebasing && io_rangeB.end === io_rangeA.begin) {
          io_resultingSegment.begin = io_rangeB.begin;
          io_resultingSegment.end = io_rangeB.end;
          io_resultingSegment.op = io_rangeB.op;
          io_resultingSegment.flag = io_rangeB.flag;
          return;
        }
        // handle real overlaps:
        if (io_rangeA.begin < io_rangeB.begin) {
          // take A up to the begin of B
          io_resultingSegment.begin = io_rangeA.begin;
          io_resultingSegment.end = io_rangeB.begin;
          // to avoid deepCopy, we just copy the necessary parts of the op:
          io_resultingSegment.op = {
            type: io_rangeA.op.type,
            operation: []
          };
          io_resultingSegment.op.operation[0] = io_rangeA.op.operation[0];
          io_resultingSegment.flag = ArrayChangeSetRangeType.partOfA;
          // cut the remaining segment entry
          if (io_rangeA.op.type === ArrayChangeSetIterator.types.REMOVE) {
            io_rangeA.op.operation[1] -= io_rangeB.begin - io_rangeA.begin;
            io_resultingSegment.op.operation[1] = io_rangeB.begin - io_rangeA.begin;
            io_rangeA.op.offset -= io_rangeB.begin - io_rangeA.begin;
          } else {
            var op1Data = io_rangeA.op.operation[1];
            if (_.isString(op1Data)) {
              io_resultingSegment.op.operation[1] = op1Data.substr(0, io_rangeB.begin - io_rangeA.begin);
              io_rangeA.op.operation[1] = op1Data.substr(io_rangeB.begin - io_rangeA.begin);
            } else {
              io_resultingSegment.op.operation[1] = op1Data.splice(0, io_rangeB.begin - io_rangeA.begin);
            }
            if (io_rangeA.op.type === ArrayChangeSetIterator.types.INSERT) {
              io_rangeA.op.offset += io_rangeB.begin - io_rangeA.begin;
            }
          }
          io_rangeA.begin = io_rangeB.begin;
          io_rangeA.op.operation[0] = io_rangeB.begin;
        } else if (io_rangeA.begin === io_rangeB.begin) {
          // find the largest common range:
          io_resultingSegment.begin = io_rangeA.begin;
          io_resultingSegment.end = undefined;
          // to avoid deepCopy, we just copy the necessary parts of the op:
          io_resultingSegment.opA = {
            type: io_rangeA.op.type,
            operation: [],
            _absoluteBegin: io_rangeA.op._absoluteBegin
          };
          io_resultingSegment.opA.operation[0] = io_rangeA.op.operation[0];
          io_resultingSegment.opB = {
            type: io_rangeB.op.type,
            operation: []
          };
          io_resultingSegment.opB.operation[0] = io_rangeB.op.operation[0];
          // who ends first, A or B?
          if (io_rangeA.end < io_rangeB.end) {
            // segment A ends first, it is consumed by the resulting merged segment!
            // a part of segment B remains and needs updates
            io_resultingSegment.end = io_rangeA.end;
            io_resultingSegment.flag = ArrayChangeSetRangeType.completeApartOfB;
            // cut the remaining segment B entries (segment A needs no update!)
            var lengthUsedInResultSegment = io_rangeA.end - io_rangeA.begin;
            // since io_rangeA.begin === io_rangeB.begin
            if (io_rangeB.op.type === ArrayChangeSetIterator.types.REMOVE) {
              io_resultingSegment.opB.operation[1] = lengthUsedInResultSegment;
              io_rangeB.op.operation[1] = io_rangeB.op.operation[1] - lengthUsedInResultSegment;
            } else {
              var op1Data = io_rangeB.op.operation[1];
              if (_.isString(op1Data)) {
                io_resultingSegment.opB.operation[1] = op1Data.substr(0, lengthUsedInResultSegment);
                io_rangeB.op.operation[1] = op1Data.substr(lengthUsedInResultSegment);
              } else {
                io_resultingSegment.opB.operation[1] = op1Data.splice(0, lengthUsedInResultSegment);
              }
            }
            // just copy opA[1]
            if (io_rangeA.op.type === ArrayChangeSetIterator.types.REMOVE) {
              io_resultingSegment.opA.operation[1] = io_rangeA.op.operation[1];
            } else {
              io_resultingSegment.opA.operation[1] = io_rangeA.op.operation[1].slice();
            }
            io_rangeB.begin = io_rangeA.end;
            io_rangeB.op.operation[0] = io_rangeA.end;
          }
          if (io_rangeA.end > io_rangeB.end) {
            // segment B ends first, it is consumed by the resulting merged segment!
            // a part of segment A remains and needs updates
            io_resultingSegment.end = io_rangeB.end;
            io_resultingSegment.flag = ArrayChangeSetRangeType.completeBpartOfA;
            // cut the remaining segment A entry
            var lengthUsedInResultSegment = io_rangeB.end - io_rangeB.begin;
            // since io_rangeA.begin === io_rangeB.begin
            if (io_rangeA.op.type === ArrayChangeSetIterator.types.REMOVE) {
              io_resultingSegment.opA.operation[1] = lengthUsedInResultSegment;
              io_rangeA.op.operation[1] -= lengthUsedInResultSegment;
              io_rangeA.op.offset -= lengthUsedInResultSegment;
            } else {
              var op1Data = io_rangeA.op.operation[1];
              if (_.isString(op1Data)) {
                io_resultingSegment.opA.operation[1] = op1Data.substr(0, lengthUsedInResultSegment);
                io_rangeA.op.operation[1] = op1Data.substr(lengthUsedInResultSegment);
              } else {
                io_resultingSegment.opA.operation[1] = op1Data.splice(0, lengthUsedInResultSegment);
              }
              if (io_rangeA.op.type === ArrayChangeSetIterator.types.INSERT) {
                io_rangeA.op.offset += lengthUsedInResultSegment;
              }
            }
            // just copy opB[1]
            if (io_rangeB.op.type === ArrayChangeSetIterator.types.REMOVE) {
              io_resultingSegment.opB.operation[1] = io_rangeB.op.operation[1];
            } else {
              io_resultingSegment.opB.operation[1] = io_rangeB.op.operation[1].slice();
            }
            io_rangeA.begin = io_rangeB.end;
            io_rangeA.op.operation[0] = io_rangeB.end;
          }
          if (io_rangeA.end === io_rangeB.end) {
            io_resultingSegment.end = io_rangeB.end;
            io_resultingSegment.flag = ArrayChangeSetRangeType.completeAcompleteB;
            // both are fully used, no cut needed!
            // copy op[1]s
            if (io_rangeA.op.type === ArrayChangeSetIterator.types.REMOVE) {
              io_resultingSegment.opA.operation[1] = io_rangeA.op.operation[1];
            } else {
              io_resultingSegment.opA.operation[1] = io_rangeA.op.operation[1].slice();
            }
            if (io_rangeB.op.type === ArrayChangeSetIterator.types.REMOVE) {
              io_resultingSegment.opB.operation[1] = io_rangeB.op.operation[1];
            } else {
              io_resultingSegment.opB.operation[1] = io_rangeB.op.operation[1].slice();
            }
          }
        } else if (io_rangeB.begin < io_rangeA.begin) {
          // take B up to the begin of A
          io_resultingSegment.begin = io_rangeB.begin;
          io_resultingSegment.end = io_rangeA.begin;
          // to avoid deepCopy, we just copy the necessary parts of the op:
          io_resultingSegment.op = {
            type: io_rangeB.op.type,
            operation: []
          };
          io_resultingSegment.op.operation[0] = io_rangeB.op.operation[0];
          io_resultingSegment.flag = ArrayChangeSetRangeType.partOfB;
          // cut the remaining segment entry
          if (io_rangeB.op.type === ArrayChangeSetIterator.types.REMOVE) {
            io_rangeB.op.operation[1] -= io_rangeA.begin - io_rangeB.begin;
            io_resultingSegment.op.operation[1] = io_rangeA.begin - io_rangeB.begin;
          } else {
            var op1Data = io_rangeB.op.operation[1];
            if (_.isString(op1Data)) {
              io_resultingSegment.op.operation[1] = op1Data.substr(0, io_rangeA.begin - io_rangeB.begin);
              io_rangeB.op.operation[1] = op1Data.substr(io_rangeA.begin - io_rangeB.begin);
            } else {
              io_resultingSegment.op.operation[1] = op1Data.splice(0, io_rangeA.begin - io_rangeB.begin);
            }
          }
          io_rangeB.begin = io_rangeA.begin;
          io_rangeB.op.operation[0] = io_rangeA.begin;
          io_rangeB.op.offset = 0;
        }
      };
      /**
       * merge in_op with the last op of that category in io_changeset (if possible)
       * e.g. merge an delete [1,3] with delete [3,2] to delete [1,5]
       * @param {{type: LYNX.Property.ChangeSetOperations.ArrayOperations.ArrayChangeSetIterator.types,
       *     operation: number[], offset: number}} in_op - the op to merge
       * @param {LYNX.Property.SerializedChangeSet} io_changeset - the changeset to merge the op to
       * @param {Number} in_targetIndex the transformed target index offset
       * @return {boolean} true if the merge was possible and executed
       */
      var mergeWithLastIfPossible = function (in_op, io_changeset, in_targetIndex) {
        var lastOp;
        switch (in_op.type) {
        case ArrayChangeSetIterator.types.INSERT: {
            if (io_changeset.insert.length === 0) {
              return false;
            }
            lastOp = io_changeset.insert[io_changeset.insert.length - 1];
            if (lastOp[0] === in_targetIndex) {
              // merge with last insert
              lastOp[1] = lastOp[1].concat(in_op.operation[1]);
            } else {
              return false;
            }
          }
          break;
        case ArrayChangeSetIterator.types.REMOVE: {
            if (io_changeset.remove.length === 0) {
              return false;
            }
            lastOp = io_changeset.remove[io_changeset.remove.length - 1];
            if (lastOp[0] + lastOp[1] === in_targetIndex) {
              if (io_changeset.insert.length > 0) {
                var lastInsertOp = io_changeset.insert[io_changeset.insert.length - 1];
                if (lastInsertOp[0] === in_targetIndex) {
                  // don't create overlapping ranges!
                  // move lastInsertOp to the beginning of the merged remove instead
                  lastInsertOp[0] = lastOp[0];
                  // test, if lastInsertOp now needs to be united with another insert op at this index
                  for (var i = io_changeset.insert.length - 2; i >= 0; i--) {
                    // if yes, remove lastInsertOp and merge with existing instead
                    if (io_changeset.insert[i][0] === lastInsertOp[0]) {
                      // hit!!
                      io_changeset.insert[i][1] = io_changeset.insert[i][1].concat(lastInsertOp[1]);
                      io_changeset.insert.splice(-1, 1);  // remove it
                    } else if (io_changeset.insert[i][0] < lastInsertOp[0]) {
                      break;  // early termination
                    }
                  }
                }
              }
              // merge with last remove
              lastOp[1] += in_op.operation[1];
            } else {
              return false;
            }
          }
          break;
        case ArrayChangeSetIterator.types.MODIFY:
          if (io_changeset.modify.length === 0) {
            return false;
          }
          lastOp = io_changeset.modify[io_changeset.modify.length - 1];
          if (lastOp[0] + lastOp[1].length === in_targetIndex) {
            // merge with last modify
            lastOp[1] = lastOp[1].concat(in_op.operation[1]);
          } else {
            return false;
          }
          break;
        default:
          throw new Error('pushOp: ' + MSG.UNKNOWN_OPERATION);
        }
        return true;
      };
      /**
       * push an operation to a changeset, will try to merge the op if possible
       * @param {{type: LYNX.Property.ChangeSetOperations.ArrayOperations.ArrayChangeSetIterator.types,
       *     operation: number[], offset: number}} in_op the operation we want to push
       * @param {LYNX.Property.SerializedChangeSet} io_changeset target
       * @param {number} in_indexOffset the current offset
       */
      var pushOp = function (in_op, io_changeset, in_indexOffset) {
        var writeTargetIndex;
        if (ArrayChangeSetIterator.types.NOP !== in_op.type) {
          writeTargetIndex = in_op.operation[0] - in_indexOffset;
          if (writeTargetIndex < 0) {
            writeTargetIndex = 0;  // TODO: investigate negative index! (LYNXDEV-861)
          }
        }
        switch (in_op.type) {
        case ArrayChangeSetIterator.types.INSERT: {
            // we may have to move the index, in the case when we fall into an remove
            // check if we have a remove around us:
            if (io_changeset.remove.length > 0) {
              var lastRemoveOp = io_changeset.remove[io_changeset.remove.length - 1];
              if (writeTargetIndex > lastRemoveOp[0] && writeTargetIndex <= lastRemoveOp[0] + lastRemoveOp[1]) {
                // don't create overlapping ranges!
                // we have to move the insert to the removes boundaries!
                writeTargetIndex = lastRemoveOp[0];
              }
            }
            if (!mergeWithLastIfPossible(in_op, io_changeset, writeTargetIndex)) {
              io_changeset.insert.push([
                writeTargetIndex,
                in_op.operation[1]
              ]);
            }
          }
          break;
        case ArrayChangeSetIterator.types.REMOVE: {
            if (!mergeWithLastIfPossible(in_op, io_changeset, writeTargetIndex)) {
              // our segmentation method currently can produce length zero remove segments
              // this is by principle and filtered out here
              var length = !_.isNumber(in_op.operation[1]) ? in_op.operation[1].length : in_op.operation[1];
              if (length > 0) {
                io_changeset.remove.push([
                  writeTargetIndex,
                  in_op.operation[1]
                ]);
              }
            }
          }
          break;
        case ArrayChangeSetIterator.types.MODIFY: {
            if (!mergeWithLastIfPossible(in_op, io_changeset, writeTargetIndex)) {
              io_changeset.modify.push([
                writeTargetIndex,
                in_op.operation[1]
              ]);
            }
          }
          break;
        case ArrayChangeSetIterator.types.NOP: {
          }
          break;
        default:
          throw new Error('pushOp: ' + MSG.UNKNOWN_OPERATION);
        }
      };
      /**
       * handle combinations of range operations
       * e.g. an insert and delete at the same place and same length nullify each other
       * @param {{opA:{}, opB:{}}} in_segment the two ops to be combined
       * @param {boolean} in_isPrimitiveType is it an array of primitive types
       * ATTENTION: We overwrite opB to save garbage (instead of creating a result OP)
       */
      var handleCombinations = function (in_segment, in_isPrimitiveType) {
        var opA = in_segment.opA;
        var opB = in_segment.opB;
        switch (opA.type) {
        case ArrayChangeSetIterator.types.INSERT: {
            switch (opB.type) {
            case ArrayChangeSetIterator.types.INSERT: {
                // this combination is not reachable since this case has already been handled before
                console.error('this combination should not occur in handleCombinations - this is a bug');
                break;
              }
            case ArrayChangeSetIterator.types.REMOVE: {
                // Attention: B removes A completely, kill A to avid zero inserts
                if (opB.operation[1] !== opA.operation[1].length) {
                  throw new Error('handleCombinations: insert-remove: unequal number of affected entries');
                }
                opB.type = ArrayChangeSetIterator.types.NOP;
                opB.operation = null;
                break;
              }
            case ArrayChangeSetIterator.types.MODIFY: {
                // we have to apply modify of B to As insert
                if (in_isPrimitiveType) {
                  // since the length of A and B is equal in here
                  // we can just insert the modified values instead
                  opB.type = ArrayChangeSetIterator.types.INSERT;
                } else {
                  // the array element is a complex types
                  // we have to recursively call the modify
                  for (var i = 0; i < opB.operation[1].length; ++i) {
                    // TypeIds MUST be stored in the entries
                    console.assert(opA.operation[1][i].typeid);
                    this._performApplyAfterOnPropertyWithTypeid(i, opA.operation[1], opB.operation[1], opA.operation[1][i].typeid, false);
                  }
                  opB.operation = opA.operation;
                  opB.type = ArrayChangeSetIterator.types.INSERT;
                }
                break;
              }
            default:
              throw new Error('handleCombinations: ' + MSG.UNKNOWN_OPERATION);
            }
            break;
          }
        case ArrayChangeSetIterator.types.REMOVE: {
            // this combination is not reachable since this case has already been handled before
            console.error('this combination should not occur in handleCombinations - this is a bug');
            break;
          }
        case ArrayChangeSetIterator.types.MODIFY: {
            if (in_isPrimitiveType) {
              // just use opB in any case
              break;
            } else {
              // we have to deal with complex types here!
              if (opB.type === ArrayChangeSetIterator.types.MODIFY) {
                for (var i = 0; i < opB.operation[1].length; ++i) {
                  // TypeIds MUST be stored in the entries
                  console.assert(opA.operation[1][i].typeid);
                  this._performApplyAfterOnPropertyWithTypeid(i, opA.operation[1], opB.operation[1], opA.operation[1][i].typeid, false);
                }
                opB.operation = opA.operation;
              }
              break;
            }
          }
        default:
          throw new Error('handleCombinations: ' + MSG.UNKNOWN_OPERATION);
        }
      };
      /**
       * handle compinations of range operations
       * e.g. an insert and delete at the same place and same length nullify each other
       * @param {{opA:{}, opB:{}}} in_segment the two ops to be combined
       * @param {Array.<LYNX.Property.ChangeSet.ConflictInfo>} out_conflicts -
       *     A list of paths that resulted in conflicts together with the type of the conflict
       * @param {string} in_basePath -
       *     Base path to get to the property processed by this function
       * @param {boolean} in_isPrimitiveType is it an array of primitive types
       *
       * ATTENTION: We overwrite opB to save garbage (instead of creating a result OP)
       *
       * We have to handle the conflicting rebase changes. The changes we do, are summarized in this table.
       * Other is the modified, rebased (on own) changeset.
       *                   BASE
       *                  /    \
       *                 /      \
       *               OWN      OTHER
       *
       * gets rebased to:
       *
       *                 BASE
       *                  /
       *               OWN
       *                  \
       *                OTHER
       *
       * conflict default behavior in ()
       *
       * -------|-----------------+------------------+------------------|
       *    \Own|    insert       |       modify     |     remove       |
       *     \  |                 |                  |                  |
       * other\ |                 |                  |                  |
       * ------\|-----------------+------------------+------------------|
       *        | conflicting     | non-conflicting  | non-conflicting  |
       * insert | inserts         | change           | change           |
       *        | (i. other after)|                  |                  |
       * -------|-----------------+------------------+------------------|
       *        | non-conflicting | merge recursively| conflict         |
       * modify | change          | (note the user)  | (delete modify   |
       *        |                 |                  | in other)        |
       *        |                 |                  |                  |
       * -------|-----------------+------------------+------------------|
       *        | non-conflicting | non-conflicting  | non-conflicting  |
       * remove | change          | change           | change           |
       *        | [rem orig. data]| (note the user)  | [rem dupl. rem]  |
       * -------|-----------------+------------------+------------------|
       */
      var handleRebaseCombinations = function (in_segment, out_conflicts, in_basePath, in_isPrimitiveType) {
        var opA = in_segment.opA;
        var opB = in_segment.opB;
        switch (opA.type) {
        case ArrayChangeSetIterator.types.INSERT: {
            switch (opB.type) {
            case ArrayChangeSetIterator.types.INSERT: {
                // conflicting inserts - report conflict, insert both
                delete opA._absoluteBegin;
                var conflict = {
                  path: in_basePath,
                  // TODO: We have to report the range or per element (LYNXDEV-863)
                  type: ChangeSet.ConflictType.INSERTED_ENTRY_WITH_SAME_KEY,
                  // todo
                  conflictingChange: opB
                };
                out_conflicts.push(conflict);
                // move to the right side of the insert
                opB.operation[0] += opA.operation[1].length;
                break;
              }
            case ArrayChangeSetIterator.types.REMOVE: {
                // non-conflicting insert - just keep B
                break;
              }
            case ArrayChangeSetIterator.types.MODIFY: {
                // non-conflicting insert - just keep B
                break;
              }
            default:
              throw new Error('handleCombinations: ' + MSG.UNKNOWN_OPERATION);
            }
            break;
          }
        case ArrayChangeSetIterator.types.REMOVE: {
            switch (opB.type) {
            case ArrayChangeSetIterator.types.INSERT: {
                // - keep the insert, if it's the beginning or last element
                // - else discard the insert (it's inside the removed range) and report
                if (opA._absoluteBegin !== opA.operation[0]) {
                  delete opA._absoluteBegin;
                  opB.type = ArrayChangeSetIterator.types.NOP;
                  opB.operation = null;
                  var conflict = {
                    path: in_basePath,
                    // TODO: We have to report the range or per element (LYNXDEV-863)
                    type: ChangeSet.ConflictType.INSERT_IN_REMOVED_RANGE,
                    conflictingChange: deepCopy(opB)
                  };
                  out_conflicts.push(conflict);
                }
                break;
              }
            case ArrayChangeSetIterator.types.REMOVE: {
                // Remove already in A, no need to add the same again -> write nop
                if (opB.operation[1] !== opA.operation[1]) {
                  throw new Error('handleRebaseCombinations: remove-remove: unequal number of affected entries, ' + 'this should never happen! Probably a bug in splitRange.');
                }
                opB.type = ArrayChangeSetIterator.types.NOP;
                opB.operation = null;
                break;
              }
            case ArrayChangeSetIterator.types.MODIFY: {
                // trying to modify something that was removed ->
                // replace the modify with a NOP and report a conflict
                if (opB.operation[1].length > 0) {
                  delete opA._absoluteBegin;
                  var conflict = {
                    path: in_basePath,
                    // TODO: We have to report the range or per element (LYNXDEV-863)
                    type: ChangeSet.ConflictType.ENTRY_MODIFIED_AFTER_REMOVE,
                    conflictingChange: deepCopy(opB)
                  };
                  out_conflicts.push(conflict);
                }
                opB.type = ArrayChangeSetIterator.types.NOP;
                opB.operation = null;
                break;
              }
            default:
              throw new Error('handleCombinations: ' + MSG.UNKNOWN_OPERATION);
            }
            break;
          }
        case ArrayChangeSetIterator.types.MODIFY: {
            if (in_isPrimitiveType) {
              // just use opB and notify accordingly
              if (opB.type === ArrayChangeSetIterator.types.MODIFY && opB.operation[1].length > 0) {
                delete opA._absoluteBegin;
                var conflict = {
                  path: in_basePath,
                  // TODO: We have to report the range or per element (LYNXDEV-863)
                  type: ChangeSet.ConflictType.COLLIDING_SET,
                  conflictingChange: deepCopy(opB)
                };
                out_conflicts.push(conflict);
              }
              if (opB.type === ArrayChangeSetIterator.types.REMOVE && opB.operation[1] > 0) {
                delete opA._absoluteBegin;
                var conflict = {
                  path: in_basePath,
                  // TODO: We have to report the range or per element (LYNXDEV-863)
                  type: ChangeSet.ConflictType.REMOVE_AFTER_MODIFY,
                  conflictingChange: deepCopy(opB)
                };
                out_conflicts.push(conflict);
              }
              break;
            } else {
              // we have to deal with complex types here!
              if (opB.type === ArrayChangeSetIterator.types.MODIFY) {
                for (var i = 0; i < opB.operation[1].length; ++i) {
                  console.assert(opA.operation[1][i].typeid);
                  this._rebaseChangeSetForPropertyEntryWithTypeid(i, opA.operation[1], opB.operation[1], opA.operation[1][i].typeid, in_basePath + '[' + i + ']', false, out_conflicts);
                }
                opB.operation = opA.operation;
              }
              break;
            }
          }
        default:
          throw new Error('handleCombinations: ' + MSG.UNKNOWN_OPERATION);
        }
      };
      /**
       * apply a range's operation to the changeset
       * @param {{begin:Number, end: Number, op:Number[], flag:ArrayChangeSetRangetype}} in_segment to be applied
       * @param {LYNX.Property.SerializedChangeSet} io_changeset target
       * @param {number} in_currentIndexOffset current offset
       * @param {boolean} in_isPrimitiveType is it an array of primitive types
       */
      var applySegment = function (in_segment, io_changeset, in_currentIndexOffset, in_isPrimitiveType) {
        if (!in_segment) {
          throw Error('applySegment: in_segment is undefined!');
        }
        if (in_segment.flag === ArrayChangeSetRangeType.completeA || in_segment.flag === ArrayChangeSetRangeType.completeB || in_segment.flag === ArrayChangeSetRangeType.partOfA || in_segment.flag === ArrayChangeSetRangeType.partOfB) {
          // just push it
          pushOp(in_segment.op, io_changeset, in_currentIndexOffset);
        } else {
          // combinations: pAB, AB or ApB
          handleCombinations.call(this, in_segment, in_isPrimitiveType);
          // modifies in_segment.opB to save garbage
          pushOp(in_segment.opB, io_changeset, in_currentIndexOffset);
        }
      };
      /**
       * apply a range's operation to the rebased changeset
       * @param {{begin:Number, end: Number, op:Number[], flag:ArrayChangeSetRangetype}} in_segment to be applied
       * @param {LYNX.Property.SerializedChangeSet} io_changeset target
       * @param {number} in_currentIndexOffset current offset
       * @param {Array.<LYNX.Property.ChangeSet.ConflictInfo>} out_conflicts -
       *     A list of paths that resulted in conflicts together with the type of the conflict
       * @param {string} in_basePath -
       *     Base path to get to the property processed by this function
       * @param {boolean} in_isPrimitiveType is it an array of primitive types
       */
      var applyRebaseSegment = function (in_segment, io_changeset, in_currentIndexOffset, out_conflicts, in_basePath, in_isPrimitiveType) {
        if (!in_segment) {
          throw Error('applySegment: in_segment is undefined!');
        }
        if (in_segment.flag === ArrayChangeSetRangeType.completeB || in_segment.flag === ArrayChangeSetRangeType.partOfB) {
          // not touching anything of A, just push it
          pushOp(in_segment.op, io_changeset, in_currentIndexOffset);
        } else if (in_segment.flag === ArrayChangeSetRangeType.completeA || in_segment.flag === ArrayChangeSetRangeType.partOfA) {
        } else {
          // combinations: pAB, AB or ApB
          handleRebaseCombinations.call(this, in_segment, out_conflicts, in_basePath, in_isPrimitiveType);
          // modifies in_segment.opB to save garbage
          pushOp(in_segment.opB, io_changeset, in_currentIndexOffset);
        }
      };
      var ChangeSetArrayFunctions = {
        /**
         * Applies a changeset to a given array property. The ChangeSet is assumed to be relative to the same
         * property root and it will be applied behind the base ChangeSet (assuming that the changes are relative to the
         * state after the base ChangeSet has been applied. It will change the base ChangeSet.)
         *
         * @param {LYNX.Property.SerializedChangeSet} io_basePropertyChanges    - The ChangeSet describing the initial state
         * @param {LYNX.Property.SerializedChangeSet} in_appliedPropertyChanges - The ChangeSet to apply to this state
         * @param {string}                            in_typeid                 - The typeid of the contents of the
         *                                                                        collection (without the collection type)
         * @private
         */
        _performApplyAfterOnPropertyArray: function (io_basePropertyChanges, in_appliedPropertyChanges, in_typeid) {
          console.assert(in_typeid, '_performApplyAfterOnPropertyArray: typeid missing');
          console.assert(!_.isString(io_basePropertyChanges), io_basePropertyChanges);
          console.assert(!_.isString(in_appliedPropertyChanges), in_appliedPropertyChanges);
          var isPrimitiveType = TypeIdHelper.isPrimitiveType(in_typeid);
          // Iterator to process the changes in the ChangeSet in the correct order
          var iteratorA = new ArrayChangeSetIterator(io_basePropertyChanges);
          var iteratorB = new ArrayChangeSetIterator(in_appliedPropertyChanges);
          var rangeA = {};
          getRangeForCurrentStateOperation(iteratorA, iteratorA.offset ? iteratorA.offset : 0, rangeA);
          var rangeB = {};
          getRangeForAppliedOperation(iteratorB, rangeB);
          var resultPropertyChanges = {};
          resultPropertyChanges.insert = [];
          resultPropertyChanges.modify = [];
          resultPropertyChanges.remove = [];
          resultPropertyChanges.writeOffset = 0;
          var currentIndexOffset = 0;
          var segment = {};
          // create ranges for A and B: A is the current state and B is the change set to be applied
          while (!iteratorA.atEnd() || !iteratorB.atEnd()) {
            // produce first segment:
            splitOverlapping(rangeA, rangeB, segment, false);
            applySegment.call(this, segment, resultPropertyChanges, currentIndexOffset, isPrimitiveType);
            // increase pointers if necessary
            if (segment.flag === ArrayChangeSetRangeType.completeA || segment.flag === ArrayChangeSetRangeType.completeApartOfB) {
              var moreAs = iteratorA.next();
              getRangeForCurrentStateOperation(iteratorA, moreAs ? iteratorA.offset : 0, rangeA);
            }
            if (segment.flag === ArrayChangeSetRangeType.completeB || segment.flag === ArrayChangeSetRangeType.completeBpartOfA) {
              iteratorB.next();
              getRangeForAppliedOperation(iteratorB, rangeB);
            }
            if (segment.flag === ArrayChangeSetRangeType.completeAcompleteB) {
              var moreAs = iteratorA.next();
              getRangeForCurrentStateOperation(iteratorA, moreAs ? iteratorA.offset : 0, rangeA);
              iteratorB.next();
              getRangeForAppliedOperation(iteratorB, rangeB);
            }
            if (iteratorA.offset !== undefined) {
              // the correct index offset for the next operation is given by A's offset
              currentIndexOffset = iteratorA.offset;
            }
          }
          // write back:
          if (resultPropertyChanges.insert.length > 0) {
            io_basePropertyChanges.insert = resultPropertyChanges.insert;
          } else {
            delete io_basePropertyChanges.insert;
          }
          if (resultPropertyChanges.modify.length > 0) {
            io_basePropertyChanges.modify = resultPropertyChanges.modify;
          } else {
            delete io_basePropertyChanges.modify;
          }
          if (resultPropertyChanges.remove.length > 0) {
            io_basePropertyChanges.remove = resultPropertyChanges.remove;
          } else {
            delete io_basePropertyChanges.remove;
          }
        },
        /**
         * Performs the rebase operation for array changes
         *
         * @param {LYNX.Property.SerializedChangeSet} in_ownPropertyChangeSet -
         *     The ChangeSet for the property stored in this object
         * @param {LYNX.Property.SerializedChangeSet} io_rebasePropertyChangeSet -
         *     The ChangeSet for the property to be rebased
         * @param {string} in_basePath -
         *     Base path to get to the property processed by this function
         * @param {Array.<LYNX.Property.ChangeSet.ConflictInfo>} out_conflicts -
         *     A list of paths that resulted in conflicts together with the type of the conflict
         * @param {string} in_typeid - The typeid of the contents of the
         *                            collection (without the collection type)
         * @private
         */
        _rebaseArrayChangeSetForProperty: function (in_ownPropertyChangeSet, io_rebasePropertyChangeSet, in_basePath, out_conflicts, in_typeid) {
          var isPrimitiveType = TypeIdHelper.isPrimitiveType(in_typeid);
          // Iterator to process the changes in the ChangeSet in the correct order
          var iteratorA = new ArrayChangeSetIterator(in_ownPropertyChangeSet);
          var iteratorB = new ArrayChangeSetIterator(io_rebasePropertyChangeSet);
          var rangeA = {};
          getRangeForAppliedOperation(iteratorA, rangeA, ArrayChangeSetRangeType.completeA);
          var rangeB = {};
          getRangeForAppliedOperation(iteratorB, rangeB);
          var resultPropertyChanges = {};
          resultPropertyChanges.insert = [];
          resultPropertyChanges.modify = [];
          resultPropertyChanges.remove = [];
          resultPropertyChanges.writeOffset = 0;
          var currentIndexOffset = 0;
          var segment = {};
          // create ranges for A and B: A is the current state and B is the change set to be applied
          while (!iteratorA.atEnd() || !iteratorB.atEnd()) {
            splitOverlapping(rangeA, rangeB, segment, true);
            applyRebaseSegment.call(this, segment, resultPropertyChanges, currentIndexOffset, out_conflicts, in_basePath, isPrimitiveType);
            // increase pointers if necessary
            if (segment.flag === ArrayChangeSetRangeType.completeA || segment.flag === ArrayChangeSetRangeType.completeApartOfB) {
              iteratorA.next();
              getRangeForAppliedOperation(iteratorA, rangeA, ArrayChangeSetRangeType.completeA);
            }
            if (segment.flag === ArrayChangeSetRangeType.completeB || segment.flag === ArrayChangeSetRangeType.completeBpartOfA) {
              iteratorB.next();
              getRangeForAppliedOperation(iteratorB, rangeB);
            }
            if (segment.flag === ArrayChangeSetRangeType.completeAcompleteB) {
              iteratorA.next();
              getRangeForAppliedOperation(iteratorA, rangeA, ArrayChangeSetRangeType.completeA);
              iteratorB.next();
              getRangeForAppliedOperation(iteratorB, rangeB);
            }
            if (iteratorA.offset !== undefined) {
              // the correct index offset for the next operation is given by A's offset
              currentIndexOffset = -iteratorA.offset;
            }
          }
          // write back:
          if (resultPropertyChanges.insert.length > 0) {
            io_rebasePropertyChangeSet.insert = resultPropertyChanges.insert;
          } else {
            delete io_rebasePropertyChangeSet.insert;
          }
          if (resultPropertyChanges.modify.length > 0) {
            io_rebasePropertyChangeSet.modify = resultPropertyChanges.modify;
          } else {
            delete io_rebasePropertyChangeSet.modify;
          }
          if (resultPropertyChanges.remove.length > 0) {
            io_rebasePropertyChangeSet.remove = resultPropertyChanges.remove;
          } else {
            delete io_rebasePropertyChangeSet.remove;
          }
        },
        /**
         * Performs the rebase operation for string changes
         *
         * @param {LYNX.Property.SerializedChangeSet} in_ownPropertyChangeSet -
         *     The ChangeSet for the property stored in this object
         * @param {Array.<LYNX.Property.SerializedChangeSet>} io_rebasePropertyChangeSetParent -
         *     The Array containing the ChangeSet for the property to be rebased
         * @param {string} in_key the key to the ChangeSet in io_rebasePropertyChangeSetParent we are
         *     rebasing on
         * @param {string} in_basePath -
         *     Base path to get to the property processed by this function
         * @param {Array.<LYNX.Property.ChangeSet.ConflictInfo>} out_conflicts -
         *     A list of paths that resulted in conflicts together with the type of the conflict
         *
         * @private
         *
         * We have to handle the conflicting rebase changes. The changes we do, are summarized in this table.
         * Other is the modified, rebased (on own) changeset.
         *                   BASE
         *                  /    \
         *                 /      \
         *               OWN      OTHER
         *
         * gets rebased to:
         *
         *                 BASE
         *                  /
         *               OWN
         *                  \
         *                OTHER
         *
         * conflict default behavior in ()
         *
         * -------|-----------------+------------------+------------------|----------------|
         *    \Own|    insert       |       modify     |     remove       |   String set   |
         *     \  |                 |                  |                  |                |
         * other\ |                 |                  |                  |                |
         * ------\|-----------------+------------------+------------------|----------------|
         *        | conflicting     | non-conflicting  | non-conflicting  |   conflict     |
         * insert | inserts         | change           | change           |(ignore insert) |
         *        | (i. other after)|                  |                  |                |
         * -------|-----------------+------------------+------------------|----------------|
         *        | non-conflicting |     conflict     | conflict         |   conflict     |
         * modify | change          |(notify the user) | (delete modify   |(ignore modify) |
         *        |                 |                  | in other)        |                |
         * -------|-----------------+------------------+------------------|----------------|
         *        | non-conflicting | non-conflicting  | non-conflicting  |   conflict     |
         * remove | change          | change           | change           |(ignore remove) |
         *        | [rem orig. data]|(notify the user) | [rem dupl. rem]  |                |
         * -------|-----------------+------------------+------------------+----------------|
         *  Str.  |                 |              conflict               |                |
         *  set   |           'other's set overwrites whatever happend before              |
         *        |                 |                  |                  |                |
         * --------------------------------------------------------------------------------|
        */
        _rebaseChangeSetForString: function (in_ownPropertyChangeSet, io_rebasePropertyChangeSetParent, in_key, in_basePath, out_conflicts) {
          if (_.isString(io_rebasePropertyChangeSetParent[in_key]) || io_rebasePropertyChangeSetParent[in_key].hasOwnProperty('value')) {
            // other overwrites any old changes, we ignore them and report the conflict
            var conflict = {
              path: in_basePath,
              type: ChangeSet.ConflictType.COLLIDING_SET,
              conflictingChange: deepCopy(in_ownPropertyChangeSet)
            };
            out_conflicts.push(conflict);
          } else if (_.isString(in_ownPropertyChangeSet) || in_ownPropertyChangeSet.hasOwnProperty('value')) {
            // we have a conflict since we cannot allow insert/remove/modify on an unknown state
            // we just ignor other's modifications and take own's set
            var conflict = {
              path: in_basePath,
              type: ChangeSet.ConflictType.COLLIDING_SET,
              conflictingChange: deepCopy(io_rebasePropertyChangeSetParent[in_key])
            };
            out_conflicts.push(conflict);
            io_rebasePropertyChangeSetParent[in_key] = in_ownPropertyChangeSet;
          } else {
            // both have no 'set' just array ops -> use array rebase!
            this._rebaseArrayChangeSetForProperty(in_ownPropertyChangeSet, io_rebasePropertyChangeSetParent[in_key], in_basePath, out_conflicts, 'String');
          }
        }
      };
      ChangeSet.prototype._performApplyAfterOnPropertyArray = ChangeSetArrayFunctions._performApplyAfterOnPropertyArray;
      ChangeSet.prototype._rebaseArrayChangeSetForProperty = ChangeSetArrayFunctions._rebaseArrayChangeSetForProperty;
      ChangeSet.prototype._rebaseChangeSetForString = ChangeSetArrayFunctions._rebaseChangeSetForString;
      exportModule('LYNX.Property.ChangeSetOperations.ArrayOperations.ArrayChangeSetIterator', ArrayChangeSetIterator);
      exportModule('LYNX.Property.ChangeSetOperations.ArrayOperations.ChangeSetArrayFunctions', ChangeSetArrayFunctions);
    }());
    (function () {
      var deepCopy = include('LYNX.Utils.deepCopy');
      var ChangeSet = include('LYNX.Property.ChangeSet');
      var _ = include('underscore');
      var PathHelper = include('LYNX.Property.PathHelper');
      var PROPERTY_PATH_DELIMITER = include('LYNX.Property._constants.PROPERTY_PATH_DELIMITER');
      var TypeIdHelper = include('LYNX.Property.TypeIdHelper');
      var Property = include('LYNX.Property', [
        'IndexedCollectionBaseProperty',
        'ArrayProperty'
      ]);
      var ArrayChangeSetIterator = include('LYNX.Property.ChangeSetOperations.ArrayOperations.ArrayChangeSetIterator');
      /**
       * Utils
       * @alias LYNX.Property.Utils
       * @class
       */
      var Utils = {};
      /**
       * Provides traversal information when parsing ChangeSets via the traverseChangeSetRecursively function.
       * @alias LYNX.Utils.TraversalContext
       * @constructor
       * @private
       */
      Utils.TraversalContext = function () {
        this._fullPath = '';
        this._lastSegment = '';
        this._lastSegmentString = '';
        this._typeid = undefined;
        this._splitTypeId = undefined;
        this._workspace = undefined;
        this._userData = undefined;
        this._traversalStopped = false;
        this._nestedChangeSet = undefined;
        this._parentNestedChangeSet = undefined;
        this._propertyContainerType = 'root';
        this._arrayLocalIndex = undefined;
        this._arrayOperationIndex = undefined;
        this._arrayOperationOffset = undefined;
        this._arrayIteratorOffset = undefined;
        this._fullPostPath = '';
        // By default, operations are modify operations
        this._operationType = 'modify';
      };
      /**
       * Stop the traversal for all nodes below the currently processed one
       * @private
       */
      Utils.TraversalContext.prototype.stopTraversal = function () {
        this._traversalStopped = true;
      };
      /**
       * Returns the operation type
       * @return {string} one of 'insert', 'modify' or 'remove'
       * @private
       */
      Utils.TraversalContext.prototype.getOperationType = function () {
        return this._operationType;
      };
      /**
       * Returns the full path to the currently visited ChangeSet
       * (from the root of the ChangeSet)
       * @return {string} The full path
       * @private
       */
      Utils.TraversalContext.prototype.getFullPath = function () {
        return this._fullPath;
      };
      /**
       * Returns the index of the last segment, either a string with the key or a number with the position in the array
       * @private
       * @return {string|number} The last segment index
       */
      Utils.TraversalContext.prototype.getLastSegment = function () {
        return this._lastSegment;
      };
      /**
       * Returns the typeid of the currently visited Property
       * @return {string|undefined} The typeid or undefined, if the current operation type is 'remove'
       * @private
       */
      Utils.TraversalContext.prototype.getTypeid = function () {
        return this._typeid;
      };
      /**
       * The ChangeSet that should be applied to the currently visited node.
       *
       * If this is a remove operation, it is the whole ChangeSet of the removal operation
       * @return {LYNX.Property.SerializedChangeSet} The ChangeSet
       * @private
       */
      Utils.TraversalContext.prototype.getNestedChangeSet = function () {
        return this._nestedChangeSet;
      };
      /**
       * Replace the current nested ChangeSet by another one
       *
       * @param {LYNX.Property.SerializedChangeSet} in_newNestedChangeset The new content
       * @private
       */
      Utils.TraversalContext.prototype.replaceNestedChangeSet = function (in_newNestedChangeset) {
        var parent = this.getParentNestedChangeSet();
        if (this.getPropertyContainerType() === 'template') {
          parent[this.getTypeid()][this.getLastSegment()] = in_newNestedChangeset;
        } else if (this.getPropertyContainerType() === 'NodeProperty') {
          parent[this.getOperationType()][this.getTypeid()][this.getLastSegment()] = in_newNestedChangeset;
        } else {
          console.warn('replaceNestedChangeSet: not implemented. type: ', this.getPropertyContainerType());
        }
      };
      /**
       * Get the ChangeSet of the parent that contains the currently visited node.
       *
       * @return {LYNX.Property.SerializedChangeSet} The parent ChangeSet
       * @private
       */
      Utils.TraversalContext.prototype.getParentNestedChangeSet = function () {
        return this._parentNestedChangeSet;
      };
      /**
       * Returns the split Typeid as returned by TypeIdHelper.extractContext
       * @return {{typeid: string, context:string, isEnum:boolean}} The split typeid
       * @private
       */
      Utils.TraversalContext.prototype.getSplitTypeID = function () {
        return this._splitTypeId;
      };
      /**
       * Sets user data, which will be passed to the recursive calls within this scope
       *
       * @param {*} in_userData - The user data
       * @private
       */
      Utils.TraversalContext.prototype.setUserData = function (in_userData) {
        this._userData = in_userData;
      };
      /**
       * Returns the user data set by the calling function
       *
       * @return {*} The user data
       * @private
       */
      Utils.TraversalContext.prototype.getUserData = function () {
        return this._userData;
      };
      /**
       * Returns the workspace object that was supplied in the initial callback
       *
       * @return {*} The workspace
       * @private
       */
      Utils.TraversalContext.prototype.getWorkspace = function () {
        return this._workspace;
      };
      /**
       * Clones the current Traversal Object
       * @return {LYNX.Utils.TraversalContext} The cloned object
       * @private
       */
      Utils.TraversalContext.prototype.clone = function () {
        var result = new Utils.TraversalContext();
        _.extend(result, this);
        return result;
      };
      /**
       * Returns the type of the property this property is contained within.
       * It can be one of ['NodeProperty', 'map', 'array', 'set', 'template', 'root']
       *
       * @return {String} The type of the property container
       * @private
       */
      Utils.TraversalContext.prototype.getPropertyContainerType = function () {
        return this._propertyContainerType;
      };
      /**
       * If this is an array operation, it returns the index of this operations
       *
       * @return {Number} The index
       * @private
       */
      Utils.TraversalContext.prototype.getArrayOperationIndex = function () {
        return this._arrayOperationIndex;
      };
      /**
       * If this is an array operation, it returns the offset of this operations
       *
       * @return {Number} The index
       * @private
       */
      Utils.TraversalContext.prototype.getArrayOperationOffset = function () {
        return this._arrayOperationOffset;
      };
      /**
       * If this is an array operation, it returns the local index of this entry within the array of entries
       * of the currently processed operation
       *
       * @return {Number} The index
       * @private
       */
      Utils.TraversalContext.prototype.getArrayLocalIndex = function () {
        return this._arrayLocalIndex;
      };
      /**
       * If this is an array operation, it returns the offset of this operation as defined by the internal
       * ArrayIteratorOffset of the currently processed operation
       * @private
       * @return {Number} The offset
       */
      Utils.TraversalContext.prototype.getArrayIteratorOffset = function () {
        return this._arrayIteratorOffset;
      };
      /**
       * If this is an array operation, it returns the valid path post this operation. For example if the original
       * path is foo.bar[0] and we insert one element at position 0, then the valid post path will be foo.bar[1].
       * @private
       * @return {Number} The offset
       */
      Utils.TraversalContext.prototype.getFullPostPath = function () {
        return this._fullPostPath;
      };
      /**
       * Traverses a ChangeSet recursively and invokes the callback for each visited property.
       * @protected
       *
       * @param {function(LYNX.Utils.TraversalContext)} in_preCallback  - The (pre-order) callback function that is invoked
       *                                                                  for each property
       * @param {function(LYNX.Utils.TraversalContext)} in_postCallback - The (post-order) callback function that is invoked
       *                                                                 for each property
       * @param {LYNX.Utils.TraversalContext}           in_context -  The traversal context for the currently processed
       *                                                              property
       */
      var _traverseChangeSetRecursively = function (in_preCallback, in_postCallback, in_context) {
        var pathSeparator = in_context._fullPath !== '' ? PROPERTY_PATH_DELIMITER : '';
        var postPathSeparator = in_context._fullPostPath !== '' ? PROPERTY_PATH_DELIMITER : '';
        var currentPath = in_context._fullPath;
        var currentPostPath = in_context._fullPostPath;
        var nestedChangeSet = in_context._nestedChangeSet;
        // Call the callback function for this ChangeSet
        in_context._traversalStopped = false;
        var splitTypeId = in_context._typeid !== undefined ? TypeIdHelper.extractContext(in_context._typeid) : undefined;
        in_context._splitTypeId = splitTypeId;
        // TODO: this duplicates the context object putting stress on the GC.
        var postOrderContext;
        if (in_postCallback) {
          postOrderContext = in_context.clone();
        }
        if (in_preCallback) {
          in_preCallback(in_context);
        }
        var currentUserData = in_context.getUserData();
        if (in_context._traversalStopped || in_context._operationType === 'remove' || TypeIdHelper.isPrimitiveType(in_context._splitTypeId.typeid) || in_context._splitTypeId.isEnum) {
          if (in_postCallback) {
            in_postCallback(postOrderContext);
          }
          return;
        }
        var currentTypeIdContext = in_context._splitTypeId.context;
        in_context._parentNestedChangeSet = nestedChangeSet;
        // Process an individual change recursively, by preparing the traversal context and invoking
        // _traverseChangeSetRecursively
        var processChange = function (in_segment, in_subChangeSet, in_nestedTypeid, in_escape, in_parentPropertyType, in_arrayOperationIndex, in_arrayLocalIndex, in_arrayOperationOffset, in_arrayIteratorOffset) {
          // Update the path
          in_context._lastSegment = in_segment;
          var escapedSegment = in_escape ? PathHelper.quotePathSegmentIfNeeded(in_segment) : in_segment;
          // Note: we don't quote the path string here, since the paths in a ChangeSet are already quoted, if necessary
          if (currentTypeIdContext === 'map' || currentTypeIdContext === 'array' || currentTypeIdContext === 'set') {
            in_context._lastSegmentString = '[' + escapedSegment + ']';
          } else {
            in_context._lastSegmentString = pathSeparator + escapedSegment;
          }
          in_context._fullPath = currentPath + in_context._lastSegmentString;
          // Store the typeid and nested ChangeSet
          in_context._typeid = in_nestedTypeid;
          in_context._nestedChangeSet = in_subChangeSet;
          in_context._propertyContainerType = in_parentPropertyType;
          in_context._arrayOperationIndex = in_arrayOperationIndex;
          in_context._arrayLocalIndex = in_arrayLocalIndex;
          in_context._arrayOperationOffset = in_arrayOperationOffset;
          in_context._arrayIteratorOffset = in_arrayIteratorOffset;
          if (in_arrayIteratorOffset) {
            in_context._fullPostPath = currentPostPath + '[' + (in_segment + in_arrayIteratorOffset) + ']';
          } else {
            in_context._fullPostPath = currentPostPath + postPathSeparator + escapedSegment;
          }
          // Continue traversal
          _traverseChangeSetRecursively(in_preCallback, in_postCallback, in_context);
          in_context._userData = currentUserData;
        };
        // If this property is a collection, we set the correct type, otherwise we assume it is a NodeProperty
        var propertyContainerType = splitTypeId.context === 'map' || splitTypeId.context === 'set' || splitTypeId.context === 'array' ? splitTypeId.context : 'NodeProperty';
        var oldOperationType = in_context._operationType;
        if (splitTypeId.context === 'array') {
          // Use the ArrayChangeSetIterator to process the changes in the ChangeSet in the correct order
          var arrayIterator = new ArrayChangeSetIterator(nestedChangeSet);
          var insertCounter = 0;
          var removeCounter = 0;
          var modifyCounter = 0;
          var i, j, typeid, typeids, paths;
          while (!arrayIterator.atEnd()) {
            switch (arrayIterator.type) {
            case ArrayChangeSetIterator.types.INSERT:
              in_context._operationType = 'insert';
              for (i = 0; i < arrayIterator.operation[1].length; ++i) {
                // The typeid is stored inline for arrays
                typeid = arrayIterator.operation[1][i].typeid;
                console.assert(typeid, 'Found a non primitive type array without typeids. This should never happen');
                processChange(arrayIterator.operation[0] + i, arrayIterator.operation[1][i], typeid, false, propertyContainerType, insertCounter, i, arrayIterator.operation[0], arrayIterator.offset);
              }
              insertCounter++;
              break;
            case ArrayChangeSetIterator.types.REMOVE:
              in_context._operationType = 'remove';
              for (i = 0; i < arrayIterator.operation[1]; ++i) {
                // For removals, we don't have a typeid and we use the ChangeSet of the removal operation as nested
                // ChangeSet -- TODO: doing this is maybe not really nice here
                processChange(arrayIterator.operation[0] + i, nestedChangeSet.remove, undefined, false, propertyContainerType, removeCounter, i, arrayIterator.operation[0], arrayIterator.offset);
              }
              removeCounter++;
              break;
            case ArrayChangeSetIterator.types.MODIFY:
              in_context._operationType = 'modify';
              for (i = 0; i < arrayIterator.operation[1].length; ++i) {
                // The typeid is stored inline for arrays
                typeid = arrayIterator.operation[1][i].typeid;
                console.assert(typeid, 'Found a non primitive type array without typeids. This should never happen');
                processChange(arrayIterator.operation[0] + i, arrayIterator.operation[1][i], typeid, false, propertyContainerType, modifyCounter, i, arrayIterator.operation[0], arrayIterator.offset);
              }
              modifyCounter++;
              break;
            default:
              throw new Error('ArrayChangeSetIterator: unknown operator ' + arrayIterator.type);
            }
            arrayIterator.next();
          }
          in_context._operationType = oldOperationType;
        } else {
          // Process insertion of dynamic property, maps and sets
          if (nestedChangeSet.insert) {
            // Once we have reached an insert operation all subsequent operations are inserts
            oldOperationType = in_context._operationType;
            in_context._operationType = 'insert';
            // Maps and NodeProperties group the insertions by type
            typeids = _.keys(nestedChangeSet.insert);
            for (i = 0; i < typeids.length; i++) {
              typeid = typeids[i];
              paths = _.keys(nestedChangeSet.insert[typeid]);
              for (j = 0; j < paths.length; j++) {
                processChange(paths[j], nestedChangeSet.insert[typeid][paths[j]], typeid, true, propertyContainerType);
              }
            }
            in_context._operationType = oldOperationType;
          }
          if (nestedChangeSet.modify) {
            // Maps and NodeProperties group modifications by type
            typeids = _.keys(nestedChangeSet.modify);
            for (i = 0; i < typeids.length; i++) {
              typeid = typeids[i];
              paths = _.keys(nestedChangeSet.modify[typeid]);
              for (j = 0; j < paths.length; j++) {
                processChange(paths[j], nestedChangeSet.modify[typeid][paths[j]], typeid, true, propertyContainerType);
              }
            }
          }
          if (nestedChangeSet.remove) {
            oldOperationType = in_context._operationType;
            in_context._operationType = 'remove';
            paths = nestedChangeSet.remove;
            if (_.isArray(paths)) {
              for (i = 0; i < paths.length; i++) {
                // For removals in irreversible CSs, we don't have a typeid and we use the ChangeSet of the
                // removal operation as nested ChangeSet
                processChange(paths[i], nestedChangeSet.remove, undefined, true, propertyContainerType);
              }
            } else {
              // for removals in reversible changesets we have an object containing the types
              typeids = _.keys(nestedChangeSet.remove);
              for (i = 0; i < typeids.length; i++) {
                typeid = typeids[i];
                paths = _.keys(nestedChangeSet.remove[typeid]);
                for (j = 0; j < paths.length; j++) {
                  processChange(paths[j], nestedChangeSet.remove[typeid][paths[j]], typeid, true, propertyContainerType);
                }
              }
            }
            in_context._operationType = oldOperationType;
          }
        }
        // Process nested properties
        if (splitTypeId.context === 'single') {
          typeids = _.keys(nestedChangeSet);
          for (i = 0; i < typeids.length; i++) {
            typeid = typeids[i];
            if (!ChangeSet.isReservedKeyword(typeid)) {
              paths = _.keys(nestedChangeSet[typeid]);
              for (j = 0; j < paths.length; j++) {
                processChange(paths[j], nestedChangeSet[typeid][paths[j]], typeid, false, 'template');
              }
            }
          }
        }
        if (in_postCallback) {
          in_postCallback(postOrderContext);
        }
      };
      /**
       * Traverses a ChangeSet recursively and invokes either a pre- or a post-order callback for each visited property.
       *
       * At least one of the pre- or post-order callbacks must be specified. Both may be specified as well.
       *
       * @param {LYNX.Property.SerializedChangeSet}     in_changeSet      - The ChangeSet to process
       * @param {function(LYNX.Utils.TraversalContext)} [in_preCallback]  - The (pre-order) callback function that is
       *                                                                    invoked for each property
       * @param {*}                                    [in_workspace]     - An optional object that is passed to all
       *                                                                    invocations of the callback via the context
       *                                                                    object that can be used to accumulate data.
       *                                                                    default, the user data will be set to the
       *                                                                    workspace, too.
       * @param {function(LYNX.Utils.TraversalContext)} [in_postCallback] - The (post-order) callback function that is
       *                                                                    invoked for each property
       * @function traverseChangeSetRecursively
       * @instance
       * @memberof LYNX.Property.Utils
       */
      Utils.traverseChangeSetRecursively = function (in_changeSet, in_preCallback, in_workspace, in_postCallback) {
        console.assert(in_preCallback || in_postCallback);
        // Initialize the traversal context
        var context = new Utils.TraversalContext();
        if (in_changeSet.typeid) {
          context._typeid = in_changeSet.typeid;
        } else {
          // By default, we assume that a ChangeSet without a typeid affects a NodeProperty, since that is the default for
          // a repository root
          context._typeid = 'NodeProperty';
        }
        context._nestedChangeSet = in_changeSet;
        context._parentNestedChangeSet = in_changeSet;
        context._splitTypeId = TypeIdHelper.extractContext(context._typeid);
        context._workspace = in_workspace;
        context._userData = in_workspace;
        // Start the traversal
        _traverseChangeSetRecursively(in_preCallback, in_postCallback, context);
      };
      /**
       * Extracts all typeIds from the given ChangeSet
       *
       * @param {LYNX.Property.SerializedChangeSet} in_changeSet - The ChangeSet to process
       *
       * @return {Array<String>} All typeids that appear in the ChangeSet
       */
      Utils.extractTypeids = function (in_changeSet) {
        var result = {};
        Utils.traverseChangeSetRecursively(in_changeSet, function (in_context) {
          if (in_context.getOperationType() === 'insert' || in_context.getOperationType() === 'modify') {
            in_context.getWorkspace()[in_context.getTypeid()] = true;
          }
        }, result);
        return _.keys(result);
      };
      /**
       * Removes all typeids from a ChangeSet
       * This is a private functions, it is only exported for the tests.
       *
       * @private
       * @param {LYNX.Property.SerializedChangeSet} io_changeSet    - The ChangeSet to process
       */
      Utils._stripTypeids = function (io_changeSet) {
        var result = {};
        Utils.traverseChangeSetRecursively(io_changeSet, function (in_context) {
          if (in_context.getFullPath() === '') {
            // We do nothing for the root
            return;
          }
          var userData = in_context.getUserData();
          if (in_context.getOperationType() === 'remove') {
            if (!userData[in_context.getOperationType()]) {
              userData[in_context.getOperationType()] = _.clone(in_context.getNestedChangeSet());
            }
            return;
          }
          var operationScope;
          if (in_context.getPropertyContainerType() !== 'template') {
            operationScope = userData[in_context.getOperationType()] = userData[in_context.getOperationType()] || (in_context.getPropertyContainerType() === 'array' ? [] : {});
          } else {
            operationScope = userData;
          }
          if (TypeIdHelper.isPrimitiveType(in_context.getTypeid())) {
            // This is a primitive type, we store it under its name in the result
            operationScope[in_context.getLastSegment()] = in_context.getNestedChangeSet();
          } else {
            var nestedUserData = {};
            if (in_context.getPropertyContainerType() === 'array') {
              if (!operationScope[in_context.getArrayOperationIndex()]) {
                operationScope[in_context.getArrayOperationIndex()] = [
                  in_context.getArrayOperationOffset(),
                  []
                ];
              }
              var arrayOperation = operationScope[in_context.getArrayOperationIndex()];
              arrayOperation[1][in_context.getArrayLocalIndex()] = nestedUserData;
            } else {
              // If it is a collection, we have to continue recursively
              operationScope[in_context.getLastSegment()] = nestedUserData;
            }
            in_context.setUserData(nestedUserData);
          }
        }, result);
        // Remove all existing keys from the ChangeSet
        _.forEach(_.keys(io_changeSet), function (key) {
          delete io_changeSet[key];
        });
        // Assign from the result workspace
        _.extend(io_changeSet, result);
      };
      /**
       * Searches through a ChangeSet and returns all Changes to a properties with a given typeid
       *
       * @param {String}                            in_typeid    - The typeid of the property to look for
       * @param {LYNX.Property.SerializedChangeSet} in_changeSet - The ChangeSet to process
       * @param {Boolean} in_excludetypeids - Exclude all typeids from the returned ChangeSet
       * @return { {insert: Object, modify: Object} } Returns the applied operations to entries of the given typeid.
       *                                              The returned maps for insert and modify map paths to ChangeSets
       */
      Utils.getChangesByType = function (in_typeid, in_changeSet, in_excludetypeids) {
        var result = {};
        // We search for the typeid by traversing the whole ChangeSet recursively
        Utils.traverseChangeSetRecursively(in_changeSet, function (in_context) {
          // If we found and instance of the requested typeid, we store it under its path
          if (in_context.getTypeid() === in_typeid) {
            var workspace = in_context.getWorkspace();
            workspace[in_context.getOperationType()] = workspace[in_context.getOperationType()] || {};
            workspace[in_context.getOperationType()][in_context.getFullPath()] = in_context.getNestedChangeSet();
          }
        }, result);
        // Exclude typeids if requested by the caller
        if (in_excludetypeids) {
          var insertKeys = _.keys(result.insert);
          for (var i = 0; i < insertKeys.length; i++) {
            result.insert[insertKeys[i]] = deepCopy(result.insert[insertKeys[i]]);
            Utils._stripTypeids(result.insert[insertKeys[i]]);
          }
          var modifyKeys = _.keys(result.modify);
          for (var i = 0; i < modifyKeys.length; i++) {
            result.modify[modifyKeys[i]] = deepCopy(result.modify[modifyKeys[i]]);
            Utils._stripTypeids(result.modify[modifyKeys[i]]);
          }
        }
        return result;
      };
      /**
       * Returns applications applied to a given path in a ChangeSet
       *
       * @param {string} in_path - The path to processChange
       * @param {LYNX.Property.BaseProperty} in_root - The root node to which the ChangeSet has been applied
       * @param {LYNX.Property.SerializedChangeSet} in_changeSet - The ChangeSet to process
       * @param {Boolean} in_excludetypeids - Exclude all typeids from the returned ChangeSet
       *
       * @return {object} - The changes that are applied to the given path
       * <pre>
       * {insert: Object|undefined, modify: Object|undefined, remove: boolean|undefined}
       * </pre>
       */
      Utils.getChangesByPath = function (in_path, in_root, in_changeSet, in_excludetypeids) {
        // if we're asked for the root, just return the root (in a modify)
        if (in_path === '') {
          return { modify: in_changeSet };
        }
        // tokenize the path we are searching for
        var pathSegments = PathHelper.tokenizePathString(in_path);
        // Recursively traverse the ChangeSet and search for the path
        var result = {};
        Utils.traverseChangeSetRecursively(in_changeSet, function (in_context) {
          // We ignore the root
          if (in_context.getFullPath() === '') {
            return;
          }
          var userData = in_context.getUserData();
          var currentSegment = pathSegments[userData.currentLevel];
          var changesetSegment = in_context.getLastSegment().toString();
          var level = userData.currentLevel;
          // We have to handle the case that a path contains nested properties. In that case we concatenate the
          // properties in the path, as long as they are a prefix of the segment we are currently looking at
          var mergedSegment = PathHelper.quotePathSegmentIfNeeded(currentSegment);
          while (changesetSegment.length > mergedSegment.length && changesetSegment.substr(0, mergedSegment.length) === mergedSegment && level < pathSegments.length - 1) {
            level++;
            mergedSegment = mergedSegment + PROPERTY_PATH_DELIMITER + PathHelper.quotePathSegmentIfNeeded(pathSegments[level]);
          }
          // Have we found the right entry in the ChangeSet?
          // TODO: This could be done more efficiently
          if (changesetSegment === mergedSegment) {
            if (in_context.getOperationType() === 'remove') {
              // If this is a removal operation, we mark the path as removed, even if we haven't reached the end of the
              // path
              result.removed = true;
              in_context.stopTraversal();
            } else {
              // Did we find the last segment in the path?
              if (level === pathSegments.length - 1) {
                // In that case, we return the result
                result[in_context.getOperationType()] = {};
                var currentChangeSet = in_context.getNestedChangeSet();
                if (in_excludetypeids) {
                  currentChangeSet = deepCopy(currentChangeSet);
                  Utils._stripTypeids(currentChangeSet);
                }
                result[in_context.getOperationType()][in_context.getFullPath()] = currentChangeSet;
              } else {
                // Otherwise, we continue recursively with the next level
                in_context.setUserData({ currentLevel: level + 1 });
              }
            }
          } else {
            // Stop the traversal, if this is the wrong segment
            in_context.stopTraversal();
          }
        }, { currentLevel: 0 });
        return result;
      };
      /**
       * Insert the necessary path for a change to a specific property into a ChangeSet.
       *
       * This function takes a property, its root property and a ChangeSet relative to that root. It returns the
       * nested ChangeSet for the supplied property. If there is already a nested changeSet for the the supplied
       * property in the changeSet, it will be returned. Otherwise, the necessary modify/insert commands are
       * created.
       *
       * @param {LYNX.Property.BaseProperty}        in_property - The property for which the nested ChangeSet is requested
       * @param {LYNX.Property.NodeProperty}        in_root - The root of the property
       * @param {LYNX.Property.SerializedChangeSet} in_changeSet - The changeSet into which the entry is added
       * @param {Boolean}                           in_insert - Do we want to crate an insert instead of a modify?
       * @return {{
       *           remove: (Boolean|undefined),
       *           insert: (Boolean|undefined),
       *           propertyChangeSet: (LYNX.Property.SerializedChangeSet|undefined)
       *         }}
       *     The return value contains the nested ChangeSet and a flag whether the property has been inserted. If
       *     the property has been removed, only remove: true will be present
       *     @private
       */
      Utils.insertPropertyChangeIntoChangeset = function (in_property, in_root, in_changeSet, in_insert) {
        // If the supplied property has a different root, it must have been removed from the
        // root property already
        if (in_property.getRoot() !== in_root) {
          return { remove: true };
        }
        // Recursively insert/modify the ChangeSet for the parent property
        var currentChangeSet = in_changeSet;
        var inserting = in_insert;
        if (in_property.getParent()) {
          var result = Utils.insertPropertyChangeIntoChangeset(in_property.getParent(), in_root, in_changeSet, in_insert);
          if (result.remove) {
            return result;
          }
          inserting = result.insert;
          currentChangeSet = result.propertyChangeSet;
        } else {
          return {
            propertyChangeSet: in_changeSet,
            insert: in_insert
          };
        }
        var typeid = in_property._getTypeidForChangeSet();
        var parentProperty = in_property.getParent();
        var propertyId = in_property.getId();
        var propertyChangeSet;
        // Handle indexed collections
        if (parentProperty instanceof Property.IndexedCollectionBaseProperty) {
          // Check for removals of the key
          if (currentChangeSet.remove) {
            if (_.isArray(currentChangeSet.remove)) {
              // Non reversible ChangeSet syntax
              if (_.contains(currentChangeSet.remove, propertyId)) {
                return { remove: true };
              }
            } else {
              // Reversible ChangeSet syntax
              if (currentChangeSet.remove[typeid][propertyId] !== undefined) {
                return { remove: true };
              }
            }
          }
          // Check, whether the entry is contained in the collection
          // (we use the internal member here, since the common base class does not export
          // a public interface)
          if (parentProperty._entries[propertyId]) {
            if (currentChangeSet.insert && currentChangeSet.insert[typeid] && currentChangeSet.insert[typeid][propertyId]) {
              // If we found an insert, we will always switch to the inserting mode
              propertyChangeSet = currentChangeSet.insert[typeid][propertyId];
              inserting = true;
            }
            if (currentChangeSet.modify && currentChangeSet.modify[typeid] && currentChangeSet.modify[typeid][propertyId]) {
              propertyChangeSet = currentChangeSet.modify[typeid][propertyId];
            }
            if (!propertyChangeSet) {
              if (inserting) {
                // Create a new insert entry
                currentChangeSet.insert = currentChangeSet.insert || {};
                currentChangeSet.insert[typeid] = currentChangeSet.insert[typeid] || {};
                currentChangeSet.insert[typeid][propertyId] = {};
                propertyChangeSet = currentChangeSet.insert[typeid][propertyId];
              } else {
                // Create a new modify entry
                currentChangeSet.modify = currentChangeSet.modify || {};
                currentChangeSet.modify[typeid] = currentChangeSet.modify[typeid] || {};
                currentChangeSet.modify[typeid][propertyId] = {};
                propertyChangeSet = currentChangeSet.modify[typeid][propertyId];
              }
            }
          }
        } else if (parentProperty instanceof Property.ArrayProperty) {
          // We have the index of the entry after the operations have been applied and we have to convert it to an
          // index before the operations have been performed. So we need to iterate over the operations and compute the
          // offset
          var index = parentProperty.getEntriesReadOnly().indexOf(in_property);
          var iterator = new ArrayChangeSetIterator(currentChangeSet);
          while (!iterator.atEnd()) {
            if (iterator.type === ArrayChangeSetIterator.types.INSERT || iterator.type === ArrayChangeSetIterator.types.MODIFY) {
              var offsetedIndex = index - iterator.offset;
              if (iterator.operation[0] <= offsetedIndex && iterator.operation[0] + iterator.operation[1].length > offsetedIndex) {
                propertyChangeSet = iterator.operation[1][offsetedIndex - iterator.operation[0]];
                inserting = iterator.type === ArrayChangeSetIterator.types.INSERT;
                break;
              }
              if (iterator.operation[0] > offsetedIndex) {
                // We reached an operation after the given index and
                // did not yet find a corresponding insert/modify
                // we can stop  here
                break;
              }
            }
            iterator.next();
          }
          // We did not find any matching operations, so we create
          // a new one
          if (!propertyChangeSet) {
            // This is just a unique token we insert into the ChangeSet to find in the search below
            var token = 'fa8d09e9-9bf4-181b-d184-0da7b7a860d5';
            // Insert a modify entry into the currentChangeSet
            ChangeSet.prototype._performApplyAfterOnPropertyArray(currentChangeSet, {
              modify: [[
                  index,
                  [{
                      typeid: in_property._getTypeidForChangeSet(false),
                      // This can never appear in a valid changeset since uniqueToken is not a valid typeid
                      uniqueToken: token
                    }]
                ]]
            }, parentProperty._getTypeidForChangeSet(true));
            // The _performApplyAfterOnPropertyArray function creates a deep copy, so we have to search for the changeset
            for (var i = 0; i < currentChangeSet.modify.length && !propertyChangeSet; i++) {
              var operation = currentChangeSet.modify[i];
              for (var j = 0; j < operation[1].length; j++) {
                if (operation[1][j].uniqueToken === token) {
                  // We found the entry
                  propertyChangeSet = operation[1][j];
                  delete propertyChangeSet.uniqueToken;
                  break;
                }
              }
            }
          }
        }
        // We haven't found the ChangeSet yet, so we have to assume that it is a static entry
        // in a templated property
        if (!propertyChangeSet) {
          currentChangeSet[typeid] = currentChangeSet[typeid] || {};
          currentChangeSet[typeid][propertyId] = currentChangeSet[typeid][propertyId] || {};
          propertyChangeSet = currentChangeSet[typeid][propertyId];
        }
        return {
          propertyChangeSet: propertyChangeSet,
          insert: inserting
        };
      };
      /**
       * Gather all properties that pass an arbitrary predicate function
       * @param {LYNX.Property.NodeProperty} in_rootProperty The root property to traverse from
       * @param {function} in_predicate The predicate function
       * @return {Array.<LYNX.Property.BaseProperty>} The list of properties that passed the predicate
       * function
       */
      Utils.gatherProperties = function (in_rootProperty, in_predicate) {
        var gatheredProperties = {};
        in_rootProperty.traverse(function (property, path) {
          if (in_predicate(property)) {
            gatheredProperties[path] = property;
          }
        });
        return gatheredProperties;
      };
      exportModule('LYNX.Property.Utils', Utils);
    }());
    (function () {
      var _ = include('underscore');
      var BaseProperty = include('LYNX.Property.BaseProperty');
      var Property = include('LYNX.Property', ['deserialize']);
      var ChangeSet = include('LYNX.Property.ChangeSet');
      var MSG = include('LYNX.Property._constants.MSG');
      /**
       * typedef {LYNX.Property.BaseProperty|string|number|boolean} LYNX.Property.IndexedCollectionBaseProperty~ValueType
       *
       * The type of the values that are set/inserted into the collection. Depending on the type of the collection, these
       * can either be property objects or primitive values
       */
      /**
       * A IndexedCollectionBaseProperty is the base class for indexed collections (maps and sets). It should not be used
       * directly.
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.BaseProperty
       * @alias LYNX.Property.IndexedCollectionBaseProperty
       */
      var IndexedCollectionBaseProperty = function (in_params) {
        BaseProperty.call(this, in_params);
        /** Stores the pending changes in the property (those that are part of the current ChangeSet) */
        this._pendingChanges = {
          insert: {},
          remove: {},
          modify: {}
        };
        /** Stores the dirty changes in the property (those that have not yet been reported to the application) */
        this._dirtyChanges = {
          insert: {},
          remove: {},
          modify: {}
        };
        /** Contains the actual entries of the set, indexed by their GUID */
        this._entries = {};
      };
      IndexedCollectionBaseProperty.prototype = Object.create(BaseProperty.prototype);
      /** Specifies, whether this is a collection of base types or of registered templates */
      IndexedCollectionBaseProperty.prototype._containsPrimitiveTypes = false;
      /**
       * Removes the dirtiness flag from this property
       *
       * @param {LYNX.Property.BaseProperty.MODIFIED_STATE_FLAGS} [in_flags] - The flags to clean, if none are supplied all
       *                                                                       will be removed
       * @private
       */
      IndexedCollectionBaseProperty.prototype._cleanDirty = function (in_flags) {
        // Invoke parent
        BaseProperty.prototype._cleanDirty.call(this, in_flags);
        if (in_flags === undefined || (in_flags & BaseProperty.MODIFIED_STATE_FLAGS.PENDING_CHANGE) !== 0) {
          // We additionally have to remove the log on the changes to our entries
          this._pendingChanges.insert = {};
          this._pendingChanges.remove = {};
          this._pendingChanges.modify = {};
        }
        if (in_flags === undefined || (in_flags & BaseProperty.MODIFIED_STATE_FLAGS.DIRTY) !== 0) {
          // We additionally have to remove the log on the changes to our entries
          this._dirtyChanges.insert = {};
          this._dirtyChanges.remove = {};
          this._dirtyChanges.modify = {};
        }
      };
      /**
       * Removes the dirtiness flag from this property and recursively from all of its children
       *
       * @param {LYNX.Property.BaseProperty.MODIFIED_STATE_FLAGS} [in_flags] - The flags to clean, if none are supplied all
       *                                                                       will be removed
       */
      IndexedCollectionBaseProperty.prototype.cleanDirty = function (in_flags) {
        in_flags = in_flags !== undefined ? in_flags : BaseProperty.MODIFIED_STATE_FLAGS.DIRTY | BaseProperty.MODIFIED_STATE_FLAGS.PENDING_CHANGE;
        // Clean all entries inside of the collection
        var entryKeys;
        if (in_flags === BaseProperty.MODIFIED_STATE_FLAGS.DIRTY) {
          // Only use the dirty entries
          entryKeys = _.keys(this._dirtyChanges.insert).concat(_.keys(this._dirtyChanges.modify));
        } else if (in_flags === BaseProperty.MODIFIED_STATE_FLAGS.PENDING_CHANGE) {
          // Only use the pending changes
          entryKeys = _.keys(this._pendingChanges.insert).concat(_.keys(this._pendingChanges.modify));
        } else {
          // TODO: How do we handle this case in the most efficient way? We would have to merge the keys from
          //       both sets above. We could either do a merge here, or iterate twice over the keys.
          //       both could be more expensive in the worst case
          entryKeys = _.union(_.keys(this._dirtyChanges.insert), _.keys(this._dirtyChanges.modify), _.keys(this._pendingChanges.insert), _.keys(this._pendingChanges.modify));
        }
        var entry;
        if (!this._containsPrimitiveTypes) {
          for (var i = 0; i < entryKeys.length; i++) {
            entry = this._entries[entryKeys[i]];
            if (entry._isDirty(in_flags)) {
              entry.cleanDirty(in_flags);
            }
          }
        }
        // Invoke parent
        BaseProperty.prototype.cleanDirty.call(this, in_flags);
        // after all entries have been cleaned, we mark this property as clean
        this._cleanDirty(in_flags);
      };
      /**
       * @inheritdoc
       */
      IndexedCollectionBaseProperty.prototype._freeCache = function (in_children) {
        // The caches
        if (this._structChange === BaseProperty._CACHES_INITIALLY_DISABLED) {
          return;
        }
        BaseProperty.prototype._freeCache.call(this, in_children);
        // do so recursively for all entries of the map too
        if (!this._containsPrimitiveTypes) {
          var keys = _.keys(this._entries);
          for (var i = 0; i < keys.length; i++) {
            this._entries[keys[i]]._freeCache(true);
          }
        }
      };
      /**
       * Inserts a property into the collection
       *
       * @param {string}                                                 in_key      -
       *     Key of the entry in the collection
       * @param {LYNX.Property.IndexedCollectionBaseProperty~ValueType}  in_value    -
       *     The value to insert
       * @param {boolean}                                                in_reportToView -
       *     By default, the dirtying will always be reported to the checkout view and trigger a modified event there.
       *     When batching updates, this can be prevented via this flag.
       */
      IndexedCollectionBaseProperty.prototype._insert = function (in_key, in_value, in_reportToView) {
        this._checkIsNotReadOnly();
        if (!this._entries[in_key]) {
          // Make sure, the property we are inserting is not already part of some other collection
          if (!this._containsPrimitiveTypes && in_value.getParent() !== undefined) {
            throw new Error(MSG.INSERTED_ENTRY_WITH_PARENT);
          }
          this._entries[in_key] = in_value;
          // We have to make sure, both this node itself and the whole tree of the inserted
          // entry are marked as dirty to make sure, they get serialized
          // We don't yet report the dirtying to the view. This happens below.
          this._setDirty(false);
          if (!this._containsPrimitiveTypes) {
            // Dirty the tree
            in_value._setDirtyTree(false);
            in_value._setParent(this);
            // In the case of a template property, we always mark it as inserted
            this._pendingChanges.insert[in_key] = true;
            this._dirtyChanges.insert[in_key] = true;
          } else {
            // For primitive types we squash remove/insert combinations to modifies
            if (this._pendingChanges.remove[in_key] && !this._pendingChanges.insert[in_key]) {
              this._pendingChanges.modify[in_key] = true;
              delete this._pendingChanges.remove[in_key];
            } else {
              this._pendingChanges.insert[in_key] = true;
            }
            if (this._dirtyChanges.remove[in_key] && !this._dirtyChanges.insert[in_key]) {
              this._dirtyChanges.modify[in_key] = true;
              delete this._dirtyChanges.remove[in_key];
            } else {
              this._dirtyChanges.insert[in_key] = true;
            }
          }
          // Now make one report
          if (in_reportToView) {
            this._reportDirtinessToView();
          }
        } else {
          throw new Error(MSG.PROPERTY_ALREADY_EXISTS + ':' + in_key);
        }
      };
      /**
       * Removes an entry with the given key
       *
       * @param {string} in_key -
       *     key of the entry
       * @param {boolean} in_reportToView -
       *     By default, the dirtying will always be reported to the checkout view and trigger a modified event there.
       *     When batching updates, this can be prevented via this flag.
       */
      IndexedCollectionBaseProperty.prototype._removeByKey = function (in_key, in_reportToView) {
        this._checkIsNotReadOnly();
        if (this._entries[in_key] !== undefined) {
          if (this._entries[in_key] instanceof BaseProperty) {
            this._entries[in_key]._setParent(undefined);
          }
          delete this._entries[in_key];
          // make sure this is not present in the insert list!
          if (this._pendingChanges.insert[in_key]) {
            delete this._pendingChanges.insert[in_key];
          } else {
            this._pendingChanges.remove[in_key] = true;
            // Also remove modifies, if any are present
            delete this._pendingChanges.modify[in_key];
          }
          // also update the list of dirty changes
          if (this._dirtyChanges.insert[in_key]) {
            delete this._dirtyChanges.insert[in_key];
          } else {
            this._dirtyChanges.remove[in_key] = true;
            // Also remove modifies, if any are present
            delete this._dirtyChanges.modify[in_key];
          }
          this._markStructChanged();
          this._setDirty(in_reportToView);
        } else {
          throw new Error(MSG.REMOVED_NON_EXISTING_ENTRY + in_key);
        }
      };
      /**
       * Serialize the property
       *
       * @param {boolean} in_dirtyOnly -
       *     Only include dirty entries in the serialization
       * @param {boolean} in_includeRootTypeid -
       *     Include the typeid of the root of the hierarchy
       * @param {LYNX.Property.BaseProperty.MODIFIED_STATE_FLAGS} [in_dirtinessType] -
       *     The type of dirtiness to use when reporting dirty changes. By default this is
       *     PENDING_CHANGE
       * @param {boolean} [in_includeReferencedRepositories=false] - If this is set to true, the serialize
       *     function will descend into referenced repositories. WARNING: if there are loops in the references
       *     this can result in an infinite loop
       *
       * @return {Object} The serialized representation of this property
       * @private
       */
      IndexedCollectionBaseProperty.prototype._serialize = function (in_dirtyOnly, in_includeRootTypeid, in_dirtinessType, in_includeReferencedRepositories) {
        var serialized = BaseProperty.prototype._serialize.call(this, in_dirtyOnly, in_includeRootTypeid, in_dirtinessType, in_includeReferencedRepositories);
        var that = this;
        // Helper function to decide whether to include a typeid or not in the ChangeSet
        var addEntryInChangeSet = function (in_changes, in_typeid, in_key, in_value) {
          // Determine where to insert the key. If necessary, an entry for the type is added.
          if (that._containsPrimitiveTypes) {
            in_changes[in_key] = that._serializeValue(in_value);
          } else {
            in_changes[in_typeid] = in_changes[in_typeid] || {};
            in_changes[in_typeid][in_key] = in_value;
          }
        };
        var changes = in_dirtinessType === BaseProperty.MODIFIED_STATE_FLAGS.PENDING_CHANGE ? this._pendingChanges : this._dirtyChanges;
        var insert = {};
        var modify = {};
        // we only remove entries when computing the delta
        var remove = in_dirtyOnly ? _.keys(changes.remove) : [];
        // Iterate over all children (and not properties)
        var typeid;
        var entryKeys = in_dirtyOnly ? _.keys(changes.modify).concat(_.keys(changes.insert)) : // Only dirty keys
        _.keys(this._entries);
        // All keys
        for (var i = 0; i < entryKeys.length; i++) {
          var key = entryKeys[i];
          var entry = this._entries[key];
          typeid = this._containsPrimitiveTypes ? this._getTypeidForChangeSet(false) : entry._getTypeidForChangeSet(false);
          if (in_dirtyOnly) {
            if (changes.insert[key]) {
              // If the key was inserted in this ChangeSet include it into the inserted list
              if (this._containsPrimitiveTypes) {
                addEntryInChangeSet(insert, typeid, key, entry);
              } else {
                addEntryInChangeSet(insert, typeid, key, entry._serialize(false, false, undefined, in_includeReferencedRepositories));
              }
            } else {
              // Check whether this is a modified entry and serialize changes when needed
              if (this._containsPrimitiveTypes) {
                if (changes.modify[key]) {
                  addEntryInChangeSet(modify, typeid, key, entry);
                }
              } else {
                if (entry._isDirty(in_dirtinessType)) {
                  var serializedChild = entry._serialize(in_dirtyOnly, false, in_dirtinessType, in_includeReferencedRepositories);
                  if (!ChangeSet.isEmptyChangeSet(serializedChild)) {
                    addEntryInChangeSet(modify, typeid, key, serializedChild);
                  }
                }
              }
            }
          } else {
            // If we serialize everything, all entries are inserted
            if (this._containsPrimitiveTypes) {
              insert[key] = this._serializeValue(entry);
            } else {
              insert[typeid] = insert[typeid] || {};
              insert[typeid][key] = entry._serialize(in_dirtyOnly, false, undefined, in_includeReferencedRepositories);
            }
          }
        }
        // Serialize the changes
        if (!_.isEmpty(insert)) {
          serialized.insert = insert;
        }
        if (!_.isEmpty(remove)) {
          serialized.remove = remove;
        }
        if (!_.isEmpty(modify)) {
          serialized.modify = modify;
        }
        return serialized;
      };
      /**
       * Function to deserialize special primitive types.
       * Some primitive types (e.g. Int64, which is not naively supported by javascript) require
       * special treatment on deserialization. For supported types, we can just return the input here.
       *
       * @param {LYNX.Property.SerializedChangeSet} in_serializedObj - The object to be deserialized
       * @return {*} the deserialized value
       */
      IndexedCollectionBaseProperty.prototype._deserializeValue = function (in_serializedObj) {
        return in_serializedObj;
      };
      /**
       * Function to serialize special primitive types.
       * Some primitive types (e.g. Int64, which is not naively supported by javascript) require
       * special treatment on serialization. For supported types, we can just return the input here.
       *
       * @param {*} in_obj - The object to be serialized
       * @return {LYNX.Property.SerializedChangeSet} the serialized object
       */
      IndexedCollectionBaseProperty.prototype._serializeValue = function (in_obj) {
        return in_obj;
      };
      /**
       * Sets the property to the state in the given normalized ChangeSet
       *
       * @param {LYNX.Property.SerializedChangeSet} in_serializedObj - The serialized changeset to apply to this node. This
       *     has to be a normalized change-set (only containing additions and property assignments. Deletes and Modify
       *     must not appear)
       * @param {boolean} [in_reportToView = true] - By default, the dirtying will always be reported to the checkout view
       *                                             and trigger a modified event there. When batching updates, this
       *                                             can be prevented via this flag.
       * @return {LYNX.Property.SerializedChangeSet} ChangeSet with the changes that actually were performed during the
       *     deserialization
       */
      // eslint-disable-next-line complexity
      IndexedCollectionBaseProperty.prototype._deserialize = function (in_serializedObj, in_reportToView) {
        var currentEntries = this._entries;
        var allInsertedKeys = {};
        var appliedChangeset = BaseProperty.prototype._deserialize.call(this, in_serializedObj, false);
        // Perform updates to the children
        // We make copies on two levels, since those are modified by the calls below
        var insertedEntries = _.mapObject(in_serializedObj.insert, function (x) {
          return _.clone(x);
        }) || {};
        var removedEntries = {};
        var modifiedEntries = {};
        // Get a list of all keys that will be inserted
        if (this._containsPrimitiveTypes) {
          var dataKeys = _.keys(insertedEntries);
          for (var iData = 0; iData < dataKeys.length; iData++) {
            var key = dataKeys[iData];
            allInsertedKeys[key] = true;
          }
        } else {
          var classKeys = _.keys(insertedEntries);
          for (var iClass = 0; iClass < classKeys.length; iClass++) {
            var typeid = classKeys[iClass];
            var dataKeys = _.keys(insertedEntries[typeid]);
            for (var iData = 0; iData < dataKeys.length; iData++) {
              var key = dataKeys[iData];
              allInsertedKeys[key] = true;
            }
          }
        }
        // Intersect the list of current keys, with the list of keys that will
        // NOT be inserted. Then insert the difference in the remove list so that
        // no objects are left dangling
        var keys = _.keys(currentEntries);
        for (var i = 0; i < keys.length; i++) {
          if (!allInsertedKeys[keys[i]]) {
            removedEntries[keys[i]] = true;
          }
        }
        // Remap adds to modify if the items are already in the document.
        // We want to make sure we reuse the current document state as much as possible
        // and simply change the bits that need to be changed. In many cases the
        // document will already contain the items but they need to be modified to
        // match the state described in_changeSet.
        if (this._containsPrimitiveTypes) {
          var addedKeys = Object.keys(insertedEntries);
          for (i = 0; i < addedKeys.length; i++) {
            if (currentEntries[addedKeys[i]] !== undefined) {
              modifiedEntries[addedKeys[i]] = insertedEntries[addedKeys[i]];
              delete insertedEntries[addedKeys[i]];
            }
          }
        } else {
          classKeys = _.keys(insertedEntries);
          for (iClass = 0; iClass < classKeys.length; iClass++) {
            typeid = classKeys[iClass];
            var addedKeys = Object.keys(insertedEntries[typeid]);
            for (i = 0; i < addedKeys.length; i++) {
              if (currentEntries[addedKeys[i]] !== undefined) {
                modifiedEntries[typeid] = modifiedEntries[typeid] || {};
                modifiedEntries[typeid][addedKeys[i]] = insertedEntries[typeid][addedKeys[i]];
                delete insertedEntries[typeid][addedKeys[i]];
              }
            }
            // Remove add entries, when they are empty
            if (_.isEmpty(insertedEntries[typeid])) {
              delete insertedEntries[typeid];
            }
          }
        }
        // Begin by removing what needs to be removed.
        keys = Object.keys(removedEntries);
        for (i = 0; i < keys.length; i++) {
          this._removeByKey(keys[i], false);
        }
        // Now get the portion of the children that must be added
        // this is a straightforward deserialize.
        if (this._containsPrimitiveTypes) {
          keys = _.keys(insertedEntries);
          for (i = 0; i < keys.length; i++) {
            this._insert(keys[i], this._deserializeValue(insertedEntries[keys[i]]), false);
          }
        } else {
          var scope = this._getScope();
          var newPsets = Property.deserialize(insertedEntries, scope);
          keys = _.keys(newPsets);
          for (i = 0; i < keys.length; i++) {
            this._insert(keys[i], newPsets[keys[i]], false);
          }
        }
        // If no typeids are included, we just use a placeholder for the iteration below
        var classKeys = this._containsPrimitiveTypes ? [undefined] : _.keys(modifiedEntries);
        // Finally modify the existing properties
        var mapWasChangedByModify = false;
        for (iClass = 0; iClass < classKeys.length; iClass++) {
          typeid = classKeys[iClass];
          var modifiedKeys = Object.keys(this._containsPrimitiveTypes ? modifiedEntries : modifiedEntries[typeid]);
          for (i = 0; i < modifiedKeys.length; i++) {
            var changes;
            var valueWasChanged = false;
            var modifiedEntriesMap;
            if (this._containsPrimitiveTypes) {
              changes = modifiedEntries[modifiedKeys[i]];
              valueWasChanged = this._entries[modifiedKeys[i]] !== changes;
              this._entries[modifiedKeys[i]] = this._deserializeValue(changes);
              modifiedEntriesMap = modifiedEntries;
              if (valueWasChanged) {
                // After modifying an entry, we have to update the flags
                // If there is a pending insert, we don't need to mark this as
                // a modify, as it will just change the insert. Otherwise, this
                // has to be reported as modify
                if (!this._pendingChanges.insert[modifiedKeys[i]]) {
                  this._pendingChanges.modify[modifiedKeys[i]] = true;
                  mapWasChangedByModify = true;
                }
                if (!this._dirtyChanges.insert[modifiedKeys[i]]) {
                  this._dirtyChanges.modify[modifiedKeys[i]] = true;
                  mapWasChangedByModify = true;
                }
              }
            } else {
              changes = this._entries[modifiedKeys[i]]._deserialize(modifiedEntries[typeid][modifiedKeys[i]], false);
              valueWasChanged = !ChangeSet.isEmptyChangeSet(changes);
              modifiedEntries[typeid] = modifiedEntries[typeid] || {};
              modifiedEntriesMap = modifiedEntries[typeid];
            }
            if (valueWasChanged) {
              modifiedEntriesMap[modifiedKeys[i]] = changes;
            } else {
              delete modifiedEntriesMap[modifiedKeys[i]];
            }
          }
          if (!this._containsPrimitiveTypes && _.isEmpty(modifiedEntries[typeid])) {
            delete modifiedEntries[typeid];
          }
        }
        // Create a ChangeSet with the actually applied changes
        if (!_.isEmpty(insertedEntries)) {
          appliedChangeset.insert = _.clone(insertedEntries);
        }
        if (!_.isEmpty(removedEntries)) {
          appliedChangeset.remove = _.keys(removedEntries);
        }
        if (!_.isEmpty(modifiedEntries)) {
          appliedChangeset.modify = modifiedEntries;
        }
        // If working with primitive types, we have to update the dirty flag, when one of the entries
        // was changed
        if (mapWasChangedByModify) {
          this._setDirty(false);
        }
        // Finally report the dirtiness to the view (we postponed this above)
        if (in_reportToView) {
          this._reportDirtinessToView();
        }
        return appliedChangeset;
      };
      /**
       * Modifies the property according to the given ChangeSet
       *
       * @param {LYNX.Property.SerializedChangeSet} in_changeSet - The changeset to apply
       * @param {boolean} [in_reportToView = true] - By default, the dirtying will always be reported to the checkout view
       *                                             and trigger a modified event there. When batching updates, this
       *                                             can be prevented via this flag.
       */
      IndexedCollectionBaseProperty.prototype._applyChangeset = function (in_changeSet, in_reportToView) {
        BaseProperty.prototype._applyChangeset.call(this, in_changeSet, false);
        // Remove existing entries
        // (we remove before we add, so that a remove+add operation in effect becomes a replace)
        if (in_changeSet.remove) {
          if (_.isArray(in_changeSet.remove)) {
            for (var i = 0; i < in_changeSet.remove.length; i++) {
              var key = in_changeSet.remove[i];
              this._removeByKey(key, false);
            }
          } else {
            // handle remove is an object case:
            if (!this._containsPrimitiveTypes) {
              var types = Object.keys(in_changeSet.remove);
              for (var i = 0; i < types.length; i++) {
                var keys = Object.keys(in_changeSet.remove[types[i]]);
                for (var j = 0; j < keys.length; j++) {
                  this._removeByKey(keys[j], false);
                }
              }
            } else {
              var keys = Object.keys(in_changeSet.remove);
              for (var j = 0; j < keys.length; j++) {
                this._removeByKey(keys[j], false);
              }
            }
          }
        }
        // Insert entries (we just have to deserialize and insert them)
        if (in_changeSet.insert) {
          var newPsets;
          if (this._containsPrimitiveTypes) {
            newPsets = in_changeSet.insert;
          } else {
            var scope = this._getScope();
            newPsets = Property.deserialize(in_changeSet.insert, scope);
          }
          var keys = Object.keys(newPsets);
          for (var i = 0; i < keys.length; i++) {
            if (!this._entries[keys[i]]) {
              this._insert(keys[i], newPsets[keys[i]], false);
            } else {
              throw new Error(MSG.INSERTED_EXISTING_ENTRY);
            }
          }
        }
        // Modify entries
        var mapWasChangedByModify = false;
        if (in_changeSet.modify) {
          var classKeys = this._containsPrimitiveTypes ? [undefined] : _.keys(in_changeSet.modify);
          for (var iClass = 0; iClass < classKeys.length; iClass++) {
            var modifiedEntries = this._containsPrimitiveTypes ? in_changeSet.modify : in_changeSet.modify[classKeys[iClass]];
            var keys = Object.keys(modifiedEntries);
            for (var i = 0; i < keys.length; i++) {
              var key = keys[i];
              if (this._entries[key] !== undefined) {
                if (this._containsPrimitiveTypes) {
                  var modifiedEntry = modifiedEntries[key];
                  if (modifiedEntry.value) {
                    modifiedEntry = modifiedEntry.value;
                  }
                  this._entries[key] = modifiedEntry;
                  // After modifying an entry, we have to update the flags
                  // If there is a pending insert, we don't need to mark this as
                  // a modify, as it will just change the insert. Otherwise, this
                  // has to be reported as modify
                  if (!this._pendingChanges.insert[key]) {
                    this._pendingChanges.modify[key] = true;
                    mapWasChangedByModify = true;
                  }
                  if (!this._dirtyChanges.insert[key]) {
                    this._dirtyChanges.modify[key] = true;
                    mapWasChangedByModify = true;
                  }
                } else {
                  this._entries[key]._applyChangeset(modifiedEntries[key], false);
                }
              } else {
                throw new Error(MSG.MODIFY_NON_EXISTING_ENTRY);
              }
            }
          }
        }
        // If working with primitive types, we have to update the dirty flag, when one of the entries
        // was changed
        if (mapWasChangedByModify) {
          this._setDirty(false);
        }
        // Finally report the dirtiness to the view (we postponed this above)
        if (in_reportToView) {
          this._reportDirtinessToView();
        }
      };
      /**
       * @inheritdoc
       */
      IndexedCollectionBaseProperty.prototype._setDirty = function (in_reportToView, in_callingChild) {
        // Mark the child as modified
        if (in_callingChild && !this._containsPrimitiveTypes) {
          var key = in_callingChild.getId();
          if (this._entries[key]) {
            if (!this._pendingChanges.insert[key]) {
              this._pendingChanges.modify[key] = true;
            }
            if (!this._dirtyChanges.insert[key]) {
              this._dirtyChanges.modify[key] = true;
            }
          }
        }
        // Forward dirtiness propagation to base class
        BaseProperty.prototype._setDirty.call(this, in_reportToView, in_callingChild);
      };
      /**
       * not implemented (see LYNXDEV-835)
       * @param {*} values to assign to the collection
       * @private
       */
      IndexedCollectionBaseProperty.prototype._setValues = function (values) {
        throw new Error('_setValues not implemented for indexed collection types');
      };
      exportModule('LYNX.Property.IndexedCollectionBaseProperty', IndexedCollectionBaseProperty);
    }());
    (function () {
      var Property = include('LYNX.Property', ['PropertyFactory']);
      var BaseProperty = include('LYNX.Property.BaseProperty');
      var MSG = { NOTHING_TO_DESERIALIZE: 'Repository deserialize(), no input given' };
      /**
       * Dummy property used to return the scope to the underlying properties
       * @param {object} in_params BaseProperty parameters
       * @param {string} in_params.scope The scope to keep track of
       * @constructor
       */
      var ScopeProperty = function (in_params) {
        // HACK: Normally, we would inherit from NodeProperty however, NodeProperty seems to not be available
        // at this point. There may be a bug with MR.
        BaseProperty.call(this, in_params);
        this._scope = in_params.scope;
      };
      ScopeProperty.prototype = Object.create(BaseProperty.prototype);
      /**
       * @override
       */
      ScopeProperty.prototype._getScope = function () {
        return this._scope;
      };
      /**
       * Serialize the input document.
       * @param {Array.<LYNX.Property.BaseProperty>} in_psets property set
       * @param {bool} in_dirtyOnly serialize dirty properties only
       * @return {{}} JSON data of the document
       * @alias LYNX.Property.serialize
       */
      var serialize = function (in_psets, in_dirtyOnly) {
        in_dirtyOnly = in_dirtyOnly || false;
        var documentData = {};
        var rootTypeid;
        var keys = Object.keys(in_psets);
        for (var i = 0; i < keys.length; i++) {
          rootTypeid = in_psets[keys[i]].getTypeid();
          if (!documentData[rootTypeid]) {
            documentData[rootTypeid] = {};
          }
          documentData[rootTypeid][keys[i]] = in_psets[keys[i]].serialize(in_dirtyOnly);
        }
        return documentData;
      };
      /**
       * Deserialize the input document
       * @param {{}} in_data the input JSON document data
       * @return {{}} an object of guid : pset
       * @param {string|undefined} in_scope The scope to construct the properties from
       * @alias LYNX.Property.deserialize
       */
      var deserialize = function (in_data, in_scope) {
        if (!in_data) {
          console.warn(MSG.NOTHING_TO_DESERIALIZE);
          return {};
        }
        var deserializedProperties = {};
        var typeid, entity, classed;
        var dataKeys = Object.keys(in_data);
        for (var iData = 0; iData < dataKeys.length; iData++) {
          typeid = dataKeys[iData];
          classed = in_data[typeid];
          var classKeys = Object.keys(classed);
          for (var iClass = 0; iClass < classKeys.length; iClass++) {
            // reconstruct entity
            entity = Property.PropertyFactory._createProperty(typeid, in_scope);
            // fresh new entity of this type
            // Store the id prior to calling entity.deserialize() since it is subject to change afterwards
            var id = entity.getId();
            // Create a scope property which captures the scope that was passed in as arguments
            // so that it can be picked up downstream by the respective deserialize functions
            var scopeProperty = new ScopeProperty({ scope: in_scope });
            scopeProperty._append(entity);
            entity.deserialize(classed[classKeys[iClass]]);
            scopeProperty._remove(id);
            entity._id = classKeys[iClass];
            // keep track of the reconstructed entities
            deserializedProperties[classKeys[iClass]] = entity;
          }
        }
        return deserializedProperties;
      };
      exportModule('LYNX.Property.serialize', serialize);
      exportModule('LYNX.Property.deserialize', deserialize);
    }());
    (function () {
      var BaseProperty = include('LYNX.Property.BaseProperty');
      /**
       * A NamedProperty has a GUID which unique identifies the property object. This makes it possible to store it in a
       * set collection.
       *
       * @param {object} in_params         - List of parameters
       * @param {string} in_params.id      - id of the property (null, if the GUID should be used for the ID)
       * @param {string} in_params.typeid  - The type identifier
       *
       * @constructor
       * @extends LYNX.Property.BaseProperty
       * @alias LYNX.Property.NamedProperty
       */
      var NamedProperty = function (in_params) {
        BaseProperty.call(this, in_params);
      };
      NamedProperty.prototype = Object.create(BaseProperty.prototype);
      /**
       * Returns a string identifying the property
       *
       * If an id has been explicitly set on this property we return that one, otherwise the GUID is used.
       *
       * @return {string} String identifying the property
       */
      NamedProperty.prototype.getId = function () {
        if (this._id !== null) {
          return this._id;
        } else {
          var guid = this.get('guid');
          return guid ? guid.value : '';
        }
      };
      /**
       * Returns the GUID of this named property
       *
       * @return {string} The GUID
       */
      NamedProperty.prototype.getGuid = function () {
        return this.get('guid').value;
      };
      exportModule('LYNX.Property.NamedProperty', NamedProperty);
    }());
    (function () {
      var _ = include('underscore');
      var IndexedCollectionBaseProperty = include('LYNX.Property.IndexedCollectionBaseProperty');
      var PathHelper = include('LYNX.Property.PathHelper');
      var PROPERTY_PATH_DELIMITER = include('LYNX.Property._constants.PROPERTY_PATH_DELIMITER');
      var MSG = include('LYNX.Property._constants.MSG');
      var BaseProperty = include('LYNX.Property.BaseProperty');
      // This cache is used to store the static keys of templated types to accelerate the traversal over static members
      var _staticChildrenCache = {
        NodeProperty: [],
        NamedNodeProperty: ['guid']
      };
      /**
       * A property object that allows to add child properties dynamically.
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.IndexedCollectionBaseProperty
       * @alias LYNX.Property.NodeProperty
       */
      var NodeProperty = function (in_params) {
        IndexedCollectionBaseProperty.call(this, in_params);
        // There can be a this._checkedOutRepositoryInfo in this object, but we don't initialize it in the constructor
        // since most node properties won't need that member and this should reduce memory consumption
        this._staticChildrenCache = undefined;
      };
      NodeProperty.prototype = Object.create(IndexedCollectionBaseProperty.prototype);
      /**
       * @return {boolean} Is this a dynamic property (only properties inheriting from NodeProperty are)
       */
      NodeProperty.prototype.isDynamic = function () {
        return true;
      };
      /**
       * Appends a property
       *
       * @param {String} [in_id]                         - The id under which the property is added. This parameter is
       *                                                   optional. For NamedProperties it can be omitted. In that case
       *                                                   the GUID of the named property will be used.
       *
       * @param {LYNX.Property.BaseProperty} in_property - The property to add
       *
       * @return {LYNX.Property.BaseProperty} The appended property
       */
      NodeProperty.prototype.insert = function (in_id, in_property) {
        if (in_property === undefined) {
          // If no id is passed, the property is passed as first parameter
          in_property = in_id;
        } else {
          if (this._entries[in_id] !== undefined) {
            throw new Error(MSG.PROPERTY_ALREADY_EXISTS + ':' + in_id);
          }
          // If an id is passed, it is stored in the child property object
          in_property._setId(in_id);
        }
        if (in_property.getId() === undefined || in_property.getId() === null) {
          throw new Error(MSG.ADDED_CHILD_WITHOUT_ID);
        }
        // Add the child property to the dynamic properties
        this._insert(in_property.getId(), in_property, true);
        return in_property;
      };
      /**
       * @override
       * @inheritdoc
       */
      NodeProperty.prototype._getScope = function () {
        if (this._parent) {
          return this.getRoot()._getScope();
        } else {
          if (this._checkedOutRepositoryInfo) {
            return this._checkedOutRepositoryInfo.getScope();
          } else {
            return undefined;
          }
        }
      };
      /**
       * Removes the given property
       *
       * @param {string|LYNX.Property.BaseProperty} in_property - The property to remove
       *                                                          (either its id or the whole property).
       */
      NodeProperty.prototype.remove = function (in_property) {
        var id = in_property;
        if (id instanceof BaseProperty) {
          id = id.getId();
        }
        this._removeByKey(id);
      };
      /**
       * Removes all dynamic children
       */
      NodeProperty.prototype.removeAll = function () {
        this._checkIsNotReadOnly();
        _.each(this._entries, this.remove.bind(this));
      };
      /**
       * Inserts a property into the collection
       *
       * @param {string}                      in_key      -
       *     Key of the entry in the collection
       * @param {LYNX.Property.NamedProperty} in_property -
       *     The property to insert
       * @param {boolean}                     in_reportToView -
       *     By default, the dirtying will always be reported to the checkout view and trigger a modified event there.
       *     When batching updates, this can be prevented via this flag.
       */
      NodeProperty.prototype._insert = function (in_key, in_property, in_reportToView) {
        this._checkIsNotReadOnly();
        // Add the child property to the dynamic properties
        IndexedCollectionBaseProperty.prototype._insert.call(this, in_key, in_property, false);
        // But also insert it into the list of children
        this._append(in_property);
        // We postponed the report above, to make sure the child property has actually been appended to this
        // node, before the report is forwarded to the view
        if (in_reportToView) {
          this._reportDirtinessToView();
        }
      };
      /**
       * Removes an entry with the given key
       *
       * @param {string} in_key - key of the entry
       * @param {boolean} in_reportToView -
       *     By default, the dirtying will always be reported to the checkout view and trigger a modified event there.
       *     When batching updates, this can be prevented via this flag.
       */
      NodeProperty.prototype._removeByKey = function (in_key, in_reportToView) {
        this._checkIsNotReadOnly();
        if (this._children[in_key]) {
          // Remove from the children
          this._remove(in_key);
          // Remove from the indexed collection
          IndexedCollectionBaseProperty.prototype._removeByKey.call(this, in_key, in_reportToView);
        } else {
          console.error('trying to remove something that does not exist!', in_key);
        }
      };
      /**
       * Traverses all static properties (properties declared in the template and not added dynamically) in the
       * hierarchy below this node
       *
       * @param {function} in_callback               - Callback to invoke for every property
       * @param {string?}  in_pathFromTraversalStart - Path from the root of the traversal to this node
       * @protected
       */
      NodeProperty.prototype._traverseStaticProperties = function (in_callback, in_pathFromTraversalStart) {
        in_pathFromTraversalStart = in_pathFromTraversalStart || '';
        var propertyKeys = this._staticChildrenCache;
        for (var i = 0; i < propertyKeys.length; i++) {
          var property = this._children[propertyKeys[i]];
          var childPath = in_pathFromTraversalStart + (in_pathFromTraversalStart.length !== 0 ? PROPERTY_PATH_DELIMITER : '') + PathHelper.quotePathSegmentIfNeeded(property.getId());
          // We only process this property, if it is not part of the entries list
          if (!this._entries[property.getId()]) {
            // We only recursively traverse BaseProperties, since these are used to define the hierarchy within
            // one template
            if (property.getTypeid() === 'BaseProperty') {
              property._traverseStaticProperties(in_callback, childPath);
            }
            in_callback(property, childPath);
          }
        }
      };
      /**
       * Stores the information to which CheckedOutRepositoryInfo object this root property belongs.
       * Note: these functions should only be used internally (within the PropertySets library)
       *
       * @param {LYNX.Property.CheckoutView~CheckedOutRepositoryInfo} in_checkedOutRepositoryInfo -
       * The checked out repository info this root property belongs to.
       * @protected
       */
      NodeProperty.prototype._setCheckedOutRepositoryInfo = function (in_checkedOutRepositoryInfo) {
        this._checkedOutRepositoryInfo = in_checkedOutRepositoryInfo;
      };
      /**
       * Stores the information to which CheckedOutRepositoryInfo object this root property belongs.
       * Note: these functions should only be used internally (within the PropertySets library)
       *
       * @return {LYNX.Property.CheckoutView~CheckedOutRepositoryInfo|undefined} If this is the root of the checked out
       *     hierarchy, this will return the checkout
       * @protected
       */
      NodeProperty.prototype._getCheckedOutRepositoryInfo = function () {
        return this._checkedOutRepositoryInfo;
      };
      /**
       * Returns the name of all the static sub-properties of this property.
       *
       * @return {Array.<string>} An array of all the static property ids
       */
      NodeProperty.prototype.getStaticIds = function () {
        /* TODO: For an unknown (maybe good) reason, the dynamic '_entries' are also
         inserted in the '_children' static. */
        var all = Object.keys(this._children);
        var dynamic = Object.keys(this._entries);
        for (var i = 0; i < dynamic.length; i++) {
          all.splice(all.indexOf(dynamic[i]), 1);
        }
        return all;
      };
      /**
       * Returns the name of all the dynamic sub-properties of this property.
       *
       * @return {Array.<string>} An array of all the dynamic property ids
       */
      NodeProperty.prototype.getDynamicIds = function () {
        return Object.keys(this._entries);
      };
      /**
       * Returns an Object with all the dynamic children of this node property.
       *
       * WARNING: This is a direct access to the internal data-structure and the collection MUST NOT be modified. It is
       * read only for fast access and iteration. Insertion and deletion MUST be done via the insert and remove functions
       * of this class.
       *
       * @return {Object<String, LYNX.Property.MapProperty~MapValueType>} The map with all entries in the map.
       */
      NodeProperty.prototype._getDynamicChildrenReadOnly = function () {
        return this._entries;
      };
      /**
       * Given an object that mirrors a PSet Template, assign the properties
       * eg.
       * <pre>
       * Templates = {
       *   properties: [
       *     { id: 'foo', typeid: 'String' },
       *     { id: 'bar', properties: [{id: 'baz', typeid: 'Uint32'}] }
       *   ]
       * }
       * </pre>
       * You would update the values like
       * `baseProperty._setValues({foo: 'hello', bar: {baz: 1}});`
       * WARNING: not completely impemented for all types (see LYNXDEV-835)
       * @param {object} in_properties The properties you would like to assign
       * @private
       */
      NodeProperty.prototype._setValues = function (in_properties) {
        // We currently forward this to the base property, which should also work for a node property
        // It is currently not possible to insert new dynamic properties via this interface
        BaseProperty.prototype._setValues.call(this, in_properties);
      };
      /**
       * Returns all children which are dirty (this only returns direct children, it does not travers recursively)
       *
       * @param {LYNX.Property.BaseProperty.MODIFIED_STATE_FLAGS} in_flags - Which types of dirtiness are we looking for?
       *                                                                     If none is given, all types are regarded as
       *                                                                     dirty
       * @return {Array.<String>} The list of keys identifying the dirty children
       * @private
       */
      NodeProperty.prototype._getDirtyChildren = function (in_flags) {
        var flags = in_flags === undefined ? ~BaseProperty.MODIFIED_STATE_FLAGS.CLEAN : in_flags;
        var rtn = [];
        var childKeys = this._staticChildrenCache;
        for (var i = 0; i < childKeys.length; i++) {
          if ((this._children[childKeys[i]]._dirty & flags) !== 0) {
            rtn.push(childKeys[i]);
          }
        }
        return rtn;
      };
      /**
       * @inheritdoc
       */
      NodeProperty.prototype._signalAllStaticMembersHaveBeenAdded = function (in_scope) {
        BaseProperty.prototype._signalAllStaticMembersHaveBeenAdded.call(this);
        // Create a unique key per scope
        var lookupKey = (in_scope ? in_scope + ':' : '') + this.getTypeid();
        // Store the keys of the static children in the cache (if needed)
        if (!_staticChildrenCache[lookupKey]) {
          var children = _.keys(this._children);
          _staticChildrenCache[lookupKey] = children;
        }
        this._staticChildrenCache = _staticChildrenCache[lookupKey];
      };
      /**
       * @inheritdoc
       */
      NodeProperty.prototype._freeCache = function (in_children) {
        // The indexed collection directly calls _freeCache of the base property, since this already
        // cleans the children and we don't need the cleaning done by the indexed collection
        BaseProperty.prototype._freeCache.call(this, in_children);
      };
      /**
       * Cleans the cache of static children per typeid. Calling this should only be necessary if a template has been
       * reregistered.
       *
       * @protected
       */
      NodeProperty._cleanStaticChildrenCache = function () {
        _staticChildrenCache = {};
      };
      exportModule('LYNX.Property.NodeProperty', NodeProperty);
    }());
    (function () {
      var NamedProperty = include('LYNX.Property.NamedProperty');
      var NodeProperty = include('LYNX.Property.NodeProperty');
      /**
       * A NamedNodeProperty is a NodeProperty that has a GUID which unique identifies the property object.
       * This makes it possible to store it in a set collection.
       *
       * @param {object} in_params         - List of parameters
       * @param {string} in_params.id      - id of the property (null, if the GUID should be used for the ID)
       * @param {string} in_params.typeid  - The type identifier
       *
       * @constructor
       * @extends LYNX.Property.NodeProperty
       * @alias LYNX.Property.NamedNodeProperty
       */
      var NamedNodeProperty = function (in_params) {
        NodeProperty.call(this, in_params);
      };
      NamedNodeProperty.prototype = Object.create(NodeProperty.prototype);
      /**
       * Returns a string identifying the property
       *
       * If an id has been explicitly set on this property we return that one, otherwise the GUID is used.
       *
       * @return {string} String identifying the property
       */
      NamedNodeProperty.prototype.getId = NamedProperty.prototype.getId;
      /**
       * Returns the GUID of this named property
       *
       * @return {string} The GUID
       */
      NamedNodeProperty.prototype.getGuid = NamedProperty.prototype.getGuid;
      exportModule('LYNX.Property.NamedNodeProperty', NamedNodeProperty);
    }());
    (function () {
      var NodePort = include('LYNX.Utils.Graph.NodePort');
      /**
       * @param {LYNX.Utils.Graph.Constants.PORT_TYPE} in_PORT_TYPE The type of port ( INPUT or OUTPUT )
       * @param {LYNX.Utils.Graph.Node} in_node A node.
       * @constructor
       * @extends LYNX.Utils.Graph..NodePort
       * @alias LYNX.PropertyGraph.Ports.ParentChildPort
       * @private
       */
      var ParentChildPort = function (in_PORT_TYPE, in_node) {
        NodePort.call(this, {
          id: 'ParentChildPort',
          PORT_TYPE: in_PORT_TYPE,
          node: in_node,
          typeid: 'ParentChildID'
        });
        this._acceptedTypes = ['ParentChildID'];
      };
      ParentChildPort.prototype = Object.create(NodePort.prototype);
      exportModule('LYNX.PropertyGraph.Ports.ParentChildPort', ParentChildPort);
    }());
    (function () {
      var Node = include('LYNX.Utils.Graph.Node');
      var _ = include('underscore');
      var ParentChildPort = include('LYNX.PropertyGraph.Ports.ParentChildPort');
      var PORT_TYPE = include('LYNX.Utils.Graph.Constants.PORT_TYPE');
      var UrnUtils = include('LYNX.Utils.UrnUtils');
      /**
       * @namespace LYNX.PropertyGraph.Nodes
       * @alias LYNX.PropertyGraph.Nodes
       * Namespace containing all node classes in the property graph
       */
      /**
       * A commit node in the commit graph
       * The commit node stores the bits of information that are used to represent a
       * particular set of changes with regards to it's input (the parent).
       * Change Sets are the data stored by these nodes but there is no logic around
       * the data itself, only the connectivity between N parent nodes to N children nodes.
       * These nodes are used to build the topology for the change graph, but no knowledge
       * about data is kept here.
       *
       * A Commit node has N inputs, and N outputs. Change sets are retrieved from the
       * node itself.
       *
       *      ____________________
       *     |     CommitNode     |
       *     |                    |
       *  ---i [parent]           |
       *     |                    |
       *     |                    |
       *     |                    |
       *     |         [children] o----
       *     |____________________|
       *
       *
       * @param {string} in_guid - The GUID of the commit
       *
       * @constructor
       * @alias LYNX.PropertyGraph.Nodes.CommitNode
       */
      var CommitNode = function (in_guid) {
        Node.call(this, { typeid: 'CommitNodeID' });
        this._guid = in_guid;
        this._urn = UrnUtils.commitUrn(in_guid);
        this._changeSet = undefined;
        // Flag indicating that the commit node has been normalized.
        // A normalized commit is the result of a series of commit changes merged into a single commit
        // eg. squashed
        this._isNormalized = false;
        this.addPort('parent', new ParentChildPort(PORT_TYPE.INPUT, this));
        this.addPort('children', new ParentChildPort(PORT_TYPE.OUTPUT, this));
        this._cachedNormalizedChangeSet = undefined;
      };
      CommitNode.prototype = Object.create(Node.prototype);
      /**
       * Gets the URN of this commit
       * @return {String} the commit urn
       */
      CommitNode.prototype.getUrn = function () {
        return this._urn;
      };
      CommitNode.prototype._addParent = function (in_commitNode) {
        this.getInput('parent').connect(in_commitNode.getOutput('children'));  // console.log( 'addParent',
                                                                               //   this.getMetadata('name') +
                                                                               //   '.parent ---- ' +
                                                                               //   'children.' +
                                                                               //   in_commitNode.getMetadata('name')  );
      };
      /**
       * Returns the first parent
       * @return {LYNX.Property.RootProperty|LYNX.PropertyGraph.Nodes.CommitNode}
       */
      CommitNode.prototype.getFirstParent = function () {
        var parentConnection = this.getInput('parent').getConnected()[0];
        return parentConnection !== undefined ? parentConnection.endPort().belongsTo() : undefined;
      };
      /**
       * @return {Array<LYNX.Property.RootProperty|LYNX.PropertyGraph.Nodes.CommitNode>} Return all of the parent nodes
       */
      CommitNode.prototype.getParents = function () {
        var parentConnections = this.getInput('parent').getConnected();
        return _.map(parentConnections, function (parentConnection) {
          return parentConnection.endPort().belongsTo();
        });
      };
      /**
       * @return {Array<LYNX.PropertyGraph.Nodes.CommitNode>} Return all of the children nodes
       */
      CommitNode.prototype.getChildren = function () {
        var childConnections = this.getOutput('children').getConnected();
        return _.map(childConnections, function (childConnection) {
          return childConnection.startPort().belongsTo();
        });
      };
      /**
       * Gets the GUID of this commit
       * @return {String} the commit guid
       */
      CommitNode.prototype.getGuid = function () {
        return this._guid;
      };
      /**
       * Comparison function between this commit node and another one to check
       * for equality. We determine the equality by checking their respective guids
       * @param {LYNX.PropertyGraph.Nodes.CommitNode} in_node The commit node to compare with
       * @return {boolean} True of the two nodes are equal
       */
      CommitNode.prototype.isEqual = function (in_node) {
        return in_node.getGuid() === this.getGuid();
      };
      /**
       *
       * @return {LYNX.Property.SerializedChangeSet} the changes
       */
      CommitNode.prototype.getChangeSetReadOnly = function () {
        var changes = this._changeSet;
        if (changes) {
          return JSON.parse(changes);
        } else {
          return {};
        }
      };
      CommitNode.prototype._setChangeSet = function (in_changeSet) {
        this._changeSet = JSON.stringify(in_changeSet);
      };
      /**
       * Returns the path from the supplied parent commit to this node
       *
       * Note: This function assumes that the given commit is direct parent (e.g. not in a branch of a merge commit,
       *       but a direct ancestor, as returned by getFirstParent).
       *
       * @param {LYNX.PropertyGraph.Nodes.CommitNode|function} in_parentCommit  - The node from which the path is computed.
       *                                                                          If a function is provided, it is used
       *                                                                          as predicate to determine whether the
       *                                                                          node is the parent.
       * @param {boolean}                                      in_includeParent - Should the  parent node itself be
       *                                                                          included in the supplied path?
       *
       * @return {Array.<LYNX.PropertyGraph.Nodes.CommitNode>} The nodes on the path from the supplied parent
       */
      CommitNode.prototype._getPathFromParentCommit = function (in_parentCommit, in_includeParent) {
        var pathToParent = [];
        var currentCommit = this;
        var lastPathEntry = undefined;
        while (currentCommit) {
          // Check whether this is the parent commit we are looking for
          var isParentCommit = in_parentCommit instanceof Function ? in_parentCommit(currentCommit) : currentCommit === in_parentCommit;
          // If we don't want to include the parent, we break here
          if (isParentCommit && !in_includeParent) {
            break;
          }
          var currentPathEntry = { node: currentCommit };
          pathToParent.push(currentPathEntry);
          if (lastPathEntry) {
            currentPathEntry.input = this.getInput('parent').getConnected()[0].endPort().getId();
            lastPathEntry.output = this.getId();
          }
          currentCommit = currentCommit.getFirstParent();
          lastPathEntry = currentPathEntry;
          // If we want to include the parent, we instead break here
          if (isParentCommit && in_includeParent) {
            break;
          }
        }
        return pathToParent.reverse();
      };
      /**
       * Set the normalized flag
       * @param {boolean} in_boolean True if the commit node is normalized
       */
      CommitNode.prototype._setNormalized = function (in_boolean) {
        this._isNormalized = !!in_boolean;
      };
      /**
       * Get the normalized flag
       * @return {boolean} Normalized flag
       */
      CommitNode.prototype.isNormalized = function () {
        return this._isNormalized;
      };
      /**
       * Is a normalized ChangeSet available for this node?
       *
       * This can either be the case, if the ChangeSet in the node is itself normalized, or if a
       * cached ChangeSet is stored in the node
       *
       * @return {boolean} Is a normalized ChangeSet available for this node?
       */
      CommitNode.prototype._hasNormalized = function () {
        return this._isNormalized || this._cachedNormalizedChangeSet !== undefined;
      };
      /**
       * Stores a cached ChangeSet in the node (or removes it when called with undefined)
       *
       * @param {LYNX.Property.SerializedChangeSet|undefined} in_normalizedChangeSet - The ChangeSet to store
       */
      CommitNode.prototype._setCachedNormalizedChangeSet = function (in_normalizedChangeSet) {
        this._cachedNormalizedChangeSet = in_normalizedChangeSet;
      };
      /**
       * Returns a normalized ChangeSet for this node, or undefined if none is available
       *
       * @return {LYNX.Property.SerializedChangeSet|undefined} The normalized ChangeSet
       */
      CommitNode.prototype._getNormalizedChangeSet = function () {
        if (this._isNormalized) {
          return this._changeSet;
        } else if (this._cachedNormalizedChangeSet !== undefined) {
          return this._cachedNormalizedChangeSet;
        } else {
          return undefined;
        }
      };
      exportModule('LYNX.PropertyGraph.Nodes.CommitNode', CommitNode);
    }());
    (function () {
      var NodePort = include('LYNX.Utils.Graph.NodePort');
      /**
       * @param {LYNX.Utils.Graph.Constants.PORT_TYPE} in_PORT_TYPE The type of port ( INPUT or OUTPUT )
       * @param {LYNX.Utils.Graph.Node} in_node A node.
       * @constructor
       * @extends LYNX.Utils.Graph..NodePort
       * @alias LYNX.PropertyGraph.Ports.LocalRemoteBranchPort
       * @private
       */
      var LocalRemoteBranchPort = function (in_PORT_TYPE, in_node) {
        NodePort.call(this, {
          id: 'LocalRemoteBranchPort',
          PORT_TYPE: in_PORT_TYPE,
          node: in_node,
          typeid: 'LocalRemoteBranchID'
        });
        this._acceptedTypes = ['LocalRemoteBranchID'];
      };
      LocalRemoteBranchPort.prototype = Object.create(NodePort.prototype);
      exportModule('LYNX.PropertyGraph.Ports.LocalRemoteBranchPort', LocalRemoteBranchPort);
    }());
    (function () {
      var Node = include('LYNX.Utils.Graph.Node');
      var LocalRemoteBranchPort = include('LYNX.PropertyGraph.Ports.LocalRemoteBranchPort');
      var MSG = include('LYNX.Property._constants.MSG');
      var PORT_TYPE = include('LYNX.Utils.Graph.Constants.PORT_TYPE');
      var UrnUtils = include('LYNX.Utils.UrnUtils');
      /**
       * Node representing a branch in the commit graph
       *
       * @param {object} in_params - List of branch meta data
       * @param {string} in_params.guid - The guid of the branch
       * @param {string} [in_params.name] - The name of the branch. Will default to the guid.
       * @param {LYNX.PropertyGraph.Nodes.CommitNode} in_headNode - The head node of the branch
       *
       * @constructor
       * @alias LYNX.PropertyGraph.Nodes.BranchNode
       */
      var BranchNode = function (in_params, in_headNode) {
        in_params = in_params || {};
        Node.call(this, { typeid: 'BranchNodeID' });
        this._isRemoteBranch = !!in_params.remote;
        console.assert(in_params.guid, 'Missing guid field');
        this._guid = in_params.guid;
        this._urn = UrnUtils.branchUrn(in_params.guid);
        if (in_params.name) {
          this._name = in_params.name;
        } else if (in_params.meta && in_params.meta.name) {
          // HFDM server returns all metadata in a separate object.
          this._name = in_params.meta.name;
        } else {
          this._name = this._guid;
        }
        this._head = in_headNode;
        this.addPort('remote_branch', new LocalRemoteBranchPort(PORT_TYPE.OUTPUT, this));
        this.addPort('local_branch', new LocalRemoteBranchPort(PORT_TYPE.INPUT, this));
      };
      BranchNode.prototype = Object.create(Node.prototype);
      /**
       * Returns the name of the branch
       * @return {String}
       */
      BranchNode.prototype.getName = function () {
        return this._name;
      };
      /**
       * Set the remote branch
       * @param {BranchNode} in_remoteBranch The remote branch to link to
       */
      BranchNode.prototype._setRemoteBranch = function (in_remoteBranch) {
        console.assert(this.isEqual(in_remoteBranch), 'Remote branch guid must equal the local branch guid');
        console.assert(in_remoteBranch.isRemoteBranch(), 'Branch is not a remote branch');
        this.getOutput('remote_branch').connect(in_remoteBranch.getInput('local_branch'));
      };
      /**
       * Check whether this branch is remote
       * @return {boolean} Returns true if the branch is remote
       */
      BranchNode.prototype.isRemoteBranch = function () {
        return this._isRemoteBranch;
      };
      /**
       * Get the remote branch
       * @return {BranchNode} Returns the remote branch
       */
      BranchNode.prototype.getRemoteBranch = function () {
        var connection = this.getOutput('remote_branch').getConnected()[0];
        return connection !== undefined ? connection.startPort().belongsTo() : undefined;
      };
      /**
       * Get the local branch
       * @return {BranchNode} Returns the local branch
       */
      BranchNode.prototype.getLocalBranch = function () {
        var connection = this.getInput('local_branch').getConnected()[0];
        return connection !== undefined ? connection.endPort().belongsTo() : undefined;
      };
      /**
       * Return the Guid of the branch
       * @return {string} The guid of the branch
       */
      BranchNode.prototype.getGuid = function () {
        return this._guid;
      };
      /**
       * Gets the URN of this branch
       * @return {String} the branch urn
       */
      BranchNode.prototype.getUrn = function () {
        return this._urn;
      };
      /**
       * Returns the head of the branch
       * @return {BranchNode}
       */
      BranchNode.prototype.getHead = function () {
        return this._head;
      };
      BranchNode.prototype._setHead = function (in_headNode) {
        this._head = in_headNode;
        return this._head;
      };
      BranchNode.prototype._setMetadata = function () {
        throw new Error(MSG.NOT_IMPLEMENTED);
      };
      /**
       * Comparison function between this branch node and another one to check
       * for equality. We determine the equality by checking their respective guids
       * @param {LYNX.PropertyGraph.Nodes.BranchNode} in_node The branch node to compare with
       * @return {boolean} True of the two nodes are equal
       */
      BranchNode.prototype.isEqual = function (in_node) {
        return in_node.getGuid() === this.getGuid();
      };
      exportModule('LYNX.PropertyGraph.Nodes.BranchNode', BranchNode);
    }());
    (function () {
      var BaseProperty = include('LYNX.Property.BaseProperty');
      var generateGUID = include('LYNX.Utils.generateGUID');
      var PathHelper = include('LYNX.Property.PathHelper');
      var MSG = include('LYNX.Property._constants.MSG');
      var CommitNode = include('LYNX.PropertyGraph.Nodes.CommitNode');
      var BranchNode = include('LYNX.PropertyGraph.Nodes.BranchNode');
      var _ = include('underscore');
      var PROPERTY_PATH_DELIMITER = include('LYNX.Property._constants.PROPERTY_PATH_DELIMITER');
      /**
       * A RepositoryReferenceProperty is used to manage references in between repositories. It stores the reference to
       * a commit in a separate repository (by storing a repository, branch and commit GUID). Adding such a property
       * to a CheckoutView will trigger loading the corresponding sub-tree into the view.
       *
       * // TODO: Should this inherit from a NamedProperty?
       *
       * @param {object} in_params       - List of parameters
       * @param {string} in_params.id    - id of the property
       * @param {string} in_params.name  - Name of the property
       * @param {string} in_params.typeid  - The type identifier
       *
       * @constructor
       * @extends LYNX.Property.BaseProperty
       * @alias LYNX.Property.RepositoryReferenceProperty
       */
      var RepositoryReferenceProperty = function (in_params) {
        BaseProperty.call(this, in_params);
        // We make sure the repository reference runtime instance is uniquely identifiable
        // Note: This is not a GUID in the pset itself, it is only used to identify the runtime object
        this._instanceGuid = generateGUID();
        // This flag is used to prevent endless cycles when serializing a PropertySet data-structure with cycles
        this._visitedDuringSerialization = false;
        // The state of the repository reference
        this._state = RepositoryReferenceProperty.STATE.EMPTY;
        // The previous values of this reference. These are kept here, to keep the old state of the
        // referenced repository available, while the reference is in the loading state
        this._previousValues = undefined;
      };
      RepositoryReferenceProperty.prototype = Object.create(BaseProperty.prototype);
      /**
       * The state of this repository reference
       * @enum {number}
       */
      RepositoryReferenceProperty.STATE = {
        /** The reference does not point to any other repository */
        EMPTY: 0,
        /** The reference is currently loading the referenced repository */
        LOADING: 1,
        /** The referenced repository has successfully been loaded and is available */
        AVAILABLE: 2,
        /** Loading the referenced repository has failed */
        FAILED: 3
      };
      /**
       * @inheritdoc
       */
      RepositoryReferenceProperty.prototype._setValues = function (in_properties) {
        var that = this;
        _.each(in_properties, function (propertyValue, propertyKey) {
          var property = that._children[propertyKey];
          if (property) {
            property._setValue(propertyValue, false);
          } else {
            // TODO: Which behavior do we want to have for _setValues? Should it recurse into the referenced
            //       repository
            console.warn(MSG.NON_EXISTING_PROPERTY_REPOSITORY_REFERENCE + propertyKey);
          }
        });
        this._setDirty(true);
      };
      /**
       * Sets the state of this repository reference property
       *
       * @param {LYNX.Property.RepositoryReferenceProperty.STATE} in_state - The current state of the reference
       * @param {String} [in_oldRepositoryGUID] - The old repository GUID (as it has been before loading started).
       *                                          Only needed if the state is LOADING.
       * @param {String} [in_oldBranchGUID]     - The old branch GUID (as it has been before loading started).
       *                                          Only needed if the state is LOADING.
       * @param {String} [in_oldCommitGUID]     - The old commit GUID (as it has been before loading started).
       *                                          Only needed if the state is LOADING.
       * @private
       */
      RepositoryReferenceProperty.prototype._setState = function (in_state, in_oldRepositoryGUID, in_oldBranchGUID, in_oldCommitGUID) {
        this._state = in_state;
        if (in_state === RepositoryReferenceProperty.STATE.LOADING) {
          this._previousValues = {
            commitGUID: in_oldCommitGUID,
            repositoryGUID: in_oldRepositoryGUID,
            branchGUID: in_oldBranchGUID
          };
        } else {
          this._previousValues = undefined;
        }
      };
      /**
       * Returns the state of this RepositoryReferenceProperty
       *
       * @return {LYNX.Property.RepositoryReferenceProperty.STATE} - The current state
       */
      RepositoryReferenceProperty.prototype.getState = function () {
        return this._state;
      };
      /**
       * @inheritdoc
       */
      RepositoryReferenceProperty.prototype._resolvePathSegment = function (in_segment, in_segmentType) {
        // Base Properties only support paths separated via dots
        if (in_segmentType === PathHelper.TOKEN_TYPES.ARRAY_TOKEN) {
          // TODO: Which semantic do we want to have? Currently, you can resolve the actual children of the repository
          //       reference property via a [] path
          return BaseProperty.prototype._resolvePathSegment.call(this, in_segment, PathHelper.TOKEN_TYPES.PATH_SEGMENT_TOKEN);
        } else if (in_segmentType === PathHelper.TOKEN_TYPES.DEREFERENCE_TOKEN) {
          // A dereference token causes the repository reference to be resolved
          return this.ref;
        } else {
          return this._getReferencedRootWithError()._resolvePathSegment(in_segment, in_segmentType);
        }
      };
      /**
       * @inheritdoc
       */
      RepositoryReferenceProperty.prototype._getPathSegmentForChildNode = function (in_childNode) {
        // in a RepositoryReferenceProperty special members are accessed via [] instead of '.'
        // we need to address this in the path getter, too:
        if (in_childNode === this.properties.followBranch || in_childNode === this.properties.branchGUID || in_childNode === this.properties.commitGUID || in_childNode === this.properties.repositoryGUID) {
          return '[' + PathHelper.quotePathSegmentIfNeeded(in_childNode.getId()) + ']';
        } else {
          return PROPERTY_PATH_DELIMITER + PathHelper.quotePathSegmentIfNeeded(in_childNode.getId());
        }
      };
      /**
       * @inheritdoc
       */
      RepositoryReferenceProperty.prototype._getChildrenCount = function () {
        // TODO: How exactly should this behave with regard to direct children versus children
        //       from the referenced repository
        return BaseProperty.prototype._getChildrenCount.call(this) + this._getReferencedRootWithError()._getChildrenCount();
      };
      /**
       * @inheritdoc
       */
      RepositoryReferenceProperty.prototype.getIds = function () {
        // TODO: How exactly should this behave with regard to direct children versus children
        //       from the referenced repository
        var ids = BaseProperty.prototype.getIds.call(this);
        if (ids.length) {
          ids = ids.concat(this._getReferencedRootWithError().getIds());
        } else {
          ids = this._getReferencedRootWithError().getIds();
        }
        return ids;
      };
      /**
       * @inheritdoc
       */
      RepositoryReferenceProperty.prototype.get = function (in_id) {
        // TODO: How exactly should this behave with regard to direct children versus children
        //       from the referenced repository
        return BaseProperty.prototype.get.call(this, in_id) || this._getReferencedRootWithError().get(in_id);
      };
      /**
       * @inheritdoc
       */
      RepositoryReferenceProperty.prototype.has = function (in_id) {
        // TODO: How exactly should this behave with regard to direct children versus children
        //       from the referenced repository
        return BaseProperty.prototype.has.call(this, in_id) || this._getReferencedRootWithError().has(in_id);
      };
      /**
       * Returns the root property of the referenced repository
       *
       * @return {LYNX.Property.NodeProperty} The root node of the repository
       */
      RepositoryReferenceProperty.prototype.getReferencedRepositoryRoot = function () {
        var myRoot = this.getRoot();
        var repositoryInfo = myRoot._checkedOutRepositoryInfo;
        if (!repositoryInfo) {
          throw new Error(MSG.PROPERTY_NOT_PART_OF_CHECKOUT_VIEW);
        }
        var repositoryGUID, checkoutGroupID, commitGUID;
        if (!this._previousValues) {
          repositoryGUID = this._children.repositoryGUID.value;
          checkoutGroupID = this._children.branchGUID.value;
          commitGUID = this._children.commitGUID.value;
        } else {
          repositoryGUID = this._previousValues.repositoryGUID;
          checkoutGroupID = this._previousValues.branchGUID;
          commitGUID = this._previousValues.commitGUID;
        }
        var checkoutView = repositoryInfo.parent;
        var referencedRepository = checkoutView._getCheckedOutRepositoryInfo(repositoryGUID, checkoutGroupID, commitGUID);
        if (!referencedRepository) {
          return undefined;
        }
        return referencedRepository.repositoryRoot;
      };
      /**
       * Checks, whether the referenced properties are available
       * @return {Boolean} Are the references available?
       */
      RepositoryReferenceProperty.prototype.areReferencesAvailable = function () {
        var myRoot = this.getRoot();
        var repositoryInfo = myRoot._checkedOutRepositoryInfo;
        if (!repositoryInfo) {
          return false;
        }
        return this.getReferencedRepositoryRoot() !== undefined;
      };
      /**
       * Internal function which tries to get the referenced root and throws an error, if it could not be retrieved
       * @return  {LYNX.Property.BaseProperty} The referenced root property
       * @private
       */
      RepositoryReferenceProperty.prototype._getReferencedRootWithError = function () {
        var referencedRoot = this.getReferencedRepositoryRoot();
        if (referencedRoot) {
          return referencedRoot;
        } else {
          throw new Error(MSG.REFERENCED_REPOSITORY_NOT_AVAILABLE);
        }
      };
      /**
       * Update the reference and set it to the given commit, branch and repository
       * TODO: Can we also provide automatic updates for the repository when this property is already in a repository?
       *
       * @param {String|LYNX.PropertyGraph.Nodes.CommitNode|LYNX.PropertyGraph.Nodes.BranchNode} [in_commitOrBranch]
       *     The commit OR a branch. If a branch is specified as first parameter, its HEAD will be used as commit.
       *     The second parameter is the interpreted as repository
       * @param {String|LYNX.PropertyGraph.Nodes.BranchNode} [in_branch]
       *     The branch. If the first parameter is already a branch, this parameter can be omitted
       * @param {String} [in_repository]
       *     The repository GUID
       */
      RepositoryReferenceProperty.prototype.updateReference = function (in_commitOrBranch, in_branch, in_repository) {
        // Parse the parameters and handle the case where only a branch is supplied
        var commitGUID;
        if (in_commitOrBranch instanceof CommitNode) {
          commitGUID = in_commitOrBranch.getGuid();
        } else if (in_commitOrBranch instanceof BranchNode) {
          if (in_branch) {
            // If we have a branch as first parameter, we assume that the second parameter is a repository
            in_repository = in_branch;
          }
          in_branch = in_commitOrBranch;
          commitGUID = in_branch.getHead().getGuid();
        } else {
          commitGUID = in_commitOrBranch;
        }
        // Update the commit GUID
        this._children.commitGUID._setValue(commitGUID, false);
        // Update the branch GUID
        if (in_branch) {
          var branchGUID = in_branch instanceof BranchNode ? in_branch.getGuid() : in_branch;
          this._children.branchGUID._setValue(branchGUID, false);
        }
        // Update the repository GUID
        if (in_repository) {
          this._children.repositoryGUID._setValue(in_repository, false);
        }
        // Finally trigger a dirty notification
        this._setDirty(true);
      };
      /**
       * @inheritdoc
       */
      RepositoryReferenceProperty.prototype._serialize = function (in_dirtyOnly, in_includeRootTypeid, in_dirtinessType, in_includeReferencedRepositories) {
        if (this._visitedDuringSerialization) {
          throw new Error(MSG.SERIALIZING_CYCLIC_PROPERTY_TREE);
        }
        var serialized = BaseProperty.prototype._serialize.call(this, in_dirtyOnly, in_includeRootTypeid, in_dirtinessType, in_includeReferencedRepositories);
        if (in_includeReferencedRepositories && this._children.commitGUID.value !== '') {
          var referencedRoot = this.getReferencedRepositoryRoot();
          if (referencedRoot) {
            // Serialize the referenced repository (and guard against endless recursion within a cyclic property tree)
            this._visitedDuringSerialization = true;
            var serializedSubrepository = referencedRoot._serialize(in_dirtyOnly, false, in_dirtinessType, in_includeReferencedRepositories);
            this._visitedDuringSerialization = false;
            if (serializedSubrepository.insert) {
              serialized.insert = serializedSubrepository.insert;
            }
            if (serializedSubrepository.modify) {
              serialized.modify = serializedSubrepository.modify;
            }
            if (serializedSubrepository.remove) {
              serialized.remove = serializedSubrepository.remove;
            }
          }
        }
        return serialized;
      };
      // Define a property to simplify accessing the referenced path
      Object.defineProperty(RepositoryReferenceProperty.prototype, 'ref', {
        get: function () {
          return this.getReferencedRepositoryRoot.apply(this, arguments);
        },
        set: function () {
          throw new Error(MSG.SETTING_REPOSITORY_REF_PROPERTY);
        }
      });
      exportModule('LYNX.Property.RepositoryReferenceProperty', RepositoryReferenceProperty);
    }());
    (function () {
      var BaseProperty = include('LYNX.Property.BaseProperty');
      var ChangeSet = include('LYNX.Property.ChangeSet');
      /**
       * This class serves as a view to read, write and listen to changes in an
       * object's value field. To do this we simply keep a pointer to the object and
       * it's associated data field that we are interested in. If no data field is
       * present this property will fail constructing.
       * @virtual
       * @param {Object=} in_params - the parameters
       * @param {Object=} in_params.dataObj optional argument containing an object
       *                  that should be used as the backing store of this value
       *                  property
       * @param {Object=} in_params.dataId optional argument must be provided when
       *                  in_params.dataObj is passed. Must contain a valid member
       *                  name of dataObj. This member will be used to set/get
       *                  values of this value property
       * @constructor
       * @extends LYNX.Property.BaseProperty
       * @alias LYNX.Property.ValueProperty
       */
      var ValueProperty = function (in_params) {
        BaseProperty.call(this, in_params);
        this._data = undefined;
      };
      ValueProperty.prototype = Object.create(BaseProperty.prototype);
      /**
       * Is this property a leaf node with regard to flattening?
       *
       * TODO: Which semantics should flattening have? It stops at primitive types and collections?
       *
       * @return {boolean} Is it a leaf with regard to flattening?
       */
      ValueProperty.prototype._isFlattenLeaf = function () {
        return true;
      };
      /**
       * Returns the current value of the ValueProperty
       * @return {Object}
       */
      ValueProperty.prototype.get = function () {
        return this._data;
      };
      /**
       * Ensure the array dirty mask is also cleaned when cleaning the tree.
       *
       * @param {LYNX.Property.BaseProperty.MODIFIED_STATE_FLAGS} [in_flags] - The flags to clean, if none are supplied all
       *                                                                       will be removed
       */
      ValueProperty.prototype.cleanDirty = function (in_flags) {
        this._cleanDirty(in_flags);
      };
      /**
       * @param {*} in_value the new value
       * @return {boolean} true if the value was actually changed
       */
      ValueProperty.prototype.set = function (in_value) {
        this._checkIsNotReadOnly();
        return this._setValue(in_value, true);
      };
      /**
       * @inheritdoc
       */
      ValueProperty.prototype._setValues = function (in_properties) {
        return this._setValue(in_properties, true);
      };
      /**
       * Internal function to update the value of a property
       *
       * @param {*} in_value the new value
       * @param {boolean} [in_reportToView = true] - By default, the dirtying will always be reported to the checkout view
       *                                             and trigger a modified event there. When batching updates, this
       *                                             can be prevented via this flag.
       * @return {boolean} true if the value was actually changed
       */
      ValueProperty.prototype._setValue = function (in_value, in_reportToView) {
        // dirtiness check: setValue casts the input e.g. in an
        // int property 1.2 gets cast to 1, in a boolean property
        // false gets cast to 0,... so we first have to cast(set)
        // and then compare the value here:
        var oldValue = this._data;
        var castedValue = this._castFunctor(in_value);
        var changed = castedValue !== oldValue;
        if (changed) {
          this._data = castedValue;
          this._setDirty(in_reportToView);
        }
        return changed;
      };
      /**
       * Sets the property to the state in the given normalized changeset
       *
       * @param {*} in_value - The serialized target value
       * @param {boolean} [in_reportToView = true] - By default, the dirtying will always be reported to the checkout view
       *                                             and trigger a modified event there. When batching updates, this
       *                                             can be prevented via this flag.
       *
       * @return {LYNX.Property.SerializedChangeSet} ChangeSet with the changes that actually were performed during the
       *     deserialization
       */
      ValueProperty.prototype._deserialize = function (in_value, in_reportToView) {
        if (ChangeSet.isEmptyChangeSet(in_value)) {
          throw new Error('ValueProperty.deserialize() called on an empty changeset: This should never happen!');
        } else {
          var changed = this._setValue(in_value, in_reportToView);
          return changed ? this.get() : undefined;
        }
      };
      /**
       * Modifies the property according to the given changeset
       *
       * @param {LYNX.Property.SerializedChangeSet} in_changeSet - The changeset to apply
       * @param {boolean} [in_reportToView = true] - By default, the dirtying will always be reported to the checkout view
       *                                             and trigger a modified event there. When batching updates, this
       *                                             can be prevented via this flag.
       */
      ValueProperty.prototype._applyChangeset = function (in_changeSet, in_reportToView) {
        if (!ChangeSet.isEmptyChangeSet(in_changeSet)) {
          var newVal = in_changeSet;
          if (in_changeSet.value) {
            newVal = in_changeSet.value;
          }
          this._setValue(newVal, in_reportToView);
        }
      };
      /**
       * Serialize the property
       *
       * @param {boolean} in_dirtyOnly -
       *     Only include dirty entries in the serialization
       * @param {boolean} in_includeRootTypeid -
       *     Include the typeid of the root of the hierarchy - has no effect for ValueProperty
       * @param {LYNX.Property.BaseProperty.MODIFIED_STATE_FLAGS} [in_dirtinessType] -
       *     The type of dirtiness to use when reporting dirty changes. By default this is
       *     PENDING_CHANGE
       * @param {boolean} [in_includeReferencedRepositories=false] - If this is set to true, the serialize
       *     function will descend into referenced repositories. WARNING: if there are loops in the references
       *     this can result in an infinite loop
       *
       * @return {*} The serialized representation of this property
       * @private
       */
      ValueProperty.prototype._serialize = function (in_dirtyOnly, in_includeRootTypeid, in_dirtinessType, in_includeReferencedRepositories) {
        if (in_dirtyOnly) {
          if (this._isDirty(in_dirtinessType)) {
            return this.get();
          } else {
            return {};
          }
        } else {
          return this.get();
        }
      };
      /**
       * Calls back the given function with a human-readable string
       * representation of the property.
       * @param {string} indent - Leading spaces to create the tree representation
       * @param {string} externalId - Name of the current property at the upper level.
       *                              Used for arrays.
       * @param {function} printFct - Function to call for printing each property
       */
      ValueProperty.prototype._prettyPrint = function (indent, externalId, printFct) {
        printFct(indent + externalId + this.getId() + ' (' + this.getTypeid() + '): ' + this.value);
      };
      Object.defineProperty(ValueProperty.prototype, 'value', {
        get: function () {
          return this.get.apply(this, arguments);
        },
        set: function () {
          this.set.apply(this, arguments);
        }
      });
      exportModule('LYNX.Property.ValueProperty', ValueProperty);
    }());
    (function () {
      var castArrays = {
        Uint32: new Uint32Array(1),
        Uint16: new Uint16Array(1),
        Uint8: new Uint8Array(1),
        Int32: new Int32Array(1),
        Int16: new Int16Array(1),
        Int8: new Int8Array(1),
        Float32: new Float32Array(1),
        Float64: new Float64Array(1)
      };
      /**
       * Performs a cast of a value by assigning it into the given data array and returning the resulting value. The
       * result is a native JavaScript datatype, that is compatible with the supplied typed array.
       *
       * @param {TypedArray}                    in_array - The data array to use for the cast
       * @param {number|string|boolean}         in_value - The value to use in the cast
       * @return {number|string|boolean} The casted value
       * @private
       */
      var _simpleCastFunctor = function (in_array, in_value) {
        in_array[0] = in_value;
        return in_array[0];
      };
      /**
       * Helper functions to cast the input value to the given type
       * @protected
       */
      var _castFunctors = {
        /**
         * Casts the input value to a Uint32
         * @param {number} in_value - The value to use in the cast
         * @return {number} The casted value
         * @protected
         */
        Uint32: _simpleCastFunctor.bind(undefined, castArrays.Uint32),
        /**
         * Casts the input value to a Uint16
         * @param {number} in_value - The value to use in the cast
         * @return {number} The casted value
         * @protected
         */
        Uint16: _simpleCastFunctor.bind(undefined, castArrays.Uint16),
        /**
         * Casts the input value to a Uint8
         * @param {number} in_value - The value to use in the cast
         * @return {number} The casted value
         * @protected
         */
        Uint8: _simpleCastFunctor.bind(undefined, castArrays.Uint8),
        /**
         * Casts the input value to a Int32
         * @param {number} in_value - The value to use in the cast
         * @return {number} The casted value
         * @protected
         */
        Int32: _simpleCastFunctor.bind(undefined, castArrays.Int32),
        /**
         * Casts the input value to a Int16
         * @param {number} in_value - The value to use in the cast
         * @return {number} The casted value
         * @protected
         */
        Int16: _simpleCastFunctor.bind(undefined, castArrays.Int16),
        /**
         * Casts the input value to a Int8
         * @param {number} in_value - The value to use in the cast
         * @return {number} The casted value
         * @protected
         */
        Int8: _simpleCastFunctor.bind(undefined, castArrays.Int8),
        /**
         * Casts the input value to a Float32
         * @param {number} in_value - The value to use in the cast
         * @return {number} The casted value
         * @protected
         */
        Float32: _simpleCastFunctor.bind(undefined, castArrays.Float32),
        /**
         * Casts the input value to a Float64
         * @param {number} in_value - The value to use in the cast
         * @return {number} The casted value
         * @protected
         */
        Float64: _simpleCastFunctor.bind(undefined, castArrays.Float64),
        /**
         * Casts the input value to a String
         * @param {number} in_value - The value to use in the cast
         * @return {number} The casted value
         * @protected
         */
        String: function (in_value) {
          return String(in_value);
        },
        /**
         * Casts the input value to a Boolean value
         * @param {boolean} in_value - The value to use in the cast
         * @return {boolean} The casted value
         * @protected
         */
        Boolean: function (in_value) {
          return !!in_value;
        }
      };
      exportModule('LYNX.Property._castFunctors', _castFunctors);
    }());
    (function () {
      var ValueProperty = include('LYNX.Property.ValueProperty');
      var _castFunctors = include('LYNX.Property._castFunctors');
      /**
       * A primitive property for an unsigned 8 bit integer value.
       * @param {Object=} in_params - the parameters
       * @constructor
       * @extends LYNX.Property.ValueProperty
       * @alias LYNX.Property.Uint8Property
       */
      var Uint8Property = function (in_params) {
        ValueProperty.call(this, in_params);
        // default for this property type is '0'
        this._data = 0;
      };
      Uint8Property.prototype = Object.create(ValueProperty.prototype);
      Uint8Property.prototype._castFunctor = _castFunctors.Uint8;
      exportModule('LYNX.Property.Uint8Property', Uint8Property);
      /**
       * A primitive property for an unsigned 16 bit integer value.
       * @param {Object=} in_params - the parameters
       * @constructor
       * @extends LYNX.Property.ValueProperty
       * @alias LYNX.Property.Uint16Property
       */
      var Uint16Property = function (in_params) {
        ValueProperty.call(this, in_params);
        // default for this property type is '0'
        this._data = 0;
      };
      Uint16Property.prototype = Object.create(ValueProperty.prototype);
      Uint16Property.prototype._castFunctor = _castFunctors.Uint16;
      exportModule('LYNX.Property.Uint16Property', Uint16Property);
      /**
       * A primitive property for an unsigned 32 bit integer value.
       * @param {Object=} in_params - the parameters
       * @constructor
       * @extends LYNX.Property.ValueProperty
       * @alias LYNX.Property.Uint32Property
       */
      var Uint32Property = function (in_params) {
        ValueProperty.call(this, in_params);
        // default for this property type is '0'
        this._data = 0;
      };
      Uint32Property.prototype = Object.create(ValueProperty.prototype);
      Uint32Property.prototype._castFunctor = _castFunctors.Uint32;
      exportModule('LYNX.Property.Uint32Property', Uint32Property);
    }());
    (function () {
      var BaseProperty = include('LYNX.Property.BaseProperty');
      var DataArrays = include('LYNX.Utils.Datastructures.DataArrays');
      var ArrayChangeSetIterator = include('LYNX.Property.ChangeSetOperations.ArrayOperations.ArrayChangeSetIterator');
      var ChangeSet = include('LYNX.Property.ChangeSet');
      var TypeIdHelper = include('LYNX.Property.TypeIdHelper');
      var _ = include('underscore');
      var MSG = include('LYNX.Property._constants.MSG');
      var Property = include('LYNX.Property', ['PropertyFactory']);
      var PathHelper = include('LYNX.Property.PathHelper');
      var deepCopy = include('LYNX.Utils.deepCopy');
      var Int64 = include('LYNX.Utils.Datastructures.Int64');
      var Uint64 = include('LYNX.Utils.Datastructures.Uint64');
      /**
       * @param {Object=}  in_params - the parameters
       * @param {Number=}  [in_params.length=0] the length of the array, if applicable
       * @param {Number=}  [in_params.length=0] the length of the array, if applicable
       * @param {Object} in_constructor the constructor for the array data
       * @param {Boolean=} [in_primitiveType=false] Is this an array of primitive types?
       * @param {string|undefined} in_scope - The scope in which the property typeid is defined
       *
       * @constructor
       * @alias LYNX.Property.ArrayProperty
       */
      var ArrayProperty = function (in_params, in_constructor, in_primitiveType, in_scope) {
        BaseProperty.call(this, in_params);
        var length = in_params.size || in_params.length || 0;
        // changesets
        this._pendingChanges = {};
        this._dirtyChanges = {};
        if (in_constructor === Array || !in_primitiveType) {
          this._isCustomTypeArrayProperty = !in_primitiveType;
          this._dataArrayRef = new DataArrays.UniversalDataArray(length);
          if (this._isCustomTypeArrayProperty) {
            for (var i = 0; i < length; i++) {
              this._dataArrayRef.setValue(i, Property.PropertyFactory._createProperty(this.getTypeid(), in_scope));
            }
          } else {
            if (this.getTypeid() === 'String' || TypeIdHelper.isReferenceTypeId(this.getTypeid())) {
              for (var i = 0; i < length; i++) {
                this._dataArrayRef.setValue(i, '');
              }
            } else if (this.getTypeid() === 'Int64') {
              for (var i = 0; i < length; i++) {
                this._dataArrayRef.setValue(i, new Int64());
              }
            } else if (this.getTypeid() === 'Uint64') {
              for (var i = 0; i < length; i++) {
                this._dataArrayRef.setValue(i, new Uint64());
              }
            } else {
              throw new Error('Unknown typeid in array: ' + this.getTypeid());
            }
          }
        } else {
          this._dataArrayRef = new DataArrays.BaseDataArray(in_constructor, length);
        }
      };
      ArrayProperty.prototype = Object.create(BaseProperty.prototype);
      /**
       * Frees the cache for all entries behind the in_startIndex
       *
       * @param {number} in_startIndex - The first index for which the cache has to be cleared
       * @private
       */
      ArrayProperty.prototype._freeCacheAfterIndex = function (in_startIndex) {
        if (this._isCustomTypeArrayProperty) {
          for (var i = in_startIndex; i < this._dataArrayRef.length; i++) {
            this._dataArrayRef.getValue(i)._freeCache(true);
          }
        }
      };
      /**
       * @inheritdoc
       */
      ArrayProperty.prototype._freeCache = function (in_children) {
        // The caches
        if (this._structChange === BaseProperty._CACHES_INITIALLY_DISABLED) {
          return;
        }
        BaseProperty.prototype._freeCache.call(this, in_children);
        // do so recursively for all entries of the array too
        this._freeCacheAfterIndex(0);
      };
      /**
       * Returns the path segment for a child
       *
       * @param {LYNX.Property.BaseProperty} in_childNode - The child for which the path is returned
       *
       * @return {string|undefined} The path segment to resolve the child property under this property
       * @protected
       */
      ArrayProperty.prototype._getPathSegmentForChildNode = function (in_childNode) {
        if (!this._isCustomTypeArrayProperty) {
          throw new Error(MSG.NO_PATHS_FOR_NON_PRIMITIVE_ARRAYS);
        }
        var index = this._dataArrayRef.getBuffer().indexOf(in_childNode);
        if (index === -1) {
          throw new Error(MSG.GET_PATH_SEGMENT_CALLED_FOR_NON_ENTRY);
        }
        return '[' + index + ']';
      };
      /**
       * Resolves a direct child node based on the given path segment
       *
       * @param {String} in_segment                                   - The path segment to resolve
       * @param {LYNX.Property.PathHelper.TOKEN_TYPES} in_segmentType - The type of segment in the tokenized path
       * @param {Object} out_disableCache                            - The member disableCache will be set to true
       *                                                                in this object, if this path must not be cached
       *
       * @return {LYNX.Property.BaseProperty|undefined} The child property that has been resolved
       * @protected
       */
      ArrayProperty.prototype._resolvePathSegment = function (in_segment, in_segmentType, out_disableCache) {
        if (!this._isCustomTypeArrayProperty) {
          throw new Error(MSG.NO_PATHS_FOR_NON_PRIMITIVE_ARRAYS);
        }
        // Base Properties only support paths separated via dots
        if (in_segmentType === PathHelper.TOKEN_TYPES.ARRAY_TOKEN) {
          var index = parseInt(in_segment, 10);
          // Specifying a non-integer index is regarded a mal-formed path and thus throws an exception
          if (_.isNaN(index)) {
            throw new Error(MSG.INVALID_NON_NUMERIC_SEGMENT_IN_PATH);
          }
          // Accessing an entry outside of the array is just a non existing property and thus
          // returns undefined
          if (index < 0 || index >= this._dataArrayRef.length) {
            return undefined;
          }
          return this._dataArrayRef.getValue(index);
        } else {
          return BaseProperty.prototype._resolvePathSegment.call(this, in_segment, in_segmentType, out_disableCache);
        }
      };
      /**
       * Insert into the array at a given position
       * @param {number} in_position target index
       * @param {*} in_value inserted value (or property)
       */
      ArrayProperty.prototype.insert = function (in_position, in_value) {
        this.insertRange(in_position, [in_value]);
      };
      /**
       * Is this property a leaf node with regard to flattening?
       *
       * TODO: Which semantics should flattening have? It stops at primitive types and collections?
       *
       * @return {boolean} Is it a leaf with regard to flattening?
       */
      ArrayProperty.prototype._isFlattenLeaf = function () {
        return true;
      };
      /**
       * push at the end of the array
       * @param {*} in_value the value to be pushed
       */
      ArrayProperty.prototype.push = function (in_value) {
        this.insertRange(this._dataArrayRef.length, [in_value]);
      };
      /**
       * remove an element of the array
       * @param {*} in_position the index that will be removed
       */
      ArrayProperty.prototype.remove = function (in_position) {
        this.removeRange(in_position, 1);
      };
      /**
       * remove the last element of the array
       */
      ArrayProperty.prototype.pop = function () {
        this.remove(this._dataArrayRef.length - 1);
      };
      /**
       * change an existing element of the array
       * @param {number} in_position the target index
       * @param {*} in_value the new value
       */
      ArrayProperty.prototype.set = function (in_position, in_value) {
        this.setRange(in_position, [in_value]);
      };
      /**
       * TODO: Warning, the semantics of this function are undefined and will change in the future
       * It's also partially unimplemented for many cases (see LYNXDEV-835)
       *
       * @param {Array<*>} values Some values to assign to the array
       * @private
       */
      ArrayProperty.prototype._setValues = function (values) {
        this._checkIsNotReadOnly();
        if (this._isCustomTypeArrayProperty) {
          throw new Error('_setValues not implemented for custom type arrays.');
        }
        if (values.length < this._dataArrayRef.length) {
          this.removeRange(values.length, this._dataArrayRef.length - values.length);
        }
        var that = this;
        var maxIndex = this._dataArrayRef.length - 1;
        _.each(values, function (value, index) {
          if (index > maxIndex) {
            that.insert(index, value);
          } else {
            that.set(index, value);
          }
        });
      };
      /**
       * @return {Array<*>} Return all values in the ArrayProperty
       * WARNING: the returned array must be read only, data written to it
       * will get lost. Please use the modify and set commands instead.
       */
      ArrayProperty.prototype.getEntriesReadOnly = function () {
        return this._dataArrayRef.getBuffer();
      };
      /**
       * Private helper function to update the internal dirty and pending changes
       * is overwritten by StringProperty
       *
       * @param {LYNX.Property.SerializedChangeSet} in_changeSet - The changeset to apply
       * @private
       */
      ArrayProperty.prototype._updateChanges = function (in_changeSet) {
        ChangeSet.prototype._performApplyAfterOnPropertyArray(this._pendingChanges, in_changeSet, this._getTypeidForChangeSet(true));
        ChangeSet.prototype._performApplyAfterOnPropertyArray(this._dirtyChanges, in_changeSet, this._getTypeidForChangeSet(true));
      };
      /**
       * inserts the content of a given array into the array property
       * @param {number} in_offset target index
       * @param {Array<*>} in_array the array to be inserted
       */
      ArrayProperty.prototype.insertRange = function (in_offset, in_array) {
        this._checkIsNotReadOnly();
        this._insertRangeWithoutDirtying(in_offset, in_array);
        this._freeCacheAfterIndex(in_offset);
        this._markStructChanged();
        this._setDirty();
      };
      /**
       * inserts the content of a given array, but doesn't dirty the property
       * this is useful for batch changes
       * @param {number} in_offset target index
       * @param {Array<*>} in_array the array to be inserted
       * @param {Boolean=} [in_setParents=true] If true, set parent of inserted properties.
       *                   If false, caller has already set parents.
       * @private
       */
      ArrayProperty.prototype._insertRangeWithoutDirtying = function (in_offset, in_array, in_setParents) {
        if (in_setParents === undefined)
          in_setParents = true;
        if (in_offset < 0 || in_offset > this.length) {
          throw Error('ArrayProperty: insert range - Start offset is invalid: ' + in_offset);
        }
        if (in_setParents && this._isCustomTypeArrayProperty) {
          for (var i = 0; i < in_array.length; ++i) {
            if (in_array[i].getParent()) {
              throw new Error('Trying to insert a property into an array that already has a parent.');
            } else {
              in_array[i]._setParent(this);
            }
          }
        }
        this._dataArrayRef.insertRange(in_offset, in_array);
        // Insert entry into changesets
        var changeSet = {
          'insert': [[
              in_offset,
              this._serializeArray(in_array)
            ]]
        };
        this._updateChanges(changeSet);
      };
      /**
       * removes a given number of elements from the array property
       * @param {number} in_offset target start index
       * @param {number} in_deleteCount number of elements to be deleted
       */
      ArrayProperty.prototype.removeRange = function (in_offset, in_deleteCount) {
        this._checkIsNotReadOnly();
        this._removeRangeWithoutDirtying(in_offset, in_deleteCount);
        this._freeCacheAfterIndex(in_offset);
        this._markStructChanged();
        this._setDirty();
      };
      /**
       * removes a given number of elements from the array property, but doesn't dirty the property
       * this is useful for batch changes
       * @param {number} in_offset target start index
       * @param {number} in_deleteCount number of elements to be deleted
       * @private
       */
      ArrayProperty.prototype._removeRangeWithoutDirtying = function (in_offset, in_deleteCount) {
        if (this._isCustomTypeArrayProperty) {
          for (var i = 0; i < in_deleteCount; ++i) {
            if (this._dataArrayRef.getValue(in_offset + i).getParent() !== this) {
              throw new Error('Trying to remove a property from an array that has not the array as parent.');
            } else {
              this._dataArrayRef.getValue(in_offset + i)._setParent(undefined);
            }
          }
        }
        this._dataArrayRef.removeRange(in_offset, in_deleteCount);
        // Insert entry into changesets
        var changeSet = {
          'remove': [[
              in_offset,
              in_deleteCount
            ]]
        };
        this._updateChanges(changeSet);
      };
      /**
       * sets the array properties elements to the content of the given array
       * all changed elements must already exist
       * @param {number} in_offset target start index
       * @param {Array<*>} in_array contains the elements to be set
       */
      ArrayProperty.prototype.setRange = function (in_offset, in_array) {
        this._checkIsNotReadOnly();
        this._setRangeWithoutDirtying(in_offset, in_array);
        this._markStructChanged();
        this._setDirty();
      };
      /**
       * sets the array properties elements to the content of the given array
       * all changed elements must already exist. This version doesn't dirty the property,
       * which is useful for batch changes
       * @param {number} in_offset target start index
       * @param {Array<*>} in_array contains the elements to be set
       */
      ArrayProperty.prototype._setRangeWithoutDirtying = function (in_offset, in_array) {
        this._modifyRangeWithoutDirtying(in_offset, in_array);
      };
      /**
       * sets the array properties elements to the content of the given array
       * all changed elements must already exist. This version doesn't dirty the property,
       * which is useful for batch changes
       * @param {number} in_offset target start index
       * @param {Array<*>} in_array contains the elements to be set
       */
      ArrayProperty.prototype._modifyRangeWithoutDirtying = function (in_offset, in_array) {
        // Has to be overloaded for arrays of properties!
        if (in_offset < 0) {
          throw Error('ArrayProperty: Modify range - Start offset is negative: ' + in_offset);
        }
        var changeSet = {};
        if (this._isCustomTypeArrayProperty) {
          // for custom array properties, we have to do a remove/insert instead:
          this._removeRangeWithoutDirtying(in_offset, in_array.length);
          this._insertRangeWithoutDirtying(in_offset, in_array);
        } else {
          this._dataArrayRef.set(in_offset, in_array);
          changeSet = {
            'modify': [[
                in_offset,
                this._serializeArray(in_array)
              ]]
          };
          this._updateChanges(changeSet);
        }
      };
      /**
       * Returns the name of all the sub-properties of this property.
       *
       * @return {Array.<string>} An array of all the property ids
       */
      ArrayProperty.prototype.getIds = function () {
        return Object.keys(this._dataArrayRef._buffer);
      };
      /**
       * get the array element at a given index
       * @param {number} in_position the target index
       * @return {*} the element at that index
       */
      ArrayProperty.prototype.get = function (in_position) {
        return this._dataArrayRef.getValue(in_position);
      };
      /**
       * @return {Number} the current length of the array
       */
      ArrayProperty.prototype.getLength = function () {
        return this._dataArrayRef.length;
      };
      /**
       * Modifies the property according to the given changeset
       *
       * @param {LYNX.Property.SerializedChangeSet} in_changeSet - The changeset to apply
       * @param {boolean} [in_reportToView = true] - By default, the dirtying will always be reported to the checkout view
       *                                             and trigger a modified event there. When batching updates, this
       *                                             can be prevented via this flag.
       */
      ArrayProperty.prototype._applyChangeset = function (in_changeSet, in_reportToView) {
        this._checkIsNotReadOnly();
        // Iterator to process the changes in the ChangeSet in the correct order
        var arrayIterator = new ArrayChangeSetIterator(in_changeSet);
        if (this._isCustomTypeArrayProperty) {
          var structChanged = false;
          var firstChangedIndex = Infinity;
          // Successively apply the changes from the changeSet
          while (!arrayIterator.atEnd()) {
            switch (arrayIterator.type) {
            case ArrayChangeSetIterator.types.INSERT:
              // Handle inserts
              var propertyDescriptions = arrayIterator.operation[1];
              var insertedPropertyInstances = [];
              var scope = this._getScope();
              for (var i = 0; i < propertyDescriptions.length; ++i) {
                var createdProperty = Property.PropertyFactory._createProperty(propertyDescriptions[i]['typeid'], scope);
                // Set parent so scope is defined for deserialization
                createdProperty._setParent(this);
                createdProperty._deserialize(propertyDescriptions[i], false);
                insertedPropertyInstances.push(createdProperty);
              }
              this._insertRangeWithoutDirtying(arrayIterator.operation[0] + arrayIterator.offset, insertedPropertyInstances, false);
              structChanged = true;
              firstChangedIndex = Math.min(arrayIterator.operation[0], firstChangedIndex);
              break;
            case ArrayChangeSetIterator.types.REMOVE:
              // Handle removes
              var numRemoved = arrayIterator.operation[1];
              if (!_.isNumber(numRemoved)) {
                numRemoved = numRemoved.length;
              }
              this._removeRangeWithoutDirtying(arrayIterator.operation[0] + arrayIterator.offset, numRemoved);
              structChanged = true;
              firstChangedIndex = Math.min(arrayIterator.operation[0], firstChangedIndex);
              break;
            case ArrayChangeSetIterator.types.MODIFY:
              // Handle modifies
              var propertyDescriptions = arrayIterator.operation[1];
              var startIndex = arrayIterator.operation[0] + arrayIterator.offset;
              for (var i = 0; i < propertyDescriptions.length; ++i) {
                var modifiedProperty = this.get(startIndex + i);
                if (!modifiedProperty) {
                  throw new Error('modified property - index invalid: ' + (startIndex + i));
                }
                modifiedProperty._applyChangeset(propertyDescriptions[i], false);
              }
              break;
            default:
              console.error('applyChangeset: ' + MSG.UNKNOWN_OPERATION);
            }
            arrayIterator.next();
          }
          // If something has been inserted and deleted, the struct has changed and this has to
          // be reported
          if (structChanged) {
            this._markStructChanged();
            this._freeCacheAfterIndex(firstChangedIndex);
          }
        } else {
          // Successively apply the changes from the changeSet
          while (!arrayIterator.atEnd()) {
            switch (arrayIterator.type) {
            case ArrayChangeSetIterator.types.INSERT:
              // Handle inserts
              this._insertRangeWithoutDirtying(arrayIterator.operation[0] + arrayIterator.offset, arrayIterator.operation[1]);
              break;
            case ArrayChangeSetIterator.types.REMOVE:
              // Handle removes
              var removeLength = arrayIterator.operation[1];
              if (_.isArray(removeLength)) {
                removeLength = removeLength.length;
              }
              this._removeRangeWithoutDirtying(arrayIterator.operation[0] + arrayIterator.offset, removeLength);
              break;
            case ArrayChangeSetIterator.types.MODIFY:
              // Handle modifies
              this._modifyRangeWithoutDirtying(arrayIterator.operation[0] + arrayIterator.offset, arrayIterator.operation[1]);
              break;
            default:
              console.error('applyChangeset: ' + MSG.UNKNOWN_OPERATION);
            }
            arrayIterator.next();
          }
        }
        // Finally mark the property as dirty (we postponed this in the previous operations to prevent multiple triggering
        // of dirtying events)
        this._setDirty(in_reportToView);
      };
      /**
       * Removes the dirtiness flag from this property
       * @param {LYNX.Property.BaseProperty.MODIFIED_STATE_FLAGS} [in_flags] - The flags to clean, if none are supplied all
       *                                                                       will be removed
       * @private
       */
      ArrayProperty.prototype._cleanDirty = function (in_flags) {
        // Invoke parent - cleans own dirty flag
        BaseProperty.prototype._cleanDirty.call(this, in_flags);
        if (in_flags === undefined || (in_flags & BaseProperty.MODIFIED_STATE_FLAGS.PENDING_CHANGE) !== 0) {
          this._pendingChanges = {
            insert: [],
            remove: [],
            modify: []
          };
        }
        if (in_flags === undefined || (in_flags & BaseProperty.MODIFIED_STATE_FLAGS.DIRTY) !== 0) {
          this._dirtyChanges = {
            insert: [],
            remove: [],
            modify: []
          };
        }
      };
      /**
       * Removes the dirtiness flag from this property and recursively from all of its children
       *
       * @param {LYNX.Property.BaseProperty.MODIFIED_STATE_FLAGS} [in_dirtinessType] - The flags to clean,
       * if none are supplied all will be removed
       */
      ArrayProperty.prototype.cleanDirty = function (in_dirtinessType) {
        if (this._isCustomTypeArrayProperty) {
          for (var i = 0; i < this._dataArrayRef.length; ++i) {
            this._dataArrayRef.getValue(i).cleanDirty(in_dirtinessType);
          }
        }
        // after all paths are clean, we are also clean!
        this._cleanDirty(in_dirtinessType);
      };
      /**
       * Given a list of non-overlapping, unordered segments, each identified by its start point and length,
       * this function computes the longest monotone, increasing sub-sequence of segments.
       *
       * Currently, this is O(n^2) in the worst case, it could be implemented in O(n log n), but I would have
       * to implement a binary search tree for this. If this becomes a bottle-neck, we should replace
       * the insertions and binary searches below, with a search tree.
       *
       * @param {Array.<number>} in_segmentStarts  - The starting points of the segments
       * @param {Array.<number>} in_segmentLengths - The lengths of the segments
       *
       * @return {Array.<Number>} List of the selected segments, given as indices of the segments
       * @private
       */
      var _getLongestIncreasingSubsequenceSegments = function (in_segmentStarts, in_segmentLengths) {
        if (in_segmentStarts.length === 0) {
          return [];
        }
        // Contains the already found sub sequences, sorted by their length
        // in increasing order. These sub-sequences have the invariant that
        // the last entry in each of the sequences is smaller than the last
        // entry in longer sequences (so the list is also sorted according to
        // the sequenceLastEntry member of the structs)
        var foundSubSequences = [];
        for (var i = 0; i < in_segmentStarts.length; i++) {
          var currentSegmentStart = in_segmentStarts[i];
          // Perform a binary search to find the largest entry in the list of found sub
          // sequences that has a sequenceEnd that is smaller or equal than currentSegmentStart
          var index = _.sortedIndex(foundSubSequences, { sequenceLastEntry: currentSegmentStart }, 'sequenceLastEntry');
          var lastEntry = index > 0 ? foundSubSequences[index - 1] : undefined;
          // Create a new entry that is obtained by concatenating the longest sequence found so far
          // with the new segment
          var newEntry = {
            sequenceLength: in_segmentLengths[i] + (lastEntry ? lastEntry.sequenceLength : 0),
            segmentIndex: i,
            sequenceLastEntry: currentSegmentStart + in_segmentLengths[i] - 1,
            previousEntry: lastEntry
          };
          // Search for the insertion position for this entry
          var insertionPoint = _.sortedIndex(foundSubSequences, newEntry, 'sequenceLength');
          if (foundSubSequences[insertionPoint] !== undefined && foundSubSequences[insertionPoint].sequenceLength === newEntry.sequenceLength) {
            insertionPoint++;
          }
          // We have to delete all entries from the foundSubSequences array, which
          // are shorter, but have a higher sequenceLastEntry (we can do that, since
          // it would be a better choice to use the new entry instead of these old
          // entries). This will preserve the invariant on the foundSubSequences
          // that they are sorted with respect to the sequenceLastEntry.
          var lowerLengthBoundary = newEntry.sequenceLength - in_segmentLengths[i];
          var j = insertionPoint - 1;
          for (; j >= 0 && foundSubSequences[j].sequenceLength > lowerLengthBoundary; j--) {
            if (foundSubSequences[j].sequenceLastEntry >= newEntry.sequenceLastEntry) {
              foundSubSequences.splice(j, 1);
              insertionPoint--;
            }
          }
          // Insert the entry as a new entry into the list of subsequences
          foundSubSequences.splice(insertionPoint, 0, newEntry);  // TODO: this should be done via a binary tree
        }
        // This should always be the case, as we checked for empty inputs above
        console.assert(foundSubSequences.length > 0);
        // Extract the result
        var longestSequence = [];
        var currentSegment = _.last(foundSubSequences);
        while (currentSegment) {
          longestSequence.unshift(currentSegment.segmentIndex);
          currentSegment = currentSegment.previousEntry;
        }
        return longestSequence;
      };
      /**
       * Internal helper function that implements the deserialize algorithm for an array of named properties.
       *
       * @param {LYNX.Property.SerializedChangeSet} in_serializedObj - The serialized changeset to apply. This
       *     has to be an normalized change-set (only containing inserts. Removes and Modifies are forbidden).
       * @param {boolean} [in_reportToView = true] - By default, the dirtying will always be reported to the checkout view
       *                                             and trigger a modified event there. When batching updates, this
       *                                             can be prevented via this flag.
       * @return {LYNX.Property.SerializedChangeSet} ChangeSet with the changes that actually were performed during the
       *     deserialization
       */
      ArrayProperty.prototype._deserializeNamedPropertyArray = function (in_serializedObj, in_reportToView) {
        if (!_.isArray(in_serializedObj.insert[0][1])) {
          throw new Error(MSG.INVALID_CHANGESET);
        }
        // When the array contains named properties, we can use an efficient diffing algorithm, which
        // takes advantage of the ability to identify entries in an unique way
        var targetArray = in_serializedObj.insert[0][1];
        // The algorithm below finds the mapping between the two given arrays which requires the smallest number of
        // inserted and removed entries. These operations are determined via the following strategy:
        // 1) We search for all consecutive segments in the input data, which map to a consecutive segments in the
        //    target array.
        // 2) We search for the longest sequence of consecutive segments in the input data which are all starting at
        //    monotone increasing points in the target array. These are the segments which will remain unmodified
        //    by insertion/removals
        // 3) Finally, we determine the necessary insertion and remove operations to fill in/remove the entries between
        //    these segments and compute modify instructions within the segments.
        // 1) Map the GUIDs in the input ChangeSet to indices
        var resultGuidToIndexMap = {};
        for (var i = 0; i < targetArray.length; i++) {
          var insertedProperty = targetArray[i];
          if (insertedProperty['String'] === undefined || insertedProperty['String']['guid'] === undefined) {
            throw new Error(MSG.MISSING_GUID_IN_NORMALIZED_CHANGESET);
          }
          var guid = insertedProperty['String']['guid'];
          // since the spec allows alternatively other changeset formats for strings, we have to support them here:
          if (!_.isString(guid) && insertedProperty['String']['guid'].insert) {
            guid = insertedProperty['String']['guid'].insert[0][1];
          }
          resultGuidToIndexMap[guid] = i;
        }
        var initialArrayLength = this._dataArrayRef.length;
        // Collect consecutive segments
        var segmentStartPointsInInitialArray = [];
        var segmentStartPointsInTargetArray = [];
        var segmentLengths = [];
        var segmentInterruped = false;
        for (var i = 0; i < initialArrayLength; i++) {
          // Get the GUID of the entry
          var guid = this._dataArrayRef.getValue(i).getGuid();
          // Check where it is stored in the target array
          var index = resultGuidToIndexMap[guid];
          if (index !== undefined) {
            // Check whether we can append the entry to the existing sequence
            if (!segmentInterruped && segmentStartPointsInTargetArray.length > 0 && _.last(segmentStartPointsInTargetArray) + _.last(segmentLengths) === index) {
              // In that case we just increase the length of the segment
              segmentLengths[segmentLengths.length - 1]++;
            } else {
              // Create a new segment
              segmentStartPointsInInitialArray.push(i);
              segmentStartPointsInTargetArray.push(index);
              segmentLengths.push(1);
              segmentInterruped = false;
            }
          } else {
            segmentInterruped = true;
          }
        }
        // 2) Get all segments in the array which we will keep (we try to keep as many as possible, so this maps
        // to finding the longest monotone increasing sequence of sub-segments)
        var orderedSegments = _getLongestIncreasingSubsequenceSegments(segmentStartPointsInTargetArray, segmentLengths);
        // 3) Now we have to convert this sequence of ordered segments to insert and remove commands
        var changes = {};
        var lastPositionInInitialArray = 0;
        var lastPositionInTargetArray = 0;
        var offset = 0;
        var structChanged = false;
        var firstChangedIndex = Infinity;
        for (var i = 0; i <= orderedSegments.length; i++) {
          var startPointInInitialArray, startPointInTargetArray, segmentLength;
          var offsetChange = 0;
          if (i < orderedSegments.length) {
            // Extract the information about the currently processed segment.
            startPointInInitialArray = segmentStartPointsInInitialArray[orderedSegments[i]];
            startPointInTargetArray = segmentStartPointsInTargetArray[orderedSegments[i]];
            segmentLength = segmentLengths[orderedSegments[i]];
          } else {
            // Special case to handle the end of the sequence: We add a segment of length 0 at the end
            startPointInInitialArray = initialArrayLength;
            startPointInTargetArray = targetArray.length;
            segmentLength = 0;
          }
          // If the start point of the segment in the initial array is larger than the last point we processed, we have
          // to remove the elements between the two points
          if (startPointInInitialArray > lastPositionInInitialArray) {
            changes.remove = changes.remove || [];
            changes.remove.push([
              lastPositionInInitialArray,
              startPointInInitialArray - lastPositionInInitialArray
            ]);
            this._removeRangeWithoutDirtying(lastPositionInInitialArray + offset, startPointInInitialArray - lastPositionInInitialArray);
            structChanged = true;
            firstChangedIndex = Math.min(lastPositionInInitialArray + offset, firstChangedIndex);
            offsetChange -= startPointInInitialArray - lastPositionInInitialArray;
          }
          // If the start point of the segment in the target array is larger than the last point we processed, we have
          // to insert the elements between the two points
          if (startPointInTargetArray > lastPositionInTargetArray) {
            changes.insert = changes.insert || [];
            changes.insert.push([
              lastPositionInInitialArray,
              deepCopy(targetArray.slice(lastPositionInTargetArray, startPointInTargetArray))
            ]);
            var insertedProperties = [];
            var scope = this._getScope();
            for (var j = lastPositionInTargetArray; j < startPointInTargetArray; ++j) {
              var createdProperty = Property.PropertyFactory._createProperty(targetArray[j]['typeid'], scope);
              // Set parent so scope is defined for deserialization
              createdProperty._setParent(this);
              createdProperty._deserialize(targetArray[j], false);
              insertedProperties.push(createdProperty);
            }
            this._insertRangeWithoutDirtying(lastPositionInInitialArray + offset, insertedProperties, false);
            structChanged = true;
            firstChangedIndex = Math.min(lastPositionInInitialArray + offset, firstChangedIndex);
            offsetChange += insertedProperties.length;
          }
          // Update the last processed points
          lastPositionInInitialArray = startPointInInitialArray + segmentLength;
          lastPositionInTargetArray = startPointInTargetArray + segmentLength;
          offset += offsetChange;
          // Recursively check the entries within the segment for modifications
          for (var j = 0; j < segmentLength; j++) {
            var existingEntry = this._dataArrayRef.getValue(startPointInInitialArray + j + offset);
            var entryChanges = existingEntry._deserialize(targetArray[startPointInTargetArray + j], false);
            // We had changes which we have to report back
            if (!ChangeSet.isEmptyChangeSet(entryChanges)) {
              // Make sure, the ChangeSet contains the typeid of the modified entry
              entryChanges.typeid = existingEntry._getTypeidForChangeSet();
              if (!changes.modify) {
                changes.modify = [[
                    startPointInInitialArray + j,
                    [entryChanges]
                  ]];
              } else {
                var lastModifiedSequence = _.last(changes.modify);
                if (lastModifiedSequence[0] + lastModifiedSequence[1].length === startPointInInitialArray + j) {
                  lastModifiedSequence[1].push(entryChanges);
                } else {
                  changes.modify.push([
                    startPointInInitialArray + j,
                    [entryChanges]
                  ]);
                }
              }
            }
          }
        }
        // If entries have been inserted or removed, we have to record the struct change
        if (structChanged) {
          this._markStructChanged();
          this._freeCacheAfterIndex(firstChangedIndex);
        }
        // If there were any changes, we have to mark this property as dirty
        if (!ChangeSet.isEmptyChangeSet(changes)) {
          this._setDirty(in_reportToView);
        }
        return changes;
      };
      /**
       * Function to deserialize special primitive types.
       * Some primitive types (e.g. Int64, which is not naively supported by javascript) require
       * special treatment on deserialization. For supported types, we can just return the input here.
       *
       * @param {LYNX.Property.SerializedChangeSet} in_serializedObj - The object to be deserialized
       * @return {*} the deserialized value
       */
      ArrayProperty.prototype._deserializeValue = function (in_serializedObj) {
        return in_serializedObj;
      };
      /**
       * Function to serialize special primitive types.
       * Some primitive types (e.g. Int64, which is not naively supported by javascript) require
       * special treatment on serialization. For supported types, we can just return the input here.
       *
       * @param {*} in_obj - The object to be serialized
       * @return {LYNX.Property.SerializedChangeSet} the serialized object
       */
      ArrayProperty.prototype._serializeValue = function (in_obj) {
        return in_obj;
      };
      /**
       * Function to serialize arrays of special primitive types.
       * Some primitive types (e.g. Int64, which is not naively supported by javascript) require
       * special treatment on serialization. For supported types, we can just return the input here.
       *
       * @param {Array} in_array - The array of special objects to be serialized
       * @return {Array<LYNX.Property.SerializedChangeSet>} the serialized object
       */
      ArrayProperty.prototype._serializeArray = function (in_array) {
        return in_array;
      };
      /**
       * Function to deserialize arrays of special primitive types.
       * Some primitive types (e.g. Int64, which is not naively supported by javascript) require
       * special treatment on deserialization. For supported types, we can just return the input here.
       *
       * @param {Array<LYNX.Property.SerializedChangeSet>} in_serializedObj the serialized object
       * @return {Array} in_array - The array of special objects that were deserialized
       */
      ArrayProperty.prototype._deserializeArray = function (in_serializedObj) {
        return in_serializedObj;
      };
      /**
       * Sets the property to the state in the given normalized changeset
       *
       * @param {LYNX.Property.SerializedChangeSet} in_serializedObj - The serialized changeset to apply. This
       *     has to be an normalized change-set (only containing inserts. Removes and Modifies are forbidden).
       * @param {boolean} [in_reportToView = true] - By default, the dirtying will always be reported to the checkout view
       *                                             and trigger a modified event there. When batching updates, this
       *                                             can be prevented via this flag.
       * @return {LYNX.Property.SerializedChangeSet} ChangeSet with the changes that actually were performed during the
       *     deserialization
       */
      ArrayProperty.prototype._deserialize = function (in_serializedObj, in_reportToView) {
        this._checkIsNotReadOnly();
        if (in_serializedObj.remove && in_serializedObj.remove.length > 0 || in_serializedObj.modify && in_serializedObj.modify.length > 0 || in_serializedObj.insert && (in_serializedObj.insert.length > 1 || in_serializedObj.insert.length === 1 && (in_serializedObj.insert[0][0] !== 0 || !_.isArray(in_serializedObj.insert[0][1])))) {
          throw new Error(MSG.NO_NORMALIZED_CHANGESET);
        }
        var arrayLength = this._dataArrayRef.length;
        if (!in_serializedObj.insert || !in_serializedObj.insert[0]) {
          // we've got an empty object, so we have to wipe everything we've got
          if (arrayLength > 0) {
            this._dataArrayRef.removeRange(0, arrayLength);
            this._markStructChanged();
            this._setDirty(in_reportToView);
            return {
              remove: [[
                  0,
                  arrayLength
                ]]
            };
          } else {
            // the array was already empty, nothing has changed
            return {};
          }
        }
        var scope = this._getScope();
        if (Property.PropertyFactory.inheritsFrom(this.getTypeid(), 'NamedProperty', { scope: scope })) {
          return this._deserializeNamedPropertyArray(in_serializedObj, in_reportToView);
        } else {
          // most simplistic diff method: Remove all existing data and insert the new data
          var simpleChanges = { insert: in_serializedObj.insert };
          if (arrayLength > 0) {
            simpleChanges.remove = [[
                0,
                arrayLength
              ]];
          }
          if (this._isCustomTypeArrayProperty) {
            var propertyDescriptions = in_serializedObj.insert[0][1];
            var result = [];
            for (var i = 0; i < propertyDescriptions.length; ++i) {
              var createdProperty = Property.PropertyFactory._createProperty(propertyDescriptions[i]['typeid'], scope);
              createdProperty._deserialize(propertyDescriptions[i], false);
              createdProperty._setParent(this);
              result.push(createdProperty);
            }
            this._dataArrayRef.removeRange(0, this._dataArrayRef.length);
            this._dataArrayRef.insertRange(0, result);
          } else {
            // Check, whether there has been any change in the array at all
            if (in_serializedObj.insert[0][1].length === this._dataArrayRef.length) {
              // We have to compare the two buffers
              var buffer = this._dataArrayRef.getBuffer();
              var changeSetArray = in_serializedObj.insert[0][1];
              var equal = true;
              for (var i = 0; i < buffer.length; i++) {
                if (buffer[i] !== changeSetArray[i]) {
                  equal = false;
                  break;
                }
              }
              if (equal) {
                return {};
              }
            }
            // set the actual array values to our array
            this._dataArrayRef.deserialize(this._deserializeArray(in_serializedObj.insert[0][1]));
          }
          // Since we don't compute a diff, we always have to assume the array structure has been changed
          this._markStructChanged();
          // Finally report the dirtiness to the view (we postponed this above)
          this._setDirty(in_reportToView);
          return simpleChanges;
        }
      };
      ArrayProperty.prototype._getChangesetForCustomTypeArray = function (in_basePropertyChangeset, in_dirtinessType, in_includeReferencedRepositories) {
        var result = {};
        if (in_basePropertyChangeset.remove && in_basePropertyChangeset.remove.length > 0) {
          result.remove = deepCopy(in_basePropertyChangeset.remove);
        }
        // get the iterator over the changes:
        var iterator = new ArrayChangeSetIterator(in_basePropertyChangeset);
        var currentArrayIndex = 0;
        var currentArraySize = this._dataArrayRef.length;
        var op, opStartIndex;
        while (!iterator.atEnd() || currentArrayIndex < currentArraySize) {
          if (!iterator.atEnd()) {
            op = iterator;
            opStartIndex = op.operation[0] + op.offset;
          } else {
            // no more ops
            op = { offset: iterator.offset };
            opStartIndex = Number.MAX_VALUE;
          }
          if (currentArrayIndex < opStartIndex) {
            // not in the influence of an insert or remove
            // we have to check if the element was modified (since that is not tracked)
            if (this._dataArrayRef.getValue(currentArrayIndex)._isDirty(in_dirtinessType)) {
              // check if we can combine modifies:
              var lastModify = undefined;
              if (result.modify && result.modify.length > 0) {
                lastModify = result.modify[result.modify.length - 1];
                if (lastModify[0] + lastModify[1].length === currentArrayIndex - op.offset) {
                } else {
                  lastModify = undefined;
                }
              }
              if (lastModify) {
                lastModify[1].push(this._dataArrayRef.getValue(currentArrayIndex).serialize({
                  'dirtyOnly': true,
                  'includeRootTypeid': true,
                  'dirtinessType': in_dirtinessType,
                  'includeReferencedRepositories': in_includeReferencedRepositories
                }));
              } else {
                // begin new modify
                if (!result.modify) {
                  result.modify = [];
                }
                result.modify.push([
                  currentArrayIndex - op.offset,
                  [this._dataArrayRef.getValue(currentArrayIndex).serialize({
                      'dirtyOnly': true,
                      'includeRootTypeid': true,
                      'dirtinessType': in_dirtinessType,
                      'includeReferencedRepositories': in_includeReferencedRepositories
                    })]
                ]);
              }
              currentArrayIndex++;
              // add more immediate modifies if possible
              while (currentArrayIndex < currentArraySize && currentArrayIndex < opStartIndex && this._dataArrayRef.getValue(currentArrayIndex)._isDirty(in_dirtinessType)) {
                result.modify[result.modify.length - 1][1].push(this._dataArrayRef.getValue(currentArrayIndex).serialize({
                  'dirtyOnly': true,
                  'includeRootTypeid': true,
                  'dirtinessType': in_dirtinessType,
                  'includeReferencedRepositories': in_includeReferencedRepositories
                }));
                currentArrayIndex++;
              }
            } else {
              currentArrayIndex++;
            }
          } else if (currentArrayIndex === opStartIndex) {
            // handle the op
            if (op.type === ArrayChangeSetIterator.types.REMOVE) {
              // no need to do something (removes are just copied)
              iterator.next();  // we've completely consumed that op
            } else if (op.type === ArrayChangeSetIterator.types.INSERT) {
              // we have to convert the inserts:
              var currentInsert = op.operation;
              var newInsert = [
                currentInsert[0],
                []
              ];
              for (var j = 0; j < currentInsert[1].length; ++j) {
                // TODO: we don't use the data from the changeset anymore, since we directly
                // TODO: read the data from the array now - remove the data from the op and
                // TODO: replace it with just the length instead  (LYNXDEV-833)
                if (!this._dataArrayRef.getValue(opStartIndex + j)) {
                  throw new Error('insert: invalid index');
                }
                newInsert[1].push(this._dataArrayRef.getValue(opStartIndex + j).serialize({
                  'dirtyOnly': false,
                  'includeRootTypeid': true,
                  'dirtinessType': in_dirtinessType,
                  'includeReferencedRepositories': in_includeReferencedRepositories
                }));
              }
              if (!result.insert) {
                result.insert = [];
              }
              result.insert.push(newInsert);
              currentArrayIndex += currentInsert[1].length;
              // we've read and used these entries above
              iterator.next();  // we've completely consumed that op
            }
          }
        }
        return result;
      };
      /**
       * Serialize the property
       *
       * @param {boolean} in_dirtyOnly -
       *     Only include dirty entries in the serialization
       * @param {boolean} in_includeRootTypeid -
       *     Include the typeid of the root of the hierarchy - has no effect for ArrayProperty
       * @param {LYNX.Property.BaseProperty.MODIFIED_STATE_FLAGS} [in_dirtinessType] -
       *     The type of dirtiness to use when reporting dirty changes. By default this is
       *     PENDING_CHANGE
       * @param {boolean} [in_includeReferencedRepositories=false] - If this is set to true, the _serialize
       *     function will descend into referenced repositories. WARNING: if there are loops in the references
       *     this can result in an infinite loop
       *
       *
       * @return {Object} The serialized representation of this property
       * @private
       */
      ArrayProperty.prototype._serialize = function (in_dirtyOnly, in_includeRootTypeid, in_dirtinessType, in_includeReferencedRepositories) {
        var result = BaseProperty.prototype._serialize.call(this, in_dirtyOnly, in_includeRootTypeid, in_dirtinessType, in_includeReferencedRepositories);
        if (this._isCustomTypeArrayProperty) {
          if (in_dirtyOnly) {
            _.extend(result, in_dirtinessType === BaseProperty.MODIFIED_STATE_FLAGS.PENDING_CHANGE ? this._getChangesetForCustomTypeArray(this._pendingChanges, in_dirtinessType, in_includeReferencedRepositories) : this._getChangesetForCustomTypeArray(this._dirtyChanges, in_dirtinessType, in_includeReferencedRepositories));
            return result;
          } else {
            // returns just an insert with the current data
            if (this._dataArrayRef.length > 0) {
              result.insert = [];
              result.insert.push([
                0,
                []
              ]);
              // we have to convert the propertySet objects to changesets
              for (var i = 0; i < this._dataArrayRef.length; i++) {
                result.insert[0][1].push(this._dataArrayRef.getValue(i)._serialize(false, true, in_dirtinessType, in_includeReferencedRepositories));
              }
            }
            return result;
          }
        } else if (in_dirtyOnly) {
          return in_dirtinessType === BaseProperty.MODIFIED_STATE_FLAGS.PENDING_CHANGE ? deepCopy(this._pendingChanges) : deepCopy(this._dirtyChanges);
        } else {
          // returns just an insert with the current data
          if (this._dataArrayRef.length > 0) {
            result.insert = [];
            result.insert.push([
              0,
              []
            ]);
            for (var i = 0; i < this._dataArrayRef.length; i++) {
              result.insert[0][1].push(this._serializeValue(this._dataArrayRef.getValue(i)));
            }
          }
          return result;
        }
      };
      /**
       * Repeatedly calls back the given function with human-readable string
       * representations of the property and of its sub-properties.
       * @param {string} indent - Leading spaces to create the tree representation
       * @param {string} externalId - Name of the current property at the upper level.
       *                              Used for arrays.
       * @param {function} printFct - Function to call for printing each property
       */
      ArrayProperty.prototype._prettyPrint = function (indent, externalId, printFct) {
        printFct(indent + externalId + this.getId() + ' (Array of ' + this.getTypeid() + '): [');
        if (this._isCustomTypeArrayProperty) {
          this._prettyPrintChildren(indent, printFct);
        } else {
          var childIndent = indent + '  ';
          var prefix = '';
          var suffix = '';
          if (this.getTypeid() === 'String') {
            prefix = '"';
            suffix = '"';
          }
          for (var i = 0; i < this._dataArrayRef.length; i++) {
            printFct(childIndent + i + ': ' + prefix + this._dataArrayRef.getValue(i) + suffix);
          }
        }
        printFct(indent + ']');
      };
      /**
       * Repeatedly calls back the given function with human-readable string
       * representations of the property's sub-properties.
       * @param {string} indent - Leading spaces to create the tree representation
       * @param {function} printFct - Function to call for printing each property
       */
      ArrayProperty.prototype._prettyPrintChildren = function (indent, printFct) {
        indent += '  ';
        var ids = this.getIds();
        for (var i = 0; i < ids.length; i++) {
          this.get(ids[i])._prettyPrint(indent, ids[i] + ': ', printFct);
        }
      };
      /**
       * Returns the full property type identifier for the ChangeSet including the array type id, if not
       * ommited by parameters
       * @param  {boolean} in_hideCollection - if true the collection type (if applicable) will be omitted
       * @return {string} The typeid
       * @private
       */
      ArrayProperty.prototype._getTypeidForChangeSet = function (in_hideCollection) {
        if (in_hideCollection) {
          return this._typeid;
        } else {
          return TypeIdHelper.createSerializationTypeId(this._typeid, 'array');
        }
      };
      /**
       * returns the length of the current array property
       */
      Object.defineProperty(ArrayProperty.prototype, 'length', {
        get: function () {
          return this.getLength();
        },
        set: function () {
          throw new Error(MSG.MODIFY_READ_ONLY);
        }
      });
      /**
       * An ArrayProperty which stores Float32 values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.ArrayProperty
       * @alias LYNX.Property.Float32ArrayProperty
       */
      var Float32ArrayProperty = function (in_params) {
        ArrayProperty.call(this, in_params, Float32Array, true);
      };
      Float32ArrayProperty.prototype = Object.create(ArrayProperty.prototype);
      /**
       * An ArrayProperty which stores Float64 values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.ArrayProperty
       * @alias LYNX.Property.Float64ArrayProperty
       */
      var Float64ArrayProperty = function (in_params) {
        ArrayProperty.call(this, in_params, Float64Array, true);
      };
      Float64ArrayProperty.prototype = Object.create(ArrayProperty.prototype);
      /**
       * An ArrayProperty which stores Uint8 values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.ArrayProperty
       * @alias LYNX.Property.Uint8ArrayProperty
       */
      var Uint8ArrayProperty = function (in_params) {
        ArrayProperty.call(this, in_params, Uint8Array, true);
      };
      Uint8ArrayProperty.prototype = Object.create(ArrayProperty.prototype);
      /**
       * An ArrayProperty which stores Int8 values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.ArrayProperty
       * @alias LYNX.Property.Int8ArrayProperty
       */
      var Int8ArrayProperty = function (in_params) {
        ArrayProperty.call(this, in_params, Int8Array, true);
      };
      Int8ArrayProperty.prototype = Object.create(ArrayProperty.prototype);
      /**
       * An ArrayProperty which stores Uint16 values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.ArrayProperty
       * @alias LYNX.Property.Uint16ArrayProperty
       */
      var Uint16ArrayProperty = function (in_params) {
        ArrayProperty.call(this, in_params, Uint16Array, true);
      };
      Uint16ArrayProperty.prototype = Object.create(ArrayProperty.prototype);
      /**
       * An ArrayProperty which stores Int16 values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.ArrayProperty
       * @alias LYNX.Property.Int16ArrayProperty
       */
      var Int16ArrayProperty = function (in_params) {
        ArrayProperty.call(this, in_params, Int16Array, true);
      };
      Int16ArrayProperty.prototype = Object.create(ArrayProperty.prototype);
      /**
       * An ArrayProperty which stores Uint32 values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.ArrayProperty
       * @alias LYNX.Property.Uint32ArrayProperty
       */
      var Uint32ArrayProperty = function (in_params) {
        ArrayProperty.call(this, in_params, Uint32Array, true);
      };
      Uint32ArrayProperty.prototype = Object.create(ArrayProperty.prototype);
      /**
       * An ArrayProperty which stores Int32 values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.ArrayProperty
       * @alias LYNX.Property.Int32ArrayProperty
       */
      var Int32ArrayProperty = function (in_params) {
        ArrayProperty.call(this, in_params, Int32Array, true);
      };
      Int32ArrayProperty.prototype = Object.create(ArrayProperty.prototype);
      /**
       * An ArrayProperty which stores Int64 values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @protected
       * @extends LYNX.Property.ArrayProperty
       * @alias LYNX.Property.Integer64ArrayProperty
       */
      var Integer64ArrayProperty = function (in_params) {
        ArrayProperty.call(this, in_params, Array, true);
      };
      Integer64ArrayProperty.prototype = Object.create(ArrayProperty.prototype);
      /**
       * Function to serialize special primitive types.
       * Some primitive types (e.g. Int64, which is not naively supported by javascript) require
       * special treatment on serialization. For supported types, we can just return the input here.
       *
       * @param {*} in_obj - The object to be serialized
       * @return {LYNX.Property.SerializedChangeSet} the serialized object
       */
      Integer64ArrayProperty.prototype._serializeValue = function (in_obj) {
        return [
          in_obj.getLow(),
          in_obj.getHigh()
        ];
      };
      /**
       * Function to serialize arrays of special primitive types.
       * Some primitive types (e.g. Int64, which is not naively supported by javascript) require
       * special treatment on serialization. For supported types, we can just return the input here.
       *
       * @param {Array} in_array - The array of special objects to be serialized
       * @return {Array<LYNX.Property.SerializedChangeSet>} the serialized object
       */
      Integer64ArrayProperty.prototype._serializeArray = function (in_array) {
        var result = [];
        for (var i = 0; i < in_array.length; i++) {
          result.push(this._serializeValue(in_array[i]));
        }
        return result;
      };
      /**
       * Function to deserialize arrays of special primitive types.
       * Some primitive types (e.g. Int64, which is not naively supported by javascript) require
       * special treatment on deserialization. For supported types, we can just return the input here.
       *
       * @param {Array<LYNX.Property.SerializedChangeSet>} in_serializedObj the serialized object
       * @return {Array} in_array - The array of special objects that were deserialized
       */
      Integer64ArrayProperty.prototype._deserializeArray = function (in_serializedObj) {
        var result = [];
        for (var i = 0; i < in_serializedObj.length; i++) {
          result.push(this._deserializeValue(in_serializedObj[i]));
        }
        return result;
      };
      /**
       * An ArrayProperty which stores Int64 values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.Integer64ArrayProperty
       * @alias LYNX.Property.Int64ArrayProperty
       */
      var Int64ArrayProperty = function (in_params) {
        Integer64ArrayProperty.call(this, in_params, Array, true);
      };
      Int64ArrayProperty.prototype = Object.create(Integer64ArrayProperty.prototype);
      /**
       * Specialized function to deserialize Int64 primitive types.
       * Some primitive types (e.g. Int64, which is not naively supported by javascript) require
       * special treatment on deserialization. For supported types, we can just return the input here.
       *
       * @param {LYNX.Property.SerializedChangeSet} in_serializedObj - The object to be deserialized
       * @return {Int64} the deserialized value
       */
      Int64ArrayProperty.prototype._deserializeValue = function (in_serializedObj) {
        return new Int64(in_serializedObj[0], in_serializedObj[1]);
      };
      /**
       * An ArrayProperty which stores Uint64 values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.Integer64ArrayProperty
       * @alias LYNX.Property.Uint64ArrayProperty
       */
      var Uint64ArrayProperty = function (in_params) {
        Integer64ArrayProperty.call(this, in_params, Array, true);
      };
      Uint64ArrayProperty.prototype = Object.create(Integer64ArrayProperty.prototype);
      /**
       * Specialized function to deserialize Uint64 primitive types.
       * Some primitive types (e.g. Uint64, which is not naively supported by javascript) require
       * special treatment on deserialization. For supported types, we can just return the input here.
       *
       * @param {LYNX.Property.SerializedChangeSet} in_serializedObj - The object to be deserialized
       * @return {Uint64} the deserialized value
       */
      Uint64ArrayProperty.prototype._deserializeValue = function (in_serializedObj) {
        return new Uint64(in_serializedObj[0], in_serializedObj[1]);
      };
      /**
       * An ArrayProperty which stores String values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.ArrayProperty
       * @alias LYNX.Property.StringArrayProperty
       */
      var StringArrayProperty = function (in_params) {
        ArrayProperty.call(this, in_params, Array, true);
      };
      StringArrayProperty.prototype = Object.create(ArrayProperty.prototype);
      exportModule('LYNX.Property.ArrayProperty', ArrayProperty);
      exportModule('LYNX.Property.Float32ArrayProperty', Float32ArrayProperty);
      exportModule('LYNX.Property.Float64ArrayProperty', Float64ArrayProperty);
      exportModule('LYNX.Property.Uint8ArrayProperty', Uint8ArrayProperty);
      exportModule('LYNX.Property.Int8ArrayProperty', Int8ArrayProperty);
      exportModule('LYNX.Property.Uint16ArrayProperty', Uint16ArrayProperty);
      exportModule('LYNX.Property.Int16ArrayProperty', Int16ArrayProperty);
      exportModule('LYNX.Property.Uint32ArrayProperty', Uint32ArrayProperty);
      exportModule('LYNX.Property.Int32ArrayProperty', Int32ArrayProperty);
      exportModule('LYNX.Property.Int64ArrayProperty', Int64ArrayProperty);
      exportModule('LYNX.Property.Uint64ArrayProperty', Uint64ArrayProperty);
      exportModule('LYNX.Property.StringArrayProperty', StringArrayProperty);
    }());
    (function () {
      var ArrayProperty = include('LYNX.Property.ArrayProperty');
      var DataArrays = include('LYNX.Utils.Datastructures.DataArrays');
      var _ = include('underscore');
      var MSG = include('LYNX.Property._constants.MSG');
      var ChangeSet = include('LYNX.Property.ChangeSet');
      /**
       * A primitive property for a string value.
       * @param {Object=} in_params - the parameters
       * @constructor
       * @extends LYNX.Property.ValueProperty
       * @alias LYNX.Property.StringProperty
       */
      var StringProperty = function (in_params) {
        ArrayProperty.call(this, in_params, Array, true);
        this._dataArrayRef = new DataArrays.StringDataArray();
      };
      StringProperty.prototype = Object.create(ArrayProperty.prototype);
      /**
       * get the string value
       * @return {string} the element at that index
       */
      StringProperty.prototype.getValue = function () {
        return this._dataArrayRef.getBuffer().toString();
      };
      /**
       * Private helper function to update the internal dirty and pending changes
       *
       * @param {LYNX.Property.SerializedChangeSet} in_changeSet - The changeset to apply
       * @private
      */
      StringProperty.prototype._updateChanges = function (in_changeSet) {
        // we need to convert the format to allow the application of the changes
        // since _performApplyAfterOnPropertyArray only understands insert/modify/remove commands
        var pendingChangesWereSetBefore = false;
        if (_.isString(this._pendingChanges)) {
          this._pendingChanges = {
            insert: [[
                0,
                this._pendingChanges
              ]]
          };
          pendingChangesWereSetBefore = true;
        }
        ChangeSet.prototype._performApplyAfterOnPropertyArray(this._pendingChanges, in_changeSet, this._getTypeidForChangeSet(true));
        if (pendingChangesWereSetBefore) {
          this._pendingChanges = this._pendingChanges.insert[0][1];
        }
        var dirtyChangesWereSetBefore = false;
        if (_.isString(this._dirtyChanges)) {
          this._dirtyChanges = {
            insert: [[
                0,
                this._dirtyChanges
              ]]
          };
          dirtyChangesWereSetBefore = true;
        }
        ChangeSet.prototype._performApplyAfterOnPropertyArray(this._dirtyChanges, in_changeSet, this._getTypeidForChangeSet(true));
        if (dirtyChangesWereSetBefore) {
          this._dirtyChanges = this._dirtyChanges.insert[0][1];
        }
      };
      /**
       * Insert into the string at a given position
       * @param {number} in_position target index
       * @param {string} in_value inserted string
       */
      StringProperty.prototype.insert = function (in_position, in_value) {
        this.insertRange(in_position, in_value);
      };
      /**
       * push at the end of the string
       * @param {string} in_value the string to be pushed
       */
      StringProperty.prototype.push = function (in_value) {
        this.insertRange(this._dataArrayRef.length, in_value);
      };
      /**
       * @return {string} the type id of the property
       * @private
       */
      StringProperty.prototype._getTypeidForChangeSet = function () {
        return this._typeid;
      };
      /**
       * Sets the property to the state in the given normalized changeset
       *
       * @param {LYNX.Property.SerializedChangeSet} in_serializedObj - The serialized changeset to apply. This
       *     has to be an normalized change-set (only containing inserts. Removes and Modifies are forbidden).
       * @param {boolean} [in_reportToView = true] - By default, the dirtying will always be reported to the checkout view
       *                                             and trigger a modified event there. When batching updates, this
       *                                             can be prevented via this flag.
       * @return {LYNX.Property.SerializedChangeSet} ChangeSet with the changes that actually were performed during the
       *     deserialization
       */
      StringProperty.prototype._deserialize = function (in_serializedObj, in_reportToView) {
        if (in_serializedObj.remove && in_serializedObj.remove.length > 0 || in_serializedObj.modify && in_serializedObj.modify.length > 0 || in_serializedObj.insert && (in_serializedObj.insert.length > 1 || in_serializedObj.insert.length === 1 && (in_serializedObj.insert[0][0] !== 0 || !_.isString(in_serializedObj.insert[0][1])))) {
          throw new Error(MSG.NO_NORMALIZED_CHANGESET);
        }
        var oldStringLength = this._dataArrayRef.length;
        var newStringData;
        if (_.isString(in_serializedObj)) {
          if (this._setValue(in_serializedObj, in_reportToView)) {
            return in_serializedObj;
          } else {
            return {};
          }
        } else {
          if (!in_serializedObj.insert || !in_serializedObj.insert[0]) {
            // we've got an empty object, so we have to wipe everything we've got
            if (oldStringLength > 0) {
              this.removeRange(0, oldStringLength, in_reportToView);
              return {
                remove: [[
                    0,
                    oldStringLength
                  ]]
              };
            } else {
              // the string was already empty, nothing has changed
              return {};
            }
          } else {
            newStringData = in_serializedObj.insert[0][1];
          }
        }
        if (newStringData === this._dataArrayRef._buffer) {
          // nothing changed
          return {};
        }
        // check if something was attached (very common case)
        if (newStringData.length > oldStringLength) {
          if (newStringData.substring(0, oldStringLength) === this._dataArrayRef._buffer) {
            var appendChanges = {
              insert: [[
                  oldStringLength,
                  newStringData.substring(oldStringLength)
                ]]
            };
            this.insertRange(oldStringLength, newStringData.substring(oldStringLength), in_reportToView);
            return appendChanges;
          }
        }
        // most simplistic diff method: Remove all existing data and insert the new data
        var simpleChanges = {
          insert: [[
              0,
              newStringData
            ]]
        };
        if (oldStringLength > 0) {
          simpleChanges.remove = [[
              0,
              oldStringLength
            ]];
          this._removeRangeWithoutDirtying(0, oldStringLength, in_reportToView);
        }
        this.insertRange(0, newStringData, in_reportToView);
        return simpleChanges;
      };
      /**
       * Serialize the property
       *
       * @param {boolean} in_dirtyOnly -
       *     Only include dirty entries in the serialization
       * @param {boolean} in_includeRootTypeid -
       *     Include the typeid of the root of the hierarchy - has no effect for ArrayProperty
       * @param {LYNX.Property.BaseProperty.MODIFIED_STATE_FLAGS} [in_dirtinessType] -
       *     The type of dirtiness to use when reporting dirty changes. By default this is
       *     PENDING_CHANGE
       * @param {boolean} [in_includeReferencedRepositories=false] - If this is set to true, the serialize
       *     function will descend into referenced repositories. WARNING: if there are loops in the references
       *     this can result in an infinite loop
       *
       * @return {Object} The serialized representation of this property
       * @private
       */
      StringProperty.prototype._serialize = function (in_dirtyOnly, in_includeRootTypeid, in_dirtinessType, in_includeReferencedRepositories) {
        if (in_dirtyOnly) {
          return ArrayProperty.prototype._serialize.call(this, in_dirtyOnly, in_includeRootTypeid, in_dirtinessType);
        } else {
          // returns just the current data
          return this._dataArrayRef.getBuffer();
        }
      };
      /**
       * @param {string} in_value the new value
       * @return {boolean} true if the value was actually changed
       */
      StringProperty.prototype.set = function (in_value) {
        this._checkIsNotReadOnly();
        return this._setValue(in_value, true);
      };
      /**
       * Internal function to update the value of a property
       *
       * @param {string} in_value the new value
       * @param {boolean} [in_reportToView = true] - By default, the dirtying will always be reported to the checkout view
       *                                             and trigger a modified event there. When batching updates, this
       *                                             can be prevented via this flag.
       * @return {boolean} true if the value was actually changed
       */
      StringProperty.prototype._setValue = function (in_value, in_reportToView) {
        var oldValue = this._dataArrayRef.getBuffer();
        var castedValue = String(in_value);
        var changed = castedValue !== oldValue;
        if (changed) {
          var stringLength = this._dataArrayRef.length;
          if (stringLength > 0) {
            this._dataArrayRef.removeRange(0, stringLength);
          }
          this._dataArrayRef.insertRange(0, castedValue);
          this._pendingChanges = castedValue;
          this._dirtyChanges = castedValue;
          this._setDirty(in_reportToView);
        }
        return changed;
      };
      /**
       * Modifies the property according to the given changeset
       *
       * @param {LYNX.Property.SerializedChangeSet} in_changeSet - The changeset to apply
       * @param {boolean} [in_reportToView = true] - By default, the dirtying will always be reported to the checkout view
       *                                             and trigger a modified event there. When batching updates, this
       *                                             can be prevented via this flag.
       */
      StringProperty.prototype._applyChangeset = function (in_changeSet, in_reportToView) {
        var stringChangeSet = in_changeSet;
        if (in_changeSet.value) {
          stringChangeSet = in_changeSet.value;
        }
        if (_.isString(stringChangeSet)) {
          this._setValue(stringChangeSet, in_reportToView);
        } else {
          // we need to convert the format to allow the application of the changes
          // since ArrayProperty._applyChangeset only understands insert/modify/remove commands
          if (_.isString(this._pendingChanges)) {
            this._pendingChanges = {
              insert: [[
                  0,
                  this._pendingChanges
                ]]
            };
          }
          if (_.isString(this._dirtyChanges)) {
            this._dirtyChanges = {
              insert: [[
                  0,
                  this._dirtyChanges
                ]]
            };
          }
          ArrayProperty.prototype._applyChangeset.call(this, in_changeSet, in_reportToView);
        }
      };
      /**
       * Calls back the given function with a human-readable string
       * representation of the property.
       * @param {string} indent - Leading spaces to create the tree representation
       * @param {string} externalId - Name of the current property at the upper level.
       *                              Used for arrays.
       * @param {function} printFct - Function to call for printing each property
       */
      StringProperty.prototype._prettyPrint = function (indent, externalId, printFct) {
        printFct(indent + externalId + this.getId() + ' (' + this.getTypeid() + '): "' + this.value + '"');
      };
      Object.defineProperty(StringProperty.prototype, 'value', {
        get: function () {
          return this.getValue();
        },
        set: function () {
          this.set.apply(this, arguments);
        }
      });
      exportModule('LYNX.Property.StringProperty', StringProperty);
    }());
    (function () {
      var ValueProperty = include('LYNX.Property.ValueProperty');
      var _castFunctors = include('LYNX.Property._castFunctors');
      /**
       * A primitive property for a 32 bit floating point value.
       * @param {Object=} in_params - the parameters
       * @constructor
       * @extends LYNX.Property.ValueProperty
       * @alias LYNX.Property.Float32Property
       */
      var Float32Property = function (in_params) {
        ValueProperty.call(this, in_params);
        // default for this property type is '0'
        this._data = 0;
      };
      Float32Property.prototype = Object.create(ValueProperty.prototype);
      Float32Property.prototype._castFunctor = _castFunctors.Float32;
      exportModule('LYNX.Property.Float32Property', Float32Property);
      /**
       * A primitive property for a 64 bit floating point value.
       * @param {Object=} in_params - the parameters
       * @constructor
       * @extends LYNX.Property.ValueProperty
       * @alias LYNX.Property.Float64Property
       */
      var Float64Property = function (in_params) {
        ValueProperty.call(this, in_params);
        // default for this property type is '0'
        this._data = 0;
      };
      Float64Property.prototype = Object.create(ValueProperty.prototype);
      Float64Property.prototype._castFunctor = _castFunctors.Float64;
      exportModule('LYNX.Property.Float64Property', Float64Property);
    }());
    (function () {
      var ValueProperty = include('LYNX.Property.ValueProperty');
      var _castFunctors = include('LYNX.Property._castFunctors');
      var ChangeSet = include('LYNX.Property.ChangeSet');
      var Int64 = include('LYNX.Utils.Datastructures.Int64');
      var Uint64 = include('LYNX.Utils.Datastructures.Uint64');
      /**
       * A primitive property for an signed 8 bit integer value.
       * @param {Object=} in_params - the parameters
       * @constructor
       * @extends LYNX.Property.ValueProperty
       * @alias LYNX.Property.Int8Property
       */
      var Int8Property = function (in_params) {
        ValueProperty.call(this, in_params);
        // default for this property type is '0'
        this._data = 0;
      };
      Int8Property.prototype = Object.create(ValueProperty.prototype);
      Int8Property.prototype._castFunctor = _castFunctors.Int8;
      exportModule('LYNX.Property.Int8Property', Int8Property);
      /**
       * A primitive property for an signed 16 bit integer value.
       * @param {Object=} in_params - the parameters
       * @constructor
       * @extends LYNX.Property.ValueProperty
       * @alias LYNX.Property.Int16Property
       */
      var Int16Property = function (in_params) {
        ValueProperty.call(this, in_params);
        // default for this property type is '0'
        this._data = 0;
      };
      Int16Property.prototype = Object.create(ValueProperty.prototype);
      Int16Property.prototype._castFunctor = _castFunctors.Int16;
      exportModule('LYNX.Property.Int16Property', Int16Property);
      /**
       * A primitive property for an signed 32 bit integer value.
       * @param {Object=} in_params - the parameters
       * @constructor
       * @extends LYNX.Property.ValueProperty
       * @alias LYNX.Property.Int32Property
       */
      var Int32Property = function (in_params) {
        ValueProperty.call(this, in_params);
        // default for this property type is '0'
        this._data = 0;
      };
      Int32Property.prototype = Object.create(ValueProperty.prototype);
      Int32Property.prototype._castFunctor = _castFunctors.Int32;
      exportModule('LYNX.Property.Int32Property', Int32Property);
      /**
       * A primitive property base class for big integer values.
       * @param {Object=} in_params - the parameters
       * @constructor
       * @extends LYNX.Property.ValueProperty
       * @alias LYNX.Property.Integer64Property
       * @protected
       * @abstract
       */
      var Integer64Property = function (in_params) {
        ValueProperty.call(this, in_params);
        // default for this property type is '0, 0'
        this._data = new this.DataConstructor();
      };
      Integer64Property.prototype = Object.create(ValueProperty.prototype);
      Integer64Property.prototype.DataConstructor = undefined;
      /**
       * Internal function to update the value of the Integer64Property
       *
       * @param {Int64} in_value the new value
       * @param {boolean} [in_reportToView = true] - By default, the dirtying will always be reported to the checkout view
       *                                             and trigger a modified event there. When batching updates, this
       *                                             can be prevented via this flag.
       * @return {boolean} true if the value was actually changed
       */
      Integer64Property.prototype._setValue = function (in_value, in_reportToView) {
        var oldHighValue = this._data.getHigh();
        var newHighValue = in_value.getHigh();
        var changed = oldHighValue !== newHighValue;
        var oldLowValue = this._data.getLow();
        var newLowValue = in_value.getLow();
        changed = changed || oldLowValue !== newLowValue;
        if (changed) {
          this._data = in_value.clone();
          this._setDirty(in_reportToView);
        }
        return changed;
      };
      Integer64Property.prototype.getHigh = function () {
        return this._data.getHigh();
      };
      /**
       * @return {number} the lower 32 bit integer part
       */
      Integer64Property.prototype.getLow = function () {
        return this._data.getLow();
      };
      /**
       * @param {number} in_high set the higher 32 bit integer part
       * @return {boolen} true if the value was actually changed
       */
      Integer64Property.prototype.setHigh = function (in_high) {
        var changed = this._data.getHigh() !== in_high;
        if (changed) {
          var newData = new this.DataConstructor(this.getLow(), in_high);
          this._data = newData;
          this._setDirty();
        }
        return changed;
      };
      /**
       * @param {number} in_low set the lower 32 bit integer part
       * @return {boolen} true if the value was actually changed
       */
      Integer64Property.prototype.setLow = function (in_low) {
        var changed = this._data.getLow() !== in_low;
        if (changed) {
          var newData = new this.DataConstructor(in_low, this.getHigh());
          this._data = newData;
          this._setDirty();
        }
        return changed;
      };
      /**
       * Sets the property to the state in the given normalized changeset
       *
       * @param {*} in_value - The serialized target value
       * @param {boolean} [in_reportToView = true] - By default, the dirtying will always be reported to the checkout view
       *                                             and trigger a modified event there. When batching updates, this
       *                                             can be prevented via this flag.
       *
       * @return {LYNX.Property.SerializedChangeSet} ChangeSet with the changes that actually were performed during the
       *     deserialization
       */
      Integer64Property.prototype._deserialize = function (in_value, in_reportToView) {
        if (ChangeSet.isEmptyChangeSet(in_value)) {
          return undefined;
        } else {
          console.assert(in_value.length === 2);
          var readValue = new this.DataConstructor(in_value[0], in_value[1]);
          var changed = this._setValue(readValue, in_reportToView);
          return changed ? this.serialize() : undefined;
        }
      };
      /**
       * Serialize the property
       *
       * @param {boolean} in_dirtyOnly -
       *     Only include dirty entries in the serialization
       * @param {boolean} in_includeRootTypeid -
       *     Include the typeid of the root of the hierarchy - has no effect for value properties
       * @param {LYNX.Property.BaseProperty.MODIFIED_STATE_FLAGS} [in_dirtinessType] -
       *     The type of dirtiness to use when reporting dirty changes. By default this is
       *     PENDING_CHANGE   * @return {*} The serialized representation of this property
       * @param {boolean} [in_includeReferencedRepositories=false] - If this is set to true, the serialize
       *     function will descend into referenced repositories. WARNING: if there are loops in the references
       *     this can result in an infinite loop
       * @return {*} The serialized representation of this property
       * @private
       */
      Integer64Property.prototype._serialize = function (in_dirtyOnly, in_includeRootTypeid, in_dirtinessType, in_includeReferencedRepositories) {
        if (in_dirtyOnly) {
          if (this._isDirty(in_dirtinessType)) {
            return [
              this._data.getLow(),
              this._data.getHigh()
            ];
          } else {
            return {};
          }
        } else {
          return [
            this._data.getLow(),
            this._data.getHigh()
          ];
        }
      };
      var BIT32 = 4294967296;
      /**
       * The toString() method returns a string representing the specified Integer64 object.
       *
       * @param {number} [radix = 10]  An integer between 2 and 36 specifying
       *      the base to use for representing numeric values.
       * @return {string} A string representing the specified Integer64 object.
       */
      Integer64Property.prototype.toString = function (radix) {
        radix = radix || 10;
        if (radix < 2 || 36 < radix) {
          throw new Error('Base is out of range. Base should be in range [2,36]');
        }
        var high = this.getHigh();
        var low = this.getLow();
        var result = '';
        var sign = !(this._data instanceof Uint64) && high & 2147483648;
        if (sign) {
          high = ~high;
          low = BIT32 - low;
        }
        do {
          var mod = high % radix * BIT32 + low;
          high = Math.floor(high / radix);
          low = Math.floor(mod / radix);
          result = (mod % radix).toString(radix) + result;
        } while (high || low);
        return sign ? '-' + result : result;
      };
      /**
       * The Integer64.fromString() method parses a string argument updates object's lower and higher 32 bit integer parts.
       *
       * @param {string} string The value to parse. Leading whitespace in the string argument is ignored.
       * @param {number} [radix = 10] An integer between 2 and 36 that represents the
       *     radix (the base in mathematical numeral systems) of the above mentioned string.
       */
      Integer64Property.prototype.fromString = function (string, radix) {
        string = string.trim();
        radix = radix || 10;
        if (radix < 2 || 36 < radix) {
          throw new Error('Base is out of range. Base should be in range [2,36]');
        }
        var position = 0;
        var negative = false;
        var high = 0;
        var low = 0;
        if (string[0] === '-') {
          negative = true;
          ++position;
        }
        if (negative && this._data instanceof Uint64) {
          throw new Error('Can\'t update value to negative');
        }
        while (position < string.length) {
          var digit = parseInt(string[position++], radix);
          if (isNaN(digit)) {
            throw new Error('Can\'t parse. String contains invalid characters');
          }
          low = low * radix + digit;
          high = high * radix + Math.floor(low / BIT32);
          low %= BIT32;
        }
        if (negative) {
          high = ~high;
          if (low) {
            low = BIT32 - low;
          } else {
            ++high;
          }
        }
        this.setHigh(high);
        this.setLow(low);
      };
      /**
       * A primitive property class for big singned integer values.
       * @param {Object=} in_params - the parameters
       * @constructor
       * @extends LYNX.Property.Integer64Property
       * @alias LYNX.Property.Int64Property
       */
      var Int64Property = function (in_params) {
        Integer64Property.call(this, in_params);
      };
      Int64Property.prototype = Object.create(Integer64Property.prototype);
      Int64Property.prototype.DataConstructor = Int64;
      /**
       * A primitive property class for big unsingned integer values.
       * @param {Object=} in_params - the parameters
       * @constructor
       * @extends LYNX.Property.Integer64Property
       * @alias LYNX.Property.Uint64Property
       */
      var Uint64Property = function (in_params) {
        Integer64Property.call(this, in_params);
      };
      Uint64Property.prototype = Object.create(Integer64Property.prototype);
      Uint64Property.prototype.DataConstructor = Uint64;
      exportModule('LYNX.Property.Int64Property', Int64Property);
      exportModule('LYNX.Property.Uint64Property', Uint64Property);
    }());
    (function () {
      var ValueProperty = include('LYNX.Property.ValueProperty');
      var _castFunctors = include('LYNX.Property._castFunctors');
      /**
       * A primitive property for a boolean value
       * @param {Object=} in_params - the parameters
       * @constructor
       * @extends LYNX.Property.ValueProperty
       * @alias LYNX.Property.BoolProperty
       */
      var BoolProperty = function (in_params) {
        ValueProperty.call(this, in_params);
        // default for this property type is 'false'
        this._data = false;
      };
      BoolProperty.prototype = Object.create(ValueProperty.prototype);
      BoolProperty.prototype._castFunctor = _castFunctors.Boolean;
      exportModule('LYNX.Property.BoolProperty', BoolProperty);
    }());
    (function () {
      var ValueProperty = include('LYNX.Property.ValueProperty');
      var TypeIdHelper = include('LYNX.Property.TypeIdHelper');
      var PathHelper = include('LYNX.Property.PathHelper');
      var BaseProperty = include('LYNX.Property.BaseProperty');
      var _castFunctors = include('LYNX.Property._castFunctors');
      /**
       * This class serves as a view to read, write and listen to changes in an
       * object's value field. To do this we simply keep a pointer to the object and
       * it's associated data field that we are interested in. If no data field is
       * present this property will have an undefined value.
       * @param {Object=} in_params - the parameters
       *
       * @constructor
       * @extends LYNX.Property.ValueProperty
       * @alias LYNX.Property.ReferenceProperty
       */
      var ReferenceProperty = function (in_params) {
        ValueProperty.call(this, in_params);
        // default for this property type is an empty string
        this._data = '';
      };
      ReferenceProperty.prototype = Object.create(ValueProperty.prototype);
      ReferenceProperty.prototype._castFunctor = _castFunctors.String;
      /**
       * Returns the typeid for the target of this reference
       *
       * Note: This is the type that is specified in the typeid of this reference and not the actual type
       * of the referenced object, which might inherit from that typeid.
       *
       * @return {string} The typeid of the nodes this reference may point to
       */
      ReferenceProperty.prototype.getReferenceTargetTypeId = function () {
        return TypeIdHelper.extractReferenceTargetTypeIdFromReference(this.getTypeid());
      };
      /**
       * Resolves the referenced property
       *
       * @return {LYNX.Property.BaseProperty|undefined} The property object the reference points to or undefined if it
       *    could not be resolved
       */
      ReferenceProperty.prototype.getReferencedProperty = function () {
        if (this.value === '') {
          return undefined;
        }
        return this.getParent().resolvePath(this.value);
      };
      /**
       * Sets the reference to point to the given property object
       *
       * @param {LYNX.Property.BaseProperty|undefined} in_property - The property to assign to the reference. If undefined
       *     is passed, the reference will be set to an empty string to indicate an empty reference.
       */
      ReferenceProperty.prototype.setReferencedProperty = function (in_property) {
        if (in_property === undefined) {
          this.set('');
        } else {
          // TODO: Check whether this is still the correct path once we start to support repository references
          this.set(in_property.getAbsolutePath());
        }
      };
      /**
       * Checks whether the reference is valid. This is either the case when it is empty or when the referenced
       * property exists.
       *
       * @return {boolean} Is this a valid reference?
       */
      ReferenceProperty.prototype.isReferenceValid = function () {
        return this.value === '' || this.ref !== undefined;
      };
      /**
       * Update the reference.
       *
       * @param {String} in_value - the new value. This must be either an empty string or a path that is
       *                            repository absolute
       * @return {boolean} true if the value was actually changed
       */
      ReferenceProperty.prototype.set = function (in_value) {
        this._checkIsNotReadOnly();
        // Forward the call to setValue
        return ValueProperty.prototype.set.call(this, in_value);
      };
      /**
       * @inheritdoc
       */
      ReferenceProperty.prototype._resolvePathSegment = function (in_segment, in_segmentType, out_disableCache) {
        out_disableCache.disableCache = true;
        // path segments and array tokens are automatically forwarded to the referenced node
        if (in_segmentType === PathHelper.TOKEN_TYPES.ARRAY_TOKEN || in_segmentType === PathHelper.TOKEN_TYPES.PATH_SEGMENT_TOKEN) {
          var referenced = this.ref;
          if (!referenced) {
            return undefined;
          } else {
            // Forward the resolution to the referenced node
            return referenced._resolvePathSegment(in_segment, in_segmentType, out_disableCache);
          }
        } else if (in_segmentType === PathHelper.TOKEN_TYPES.DEREFERENCE_TOKEN) {
          // A dereference token causes the reference to be resolved
          return this.ref;
        } else {
          // Everything else is handled by the implementation in the base property
          return BaseProperty.prototype._resolvePathSegment.call(this, in_segment, in_segmentType, out_disableCache);
        }
      };
      // Define a property to simplify accessing the referenced path
      Object.defineProperty(ReferenceProperty.prototype, 'ref', {
        get: function () {
          return this.getReferencedProperty.apply(this, arguments);
        },
        set: function () {
          this.setReferencedProperty.apply(this, arguments);
        }
      });
      exportModule('LYNX.Property.ReferenceProperty', ReferenceProperty);
    }());
    (function () {
      var Int32Property = include('LYNX.Property.Int32Property');
      var _castFunctors = include('LYNX.Property._castFunctors');
      var ValueProperty = include('LYNX.Property.ValueProperty');
      var MSG = include('LYNX.Property._constants.MSG');
      var _ = include('underscore');
      var TypeIdHelper = include('LYNX.Property.TypeIdHelper');
      /**
       * A primitive property for enums.
       * @param {Object=} in_params - the parameters
       * @constructor
       * @extends LYNX.Property.Int32Property
       * @alias LYNX.Property.EnumProperty
       */
      var EnumProperty = function (in_params) {
        Int32Property.call(this, in_params);
        // default for this property type is '0'
        this._data = 0;
        // whenever an EnumProperty is created by the PropertyFactory, we get a
        // dictonary [value->enum] and [enum->value] to efficiently lookup
        // values/enums for the property.
        this._enumDictionary = in_params._enumDictionary;
      };
      EnumProperty.prototype = Object.create(Int32Property.prototype);
      EnumProperty.prototype._castFunctor = _castFunctors.Int32;
      /**
       * Returns the current enum string
       * @return {string} the string value of the property
       */
      EnumProperty.prototype.getEnumString = function () {
        var resultEntry = this._enumDictionary.enumEntriesByValue[this._data];
        if (!resultEntry) {
          throw new Error(MSG.UNKNOWN_ENUM + this._data);
        } else {
          return resultEntry.id;
        }
      };
      /**
       * Sets the (internal, integer) value of the property
       *
       * @param {Number|string} in_value the new integer value - it must be a valid enum integer for this property
       *                                 or
       *                                 the new enum value in form of a valid enum string for this EnumProperty
       */
      EnumProperty.prototype.set = function (in_value) {
        this._checkIsNotReadOnly();
        // check if we've got a string
        if (_.isString(in_value)) {
          this.setEnumByString(in_value);
        } else if (!this._enumDictionary.enumEntriesByValue[in_value]) {
          throw new Error(MSG.UNKNOWN_ENUM + in_value);
        } else {
          ValueProperty.prototype.set.call(this, in_value);
        }
      };
      /**
       * Sets the property by am enum string
       *
       * @param {string} in_stringId the enum string we want to switch to
       */
      EnumProperty.prototype.setEnumByString = function (in_stringId) {
        var internalEnum = this._enumDictionary.enumEntriesById[in_stringId];
        if (!internalEnum) {
          throw new Error(MSG.UNKNOWN_ENUM + in_stringId);
        } else {
          var internalValue = internalEnum.value;
          this.set(internalValue);
        }
      };
      /**
       * Returns the full property type identifier for the ChangeSet including the enum type id
       * @param  {boolean} in_hideCollection - if true the collection type (if applicable) will be omitted
       *                since that is not aplicable here, this param is ignored
       * @return {string} The typeid
       * @private
       */
      EnumProperty.prototype._getTypeidForChangeSet = function (in_hideCollection) {
        return TypeIdHelper.createSerializationTypeId(this._typeid, 'single', true);
      };
      /**
       * Get the annotation of the current enum
       * @return {*} the annotation data for the current enum
       */
      EnumProperty.prototype.getAnnotationForEnumValue = function () {
        return this._enumDictionary.enumEntriesByValue[this._data].annotation;
      };
      /**
       * let the user to query all valid entries of an enum
       * @return {{}} all valid (string) entries and their (int) values
       */
      EnumProperty.prototype.getListOfValidEnumStringsAndValues = function () {
        return this._enumDictionary.enumEntriesById;
      };
      exportModule('LYNX.Property.EnumProperty', EnumProperty);
    }());
    (function () {
      var BaseProperty = include('LYNX.Property.BaseProperty');
      var NamedProperty = include('LYNX.Property.NamedProperty');
      var NamedNodeProperty = include('LYNX.Property.NamedNodeProperty');
      var IndexedCollectionBaseProperty = include('LYNX.Property.IndexedCollectionBaseProperty');
      var TypeIdHelper = include('LYNX.Property.TypeIdHelper');
      var _ = include('underscore');
      var PathHelper = include('LYNX.Property.PathHelper');
      var MSG = include('LYNX.Property._constants.MSG');
      /**
       * A SetProperty is a collection class that can contain an unordered set of properties. These properties must derive
       * from NamedProperty and their GUID is used to identify them within the set.
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.IndexedCollectionBaseProperty
       * @alias LYNX.Property.SetProperty
       */
      var SetProperty = function (in_params) {
        IndexedCollectionBaseProperty.call(this, in_params);
        // A set property falls back to NamedProperty, if none is specified
        this._typeid = in_params.typeid || 'NamedProperty';
      };
      SetProperty.prototype = Object.create(IndexedCollectionBaseProperty.prototype);
      /**
       * Is this property a leaf node with regard to flattening?
       *
       * TODO: Which semantics should flattening have? It stops at primitive types and collections?
       *
       * @return {boolean} Is it a leaf with regard to flattening?
       */
      SetProperty.prototype._isFlattenLeaf = function () {
        return true;
      };
      /**
       * Returns the full property type identifier for the ChangeSet including the enum type id
       * @param  {boolean} in_hideCollection - if true the collection type (if applicable) will be omitted
       * @return {string} The typeid
       * @private
       */
      SetProperty.prototype._getTypeidForChangeSet = function (in_hideCollection) {
        if (in_hideCollection) {
          return this._typeid;
        } else {
          return TypeIdHelper.createSerializationTypeId(this._typeid, 'set');
        }
      };
      /**
       * Returns the path segment for a child
       *
       * @param {LYNX.Property.NamedProperty} in_childNode - The child for which the path is returned
       *
       * @return {string} The path segment to resolve the child property under this property
       * @protected
       */
      SetProperty.prototype._getPathSegmentForChildNode = function (in_childNode) {
        return '[' + in_childNode.getGuid() + ']';
      };
      /**
       * Resolves a direct child node based on the given path segment
       *
       * @param {String} in_segment                                   - The path segment to resolve
       * @param {LYNX.Property.PathHelper.TOKEN_TYPES} in_segmentType - The type of segment in the tokenized path
       * @param {Object} out_disableCache                            - The member disableCache will be set to true
       *                                                                in this object, if this path must not be cached
       *
       * @return {LYNX.Property.BaseProperty|undefined} The child property that has been resolved
       * @protected
       */
      SetProperty.prototype._resolvePathSegment = function (in_segment, in_segmentType, out_disableCache) {
        // Base Properties only support paths separated via dots
        if (in_segmentType === PathHelper.TOKEN_TYPES.ARRAY_TOKEN) {
          return this._entries[in_segment];
        } else {
          return BaseProperty.prototype._resolvePathSegment.call(this, in_segment, in_segmentType, out_disableCache);
        }
      };
      /**
       * Inserts a property into the set
       *
       * @param {LYNX.Property.NamedProperty} in_property - The property to insert
       */
      SetProperty.prototype.insert = function (in_property) {
        if (in_property instanceof NamedProperty || in_property instanceof NamedNodeProperty) {
          var guid = in_property.getGuid();
          this._insert(guid, in_property, true);
        } else {
          console.error(MSG.CANT_INSERT_NON_NAMED_PROPERTIES);
        }
      };
      /**
       * Removes the given property from the set
       *
       * @param {LYNX.Property.NamedProperty|string} in_entry - The property or its GUID to remove from the set
       */
      SetProperty.prototype.remove = function (in_entry) {
        if (_.isString(in_entry)) {
          this._removeByKey(in_entry, true);
        } else {
          this._removeByKey(in_entry.getGuid(), true);
        }
      };
      /**
       * Returns an Object with all the entries of the set.
       *
       * WARNING: This is a direct access to the internal data-structure and the collection MUST NOT be modified. It is
       * read only for fast access and iteration. Insertion and deletion MUST be done via the insert and remove functions
       * of this class.
       *
       * @return {Object<String, LYNX.Property.NamedProperty>} The map with all entries in the set.
       */
      SetProperty.prototype.getEntriesReadOnly = function () {
        return this._entries;
      };
      /**
       * Returns the name of all the sub-properties of this property.
       *
       * @return {Array.<string>} An array of all the property ids
       */
      SetProperty.prototype.getIds = function () {
        return Object.keys(this._entries);
      };
      /**
       * Returns the collection entry with the given GUID
       *
       * @param {string} in_guid - GUID of the entry to return
       *
       * @return {LYNX.Property.NamedProperty|undefined} The entry in the collection or undefined if none could be found
       */
      SetProperty.prototype.get = function (in_guid) {
        return this._entries[in_guid];
      };
      /**
       * Checks whether a property with the given name exists
       *
       * @param {string} in_id - Name of the property
       * @return {boolean} Does the property exist?
       */
      SetProperty.prototype.has = function (in_id) {
        return this._entries[in_id] !== undefined;
      };
      /**
       * Returns all entries of the set as an array.
       *
       * NOTE: This function creates a copy and thus is less efficient as getEntriesReadOnly.
       *
       * @return {Array.<LYNX.Property.NamedProperty>} Array with all entries of the set. This array is a shallow copy
       * which can be modified by the caller without effects on the set.
       */
      SetProperty.prototype.getAsArray = function () {
        return _.values(this._entries);
      };
      exportModule('LYNX.Property.SetProperty', SetProperty);
    }());
    (function () {
      var BaseProperty = include('LYNX.Property.BaseProperty');
      var IndexedCollectionBaseProperty = include('LYNX.Property.IndexedCollectionBaseProperty');
      var _ = include('underscore');
      var TypeIdHelper = include('LYNX.Property.TypeIdHelper');
      var PathHelper = include('LYNX.Property.PathHelper');
      var MSG = include('LYNX.Property._constants.MSG');
      /**
       * typedef {LYNX.Property.BaseProperty|string|number|boolean} LYNX.Property.MapProperty~MapValueType
       *
       * The type of the values that are set/inserted into the map. Depending on the type of the map, these can either
       * be property objects or primitive values
       */
      /**
       * A MapProperty is a collection class that can contain an dictionary that maps from strings to properties.
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.IndexedCollectionBaseProperty
       * @alias LYNX.Property.MapProperty
       */
      var MapProperty = function (in_params) {
        IndexedCollectionBaseProperty.call(this, in_params);
      };
      MapProperty.prototype = Object.create(IndexedCollectionBaseProperty.prototype);
      /**
       * Returns the full property type identifier for the ChangeSet including the enum type id
       * @param  {boolean} in_hideCollection - if true the collection type (if applicable) will be omitted
       * @return {string} The typeid
       * @private
       */
      MapProperty.prototype._getTypeidForChangeSet = function (in_hideCollection) {
        if (in_hideCollection) {
          return this._typeid;
        } else {
          return TypeIdHelper.createSerializationTypeId(this._typeid, 'map');
        }
      };
      /**
       * Is this property a leaf node with regard to flattening?
       *
       * TODO: Which semantics should flattening have? It stops at primitive types and collections?
       *
       * @return {boolean} Is it a leaf with regard to flattening?
       */
      MapProperty.prototype._isFlattenLeaf = function () {
        return true;
      };
      /**
       * TODO: Warning, the semantics of this function are undefined and will change in the future
       * It's also partially unimplemented for many cases
       *
       * @param {*} values to assign to the collection
       * @private
       */
      MapProperty.prototype._setValues = function (values) {
        if (this._containsPrimitiveTypes) {
          var that = this;
          _.each(values, function (propertyValue, propertyKey) {
            that.insert(propertyKey, propertyValue);
          });
        } else {
          throw new Error('_setValues not implemented for maps of non-primitive types');
        }
      };
      /**
       * Returns the path segment for a child
       *
       * @param {LYNX.Property.BaseProperty} in_childNode - The child for which the path is returned
       *
       * @return {string} The path segment to resolve the child property under this property
       * @protected
       */
      MapProperty.prototype._getPathSegmentForChildNode = function (in_childNode) {
        return '[' + PathHelper.quotePathSegmentIfNeeded(in_childNode._id) + ']';
      };
      /**
       * Resolves a direct child node based on the given path segment
       *
       * @param {String} in_segment                                   - The path segment to resolve
       * @param {LYNX.Property.PathHelper.TOKEN_TYPES} in_segmentType - The type of segment in the tokenized path
       * @param {Object} out_disableCache                            - The member disableCache will be set to true
       *                                                                in this object, if this path must not be cached
       *
       * @return {LYNX.Property.BaseProperty|undefined} The child property that has been resolved
       * @protected
       */
      MapProperty.prototype._resolvePathSegment = function (in_segment, in_segmentType, out_disableCache) {
        // Base Properties only support paths separated via dots
        if (in_segmentType === PathHelper.TOKEN_TYPES.ARRAY_TOKEN) {
          return this._entries[in_segment];
        } else {
          return BaseProperty.prototype._resolvePathSegment.call(this, in_segment, in_segmentType, out_disableCache);
        }
      };
      /**
       * Inserts a property into the map
       *
       * Note: This will trigger an exception when this key already exists in the map. If you want to overwrite
       *       existing entries you can use the set function.
       *
       * @param {string}                                  in_key   - The key under which the entry is added
       * @param {LYNX.Property.MapProperty~MapValueType}  in_value - The value or property to insert
       */
      MapProperty.prototype.insert = function (in_key, in_value) {
        if (this._entries[in_key] !== undefined) {
          throw new Error(MSG.PROPERTY_ALREADY_EXISTS + ':' + in_key);
        }
        if (in_value.getParent() === undefined) {
          // Set the ID of the entry, to make sure it corresponds to the used key
          in_value._setId(in_key);
          // Insert the entry into the collection
          this._insert(in_key, in_value, true);
        } else {
          throw new Error(MSG.INSERTED_ENTRY_WITH_PARENT);
        }
      };
      /**
       * Removes the entry with the given key from the map
       *
       * @param {string} in_key - The key of the entry to remove from the map
       */
      MapProperty.prototype.remove = function (in_key) {
        this._removeByKey(in_key, true);
      };
      /**
       * Sets the entry with the given key to the value passed in
       *
       * Note: this will overwrite an already existing value
       *
       * @param {string}                                  in_key    - The key under which the entry is stored
       * @param {LYNX.Property.MapProperty~MapValueType}  in_value  - The value or property to store in the map
       */
      MapProperty.prototype.set = function (in_key, in_value) {
        if (this._entries[in_key] !== in_value) {
          if (this._containsPrimitiveTypes === false && in_value.getParent() !== undefined) {
            throw new Error(MSG.INSERTED_ENTRY_WITH_PARENT);
          }
          if (this._entries[in_key] !== undefined) {
            this._removeByKey(in_key, false);
          }
          // Set the ID of the entry, to make sure it corresponds to the used key
          if (this._containsPrimitiveTypes === false) {
            in_value._setId(in_key);
          }
          this._insert(in_key, in_value, false);
          // Make one final report
          this._reportDirtinessToView();
        } else {
          this._checkIsNotReadOnly();
        }
      };
      /**
       * Returns an Object with all the entries of the map.
       *
       * WARNING: This is a direct access to the internal data-structure and the collection MUST NOT be modified. It is
       * read only for fast access and iteration. Insertion and deletion MUST be done via the insert and remove functions
       * of this class.
       *
       * @return {Object<String, LYNX.Property.MapProperty~MapValueType>} The map with all entries in the map.
       */
      MapProperty.prototype.getEntriesReadOnly = function () {
        return this._entries;
      };
      /**
       * Returns the collection entry with the given key
       *
       * @param {string} in_key - key of the entry to return
       *
       * @return {LYNX.Property.MapProperty~MapValueType|undefined} The entry in the collection or undefined
       *     if none could be found
       */
      MapProperty.prototype.get = function (in_key) {
        return this._entries[in_key];
      };
      /**
       * Checks whether a property with the given name exists
       *
       * @param {string} in_id - Name of the property
       * @return {boolean} Does the property exist?
       */
      MapProperty.prototype.has = function (in_id) {
        return this._entries[in_id] !== undefined;
      };
      /**
       * Returns all entries of the map as an array.
       *
       * NOTE: This function creates a copy and thus is less efficient as getEntriesReadOnly.
       *
       * @return {Array.<LYNX.Property.MapProperty~MapValueType>} Array with all entries of the map. This array
       *     is a shallow copy which can be modified by the caller without effects on the map.
       */
      MapProperty.prototype.getAsArray = function () {
        return _.values(this._entries);
      };
      /**
       * Returns all keys in the map
       *
       * NOTE: This function creates a copy and thus is less efficient as getEntriesReadOnly.
       *
       * @return {Array.<string>} The keys
       */
      MapProperty.prototype.getIds = function () {
        return Object.keys(this._entries);
      };
      exportModule('LYNX.Property.MapProperty', MapProperty);
    }());
    (function () {
      var ArrayProperty = include('LYNX.Property.ArrayProperty');
      var PathHelper = include('LYNX.Property.PathHelper');
      var BaseProperty = include('LYNX.Property.BaseProperty');
      var TypeIdHelper = include('LYNX.Property.TypeIdHelper');
      /**
       * An ArrayProperty which stores reference values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.ArrayProperty
       * @alias LYNX.Property.ReferenceArrayProperty
       */
      var ReferenceArrayProperty = function (in_params) {
        ArrayProperty.call(this, in_params, Array, true);
      };
      ReferenceArrayProperty.prototype = Object.create(ArrayProperty.prototype);
      /**
       * Returns the typeid for the target of this reference
       *
       * Note: This is the type that is specified in the typeid of this reference and not the actual type
       * of the referenced object, which might inherit from that typeid.
       *
       * @return {string} The typeid of the nodes this reference may point to
       */
      ReferenceArrayProperty.prototype.getReferenceTargetTypeId = function () {
        return TypeIdHelper.extractReferenceTargetTypeIdFromReference(this.getTypeid());
      };
      /**
       * Resolves the referenced property for the given key
       *
       * @param {number} in_position the target index
       *
       * @return {LYNX.Property.BaseProperty|undefined} The property object the reference points to or undefined if it
       *    could not be resolved or the key could not be found in the map
       */
      ReferenceArrayProperty.prototype.getReferencedProperty = function (in_position) {
        var value = this.get(in_position);
        if (value === undefined || value === '') {
          return undefined;
        }
        return this.getParent().resolvePath(value);
      };
      /**
       * Checks whether the reference is valid. This is either the case when it is empty or when the referenced
       * property exists.
       *
       * @param {number} in_position the target index
       * @return {boolean} Is this a valid reference?
       */
      ReferenceArrayProperty.prototype.isReferenceValid = function (in_position) {
        return this.get(in_position) === '' || this.getReferencedProperty(in_position) !== undefined;
      };
      /**
       * Sets the reference to point to the given property object
       *
       * @param {number}                               in_position - The target index
       * @param {LYNX.Property.BaseProperty|undefined} in_property - The property to assign to the reference. If undefined
       *     is passed, the reference will be set to an empty string to indicate an empty reference.
       */
      ReferenceArrayProperty.prototype.setReferencedProperty = function (in_position, in_property) {
        this.setReferencedPropertyRange(in_position, [in_property]);
      };
      /**
       * Sets the range in the array to point to the given property objects
       *
       * @param {number} in_offset - target start index
       * @param {Array<LYNX.Property.BaseProperty|undefined>} in_array - contains the properties to be set. If undefined
       *     is passed, the reference will be set to an empty string to indicate an empty reference.
       */
      ReferenceArrayProperty.prototype.setReferencedPropertyRange = function (in_offset, in_array) {
        for (var i = 0; i < in_array.length; i++) {
          if (in_array[i] === undefined) {
            in_array[i] = '';
          } else {
            // TODO: Check whether this is still the correct path once we start to support repository references
            in_array[i] = in_array[i].getAbsolutePath();
          }
        }
        this.setRange(in_offset, in_array);
      };
      /**
       * Insert a range which points to the given property objects into the array
       *
       * @param {number}                                      in_offset - target start index
       * @param {Array<LYNX.Property.BaseProperty|undefined>} in_array  - contains the properties to be set. If undefined
       *     is passed, the reference will be set to an empty string to indicate an empty reference.
       */
      ReferenceArrayProperty.prototype.insertReferencedPropertyRange = function (in_offset, in_array) {
        for (var i = 0; i < in_array.length; i++) {
          if (in_array[i] === undefined) {
            in_array[i] = '';
          } else {
            // TODO: Check whether this is still the correct path once we start to support repository references
            in_array[i] = in_array[i].getAbsolutePath();
          }
        }
        this.insertRange(in_offset, in_array);
      };
      /**
       * Insert a reference to the given property objects into the array
       *
       * @param {number}                               in_position - The target index
       * @param {LYNX.Property.BaseProperty|undefined} in_property - The property to assign to the reference. If undefined
       *     is passed, the reference will be set to an empty string to indicate an empty reference.
       */
      ReferenceArrayProperty.prototype.insertReferencedProperty = function (in_position, in_property) {
        this.insertReferencedPropertyRange(in_position, [in_property]);
      };
      /**
       * Pushes a reference to the given property objects onto the array
       *
       * @param {LYNX.Property.BaseProperty|undefined} in_property - The property to assign to the reference. If undefined
       *     is passed, the reference will be set to an empty string to indicate an empty reference.
       */
      ReferenceArrayProperty.prototype.pushReferencedProperty = function (in_property) {
        this.insertReferencedPropertyRange(this._dataArrayRef.length, [in_property]);
      };
      /**
       * @inheritdoc
       */
      ReferenceArrayProperty.prototype._resolvePathSegment = function (in_segment, in_segmentType, out_disableCache) {
        out_disableCache.disableCache = true;
        // Array tokens are automatically resolved
        if (in_segmentType === PathHelper.TOKEN_TYPES.ARRAY_TOKEN) {
          return this.getReferencedProperty(in_segment);
        } else {
          // Everything else is handled by the implementation in the base property
          return BaseProperty.prototype._resolvePathSegment.call(this, in_segment, in_segmentType, out_disableCache);
        }
      };
      /**
       * @inheritdoc
       */
      ReferenceArrayProperty.prototype._insertRangeWithoutDirtying = function (in_offset, in_array) {
        ArrayProperty.prototype._insertRangeWithoutDirtying.call(this, in_offset, in_array);
      };
      /**
       * @inheritdoc
       */
      ReferenceArrayProperty.prototype._modifyRangeWithoutDirtying = function (in_offset, in_array) {
        ArrayProperty.prototype._modifyRangeWithoutDirtying.call(this, in_offset, in_array);
      };
      exportModule('LYNX.Property.ReferenceArrayProperty', ReferenceArrayProperty);
    }());
    (function () {
      var _ = include('underscore');
      var ArrayProperty = include('LYNX.Property.ArrayProperty');
      var MSG = include('LYNX.Property._constants.MSG');
      var TypeIdHelper = include('LYNX.Property.TypeIdHelper');
      /**
       * This class is a specialized version of the ArrayProperty for enums.
       * Since we internally represent enums as Int32Array this is much more
       * efficient and convinient. Additionally, we provide direct access
       * methods to the enums in the array, e.g. .getEnumString(3) directly
       * returns the enum string at position 3 of the array
       * @param {Object}  in_params - the parameters
       * @param {Number=} [in_params.length=0] the length of the array, if applicable
       * @param {Object}  in_params._enumDictionary the value<->enum dictonary needed to convert the values
       * @constructor
       * @alias LYNX.Property.EnumArrayProperty
       */
      var EnumArrayProperty = function (in_params) {
        ArrayProperty.call(this, in_params, Int32Array, true);
        // whenever an EnumProperty is created by the PropertyFactory, we get a
        // dictonary [value->enum] and [enum->value] to efficiently lookup
        // values/enums for the property.
        this._enumDictionary = in_params._enumDictionary;
      };
      EnumArrayProperty.prototype = Object.create(ArrayProperty.prototype);
      /**
       * Since an enum can be identified by its value and its enum string,
       * we have to check/convert the type here. We also check if a value
       * is suitable for this enum type.
       * @param {number|string} in_value value to be checked/converted
       * @return {number} internal value for this enum type
       */
      EnumArrayProperty.prototype._convertEnumToInternalValue = function (in_value) {
        // check if we've got a string
        if (_.isString(in_value)) {
          var internalEnum = this._enumDictionary.enumEntriesById[in_value];
          if (!internalEnum) {
            throw new Error(MSG.UNKNOWN_ENUM + in_value);
          }
          return internalEnum.value;
        } else {
          if (!this._enumDictionary.enumEntriesByValue[in_value]) {
            throw new Error(MSG.UNKNOWN_ENUM + in_value);
          } else {
            return in_value;
          }
        }
      };
      /**
       * inserts the content of a given array into the array property
       * @param {number} in_offset target index
       * @param {Array<*>} in_array the array to be inserted
       */
      EnumArrayProperty.prototype.insertRange = function (in_offset, in_array) {
        var internalValueArray = [];
        var that = this;
        _.each(in_array, function (element) {
          internalValueArray.push(that._convertEnumToInternalValue(element));
        });
        ArrayProperty.prototype.insertRange.call(this, in_offset, internalValueArray);
      };
      /**
       * sets the array properties elements to the content of the given array
       * all changed elements must already exist
       * @param {number} in_offset target start index
       * @param {Array<*>} in_array contains the elements to be set
       */
      EnumArrayProperty.prototype.setRange = function (in_offset, in_array) {
        var internalValueArray = [];
        var that = this;
        _.each(in_array, function (element) {
          internalValueArray.push(that._convertEnumToInternalValue(element));
        });
        ArrayProperty.prototype.setRange.call(this, in_offset, internalValueArray);
      };
      /**
       * get the array element at a given index
       * @param {number} in_position the target index
       * @return {string} the enum string at that index
       */
      EnumArrayProperty.prototype.getEnumString = function (in_position) {
        var internalValue = this._dataArrayRef.getValue(in_position);
        var resultEntry = this._enumDictionary.enumEntriesByValue[internalValue];
        if (!resultEntry) {
          throw new Error(MSG.UNKNOWN_ENUM + internalValue);
        } else {
          return resultEntry.id;
        }
      };
      /**
       * get an array of the enum strings starting at a given index
       * @param {number} in_offset the start index
       * @param {number} in_length how many should be read
       * @return {Array<string>} the enum strings we asked for
       */
      EnumArrayProperty.prototype.getEnumStrings = function (in_offset, in_length) {
        var result = [];
        for (var i = 0; i < in_length; i++) {
          result.push(this.getEnumString(i + in_offset));
        }
        return result;
      };
      /**
       * Returns the full property type identifier for the ChangeSet including the enum type id
       * @param  {boolean} in_hideCollection - if true the collection type (if applicable) will be omitted
       * @return {string} The typeid
       * @private
       */
      EnumArrayProperty.prototype._getTypeidForChangeSet = function (in_hideCollection) {
        if (in_hideCollection) {
          return TypeIdHelper.createSerializationTypeId(this._typeid, '', true);
        } else {
          return TypeIdHelper.createSerializationTypeId(this._typeid, 'array', true);
        }
      };
      exportModule('LYNX.Property.EnumArrayProperty', EnumArrayProperty);
    }());
    (function () {
      var MapProperty = include('LYNX.Property.MapProperty');
      var _castFunctors = include('LYNX.Property._castFunctors');
      var Int64 = include('LYNX.Utils.Datastructures.Int64');
      var Uint64 = include('LYNX.Utils.Datastructures.Uint64');
      /**
       * A ValueMapProperty is a collection class that can contain an dictionary that maps from strings to primitive types.
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.MapProperty
       * @alias LYNX.Property.ValueMapProperty
       */
      var ValueMapProperty = function (in_params) {
        MapProperty.call(this, in_params);
      };
      ValueMapProperty.prototype = Object.create(MapProperty.prototype);
      // The value map contains primitive types
      ValueMapProperty.prototype._containsPrimitiveTypes = true;
      /**
       * Inserts a property into the map
       *
       * @param {string}                      in_key      - The key under which the entry is added
       * @param {number|string|boolean}       in_value    - The primitive type value to set
       */
      ValueMapProperty.prototype.insert = function (in_key, in_value) {
        var castedValue = this._castFunctor ? this._castFunctor(in_value) : in_value;
        this._insert(in_key, castedValue, true);
      };
      /**
       * Sets the entry with the given key to the value passed in
       *
       * Note: this will overwrite an already existing value
       *
       * @param {string}                in_key    - The key under which the entry is stored
       * @param {number|string|boolean} in_value  - The value or property to store in the map
       */
      ValueMapProperty.prototype.set = function (in_key, in_value) {
        var castedValue = this._castFunctor ? this._castFunctor(in_value) : in_value;
        MapProperty.prototype.set.call(this, in_key, castedValue);
      };
      /**
       * A ValueMapProperty which stores Float32 values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.ValueMapProperty
       * @alias LYNX.Property.Float32MapProperty
       */
      var Float32MapProperty = function (in_params) {
        ValueMapProperty.call(this, in_params);
      };
      Float32MapProperty.prototype = Object.create(ValueMapProperty.prototype);
      Float32MapProperty.prototype._castFunctor = _castFunctors.Float32;
      /**
       * A ValueMapProperty which stores Float64 values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.ValueMapProperty
       * @alias LYNX.Property.Float64MapProperty
       */
      var Float64MapProperty = function (in_params) {
        ValueMapProperty.call(this, in_params);
      };
      Float64MapProperty.prototype = Object.create(ValueMapProperty.prototype);
      Float64MapProperty.prototype._castFunctor = _castFunctors.Float64;
      /**
       * A ValueMapProperty which stores Uint32 values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.ValueMapProperty
       * @alias LYNX.Property.Uint32MapProperty
       */
      var Uint32MapProperty = function (in_params) {
        ValueMapProperty.call(this, in_params);
      };
      Uint32MapProperty.prototype = Object.create(ValueMapProperty.prototype);
      Uint32MapProperty.prototype._castFunctor = _castFunctors.Uint32;
      /**
       * A ValueMapProperty which stores Uint16 values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.ValueMapProperty
       * @alias LYNX.Property.Uint16MapProperty
       */
      var Uint16MapProperty = function (in_params) {
        ValueMapProperty.call(this, in_params);
      };
      Uint16MapProperty.prototype = Object.create(ValueMapProperty.prototype);
      Uint16MapProperty.prototype._castFunctor = _castFunctors.Uint16;
      /**
       * A ValueMapProperty which stores Uint8 values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.ValueMapProperty
       * @alias LYNX.Property.Uint8MapProperty
       */
      var Uint8MapProperty = function (in_params) {
        ValueMapProperty.call(this, in_params);
      };
      Uint8MapProperty.prototype = Object.create(ValueMapProperty.prototype);
      Uint8MapProperty.prototype._castFunctor = _castFunctors.Uint8;
      /**
       * A ValueMapProperty which stores Int32 values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.ValueMapProperty
       * @alias LYNX.Property.Int32MapProperty
       */
      var Int32MapProperty = function (in_params) {
        ValueMapProperty.call(this, in_params);
      };
      Int32MapProperty.prototype = Object.create(ValueMapProperty.prototype);
      Int32MapProperty.prototype._castFunctor = _castFunctors.Int32;
      /**
       * An abstract base class for 64 bit integer map properties
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @protected
       * @extends LYNX.Property.ValueMapProperty
       * @alias LYNX.Property.Integer64MapProperty
       */
      var Integer64MapProperty = function (in_params) {
        ValueMapProperty.call(this, in_params);
      };
      Integer64MapProperty.prototype = Object.create(ValueMapProperty.prototype);
      /**
       * Sets the entry with the given key to the value passed in
       *
       * Note: this will overwrite an already existing value
       *
       * @param {string}                                  in_key    - The key under which the entry is stored
       * @param {Int64|Uint64}  in_value  - The value or property to store in the map
       */
      Integer64MapProperty.prototype.set = function (in_key, in_value) {
        var castedValue = this._castFunctor ? this._castFunctor(in_value) : in_value;
        var myValue = this._entries[in_key];
        if (myValue === undefined) {
          this._insert(in_key, castedValue, true);
        } else if (myValue.getHigh() !== castedValue.getHigh() || myValue.getLow() !== castedValue.getLow()) {
          this._removeByKey(in_key, false);
          this._insert(in_key, in_value, false);
          // Make one final report
          this._reportDirtinessToView();
        }
      };
      /**
       * Function to serialize special primitive types.
       * Some primitive types (e.g. Int64, which is not naively supported by javascript) require
       * special treatment on serialization. For supported types, we can just return the input here.
       *
       * @param {*} in_obj - The object to be serialized
       * @return {LYNX.Property.SerializedChangeSet} the serialized object
       */
      Integer64MapProperty.prototype._serializeValue = function (in_obj) {
        return [
          in_obj.getLow(),
          in_obj.getHigh()
        ];
      };
      /**
       * A ValueMapProperty which stores Int64 Properties
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends Integer64MapProperty
       * @alias LYNX.Property.Int64MapProperty
       */
      var Int64MapProperty = function (in_params) {
        Integer64MapProperty.call(this, in_params);
      };
      Int64MapProperty.prototype = Object.create(Integer64MapProperty.prototype);
      Int64MapProperty.prototype._castFunctor = function (io_value) {
        if (!(io_value instanceof Int64)) {
          throw new Error('Tried to use Int64MapProperty with a non Int64 type.');
        } else {
          return io_value;
        }
      };
      /**
       * Specialized function to deserialize Int64 primitive types.
       * Some primitive types (e.g. Int64, which is not naively supported by javascript) require
       * special treatment on deserialization. For supported types, we can just return the input here.
       *
       * @param {LYNX.Property.SerializedChangeSet} in_serializedObj - The object to be deserialized
       * @return {Int64} the deserialized value
       */
      Int64MapProperty.prototype._deserializeValue = function (in_serializedObj) {
        return new Int64(in_serializedObj[0], in_serializedObj[1]);
      };
      /**
       * A ValueMapProperty which stores Uint64 Properties
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends Integer64MapProperty
       * @alias LYNX.Property.Uint64MapProperty
       */
      var Uint64MapProperty = function (in_params) {
        Integer64MapProperty.call(this, in_params);
      };
      Uint64MapProperty.prototype = Object.create(Integer64MapProperty.prototype);
      Uint64MapProperty.prototype._castFunctor = function (io_value) {
        if (!(io_value instanceof Uint64)) {
          throw new Error('Tried to use Int64MapProperty with a non Int64 type.');
        } else {
          return io_value;
        }
      };
      /**
       * Specialized function to deserialize Uint64 primitive types.
       * Some primitive types (e.g. Uint64, which is not naively supported by javascript) require
       * special treatment on deserialization. For supported types, we can just return the input here.
       *
       * @param {LYNX.Property.SerializedChangeSet} in_serializedObj - The object to be deserialized
       * @return {Uint64} the deserialized value
       */
      Uint64MapProperty.prototype._deserializeValue = function (in_serializedObj) {
        return new Uint64(in_serializedObj[0], in_serializedObj[1]);
      };
      /**
       * A ValueMapProperty which stores Int16 values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.ValueMapProperty
       * @alias LYNX.Property.Int16MapProperty
       */
      var Int16MapProperty = function (in_params) {
        ValueMapProperty.call(this, in_params);
      };
      Int16MapProperty.prototype = Object.create(ValueMapProperty.prototype);
      Int16MapProperty.prototype._castFunctor = _castFunctors.Int16;
      /**
       * A ValueMapProperty which stores Int8 values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.ValueMapProperty
       * @alias LYNX.Property.Int8MapProperty
       */
      var Int8MapProperty = function (in_params) {
        ValueMapProperty.call(this, in_params);
      };
      Int8MapProperty.prototype = Object.create(ValueMapProperty.prototype);
      Int8MapProperty.prototype._castFunctor = _castFunctors.Int8;
      /**
       * A ValueMapProperty which stores string values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.ValueMapProperty
       * @alias LYNX.Property.StringMapProperty
       */
      var StringMapProperty = function (in_params) {
        ValueMapProperty.call(this, in_params);
      };
      StringMapProperty.prototype = Object.create(ValueMapProperty.prototype);
      StringMapProperty.prototype._castFunctor = _castFunctors.String;
      /**
       * A ValueMapProperty which stores boolean values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.ValueMapProperty
       * @alias LYNX.Property.BoolMapProperty
       */
      var BoolMapProperty = function (in_params) {
        ValueMapProperty.call(this, in_params);
      };
      BoolMapProperty.prototype = Object.create(ValueMapProperty.prototype);
      BoolMapProperty.prototype._castFunctor = _castFunctors.Boolean;
      exportModule('LYNX.Property.ValueMapProperty', ValueMapProperty);
      exportModule('LYNX.Property.Float64MapProperty', Float64MapProperty);
      exportModule('LYNX.Property.Float32MapProperty', Float32MapProperty);
      exportModule('LYNX.Property.Uint32MapProperty', Uint32MapProperty);
      exportModule('LYNX.Property.Uint16MapProperty', Uint16MapProperty);
      exportModule('LYNX.Property.Uint64MapProperty', Uint64MapProperty);
      exportModule('LYNX.Property.Uint8MapProperty', Uint8MapProperty);
      exportModule('LYNX.Property.Int32MapProperty', Int32MapProperty);
      exportModule('LYNX.Property.Int16MapProperty', Int16MapProperty);
      exportModule('LYNX.Property.Int64MapProperty', Int64MapProperty);
      exportModule('LYNX.Property.Int8MapProperty', Int8MapProperty);
      exportModule('LYNX.Property.StringMapProperty', StringMapProperty);
      exportModule('LYNX.Property.BoolMapProperty', BoolMapProperty);
    }());
    (function () {
      var StringMapProperty = include('LYNX.Property.StringMapProperty');
      var TypeIdHelper = include('LYNX.Property.TypeIdHelper');
      var PathHelper = include('LYNX.Property.PathHelper');
      var BaseProperty = include('LYNX.Property.BaseProperty');
      /**
       * A StringMapProperty which stores reference values
       *
       * @param {Object} in_params - Input parameters for property creation
       *
       * @constructor
       * @extends LYNX.Property.StringMapProperty
       * @alias LYNX.Property.ReferenceMapProperty
       */
      var ReferenceMapProperty = function (in_params) {
        StringMapProperty.call(this, in_params);
      };
      ReferenceMapProperty.prototype = Object.create(StringMapProperty.prototype);
      /**
       * Returns the typeid for the target of this reference
       *
       * Note: This is the type that is specified in the typeid of this reference and not the actual type
       * of the referenced object, which might inherit from that typeid.
       *
       * @return {string} The typeid of the nodes this reference may point to
       */
      ReferenceMapProperty.prototype.getReferenceTargetTypeId = function () {
        return TypeIdHelper.extractReferenceTargetTypeIdFromReference(this.getTypeid());
      };
      /**
       * Resolves the referenced property for the given key
       *
       * @param {string} in_key - key of the entry to return
       *
       * @return {LYNX.Property.BaseProperty|undefined} The property object the reference points to or undefined if it
       *    could not be resolved or the key could not be found in the map
       */
      ReferenceMapProperty.prototype.getReferencedProperty = function (in_key) {
        var value = this.get(in_key);
        if (value === undefined || value === '') {
          return undefined;
        }
        return this.getParent().resolvePath(value);
      };
      /**
       * Sets the reference to point to the given property object
       *
       * @param {string}                               in_key      - The key under which the entry is stored
       * @param {LYNX.Property.BaseProperty|undefined} in_property - The property to assign to the reference. If undefined
       *     is passed, the reference will be set to an empty string to indicate an empty reference.
       */
      ReferenceMapProperty.prototype.setReferencedProperty = function (in_key, in_property) {
        if (in_property === undefined) {
          this.set(in_key, '');
        } else {
          // TODO: Check whether this is still the correct path once we start to support repository references
          this.set(in_key, in_property.getAbsolutePath());
        }
      };
      /**
       * Inserts the reference that points  to the given property object
       *
       * @param {string}                               in_key      - The key under which the entry is stored
       * @param {LYNX.Property.BaseProperty|undefined} in_property - The property to assign to the reference. If undefined
       *     is passed, the reference will be set to an empty string to indicate an empty reference.
       */
      ReferenceMapProperty.prototype.insertReferencedProperty = function (in_key, in_property) {
        if (in_property === undefined) {
          this.insert(in_key, '');
        } else {
          // TODO: Check whether this is still the correct path once we start to support repository references
          this.insert(in_key, in_property.getAbsolutePath());
        }
      };
      /**
       * Checks whether the reference is valid. This is either the case when it is empty or when the referenced
       * property exists.
       *
       * @param {string} in_key - key of the entry to check
       * @return {boolean} Is this a valid reference?
       */
      ReferenceMapProperty.prototype.isReferenceValid = function (in_key) {
        return this.has(in_key) && (this.get(in_key) === '' || this.getReferencedProperty(in_key) !== undefined);
      };
      /**
       * Sets the entry with the given key to the value passed in
       *
       * @param {string} in_key   - The key under which the entry is stored
       * @param {String} in_value - the new value. This must be either an empty string or a path that is
       *                            repository absolute.
       */
      ReferenceMapProperty.prototype.set = function (in_key, in_value) {
        // Forward the call to setValue
        StringMapProperty.prototype.set.call(this, in_key, in_value);
      };
      /**
       * Inserts a property into the map
       *
       * @param {string}                      in_key      - the new value. This must be either an empty string or a
       *                                                    path that is repository absolute.
       * @param {number|string|boolean}       in_value    - The primitive type value to set
       */
      ReferenceMapProperty.prototype.insert = function (in_key, in_value) {
        StringMapProperty.prototype.insert.call(this, in_key, in_value);
      };
      /**
       * @inheritdoc
       */
      ReferenceMapProperty.prototype._resolvePathSegment = function (in_segment, in_segmentType, out_disableCache) {
        out_disableCache.disableCache = true;
        // Array tokens are automatically resolved
        if (in_segmentType === PathHelper.TOKEN_TYPES.ARRAY_TOKEN) {
          return this.getReferencedProperty(in_segment);
        } else {
          // Everything else is handled by the implementation in the base property
          return BaseProperty.prototype._resolvePathSegment.call(this, in_segment, in_segmentType, out_disableCache);
        }
      };
      exportModule('LYNX.Property.ReferenceMapProperty', ReferenceMapProperty);
    }());
    (function () {
      /**
       * @constructor
       * @alias LYNX.Property.JSONSchemaToPropertySetsTemplateConverter
       */
      var JSONSchemaToPropertySetsTemplateConverter = function () {
        // This is where we put references to all the schema definitions that we
        // ever parsed so that other schemas can refer to them without having to
        // redefine them.
        var schemaDefinitions = {};
        // Private functions
        /**
         * Returns the absolute document URI of the given definition.
         *
         * A definition can modify the document URI for its sub-definitions if its id
         * is an absolute URI for example or if it specifies a new relative URI.
         *
         * @param {String} in_docPath The current document path
         * @param {String} in_defId The definition id
         * @return {String} Absolute document URI
         */
        var getDefinitionDocPath = function (in_docPath, in_defId) {
          // TODO: Url parsing is much too naive, do something better.
          // By default, the path is preserved.
          var docPath = in_docPath;
          if (in_defId !== undefined) {
            if (typeof in_defId !== 'string') {
              throw new Error('Value "id" of a definition should be a string. "' + in_defId + '" (' + typeof in_defId + ') is not valid.');
            }
            if (in_defId.indexOf('#') === 0) {
            } else {
              if (in_defId.indexOf(':') !== -1) {
                // This is an absolute URI.
                docPath = in_defId;
              } else {
                // This is a document path based on the base URI.
                // Let's first extract the base URI.
                var slashPos = in_docPath.lastIndexOf('/');
                if (slashPos !== -1) {
                  docPath = in_docPath.slice(0, slashPos + 1);
                } else {
                  docPath = '';
                }
                // Let's extract the absolute name of the document (before any '#').
                var sharpPos = in_defId.indexOf('#');
                if (sharpPos !== -1) {
                  docPath += in_defId.slice(0, sharpPos);
                } else {
                  docPath += in_defId;
                }
              }
            }
            // For consistency, let's strip the final # if any.
            if (docPath.lastIndexOf('#') === docPath.length - 1) {
              docPath = docPath.slice(0, -1);
            }
          }
          return docPath;
        };
        /**
         * Returns an absolute defintion id based on an absolute document path and a definition id.
         *
         * The absolute id of a definition depends on the current document path if the id
         * of the definition is not itself an absolute path.
         *
         * @param {String} in_docPath The current document path
         * @param {String} in_defId The definition id
         * @return {String} Absolute definition URI
         */
        var getDefinitionAbsoluteId = function (in_docPath, in_defId) {
          // TODO: Url parsing is much too naive, do something better.
          var absId;
          if (in_defId !== undefined) {
            if (typeof in_defId !== 'string') {
              throw new Error('Field "id" of a definition should be a string. "' + in_defId + '" (' + typeof in_defId + ') is not valid.');
            }
            if (in_defId.length === 0) {
              absId = in_docPath;
            } else if (in_defId.indexOf('#') === 0) {
              // This is a document-local identifier so append the definition id to
              // the doc path to get the absolute id of the definition.
              absId = in_docPath + (in_defId.length === 1 ? '' : in_defId);
            } else if (in_defId.indexOf(':') !== -1) {
              // This is an absolute URI.
              absId = in_defId;
              // TODO: This is too brutal, I need to remove empty fragment only, not any terminating '#'.
              // For consistency, let's strip empty fragment if any.
              if (absId.lastIndexOf('#') === absId.length - 1) {
                absId = absId.slice(0, -1);
              }
            } else if (in_defId.indexOf('//') === 0) {
              // This is a new authority. Let's keep the scheme and replace all the rest.
              var colonPos = in_docPath.indexOf(':');
              if (colonPos !== -1) {
                absId = in_docPath.slice(0, colonPos + 1) + in_defId;
              } else {
                absId = in_defId;
              }
            } else if (in_defId.indexOf('/') === 0) {
              // This is a new path. Let's keep scheme:authority and replace all the rest.
              var authPos = in_docPath.indexOf('//');
              if (authPos !== -1) {
                // Found the authority, let's find the start of the path.
                var pathEndPos = in_docPath.indexOf('/', authPos + 2);
                absId = in_docPath.slice(0, pathEndPos) + in_defId;
              } else {
                // No authority, let's find scheme
                var colonPos = in_docPath.indexOf(':');
                if (colonPos !== -1) {
                  absId = in_docPath.slice(0, colonPos + 1) + in_defId;
                } else {
                  absId = in_defId;
                }
              }
            } else {
              // This is a document path based on the base URI so append the
              // definition id to the base URI to get the absolute id of the definition.
              // Let's first extract the base URI.
              var slashPos = in_docPath.lastIndexOf('/');
              if (slashPos !== -1) {
                absId = in_docPath.slice(0, slashPos + 1) + in_defId;
              } else {
                absId = in_defId;
              }
            }
          }
          return absId;
        };
        /**
         * Recursively parses the object refered by the given reference ('$ref').
         *
         * If the refered object is an individual Property Sets Template, just add a
         * reference to it. Otherwise expands the refered object in place.
         *
         * @param {String} in_ref The reference
         * @param {String} in_docPath The current document path
         * @param {Object} io_templates The generated PropertySets Templates
         * @return {Object} The parsed property
         */
        var parseSchemaReference = function (in_ref, in_docPath, io_templates) {
          var slashPos, id, arrNames, absId, def, i;
          if (typeof in_ref !== 'string') {
            throw new Error('Value of "$ref" should be a string. "' + in_ref + '" (' + typeof in_ref + ') is not valid.');
          }
          // This ref contains a path to the document followed by a path to the
          // specific definition in it (in JSON pointer notation). I need to find the
          // separation between the two.
          slashPos = in_ref.indexOf('#/');
          if (slashPos === -1) {
            // No path to a definition, only a path to a document.
            id = in_ref;
            arrNames = [];
          } else {
            id = in_ref.slice(0, slashPos);
            arrNames = in_ref.slice(slashPos + 2).split('/');
          }
          absId = getDefinitionAbsoluteId(in_docPath, id);
          if (!schemaDefinitions.hasOwnProperty(absId)) {
            throw new Error('Couldn\'t find reference "' + absId + '".');
          }
          // I could find the document referenced. Now let's find the precise
          // definition inside this document.
          def = schemaDefinitions[absId];
          for (i = 0; i < arrNames.length; i++) {
            if (typeof def !== 'object') {
              throw new Error('A referenced definition should be an object. "' + def + '" (' + typeof def + ') is invalid.');
            }
            def = def[arrNames[i]];
          }
          // I could find the precise definition. Let's parse it.
          return parseSchemaDefinition(def, in_docPath, true, io_templates);  // eslint-disable-line no-use-before-define, max-len
        };
        /**
         * Recursively parses the given properties ('properties': {...}) and adds them
         * to 'out_props' argument.
         *
         * @param {String} in_properties The properties to parse
         * @param {String} in_docPath The current document path
         * @param {Object} io_templates The generated PropertySets Templates
         * @param {Object} out_props The generated properties
         */
        var parseSchemaProperties = function (in_properties, in_docPath, io_templates, out_props) {
          var keys, i, name, prop, obj;
          // 'properties'
          if (in_properties !== undefined) {
            if (typeof in_properties !== 'object') {
              throw new Error('The "properties" value should be an object. "' + in_properties + '" (' + typeof in_properties + ') is invalid.');
            }
            keys = Object.keys(in_properties);
            for (i = 0; i < keys.length; i++) {
              name = keys[i];
              prop = in_properties[name];
              if (name === '$ref') {
                obj = parseSchemaReference(prop, in_docPath, io_templates);
                // I need to drop the new object and only append its properties to
                // the ones I already have.
                if (obj && obj.properties) {
                  // I can't use Array.concat() because I need to add the properties to the existing array.
                  for (i = 0; i < obj.properties.length; i++) {
                    out_props.push(obj.properties[i]);
                  }
                }
              } else {
                prop = parseSchemaDefinition(prop, in_docPath, false, io_templates);
                // eslint-disable-line no-use-before-define, max-len
                if (prop) {
                  prop.id = name;
                  out_props.push(prop);
                }
              }
            }
          }
        };
        /**
         * Recursively parses the given oneOf array ('oneOf': [...]) and adds the properties
         * to 'out_props' argument.
         *
         * TODO: Support arrays of more than one element.
         *
         * @param {String} in_oneOf The properties to parse
         * @param {String} in_docPath The current document path
         * @param {Object} io_templates The generated PropertySets Templates
         * @param {Object} out_props The generated properties
         */
        var parseSchemaOneOf = function (in_oneOf, in_docPath, io_templates, out_props) {
          // 'oneOf' is supported if there is only one element.
          if (in_oneOf !== undefined) {
            if (Object.prototype.toString.call(in_oneOf) !== '[object Array]' || in_oneOf.length > 1) {
              throw new Error('The "oneOf" object is supported only for arrays of one object.');
            }
            if (in_oneOf.length === 1) {
              if (typeof in_oneOf[0] !== 'object') {
                throw new Error('The "oneOf" array should contain objects. "' + typeof in_oneOf[0] + '" is invalid.');
              }
              parseSchemaProperties(in_oneOf[0], in_docPath, io_templates, out_props);
            }
          }
        };
        /**
         * Recursively parses the given allOf array ('allOf': [...]) and adds the properties
         * to 'out_props' argument.
         *
         * @param {String} in_allOf The properties to parse
         * @param {String} in_docPath The current document path
         * @param {Object} io_templates The generated PropertySets Templates
         * @param {Object} out_props The generated properties
         */
        var parseSchemaAllOf = function (in_allOf, in_docPath, io_templates, out_props) {
          var i;
          // 'allOf'.
          if (in_allOf !== undefined) {
            if (Object.prototype.toString.call(in_allOf) !== '[object Array]') {
              throw new Error('The "allOf" object should be an array.');
            }
            for (i = 0; i < in_allOf.length; i++) {
              if (typeof in_allOf[i] !== 'object') {
                throw new Error('The "allOf" array should contain objects. Element ' + i + '(' + typeof in_allOf[i] + ') is invalid.');
              }
              parseSchemaProperties(in_allOf[i], in_docPath, io_templates, out_props);
            }
          }
        };
        /**
         * Adds the given PropertySets Template to the generated templates if not already there.
         *
         * @param {Object} in_pst The newly generated PropertySets Template
         * @param {Object} io_templates The generated PropertySets Templates
         */
        var addToTemplates = function (in_pst, io_templates) {
          var i;
          for (i = 0; i < io_templates.length; ++i) {
            if (io_templates[i].typeid === in_pst.typeid) {
              break;
            }
          }
          if (i === io_templates.length) {
            io_templates.push(in_pst);
          }
        };
        /**
         * Recursively parses the given definition and returns it.
         *
         * @param {String} in_obj The definition to parse
         * @param {String} in_docPath The current document path
         * @param {Boolean} in_isDeclaration True if this is a definition declaration
         * @param {Object} io_templates The generated PropertySets Templates
         * @return {Object} The parsed definition
         */
        var parseSchemaDefinition = function (in_obj, in_docPath, in_isDeclaration, io_templates) {
          var obj = {};
          var properties = [];
          var docPath;
          // This is a bit ugly, but to prevent infinite recursions we put a
          // '__parsing' variable inside the definitions we are currently parsing.
          // We should never start parsing while we're already parsing.
          if (in_obj.hasOwnProperty('__parsing')) {
            throw new Error('Infinite recursion detected in ' + in_docPath);
          }
          in_obj.__parsing = true;
          try {
            // id
            docPath = getDefinitionDocPath(in_docPath, in_obj.id);
            // $ref: According to JSON Schema spec., any object having a $ref property
            //       inherits everything from this property and then any local
            //       property must be ignored.
            if (in_obj.$ref) {
              obj = parseSchemaReference(in_obj.$ref, docPath, io_templates);
            } else {
              // description
              if (in_obj.description) {
                obj.annotation = { description: in_obj.description };
              }
              // type
              if (!in_obj.type) {
                // We need a type. Let's suppose it's an object.
                // TODO: Support 'enum' that doesn't require a type.
                in_obj.type = 'object';
              }
              if (in_obj.type === 'string') {
                obj.typeid = 'String';
              } else if (in_obj.type === 'integer') {
                obj.typeid = 'Int32';
              } else if (in_obj.type === 'number') {
                obj.typeid = 'Float64';
              } else if (in_obj.type === 'object') {
                // 'oneOf'
                parseSchemaOneOf(in_obj.oneOf, docPath, io_templates, properties);
                // 'allOf'
                parseSchemaAllOf(in_obj.allOf, docPath, io_templates, properties);
                // 'properties'
                parseSchemaProperties(in_obj.properties, docPath, io_templates, properties);
              } else {
                throw new Error('Unsupported value of field "type": ' + in_obj.type + '.');
              }
              if (properties.length > 0) {
                if (Object.prototype.toString.call(in_obj.required) === '[object Array]') {
                  var i, reqPropName, j;
                  for (i = 0; i < in_obj.required.length; i++) {
                    reqPropName = in_obj.required[i];
                    if (typeof reqPropName !== 'string') {
                      console.warn('Required property name should be a string, "' + reqPropName + '" (' + typeof reqPropName + ') is not valid.');
                    } else {
                      for (j = 0; j < properties.length; j++) {
                        if (properties[j].id === reqPropName) {
                          properties[j].required = true;
                          break;
                        }
                      }
                      if (j >= properties.length) {
                        console.warn('Required property name "' + reqPropName + '" doesn\'t match any property in object.');
                      }
                    }
                  }
                }
                obj.properties = properties;
              }
              /* Some keywords don't have any meaning for JSON Schema but are useful for
               Property Sets Templates. We can insert these keywords in a JSON Schema
               without hurt. They will be copied as-is to the Property Set Template
               (after some basic validation). */
              // inherits
              if (in_obj.inherits) {
                // MUST be a string or an array of strings
                if (typeof in_obj.inherits === 'string') {
                  obj.inherits = in_obj.inherits;
                } else {
                  if (Object.prototype.toString.call(in_obj.inherits) !== '[object Array]') {
                    throw new Error('The "inherits" object should be a string or an array of strings.');
                  }
                  // MUST be an array of strings so a shallow copy of the array is enough.
                  obj.inherits = in_obj.inherits.slice();
                }
              }
              // context
              if (in_obj.context) {
                if (typeof in_obj.context !== 'string') {
                  throw new Error('The "context" value should be a string.');
                }
                obj.context = in_obj.context;
              }
              // length
              if (in_obj.length) {
                if (in_obj.context !== 'array') {
                  console.log('Warning: ignoring "length" value since "context" is not "array".');
                } else {
                  if (typeof in_obj.length !== 'number') {
                    throw new Error('The "length" value should be a number.');
                  }
                  obj.length = in_obj.length;
                }
              }
              // typeid
              if (in_obj.typeid) {
                obj.typeid = in_obj.typeid;
                if (in_isDeclaration) {
                  // When we have a typeid we want to create a standalone PropertySets Template.
                  addToTemplates(obj, io_templates);
                  // and we return an object refering to this template instead
                  obj = { 'typeid': in_obj.typeid };
                }
              }
            }
          } finally {
            delete in_obj.__parsing;
          }
          return obj;
        };
        /**
         * Recursively indexes the definitions of the schema.
         *
         * @param {String} in_docPath The current document path
         * @param {Object} in_newDefs The new definitions to index
         */
        var indexSchemaDefinitions = function (in_docPath, in_newDefs) {
          if (in_newDefs) {
            var keys = Object.keys(in_newDefs);
            var i, def, docPath, absId;
            for (i = 0; i < keys.length; i++) {
              def = in_newDefs[keys[i]];
              if (def.id) {
                absId = getDefinitionAbsoluteId(in_docPath, def.id);
                // Detect duplicates
                if (schemaDefinitions.hasOwnProperty(absId)) {
                  throw new Error('Duplicate definition for ' + absId);
                }
                schemaDefinitions[absId] = def;
              }
              docPath = getDefinitionDocPath(in_docPath, def.id);
              // Let's recursively index the definitions.
              indexSchemaDefinitions(docPath, def.definitions);
            }
          }
        };
        // Privileged functions
        /**
         * Recursively parses the given JSON Schema and returns the corresponding
         * array of PropertySets Templates.
         *
         * @param {Object} in_schema  The JSON Schema to convert
         * @return {Array.<object>} Array of Property Sets Templates
         */
        this.getPropertySetsTemplates = function (in_schema) {
          var psetTemplates = [];
          // TODO: Validate Schema
          // id
          if (!in_schema.id) {
            throw new Error('Field "id" is required.');
          }
          var schemaUri = getDefinitionDocPath('', in_schema.id);
          // typeid
          if (!in_schema.typeid) {
            throw new Error('Field "typeid" is required. It is the "typeid" of the resulting PropertySets Template.');
          }
          // definitions
          // Let's index the definitions first in case we need one of them while
          // parsing the schema.
          indexSchemaDefinitions(schemaUri, { 'root': in_schema });
          // console.log('Indexed definitions:');
          // console.log(schemaDefinitions);
          // Let's now parse the element and its properties.
          parseSchemaDefinition(in_schema, schemaUri, true, psetTemplates);
          // console.log('Final PSet Template:');
          // console.log(psetTemplates);
          return psetTemplates;
        };
      };
      exportModule('LYNX.Property.JSONSchemaToTemplateConverter', JSONSchemaToPropertySetsTemplateConverter);
    }());
    (function () {
      /**
       * @namespace LYNX.Property
       * @alias LYNX.Property
       * Namespace containing all classes related to the Lynx property sets
       */
      var Collection = include('LYNX.Utils.Datastructures.Collection');
      var SortedCollection = include('LYNX.Utils.Datastructures.SortedCollection');
      var deepCopy = include('LYNX.Utils.deepCopy');
      var PropertyTemplate = include('LYNX.Property.PropertyTemplate');
      var TypeIdHelper = include('LYNX.Property.TypeIdHelper');
      var _ = include('underscore');
      // Include the property classes
      var BaseProperty = include('LYNX.Property.BaseProperty');
      var NamedProperty = include('LYNX.Property.NamedProperty');
      var NodeProperty = include('LYNX.Property.NodeProperty');
      var NamedNodeProperty = include('LYNX.Property.NamedNodeProperty');
      var RepositoryReferenceProperty = include('LYNX.Property.RepositoryReferenceProperty');
      // Include all primitive properties –- will register at the end.
      var Uint32Property = include('LYNX.Property.Uint32Property');
      var StringProperty = include('LYNX.Property.StringProperty');
      var Float32Property = include('LYNX.Property.Float32Property');
      var Int32Property = include('LYNX.Property.Int32Property');
      var Int64Property = include('LYNX.Property.Int64Property');
      var Uint64Property = include('LYNX.Property.Uint64Property');
      var BoolProperty = include('LYNX.Property.BoolProperty');
      var ReferenceProperty = include('LYNX.Property.ReferenceProperty');
      var Float64Property = include('LYNX.Property.Float64Property');
      var Uint16Property = include('LYNX.Property.Uint16Property');
      var Uint8Property = include('LYNX.Property.Uint8Property');
      var Int16Property = include('LYNX.Property.Int16Property');
      var Int8Property = include('LYNX.Property.Int8Property');
      var EnumProperty = include('LYNX.Property.EnumProperty');
      // Include collection properties
      var ArrayProperty = include('LYNX.Property.ArrayProperty');
      var SetProperty = include('LYNX.Property.SetProperty');
      var MapProperty = include('LYNX.Property.MapProperty');
      var Float32ArrayProperty = include('LYNX.Property.Float32ArrayProperty');
      var Float64ArrayProperty = include('LYNX.Property.Float64ArrayProperty');
      var Uint32ArrayProperty = include('LYNX.Property.Uint32ArrayProperty');
      var Int32ArrayProperty = include('LYNX.Property.Int32ArrayProperty');
      var Uint64ArrayProperty = include('LYNX.Property.Uint64ArrayProperty');
      var Int64ArrayProperty = include('LYNX.Property.Int64ArrayProperty');
      var Uint16ArrayProperty = include('LYNX.Property.Uint16ArrayProperty');
      var Int16ArrayProperty = include('LYNX.Property.Int16ArrayProperty');
      var Uint8ArrayProperty = include('LYNX.Property.Uint8ArrayProperty');
      var Int8ArrayProperty = include('LYNX.Property.Int8ArrayProperty');
      var StringArrayProperty = include('LYNX.Property.StringArrayProperty');
      var ReferenceArrayProperty = include('LYNX.Property.ReferenceArrayProperty');
      var EnumArrayProperty = include('LYNX.Property.EnumArrayProperty');
      var Float64MapProperty = include('LYNX.Property.Float64MapProperty');
      var Float32MapProperty = include('LYNX.Property.Float32MapProperty');
      var Uint32MapProperty = include('LYNX.Property.Uint32MapProperty');
      var Uint64MapProperty = include('LYNX.Property.Uint64MapProperty');
      var Uint16MapProperty = include('LYNX.Property.Uint16MapProperty');
      var Uint8MapProperty = include('LYNX.Property.Uint8MapProperty');
      var Int32MapProperty = include('LYNX.Property.Int32MapProperty');
      var Int64MapProperty = include('LYNX.Property.Int64MapProperty');
      var Int16MapProperty = include('LYNX.Property.Int16MapProperty');
      var Int8MapProperty = include('LYNX.Property.Int8MapProperty');
      var StringMapProperty = include('LYNX.Property.StringMapProperty');
      var ReferenceMapProperty = include('LYNX.Property.ReferenceMapProperty');
      var BoolMapProperty = include('LYNX.Property.BoolMapProperty');
      var semver = include('LYNX.Utils.ThirdParty.semver');
      var generateGUID = include('LYNX.Utils.generateGUID');
      var TemplateValidator = include('LYNX.Property.TemplateValidator');
      var MSG = include('LYNX.Property._constants.MSG');
      var JSONSchemaToTemplateConverter = include('LYNX.Property.JSONSchemaToTemplateConverter');
      /**
       * Creates a new collection of property templates used later on
       * to instantiate property sets based on the typeid (Type identifier).
       *
       * @constructor
       * @alias LYNX.Property.PropertyFactory
       */
      var PropertyFactory = function () {
        this._templateValidator = new TemplateValidator();
        // Collection containing both local templates and primitive properties
        this._localPrimitivePropertiesAndTemplates = new Collection();
        // Collection containing the local templates sorted by their version number in an ascending order
        this._localVersionedTemplates = new Collection();
        // Collection containing the remote templates sorted by their version number in an ascending order
        // within a specified scope.
        this._remoteScopedAndVersionedTemplates = new Collection();
        // Converter from JSON Schemas to PropertySets Templates
        this._JSONSchemaToTemplateConverter = new JSONSchemaToTemplateConverter();
        this._init();
      };
      /**
       * Initialize the PropertyFactory by registering primitive types
       */
      PropertyFactory.prototype._init = function () {
        // Register all primitive properties
        this._registerTypeId('BaseProperty', BaseProperty, 'all');
        this._registerTypeId('NodeProperty', NodeProperty, 'all');
        // Register the primitive types for the context single
        this._registerTypeId('Int8', Int8Property);
        this._registerTypeId('Uint8', Uint8Property);
        this._registerTypeId('Int16', Int16Property);
        this._registerTypeId('Uint16', Uint16Property);
        this._registerTypeId('Int32', Int32Property);
        this._registerTypeId('Int64', Int64Property);
        this._registerTypeId('Uint64', Uint64Property);
        this._registerTypeId('Uint32', Uint32Property);
        this._registerTypeId('Float32', Float32Property);
        this._registerTypeId('Float64', Float64Property);
        this._registerTypeId('Bool', BoolProperty);
        this._registerTypeId('String', StringProperty);
        this._registerTypeId('Reference', ReferenceProperty);
        this._registerTypeId('Enum', EnumProperty);
        // Register the primitive types for the context array
        this._registerTypeId('Int8', Int8ArrayProperty, 'array');
        this._registerTypeId('Uint8', Uint8ArrayProperty, 'array');
        this._registerTypeId('Int16', Int16ArrayProperty, 'array');
        this._registerTypeId('Uint16', Uint16ArrayProperty, 'array');
        this._registerTypeId('Int32', Int32ArrayProperty, 'array');
        this._registerTypeId('Uint32', Uint32ArrayProperty, 'array');
        this._registerTypeId('Int64', Int64ArrayProperty, 'array');
        this._registerTypeId('Uint64', Uint64ArrayProperty, 'array');
        this._registerTypeId('Float32', Float32ArrayProperty, 'array');
        this._registerTypeId('Float64', Float64ArrayProperty, 'array');
        this._registerTypeId('String', StringArrayProperty, 'array');
        this._registerTypeId('Reference', ReferenceArrayProperty, 'array');
        // Register the primitive types for the context map
        this._registerTypeId('Int8', Int8MapProperty, 'map');
        this._registerTypeId('Uint8', Uint8MapProperty, 'map');
        this._registerTypeId('Int16', Int16MapProperty, 'map');
        this._registerTypeId('Uint16', Uint16MapProperty, 'map');
        this._registerTypeId('Int32', Int32MapProperty, 'map');
        this._registerTypeId('Uint32', Uint32MapProperty, 'map');
        this._registerTypeId('Int64', Int64MapProperty, 'map');
        this._registerTypeId('Uint64', Uint64MapProperty, 'map');
        this._registerTypeId('Float32', Float32MapProperty, 'map');
        this._registerTypeId('Float64', Float64MapProperty, 'map');
        this._registerTypeId('Bool', BoolMapProperty, 'map');
        this._registerTypeId('String', StringMapProperty, 'map');
        this._registerTypeId('Reference', ReferenceMapProperty, 'map');
        // Register the default templates
        var NamedPropertyTemplate = {
          typeid: 'NamedProperty',
          properties: [{
              id: 'guid',
              typeid: 'String'
            }]
        };
        var NamedNodePropertyTemplate = {
          typeid: 'NamedNodeProperty',
          inherits: [
            'NodeProperty',
            'NamedProperty'
          ]
        };
        var RepositoryReferencePropertyTemplate = {
          typeid: 'RepositoryReferenceProperty',
          properties: [
            {
              id: 'repositoryGUID',
              typeid: 'String'
            },
            {
              id: 'commitGUID',
              typeid: 'String'
            },
            {
              id: 'branchGUID',
              typeid: 'String'
            },
            {
              id: 'followBranch',
              typeid: 'Bool'
            }
          ]
        };
        this._registerTypeId(NamedPropertyTemplate.typeid, NamedPropertyTemplate);
        this._registerTypeId(NamedNodePropertyTemplate.typeid, NamedNodePropertyTemplate);
        this._registerTypeId(RepositoryReferencePropertyTemplate.typeid, RepositoryReferencePropertyTemplate);
      };
      /**
       * This symbol is used to enable the experimental, unfinished repository references feature in the corresponding
       * tests.
       *
       * @type {boolean}
       * @private
       */
      PropertyFactory._enableExperimentalRepositoryReferences = false;
      /**
       * Helper function used to extract the error messages from a list of Error objects
       * @param {Array.<Error>} in_errors List of error objects
       * @private
       * @return {Array.<string>} List of error messages
       */
      var _extractErrorMessage = function (in_errors) {
        return _.map(in_errors, function (error) {
          return error.message;
        });
      };
      /**
       * Helper function used to create a sorted collection
       * @return {LYNX.Utils.Datastructures.SortedCollection} Empty sorted collection
       * @private
       */
      var _createVersionedSortedCollection = function () {
        var collection = new SortedCollection();
        collection.setComparisonFunction(function (versionA, versionB) {
          if (semver.gt(versionA, versionB)) {
            return 1;
          } else if (semver.lt(versionA, versionB)) {
            return -1;
          }
          return 0;
        });
        return collection;
      };
      /**
       * Register a template
       *
       * @param {LYNX.Property.PropertyTemplate|object} in_template - the template to register.
       */
      PropertyFactory.prototype.register = function (in_template) {
        var typeid = in_template.typeid;
        if (!this._isNativePropertyConstructor(in_template)) {
          if (!(in_template instanceof PropertyTemplate)) {
            in_template = new PropertyTemplate(in_template);
          }
          // Here we are registering a user defined template. We need to check whether it is already registered
          // as a remote template.
          if (this._localPrimitivePropertiesAndTemplates.has(typeid)) {
            console.warn('Registering a typeid that already exists typeid = ' + typeid);
            // Template already exists. The incoming template MUST match what is currently registered.
            // If they do not match, throw an error letting the user know that the templates are incompatible.
            var registeredTemplate = this._localPrimitivePropertiesAndTemplates.item(typeid);
            var templateValidator = new TemplateValidator({ skipSemver: true });
            var validationResults = templateValidator.validate(registeredTemplate.serialize(), in_template.serialize());
            if (!validationResults.isValid) {
              throw new Error(MSG.TEMPLATE_MISMATCH + typeid + '\n  errors = ' + JSON.stringify(_extractErrorMessage(validationResults.errors), 0, 2));
            } else {
              // Template is already registered. Do nothing.
              return;
            }
          } else if (in_template.isVersioned()) {
            var validationResult = this.validate(in_template.serialize());
            if (validationResult.isValid) {
              var typeidWithoutVersion = in_template.getTypeidWithoutVersion();
              var version = in_template.getVersion();
              this._validateSemver(in_template, true);
              // Semver validation passed. Add the template to the local versioned templates collection
              if (this._localVersionedTemplates.has(typeidWithoutVersion)) {
                this._localVersionedTemplates.item(typeidWithoutVersion).add(version, in_template);
              } else {
                var collection = _createVersionedSortedCollection();
                this._localVersionedTemplates.add(typeidWithoutVersion, collection);
                collection.add(version, in_template);
              }
            } else {
              throw new Error('Failed to register typeid = ' + typeid + '\n  errors = ' + JSON.stringify(_extractErrorMessage(validationResult.errors), 0, 2));
            }
          } else {
            throw new Error(MSG.UNVERSIONED_TEMPLATE + ' Template with typeid = ' + typeid + ' is not versioned.');
          }
          // Forward to the internal function
          this._registerTypeId(typeid, in_template);
        } else {
          throw new Error('Cannot register a primitive property with the public `register` function typeid = ' + typeid);
        }
      };
      /**
       * Recursively parses the object of the specified type and returns the created
       * array of PropertySets Templates. It does the same thing as the registerFrom()
       * function, but it returns the array of templates instead of registering them.
       * Throws an error if any conversion error occurs.
       *
       * @param {String} in_fromType  The type of the object to convert.
       *                              The only type supported so far is 'JSONSchema'.
       * @param {Object} in_toConvert  The object to convert
       * @return {Array.<object>} Array of Property Sets Templates
       */
      PropertyFactory.prototype.convertToTemplates = function (in_fromType, in_toConvert) {
        switch (in_fromType) {
        case 'JSONSchema':
          return this._JSONSchemaToTemplateConverter.getPropertySetsTemplates(in_toConvert);
        default:
          throw new Error('Unknown type');
        }
      };
      /**
       * Recursively parses the object of the specified type and registers the created
       * Property Sets Templates. It does the same work as the convertToTemplates()
       * function, but it registers the templates for you instead of returning them.
       * Throws an error if any conversion error occurs.
       *
       * @param {String} in_fromType  The type of the object to convert.
       *                              The only type supported so far is 'JSONSchema'.
       * @param {Object} in_toConvert  The object to convert
       */
      PropertyFactory.prototype.registerFrom = function (in_fromType, in_toConvert) {
        var psetsTemplates = this.convertToTemplates(in_fromType, in_toConvert);
        for (var i = 0; i < psetsTemplates.length; i++) {
          this.register(psetsTemplates[i]);
        }
      };
      /**
       * Validate semver.
       * Here we compare the incoming template with its previous/next version in the
       * local and remote registry with the intent of detecting semver violations.
       * The semver rules for templates are as follows:
       * - If the template structure has been altered (delete/modify existing field) then the MAJOR version should be bumped
       * - If the template structure has been extended (add new fields) then the MINOR version should be bumped
       * - If the annotation field has been updated then the PATCH version should be bumped
       * If any of these rules have been broken then a warning message is printed onto the console.
       * @param {object|LYNX.Property.PropertyTemplate} in_template - the template to compare against
       *  its previous or next versions
       * @param {boolean} in_compareRemote - Flag indicating whether we want to compare the given
       *  template against the remote registry
       * @private
       */
      PropertyFactory.prototype._validateSemver = function (in_template, in_compareRemote) {
        var typeidWithoutVersion = in_template.getTypeidWithoutVersion();
        var version = in_template.getVersion();
        var typeid = in_template.typeid;
        var validationResults;
        var warnings = [];
        if (this._localVersionedTemplates.has(typeidWithoutVersion)) {
          var previousLocalVersion = this._localVersionedTemplates.item(typeidWithoutVersion).getNearestPreviousItem(version);
          if (previousLocalVersion) {
            validationResults = this._templateValidator.validate(in_template.serialize(), previousLocalVersion.serialize());
            warnings.push.apply(warnings, validationResults.warnings);
          } else {
            var nextLocalVersion = this._localVersionedTemplates.item(typeidWithoutVersion).getNearestNextItem(version);
            if (nextLocalVersion) {
              validationResults = this._templateValidator.validate(nextLocalVersion.serialize(), in_template.serialize());
              warnings.push.apply(warnings, validationResults.warnings);
            }
          }
        }
        if (in_compareRemote) {
          var that = this;
          this._remoteScopedAndVersionedTemplates.iterate(function (scope, remoteVersionedTemplates) {
            if (remoteVersionedTemplates.has(typeidWithoutVersion)) {
              var previousRemoteVersion = remoteVersionedTemplates.item(typeidWithoutVersion).getNearestPreviousItem(version);
              if (previousRemoteVersion) {
                validationResults = that._templateValidator.validate(in_template.serialize(), previousRemoteVersion.serialize());
                warnings.push.apply(warnings, validationResults.warnings);
              } else {
                var nextRemoteVersion = remoteVersionedTemplates.item(typeidWithoutVersion).getNearestNextItem(version);
                if (nextRemoteVersion) {
                  validationResults = that._templateValidator.validate(nextRemoteVersion.serialize(), in_template.serialize());
                  warnings.push.apply(warnings, validationResults.warnings);
                }
              }
            }
          });
        }
        if (!_.isEmpty(warnings)) {
          console.warn('Template with typeid = ' + typeid + ' is valid but with the following warnings = ' + JSON.stringify(warnings, 0, 2));
        }
      };
      /**
       * Internal method used to register remote templates coming over the wire.
       * @param {LYNX.Property.PropertyTemplate|object} in_remoteTemplate - The remote template to register
       * @param {string} in_scope - The scope in which the template will be stored in. The scope is usually determined by
       * the currently checked out workspaces. Each workspace can have their own set of versioned templates
       * that may be different from other workspaces.
       * @protected
       */
      PropertyFactory.prototype._registerRemoteTemplate = function (in_remoteTemplate, in_scope) {
        if (!(in_remoteTemplate instanceof PropertyTemplate)) {
          in_remoteTemplate = new PropertyTemplate(in_remoteTemplate);
        }
        var typeidWithoutVersion = in_remoteTemplate.getTypeidWithoutVersion();
        var version = in_remoteTemplate.getVersion();
        var typeid = in_remoteTemplate.typeid;
        if (this._localPrimitivePropertiesAndTemplates.has(typeid)) {
          // Template already exists. The incoming template MUST match what is registered.
          // If they do not match, throw an error letting the user know that the templates are incompatible.
          // This is likely due to the fact that the developer did not bump its version.
          var registeredTemplate = this._localPrimitivePropertiesAndTemplates.item(typeid);
          var templateValidator = new TemplateValidator({ skipSemver: true });
          var validationResults = templateValidator.validate(registeredTemplate.serialize(), in_remoteTemplate.serialize());
          if (!validationResults.isValid) {
            throw new Error(MSG.TEMPLATE_MISMATCH + typeid + '\n  errors = ' + JSON.stringify(_extractErrorMessage(validationResults.errors), 0, 2));
          }
        } else if (in_remoteTemplate.isVersioned()) {
          this._validateSemver(in_remoteTemplate);
          if (this._remoteScopedAndVersionedTemplates.has(in_scope)) {
            if (this._remoteScopedAndVersionedTemplates.item(in_scope).has(typeidWithoutVersion)) {
              if (!this._remoteScopedAndVersionedTemplates.item(in_scope).item(typeidWithoutVersion).has(version)) {
                this._remoteScopedAndVersionedTemplates.item(in_scope).item(typeidWithoutVersion).add(version, in_remoteTemplate);
              }
            } else {
              var versionCollection = _createVersionedSortedCollection();
              versionCollection.add(version, in_remoteTemplate);
              this._remoteScopedAndVersionedTemplates.item(in_scope).add(typeidWithoutVersion, versionCollection);
            }
          } else {
            var namespaceCollection = new Collection();
            var versionCollection = _createVersionedSortedCollection();
            namespaceCollection.add(typeidWithoutVersion, versionCollection);
            versionCollection.add(version, in_remoteTemplate);
            this._remoteScopedAndVersionedTemplates.add(in_scope, namespaceCollection);
          }
        } else {
          throw new Error(MSG.UNVERSIONED_REMOTE_TEMPLATE + ' \n' + JSON.stringify(in_remoteTemplate, 0, 2));
        }
      };
      /**
       * Remove the scope from the remote templates collection
       * @param {string} in_scope The scope to remove
       * @protected
       */
      PropertyFactory.prototype._removeScope = function (in_scope) {
        this._remoteScopedAndVersionedTemplates.remove(in_scope);
      };
      /**
       * Register a template or a primitive property
       *
       * This is the internal function used to register templates and primitive properties.
       *
       * @param {LYNX.Property.PropertyTemplate|string}                            in_typeid  -
       *     typeid of for the property the given template/constructor represents
       * @param {LYNX.Property.PropertyTemplate|object|LYNX.Property.BaseProperty} in_templateOrProperty
       *     Template/native property class to associate with the typeid
       * @param {string}                                                          [in_context='single'] -
       *     The context for which the parameter is added (if it is set to all the object will be used in
       *     all contexts)
       */
      PropertyFactory.prototype._registerTypeId = function (in_typeid, in_templateOrProperty, in_context) {
        // If the input is not yet a BaseProperty derived type or a
        // PropertyTemplate, we create a PropertyTemplate object for it
        if (!(in_templateOrProperty instanceof PropertyTemplate || this._isNativePropertyConstructor(in_templateOrProperty))) {
          in_templateOrProperty = new PropertyTemplate(in_templateOrProperty);
        }
        // If no context is specified we assign one
        if (!in_context) {
          // By default templates are registered for all contexts together, BaseProperties are registered separately
          in_context = in_templateOrProperty instanceof PropertyTemplate ? 'all' : 'single';
        }
        if (in_context !== 'all') {
          if (!this._localPrimitivePropertiesAndTemplates.has(in_typeid)) {
            this._localPrimitivePropertiesAndTemplates.add(in_typeid, new Collection());
          }
          this._localPrimitivePropertiesAndTemplates.item(in_typeid).add(in_context, in_templateOrProperty);
        } else if (!this._localPrimitivePropertiesAndTemplates.has(in_typeid)) {
          this._localPrimitivePropertiesAndTemplates.add(in_typeid, in_templateOrProperty);
        }
      };
      /**
       * Validate a template
       * Check that the template is syntactically correct as well as semantically correct.
       * @param {object|LYNX.Property.PropertyTemplate} in_template The template to check against
       * @return {object|undefined} map of key-value pairs
       *  where the path of the invalid property is the key and the value is the error message
       *  i.e.
       *  <pre>
       *    {
       *      'isValid': true or false,
       *      'typeid': 'The typeid of the object being parsed',
       *      'unresolvedTypes': [ 'An array', 'of strong typeids', 'that were found',
       *        'in the document', 'but not resolved from the local cache' ],
       *      'resolvedTypes': [ 'Array of', 'strong types resolved', 'during template parsing'],
       *      'errors': [ 'Array of', 'objects describing', 'syntax errors in the template' ]
       *      ...
       *    }
       *  </pre>
       */
      PropertyFactory.prototype.validate = function (in_template) {
        return this._templateValidator.validate(in_template);
      };
      /**
       * Get a template or property object based on a typeid and a context
       *
       * @param {string} in_typeid    - The type unique identifier
       * @param {string} [in_context]  - The context of the property to create
       * @param {string|undefined} in_scope - The scope in which the property typeid is defined
       *
       * @return {LYNX.Property.PropertyTemplate|object|LYNX.Property.BaseProperty|undefined}
       *     Template/Property identified by the typeid.
       */
      PropertyFactory.prototype._get = function (in_typeid, in_context, in_scope) {
        if (this._localPrimitivePropertiesAndTemplates.has(in_typeid)) {
          var typeidItem = this._localPrimitivePropertiesAndTemplates.item(in_typeid);
          if (!(typeidItem instanceof Collection)) {
            return typeidItem;
          } else {
            var context = in_context || 'single';
            return this._localPrimitivePropertiesAndTemplates.item(in_typeid).item(context);
          }
        } else if (in_scope && this._remoteScopedAndVersionedTemplates.has(in_scope)) {
          var splitTypeId = TypeIdHelper.extractVersion(in_typeid);
          if (splitTypeId.version) {
            var typeidWithoutVersion = splitTypeId.typeidWithoutVersion;
            var version = splitTypeId.version;
            if (this._remoteScopedAndVersionedTemplates.item(in_scope).has(typeidWithoutVersion) && this._remoteScopedAndVersionedTemplates.item(in_scope).item(typeidWithoutVersion).has(version)) {
              return this._remoteScopedAndVersionedTemplates.item(in_scope).item(typeidWithoutVersion).item(version);
            }
          }
        }
        return undefined;
      };
      /**
       * Get template based on typeid
       *
       * @param {string} in_typeid - The type unique identifier
       * @return {LYNX.Property.PropertyTemplate|undefined} Template identified by the typeid.
       */
      PropertyFactory.prototype.getTemplate = function (in_typeid) {
        if (this._localPrimitivePropertiesAndTemplates.has(in_typeid)) {
          return this._localPrimitivePropertiesAndTemplates.item(in_typeid);
        } else
          return undefined;
      };
      /**
       * Create an instance of the given property typeid if there is a template registered for it.
       * Otherwise, this method returns undefined.
       *
       * @param {string} in_typeid   - The type unique identifier
       * @param {string} in_context  - The type of collection of values that the property contains.
       *                               Accepted values are "single" (default), "array", "map" and "set".
       * @param {object=} in_initialProperties A set of initial values for the PropertySet being created
       * @return {LYNX.Property.BaseProperty|undefined} the property instance
       */
      PropertyFactory.prototype.create = function (in_typeid, in_context, in_initialProperties) {
        if (!in_context) {
          // If no context is supplied, we check whether the typeid contains a context
          if (!TypeIdHelper.isReferenceTypeId(in_typeid)) {
            var splitTypeId = TypeIdHelper.extractContext(in_typeid);
            in_typeid = splitTypeId.typeid;
            in_context = splitTypeId.context;
          }
        }
        var property = this._createFromPropertyDeclaration({
          typeid: in_typeid,
          context: in_context || 'single'
        });
        /* TODO: Warning, the semantics for defining initial values is undefined.
         Also not all cases have been implemented (LYNXDEV-645) */
        if (in_initialProperties !== undefined) {
          property._setValues(in_initialProperties);
        }
        return property;
      };
      /**
       * Create a property from a given scope
       * @param {string} in_typeid   - The type unique identifier
       * @param {string|undefined} in_scope - The scope in which the property typeid is defined
       * @return {LYNX.Property.BaseProperty|undefined} the property instance
       * @private
       */
      PropertyFactory.prototype._createProperty = function (in_typeid, in_scope) {
        var context;
        if (!TypeIdHelper.isReferenceTypeId(in_typeid)) {
          var splitTypeId = TypeIdHelper.extractContext(in_typeid);
          in_typeid = splitTypeId.typeid;
          context = splitTypeId.context;
        }
        return this._createFromPropertyDeclaration({
          typeid: in_typeid,
          context: context || 'single'
        }, undefined, in_scope);
      };
      /**
       * Creates a property object that serves as parent for the template with the given typeid, when none has yet
       * been created,
       *
       * @param {string}                               in_typeid - The type unique identifier
       * @param {string}                               in_id     - The id of the property to create
       * @param {LYNX.Property.BaseProperty|undefined} in_parent - The parent property object. If
       *                                                           it exists it will be returned
       * @param {LYNX.Property.PropertyTemplate|object|LYNX.Property.BaseProperty} in_templateOrConstructor -
       *        the Template/Property for this in_typeid
       * @param {string|undefined} in_scope - The scope in which the property typeid is defined
       *
       * @return {LYNX.Property.BaseProperty} The property that serves as parent for the properties in the template
       * @private
       */
      PropertyFactory.prototype._ensurePropertyParentExists = function (in_typeid, in_id, in_parent, in_templateOrConstructor, in_scope) {
        // If we already have a parent, we just return it
        if (in_parent) {
          return in_parent;
        }
        // Otherwise, we check the inheritance hierarchy to determine which internal property object we have to use
        var parents = {};
        this._getAllParentsForTemplateInternal(in_typeid, parents, undefined, in_scope);
        parents[in_typeid] = true;
        if (parents['NodeProperty'] && parents['NamedProperty']) {
          // We have a named node property
          return new NamedNodeProperty({
            typeid: in_typeid,
            id: in_id || null  // An id of NULL means that the GUID of the property is used
          });
        } else if (parents['NodeProperty']) {
          // We have a node property
          return new NodeProperty({
            typeid: in_typeid,
            id: in_id || null  // An id of NULL means that the GUID of the property is used
          });
        } else if (parents['NamedProperty']) {
          // We have a named property
          return new NamedProperty({
            typeid: in_typeid,
            id: in_id || null  // An id of NULL means that the GUID of the property is used
          });
        } else if (parents['RepositoryReferenceProperty']) {
          if (!PropertyFactory._enableExperimentalRepositoryReferences) {
            throw new Error('Repository references are not yet fully implemented and may not yet be used!');
          }
          // We have a repository reference property
          return new RepositoryReferenceProperty({
            typeid: in_typeid,
            id: in_id || null  // An id of NULL means that the GUID of the property is used
          });
        } else if (parents['Enum']) {
          return new EnumProperty({
            typeid: in_typeid,
            id: in_id || null,
            _enumDictionary: in_templateOrConstructor._enumDictionary
          });
        } else {
          // Otherwise we just use a simple base property
          return new BaseProperty({
            typeid: in_typeid,
            id: in_id
          });
        }
      };
      /**
       * Check whether a typeid is registered
       * @param {string} in_typeid The type unique identifier
       * @param {string|undefined} in_scope - The scope in which the property typeid is defined
       * @return {boolean} True if the typeid is registered. False otherwise.
       * @private
       */
      PropertyFactory.prototype._isRegisteredTypeid = function (in_typeid, in_scope) {
        return !!this._get(in_typeid, undefined, in_scope);
      };
      /**
       * Check whether the given typeid is a specialized constructor
       * Specialized constructors are of Array or Map types
       * @param {string} in_typeid The type unique identifier
       * @return {boolean} Returns true if the typeid is a specialized constructor
       * @private
       */
      PropertyFactory.prototype._isSpecializedConstructor = function (in_typeid) {
        return this._localPrimitivePropertiesAndTemplates.item(in_typeid) instanceof Collection;
      };
      /**
       * Create an instance of the given property from an entry in the properties list.
       *
       * @param {Object}                       in_propertiesEntry             - Describes the property object to create
       * @param {string=}                     [in_propertiesEntry.id]         - The name of the property
       * @param {string=}                     [in_propertiesEntry.typeid]     - The type identifier
       * @param {string=}                     [in_propertiesEntry.context]    - Context in which the property is created
       * @param {Object=}                     [in_propertiesEntry.properties] - Context in which the property is created
       * @param {number}                      [in_propertiesEntry.length]     - The length of an array property
       * @param {LYNX.Property.BaseProperty=}  in_parent                      - The parent property which will be used as
       *                                                                        the root to construct the property template
       * @param {string|undefined} in_scope - The scope in which the property typeid is defined
       *
       * @return {LYNX.Property.BaseProperty|undefined} the property instance
       */
      PropertyFactory.prototype._createFromPropertyDeclaration = function (in_propertiesEntry, in_parent, in_scope) {
        var typeid = in_propertiesEntry.typeid;
        var context = in_propertiesEntry.context !== undefined ? in_propertiesEntry.context : 'single';
        var parent = undefined;
        // We create a polymorphic collection (one inheriting from BaseProperty), if no typeid is specified
        // but a context is given
        if (in_propertiesEntry.typeid === undefined && in_propertiesEntry.context !== undefined && in_propertiesEntry.context !== 'single') {
          typeid = in_propertiesEntry.context !== 'set' ? 'BaseProperty' : 'NamedProperty';
        }
        if (in_propertiesEntry.typeid && TypeIdHelper.isReferenceTypeId(in_propertiesEntry.typeid)) {
          typeid = 'Reference';
        }
        if (typeid) {
          if (this._isRegisteredTypeid(typeid, in_scope)) {
            var templateOrConstructor = this._get(typeid, context, in_scope);
            var isSpecializedConstructor = this._isSpecializedConstructor(typeid);
            if (this._isNativePropertyConstructor(templateOrConstructor) && (isSpecializedConstructor || context === 'single')) {
              // If this is a primitive type, we create it via the registered constructor
              var result = new templateOrConstructor(in_propertiesEntry);
              // eslint-disable-line new-cap
              result._signalAllStaticMembersHaveBeenAdded(in_scope);
              return result;
            } else {
              if (context === 'single') {
                // If we have a template in a single context, we create it directly here
                // Create the base object
                parent = this._ensurePropertyParentExists(typeid, in_propertiesEntry.id, in_parent, templateOrConstructor, in_scope);
                // start from the inherited property
                if (templateOrConstructor.inherits) {
                  // deal with [ 'inherits' ] or 'inherits'
                  if (templateOrConstructor.inherits instanceof Array && templateOrConstructor.inherits.length > 0) {
                    for (var i = 0; i < templateOrConstructor.inherits.length; i++) {
                      this._createFromPropertyDeclaration({
                        typeid: templateOrConstructor.inherits[i],
                        context: 'single'
                      }, parent, in_scope);
                    }
                  } else if (_.isString(templateOrConstructor.inherits)) {
                    this._createFromPropertyDeclaration({
                      typeid: templateOrConstructor.inherits,
                      context: 'single'
                    }, parent, in_scope);
                  } else {
                    console.error('Error creating ' + typeid + '. Inherits must be an Array or a String');
                  }
                }
                this._parseTemplate(templateOrConstructor, parent, in_scope);
              } else {
                // If we have other contexts, we have to create the corresponding property object for that context
                // check if a specialized collection is neede
                var isEnum = this.inheritsFrom(typeid, 'Enum', { scope: in_scope });
                var result;
                switch (context) {
                case 'array':
                  if (isEnum) {
                    var enumPropertyEntry = deepCopy(in_propertiesEntry);
                    enumPropertyEntry._enumDictionary = templateOrConstructor._enumDictionary;
                    result = new EnumArrayProperty(enumPropertyEntry);
                  } else {
                    result = new ArrayProperty(in_propertiesEntry, templateOrConstructor, false, in_scope);
                  }
                  break;
                case 'set':
                  result = new SetProperty(in_propertiesEntry, templateOrConstructor);
                  break;
                case 'map':
                  result = new MapProperty(in_propertiesEntry, templateOrConstructor);
                  break;
                case 'enum':
                  var enumPropertyEntry = deepCopy(in_propertiesEntry);
                  enumPropertyEntry._enumDictionary = templateOrConstructor._enumDictionary;
                  result = new EnumProperty(enumPropertyEntry);
                  break;
                default:
                  throw new Error('Unknown context specified: ' + context);
                }
                result._signalAllStaticMembersHaveBeenAdded(in_scope);
                return result;
              }
            }
          } else {
            // We tried to create a property with an unknown typeid
            // that means we have no template and don't know what to instanciate
            // TODO: look for and use the missing template somehow at this point
            throw new Error('Unknown typeid specified: ' + typeid);
          }
        } else if (in_propertiesEntry.properties) {
          if (!parent) {
            // If this is a declaration which contains a properties list, we have to create a new base property for it
            parent = new BaseProperty(in_propertiesEntry);
          }
          // And then parse the entry like a template
          this._parseTemplate(in_propertiesEntry, parent, in_scope);
        } else {
          throw new Error('Invalid syntax in properties entry. It must either have a typeid or a properties entry.' + JSON.stringify(in_propertiesEntry));
        }
        // If this property inherits from NamedProperty we assign a random GUID
        if (parent instanceof NamedProperty || parent instanceof NamedNodeProperty) {
          parent.get('guid').value = generateGUID();
        }
        // We only signal that we have finished the processing, once
        // we have processed all templates that this template inherits from
        // Which means, that the function has been called without a parent
        // as parameter
        if (in_parent === undefined) {
          parent._signalAllStaticMembersHaveBeenAdded(in_scope);
        }
        return parent;
      };
      /**
       * Method used to determine whether the given object is a property constructor
       *
       * @param {Object} in_obj Object to check.
       * @return {boolean} True if the object is a BaseProperty.
       * @private
       */
      PropertyFactory.prototype._isNativePropertyConstructor = function (in_obj) {
        // TODO: This tests seems dangerous. I think it is based on the assumption that constructor is not
        //       overwritten in the derived classes (which it probably should be)
        return in_obj.constructor && in_obj.constructor === BaseProperty.constructor;
      };
      /**
       * Parse a given property template appending its property objects to the
       * to the given property parent object
       *
       * @param {LYNX.Property.PropertyTemplate} in_template - template for the property
       * @param {LYNX.Property.BaseProperty}     in_parent   - the parent
       * @param {string|undefined} in_scope - The scope in which in_template is defined in
       * @private
       */
      PropertyFactory.prototype._parseTemplate = function (in_template, in_parent, in_scope) {
        // Check if there are nested property arrays
        if (in_template.properties) {
          var properties = in_template.properties;
          if (in_template.inherits && in_template.inherits[0] && in_template.inherits[0] === 'Enum') {
          } else {
            for (var i = 0; i < properties.length; i++) {
              var property = this._createFromPropertyDeclaration(properties[i], undefined, in_scope);
              in_parent._append(property);
            }
          }
        }
      };
      /**
       * Checks whether the template with typeid in_templateTypeid inherits from the template in in_baseTypeid
       *
       * Note: By default, this also returns true if in_templateTypeid === in_baseTypeid, since in most use cases
       *       the user wants to check whether a given template has all members as another template and so this is
       *       true for the template itself
       *
       * @param {string}  in_templateTypeid     - Template for which we want to check, whether in_baseTypeid is a parent
       * @param {string}  in_baseTypeid         - The base template to check for
       * @param {object} [in_options]          - Additional options
       * @param {boolean} [in_options.includeSelf=true] - Also return true if in_templateTypeid === in_baseTypeid
       *
       * @return {boolean} true if in_baseTypeid is a parent of in_templateTypeid or
       *                   if (in_includeSelf == true and in_templateTypeid == in_baseTypeid)
       */
      /* @param {string} [in_options.scope]    - The scope in which the property typeid is defined */
      PropertyFactory.prototype.inheritsFrom = function (in_templateTypeid, in_baseTypeid, in_options) {
        in_options = in_options || {};
        if (in_templateTypeid === in_baseTypeid && (!!in_options.includeSelf || in_options.includeSelf === undefined)) {
          return true;
        }
        var parents = {};
        this._getAllParentsForTemplateInternal(in_templateTypeid, parents, true, in_options.scope);
        return parents[in_baseTypeid] !== undefined;
      };
      /**
       * Returns all the typeids the template inherits from (including all possible paths through multiple inheritance).
       * The order of the elements in the array is unspecified.
       *
       * @param {string} in_typeid - typeid of the template
       * @param {object} [in_options] - Additional options
       * @param {boolean} [in_options.includeBaseProperty=false] - Include BaseProperty as parent.
       *                                                   Everything implicitly inherits
       *                                                   from BaseProperty, but it is not explicitly listed in the
       *                                                   template, so it is only included if explicitly requested
       *
       * @return {Array.<string>} typeids of all inherited types (in unspecified order)
       */
      /* @param {string|undefined}  [in_options.scope] - The scope in which the template was stored. */
      PropertyFactory.prototype.getAllParentsForTemplate = function (in_typeid, in_options) {
        in_options = in_options || {};
        // We just forward the request to the internal function
        var parents = {};
        this._getAllParentsForTemplateInternal(in_typeid, parents, !!in_options.includeBaseProperty, in_options.scope);
        return _.keys(parents);
      };
      /**
       * Returns all the typeids the template inherits from (including all possible paths through multiple inheritance).
       *
       * @param {string}  in_typeid              - typeid of the template
       * @param {Object}  out_parents            - map containing the parents
       * @param {Boolean} in_includeBaseProperty - Include BaseProperty as parent. Everything implicitly inherits
       *                                           from BaseProperty, but it is not explicitly listed in the
       *                                           template, so it is only be included if explicitly requested
       * @param {string|undefined} in_scope - The scope in which the property typeid is defined
       */
      PropertyFactory.prototype._getAllParentsForTemplateInternal = function (in_typeid, out_parents, in_includeBaseProperty, in_scope) {
        if (TypeIdHelper.isPrimitiveType(in_typeid)) {
          // Everything inherits from BaseProperty.
          if (in_includeBaseProperty) {
            out_parents['BaseProperty'] = true;
          }
          return;
        }
        var template = this._get(in_typeid, undefined, in_scope);
        if (!template) {
          throw new Error('Non existing typeid: ' + in_typeid);
        }
        // Everything inherits from BaseProperty.
        if (in_includeBaseProperty) {
          out_parents['BaseProperty'] = true;
        }
        // Run over all parents and insert them into the parents array
        if (template.inherits) {
          // We have to distinguish the cases where the parents are either specified as a single string or an array
          var parents = _.isArray(template.inherits) ? template.inherits : [template.inherits];
          for (var i = 0; i < parents.length; i++) {
            // Mark it as parent
            out_parents[parents[i]] = true;
            // Continue recursively
            this._getAllParentsForTemplateInternal(parents[i], out_parents, undefined, in_scope);
          }
        }
      };
      /**
       * Internal function used to clear and reinitialize the PropertyFactory
       * @private
       */
      PropertyFactory.prototype._clear = function () {
        this._localPrimitivePropertiesAndTemplates = new Collection();
        this._localVersionedTemplates = new Collection();
        this._remoteScopedAndVersionedTemplates = new Collection();
        this._init();
      };
      /**
       * Reregisters a template (by overwriting the existing template).
       *
       * This should NEVER be necessary in the final application, but it might be helpful during interactive debugging
       * sessions, when trying out different templates.
       *
       * @protected
       * @param {LYNX.Property.PropertyTemplate|object|LYNX.Property.BaseProperty} in_template - The template to reregister
       */
      PropertyFactory.prototype._reregister = function (in_template) {
        var typeid = in_template.typeid;
        if (!(in_template instanceof PropertyTemplate)) {
          in_template = new PropertyTemplate(in_template);
        }
        var typeidWithoutVersion = in_template.getTypeidWithoutVersion();
        var version = in_template.getVersion();
        // Remove the existing entry
        this._localPrimitivePropertiesAndTemplates.remove(typeid);
        if (this._localVersionedTemplates.has(typeidWithoutVersion)) {
          if (this._localVersionedTemplates.item(typeidWithoutVersion).has(version)) {
            this._localVersionedTemplates.item(typeidWithoutVersion).remove(version);
          }
        }
        // Invalidate the cache of static children per typeid
        NodeProperty._cleanStaticChildrenCache();
        // And repeat the registration
        this.register(in_template);
      };
      var PropertyFactory = new PropertyFactory();
      exportModule('LYNX.Property.PropertyFactory', PropertyFactory);
    }());
    (function () {
      var ChangeSet = include('LYNX.Property.ChangeSet');
      var TypeIdHelper = include('LYNX.Property.TypeIdHelper');
      var joinPaths = include('LYNX.Utils.Strings.joinPaths');
      var PROPERTY_PATH_DELIMITER = include('LYNX.Property._constants.PROPERTY_PATH_DELIMITER');
      var PathHelper = include('LYNX.Property.PathHelper');
      var _ = include('underscore');
      var deepCopy = include('LYNX.Utils.deepCopy');
      var MSG = { EXISTING_ENTRY_INSERTED: 'Added an already existing entry: ' };
      /**
       * @namespace LYNX.Property.ChangeSetOperations.IndexedCollectionOperations
       * @alias LYNX.Property.ChangeSetOperations.IndexedCollectionOperations
       * Helper functions and classes to perform operations on ChangeSets with indexed collections (sets and maps)
       */
      /**
       * Checks whether an object is empty (has no keys)
       * This function should be a bit faster than the _.isEmpty from
       * underscore. Unfortunately, at least on Chrome, it is still in
       * O(n)
       *
       * @param {Object} in_object  - The object to check
       * @returns {boolean} Is it empty?
       * @private
       */
      var _fastIsEmptyObject = function (in_object) {
        if (!in_object || _.isArray(in_object) || !_.isObject(in_object)) {
          return _.isEmpty(in_object);
        }
        for (var key in in_object) {
          // eslint-disable-line guard-for-in
          return false;
        }
        return true;
      };
      var ChangeSetIndexedCollectionFunctions = {
        /**
         * Applies a ChangeSet to a given indexed collection property (recursively). The ChangeSet is assumed to be relative
         * to the same property root and it will be applied behind the base ChangeSet (assuming that the changes are
         * relative to the state after the base ChangeSet has been applied. It will change the base ChangeSet.
         *
         * @param {LYNX.Property.SerializedChangeSet} io_basePropertyChanges    - The ChangeSet describing the initial state
         * @param {LYNX.Property.SerializedChangeSet} in_appliedPropertyChanges - The ChangeSet to apply to this state
         * @param {string}                            in_typeid                 - The typeid of the contents collection
         *                                                                        (without the collection type)
         *
         * @private
         */
        _performApplyAfterOnPropertyIndexedCollection: function (io_basePropertyChanges, in_appliedPropertyChanges, in_typeid) {
          var isPrimitiveType = TypeIdHelper.isPrimitiveType(in_typeid);
          // Handle remove entry operations
          if (in_appliedPropertyChanges.remove) {
            // Get and initialize the corresponding entries in the existing collection
            var removedEntries = in_appliedPropertyChanges.remove;
            io_basePropertyChanges = io_basePropertyChanges || {};
            io_basePropertyChanges.remove = io_basePropertyChanges.remove || [];
            var baseInserted = io_basePropertyChanges.insert || {};
            var baseRemoved = io_basePropertyChanges.remove;
            var baseModified = io_basePropertyChanges.modify;
            var done = false;
            if (!_.isArray(removedEntries)) {
              if (isPrimitiveType) {
                removedEntries = Object.keys(removedEntries);
              } else {
                // this is a reversible change set of templated types
                var removedTypes = Object.keys(removedEntries);
                for (var t = 0; t < removedTypes.length; t++) {
                  var removedKeys = Object.keys(removedEntries[removedTypes[t]]);
                  for (var i = 0; i < removedKeys.length; i++) {
                    if (baseInserted[removedTypes[t]] && baseInserted[removedTypes[t]][removedKeys[i]] !== undefined) {
                      delete baseInserted[removedTypes[t]][removedKeys[i]];
                      // If all entries for a typeid have been removed, we can remove
                      // the whole typeid from the inserted section
                      if (baseInserted && _.isEmpty(baseInserted[removedTypes[t]])) {
                        delete baseInserted[removedTypes[t]];
                      }
                    } else {
                      if (baseModified && baseModified[removedTypes[t]] && baseModified[removedTypes[t]][removedKeys[i]] !== undefined) {
                        delete baseModified[removedTypes[t]][removedKeys[i]];
                        // If all entries for a typeid have been removed, we can remove
                        // the whole typeid from the inserted section
                        if (baseModified && _.isEmpty(baseModified[removedTypes[t]])) {
                          delete baseModified[removedTypes[t]];
                        }
                      }
                      if (_.isArray(baseRemoved)) {
                        baseRemoved.push(removedKeys[i]);
                      } else {
                        if (!baseRemoved[removedTypes[t]]) {
                          baseRemoved[removedTypes[t]] = {};
                        }
                        baseRemoved[removedTypes[t]][removedKeys[i]] = removedEntries[removedTypes[t]][removedKeys[i]];
                      }
                    }
                  }
                }
                done = true;
              }
            }
            if (!done) {
              if (isPrimitiveType) {
                for (var i = 0; i < removedEntries.length; i++) {
                  var key = removedEntries[i];
                  // If there is an insert for this key, we just remove it
                  if (baseInserted[key] !== undefined) {
                    delete baseInserted[key];
                  } else {
                    // There could be a modify entry for this key, which we have to remove
                    if (baseModified && baseModified[key] !== undefined) {
                      delete baseModified[key];
                    }
                    // Otherwise we add it to the remove list
                    baseRemoved.push(key);
                  }
                }
              } else {
                var baseInsertedTypeids = _.keys(baseInserted);
                for (var i = 0; i < removedEntries.length; i++) {
                  var key = removedEntries[i];
                  var foundInTypeid = undefined;
                  // Since we only have a flat remove list (without typeid) in the changeset, we have
                  // to check all inserts
                  for (var j = 0; j < baseInsertedTypeids.length; j++) {
                    if (baseInserted[baseInsertedTypeids[j]] && baseInserted[baseInsertedTypeids[j]][key] !== undefined) {
                      foundInTypeid = baseInsertedTypeids[j];
                      break;
                    }
                  }
                  if (foundInTypeid) {
                    // If this key was inserted by this ChangeSet, we just remove it from the inserted list
                    delete baseInserted[foundInTypeid][key];
                    // If all entries for a typeid have been removed, we can remove
                    // the whole typeid from the inserted or modified section
                    if (baseInserted && _.isEmpty(baseInserted[foundInTypeid])) {
                      delete baseInserted[foundInTypeid];
                    }
                    if (baseModified && _.isEmpty(baseModified[foundInTypeid])) {
                      delete baseModified[foundInTypeid];
                    }
                  } else {
                    // There could be a modify entry for this key, which we have to remove
                    var baseModifiedTypeids = _.keys(baseModified);
                    for (var j = 0; j < baseModifiedTypeids.length; j++) {
                      if (baseModified[baseModifiedTypeids[j]][key]) {
                        foundInTypeid = baseModifiedTypeids[j];
                        delete baseModified[foundInTypeid][key];
                        break;
                      }
                    }
                    // Otherwise we add it to the remove list
                    baseRemoved.push(key);
                  }
                }
              }
            }
          }
          // Apply insert operations
          if (in_appliedPropertyChanges.insert) {
            // Get and initialize the corresponding entries from the existing collection
            var insertedEntries = in_appliedPropertyChanges.insert;
            io_basePropertyChanges = io_basePropertyChanges || {};
            io_basePropertyChanges.insert = io_basePropertyChanges.insert || {};
            var baseInserted = io_basePropertyChanges.insert;
            var baseRemoved = io_basePropertyChanges.remove;
            // Insert the inserted entries
            // If no typeids are included, we just use a placeholder for the iteration below
            var insertedTypeids = isPrimitiveType ? [undefined] : _.keys(insertedEntries);
            for (var i = 0; i < insertedTypeids.length; i++) {
              var typeid = insertedTypeids[i];
              var insertedKeys = _.keys(isPrimitiveType ? insertedEntries : insertedEntries[typeid]);
              for (var j = 0; j < insertedKeys.length; j++) {
                var key = insertedKeys[j];
                if (isPrimitiveType && baseRemoved && (_.isArray(baseRemoved) && _.contains(baseRemoved, key) || baseRemoved[key] !== undefined)) {
                  // A remove and insert are combined into a modify for primitive types
                  // Remove the old remove command
                  if (_.isArray(io_basePropertyChanges.remove)) {
                    io_basePropertyChanges.remove = _.without(io_basePropertyChanges.remove, key);
                  } else {
                    delete io_basePropertyChanges.remove[key];
                  }
                  // Insert a modify command instead
                  io_basePropertyChanges.modify = io_basePropertyChanges.modify || {};
                  io_basePropertyChanges.modify[key] = insertedEntries[key];
                } else if (!baseInserted[typeid] || !baseInserted[typeid][key]) {
                  if (isPrimitiveType) {
                    baseInserted[key] = insertedEntries[key];
                  } else {
                    baseInserted[typeid] = baseInserted[typeid] || {};
                    baseInserted[typeid][key] = deepCopy(insertedEntries[typeid][key]);
                  }
                } else {
                  throw new Error(MSG.EXISTING_ENTRY_INSERTED + key);
                }
              }
            }
          }
          // Handle modification operations
          if (in_appliedPropertyChanges.modify) {
            // Get and initialize the corresponding entries from the existing collection
            var modifiedEntries = in_appliedPropertyChanges.modify;
            io_basePropertyChanges = io_basePropertyChanges || {};
            io_basePropertyChanges.modify = io_basePropertyChanges.modify || {};
            var baseModified = io_basePropertyChanges.modify;
            var baseInserted = io_basePropertyChanges.insert || {};
            // Process the modifications
            // If no typeids are included, we just use a placeholder for the iteration below
            var modifiedTypeids = isPrimitiveType ? [undefined] : _.keys(modifiedEntries);
            for (var i = 0; i < modifiedTypeids.length; i++) {
              var typeid = modifiedTypeids[i];
              var modifyKeys = _.keys(isPrimitiveType ? modifiedEntries : modifiedEntries[typeid]);
              for (var j = 0; j < modifyKeys.length; j++) {
                var key = modifyKeys[j];
                if (isPrimitiveType) {
                  var newValue = modifiedEntries[key];
                  if (newValue.hasOwnProperty('value')) {
                    newValue = newValue.value;
                  }
                  if (baseInserted[key] !== undefined) {
                    // If this entry was added by this ChangeSet, we modify the insert operation according to the
                    // new ChangeSet
                    baseInserted[key] = newValue;
                  } else {
                    if (baseModified[key] && baseModified[key].hasOwnProperty('value')) {
                      baseModified[key].value = newValue;
                    } else {
                      baseModified[key] = newValue;
                    }
                  }
                } else {
                  // If this is a polymorphic collection, we can still have individual entries with
                  // primitive types
                  var isEntryPrimitiveType = TypeIdHelper.isPrimitiveType(typeid);
                  if (baseInserted[typeid] && baseInserted[typeid][key] !== undefined) {
                    // If this entry was added by this ChangeSet, we modify the insert operation according to the
                    // new ChangeSet
                    if (isEntryPrimitiveType && typeid !== 'String') {
                      var newValue = modifiedEntries[typeid][key];
                      if (modifiedEntries[typeid][key].hasOwnProperty('value')) {
                        newValue = modifiedEntries[typeid][key].value;
                      }
                      if (baseInserted[typeid][key].hasOwnProperty('value')) {
                        baseInserted[typeid][key].oldValue = baseInserted[typeid][key].value;
                        baseInserted[typeid][key].value = newValue;
                      } else {
                        baseInserted[typeid][key] = newValue;
                      }
                    } else {
                      this._performApplyAfterOnPropertyWithTypeid(key, baseInserted[typeid], modifiedEntries[typeid], typeid, false);
                    }
                  } else if (baseModified[typeid] && baseModified[typeid][key] !== undefined) {
                    // If there was a previous modification operation, we have to merge the two
                    if (isEntryPrimitiveType && typeid !== 'String') {
                      // Primitive types can simply be overwritten, however we have an exception for
                      // 64 bit integers (until javascript natively supports them)
                      if (typeid === 'Int64' || typeid === 'Uint64') {
                        var appliedVal = modifiedEntries[typeid][key];
                        if (appliedVal.hasOwnProperty('value')) {
                          appliedVal = appliedVal.value;
                        }
                        baseModified[typeid][key] = appliedVal.slice();
                      } else {
                        baseModified[typeid][key] = modifiedEntries[typeid][key];
                      }
                    } else {
                      this._performApplyAfterOnPropertyWithTypeid(key, baseModified[typeid], modifiedEntries[typeid], typeid, true);
                    }
                  } else {
                    baseModified[typeid] = baseModified[typeid] || {};
                    baseModified[typeid][key] = deepCopy(modifiedEntries[typeid][key]);
                  }
                }
              }
            }
          }
          // Remove unnecessary entries from the ChangeSet
          this._cleanIndexedCollectionChangeSet(io_basePropertyChanges, !isPrimitiveType);
        },
        /**
         * Performs the rebase operation for set and map collections
         *
         * @param {LYNX.Property.SerializedChangeSet} in_ownPropertyChangeSet -
         *     The ChangeSet for this collection
         * @param {LYNX.Property.SerializedChangeSet} io_rebasePropertyChangeSet -
         *     The ChangeSet for the collection to be rebased
         * @param {string} in_basePath -
         *     Base path to get to the property processed by this function
         * @param {string} in_typeid -
         *     The typeid of the contents collection (without the collection type)
         * @param {boolean} in_useSquareBracketsInPath -
         *     If set to true, paths will be created using the angular brackets syntax (for
         *     arrays), otherwise dots will be used (for NodeProperties)
         * @param {Array.<LYNX.Property.ChangeSet.ConflictInfo>} out_conflicts -
         *     A list of paths that resulted in conflicts together with the type of the conflict
         *
         * @private
         */
        _rebaseIndexedCollectionChangeSetForProperty: function (in_ownPropertyChangeSet, io_rebasePropertyChangeSet, in_basePath, in_typeid, in_useSquareBracketsInPath, out_conflicts) {
          var isPrimitiveType = TypeIdHelper.isPrimitiveType(in_typeid);
          var changesByKeys = {};
          // Helper function which stores the changes indexed by key in the changesByKeys array to
          // make it easier to compare the related changes in the two ChangeSets
          var addChanges = function (in_collection, in_changeIdentifier, in_changePrefix, in_typeidChange) {
            // Collection didn't exist in this ChangeSet
            if (in_collection === undefined) {
              return;
            }
            // For remove operations, the ChangeSet is only an array of keys, otherwise it is a map, so we have to
            // distinguish the two cases here
            var keys = _.isArray(in_collection) ? in_collection : _.keys(in_collection);
            // Add all entries indexed with the key
            for (var j = 0; j < keys.length; j++) {
              var key = keys[j];
              // Store the type of the change
              changesByKeys[key] = changesByKeys[key] || {};
              changesByKeys[key][in_changePrefix] = changesByKeys[key][in_changePrefix] ? changesByKeys[key][in_changePrefix] + '_' + in_changeIdentifier : in_changeIdentifier;
              // If applicable store the typeid of the change
              if (in_typeidChange) {
                changesByKeys[key][in_changePrefix + 'Typeid'] = in_typeidChange;
              }
              // Store the ChangeSet
              if (in_changePrefix === 'other') {
                if (!_.isArray(in_collection)) {
                  changesByKeys[key].change = in_collection[key];
                } else {
                  changesByKeys[key].change = key;
                }
              }
            }
          };
          // Helper function which adds the Changes for a ChangeSet that is ordered by typeid
          var addChangesWithTypeids = function (in_collection, in_changeIdentifier, in_changePrefix) {
            if (in_collection === undefined) {
              return;
            }
            // Iterate over the typeids (or use dummy entry for the iteration
            var addedKeyTypeids = _.keys(in_collection);
            for (var i = 0; i < addedKeyTypeids.length; i++) {
              var Typeid = addedKeyTypeids[i];
              addChanges(in_collection[Typeid], in_changeIdentifier, in_changePrefix, Typeid);
            }
          };
          // Insert all changes from the ChangeSet into the lookup map
          if (_.isArray(in_ownPropertyChangeSet.remove)) {
            addChanges(in_ownPropertyChangeSet.remove, 'remove', 'own');
          } else {
            if (isPrimitiveType) {
              addChanges(in_ownPropertyChangeSet.remove, 'remove', 'own');
            } else {
              addChangesWithTypeids(in_ownPropertyChangeSet.remove, 'remove', 'own');
            }
          }
          if (_.isArray(io_rebasePropertyChangeSet.remove)) {
            addChanges(io_rebasePropertyChangeSet.remove, 'remove', 'other');
          } else {
            if (isPrimitiveType) {
              addChanges(io_rebasePropertyChangeSet.remove, 'remove', 'other');
            } else {
              addChangesWithTypeids(io_rebasePropertyChangeSet.remove, 'remove', 'other');
            }
          }
          if (isPrimitiveType) {
            addChanges(in_ownPropertyChangeSet.insert, 'insert', 'own');
            addChanges(in_ownPropertyChangeSet.modify, 'modify', 'own');
            addChanges(io_rebasePropertyChangeSet.insert, 'insert', 'other');
            addChanges(io_rebasePropertyChangeSet.modify, 'modify', 'other');
          } else {
            addChangesWithTypeids(in_ownPropertyChangeSet.insert, 'insert', 'own');
            addChangesWithTypeids(in_ownPropertyChangeSet.modify, 'modify', 'own');
            addChangesWithTypeids(io_rebasePropertyChangeSet.insert, 'insert', 'other');
            addChangesWithTypeids(io_rebasePropertyChangeSet.modify, 'modify', 'other');
          }
          // Check for modifications that affect the same object
          var changedKeys = _.keys(changesByKeys);
          for (var i = 0; i < changedKeys.length; i++) {
            var key = changedKeys[i];
            var newPath = in_useSquareBracketsInPath ? in_basePath + '[' + PathHelper.quotePathSegmentIfNeeded(key) + ']' : joinPaths(in_basePath, PathHelper.quotePathSegmentIfNeeded(key), PROPERTY_PATH_DELIMITER);
            var modification = changesByKeys[key];
            if (modification.own && modification.other) {
              // We found a key that was changed by both ChangeSets at the same time
              // We now have to handle the conflicting changes. The changes we do, are summarized in this table:
              //
              // -------|-----------------+------------------+------------------+-------------------------------------|
              //    \Own|    insert       |       modify     |     remove       |    remove+insert                    |
              //     \  |                 |                  |                  |                                     |
              // other\ |                 |                  |                  |                                     |
              // ------\|-----------------+------------------+------------------+-------------------------------------|
              //        | conflicting     | incompatible     | incompatible     | incompatible                        |
              // insert | inserts         | psets            | psets            | psets                               |
              //        |                 |                  |                  |                                     |
              // -------|-----------------+------------------+------------------+-------------------------------------|
              //        | incompatible    | merge recursively| conflict         | conflict                            |
              // modify | psets           |                  | delete modify    | modify can not be applied due to    |
              //        |                 |                  | in other         | to incompatible base                |
              //        |                 |                  |                  | - delete modify in other            |
              // -------|-----------------+------------------+------------------+-------------------------------------|
              //        | incompatible    | non-conflicting  | non-conflicting  | incompatible                        |
              // remove | psets           | change           | change           | psets                               |
              //        |                 |                  | -rem dupl. remove|                                     |
              // -------|-----------------+------------------+------------------+-------------------------------------|
              //        | incompatible    | non-conflicting  | non-conflicting  | non-conflicting                     |
              // remove+| psets           | change           | change           | change                              |
              // insert |                 |                  |                  |                                     |
              //        |                 |                  | -rem dupl. remove|                                     |
              // -------|-----------------+------------------+------------------+-------------------------------------|
              // A key was modified after it had been removed
              if (modification.own === 'modify' && modification.other === 'modify') {
                if (isPrimitiveType || TypeIdHelper.isPrimitiveType(modification.ownTypeid) && modification.ownTypeid !== 'String') {
                  // We have two modification operations that affect the same entry for a base type.
                  // This is a legal operation, the second one will overwrite the first one, but we
                  // report it as a possible conflict
                  var modifyMap = modification.otherTypeid ? in_ownPropertyChangeSet.modify[modification.otherTypeid] : in_ownPropertyChangeSet.modify;
                  var conflict = {
                    path: newPath,
                    type: ChangeSet.ConflictType.COLLIDING_SET,
                    conflictingChange: modifyMap[key]
                  };
                  out_conflicts.push(conflict);
                } else {
                  this._rebaseChangeSetForPropertyEntryWithTypeid(key, in_ownPropertyChangeSet.modify[modification.ownTypeid], io_rebasePropertyChangeSet.modify[modification.otherTypeid], modification.ownTypeid, newPath, true, out_conflicts);
                }
              } else if (modification.own === 'remove' && modification.other === 'modify') {
                var modifyMap = modification.otherTypeid ? io_rebasePropertyChangeSet.modify[modification.otherTypeid] : io_rebasePropertyChangeSet.modify;
                // Create the conflict information
                var conflict = {
                  path: newPath,
                  type: ChangeSet.ConflictType.ENTRY_MODIFIED_AFTER_REMOVE,
                  conflictingChange: modifyMap[key]
                };
                out_conflicts.push(conflict);
                // Delete the modification from the rebased ChangeSet
                delete modifyMap[key];
              } else if (modification.own === 'remove_insert' && modification.other === 'modify') {
                // We have a conflicting change. A node was removed and inserted (replaced) in the original
                // ChangeSet and then modified by the rebased ChangeSet. Since the base of the modification
                // can have been changed significantly by this operation, we don't know whether we can
                // apply the modification
                // Create the conflict information
                var conflict = {
                  path: newPath,
                  type: ChangeSet.ConflictType.ENTRY_MODIFICATION_AFTER_REMOVE_INSERT,
                  conflictingChange: io_rebasePropertyChangeSet.modify[modification.otherTypeid][key]
                };
                out_conflicts.push(conflict);
                // Delete the modification from the rebased ChangeSet
                delete io_rebasePropertyChangeSet.modify[key];
              } else if ((modification.own === 'modify' || modification.own === 'remove') && (modification.other === 'remove' || modification.other === 'remove_insert')) {
                if (modification.own === 'modify') {
                  var modifyMap = modification.ownTypeid ? in_ownPropertyChangeSet.modify[modification.ownTypeid] : in_ownPropertyChangeSet.modify;
                  // Create the conflict information
                  var conflict = {
                    path: newPath,
                    type: ChangeSet.ConflictType.REMOVE_AFTER_MODIFY,
                    conflictingChange: modifyMap[key]
                  };
                  out_conflicts.push(conflict);
                }
                // If we have a duplicated delete, we remove it from the new ChangeSet
                if (modification.own === 'remove') {
                  if (_.isArray(io_rebasePropertyChangeSet.remove)) {
                    io_rebasePropertyChangeSet.remove = _.without(io_rebasePropertyChangeSet.remove, key);
                  } else {
                    if (isPrimitiveType) {
                      delete io_rebasePropertyChangeSet.remove[key];
                    } else {
                      delete io_rebasePropertyChangeSet.remove[modification.otherTypeid][key];
                    }
                  }
                }
              } else if (modification.own === 'insert' && modification.other === 'insert') {
                if (isPrimitiveType || TypeIdHelper.isPrimitiveType(modification.ownTypeid)) {
                  var insertMap = modification.otherTypeid ? io_rebasePropertyChangeSet.insert[modification.otherTypeid] : io_rebasePropertyChangeSet.insert;
                  // We have two insert operations that affect the same key for a primitive type.
                  // This is a legal operation, the second one will overwrite the first one, but we
                  // report it as a possible conflicting set
                  var conflict = {
                    path: newPath,
                    type: ChangeSet.ConflictType.COLLIDING_SET,
                    conflictingChange: insertMap[key]
                  };
                  out_conflicts.push(conflict);
                  // Convert to modify
                  if (modification.otherTypeid) {
                    io_rebasePropertyChangeSet.modify = io_rebasePropertyChangeSet.modify || {};
                    io_rebasePropertyChangeSet.modify[modification.otherTypeid] = io_rebasePropertyChangeSet.modify[modification.otherTypeid] || {};
                    modifyMap = io_rebasePropertyChangeSet.modify[modification.otherTypeid];
                  } else {
                    io_rebasePropertyChangeSet.modify = io_rebasePropertyChangeSet.modify || {};
                    modifyMap = io_rebasePropertyChangeSet.modify;
                  }
                  modifyMap[key] = insertMap[key];
                  delete insertMap[key];
                } else {
                  // Here we have two insert operations for objects. Since these affect a whole sub-tree and not
                  // just a single value, we cannot easily convert it into a modify and instead report it as invalid
                  var insertMap = modification.otherTypeid ? io_rebasePropertyChangeSet.insert[modification.otherTypeid] : io_rebasePropertyChangeSet.insert;
                  // Create the conflict information
                  var conflict = {
                    path: newPath,
                    type: ChangeSet.ConflictType.INSERTED_ENTRY_WITH_SAME_KEY,
                    conflictingChange: insertMap[key]
                  };
                  out_conflicts.push(conflict);
                  // Delete the modification from the rebased ChangeSet
                  delete insertMap[key];
                }
              } else {
                // All other operations are conflicting changes, which only occur for ChangeSets that are relative
                // to different bases
                // Create the conflict information
                var conflict = {
                  path: newPath,
                  type: ChangeSet.ConflictType.INVALID_CHANGESET_BASE,
                  conflictingChange: modification.change
                };
                out_conflicts.push(conflict);
                // Remove the change from the ChangeSet
                if (modification.other !== 'remove') {
                  if (modification.otherTypeid !== undefined) {
                    delete io_rebasePropertyChangeSet[modification.other][modification.otherTypeid][key];
                  } else {
                    delete io_rebasePropertyChangeSet[modification.other][key];
                  }
                } else {
                  // Remove remove operations from the ChangeSet
                  if (_.isArray(io_rebasePropertyChangeSet[modification.other])) {
                    io_rebasePropertyChangeSet[modification.other] = _.without(io_rebasePropertyChangeSet[modification.other], key);
                  } else {
                    delete io_rebasePropertyChangeSet[modification.other][key];
                  }
                }
                console.error('Rebase operation with conflicting ChangeSets. Probably incorrect bases.');
              }
            }
          }
          // Remove unnecessary entries from the ChangeSet
          this._cleanIndexedCollectionChangeSet(io_rebasePropertyChangeSet, !isPrimitiveType);
        },
        /**
         * Removes empty entries from the .children collection of the ChangeSet
         *
         * @param {LYNX.Property.SerializedChangeSet} in_propertyChanges - The ChangeSet to clean up
         * @param {boolean}                           in_containsTypeids - Does this ChangeSet contain typeids
         * @private
         */
        _cleanIndexedCollectionChangeSet: function (in_propertyChanges, in_containsTypeids) {
          var changes = in_propertyChanges;
          // Clean inserts
          // First remove unused typeid sections
          if (in_containsTypeids) {
            var typeidList = _.keys(changes['insert']);
            for (var j = 0; j < typeidList.length; j++) {
              if (_fastIsEmptyObject(changes['insert'][typeidList[j]])) {
                delete changes['insert'][typeidList[j]];
              }
            }
          }
          // Remove add group if no operations are present
          if (_fastIsEmptyObject(changes.insert)) {
            delete changes['insert'];
          }
          // First remove unused typeid sections
          if (in_containsTypeids) {
            var typeidList = _.keys(changes['remove']);
            for (var j = 0; j < typeidList.length; j++) {
              if (_fastIsEmptyObject(changes['remove'][typeidList[j]])) {
                delete changes['remove'][typeidList[j]];
              }
            }
          }
          // Remove remove group if no operations are present
          if (_fastIsEmptyObject(changes.remove)) {
            delete changes['remove'];
          }
          // Clean modifies
          // First remove unused typeid sections
          if (in_containsTypeids) {
            var typeidList = _.keys(changes['modify']);
            for (var j = 0; j < typeidList.length; j++) {
              var modifies = changes['modify'][typeidList[j]];
              var modifyKeys = _.keys(modifies);
              for (var k = 0; k < modifyKeys.length; k++) {
                if (ChangeSet.isEmptyChangeSet(modifies[modifyKeys[k]])) {
                  delete modifies[modifyKeys[k]];
                }
              }
              if (_fastIsEmptyObject(changes['modify'][typeidList[j]])) {
                delete changes['modify'][typeidList[j]];
              }
            }
          }
          // Remove modify group if no operations are present
          if (_fastIsEmptyObject(changes.modify)) {
            delete changes['modify'];
          }
        }
      };
      // Add the indexed collection functions into the prototype of the ChangeSet
      ChangeSet.prototype._performApplyAfterOnPropertyIndexedCollection = ChangeSetIndexedCollectionFunctions._performApplyAfterOnPropertyIndexedCollection;
      ChangeSet.prototype._cleanIndexedCollectionChangeSet = ChangeSetIndexedCollectionFunctions._cleanIndexedCollectionChangeSet;
      ChangeSet.prototype._rebaseIndexedCollectionChangeSetForProperty = ChangeSetIndexedCollectionFunctions._rebaseIndexedCollectionChangeSetForProperty;
      exportModule('LYNX.Property.ChangeSetOperations.IndexedCollectionOperations.ChangeSetIndexedCollectionFunctions', ChangeSetIndexedCollectionFunctions);
    }());
    (function () {
      var _ = include('underscore');
      /**
       * An internal helper class, that keeps track of changes to repository references and computes the order of the update
       * operations which result from these reference changes
       *
       * @alias LYNX.Property._internal.RepositoryUpdateOperationsGraph
       * @constructor
       * @private
       */
      var RepositoryUpdateOperationsGraph = function () {
        this.nodes = {};
        // This represents a checkedOutRepositoryInformation
        this.operations = {};  // This represents an operation to move from one repositoryState to a different one
      };
      /**
       * @typedef {Object} LYNX.Property.RepositoryUpdateNode
       *
       * A node in the graph of repository update operations. It represents a CheckedOutRepositoryInfo object in the
       * CheckOutView, which is characterized by a repositoryGUID, checkoutGroupID and a commitGUID
       *
       * @property {String}  repositoryGUID
       *     The GUID of the repository of the represented CheckedOutRepositoryInfo
       * @property {String}  checkoutGroupID
       *     The GUID of the checkoutGroup of the represented CheckedOutRepositoryInfo
       * @property {String}  commitGUID
       *     The GUID of the commit of the represented CheckedOutRepositoryInfo
       * @property {Array<LYNX.Property.RepositoryUpdateOperation>}   incomingOperations
       *     The incoming operations are those operations, that were caused by a reference pointing to this node. They thus
       *     will result in the creation of / moving of an existing CheckedOutRepositoryInfo object with/to the represented
       *     state.
       * @property {Array<LYNX.Property.RepositoryUpdateOperation>}   outgoingOperations:
       *     The outgoing operations are those operations, that were caused by a reference no longer pointing to this node.
       *     They thus will result in the moving of an existing CheckedOutRepositoryInfo object to a different state.
       * @property {Number}  staticReferences
       *     The number of references that point to this state and that have not been changed at all
       * @property {string}  key
       *     A unique key that identifies this node
       *
       *
       * @property {Array<LYNX.Property.RepositoryUpdateOperation>} _processedOutgoingOperations
       *     The outgoing operations that point to nodes that have already been processed. Once all nodes referenced by
       *     outgoing operations have been processed, this node is ready for processing itself
       * @property {Number}  _executedInOperations
       *     The number of incoming operations that have already been pushed on the execution stack
       * @property {Number}  _executedInOperations
       *     The number of outgoing operations that have already been pushed on the execution stack
       */
      /**
       * @typedef {Object} LYNX.Property.RepositoryUpdateOperation
       *
       * An operation describing the change of the target of one or multiple repository reference property
       *
       * @property {LYNX.Property.RepositoryUpdateNode|undefined} from -
       *     The previous state of the reference properties. If this is set to undefined, it indicates that the
       *     repository reference property did not exist prior to the operations and was newly created
       * @property {LYNX.Property.RepositoryUpdateNode} to -
       *     The new state of the reference property
       * @property {Array} referenceProperties -
       *     An array with information about all the reference properties that are represented by this operation
       * @property {Boolean} mergeRequired -
       *     Does this operation need to be merged? That is the case, if the to state already exists when the operation
       *     is executed
       * @property {Boolean} removeFrom -
       *     After this operation has been executed, the originating repository will be removed. We will send an additional
       *     delete event for this, but this flag might help the application to optimize that case.
       */
      /**
       * Creates a new node in the operations graph. A node represents a given CheckedOutRepositoryInfo object and
       * is characterized by a repositoryGUID, a checkoutGroupID and a commitGUID
       *
       * @param {string} in_repositoryGUID         - The repository GUID of the CheckedOutRepositoryInfo
       * @param {string} in_checkoutGroupID        - The ID of the checkout group
       * @param {string} in_commitGUID             - The commitGuid for which the repository should be returned.
       *
       * @return {LYNX.Property.RepositoryUpdateNode}
       *     The created node in the repository graph
       * @private
       */
      RepositoryUpdateOperationsGraph.prototype._getOrCreateNode = function (in_repositoryGUID, in_checkoutGroupID, in_commitGUID) {
        // Create a unique key to find the node in the nodes map
        var key = in_repositoryGUID + in_checkoutGroupID + in_commitGUID;
        if (!this.nodes[key]) {
          // If no node exists, yet, create a new one
          this.nodes[key] = {
            repositoryGUID: in_repositoryGUID,
            checkoutGroupID: in_checkoutGroupID,
            commitGUID: in_commitGUID,
            incomingOperations: [],
            outgoingOperations: [],
            staticReferences: 0,
            key: key,
            _processedOutgoingOperations: [],
            _executedInOperations: 0,
            _executedOutOperations: 0
          };
        }
        return this.nodes[key];
      };
      /**
       * Returns an operation that describes the modification/creation of a reference
       * If there is not yet an existing operation, it will be created, otherwise the already existing one is returned.
       *
       * @param {LYNX.Property.RepositoryUpdateNode|undefined} previousNode
       *     The node that describes the state of the reference before the operation has been executed. It can be undefined
       *     to indicate that the reference has been newly created
       * @param {LYNX.Property.RepositoryUpdateNode} nextNode
       *     The node that describes the state of the reference after the operation has been executed
       *
       * @return {LYNX.Property.RepositoryUpdateOperation}
       *     The created/retrieved operation object
       * @private
       */
      RepositoryUpdateOperationsGraph.prototype._getOrCreateOperation = function (previousNode, nextNode) {
        // Create a key that uniquely identifies the operation for deduplication
        var operationKey = previousNode ? previousNode.key + '->' + nextNode.key : nextNode.key;
        // If the operation already exists, there is nothing we have to do
        if (this.operations[operationKey]) {
          return this.operations[operationKey];
        }
        // Otherwise, we have to create a new operation object
        var newOperation = {
          from: previousNode,
          to: nextNode,
          referenceProperties: [],
          mergeRequired: false,
          removeFrom: false
        };
        this.operations[operationKey] = newOperation;
        // And insert it into the corresponding nodes
        if (previousNode) {
          previousNode.outgoingOperations.push(newOperation);
        }
        nextNode.incomingOperations.push(newOperation);
        return newOperation;
      };
      /**
       * Update the mergeRequired flag in an operation
       *
       * @param {LYNX.Property.RepositoryUpdateOperation} in_operation
       *     The operation to update
       * @private
       */
      RepositoryUpdateOperationsGraph.prototype._updateMergeRequiredForOperation = function (in_operation) {
        // A merging operation has to be performed, when the node existed prior to the executed
        // operation (it thus had more than one static reference), or more than one operation
        // has already been performed
        in_operation.mergeRequired = in_operation.to.staticReferences > 0 || in_operation.to._executedInOperations > 0;
        if (in_operation.from) {
          in_operation.from._executedOutOperations++;
          in_operation.removeFrom = in_operation.from.staticReferences === 0 && in_operation.from._executedOutOperations === in_operation.from.outgoingOperations.length && in_operation.from.incomingOperations.length === 0;
        }
        // We have to increment the execution counter, so that all other operations affecting this node
        // will require a merge
        in_operation.to._executedInOperations++;
      };
      /**
       * Inserts a new operation that represents the state change as characterized by a reference information object
       *
       * @param {LYNX.Property.CheckoutView~CheckedOutRepositoryInfo~ReferenceInformation} in_referenceInformation
       *     Information about the reference for which an operation should be inserted. If it contains an old state
       *     it is interpreted as a reference move operation, otherwise as an initial load.
       *
       * @param {Boolean} in_failed - Has loading the referenced repository failed?
       */
      RepositoryUpdateOperationsGraph.prototype.insertOperationFromReference = function (in_referenceInformation, in_failed) {
        var previousNode, nextNode;
        // We only have a previous node, if there was an old commit GUID in the reference
        if (in_referenceInformation.oldCommitGUID) {
          previousNode = this._getOrCreateNode(in_referenceInformation.oldRepositoryGUID, in_referenceInformation.oldCheckoutGroupID, in_referenceInformation.oldCommitGUID);
        }
        // Create of get the target node
        nextNode = this._getOrCreateNode(in_failed ? '' : in_referenceInformation.repositoryGUID, in_failed ? '' : in_referenceInformation.checkoutGroupID, in_failed ? '' : in_referenceInformation.commitGUID);
        // Make sure a link for this operation exits in the graph
        var operation = this._getOrCreateOperation(previousNode, nextNode);
        // Store the reference property that caused the execution of this operation
        operation.referenceProperties.push(in_referenceInformation);
      };
      /**
       * Adds a static reference to a CheckedOutRepository. This indicates that a reference to this repository exists
       * but has not been changed/created
       *
       * @param {LYNX.Property.CheckoutView~CheckedOutRepositoryInfo~ReferenceInformation} in_referenceInformation
       *     Information about the reference for which a static reference should be inserted.
       * @param {Boolean} in_useOldValue - Use the old entry of the reference
       */
      RepositoryUpdateOperationsGraph.prototype.addStaticReference = function (in_referenceInformation, in_useOldValue) {
        var repositoryGUID, checkoutGroupID, commitGUID;
        if (!in_useOldValue) {
          repositoryGUID = in_referenceInformation.repositoryGUID;
          checkoutGroupID = in_referenceInformation.checkoutGroupID;
          commitGUID = in_referenceInformation.commitGUID;
        } else {
          repositoryGUID = in_referenceInformation.oldRepositoryGUID;
          checkoutGroupID = in_referenceInformation.oldCheckoutGroupID;
          commitGUID = in_referenceInformation.oldCommitGUID;
        }
        // Get the node this reference points to
        var referenceNode = this._getOrCreateNode(repositoryGUID, checkoutGroupID, commitGUID);
        // Increase the count of static references
        referenceNode.staticReferences++;
      };
      /**
       * Returns an ordered list of operations to perform to update the repositories to reflect the new state
       *
       * This will order the operations in such a way, that they can be evaluated without causing conflicts. This means
       * that when a reference has been updated to a new state, we have to execute operations that originate from that
       * state before executing the operation that will overwrite that state. Additionally, we have to keep track
       * of cases where multiple states have to be merged, or where an existing state has to be split
       *
       * @return {Array<Array<LYNX.Property.RepositoryUpdateOperation>>}
       *     The ordered operations to perform. They are grouped by the originating node.
       */
      RepositoryUpdateOperationsGraph.prototype.getOrderedOperations = function () {
        // Filter out the static nodes which have no operations affecting them
        var nodes = _.filter(_.values(this.nodes), function (node) {
          return node.outgoingOperations.length > 0 || node.incomingOperations.length > 0;
        });
        if (_.isEmpty(nodes)) {
          return [];
        }
        // We start the traversal at nodes which have no outgoing operations.
        // These cannot overwrite an existing checkedOutRepository and thus are safe to process first
        var processingQueue = _.filter(nodes, function (node) {
          return node.outgoingOperations.length === 0;
        });
        if (processingQueue.length === 0) {
          throw new Error('Repository references have been moved in a cycle. This is currently not supported.');
        }
        var operations = [];
        // Breadth first traversal of the node graph
        while (!_.isEmpty(processingQueue)) {
          var node = processingQueue.pop();
          // Process the outgoing operations for this node
          if (node.outgoingOperations.length > 0) {
            // TODO: Check, which of the operations have to split the node
            operations.push(node.outgoingOperations);
            for (var i = 0; i < node.outgoingOperations.length; i++) {
              this._updateMergeRequiredForOperation(node.outgoingOperations[i]);
            }
          }
          // Process the incoming operations
          for (var j = 0; j < node.incomingOperations.length; j++) {
            var operation = node.incomingOperations[j];
            var originatingNode = operation.from;
            if (!originatingNode) {
              // If there is no previousNode for the incoming operation, this is a simple load operation
              operations.push([operation]);
              this._updateMergeRequiredForOperation(operation);
            } else {
              // Mark this operation as processed
              originatingNode._processedOutgoingOperations.push(operation);
              // If all operations of the referenced node have been finished, it is ready to be processed
              if (originatingNode._processedOutgoingOperations.length === originatingNode.outgoingOperations.length) {
                // Store it in the queue
                processingQueue.unshift(originatingNode);
              }
            }
          }
        }
        return operations;
      };
      exportModule('LYNX.Property._internal.RepositoryUpdateOperationsGraph', RepositoryUpdateOperationsGraph);
    }());
    (function () {
      var BaseProperty = include('LYNX.Property.BaseProperty');
      var NodeProperty = include('LYNX.Property.NodeProperty');
      var ChangeSet = include('LYNX.Property.ChangeSet');
      var Property_Utils = include('LYNX.Property.Utils');
      var PropertyFactory = include('LYNX.Property.PropertyFactory');
      var TypeIdHelper = include('LYNX.Property.TypeIdHelper');
      var _ = include('underscore');
      var EventEmitter = include('LYNX.Utils.Events.EventEmitter');
      var MSG = include('LYNX.Property._constants.MSG');
      var generateGUID = include('LYNX.Utils.generateGUID');
      var Utils = include('LYNX.Property.Utils');
      var RepositoryUpdateOperationsGraph = include('LYNX.Property._internal.RepositoryUpdateOperationsGraph');
      var RepositoryReferenceProperty = include('LYNX.Property.RepositoryReferenceProperty');
      var LynxProperty = include('LYNX.Property', ['Repository']);
      var insertInNestedObjects = include('LYNX.Utils.NestObjectHelpers.insertInNestedObjects');
      var getInNestedObjects = include('LYNX.Utils.NestObjectHelpers.getInNestedObjects');
      var getOrInsertDefaultInNestedObjects = include('LYNX.Utils.NestObjectHelpers.getOrInsertDefaultInNestedObjects');
      var existsInNestedObjects = include('LYNX.Utils.NestObjectHelpers.existsInNestedObjects');
      var deleteInNestedObjects = include('LYNX.Utils.NestObjectHelpers.deleteInNestedObjects');
      var traverseNestedObjects = include('LYNX.Utils.NestObjectHelpers.traverseNestedObjects');
      /**
       * The CheckoutView class represents a checked-out view on a slice through the commit graph starting at a
       * supplied base commit.
       *
       * @augments LYNX.Utils.Events.EventEmitter
       * @constructor
       * @alias LYNX.Property.CheckoutView
       * @private
       */
      var CheckoutView = function () {
        EventEmitter.call(this);
        /**
         * Map of maps with the checked out repositories, indexed by repositoryGUID and then commitGUID
         *
         * @type {Object.<Object.<LYNX.Property.CheckoutView~CheckedOutRepositoryInfo>>}
         * @private
         */
        this._checkedOutRepositories = {};
        /**
         * The parent of the checked out repositories within this view
         *
         * @type {LYNX.Property.CheckoutView~CheckedOutRepositoryInfo}
         * @private
         */
        this._parentRepository = undefined;
        // Notification scope counter used to delay events from being triggered until the counter reaches 0
        this._notificationDelayScope = 0;
        // This is used within the popNotificationDelayScope to avoid triggering during the individual updates
        this._internalTriggeringDelayScope = 0;
        // The CheckoutView needs to know its HFDM object to check out referenced repositories
        this._hfdmObject = undefined;
        // When updating the parent repository, we have to delay the reference updates to avoid deleting the
        // parent right aways
        this._repositoryReferenceUpdatesDelayScope = 0;
        // This keeps track of all checked out repository infos that have been merged during the last reference update
        this._mergedCheckedOutRepositoryInfos = [];
        // Contains the modifications to checked out repositories, grouped by
        // repositoryGUID, in_checkoutGroupID, in_commitGUID
        this._modifiedRepositoryInformation = {};
        // Contains the modifications to checked out repositories, as an array, ordered by their execution order
        this._orderedModifiedRepositoryInformation = [];
        // Contains a stack with the currently processed repository operations
        this._updateOperationStack = [];
        // Is reporting of merged changesets enabled?
        this._enableMergedChangeSet = true;
        // Keeps track of all commits which have been requested but not yet received
        this._pendingRequestedCommits = {};
        // There are still pending requests which have not yet triggered a onAllReferencesLoaded event
        this._hasUnreportedPendingRequests = false;
        // Keeps track of references that failed to load
        this._failedReferencesByCommit = {};
        this._failedReferencesByReferenceGUID = {};
      };
      CheckoutView.prototype = Object.create(EventEmitter.prototype);
      /**
       * Event when a property has been modified
       *
       * This event is triggered every time a property in the checked out property tree is inserted, modified, or
       * deleted. The event is triggered synchronously when the modification happens, unless the modification happens
       * within a scope (which can be entered/left via pushNotificationDelayScope and popNotificationDelayScope).
       *
       * When merged ChangeSets are enabled (via enableMergedOnModifiedChangeset) the returned ChangeSet will
       * recursively contain changes to referenced repositories. Otherwise, only the changes on the root repository
       * will be reported.
       *
       * @event LYNX.Property.CheckoutView#modified
       * @param {LYNX.Property.CheckoutView}        checkoutView - The checkoutView that triggered the event
       * @param {LYNX.Property.SerializedChangeSet} changeSet -    ChangeSet with the modifications that were performed
       */
      /**
       * Event when a property has been modified with per repository modification information
       *
       * This event is triggered every time a property in the checked out property tree is inserted, modified, or
       * deleted. The event is triggered synchronously when the modification happens, unless the modification happens
       * within a scope (which can be entered/left via pushNotificationDelayScope and popNotificationDelayScope).
       *
       * It reports the changes for each of the checked out repositories individually. A specific checkout is identified
       * by the combination of repositoryGUID, commitGUID and checkoutGroupID. The checkoutGroupID can be either 'root'
       * for the repository and commit which is checked out at root of the checkout view or is set to the branchGUID
       * for all other referenced repositories.
       *
       * The event will be invoked with an array in which each entry described the changes to one checked out
       * repository (which do not include the changes in referenced repositories). The array is ordered in such a way
       * that the operation can be applied sequentially, but the application must be aware that there are cases in which
       * the operations have the same checked out repository as source/target (which we call a merge/split).
       *
       * @event LYNX.Property.CheckoutView#onModifiedPerRepository
       * @param {LYNX.Property.CheckoutView}                                                   checkoutView -
       *     The checkoutView that triggered the event
       * @param {Array.<LYNX.Property.CheckoutView.RepositoryCheckoutModification>}            modifications -
       *     ChangeSet with the modifications that were performed
       */
      /**
       * Event that is invoked when fetching a commit from a repository reference has failed
       *
       * @event LYNX.Property.CheckoutView#onRepositoryReferenceFetchFailed
       * @param {LYNX.Property.CheckoutView}                                                   checkoutView -
       *     The checkoutView that triggered the event
       * @param {String}                                                                       commitGUID -
       *     The commit GUID that failed to be fetched
       */
      /**
       * Event that is invoked when all asynchronous loading operations have been completed (either succeeded
       * or failed)
       *
       * @event LYNX.Property.CheckoutView#onAllReferencesLoaded
       * @param {LYNX.Property.CheckoutView}                                                   checkoutView -
       *     The checkoutView that triggered the event
       */
      /**
       * @typedef {Object} LYNX.Property.CheckoutView.RepositoryCheckoutModification
       *
       * Datastructure which describes the modification that hase been applied to an individual checked out repository
       * within this CheckoutView (not containing changes to referenced repositories.
       *
       * @property {String} repositoryGUID   - The GUID of the checked out repository after the modification
       * @property {String} commitGUID       - The GUID of the checked out commit after the modification
       * @property {String} checkoutGroupID  - The checkout group of this checked out repository. This can be 'root' for the
       *                                    repository at the root of the CheckoutView or it contains the BranchGUID from
       *                                    the repository reference property.
       *
       * @property {LYNX.Property.SerializedChangeSet}       changeset -
       *     The ChangeSet that describes the changes to the repository
       * @property {LYNX.Property.RepositoryUpdateOperation|undefined} repositoryOperation -
       *     If this structure describes the modification to a referenced repository, and the changes were caused by a
       *     change to the reference property, this entry contains information about the change that was applied to the
       *     reference property and which effect this had on the checked out repositories.
       * @property {Boolean|undefined} deleted -
       *     The checked out repository has been deleted. The delete operations will always be reported after other
       *     operations referring to the checked out repository as source.
       */
      /**
       * Return the root of the check out
       *
       * @return {LYNX.Property.NodeProperty} The root node
       */
      CheckoutView.prototype.getRoot = function () {
        if (this._parentRepository === undefined) {
          throw new Error(MSG.NO_REPOSITORY_CHECKED_OUT);
        }
        return this._parentRepository.repositoryRoot;
      };
      /**
       * Return the branch of the check out
       *
       * @return {LYNX.Property.BranchNode|string} The branch or Repository.DETACHED_HEAD
       */
      CheckoutView.prototype.getActiveBranch = function () {
        if (this._parentRepository === undefined) {
          throw new Error(MSG.NO_REPOSITORY_CHECKED_OUT);
        }
        return this._parentRepository.branchNode;
      };
      /**
      * Returns if the active branch is detached.
      * @return {Boolean} Whether the branch is detached.
      */
      CheckoutView.prototype.isDetachedHead = function () {
        return this.getActiveBranch() === LynxProperty.Repository.DETACHED_HEAD;
      };
      /**
       * Return the commit of the check out
       *
       * @return {LYNX.Property.BranchNode|string} The commit or Repository.DETACHED_HEAD
       */
      CheckoutView.prototype.getActiveCommit = function () {
        if (this._parentRepository === undefined) {
          throw new Error(MSG.NO_REPOSITORY_CHECKED_OUT);
        }
        return this._parentRepository.commitNode;
      };
      /**
       * Check whether the CheckoutView has checked out anything at all
       * @return {boolean} Returns true if the CheckoutView has checked out a repo
       */
      CheckoutView.prototype.isCheckedOut = function () {
        return !!this._parentRepository;
      };
      /**
       * Set the parent repository of the checkout view.
       * The parent repository must be in the list of checked out repositories.
       * If the new parent is referencing a different repository than the current parent,
       * delete the old repository from the list of checked out repositories along with all its dependencies.
       * @param {string} in_repositoryGuid The Repository Guid
       * @param {string} in_commitGuid The commit guid
       * @protected
       */
      CheckoutView.prototype._setParentRepository = function (in_repositoryGuid, in_commitGuid) {
        // A parent repository must have been registered with the root checkoutGroupID
        if (!this._checkedOutRepositories[in_repositoryGuid] || !this._checkedOutRepositories[in_repositoryGuid]['root'] || !this._checkedOutRepositories[in_repositoryGuid]['root'][in_commitGuid]) {
          throw new Error(MSG.SET_PARENT_REPO_INVALID);
        }
        var newParent = this._checkedOutRepositories[in_repositoryGuid]['root'][in_commitGuid];
        if (!_.isUndefined(this._parentRepository) && this._parentRepository.repositoryGUID !== in_repositoryGuid) {
          var oldRepositoryGuid = this._parentRepository.repositoryGUID;
          delete this._checkedOutRepositories[oldRepositoryGuid];  // TODO: Let the application know about the removal of the old repository + all its dependencies
                                                                   //       remove the dependencies
                                                                   // see LYNXDEV-140
        }
        this._parentRepository = newParent;
      };
      /**
       * Returns the parent repository guid
       * @return {string|undefined} The repository guid
       */
      CheckoutView.prototype.getParentRepositoryGuid = function () {
        if (this._parentRepository) {
          return this._parentRepository.repositoryGUID;
        } else {
          return undefined;
        }
      };
      CheckoutView.prototype.getFailedReferences = function () {
        return _.flatten(_.map(_.values(this._failedReferencesByCommit), function (referenceInfo) {
          return _.map(referenceInfo.referenceProperties, function (reference) {
            return reference.property;
          });
        }), true);
      };
      /**
       * If this flag is set to true, the CheckoutView will generate a merged changeset for the modified events
       * that contains all changes to referenced repositories. If it is set to false, the modified event only contains the
       * changes to the root. If an application uses the onModifiedPerRepository events instead of the modified,
       * disabling the creation of merged changesets for the modified events avoids unnecessary computations.
       * By default, this is set to enabled when the class is created.
       *
       * @param {Boolean} in_enabled - Are merged Changesets enabled
       */
      CheckoutView.prototype._enableMergedOnModifiedChangeset = function (in_enabled) {
        this._enableMergedChangeSet = in_enabled;
      };
      /**
       * Returns the repository information for the give GUID.
       *
       * This is a function that should only be used by Repository and which is only temporary as long as we only have one
       * repository per GUID registered.
       *
       * @param {string} in_RepositoryGuid         - The repository GUID for which the reference object is returned
       * @param {string} [in_checkoutGroupID]      - The ID of the checkout group
       *                                             If none is supplied 'root' will be used
       * @param {string} [in_commitGuid]           - The commitGuid for which the repository should be returned.
       *                                             If none is supplied an arbitrary one is returned
       *
       * @return {LYNX.Property.CheckoutView~CheckedOutRepositoryInfo|undefined} The checked out repository information or
       *     undefined if none could be found
       * @protected
       */
      CheckoutView.prototype._getCheckedOutRepositoryInfo = function (in_RepositoryGuid, in_checkoutGroupID, in_commitGuid) {
        in_checkoutGroupID = in_checkoutGroupID !== undefined ? in_checkoutGroupID : 'root';
        if (!this._checkedOutRepositories[in_RepositoryGuid]) {
          return undefined;
        }
        var repositoryInfos = this._checkedOutRepositories[in_RepositoryGuid][in_checkoutGroupID];
        if (!repositoryInfos || _.isEmpty(repositoryInfos)) {
          return undefined;
        }
        if (in_commitGuid) {
          return repositoryInfos[in_commitGuid];
        } else {
          return _.values(repositoryInfos)[0];
        }
      };
      /**
       * Deletes a checked out repository info
       *
       * @param {string} in_repositoryGUID  - The GUID of the repository
       * @param {string} in_checkoutGroupID - The ID of the checkout group
       * @param {string} in_commitGUID      - The GUID of the commit
       * @private
       */
      CheckoutView.prototype._removeCheckedOutRepository = function (in_repositoryGUID, in_checkoutGroupID, in_commitGUID) {
        // Make sure all references to other repositories are cleaned
        var repositoryInfo = getInNestedObjects(this._checkedOutRepositories, in_repositoryGUID, in_checkoutGroupID, in_commitGUID);
        var removedReferences = [];
        var changeset = {};
        if (repositoryInfo) {
          var guids = _.keys(repositoryInfo._repositoryReferenceProperties);
          for (var i = 0; i < guids.length; i++) {
            var referenceProperty = repositoryInfo._repositoryReferenceProperties[guids[i]];
            var referencingRepositoryInfo = getInNestedObjects(this._checkedOutRepositories, referenceProperty.repositoryGUID, referenceProperty.checkoutGroupID, referenceProperty.commitGUID);
            referencingRepositoryInfo.removeReferencingRepositoryReferenceProperty(referenceProperty.property._instanceGuid);
            removedReferences.push(referenceProperty);
          }
          changeset = { remove: repositoryInfo.repositoryRoot ? repositoryInfo.repositoryRoot.getDynamicIds() : [] };
        }
        deleteInNestedObjects(this._checkedOutRepositories, in_repositoryGUID, in_checkoutGroupID, in_commitGUID);
        // Create a structure describing the modification to the checked out repository
        var modificationInformation = {
          repositoryGUID: in_repositoryGUID,
          commitGUID: in_commitGUID,
          checkoutGroupID: in_checkoutGroupID,
          deleted: true,
          removedReferences: removedReferences,
          // If a reference is set to empty / a non existing commit,
          // we have to remove all entries from that repository
          changeset: changeset
        };
        var alreadyExisting = insertInNestedObjects(this._modifiedRepositoryInformation, in_repositoryGUID, in_checkoutGroupID, in_commitGUID, modificationInformation);
        this._orderedModifiedRepositoryInformation.push(modificationInformation);
        if (alreadyExisting) {
          throw new Error(MSG.DUPLICATED_COMMIT_UPDATE);
        }
      };
      /**
       * Registers the given checked out repository information for the supplied object. If an old commitGUID is given
       * the old object is removed and replaced by the new one
       *
       * @param {LYNX.Property.CheckoutView~CheckedOutRepositoryInfo} in_repositoryInfo - The checked out repository
       *     information to be registered
       * @param {string} in_oldCommitGUID - The guid of the old commit that is replaced by this repository information
       * @param {string} in_newCommitGUID - The guid of the new commit that will replace the old one
       * @param {string} [in_checkoutGroupID] - The ID of the checkout group. If none is supplied 'root' will be used
       * @private
       */
      CheckoutView.prototype._updateRepositoryInfo = function (in_repositoryInfo, in_oldCommitGUID, in_newCommitGUID, in_checkoutGroupID) {
        var repositoryGUID = in_repositoryInfo.repositoryGUID;
        var oldCheckoutGroupID = in_repositoryInfo._checkoutGroupID;
        // If present, remove the old checked out repository definition
        if (in_oldCommitGUID) {
          deleteInNestedObjects(this._checkedOutRepositories, repositoryGUID, oldCheckoutGroupID, in_oldCommitGUID);
        }
        // Update the checkoutGroupID to the new
        if (in_checkoutGroupID) {
          in_repositoryInfo._checkoutGroupID = in_checkoutGroupID;
        }
        var checkoutGroupID = in_repositoryInfo._checkoutGroupID;
        in_repositoryInfo.setParent(this);
        if (this._mergedCheckedOutRepositoryInfos.length > 0) {
          var lastMergedRepo = this._mergedCheckedOutRepositoryInfos[this._mergedCheckedOutRepositoryInfos.length - 1];
          if (lastMergedRepo.commitGUID === in_newCommitGUID && lastMergedRepo.repositoryInfo === undefined) {
            lastMergedRepo.repositoryInfo = in_repositoryInfo;
            return;
          }
        }
        // Insert the new entry
        var alreadyExisting = insertInNestedObjects(this._checkedOutRepositories, repositoryGUID, checkoutGroupID, in_newCommitGUID, in_repositoryInfo);
        if (alreadyExisting) {
          throw new Error(MSG.DUPLICATED_COMMIT_UPDATE);
        }
        // If a repository info has been updated, we have to check, whether we have
        // to load more repositories recursively
        this._updateReferencedRepositories();
      };
      /**
       * Returns the notification delay counter
       * @return {number} The notification delay counter
       */
      CheckoutView.prototype.getNotificationDelayScope = function () {
        return this._notificationDelayScope;
      };
      /**
       * Delays notifications until popNotificationDelayScope has been called the same number of times as
       * pushNotificationDelayScope.
       */
      CheckoutView.prototype.pushNotificationDelayScope = function () {
        this._notificationDelayScope++;
        var that = this;
        // We just forward this to all checkedOutRepositoryInfo objects
        this._forEachCheckedOutRepository(function (repositoryInfo) {
          repositoryInfo.setNotificationDelayScope(that._notificationDelayScope);
        });
      };
      /**
       * Reenables notifications when popNotificationDelayScope has been called the same number of times as
       * pushNotificationDelayScope.
       */
      CheckoutView.prototype.popNotificationDelayScope = function () {
        if (this.notificationDelayScope === 0) {
          console.error('Unbalanced number of pushNotificationDelayScope and popNotificationDelayScope calls');
        }
        this._notificationDelayScope--;
        var that = this;
        // We just forward this to all checkedOutRepositoryInfo objects
        this._internalTriggeringDelayScope++;
        this._forEachCheckedOutRepository(function (repositoryInfo) {
          repositoryInfo.setNotificationDelayScope(that._notificationDelayScope);
        });
        this._internalTriggeringDelayScope--;
        this._triggerUnreportedModifyMessages();
      };
      /**
       * Triggers unreported onModifiedPerRepository and modified events
       * @private
       */
      CheckoutView.prototype._triggerUnreportedModifyMessages = function () {
        if (this._notificationDelayScope === 0 && this._internalTriggeringDelayScope === 0 && !_.isEmpty(this._orderedModifiedRepositoryInformation)) {
          // We first copy the modification information and reset it, so that
          // recursively called handlers won't modify the existing entries
          var modifiedRepositoryInformation = this._modifiedRepositoryInformation;
          var orderedModifiedRepositoryInformation = this._orderedModifiedRepositoryInformation;
          // Reset all tracked modifications
          this._modifiedRepositoryInformation = {};
          this._orderedModifiedRepositoryInformation = [];
          this.trigger('onModifiedPerRepository', this, [
            this,
            orderedModifiedRepositoryInformation
          ]);
          if (this._enableMergedChangeSet) {
            this._createMergedOnModifyChangeSet(modifiedRepositoryInformation, orderedModifiedRepositoryInformation);
          }
          // Report modifications on the root property
          var rootPropertyInformation = getInNestedObjects(modifiedRepositoryInformation, this._parentRepository.repositoryGUID, this._parentRepository._checkoutGroupID, this._parentRepository.commitNode.getGuid());
          if (rootPropertyInformation === undefined && this._enableMergedChangeSet) {
            throw new Error('Internal error: ' + 'Currently, there should be no modifications for ' + 'sub-repositories without a change on the root.');
          }
          if (rootPropertyInformation !== undefined) {
            if (!ChangeSet.isEmptyChangeSet(rootPropertyInformation.changeset)) {
              this.trigger('modified', this, [
                this,
                new ChangeSet(rootPropertyInformation.changeset)
              ]);
            }
          }
          // Trigger an event, once all pending references have been loaded
          if (_.isEmpty(this._pendingRequestedCommits)) {
            if (this._hasUnreportedPendingRequests) {
              this.trigger('onAllReferencesLoaded', this, [this]);
              this._hasUnreportedPendingRequests = false;
            }
          } else {
            this._hasUnreportedPendingRequests = true;
          }
        }
      };
      /**
       * Checks whether all or a specific checked out repository has any pending changes
       * @param {string=} [in_repositoryGuid] The repository guid to check against.
       *    If not specified, we will check against all checked out repositories.
       *
       * @return {boolean} Returns true if there are any changes
       */
      CheckoutView.prototype.hasPendingChanges = function (in_repositoryGuid) {
        if (in_repositoryGuid) {
          return this._getCheckedOutRepositoryInfo(in_repositoryGuid).hasPendingChanges();
        } else {
          return !!this._forEachCheckedOutRepository(function (repositoryInfo) {
            if (repositoryInfo.hasPendingChanges()) {
              return true;
            } else {
              // We return undefined, as in that case the traversal is continued
              return undefined;
            }
          });
        }
      };
      /**
       * Invokes a callback for each of the checkout repositories
       *
       * @param {function(LYNX.Property.CheckoutView~CheckedOutRepositoryInfo, string, string, string)} in_callback -
       *     The callback that is invoked for each checked out repository. It will be passed the repository information
       *     and the repository guid, the checkoutGroupID and the commit guid for that repository. If it returns undefined,
       *     the traversal will be continued, otherwise the returned value will be returned
       *
       * @return {*} The value returned by the last invoked callback or undefined if all callbacks returned undefined
       * @private
       */
      CheckoutView.prototype._forEachCheckedOutRepository = function (in_callback) {
        // We just forward this to all checkedOutRepositoryInfo objects
        var repositoryKeys = _.keys(this._checkedOutRepositories);
        var checkedOutRepositoryInfo;
        for (var i = 0; i < repositoryKeys.length; i++) {
          var groupIDKeys = _.keys(this._checkedOutRepositories[repositoryKeys[i]]);
          for (var j = 0; j < groupIDKeys.length; j++) {
            var commitKeys = _.keys(this._checkedOutRepositories[repositoryKeys[i]][groupIDKeys[j]]);
            for (var k = 0; k < commitKeys.length; k++) {
              checkedOutRepositoryInfo = this._checkedOutRepositories[repositoryKeys[i]][groupIDKeys[j]][commitKeys[k]];
              var result = in_callback(checkedOutRepositoryInfo, repositoryKeys[i], groupIDKeys[j], commitKeys[k]);
              if (result !== undefined) {
                return result;
              }
            }
          }
        }
        return undefined;
      };
      /**
       * @return {number} the number of currently checked out repositories
       */
      CheckoutView.prototype.getNumberOfCheckedOutRepositories = function () {
        var count = 0;
        this._forEachCheckedOutRepository(function () {
          count++;
        });
        return count;
      };
      /**
       * Computes a list of all referenced repositories, compares it with those
       * that are currently checked out and updates those by deleting the
       * no longer needed ones as well as checking out the missing ones
       */
      CheckoutView.prototype._updateReferencedRepositories = function () {
        var that = this;
        if (this._repositoryReferenceUpdatesDelayScope > 0) {
          return;
        }
        // This CheckoutView is not managed via the HFDM object, so we are not able to support repository hierarchies
        if (!this._hfdmObject) {
          return;
        }
        if (this._parentRepository === undefined) {
          // Nothing has yet been checked out
          // TODO: Do we need to delete all repositories?
          return;
        }
        this._internalTriggeringDelayScope++;
        var removedRepositories = {};
        while (this._updateReferencedRepositoriesInternal(removedRepositories)) {
        }
        traverseNestedObjects(removedRepositories, 3, false, function (repositoryGUID, checkoutGroupID, commitGUID, references) {
          that._removeCheckedOutRepository(repositoryGUID, checkoutGroupID, commitGUID);
        });
        this._internalTriggeringDelayScope--;
        if (this._repositoryReferenceUpdatesDelayScope === 0) {
          // Notify the application via an event
          this._triggerUnreportedModifyMessages();
          // Remove the no longer used list of merged repositories
          this._mergedCheckedOutRepositoryInfos = [];
        }
      };
      /**
       * Computes a list of all referenced repositories, compares it with those
       * that are currently checked out and updates those by deleting the
       * no longer needed ones as well as checking out the missing ones
       *
       * This is an internal helper function, that only performs one level of updates. After checking out the repositories
       * in the load list, it might be necessary to invoke it again
       *
       * @param {Object} io_removedRepositories - This object will be populated with the repositories that have to be
       *     removed after all operations have been applied.
       *
       * @return {boolean} Returns true if a new repository has been loaded
       */
      CheckoutView.prototype._updateReferencedRepositoriesInternal = function (io_removedRepositories) {
        // Create a list of all currently referenced repositories
        // by default, we insert all repositories we have loaded and mark them as not referenced
        var referencedRepositories = {};
        this._forEachCheckedOutRepository(function (in_repositoryInfo, repositoryGUID, checkoutGroupID, commitGUID) {
          insertInNestedObjects(referencedRepositories, repositoryGUID, checkoutGroupID, commitGUID, []);
        });
        // We use this to compile all operations that have to be performed to update the repositories
        var repositoryOperations = new RepositoryUpdateOperationsGraph();
        var unprocessedStack = [{
            repositoryGUID: this._parentRepository.repositoryGUID,
            checkoutGroupID: this._parentRepository._checkoutGroupID,
            commitGUID: this._parentRepository.commitNode.getGuid()
          }];
        // Mark the root repository as referenced
        insertInNestedObjects(referencedRepositories, this._parentRepository.repositoryGUID, this._parentRepository._checkoutGroupID, this._parentRepository.commitNode.getGuid(), ['root']);
        // Magic value to indicate that this is the root
        while (!_.isEmpty(unprocessedStack)) {
          var repo = unprocessedStack.pop();
          // Otherwise append all repositories referenced by this one recursively to the list of repositories to process
          var checkedOutInfo = this._checkedOutRepositories[repo.repositoryGUID][repo.checkoutGroupID][repo.commitGUID];
          var referenceInstances = _.keys(checkedOutInfo._repositoryReferenceProperties);
          for (var i = 0; i < referenceInstances.length; i++) {
            var referenceInformation = checkedOutInfo._repositoryReferenceProperties[referenceInstances[i]];
            // Ignore references, which we already tried to fetch and failed
            var failedReference = this._failedReferencesByCommit[referenceInformation.commitGUID];
            // We just ignore failed references without an old state
            if (failedReference) {
              if (referenceInformation.oldCommitGUID) {
                repositoryOperations.insertOperationFromReference(referenceInformation, true);
              }
              continue;
            }
            // If the reference is currently loading, we regard it as a static reference on its old value
            var repositoryGUID, checkoutGroupID, commitGUID;
            var referenceIsCurrentlyLoading = this._pendingRequestedCommits[referenceInformation.commitGUID] !== undefined;
            if (!referenceIsCurrentlyLoading) {
              repositoryGUID = referenceInformation.repositoryGUID;
              checkoutGroupID = referenceInformation.checkoutGroupID;
              commitGUID = referenceInformation.commitGUID;
            } else {
              repositoryGUID = referenceInformation.oldRepositoryGUID;
              checkoutGroupID = referenceInformation.oldCheckoutGroupID;
              commitGUID = referenceInformation.oldCommitGUID;
            }
            var currentReferences = getOrInsertDefaultInNestedObjects(referencedRepositories, repositoryGUID, checkoutGroupID, commitGUID, []);
            var repositoryHasAlreadyBeenLoaded = existsInNestedObjects(this._checkedOutRepositories, repositoryGUID, checkoutGroupID, commitGUID);
            if (currentReferences.length === 0 && repositoryHasAlreadyBeenLoaded) {
              // We haven't visited this repository yet, and it is available,
              // so put it on the stack of repositories to process
              unprocessedStack.push({
                repositoryGUID: repositoryGUID,
                checkoutGroupID: checkoutGroupID,
                commitGUID: commitGUID
              });
            }
            // Store this reference in the list of references for the repository
            currentReferences.push(referenceInformation);
            // If the repository hasn't been loaded yet, or if a reference has been moved, we have to create an operation
            // that reflects this change
            if ((!repositoryHasAlreadyBeenLoaded || referenceInformation.oldCommitGUID) && !referenceIsCurrentlyLoading) {
              repositoryOperations.insertOperationFromReference(referenceInformation, false);
            } else {
              // Otherwise, we just increase the number of unchanged references
              repositoryOperations.addStaticReference(referenceInformation, referenceIsCurrentlyLoading);
            }
          }
        }
        // Load the repositories that are currently missing
        var updateCounts = {};
        var newRepositoryLoaded = false;
        var operations = repositoryOperations.getOrderedOperations();
        for (var i = 0; i < operations.length; i++) {
          var operationGroup = operations[i];
          for (var j = 0; j < operationGroup.length; j++) {
            var repositoryOperation = operationGroup[j];
            this._updateOperationStack.push(repositoryOperation);
            this._pushRepositoryReferenceUpdatesDelayScope();
            // If we do not yet have a checked out repository info object for this combination of repositoryGUID,
            // checkoutGroupID and commitGUID, we create a new one, otherwise, we use the existing one
            var checkedOutRepositoryInfo = undefined;
            if (repositoryOperation.from) {
              // If there are no further references to this repository, we can update it to the new GUID
              checkedOutRepositoryInfo = this._getCheckedOutRepositoryInfo(repositoryOperation.from.repositoryGUID, repositoryOperation.from.checkoutGroupID, repositoryOperation.from.commitGUID);
              if ((repositoryOperation.from.staticReferences !== 0 || (updateCounts[repositoryOperation.from.key] || 0) < repositoryOperation.from.outgoingOperations.length - 1) && repositoryOperation.to.commitGUID !== '') {
                var newCheckedOutRepositoryInfo = new CheckoutView.CheckedOutRepositoryInfo(repositoryOperation.to.repositoryGUID, repositoryOperation.to.checkoutGroupID);
                newCheckedOutRepositoryInfo._initialChangeSet = checkedOutRepositoryInfo.repositoryRoot.serialize();
                checkedOutRepositoryInfo = newCheckedOutRepositoryInfo;
                updateCounts[repositoryOperation.from.key] = (updateCounts[repositoryOperation.from.key] || 0) + 1;
              }
            }
            if (!checkedOutRepositoryInfo) {
              // We can get here, if a repository has been deleted in the previous cycle, but a reference to it was created
              // before it got deleted. Currently, we reload the repository in that case. We probably should detect this
              // case and shouldn't delete it in the first place
              delete repositoryOperation.from;
              repositoryOperation.removeFrom = false;
              checkedOutRepositoryInfo = new CheckoutView.CheckedOutRepositoryInfo(repositoryOperation.to.repositoryGUID, repositoryOperation.to.checkoutGroupID);
            }
            // We have to make sure, that this does not trigger recursive reference updates. Instead we will perform the
            // updates once all referenced repositories have been loaded in the loop in _updateReferencedRepositories
            if (repositoryOperation.mergeRequired) {
              this._mergedCheckedOutRepositoryInfos.push({
                repositoryGuid: repositoryOperation.to.repositoryGUID,
                checkoutGroupID: repositoryOperation.to.checkoutGroupID,
                commitGUID: repositoryOperation.to.commitGUID,
                repositoryInfo: undefined
              });
            }
            var referenceTargetNonExisting = false;
            if (repositoryOperation.to.commitGUID !== '') {
              var repository = this._hfdmObject._checkoutSingleRepository(repositoryOperation.to.commitGUID, this, checkedOutRepositoryInfo, repositoryOperation.to.checkoutGroupID);
              // When we merged a repository, the checkoutRepositoryInfo of that repository is no longer registered in the
              // _checkedOutRepositories structure (we removed it under its previous name, but could not insert it under its
              // new names, as there has already been a repository under that identifier). When this happens, the
              // _notification delay scope of the checkoutRepositoryInfo is no longer updated by the popDelayScope in the
              // _checkoutSingeRepository call above and thus we have to do it manually here.
              if (repositoryOperation.mergeRequired) {
                if (checkedOutRepositoryInfo.notificationDelayScope !== this._notificationDelayScope) {
                  checkedOutRepositoryInfo.setNotificationDelayScope(this._notificationDelayScope);
                }
              }
              if (repository) {
                for (var i = 0; i < repositoryOperation.referenceProperties.length; i++) {
                  // Remove the information about the previous commit from the entry, after it has been loaded
                  var reference = repositoryOperation.referenceProperties[i];
                  reference.oldCommitGUID = undefined;
                  reference.oldBranchGUID = undefined;
                  reference.oldRepositoryGUID = undefined;
                  reference.oldCheckoutGroupID = undefined;
                  // Mark the repository as referenced by this reference
                  checkedOutRepositoryInfo.addReferencingRepositoryReferenceProperty(reference.property);
                  // Mark the references as no longer failed
                  this._removeFailedReferenceProperty(reference.property);
                  // And set the state to available
                  reference.property._setState(RepositoryReferenceProperty.STATE.AVAILABLE);
                }
                newRepositoryLoaded = true;
              } else {
                // The commit has already been requested
                if (!this._pendingRequestedCommits[repositoryOperation.to.commitGUID]) {
                  // If a synchronous checkout was not possible, we have to asynchronously request the commit
                  this._hfdmObject._fetchCommit(repositoryOperation.to.commitGUID, // TODO: We have to explicitly store the  branch in the
                  // to operation. Currently the checkoutGroupID always contains
                  // the branch, but we might need to store it separately
                  repositoryOperation.to.checkoutGroupID, {}, CheckoutView.prototype._commitFetchedHandler.bind(this));
                  // And insert it into the the list of currently pending repositories
                  this._pendingRequestedCommits[repositoryOperation.to.commitGUID] = {
                    repository: repositoryOperation.to.repositoryGUID,
                    referenceProperties: []
                  };
                  newRepositoryLoaded = true;
                }
                // Keep track of references that are involved in this fetch
                var referenceProperties = this._pendingRequestedCommits[repositoryOperation.to.commitGUID].referenceProperties;
                for (var i = 0; i < repositoryOperation.referenceProperties.length; i++) {
                  var reference = repositoryOperation.referenceProperties[i];
                  referenceProperties.push(reference);
                  reference.property._setState(RepositoryReferenceProperty.STATE.LOADING, reference.oldRepositoryGUID, reference.oldBranchGUID, reference.oldCommitGUID);
                }
                referenceTargetNonExisting = false;
              }
            } else {
              // Repository reference has been set to an empty string, so we mark the reference as empty
              referenceTargetNonExisting = true;
              for (var i = 0; i < repositoryOperation.referenceProperties.length; i++) {
                // Remove the information about the previous commit from the entry, after it has been loaded
                var reference = repositoryOperation.referenceProperties[i];
                reference.oldCommitGUID = undefined;
                reference.oldBranchGUID = undefined;
                reference.oldRepositoryGUID = undefined;
                reference.oldCheckoutGroupID = undefined;
                // If the reference has been set to an empty string
                if (reference.property.get('commitGUID').value === '') {
                  // We mark the references as no longer failed
                  this._removeFailedReferenceProperty(reference.property);
                  // And set the state to available
                  reference.property._setState(RepositoryReferenceProperty.STATE.EMPTY);
                  checkedOutRepositoryInfo._currentlyEmptyRepositoryReferenceProperties[reference.property._instanceGuid] = reference.property;
                }
              }
            }
            this._popRepositoryReferenceUpdatesDelayScope(false);
            if (referenceTargetNonExisting) {
              // We have to push an explicit operation indicating that the reference has
              // been set to empty
              var modificationInformation = {
                // If a reference is set to empty / a non existing commit,
                // we have to remove all entries from that repository
                changeset: { remove: checkedOutRepositoryInfo.repositoryRoot ? checkedOutRepositoryInfo.repositoryRoot.getDynamicIds() : [] },
                repositoryGUID: '',
                commitGUID: '',
                checkoutGroupID: '',
                referenceTargetNonExisting: true,
                repositoryOperation: repositoryOperation
              };
              this._orderedModifiedRepositoryInformation.push(modificationInformation);
            }
            this._updateOperationStack.pop();
          }
        }
        // Remove all repositories that are no longer referenced anywhere
        traverseNestedObjects(referencedRepositories, 3, false, function (referencedRepositoryGUID, referencedCheckoutGroupID, referencedCommitGUID, references) {
          if (references.length === 0) {
            insertInNestedObjects(io_removedRepositories, referencedRepositoryGUID, referencedCheckoutGroupID, referencedCommitGUID, true);
          } else {
            deleteInNestedObjects(io_removedRepositories, referencedRepositoryGUID, referencedCheckoutGroupID, referencedCommitGUID);
          }
        });
        return newRepositoryLoaded;
      };
      /**
       * Handler that is invoked when a new commit has been fetched by the backend
       *
       * @param {Error|null}                  in_error  - Error that occurred when fetching the commit
       * @param {LYNX.Property.Commit|String} in_commit - The fetched commit object / commit GUID
       */
      CheckoutView.prototype._commitFetchedHandler = function (in_error, in_commit) {
        if (in_error) {
          if (!this._failedReferences) {
            var requestInfo = this._pendingRequestedCommits[in_commit];
            this._failedReferencesByCommit[in_commit] = { referenceProperties: requestInfo.referenceProperties };
            for (var i = 0; i < requestInfo.referenceProperties.length; i++) {
              var reference = requestInfo.referenceProperties[i];
              this._failedReferencesByReferenceGUID[reference.property._instanceGuid] = in_commit;
              reference.property._setState(RepositoryReferenceProperty.STATE.FAILED);
              reference.property.getRoot()._getCheckedOutRepositoryInfo()._currentlyEmptyRepositoryReferenceProperties[reference.property._instanceGuid] = reference.property;
            }
          }
          delete this._pendingRequestedCommits[in_commit];
          this._updateReferencedRepositories();
          this.trigger('onRepositoryReferenceFetchFailed', this, [
            this,
            in_commit
          ]);
          return;
        }
        if (this._pendingRequestedCommits[in_commit.getGuid()]) {
          delete this._pendingRequestedCommits[in_commit.getGuid()];
          this._updateReferencedRepositories();
        } else {
          console.warning('Received unrequested commit: ' + in_commit.getGuid());
        }
      };
      /**
       * Removes a reference property from the list of failed reference properties
       *
       * @param {LYNX.Property.RepositoryReferenceProperty} in_property - The property to remove
       * @private
       */
      CheckoutView.prototype._removeFailedReferenceProperty = function (in_property) {
        var instanceGuid = in_property._instanceGuid;
        if (this._failedReferencesByReferenceGUID[instanceGuid]) {
          // Remove the reference from the by reference GUID map
          var commitGUID = this._failedReferencesByReferenceGUID[instanceGuid];
          delete this._failedReferencesByReferenceGUID[instanceGuid];
          // Remove the reference from the by commit map
          var failedByCommit = this._failedReferencesByCommit[commitGUID];
          for (var i = 0; i < failedByCommit.referenceProperties.length; i++) {
            if (failedByCommit.referenceProperties[i].property === in_property) {
              failedByCommit.referenceProperties.splice(i, 1);
              break;
            }
          }
          if (failedByCommit.referenceProperties.length === 0) {
            delete this._failedReferencesByCommit[commitGUID];
          }
        }
      };
      /**
       * When checking out a new parent repository, we have to delay the updates until the parent has been
       * registered as the new parent. So we have a scope for this, which is pushed in this function.
       *
       * @protected
       */
      CheckoutView.prototype._pushRepositoryReferenceUpdatesDelayScope = function () {
        this._repositoryReferenceUpdatesDelayScope++;
      };
      /**
       * When checking out a new parent repository, we have to delay the updates until the parent has been
       * registered as the new parent. So we have a scope for this, which is popped in this function.
       *
       * @param {Boolean} in_invokeOnPop - Should a repository reference update
       * @private
       */
      CheckoutView.prototype._popRepositoryReferenceUpdatesDelayScope = function (in_invokeOnPop) {
        this._repositoryReferenceUpdatesDelayScope--;
        if (this._repositoryReferenceUpdatesDelayScope === 0 && in_invokeOnPop && this._notificationDelayScope === 0) {
          this._updateReferencedRepositories();
        }
      };
      /**
       * Set the HFDM object the CheckOutView belongs to.
       * This should only be called by the HFDM object itself.
       *
       * @param {LYNX.Property.HFDM} in_hfdmObject - The HFDM object
       * @protected
       */
      CheckoutView.prototype._setHFDMObject = function (in_hfdmObject) {
        this._hfdmObject = in_hfdmObject;
      };
      /**
       * Joins per-repository changesets into a merged common changeset via the repository references between the
       * repositories.
       *
       * This function iterates over the independent per repository operations in this._orderedModifiedRepositoryInformation
       * and connects the changesets in these operations to each other. For each change to a repository reference,
       * the changeset in the corresponding repository operation is inserted. Additionally, the case where a
       * new repository reference to an already existing repository without a corresponding loading operation is handled.
       *
       * @param {Object} in_modifiedRepositoryInformation        - The modifications from
       *                                                           this._modifiedRepositoryInformation
       * @param {Object} in_orderedModifiedRepositoryInformation - The modifications from
       *                                                           this._orderedModifiedRepositoryInformation
       * @private
       */
      CheckoutView.prototype._createMergedOnModifyChangeSet = function (in_modifiedRepositoryInformation, in_orderedModifiedRepositoryInformation) {
        var that = this;
        var availableLoadingOperations = {};
        var alreadyUpdatedReferencePropertyGUIDs = {};
        // Extract all pure loading operations (i.e. initial checkouts of new repositories)
        for (var i = 0; i < in_orderedModifiedRepositoryInformation.length; i++) {
          var modification = in_orderedModifiedRepositoryInformation[i];
          if (modification.repositoryOperation && !modification.repositoryOperation.from && !modification.deleted) {
            var toKey = modification.repositoryOperation.referenceTargetNonExisting ? modification.repositoryOperation.to.key : '';
            availableLoadingOperations[toKey] = in_orderedModifiedRepositoryInformation;
          }
        }
        // Links the changeSet for the supplied property to the changeSet in the given
        // modifiedRepositoryInformation
        var linkPropertyToModificationInformation = function (property, modifiedRepositoryInformation) {
          if (alreadyUpdatedReferencePropertyGUIDs[property._instanceGuid]) {
            return;
          }
          var rootRepositoryInfo = property.getRoot()._getCheckedOutRepositoryInfo();
          var root = property.getRoot();
          var rootRepositoryModificationInformation = getInNestedObjects(in_modifiedRepositoryInformation, rootRepositoryInfo.repositoryGUID, rootRepositoryInfo._checkoutGroupID, rootRepositoryInfo.commitNode.getGuid());
          var rootRepositoryModificationInformationHasBeenInserted = false;
          if (!rootRepositoryModificationInformation) {
            rootRepositoryModificationInformation = {
              changeset: {},
              repositoryGUID: rootRepositoryInfo.repositoryGUID,
              checkoutGroupID: rootRepositoryInfo._checkoutGroupID,
              commitGUID: rootRepositoryInfo.commitNode.getGuid()
            };
            insertInNestedObjects(in_modifiedRepositoryInformation, rootRepositoryInfo.repositoryGUID, rootRepositoryInfo._checkoutGroupID, rootRepositoryInfo.commitNode.getGuid(), rootRepositoryModificationInformation);
            in_orderedModifiedRepositoryInformation.push(rootRepositoryModificationInformation);
            rootRepositoryModificationInformationHasBeenInserted = true;
          }
          // Add the modification information into the joined changeSet
          // Get the changeset for the reference property
          var insertInfo = Utils.insertPropertyChangeIntoChangeset(property, root, rootRepositoryModificationInformation.changeset, false);
          var nestedChangeSet = insertInfo.propertyChangeSet;
          if (!insertInfo.remove) {
            if (nestedChangeSet.insert || nestedChangeSet.modify || nestedChangeSet.remove) {
              throw new Error('Internal error: There should not be any modifications ' + 'of dynamic entries on the reference property');
            }
            // Insert the operations recursively
            if (modifiedRepositoryInformation.changeset.insert) {
              nestedChangeSet.insert = modifiedRepositoryInformation.changeset.insert;
            }
            if (modifiedRepositoryInformation.changeset.modify) {
              nestedChangeSet.modify = modifiedRepositoryInformation.changeset.modify;
            }
            if (modifiedRepositoryInformation.changeset.remove) {
              nestedChangeSet.remove = modifiedRepositoryInformation.changeset.remove;
            }
          }
          // Mark this property as already updated
          alreadyUpdatedReferencePropertyGUIDs[property._instanceGuid] = true;
          // If we inserted a new modification information, we have to update all references that point to this
          // new modification information recursively
          if (rootRepositoryModificationInformationHasBeenInserted) {
            var checkoutInfo = that._getCheckedOutRepositoryInfo(rootRepositoryInfo.repositoryGUID, rootRepositoryInfo._checkoutGroupID, rootRepositoryInfo.commitNode.getGuid());
            var guids = _.keys(checkoutInfo._referencedByPropertyInstanceGUIDs);
            for (var j = 0; j < guids.length; j++) {
              var referencingCheckoutInfo = checkoutInfo._referencedByPropertyInstanceGUIDs[guids[j]];
              if (referencingCheckoutInfo) {
                var referencingProperty = referencingCheckoutInfo._repositoryReferenceProperties[guids[j]].property;
                linkPropertyToModificationInformation(referencingProperty, rootRepositoryModificationInformation);
              }
            }
          }
        };
        for (var i = 0; i < in_orderedModifiedRepositoryInformation.length; i++) {
          var modification = in_orderedModifiedRepositoryInformation[i];
          if (modification.repositoryOperation) {
            // Loop over all referenced properties that are affected by this operations
            for (var j = 0; j < modification.repositoryOperation.referenceProperties.length; j++) {
              var property = modification.repositoryOperation.referenceProperties[j].property;
              // And link them to the corresponding ChangeSets
              linkPropertyToModificationInformation(property, modification);
            }
          }
          // Handle the removed references
          if (modification.deleted) {
            for (var j = 0; j < modification.removedReferences.length; j++) {
              var property = modification.removedReferences[j].property;
              // And link them to the corresponding ChangeSets
              if (property.getRoot()._getCheckedOutRepositoryInfo()) {
                linkPropertyToModificationInformation(property, modification);
              }
            }
          }
          // Check whether we have inserted reference properties that do not refer to a
          // an operation
          if (modification.changeset && !modification.deleted && !modification.referenceTargetNonExisting) {
            var checkoutInfo = this._getCheckedOutRepositoryInfo(modification.repositoryGUID, modification.checkoutGroupID, modification.commitGUID);
            var insertedReferences = checkoutInfo._insertedRepositoryReferenceProperties;
            var paths = _.keys(insertedReferences);
            for (var j = 0; j < paths.length; j++) {
              var referenceProperty = insertedReferences[paths[j]];
              // Check, whether we have a loading operation for this reference property
              var loadingOperations = availableLoadingOperations[referenceProperty.get('repositoryGUID').value + '_' + referenceProperty.get('branchGUID').value + '_' + referenceProperty.get('commitGUID').value];
              if (loadingOperations) {
                // Link the property to the ChangeSet in the loading operation
                linkPropertyToModificationInformation(referenceProperty, loadingOperations);
              } else {
                // We haven't found an operation for this property, this means we added a reference property to an
                // already existing checkoutView. In that case, we have to serialize the existing repository
                // and create an insert for it
                var referencedRoot = referenceProperty.getReferencedRepositoryRoot();
                if (referencedRoot) {
                  var serializedReferencedRepository = referencedRoot.serialize({
                    'dirtyOnly': false,
                    'includeRootTypeid': false,
                    'dirtinessType': undefined,
                    'includeReferencedRepositories': true
                  });
                  if (serializedReferencedRepository.insert) {
                    // We add an insert instruction for the whole serialized subrepository
                    var insertInfo = Utils.insertPropertyChangeIntoChangeset(referenceProperty, referenceProperty.getRoot(), modification.changeset, false);
                    if (insertInfo.propertyChangeSet) {
                      insertInfo.propertyChangeSet.insert = serializedReferencedRepository.insert;
                    }
                  }
                }
              }
              alreadyUpdatedReferencePropertyGUIDs[referenceProperty._instanceGuid] = true;
              // Delete the references after they have been reported
              checkoutInfo._insertedRepositoryReferenceProperties = {};
            }
          }
        }
      };
      /**
       * Data-structure that contains information about a specific check out state for a given repository
       *
       * @param {string}                                  in_repositoryGUID - The GUID of the repository
       * @param {string}                                  in_checkoutGroupID - Defines a scope which is used to determine
       *                                                                       a group of repository references which
       *                                                                       share a checked out repositories
       *
       * @property {string}                               repositoryGUID - The GUID identifying the checked out repository
       * @property {?LYNX.PropertyGraph.Nodes.CommitNode} commitNode    - The node of the checked out commit
       * @property {?LYNX.PropertyGraph.Nodes.BranchNode} branchNode    - The branch that has been checked out
       * @property {?LYNX.Property.NodeProperty}          repositoryRoot - The root of the checked out properties within
       *                                                                  the repository
       *
       * @constructor
       * @alias LYNX.Property.CheckoutView~CheckedOutRepositoryInfo
       * @private
       */
      CheckoutView.CheckedOutRepositoryInfo = function (in_repositoryGUID, in_checkoutGroupID) {
        this.repositoryGUID = in_repositoryGUID;
        this.commitNode = undefined;
        this.branchNode = undefined;
        this.repositoryRoot = undefined;
        // The scope in which the remote templates will reside
        this.scope = generateGUID();
        this.templates = {};
        this._checkoutGroupID = in_checkoutGroupID;
        /**
         * @type {LYNX.Property.CheckoutView|undefined}
         */
        this.parent = undefined;
        this._unreportedSerializedChangeSet = undefined;
        // Counts how often notifications have been delayed
        this.notificationDelayScope = 0;
        /**
         * Keeps a list of all repository reference properties in this checked out repository
         * @type LYNX.Property.CheckoutView~CheckedOutRepositoryInfo~ReferenceInformation
         */
        this._repositoryReferenceProperties = {};
        /**
         * A lookup table which maps the GUIDs of all the reference property objects that are currently referencing
         * this checked out repository info to the corresponding CheckedOutRepositoryInfo objects
         * @type {{Object<string, LYNX.Property.CheckoutView~CheckedOutRepositoryInfo>}}
         * @private
         */
        this._referencedByPropertyInstanceGUIDs = {};
        /**
         * Is this CheckedOutRepositoryInfo currently performing a checkout
         * @type {Boolean}
         * @private
         */
        this._currentlyCheckingOut = false;
        /**
         * Disables notifications when changes to this CheckedOutRepositoryInfo are performed
         * @type {boolean}
         * @private
         */
        this._disableNotifications = false;
        /**
         * Indicates whether the first modification notification for this CheckedOutRepositoryInfo has already been done
         * @type {boolean}
         * @private
         */
        this._initialCheckout = true;
        /**
         * Keeps track of all repository reference properties that have been inserted since the last time an
         * onModify event has been reported
         *
         * @type {Object.<string, LYNX.Property.RepositoryReferenceProperty>}
         * @private
         */
        this._insertedRepositoryReferenceProperties = {};
        /**
         * Keeps track of reference properties which currently did not have any loaded properties during the last
         * onModify envent (i.e. they were in the EMPTY or FAILED state)
         *
         * @type {Object.<string, LYNX.Property.RepositoryReferenceProperty>}
         * @private
         */
        this._currentlyEmptyRepositoryReferenceProperties = {};
      };
      /**
       * @typedef {Object} LYNX.Property.CheckoutView~CheckedOutRepositoryInfo~ReferenceInformation
       * Stores information about a repository reference property
       *
       * @property {String} path
       *     The path to the repository reference property
       * @property {LYNX.Property.RepositoryReferenceProperty} property
       *     The property object
       * @property {String} repositoryGUID
       *     The GUID of the repository that is currently referenced by this reference property
       * @property {String} branchGUID
       *     The GUID of the branch that is currently referenced by this reference property
       * @property {String} commitGUID
       *     The GUID of the commit that is currently referenced by this reference property
       * @property {String} checkoutGroupID
       *     The ID of the checkout group this reference belongs to
       * @property {String|undefined} oldCommitGUID
       *     The commitGUID of the reference, before it has been updated. This will be set to undefined, once the
       *     corresponding repository updates have been triggered
       * @property {String|undefined} oldBranchGUID
       *     The branchGUID of the reference, before it has been updated. This will be set to undefined, once the
       *     corresponding repository updates have been triggered
       * @property {String|undefined} oldRepositoryGUID
       *     The repositoryGUID of the reference, before it has been updated. This will be set to undefined, once the
       *     corresponding repository updates have been triggered
       * @property {String|undefined} oldCheckoutGroupID
       *     The checkoutGroupID of the reference, before it has been updated. This will be set to undefined, once the
       *     corresponding repository updates have been triggered
       */
      /**
       * Return the scope
       * This is effectively the scope of the underlying property sets.
       * @return {string} The scope of the CheckedOutRepositoryInfo
       */
      CheckoutView.CheckedOutRepositoryInfo.prototype.getScope = function () {
        return this.scope;
      };
      /**
       * Sets the checkout view this CheckedOutRepositoryInfo belongs to
       * @param {LYNX.Property.CheckoutView} in_parent - The parent view
       */
      CheckoutView.CheckedOutRepositoryInfo.prototype.setParent = function (in_parent) {
        this.parent = in_parent;
        // The notification delay scope is managed by the check-out view and shared by all repositories.
        // Therefore, we have to set it to the inherited value, when creating
        // a new repository to keep it consistent among all repositories.
        this.setNotificationDelayScope(this.parent.getNotificationDelayScope());
      };
      /**
       * Adds a reference to a repository reference property that points to this repository
       *
       * @param {LYNX.Property.RepositoryReferenceProperty} in_property - The property that currently points to this
       *     CheckedOutRepositoryInfo
       */
      CheckoutView.CheckedOutRepositoryInfo.prototype.addReferencingRepositoryReferenceProperty = function (in_property) {
        this._referencedByPropertyInstanceGUIDs[in_property._instanceGuid] = in_property.getRoot()._getCheckedOutRepositoryInfo();
      };
      /**
       * Removes a reference to a repository reference property that points to this repository
       *
       * @param {String} in_instanceGuid - The instance GUID of the property for which the reference should be removed
       */
      CheckoutView.CheckedOutRepositoryInfo.prototype.removeReferencingRepositoryReferenceProperty = function (in_instanceGuid) {
        delete this._referencedByPropertyInstanceGUIDs[in_instanceGuid];
      };
      /**
       * Revert the document to a state, given a changeset
       *
       * @param {LYNX.Property.SerializedChangeSet} in_changeSet - The changeset to apply TODO: Should we use the
       *                                                           ChangeSet object here?
       *
       * @return {LYNX.Property.SerializedChangeSet} A changeset with the actually applied changes
       */
      CheckoutView.CheckedOutRepositoryInfo.prototype.revertToState = function (in_changeSet) {
        if (!this.repositoryRoot) {
          // TODO: We preliminary create the repository root here. How has this to be done?
          var params = {};
          params.name = 'Untitled Repository';
          // the only specified property atm
          params.typeid = 'NodeProperty';
          // We have to use the typeid from the changeSet
          params.dynamic = true;
          // all documents are dynamic
          params.id = 'root';
          // TODO: Where should this root property be initialized? How do we define its type and template?
          this.repositoryRoot = new NodeProperty(params);
          this.repositoryRoot._signalAllStaticMembersHaveBeenAdded(this.getScope());
          // Set checkout view-related info on the root property. Among other things, this defines the scope
          // for child property creation/deserialization.
          this.repositoryRoot._setCheckedOutRepositoryInfo(this);
        }
        // Before we do the deserialization, we store all yet unreported dirty changes
        var serializedDirtyEntries = new ChangeSet(this.repositoryRoot._serialize(true, false, BaseProperty.MODIFIED_STATE_FLAGS.DIRTY));
        // We do not want to have any notifications during the actual deserialization operations. We will do that
        // later
        this.parent.pushNotificationDelayScope();
        this._registerRemoteTemplates(in_changeSet.insertTemplates);
        // Apply changes to the root property
        // We use this flag, to enable modifications of properties in the referenced repositories,
        // which are forbidden outside of a checkout.
        this._currentlyCheckingOut = true;
        if (this._initialChangeSet) {
          this.repositoryRoot.deserialize(this._initialChangeSet);
          this.repositoryRoot.cleanDirty();
          this._initialChangeSet = undefined;
        }
        var actualChanges = this.repositoryRoot.deserialize(in_changeSet);
        this._currentlyCheckingOut = false;
        // Trigger a modified event
        serializedDirtyEntries.applyChangeSet(actualChanges);
        if (!_.isEmpty(serializedDirtyEntries.getSerializedChangeSet())) {
          // We store the changes that were done during the deserialization in the _unreportedSerializedChangeSet.
          // These will be reported to the application the next time the notification delay scope reaches 0.
          // Note: Just storing the change here is ok.
          //       The scope here cannot be 0, since this code is bracketed by push/pop calls.
          if (this._unreportedSerializedChangeSet) {
            this._unreportedSerializedChangeSet.applyChangeSet(serializedDirtyEntries);
          } else {
            this._unreportedSerializedChangeSet = serializedDirtyEntries;
          }
        }
        // Make sure everything is clean again. After the reset all changes have been reverted
        this.repositoryRoot.cleanDirty();
        this.repositoryRoot._freeCache();
        this.parent.popNotificationDelayScope();
        return actualChanges;
      };
      /**
       * Removes all unreported changes from this CheckoutRepositoryInfo object. On the next popScope
       * it won't report any dirty entries.
       * @private
       */
      CheckoutView.CheckedOutRepositoryInfo.prototype._cleanAllChanges = function () {
        this.repositoryRoot.cleanDirty(BaseProperty.MODIFIED_STATE_FLAGS.DIRTY);
        this._unreportedSerializedChangeSet = undefined;
      };
      /**
       * Register remote templates
       * We first remove all templates from PropertyFactory registered within the scope and reregister them
       * @param {Array<object>} in_remoteTemplates List of remote templates to register
       * @private
       */
      CheckoutView.CheckedOutRepositoryInfo.prototype._registerRemoteTemplates = function (in_remoteTemplates) {
        PropertyFactory._removeScope(this.scope);
        this.templates = {};
        this._registerAdditionalRemoteTemplates(in_remoteTemplates);
      };
      /**
       * Register remote templates and add them to the existing ones
       *
       * @param {Array<object>} in_remoteTemplates List of remote templates to register
       * @private
       */
      CheckoutView.CheckedOutRepositoryInfo.prototype._registerAdditionalRemoteTemplates = function (in_remoteTemplates) {
        var that = this;
        _.each(in_remoteTemplates, function (template) {
          PropertyFactory._registerRemoteTemplate(template, that.scope);
          that.templates[template.typeid] = template;
        });
      };
      /**
       * Applies the changes from the supplied change set to the checked out state
       *
       * @param {LYNX.Property.SerializedChangeSet} in_changeSet - The changeset to apply TODO: Should we use the
       *                                                           ChangeSet object here?
       */
      CheckoutView.CheckedOutRepositoryInfo.prototype.applyChangeset = function (in_changeSet) {
        // Modify can only be used, when there is already a repository checked out
        if (!this.repositoryRoot) {
          throw Error(MSG.NO_ROOT_IN_MODIFY);
        }
        if (in_changeSet.insertTemplates) {
          this._registerAdditionalRemoteTemplates(in_changeSet.insertTemplates);
        }
        // TODO: How do we want to handle this, once we have multiple repositories?
        this.parent.pushNotificationDelayScope();
        this.repositoryRoot.applyChangeset(in_changeSet);
        this.parent.popNotificationDelayScope();
      };
      /**
       * Checks whether the CheckedOutRepositoryInfo has any pending changes
       * @return {boolean} Are there changes?
       */
      CheckoutView.CheckedOutRepositoryInfo.prototype.hasPendingChanges = function () {
        return this.repositoryRoot.hasPendingChanges();
      };
      /**
       * Get all dirty non-root items of this CheckedOutRepositoryInfo
       * @deprecated
       *
       * @param {boolean} [in_cleanDirty] - optional argument to also clean all of the visited paths.
       *
       * @return {Object} the dirty items
       */
      CheckoutView.CheckedOutRepositoryInfo.prototype.getDirtyItems = function (in_cleanDirty) {
        console.warn('CheckedOutRepositoryInfo.getDirtyItems: Deprecated function');
        in_cleanDirty = !!in_cleanDirty;
        return this.repositoryRoot.getDirtyItems(in_cleanDirty);
      };
      /**
       * Get the list of pending changes on this head
       * @return {{}} a variant of a changeset, representing the pending changes
       * in terms of guids. This is useful when we are not interested in the actual
       * changes, but in knowing the list of things that have changed so far.
       */
      /* CheckoutView.CheckedOutRepositoryInfo.prototype.listPendingChanges = function() {

          var pending = this._pendingChanges;

          var rtn = {};
          rtn.add = Object.keys( pending.add );
          rtn.remove = Object.keys( pending.remove );
          rtn.modify = [];

          var mod = this._getDirtyChildren();

          // cleanup the 'modify' to ensure that it is either added or modified, not both.
          for ( var i = 0; i < mod.length; i++ ) {
            if ( !pending.add[mod[i]] ) {
              rtn.modify.push( mod[i] );
            }
          }

          if ( rtn.add.length + rtn.remove.length + rtn.modify.length === 0 ) {
            return EMPTY_HEAD;
          }

          return rtn;
        };*/
      /**
       * Delays notifications until setNotificationDelyaScope has been called with a 0 argument
       * pushNotificationDelayScope.
       * @param {number} in_counter The number to set the notification delay scope variable to
       */
      CheckoutView.CheckedOutRepositoryInfo.prototype.setNotificationDelayScope = function (in_counter) {
        // set the scope counter
        this.notificationDelayScope = in_counter;
        // If we reach 0, we have to report unreported changes
        if (this.notificationDelayScope === 0) {
          this._triggerUnreportedModifyMessages();
        }
      };
      /**
       * Returns a serialized list of the changes in the CheckedOutRepositoryInfo
       *
       * @return {LYNX.Property.ChangeSet} The serialized changes
       */
      CheckoutView.CheckedOutRepositoryInfo.prototype.serializePendingChanges = function () {
        var serialized = this.repositoryRoot._serialize(true, false, BaseProperty.MODIFIED_STATE_FLAGS.PENDING_CHANGE);
        var typeids = Property_Utils.extractTypeids(serialized);
        var unknownTemplates = this._gatherUnknownTemplates(typeids);
        if (!_.isEmpty(unknownTemplates)) {
          serialized.insertTemplates = unknownTemplates;
        }
        return new ChangeSet(serialized);
      };
      /**
       * Recursively traverse the tempate in order to gather all nested typeids
       * @param {object} in_template The template to recursively gather typeids from
       * @return {array} List of typeids
       * @private
       */
      var _gatherNestedPropertyTypeids = function (in_template) {
        var typeids = new Set();
        _.each(in_template._properties, function (property) {
          if (property.typeid) {
            typeids.add(property.typeid);
          }
          _.each(_gatherNestedPropertyTypeids(property), function (typeid) {
            typeids.add(typeid);
          });
        });
        return _.compact(Array.from(typeids));
      };
      /**
       * Gather unknown templates by traversing the list of typeids, fetching the templates and recursively
       * traverse its ancestors along with the sub properties.
       * @param {Array<string>} in_typeids The list of typeids to traverse
       * @param {Object=} in_gatheredTemplates A running set of all gathered unknown templates
       * @return {object} Key/value map containing all unknown templates
       */
      CheckoutView.CheckedOutRepositoryInfo.prototype._gatherUnknownTemplates = function (in_typeids, in_gatheredTemplates) {
        var that = this;
        var unknownTemplates = in_gatheredTemplates || {};
        // Gather all unknown templates and store them in the ChangeSet
        _.each(in_typeids, function (typeid) {
          typeid = TypeIdHelper.extractContext(typeid).typeid;
          // check for reference typeid
          // if it is a refernce typeid
          //  extract typeid of what is referenced
          if (TypeIdHelper.isReferenceTypeId(typeid)) {
            typeid = TypeIdHelper.extractReferenceTargetTypeIdFromReference(typeid);
          }
          if (!that.templates[typeid] && !TypeIdHelper.isPrimitiveType(typeid) && !TypeIdHelper.isReservedType(typeid) && !unknownTemplates[typeid] && PropertyFactory._get(typeid)) {
            unknownTemplates[typeid] = PropertyFactory._get(typeid).serialize();
            // Include nested properties
            var subPropertyTypeIds = _gatherNestedPropertyTypeids(unknownTemplates[typeid]);
            var parentTypeIds = PropertyFactory.getAllParentsForTemplate(typeid);
            that._gatherUnknownTemplates(subPropertyTypeIds, unknownTemplates);
            that._gatherUnknownTemplates(parentTypeIds, unknownTemplates);
          }
        });
        return unknownTemplates;
      };
      /**
       * Returns a serialized representation of the content of this CheckedOutRepositoryInfo
       *
       * @return {LYNX.Property.ChangeSet} The serialized repository
       */
      CheckoutView.CheckedOutRepositoryInfo.prototype.serialize = function () {
        var serialized = this.repositoryRoot._serialize(false, true);
        return new ChangeSet(serialized);
      };
      /**
       * This function is invoked by its own root property, when any of the properties in the hierarchy has been dirtied.
       * @protected
       */
      CheckoutView.CheckedOutRepositoryInfo.prototype._propertyDirtied = function () {
        if (this.notificationDelayScope === 0) {
          this._triggerUnreportedModifyMessages();
        }
      };
      /**
       * Invokes the modified trigger, when there are still unreported modifications in this CheckedOutRepositoryInfo
       * @private
       */
      CheckoutView.CheckedOutRepositoryInfo.prototype._triggerUnreportedModifyMessages = function () {
        if (this._disableNotifications) {
          return;
        }
        var serializedDirtyEntries = this.repositoryRoot._serialize(true, false, BaseProperty.MODIFIED_STATE_FLAGS.DIRTY);
        if (this._unreportedSerializedChangeSet) {
          // If there are yet unreported changes from the last deserialization,
          // we have to report them together with the currently active ChangeSet.
          // In that case, those have to be applied before the new changes.
          this._unreportedSerializedChangeSet.applyChangeSet(serializedDirtyEntries);
          serializedDirtyEntries = this._unreportedSerializedChangeSet;
          this._unreportedSerializedChangeSet = undefined;
        } else {
          serializedDirtyEntries = new ChangeSet(serializedDirtyEntries);
        }
        if (ChangeSet.isEmptyChangeSet(serializedDirtyEntries.getSerializedChangeSet()) && !this._initialCheckout) {
          return;
        }
        // Perform an update of the repository references in this checkout view
        var repositoryReferencesChanged = this._updateRepositoryReferences(serializedDirtyEntries) || this._initialCheckout;
        // After the initial checkout, we always have to update
        // the repository references
        this._initialCheckout = false;
        if (!_.isEmpty(serializedDirtyEntries.getSerializedChangeSet())) {
          // Since we have reported the dirtiness to the client
          this.repositoryRoot.cleanDirty(BaseProperty.MODIFIED_STATE_FLAGS.DIRTY);
        }
        // Create a structure describing the modification to the checked out repository
        var modificationInformation = {
          changeset: serializedDirtyEntries.getSerializedChangeSet(),
          repositoryGUID: this.repositoryGUID,
          commitGUID: this.commitNode.getGuid(),
          checkoutGroupID: this._checkoutGroupID
        };
        if (this.parent._updateOperationStack.length > 0) {
          // Add information if this has been a repository operation (e.g. a repository reference has been changed)
          modificationInformation.repositoryOperation = this.parent._updateOperationStack[this.parent._updateOperationStack.length - 1];
        }
        // Store the information about the modification to notify the user later
        var alreadyExisting = insertInNestedObjects(this.parent._modifiedRepositoryInformation, this.repositoryGUID, this._checkoutGroupID, this.commitNode.getGuid(), modificationInformation);
        this.parent._orderedModifiedRepositoryInformation.push(modificationInformation);
        if (alreadyExisting && modificationInformation.repositoryOperation && !modificationInformation.repositoryOperation.mergeRequired) {
          throw new Error('Multiple modifications for the same CheckedOutRepositoryInfo');
        }
        if (repositoryReferencesChanged) {
          this.parent._updateReferencedRepositories();
        } else {
          this.parent._triggerUnreportedModifyMessages();
        }
      };
      /**
       * Checks, whether this is the root of the checkouts in the checkout view
       *
       * @return {boolean} Is this the root?
       * @protected
       */
      CheckoutView.CheckedOutRepositoryInfo.prototype._isReadOnly = function () {
        // Is this the parent repository of the checkout tree?
        return !this._currentlyCheckingOut && this.parent._parentRepository && this.parent._parentRepository !== this;
      };
      /**
       * Updates the repository references within the repository according to the given ChangeSet
       *
       * @param {LYNX.Property.ChangeSet} in_changeSet - The ChangeSet that describes the modifications to this buffer
       * @return {boolean} Has the list of referenced repositories been changed?
       * @private
       */
      CheckoutView.CheckedOutRepositoryInfo.prototype._updateRepositoryReferences = function (in_changeSet) {
        var that = this;
        // Retrieve all modified RepositoryReference objects
        var modifiedRepositoryReferences = Utils.getChangesByType('RepositoryReferenceProperty', in_changeSet.getSerializedChangeSet(), false);
        // Has there been any change to the repositories?
        var changed = false;
        // Removes an old repository reference entry
        var removeRepositoryReferenceEntry = function (instanceGuid) {
          // If there is an old entry for this reference, we have to remove the reference from that
          // repository
          if (that._repositoryReferenceProperties[instanceGuid]) {
            var oldReference = that._repositoryReferenceProperties[instanceGuid];
            var oldCheckedOutRepository = that.parent._getCheckedOutRepositoryInfo(oldReference.repositoryGUID, oldReference.commitGUID);
            if (oldCheckedOutRepository) {
              oldCheckedOutRepository.removeReferencingRepositoryReferenceProperty(instanceGuid);
            }
          }
          delete that._repositoryReferenceProperties[instanceGuid];
        };
        // Updates repository entries in the referenceProperties list
        var updateRepositoryEntry = function (changeSet, path, inserted) {
          var referenceProperty = that.repositoryRoot.resolvePath(path);
          // If the property has been assigned from an empty state, we treat this as an insertion
          if (that._currentlyEmptyRepositoryReferenceProperties[referenceProperty._instanceGuid]) {
            inserted = true;
            delete that._currentlyEmptyRepositoryReferenceProperties[referenceProperty._instanceGuid];
          }
          // Preserve the previous state of the reference to enable tracking of modifications
          var oldCommit, oldBranch, oldRepository, oldCheckoutGroupID;
          if (that._repositoryReferenceProperties[referenceProperty._instanceGuid]) {
            // TODO: create a test that this tracking of oldCommits works over multiple changes
            var oldPropertyInfo = that._repositoryReferenceProperties[referenceProperty._instanceGuid];
            if (oldPropertyInfo.oldCommitGUID) {
              // The entries in the reference have been changed, but we still have an old state stored for
              // the reference. This code will cause the original old state to be preserved, since we want
              // to load from that state and not the intermediate state that has not yet been taken effect. However,
              // if the state has been set back to the original state, we will just reset the old state variables
              // since we no longer need any loading operation.
              if (referenceProperty._properties.commitGUID.value === oldPropertyInfo.oldCommitGUID && referenceProperty._properties.branchGUID.value === oldPropertyInfo.oldBranchGUID && referenceProperty._properties.repositoryGUID.value === oldPropertyInfo.oldRepositoryGUID) {
                oldCommit = undefined;
                oldBranch = undefined;
                oldRepository = undefined;
                oldCheckoutGroupID = undefined;
              } else {
                oldCommit = oldPropertyInfo.oldCommitGUID;
                oldBranch = oldPropertyInfo.oldBranchGUID;
                oldRepository = oldPropertyInfo.oldRepositoryGUID;
                oldCheckoutGroupID = oldPropertyInfo.oldCheckoutGroupID;
              }
            } else {
              oldCommit = oldPropertyInfo.commitGUID;
              oldBranch = oldPropertyInfo.branchGUID;
              oldRepository = oldPropertyInfo.repositoryGUID;
              oldCheckoutGroupID = oldPropertyInfo.checkoutGroupID;
            }
          }
          // Remove an old entry if it exists
          removeRepositoryReferenceEntry(referenceProperty._instanceGuid);
          console.assert(referenceProperty);
          that._repositoryReferenceProperties[referenceProperty._instanceGuid] = {
            path: path,
            property: referenceProperty,
            repositoryGUID: referenceProperty._properties.repositoryGUID.value,
            branchGUID: referenceProperty._properties.branchGUID.value,
            commitGUID: referenceProperty._properties.commitGUID.value,
            checkoutGroupID: referenceProperty._properties.branchGUID.value,
            oldCommitGUID: oldCommit,
            oldBranchGUID: oldBranch,
            oldRepositoryGUID: oldRepository,
            oldCheckoutGroupID: oldCheckoutGroupID
          };
          // If the CheckedOutContainerInfo for this reference property already exists, we have to add a new
          // reference
          var checkedOutRepository = that.parent._getCheckedOutRepositoryInfo(referenceProperty._properties.repositoryGUID.value, referenceProperty._properties.commitGUID.value);
          if (checkedOutRepository) {
            checkedOutRepository.addReferencingRepositoryReferenceProperty(referenceProperty);
          }
          if (inserted) {
            that._insertedRepositoryReferenceProperties[path] = referenceProperty;
          }
          changed = true;
        };
        if (modifiedRepositoryReferences.insert) {
          _.each(modifiedRepositoryReferences.insert, function (changeSet, path) {
            updateRepositoryEntry(changeSet, path, true);
          });
        }
        if (modifiedRepositoryReferences.modify) {
          _.each(modifiedRepositoryReferences.modify, function (changeSet, path) {
            updateRepositoryEntry(changeSet, path, false);
          });
        }
        // Check whether any of the properties has to be deleted and rebuild the
        // repositories and commits that are actually referenced by this commit.
        // TODO: this is rather inefficient. We should instead probably check for deletes
        // in the ChangeSet but that is more difficult
        var guids = _.keys(this._repositoryReferenceProperties);
        for (var i = 0; i < guids.length; i++) {
          var property = this._repositoryReferenceProperties[guids[i]].property;
          // Check whether the property has been deleted from this repository
          if (property.getRoot() !== this.repositoryRoot) {
            // Remove it from the list of newly inserted reference properties
            delete this._insertedRepositoryReferenceProperties[this._repositoryReferenceProperties[guids[i]].path];
            removeRepositoryReferenceEntry(guids[i]);
            changed = true;
          }
        }
        return changed;
      };
      /**
       * Returns the shortest path from the root of the CheckOut view to this checked out repository's root node
       *
       * There can be multiple different paths to this repository, in which case we guarantee that this is the shortest one
       * (in regard to the number of hops), but we do not restrict which one, if there are multiple paths of the same
       * length, will be returned
       *
       * @returns {string} The path from the root of the checkout view to the root of the checked out repository
       * @private
       */
      /* CheckoutView.CheckedOutRepositoryInfo.prototype._getShortestPathFromViewRoot = function() {
        throw new Error('Not yet implemented');
        return '';
      };*/
      exportModule('LYNX.Property.CheckoutView', CheckoutView);
    }());
    (function () {
      var Graph = include('LYNX.Utils.Graph.Graph');
      var Collection = include('LYNX.Utils.Datastructures.Collection');
      var _ = include('underscore');
      /**
       * Stores the commit graph locally
       *
       * @alias LYNX.PropertyGraph.CommitGraph
       * @constructor
       * @private
       */
      var CommitGraph = function () {
        Graph.call(this);
        this._nodesByTypeid = new Collection('typeid indexed nodes', Collection);
        // a 2D collection
        var self = this;
        this._masterBranch = undefined;
        // will be done in init()
        self._nodesByTypeid.add('BranchNodeID', new Collection('BranchNodeID'));
        self._nodesByTypeid.add('CommitNodeID', new Collection('CommitNodeID'));
        this.register('onAdd', function (in_uid, in_node) {
          var typeid = in_node.getTypeid();
          if (self._nodesByTypeid.item(typeid)) {
            self._nodesByTypeid.item(typeid).add(in_uid, in_node);
          }
        });
        this.register('onRemove', function (in_node) {
          var typeid = in_node.getTypeid();
          if (self._nodesByTypeid.item(typeid)) {
            self._nodesByTypeid.item(typeid).remove(in_node.getGuid());
          }
        });
      };
      CommitGraph.prototype = Object.create(Graph.prototype);
      CommitGraph.prototype.getBranchNodes = function () {
        return this._nodesByTypeid.item('BranchNodeID').getItems();
      };
      CommitGraph.prototype.getBranchNode = function (in_guid) {
        return this._nodesByTypeid.item('BranchNodeID').item(in_guid);
      };
      CommitGraph.prototype.getBranches = function () {
        return this._nodesByTypeid.item('BranchNodeID').getKeys();
      };
      CommitGraph.prototype.getCommitNodes = function () {
        return this._nodesByTypeid.item('CommitNodeID').getItems();
      };
      CommitGraph.prototype.getCommitNode = function (in_guid) {
        return this._nodesByTypeid.item('CommitNodeID').item(in_guid);
      };
      CommitGraph.prototype.getCommitGuids = function () {
        return this._nodesByTypeid.item('CommitNodeID').getKeys();
      };
      CommitGraph.prototype.has = function (in_commitGuid) {
        return this._nodesByTypeid.item('CommitNodeID').has(in_commitGuid);
      };
      /**
       * Delete all commits that only belong to a single branch
       * @param  {LYNX.PropertyGraph.Nodes.CommitNode} in_parentNode - The node which will be removed (up to the next
       *                                                               branching point)
       */
      CommitGraph.prototype._deleteBranchCommits = function (in_parentNode) {
        var deletionStack = [in_parentNode];
        while (!_.isEmpty(deletionStack)) {
          var parentNode = deletionStack.pop();
          var children = parentNode.getChildren();
          var childrenNodeGUIDs = _.invoke(children, 'getGuid');
          if (childrenNodeGUIDs.length <= 1) {
            var grandparentNodes = parentNode.getParents();
            _.each(grandparentNodes, function (grandparentNode) {
              deletionStack.push(grandparentNode);
            });
            if (!parentNode.isRoot()) {
              // TODO: There is a bug where a commit references another commit that does not exist in the graph
              // FIX THIS.
              if (this.has(parentNode.getGuid())) {
                this.remove(parentNode.getGuid());
              }
            }
          }
        }
      };
      /**
       * Given a branch GUID, remove the branch and associated commits from our internal state
       * @param  {string} in_branchGuid - The branch GUID to remove
       */
      CommitGraph.prototype.removeBranch = function (in_branchGuid) {
        var branchNode = this.getBranchNode(in_branchGuid);
        this._deleteBranchCommits(branchNode.getHead());
        this.remove(in_branchGuid);
      };
      /**
       * Returns the branches that have the given commit as HEAD
       *
       * @param {string} in_commitGuid - The GUID of the commit on which we are looking for branches
       * @return {Array.<LYNX.PropertyGraph.Nodes.BranchNode>} The branches
       */
      CommitGraph.prototype.findBranchesOnCommit = function (in_commitGuid) {
        var foundBranches = [];
        var allBranches = this.getBranchNodes();
        var branchNames = _.keys(allBranches);
        for (var i = 0; i < branchNames.length; i++) {
          if (allBranches[branchNames[i]].getHead().getGuid() === in_commitGuid) {
            foundBranches.push(allBranches[branchNames[i]]);
          }
        }
        return foundBranches;
      };
      exportModule('LYNX.PropertyGraph.CommitGraph', CommitGraph);
    }());
    (function () {
      /**
       * @readonly
       * @enum {number}
       * @alias LYNX.Property.RepositoryState
       */
      var RepositoryState = {
        INIT: 0,
        LOADING: 1,
        LOADED: 2,
        UPDATING: 3,
        EDITING: 4,
        SAVING: 5
      };
      exportModule('LYNX.Property.RepositoryState', RepositoryState);
    }());
    (function () {
      var EventEmitter = include('LYNX.Utils.Events.EventEmitter');
      var generateGUID = include('LYNX.Utils.generateGUID');
      var CommitNode = include('LYNX.PropertyGraph.Nodes.CommitNode');
      var BranchNode = include('LYNX.PropertyGraph.Nodes.BranchNode');
      var CommitGraph = include('LYNX.PropertyGraph.CommitGraph');
      var RepositoryState = include('LYNX.Property.RepositoryState');
      var CheckoutView = include('LYNX.Property.CheckoutView');
      var ChangeSet = include('LYNX.Property.ChangeSet');
      var _ = include('underscore');
      var deepCopy = include('LYNX.Utils.deepCopy');
      var LRUCache = include('LYNX.Utils.Datastructures.LRUCache');
      var UrnUtils = include('LYNX.Utils.UrnUtils');
      var MSG = {
        CANNOT_COMMIT_TO: 'Cannot commit to: ',
        NOT_DOC_ENTITY: 'NOT a document entity',
        DOC_MODIFY: 'Repository MODIFY : ',
        NOTHING_TO_COMMIT: 'Nothing to commit',
        OBJECT_DOES_NOT_EXIST: 'The object does not exist in the document',
        CANNOT_MERGE_TO_DETACHED_HEAD: 'Cannot merge to detached head',
        MODIFY_ERROR_INPUT_FORMAT: 'Repository.modify() must be called with add ' + 'remove or modify in the format ' + '\n { add : {guid:BaseProperty, remove : [guid], modify: {guid:BaseProperty}} }',
        NO_CHECKOUT_VIEW: 'No checkout view has been supplied.',
        NO_COMMIT_IN_BRANCH: 'No commit given in branch operation',
        NO_BRANCH_IN_RESET: 'No valid branch given in resetBranch operation',
        NO_COMMIT_IN_RESET: 'No valid commit given in resetBranch operation',
        CANNOT_REBASE_DETACHED_HEAD: 'Rebase not possible for a CheckOutView in a detached head state',
        INVALID_ONTO_IN_REBASE: 'Invalid onto commit in rebase specified',
        INVALID_REBASE_BRANCH: 'Invalid rebase branch specified',
        NON_EXISTING_NODE_IN_CACHE: 'Cache contained a non existing node',
        UNBALANCED_CACHE_UPDATES: 'Unbalanced inserts and removes for the commit cache'
      };
      var DETACHED_HEAD = 'DETACHED HEAD';
      /**
       * @typedef {LYNX.PropertyGraph.Nodes.CommitNode|
       *           LYNX.PropertyGraph.Nodes.BranchNode|
       *           string} LYNX.Property.CommitOrBranchIdentifier
       *
       * Identifies a specific commit.
       *
       * If a commit node is directly given, that commit will be used. If a branch node is given the HEAD commit of that
       * branch will be used. If a string is supplied, the corresponding branch or commit node will be resolved.
       */
      /**
       * @typedef {LYNX.PropertyGraph.Nodes.BranchNode|string} LYNX.Property.BranchIdentifier
       *
       * Identifies a specific branch.
       *
       * If a branch node is given that branch is used. If a string is supplied, the corresponding branch node will be
       * resolved.
       */
      /**
       * @typedef {LYNX.PropertyGraph.Nodes.CommitNode|string} LYNX.Property.CommitIdentifier
       *
       * Identifies a specific commit.
       *
       * If a string is supplied, the corresponding commit node will be resolved.
       */
      /**
       * The repository object.
       * Property are the core data structure for managing changes, persistency and
       * collaboration in the Lagoa framework. Property are structured via properties
       * and simultaneously Property are properties themselves. Property can contain
       * references to as many properties as they like, and may store all of it's change
       * states. Property are reversable to a state, and are atomically changed via
       * commits, branches and merge actions.
       * @param {object} in_params List of parameters
       * @param {string=} in_params.name the name of this repository.
       * @param {string=} in_params.creatorId The oxygen user ID of the person who creates the repository
       * @param {string} in_params.guid the guid of the repository
       * @param {number=} [in_params.commitCacheSize = 5] the size of the cache for old commits
       * @constructor
       * @alias LYNX.Property.Repository
       */
      var Repository = function (in_params) {
        in_params = in_params || {};
        EventEmitter.call(this);
        this._state = RepositoryState.INIT;
        this._name = in_params.name;
        this._commitGraph = new CommitGraph();
        // GUID of the repository. TODO: Who is responsible for assigning this?
        this._guid = in_params.guid || generateGUID();
        this._urn = UrnUtils.repoUrn(this._guid);
        this._commitCache = {
          LRU: new LRUCache(),
          cachedCommits: {},
          size: in_params.commitCacheSize !== undefined ? in_params.commitCacheSize : 5
        };
        this._metadata = {};
        // @type Object
        this._setMetadata('creatorId', in_params.creatorId);
      };
      Repository.prototype = Object.create(EventEmitter.prototype);
      Repository.DETACHED_HEAD = DETACHED_HEAD;
      /**
       * @param {String} in_key Get metadata on this node with a key
       * @return {*} Whatever metadata was stored
       * @function getMetadata
       * @instance
       * @memberof LYNX.Property.Repository
       */
      Repository.prototype.getMetadata = function (in_key) {
        return this._metadata[in_key];
      };
      /**
       * Set metadata on this node with a key and a value
       * @param {String} in_key the key
       * @param {*} in_value the value
       * @return {*} returns the set value
       */
      Repository.prototype._setMetadata = function (in_key, in_value) {
        this._metadata[in_key] = in_value;
        return in_value;
      };
      /**
       * Sets the size of the commit cache
       * @param {Number} in_size - The new size of the commit cache
       */
      Repository.prototype._setCacheSize = function (in_size) {
        this._commitCache.size = in_size;
        this._trimCache();
      };
      /**
       * Sets the size of the commit cache
       * @return {number} The cache size
       */
      Repository.prototype._getCacheSize = function () {
        return this._commitCache.size;
      };
      /**
       * Init the master branch
       *
       */
      Repository.prototype._initMaster = function () {
        var rootGuid = generateGUID();
        var firstCommit = new CommitNode(rootGuid);
        firstCommit._setMetadata('name', 'root');
        this._commitGraph.add(rootGuid, firstCommit);
        this._setRootNode(firstCommit);
        var rootBranch = this._branch({ name: 'root' }, firstCommit);
        var masterBranch = this._branch({ name: 'master' }, firstCommit);
        // Trigger the onAddCommitNode since we are adding a new commit to the graph
        this.trigger('onAddCommitNode', this, [firstCommit]);
        this.trigger('branchMoved', this, [
          this,
          rootBranch,
          firstCommit
        ]);
        this.trigger('branchMoved', this, [
          this,
          masterBranch,
          firstCommit
        ]);
      };
      /**
       * Returns the branch node that corresponds to the identifier.
       *
       * @param {LYNX.Property.BranchIdentifier} in_branchIdentifier - Identifies the branch to return
       *
       * @return {LYNX.PropertyGraph.Nodes.BranchNode} - The resolved branch node
       * @function getBranch
       * @instance
       * @memberof LYNX.Property.Repository
       */
      Repository.prototype.getBranch = function (in_branchIdentifier) {
        // Resolve a string if necessary
        var node = _.isString(in_branchIdentifier) ? this._commitGraph.getNodeByUid(in_branchIdentifier) : in_branchIdentifier;
        if (node instanceof BranchNode) {
          // If it is a branch, we have to return the head
          return node;
        } else {
          return undefined;
        }
      };
      /**
       * Returns the branch with the given name
       * @param  {string} in_branchName The name of the branch
       * @return {LYNX.PropertyGraph.Nodes.BranchNode} the branch node
       * @function getBranchByName
       * @instance
       * @memberof LYNX.Property.Repository
       */
      Repository.prototype.getBranchByName = function (in_branchName) {
        var brancheNodes = this._getCommitGraph().getBranchNodes();
        return _.find(brancheNodes, function (branchNode) {
          return branchNode.getName() === in_branchName;
        });
      };
      /**
       * Get the list of BranchNodes from the graph
       * @param {object} in_options List of options
       * @param {boolean=} in_options.array (optional) Flag used to control the return
       *  type of the function. If set to true, the return value will be an Array
       * @return {object|Array} List of branch nodes
       * @function getBranchNodes
       * @instance
       * @memberof LYNX.Property.Repository
       */
      Repository.prototype.getBranchNodes = function (in_options) {
        in_options = in_options || {};
        var branchNodes = this._commitGraph.getBranchNodes();
        if (in_options.array) {
          branchNodes = _.values(branchNodes);
        }
        return branchNodes;
      };
      /**
       * Return the guid of the repository
       * @return {string} Guid of the repository
       * @function getGuid
       * @instance
       * @memberof LYNX.Property.Repository
       */
      Repository.prototype.getGuid = function () {
        return this._guid;
      };
      /**
       * Return the Urn of the repository
       * @return {string} Urn of the repository
       * @function getUrn
       * @instance
       * @memberof LYNX.Property.Repository
       */
      Repository.prototype.getUrn = function () {
        return this._urn;
      };
      /**
       * Returns the commit node that corresponds to the given identifier.
       *
       * @param {LYNX.Property.CommitIdentifier} in_commitIdentifier - Identifier for the commit node
       *
       * @return {LYNX.PropertyGraph.Nodes.CommitNode|undefined} The commit node or undefined if none was found
       * @function getCommit
       * @instance
       * @memberof LYNX.Property.Repository
       */
      Repository.prototype.getCommit = function (in_commitIdentifier) {
        // Resolve a string if necessary
        var node = _.isString(in_commitIdentifier) ? this._commitGraph.getNodeByUid(in_commitIdentifier) : in_commitIdentifier;
        if (node instanceof CommitNode) {
          // We can directly return a commit
          return node;
        } else if (node instanceof BranchNode) {
          // If it is a branch, we have to return the head
          return node.getHead();
        } else {
          return undefined;
        }
      };
      /**
       * Create a branch from in_parentCommit or the current commitNode.
       * If no in_parentCommit is specified, the new branch will have the current
       * active commit as it's parent.
       * @protected
       *
       * @param {object} in_branchMetaData The branch metadata
       * @param {string} [in_branchMetaData.name] The human readable name of the branch.
       *  If not specified, it defaults to the guid.
       * @param {string} [in_branchMetaData.guid] The guid of the branch. If not specified, a guid will be generated
       * @param {LYNX.PropertyGraph.Nodes.CommitNode=} in_parentCommit Optional param to specify which
       * commit Node we want to branch from.
       * @param {Object} in_options - The options
       * @param {boolean} [in_options.trackRemoteBranch] - Flag to track the remote branch.
       *  NOTE: This results in a BranchNode (remote) to be crated and linked to the new BranchNode (local)
       * @return {LYNX.PropertyGraph.Nodes.BranchNode} The created branch node
       */
      Repository.prototype._branch = function (in_branchMetaData, in_parentCommit, in_options) {
        in_options = in_options || {};
        var parentCommit = this.getCommit(in_parentCommit);
        if (!parentCommit) {
          // get current head
          throw new Error(MSG.NO_COMMIT_IN_BRANCH);
        }
        in_branchMetaData = in_branchMetaData || {};
        _.defaults(in_branchMetaData, { guid: generateGUID() });
        var newBranch = new BranchNode(in_branchMetaData, parentCommit);
        if (in_options.trackRemoteBranch) {
          var remoteBranch = new BranchNode(_.extend({}, in_branchMetaData, {
            name: newBranch.getName() + ' (remote)',
            remote: true
          }), parentCommit);
          newBranch._setRemoteBranch(remoteBranch);
        }
        this._commitGraph.add(newBranch.getGuid(), newBranch);
        this.trigger('onBranch', this, [newBranch]);
        return newBranch;
      };
      /**
       * Sets a branch to point to the given commit
       * @protected
       *
       * @param {LYNX.Property.BranchIdentifier}         in_branch  - The branch to update
       * @param {LYNX.Property.CommitOrBranchIdentifier} in_commit  - The commit to which the branch is set
       * @param {Boolean}                                [in_noBranchMovedEvent = false] - Should this function trigger
       *                                                                                   a branch moved event? If set to
       *                                                                                   true, the event will not be
       *                                                                                   triggered
       */
      Repository.prototype._resetBranch = function (in_branch, in_commit, in_noBranchMovedEvent) {
        var branch = this.getBranch(in_branch);
        var commit = this.getCommit(in_commit);
        var oldBranchHead = branch.getHead();
        // Make sure input is valid
        if (!commit) {
          throw new Error(MSG.NO_COMMIT_IN_RESET);
        }
        if (!branch) {
          throw new Error(MSG.NO_BRANCH_IN_RESET);
        }
        branch._setHead(commit);
        this.trigger('onBranchReset', this, [
          this,
          branch,
          commit
        ]);
        if (!in_noBranchMovedEvent && oldBranchHead.getGuid() !== in_commit.getGuid()) {
          this.trigger('branchMoved', this, [
            this,
            branch,
            commit
          ]);
        }
      };
      /**
       * Validate an object addition
       * @param {guid} in_key the guid of the object to be validated
       * @param {{}} in_object The object to be added
       * @return {boolean} true if it passed, false otherwise
       * @private
       */
      // TODO: We do not yet make these checks in the new Checkout View. Do we need to port this code?
      /* Repository.prototype._validateAdd = function( in_key, in_object ) {

          if (!isGUID( in_key ) || !in_object instanceof BaseProperty ) {
            return false;
          }

          return true;
        };*/
      /**
       * Return the commit graph object maintaned by this document
       * @return {*} the commit graph
       * @private
       */
      Repository.prototype._getCommitGraph = function () {
        return this._commitGraph;
      };
      /**
       * Commit the current pending changes. The new commit node will be
       * the new head of the current branch.
       * This only works if you are at the tip of a branch – no commits on a
       * detached head!
       * @protected
       *
       * @param {String}                     in_msg          - the commit message
       * @param {LYNX.Property.CheckoutView} in_checkoutView - The view to commit
       *
       * @return {LYNX.PropertyGraph.Nodes.CommitNode} the new commit node (or the old one, if there was nothing to commit)
       */
      Repository.prototype._commit = function (in_msg, in_checkoutView) {
        if (!in_checkoutView) {
          throw new Error(MSG.NO_CHECKOUT_VIEW);
        }
        // Get the correct CheckedOutRepositoryInfo for this repository
        // TODO: What do we do here, once we support multiple checked out states of the same repository in one view?
        var repositoryInfo = in_checkoutView._getCheckedOutRepositoryInfo(this._guid);
        // get pending changes
        var serializedChangeSet = repositoryInfo.serializePendingChanges().getSerializedChangeSet();
        if (!_.isEmpty(serializedChangeSet)) {
          // We cannot commit changes to a detached head state
          var branch = repositoryInfo.branchNode;
          if (branch === DETACHED_HEAD) {
            throw new Error(MSG.CANNOT_COMMIT_TO + DETACHED_HEAD);
          }
          // tell everybody what's about to happen
          this.trigger('onCommitBegin', this, [this]);
          var newCommitGuid = generateGUID();
          var newCommitNode = this._addCommitNode({
            guid: newCommitGuid,
            meta: { name: in_msg },
            changeSet: serializedChangeSet
          });
          newCommitNode._addParent(branch.getHead());
          branch._setHead(newCommitNode);
          // tell everybody about what just happened
          // Do we need this any longer? It is probably only triggered for local commits
          this.trigger('onCommitEnd', this, [
            this,
            newCommitGuid,
            newCommitNode,
            branch
          ]);
          // Move the CheckoutView to the new commit
          this._checkout(branch, in_checkoutView, undefined, undefined, true);
          // This one should also be triggered when commits are added by the backend
          this.trigger('branchMoved', this, [
            this,
            branch,
            newCommitNode
          ]);
          return newCommitNode;
        } else {
          // There were no changes to commit, so we return the old commit and show a warning
          console.warn(MSG.NOTHING_TO_COMMIT);
          return repositoryInfo.commitNode;
        }
      };
      /**
       * Set the root node of the repository commit graph
       * @param {LYNX.PropertyGraph.Nodes.CommitNode} in_node the node to be set as root
       * @private
       */
      Repository.prototype._setRootNode = function (in_node) {
        // TODO: quick hack until I get terminal ports to work.
        in_node.isRoot = function () {
          return true;
        };
        var rootNode = this._commitGraph.getRoot();
        this._commitGraph.setRoot(in_node);
        if (rootNode.getTypeid() === 'CommitNodeID') {
          this._commitGraph.remove(rootNode.getGuid());
        } else {
          this._commitGraph.remove('root');
        }
      };
      /**
       * Get the root node of the repository commit graph
       * @return {LYNX.PropertyGraph.Nodes.CommitNode} the root node of the commit graph
       * @function getRootNode
       * @instance
       * @memberof LYNX.Property.Repository
       */
      Repository.prototype.getRootNode = function () {
        return this._commitGraph.getRoot();
      };
      /**
       * Add a commit node to the repository commit graph
       * @param {object} in_commit CommitNode parameter object
       * @param {string=} in_branch (optional) The branch to attach to commit node to.
       *  Setting this will add the CommitNode to the head of the branch and will trigger the
       *  branchMoved event.
       * @param {object} [in_options] - Set of options
       * @param {boolean} [in_options.updateRemoteBranch] -
       *  Flag to also update the remote branch when adding a new commit (if applicable)
       * @return {LYNX.PropertyGraph.Nodes.CommitNode} The newly added commit node
       * @private
       */
      Repository.prototype._addCommitNode = function (in_commit, in_branch, in_options) {
        var existingCommitNode = this.getCommit(in_commit.guid);
        if (existingCommitNode) {
          return existingCommitNode;
        }
        // Make new commit node.
        var newCommitNode = new CommitNode(in_commit.guid);
        var branch;
        if (in_branch) {
          branch = this.getBranch(in_branch);
          var remoteBranch = branch.getRemoteBranch();
          if (in_options.updateRemoteBranch && remoteBranch) {
            branch = remoteBranch;
          }
        }
        if (in_commit.meta) {
          newCommitNode._setMetadata('name', in_commit.meta.name);
        }
        var parent;
        if (in_commit.base) {
          parent = this.getCommit(in_commit.base.guid);
        }
        if (parent) {
          newCommitNode._addParent(parent);
        } else if (branch) {
          newCommitNode._addParent(branch.getHead());
        }
        newCommitNode._setChangeSet(in_commit.changeSet);
        // store it in the new commit node
        this._commitGraph.add(in_commit.guid, newCommitNode);
        if (branch) {
          branch._setHead(newCommitNode);
        }
        // We always update the squash cache when we receive a remote branch
        // This way, we make sure that we always have a separate up-to-date
        // cache entry for the remote cache. This could probably be handled a bit
        // more efficiently by doing this update lazyly, but that should not make much
        // of a difference
        if (branch && branch.isRemoteBranch()) {
          this._updateSquashCache(branch, this.getNormalizedChangeSetForCommit(branch.getHead()));
        }
        this.trigger('onAddCommitNode', this, [newCommitNode]);
        if (branch) {
          this.trigger('branchMoved', this, [
            this,
            branch,
            newCommitNode
          ]);
        }
        return newCommitNode;
      };
      /**
       * Accumulates and squashes all changes along the supplied path into a single ChangeSet.
       *
       * @param {Array.<{node: LYNX.PropertyGraph.Nodes.CommitNode}>} in_commitPath  - a node path
       * @param {boolean} in_useNormalizedCache - Should nodes with a cached normalized ChangeSet along the path
       *                                          be used to return a full normalized ChangeSet?
       *
       * @return {LYNX.Property.SerializedChangeSet} the squashed ChangeSet
       * @private
       */
      Repository.prototype._getSquashedChangeSet = function (in_commitPath, in_useNormalizedCache) {
        // accumulate changes (if any) and create a merged change set for all nodes along the supplied path
        var accumulatedChangeSet = new ChangeSet();
        if (in_commitPath) {
          for (var i = 0; i < in_commitPath.length; i++) {
            var node = in_commitPath[i].node;
            if (in_useNormalizedCache && node._hasNormalized()) {
              accumulatedChangeSet = new ChangeSet(deepCopy(node._getNormalizedChangeSet()));
            } else {
              var changeset = node.getChangeSetReadOnly();
              accumulatedChangeSet.applyChangeSet(changeset);
            }
          }
        }
        // Convert back to the old changeset format
        return accumulatedChangeSet.getSerializedChangeSet();
      };
      /**
       * Performs a rebase of a branch onto another branch.
       * @protected
       *
       * This will search for the first common parent commit between two supplied branch tips (only along the direct parents
       * not via merge branches). Once this common parent is found, the changes between this parent and the rebase branch
       * will be reapplied at the tip of the onto branch. This involves a rebase transformation of the ChangeSet to take the
       * changes in the onto branch into account. For the baseProperties this will be done automatically, for more complex
       * transformations, the user can supply a callback.
       *
       * @param {LYNX.Property.BranchIdentifier|LYNX.Property.CheckoutView} in_rebaseBranchOrView -
       *     The branch or view that will be rebased. If only a branch is supplied it will be rebased on the supplied
       *     onto-branch. If a CheckoutView is supplied, first its branch will be rebased, the new rebased branch will be
       *     checked out and then then the view's pending changes will be rebased and reapplied as modified
       * @param {LYNX.Property.CommitOrBranchIdentifier} in_ontoCommitOrBranch -
       *     The commit onto which the branch should be rebased
       *
       * @param {Object} [in_options] - Options
       * @param {Function} [in_options.rebaseCallback] -
       *     A callback that is invoked to perform the rebase operation. It will be invoked separately for each commit that
       *     is rebased, and then finally again for the pending changes if applicable. The function indicates via its
       *     return value, whether the rebase was successful. If true is returned the rebase will continue, if false is
       *     returned, it will be aborted (and no changes will occur). Furthermore, the function can modify the ChangeSet
       *     in the parameter transformedChangeSet to adapt the changes to the changes in the onto-branch.
       *
       *     It will be passed an Object with these members:
       *     * {LYNX.Property.SerializedChangeSet}  transformedChangeSet - The ChangeSet that resulted from performing the
       *                                                                   rebase operation on the primitive types and
       *                                                                   collections. This ChangeSet can be modified
       *                                                                   to adapt the changes to the changes in the
       *                                                                   onto-branch.
       *     * {LYNX.Property.SerializedChangeSet}  originalChangeSet    - The original ChangeSet before the rebase
       *     * {LYNX.Property.SerializedChangeSet}  ontoBranchChangeSet  - The changes between the common parent commit and
       *                                                                   the tip of the onto branch
       *     * {LYNX.Property.SerializedChangeSet}  [currentState]       - The normalized ChangeSet for the whole repository
       *                                                                   before the application of the
       *                                                                   transformedChangeSet. It will only be supplied
       *                                                                   when in_options.trackState===true, since
       *                                                                   computing this state can be expensive.
       *     * {Array.<LYNX.Property.ConflictInfo>} conflicts            - List with the conflicts that occurred during the
       *                                                                   rebase
       *     * {LYNX.PropertyGraph.Nodes.CommitNode} [commitNode]        - The commit node that is rebased. This is
       *                                                                   undefined, when the pending changes are rebased
       *     * {LYNX.PropertyGraph.Nodes.CommitNode} commonParentCommitNode - The commit node of the common parent
       *
       * @param {boolean} [in_options.trackState = true] -
       *     Enable tracking of the normalized ChangeSet during the rebase operation. This can be disabled, since the
       *     computation of the normalized ChangeSet incurs additional costs and should only be done when it is needed
       *     by the rebase function.
       *
       *
       * @return {boolean} - Returns a flag indicating success. The rebase callback can cancel the rebase. In that case
       *     false will be returned.
       */
      Repository.prototype._rebase = function (in_rebaseBranchOrView, in_ontoCommitOrBranch, in_options) {
        // Get the onto commit
        var ontoCommit = this.getCommit(in_ontoCommitOrBranch);
        if (!ontoCommit) {
          throw new Error(MSG.INVALID_ONTO_IN_REBASE);
        }
        var rebaseTipCommit, rebaseBranch, baseRepositoryInfo, pendingChangeSet;
        if (in_rebaseBranchOrView instanceof CheckoutView) {
          // Get the repository info into which this checkout should happen.
          // TODO: Once we have multiple checked out repositories at different versions, we have to decide which
          // of those to use
          baseRepositoryInfo = in_rebaseBranchOrView._getCheckedOutRepositoryInfo(this._guid);
          // Determine tip commit, branch and currently stored ChangeSet from the CheckOutView
          rebaseTipCommit = baseRepositoryInfo.commitNode;
          rebaseBranch = baseRepositoryInfo.branchNode;
          pendingChangeSet = baseRepositoryInfo.serializePendingChanges().getSerializedChangeSet();
          // Make sure we have a valid branch
          if (rebaseBranch === DETACHED_HEAD) {
            throw new Error(MSG.CANNOT_REBASE_DETACHED_HEAD);
          }
        } else {
          rebaseBranch = this.getBranch(in_rebaseBranchOrView);
          rebaseTipCommit = this.getCommit(in_rebaseBranchOrView);
        }
        if (!rebaseBranch || !rebaseTipCommit) {
          throw new Error(MSG.INVALID_REBASE_BRANCH);
        }
        // Find the common parent commit
        var commonParent = this._commitGraph.lowestCommonAncestor(rebaseTipCommit, ontoCommit);
        // Get the squashed ChangeSet for all commits we have to skip over during the rebase
        var pathToOntoCommit = ontoCommit._getPathFromParentCommit(commonParent);
        // Check, whether there is any parallel branch, or whether the onto commit is already a
        // parent of the current  rebaseTipCommit
        if (pathToOntoCommit.length === 0) {
          return true;
        }
        var squashedChanges = new ChangeSet(this._getSquashedChangeSet(pathToOntoCommit, false));
        // Get the list of all changes we have to rebase
        var changesToRebase = _.map(rebaseTipCommit._getPathFromParentCommit(commonParent), function (x) {
          return x.node;
        });
        // If there were pending changes in the CheckoutView, we add their ChangeSet to the list of changes that have
        // to be rebased
        if (pendingChangeSet && !_.isEmpty(pendingChangeSet)) {
          changesToRebase.push(pendingChangeSet);
        }
        // Check, whether a callback was supplied
        var rebaseCallback = in_options && in_options.rebaseCallback ? in_options.rebaseCallback : undefined;
        var trackState = in_options && in_options.trackState ? in_options.trackState : false;
        var currentState = undefined;
        if (rebaseCallback && trackState) {
          currentState = new ChangeSet(this.getNormalizedChangeSetForCommit(ontoCommit));
        }
        var newCommitNodes = [];
        var currentCommitNode = ontoCommit;
        var newHeadAfterRebase = ontoCommit;
        // We start with the onto commit
        var rebaseSucceeded = true;
        var copiedOriginalChangeSet;
        var squashedChangesToRebase;
        // Squash only when we have more than one changes to rebase. Otherwise, we don't need to squash.
        if (in_options.squash && changesToRebase.length > 1) {
          squashedChangesToRebase = _.reduce(changesToRebase, function (memo, commitOrChangeSet) {
            memo.applyChangeSet(commitOrChangeSet instanceof CommitNode ? commitOrChangeSet.getChangeSetReadOnly() : commitOrChangeSet);
            return memo;
          }, new ChangeSet());
          changesToRebase = [squashedChangesToRebase.getSerializedChangeSet()];
        }
        // Now successively rebase each of the changes in the parallel tree and apply all of them to the base
        // commit
        for (var i = 0; i < changesToRebase.length; i++) {
          // Get the ChangeSet to rebase (either from the commit node, or directly from the array for the
          // pending changes
          var originalChangeSet = changesToRebase[i] instanceof CommitNode ? changesToRebase[i].getChangeSetReadOnly() : changesToRebase[i];
          if (rebaseCallback) {
            // We have to copy it, since the rebase operation directly modifies the ChangeSet
            copiedOriginalChangeSet = deepCopy(originalChangeSet);
          }
          // Perform the rebase for the primitive types in this ChangeSet
          var conflicts = [];
          var transformedChangeSet = squashedChanges._rebaseChangeSet(originalChangeSet, conflicts);
          // If the user supplied a rebase callback, we have to invoke it here
          if (rebaseCallback) {
            var success = rebaseCallback({
              transformedChangeSet: transformedChangeSet,
              originalChangeSet: copiedOriginalChangeSet,
              currentState: trackState ? currentState.getSerializedChangeSet() : undefined,
              conflicts: conflicts,
              ontoBranchChangeSet: squashedChanges.getSerializedChangeSet(),
              commitNode: changesToRebase[i] instanceof CommitNode ? changesToRebase[i] : undefined,
              commonParentCommitNode: commonParent
            });
            rebaseSucceeded = rebaseSucceeded && success;
            if (!success) {
              break;
            }
            // Update the current state with the new ChangeSet
            if (trackState) {
              currentState.applyChangeSet(transformedChangeSet);
            }
          }
          // Store the rebased ChangeSet in a new rebased commit node
          if (changesToRebase[i] instanceof CommitNode) {
            var originalCommit = changesToRebase[i];
            // Create a new commit node
            var newCommitGuid = generateGUID();
            var newCommitNode = new CommitNode(newCommitGuid);
            // Use the same meta-data as the original node (do we need a callback to rewrite this?)
            newCommitNode.copyMetaDataFromNode(originalCommit);
            // Update parent and ChangeSet
            newCommitNode._addParent(currentCommitNode);
            newCommitNode._setChangeSet(transformedChangeSet);
            // Store the new node (we postpone adding to the graph to allow canceling a rebase in the callback)
            newCommitNodes.push(newCommitNode);
            currentCommitNode = newCommitNode;
          } else {
            // For the last node, we just overwrite the pending ChangeSet with the rebased one
            pendingChangeSet = transformedChangeSet;
          }
        }
        // The rebase failed (it was terminated in the callback). Nothing will be changed and we return false
        if (!rebaseSucceeded) {
          return false;
        }
        // Add all new commit nodes to the graph
        // If there are any, we can also only have local changes that have to be rebased
        if (newCommitNodes.length > 0) {
          for (var i = 0; i < newCommitNodes.length; i++) {
            // store it in the new commit node
            this._commitGraph.add(newCommitNodes[i].getGuid(), newCommitNodes[i]);
            this.trigger('onAddCommitNode', this, [newCommitNodes[i]]);
          }
          // If other node were added during the rebase, we have to update the new head
          newHeadAfterRebase = newCommitNodes[newCommitNodes.length - 1];
        } else if (!_.isUndefined(squashedChangesToRebase)) {
          var squashedCommit = new CommitNode(generateGUID());
          squashedCommit._setMetadata('name', 'Squashed Rebased ChangeSet');
          squashedCommit._setChangeSet(pendingChangeSet);
          squashedCommit._addParent(ontoCommit);
          // Add the new commit to the graph
          this._commitGraph.add(squashedCommit.getGuid(), squashedCommit);
          this.trigger('onAddCommitNode', this, [squashedCommit]);
          newHeadAfterRebase = squashedCommit;
        }
        var branchResetted = false;
        if (newHeadAfterRebase.getGuid() !== rebaseBranch.getGuid()) {
          // Move the branch to the last commit in the list of rebased commits
          // (or the onto commit, if no commit had to be rebased)
          this._resetBranch(rebaseBranch, newHeadAfterRebase, true);
          branchResetted = true;
        }
        // If this was called with a CheckOutView, we check out the new local state and
        // reapply the rebased local changes
        if (in_rebaseBranchOrView instanceof CheckoutView) {
          // TODO: How do we handle this if there are repository references?
          var view = in_rebaseBranchOrView;
          // We push the notification scope to prevent notifications during the checkout
          view.pushNotificationDelayScope();
          // We delay repository reference updates until the new parent repository has been set
          view._pushRepositoryReferenceUpdatesDelayScope();
          // Check out the new state into the view
          this._checkout(rebaseBranch, view);
          view._setParentRepository(this.getGuid(), rebaseBranch.getHead().getGuid());
          // Reapply the local changes
          if (pendingChangeSet && _.isUndefined(squashedChangesToRebase)) {
            baseRepositoryInfo.applyChangeset(pendingChangeSet);
          }
          // Now that the new head has been registered, we can update the other repositories
          view._popRepositoryReferenceUpdatesDelayScope(true);
          view.popNotificationDelayScope();
        }
        // We trigger the branch moved event here (and not in the resetBranch function above)
        // to make sure, the checkout view has already been updated, when the event is triggered
        if (branchResetted) {
          this.trigger('branchMoved', this, [
            this,
            rebaseBranch,
            newHeadAfterRebase
          ]);
        }
        return true;
      };
      /**
       * Checks out the given repository into the supplied checkout view. This is the function that is internally used
       * by HFDM. We provide Repository.prototype.checkout which can be used when using repositories directly.
       *
       * @param {string}                                                 in_commitGuidOrBranchName -
       *     GUID identifying the commit or branch to check out
       * @param {LYNX.Property.CheckoutView}                             in_checkoutView -
       *     The checkout view the commit is checked out into
       * @param {LYNX.Property.CheckoutView~CheckedOutRepositoryInfo}   [in_checkedOutRepositoryInfo] -
       *     The checkedOut repository info into which the checkout will happen. If none is given, it will be chosen based
       *     on the commit GUID
       * @param {string} [in_checkoutGroupID] - Defines a scope which is used to determine a group of repository
       *     references which share a checked out repositories
       * @param {boolean} [in_alreadyOnCommit] - This indicates that the CheckoutView already has the correct data structure
       *     checked out and only needs to clean the dirty flag. This is used to accellerate commits, in which the
       *     CheckoutView needs to be udpate to the new commit, but already has the correct data strcutures checked out.
       *
       * @protected
       * @return {undefined}
       */
      Repository.prototype._checkout = function (in_commitGuidOrBranchName, in_checkoutView, in_checkedOutRepositoryInfo, in_checkoutGroupID, in_alreadyOnCommit) {
        if (!in_checkoutView) {
          throw new Error(MSG.NO_CHECKOUT_VIEW);
        }
        // Get the repository info into which this checkout should happen.
        // TODO: Once we have multiple checked out repositories at different versions, we have to decide which
        // of those to use
        var baseRepositoryInfo = in_checkedOutRepositoryInfo;
        if (!baseRepositoryInfo) {
          baseRepositoryInfo = in_checkoutView._getCheckedOutRepositoryInfo(this._guid, 'root');
          if (!baseRepositoryInfo) {
            // Create a new checked out repository information structure, if none exists so far
            baseRepositoryInfo = new CheckoutView.CheckedOutRepositoryInfo(this._guid, 'root');
          }
        }
        // Perform the checkout into the repository info structure
        this._checkoutRepositoryInfo(in_commitGuidOrBranchName, baseRepositoryInfo, in_checkoutView, in_checkoutGroupID, in_alreadyOnCommit);
      };
      /**
       * Internal function to check out a commit from the repository into a checked out CheckedOutRepositoryInfo node in a
       * checkout view
       *
       * @param {string} in_commitGuidOrBranchName - GUID identifying the commit or branch to check out
       * @param {LYNX.Property.CheckoutView~CheckedOutRepositoryInfo} in_checkoutRepositoryInfo
       *     The CheckedOutRepositoryInfo object the commit is checked out into
       * @param {LYNX.Property.CheckoutView} in_checkoutView - The checkout view the commit is checked out into
       * @param {string} [in_checkoutGroupID] - Defines a scope which is used to determine a group of repository
       *     references which share a checked out repositories
       * @param {boolean} [in_alreadyOnCommit] - This indicates that the CheckoutView already has the correct data structure
       *     checked out and only needs to clean the dirty flag. This is used to accellerate commits, in which the
       *     CheckoutView needs to be udpate to the new commit, but already has the correct data strcutures checked out.
       * @private
       */
      Repository.prototype._checkoutRepositoryInfo = function (in_commitGuidOrBranchName, in_checkoutRepositoryInfo, in_checkoutView, in_checkoutGroupID, in_alreadyOnCommit) {
        var branchNode = this.getBranch(in_commitGuidOrBranchName);
        var commitNode = this.getCommit(in_commitGuidOrBranchName);
        if (!commitNode) {
          console.error('cannot find ' + in_commitGuidOrBranchName);
          return;
        }
        // We only have to do something, if the commit has been changed
        if (in_checkoutRepositoryInfo.commitNode !== commitNode || in_checkoutRepositoryInfo.hasPendingChanges()) {
          var oldCommitGuid = in_checkoutRepositoryInfo.commitNode ? in_checkoutRepositoryInfo.commitNode.getGuid() : undefined;
          // Update the registration for the structure
          in_checkoutView._updateRepositoryInfo(in_checkoutRepositoryInfo, oldCommitGuid, commitNode.getGuid(), in_checkoutGroupID);
          // Update the commit node within the checked out repository information
          in_checkoutRepositoryInfo.commitNode = commitNode;
          var changes, accumulatedChanges;
          if (in_alreadyOnCommit) {
            changes = commitNode.getChangeSetReadOnly();
            in_checkoutRepositoryInfo.repositoryRoot.cleanDirty();
            in_checkoutRepositoryInfo._registerAdditionalRemoteTemplates(changes.insertTemplates);  // We keep accumulatedChanges === undefined in this branch. This way
                                                                                                    // the squash cache will perform the ChangeSet update internally
          } else {
            accumulatedChanges = this.getNormalizedChangeSetForCommit(commitNode);
            changes = in_checkoutRepositoryInfo.revertToState(accumulatedChanges);
          }
          // Trigger an event in the checkout view
          in_checkoutView.trigger('onCheckoutEnd', in_checkoutView, [
            in_commitGuidOrBranchName,
            in_checkoutView,
            commitNode,
            new ChangeSet(changes)
          ]);
          // If we have a branch, we insert the commit under the branch identifier into the cache, otherwise, we use the
          // commit
          this._updateSquashCache(branchNode || commitNode, accumulatedChanges);
        }
        // Update the branch nodes within the checked out repository information
        if (branchNode instanceof BranchNode) {
          in_checkoutRepositoryInfo.branchNode = branchNode;
        } else {
          in_checkoutRepositoryInfo.branchNode = DETACHED_HEAD;
        }
      };
      /**
         * Update the cache of normalized commits. We keep an LRU cache, which tracks the most recently used branches
         * and commits to enable faster checkout.
         *
         * @param {LYNX.PropertyGraph.Nodes.CommitNode|LYNX.PropertyGraph.Nodes.BranchNode} in_node -
         *    The node for which the cache entry is created. If a branch node is given, we explicitly track the head of the
         *    branch (and count the branch as one cache entry) otherwise, we create an entry for that specific commit
         * @param {LYNX.Property.SerializedChangeSet} [in_normalizedChangeSet] -
         *     The normalized ChangeSet. If set to undefined, it will be computed for the given commit.
      * @private
         */
      Repository.prototype._updateSquashCache = function (in_node, in_normalizedChangeSet) {
        // Check, whether we already have an entry for this node
        var key = in_node.getGuid();
        // Create unique keys for remote branches
        if (in_node instanceof BranchNode && in_node.isRemoteBranch()) {
          key = key + '_remote';
        }
        var existingNode = this._commitCache.LRU.get(key);
        var commitNode = this.getCommit(in_node);
        if (existingNode) {
          // If there is already an entry for a branch node in the cache,
          // we have to update the cache to the newest head of the branch.
          // This way, the cache keeps tracking the branch head under the branch
          // identifier in the cache
          if (in_node instanceof BranchNode) {
            // Remove the old entry from the cache
            var oldGUID = existingNode.commitGuid;
            var oldNode = this.getCommit(oldGUID);
            if (in_normalizedChangeSet === undefined) {
              if (oldNode && commitNode.getFirstParent() === oldNode && !oldNode.isNormalized() && // Check, whether this node has a cache, but is not itself
                oldNode._hasNormalized() && // a normalized node
                this._commitCache.cachedCommits[oldNode.getGuid()] && // Only do the update if this node is
                this._commitCache.cachedCommits[oldNode.getGuid()].count === 1) {
                // referenced by only one branch
                // Optimized case, where we only move the ChangeSet from the parent to
                // to the next node on the branch. This keeps the internal data structure and
                // only applies the changes to it and moves it to the new node, without copying it
                var changeSet = new ChangeSet(oldNode._getNormalizedChangeSet());
                changeSet.applyChangeSet(commitNode.getChangeSetReadOnly());
                in_normalizedChangeSet = changeSet.getSerializedChangeSet();
              } else {
                // Create a new normalized ChangeSet for this target node
                in_normalizedChangeSet = this.getNormalizedChangeSetForCommit(commitNode);
              }
            }
            if (!oldNode) {
              console.warn(MSG.NON_EXISTING_NODE_IN_CACHE);
            } else {
              this._removeCommitFromCache(oldNode);
            }
            // And insert a new one
            existingNode.commitGuid = commitNode.getGuid();
            this._insertCommitInCache(commitNode, in_normalizedChangeSet);
          }
        } else {
          if (in_normalizedChangeSet === undefined) {
            in_normalizedChangeSet = this.getNormalizedChangeSetForCommit(commitNode);
          }
          // Create a new cache entry for the given node
          this._commitCache.LRU.insert(key, { commitGuid: commitNode.getGuid() });
          this._insertCommitInCache(commitNode, in_normalizedChangeSet);
          // And trim the cache down to the requested cache size
          this._trimCache();
        }
      };
      /**
       * Trims the commit cache to the allowed size
       * @private
       */
      Repository.prototype._trimCache = function () {
        var that = this;
        this._commitCache.LRU.trimTo(this._commitCache.size, function (entryKey, node) {
          var nodeToRemove = that.getCommit(node.commitGuid);
          if (!nodeToRemove) {
            console.warn(MSG.NON_EXISTING_NODE_IN_CACHE);
          } else {
            that._removeCommitFromCache(nodeToRemove);
          }
        });
      };
      /**
       * Inserts an entry into the cache of commits for which a normalized ChangeSet has been stored.
       *
       * This cache counts how often a commit has been inserted and removes the cache after a symmetric number of calls to
       * _removeCommitFromCache.
       *
       * @param {LYNX.PropertyGraph.Nodes.CommitNode} in_commitNode -
       *     The commit node for which the normalized ChangeSet should be cached
       * @param {LYNX.Property.SerializedChangeSet} in_normalizedChangeSet -
       *     The normalized ChangeSet
       * @private
       */
      Repository.prototype._insertCommitInCache = function (in_commitNode, in_normalizedChangeSet) {
        var guid = in_commitNode.getGuid();
        if (this._commitCache.cachedCommits[guid]) {
          // If we already have an entry, we just increment the count
          this._commitCache.cachedCommits[guid].count++;
        } else {
          // Otherwise, we create an entry
          this._commitCache.cachedCommits[guid] = { count: 1 };
          // And store the changeSet in the commit node
          in_commitNode._setCachedNormalizedChangeSet(in_normalizedChangeSet);
        }
      };
      /**
         * Removes an entry from the cache of commits for which a normalized ChangeSet has been stored.
         *
         * This cache counts how often a commit has been inserted and removes the cache after a symmetric number of calls to
         * _removeCommitFromCache.
         *
         * @param {LYNX.PropertyGraph.Nodes.CommitNode} in_commitNode -
         *     The commit node for which the cache should be removed

         * @private
         */
      Repository.prototype._removeCommitFromCache = function (in_commitNode) {
        var guid = in_commitNode.getGuid();
        if (this._commitCache.cachedCommits[guid]) {
          // Decrement the count in the existing entry
          this._commitCache.cachedCommits[guid].count--;
          if (this._commitCache.cachedCommits[guid].count === 0) {
            // And remove it, if the count reaches zero
            delete this._commitCache.cachedCommits[guid];
            in_commitNode._setCachedNormalizedChangeSet(undefined);
          }
        } else {
          throw new Error(MSG.UNBALANCED_CACHE_UPDATES);
        }
      };
      /**
       * Returns a normalized ChangeSet (one that contains no modify operations) that describes
       * the state at a give commit by squashing all nodes between the commit and the root.
       *
       * @param {LYNX.Property.CommitOrBranchIdentifier} in_commit - The commit for which the ChangeSet is computed
       *
       * @return {LYNX.Property.SerializedChangeSet} - The normalized ChangeSet
       * @function getNormalizedChangeSetForCommit
       * @instance
       * @memberof LYNX.Property.Repository
       */
      Repository.prototype.getNormalizedChangeSetForCommit = function (in_commit) {
        var path = in_commit._getPathFromParentCommit(function (node) {
          return node._hasNormalized();
        }, true);
        return this._getSquashedChangeSet(path, true);
      };
      /**
       * Given a branch GUID, delete that branch from the graph along with its nodes
       * @param  {string} branchGuid - GUID of the branch to remove
       * @private
       */
      Repository.prototype._removeBranch = function (branchGuid) {
        this._commitGraph.removeBranch(branchGuid);
      };
      /**
       * Merge in_branchName into the active branch
       * @param {String} in_branchName Merge this branch into the active branch
       *
       * TODO: We have to implement this for the new checkout view
       */
      /* Repository.prototype.merge = function( in_branchName ) {

          if (this._activeBranch === DETACHED_HEAD) {
            console.error(MSG.CANNOT_MERGE_TO_DETACHED_HEAD);
            return;
          }

          var branchNode = this._commitGraph.getNodeByUid( in_branchName );

          if (branchNode) {

            var mergeBranchTipCommit = branchNode.getHead();

            // tell everybody what's about to happen
            this.trigger( 'onMergeBegin', this, [this] );

            var activeCommit = this._activeCommit;

            // find divergence point and create a changeset that is the result
            // set of accumulated changes from the divergent point, to the head of the
            // mergeTo branch
            var commonParent = this._commitGraph.lowestCommonAncestor( activeCommit, mergeBranchTipCommit );

            console.log('common parent', commonParent._guid, activeCommit, mergeBranchTipCommit);

            // Find the path from root to the mergeBranchTipCommit, then slice it
            // from the divergence point to the tip.
            var mergePath = mergeBranchTipCommit.getAllPathsFromRoot()[0];
            for (var i = 0; i < mergePath.length; i++) {
              if ( mergePath[i].node === commonParent ) {
                mergePath = mergePath.splice(i + 1, mergePath.length);
                break;
              }
            }

            // Accumulate the changes from the common parent, to the tip of the
            // merge branch.
            var mergeChangeSet = this._getSquashedChangeSet( mergePath );

            // Make a merge node
            var mergeCommitGuid = generateGUID();
            var mergeCommitNode  = new CommitNode(mergeCommitGuid);
            mergeCommitNode._setMetadata( 'name', 'merge ' + activeCommit.getMetadata('name') + ' <> ' +
                mergeBranchTipCommit.getMetadata('name') );

            // Setupthe commit merge node
            mergeCommitNode._setChangeSet( mergeChangeSet );
            mergeCommitNode._addParent( activeCommit );
            mergeCommitNode._addParent( mergeBranchTipCommit );

            // Store merge node in the graph
            this._commitGraph.add( mergeCommitGuid, mergeCommitNode );

            // clean current head
            // TODO we discard all pending changes in current branch!
            this.resetPendingChanges();
            this.cleanDirty();

            // Move the head of current branch to the new commit node
            this._activeBranch._setHead(mergeCommitNode);

            // tell everybody about what just happened
            this.trigger( 'onMergeEnd', this, [ this, in_branchName, this._activeBranch.getName(),
              mergeCommitGuid, mergeCommitNode ] );

          } else {
            console.error('no such branch ' + in_branchName );
          }
        };*/
      /**
       * Comparison function between this repository and another one to check
       * for equality. We determine the equality by checking their respective guids
       * @param {LYNX.Property.Repository} in_node The commit node to compare with
       * @return {boolean} True of the two nodes are equal
       * @function isEqual
       * @instance
       * @memberof LYNX.Property.Repository
       */
      Repository.prototype.isEqual = function (repository) {
        return repository.getGuid() === this.getGuid();
      };
      exportModule('LYNX.Property.Repository', Repository);
    }());
    (function () {
      var MSG = include('LYNX.Property._constants.MSG');
      var CheckoutView = include('LYNX.Property.CheckoutView');
      var EventEmitter = include('LYNX.Utils.Events.EventEmitter');
      var LYNXProperty = include('LYNX.Property', ['HFDM']);
      var _ = include('underscore');
      var UrnUtils = include('LYNX.Utils.UrnUtils');
      /**
       * The Workspace object encapsulates a HFDM to proxy function calls.
       *
       * @param LYNX.Property.HFDM in_HFDM
       * @constructor
       * @alias LYNX.Property.Workspace
       */
      var Workspace = function (in_HFDM) {
        console.assert(in_HFDM instanceof LYNXProperty.HFDM, 'Missing reference to hfdm instance');
        this._HFDM = in_HFDM;
        this._checkoutView = new CheckoutView();
        this._syncMode = this.SYNC_MODE.SYNCHRONIZE;
        /**
         * current active branch/commit URN
         * @type {string}
         */
        this._activeUrn = undefined;
        this._bindings = [];
        this._initBindings();
        /**
         * currently in the process of updating the local to the remote branch
         * we need it to not trigger the rebase process again while it's running
         * @type {boolean}
         */
        this._currentlyUpdatingRemote = false;
        EventEmitter.call(this);
      };
      /**
       * Workspace should inherit from EventEmitter.
       */
      Workspace.prototype = Object.create(EventEmitter.prototype);
      /**
       * Used to Set the synchronization mode of the Workspace.
       * @enum {number}
       * @alias LYNX.Property.Workspace.SYNC_MODE
       */
      Workspace.prototype.SYNC_MODE = {
        /** Application needs to update and push manually. Commit is local in this mode. */
        MANUAL: 0,
        /** Workspace automatically pushes local commits to the server */
        PUSH: 1,
        /** Workspace automatically pulls remote changes without pushing it's changes back */
        PULL: 2,
        /** Workspace updates and pushes automatically (default) */
        SYNCHRONIZE: 3
      };
      /**
       * Initialize an empty workspace or load an existing workspace
       *
       * If an URN is provided in in_options.urn (either a branch URN or a commit URN),
       * this URN is checked out. Otherwise, a new repository is created and the
       * main branch is checked out.
       *
       * @param {object=} [in_options] Additional options
       * @param {string=} [in_options.urn] The urn of the branch or commit to load
       * @param {object=} [in_options.metadata] The branch metadata
       * @param {string} [in_options.metadata.name] The human readable name of the branch.
       *                                            If not specified, it defaults to the guid.
       * @param {string} [in_options.metadata.guid] The guid of the branch. If not specified, a guid will be generated
       * @param {object=} [in_options.headers] Objects to be appended as headers in the request
       * @param {boolean=} [in_options.local] Flag used to create local repositories
       * @param {boolean=} [in_options.checkout] Flag used to indicate whether we want to checkout the newly created branch
       * @param {object=} [in_options.qs] Parameters appended to the query string
       * @return {Promise.<LYNX.PropertyGraph.Nodes.BranchNode>} Branch node
       */
      Workspace.prototype.initialize = function (in_options) {
        var that = this;
        in_options = Object(in_options);
        if (in_options.urn === undefined) {
          /* No urn provided, we need to create a new repo. */
          return that._HFDM._createRepository(in_options).then(function (params) {
            return that.checkout(params.branch.getUrn());
          });
        } else {
          /* An urn is provided, let's check it out. Can be repo, branch or commit. */
          return that.checkout(in_options.urn);
        }
      };
      /**
       * Create and (optionally) checkout a branch from the currently active commit
       * @param {object=} [in_options] Additional options
       * @param {object=} [in_options.metadata] The branch metadata
       * @param {string} [in_options.metadata.name] The human readable name of the branch.
       * If not specified, it defaults to the guid.
       * @param {object=} [in_options.headers] Objects to be appended as headers in the request
       * @param {boolean=} [in_options.local] Flag used to create local repositories
       * @param {boolean=} [in_options.checkout] Flag used to indicate whether we want to checkout the newly created branch
       * @param {object=} [in_options.qs] Parameters appended to the query string
       * @return {Promise.<LYNX.PropertyGraph.Nodes.BranchNode>} Branch node
       */
      Workspace.prototype.branch = function (in_options) {
        var that = this;
        in_options = in_options || {};
        in_options.checkout = in_options.checkout === undefined ? true : !!in_options.checkout;
        return new Promise(function (resolve, reject) {
          that._HFDM._branch(that.getActiveCommit().getGuid(), in_options).then(function (branchNode) {
            if (in_options.checkout) {
              that.checkout(branchNode.getUrn()).then(function () {
                resolve(branchNode);
              }).catch(function (error) {
                reject(error);
              });
            } else {
              resolve(branchNode);
            }
          }).catch(function (error) {
            reject(error);
          });
        });
      };
      /**
       * Commit the current pending changes. The new commit node will be
       * the new head of the current branch.
       * This only works if you are at the tip of a branch – no commits on a detached head!
       * If the HFDM is connected to the backend, the new commit will also be persisted and broadcasted.
       * @param {object=} [in_options] Additional options
       * @param {object=} [in_options.headers] Objects to be appended as headers in the request
       * @param {boolean=} [in_options.local] Flag used to create local repositories
       * @param {object=} [in_options.qs] Parameters appended to the query string
       * @param {object=} [in_options.metadata] Object containing the commit meta data
       * @return {Promise.<LYNX.PropertyGraph.Nodes.CommitNode>} On resolve, the promise returns the commitNode.
       */
      Workspace.prototype.commit = function (in_options) {
        in_options = in_options || {};
        var metadata = in_options.metadata;
        if (metadata) {
          delete in_options.metadata;
        }
        var that = this;
        if (this.isDetachedHead()) {
          console.warn('You are in a read only state. To save your work, we created a new branch and ' + 'committed your changes there');
          return this.branch(null, in_options).then(function (branchNode) {
            return that._HFDM._commit(metadata, that._checkoutView, in_options);
          });
        } else {
          return this._HFDM._commit(metadata, this._checkoutView, in_options);
        }
      };
      /**
       * Check whether the Workspace has already checked out a given point in time
       * @return {boolean} Returns true if the Workspace has checkedout a repo
       */
      Workspace.prototype.isCheckedOut = function () {
        return this._checkoutView.isCheckedOut();
      };
      /**
       * Return the commit of the check out
       *
       * @return {LYNX.Property.BranchNode|string} The commit or Repository.DETACHED_HEAD
       */
      Workspace.prototype.getActiveCommit = function () {
        return this._checkoutView.getActiveCommit();
      };
      /**
       * Return the branch of the check out
       *
       * @return {LYNX.Property.BranchNode|string} The branch or Repository.DETACHED_HEAD
       */
      Workspace.prototype.getActiveBranch = function () {
        return this._checkoutView.getActiveBranch();
      };
      /**
       * Returns if the active branch is detached.
       * @return {Boolean} Whether the branch is detached.
       */
      Workspace.prototype.isDetachedHead = function () {
        return this._checkoutView.isDetachedHead();
      };
      /**
       * Get the repository that the Workspace has checked out.
       * @return {Object}  The root repository.
       */
      Workspace.prototype.getActiveRepository = function () {
        return this._HFDM.getRepository(UrnUtils.repoUrn(this._checkoutView.getParentRepositoryGuid()));
      };
      /**
       * Get the Urn currently used by the workspace
       * It can be a commit Urn (if the workspace was initialized with a specific commit Urn)
       * or a branch Urn (if the workspace was initialized with a branch Urn)
       * @return {string} Urn
       */
      Workspace.prototype.getActiveUrn = function () {
        return this._activeUrn;
      };
      /**
       * Returns whether there are pending changes.
       *
       * @return {Boolean}    -If there are pending changes from the checkout view.
       */
      Workspace.prototype.hasPendingChanges = function () {
        return this._checkoutView.hasPendingChanges();
      };
      /**
       * Returns the ChangeSet of all pending changes.
       *
       * @return {LYNX.Property.ChangeSet} The serialized changes
       */
      Workspace.prototype.getPendingChanges = function () {
        console.assert(this.isCheckedOut(), 'Cannot get pending changes. ' + MSG.NO_REPOSITORY_CHECKED_OUT);
        return this._checkoutView.getRoot().getPendingChanges();
      };
      /**
       * Whether or not the current branch is up to date with the current commit.
       *
       * @return {Boolean}     -If there are pending changes compared to the current commit.
       */
      Workspace.prototype.isSynchronized = function () {
        if (this.isDetachedHead()) {
          return false;
        }
        var branchNode = this._HFDM.getBranch(this.getActiveBranch().getUrn());
        if (branchNode.getRemoteBranch()) {
          branchNode = branchNode.getRemoteBranch();
        } else {
          console.warn('Cannot compare to remote branch because it is not tracked');
        }
        return branchNode.getHead().isEqual(this.getActiveCommit());
      };
      /**
       * Performs a rebase of a branch onto another branch.
       *
       * This will search for the first common parent commit between two supplied branch tips (only along the direct parents
       * not via merge branches). Once this common parent is found, the changes between this parent and the rebase branch
       * will be reapplied at the tip of the onto branch. This involves a rebase transformation of the ChangeSet to take the
       * changes in the onto branch into account. For the baseProperties this will be done automatically, for more complex
       * transformations, the user can supply a callback.
       *
       * @param {object|undefined} [in_options] - Rebase Options has the following members:
       *  @param {boolean} [in_options.push=true] - Flag used to indicate that any local commits that have been rebased
       *                                      onto the new commit will not be pushed to the the server.
       * @return {Promise.Boolean} - Success
       */
      Workspace.prototype.rebase = function (in_options) {
        var that = this;
        return new Promise(function (resolve, reject) {
          if (that.isDetachedHead()) {
            var error = new Error('Cannot rebase - ' + MSG.DETACHED_HEAD);
            console.error(error);
            reject(error);
            return;
          }
          try {
            var branch;
            if (that.getActiveBranch().getRemoteBranch()) {
              branch = that.getActiveBranch().getRemoteBranch();
            } else {
              branch = that.getActiveBranch();
            }
            in_options = in_options || {};
            if (that._conflictHandler) {
              in_options.pushCallback = that._conflictHandler;
            }
            // TODO: We will need to refactor "HFDM.rebase" to be asynchronous in order to support
            // referenced repository hierarchies.
            var success = that._HFDM._rebase(that._checkoutView, branch, in_options);
            resolve(success);
            return;
          } catch (e) {
            reject(e);
          }
        });
      };
      /**
       *
       * setConflictHandler is used to set the global handler to deal with conflicts.
       * NOTE: This is a temporary solution until we design a conflict resolution API.
       *
       * @param {function} [in_callback] -
       *    Callback invoked for every ChangeSet that is applied
       *     It will be passed an Object with these members:
       *     * {LYNX.Property.SerializedChangeSet}  transformedChangeSet - The ChangeSet that resulted from performing the
       *                                                                   rebase operation on the base types. This
       *                                                                   ChangeSet can be modified to adapt the changes
       *                                                                   to the changes in the onto-branch.
       *     * {LYNX.Property.SerializedChangeSet}  originalChangeSet    - The original ChangeSet before the rebase
       *     * {LYNX.Property.SerializedChangeSet}  ontoBranchChangeSet  - The changes between the common parent commit and
       *                                                                   the tip of the onto branch
       *     * {LYNX.Property.SerializedChangeSet}  [currentState]       - The normalized ChangeSet for the whole Repository
       *                                                                   before the application of the
       *                                                                   transformedChangeSet. It will only be supplied
       *                                                                   when in_options.trackState===true, since
       *                                                                   computing this state can be expensive.
       *     * {Array.<LYNX.Property.ConflictInfo>} conflicts            - List with the conflicts that occurred during the
       *                                                                   rebase
       *     * {LYNX.PropertyGraph.Nodes.CommitNode} [commitNode]        - The commit node that is rebased. This is
       *                                                                   undefined, when the pending changes are rebased
       */
      Workspace.prototype.setConflictHandler = function (in_callback) {
        console.assert(_.isFunction(in_callback), 'in_callback must be a function');
        this._conflictHandler = in_callback;
      };
      /**
       * Synchronize the workspace with current checked out branch.
       *
       * @return {Promise.Boolean} On resolve, the promise returns a boolean indicating success.
       */
      Workspace.prototype.synchronize = function () {
        var that = this;
        return new Promise(function (resolve, reject) {
          if (that.isDetachedHead()) {
            var error = new Error('Cannot update - ' + MSG.DETACHED_HEAD);
            console.error(error);
            reject(error);
            return;
          }
          var remoteBranch = that.getActiveBranch().getRemoteBranch();
          if (remoteBranch) {
            that.rebase().then(function (success) {
              resolve(success);
            }).catch(function (rebaseError) {
              reject(error);
            });
          } else {
            that.rebase().then(function (success) {
              resolve(success);
            }).catch(function (rebaseError) {
              reject(error);
            });
          }
        });
      };
      /**
       * Synchronize the workspace with current checked out branch, but do not push local changes
       *
       * @return {Promise.Boolean} On resolve, the promise returns a boolean indicating success.
       */
      Workspace.prototype.pull = function () {
        return this.rebase({ push: false });
      };
      /**
       * Set the synchronization mode of the Workspace.
       * These include:
       * - MANUAL: Application needs to update and push manually. Commit is local in this mode.
       * - PUSH: Workspace automatically pushes local commits to the server
       * - PULL: Workspace automatically pulls remote changes without pushing it's changes back
       * - SYNCHRONIZE: Workspace updates and pushes automatically
       * Defaults to SYNCHRONIZE
       *
       * @param {LYNX.Property.Workspace.SYNC_MODE} in_value - Whether or not to auto pull/push.
       */
      Workspace.prototype.setSynchronizeMode = function (in_value) {
        if (in_value !== this.SYNC_MODE.MANUAL && in_value !== this.SYNC_MODE.SYNCHRONIZE && in_value !== this.SYNC_MODE.PULL && in_value !== this.SYNC_MODE.PUSH) {
          console.error('Workspace.setSynchronizeMode: Value unknown.');
        } else {
          this._syncMode = in_value;
        }
      };
      /**
       * Push local commits onto the remote branch.
       * @param {object} [in_options] - Set of options. NOTE: options are not supported yet
       * @return {Promise} - Array of commit nodes if resolved
       */
      Workspace.prototype.push = function (in_options) {
        return this._HFDM._push(this._checkoutView, in_options);
      };
      /**
       * Checks out the given Repository into the supplied checkout view
       *
       * @param {string} in_commitOrBranchUrn - URN identifying the commit or branch to check out
       * @return {Promise}                    - Promise
       */
      Workspace.prototype.checkout = function (in_commitOrBranchUrn) {
        var that = this;
        return this._HFDM._checkoutAttempt(in_commitOrBranchUrn, this._checkoutView).then(function () {
          that._activeUrn = in_commitOrBranchUrn;
        });
      };
      /**
       * Expand a path to a given property within the PropertySet that the workspace has checked out and
       * return the value at the end.
       *
       * @param {string} in_path - Path to be resolved.
       * @return {LYNX.Property.BaseProperty|undefined} resolved path
       */
      Workspace.prototype.resolvePath = function (in_path) {
        console.assert(this.isCheckedOut(), 'Cannot resolve path. ' + MSG.NO_REPOSITORY_CHECKED_OUT);
        return this._checkoutView.getRoot().resolvePath(in_path);
      };
      /**
       * Delays notifications until popModifiedEventScope has been called the same number of times as
       * pushModifiedEventScope.
       */
      Workspace.prototype.pushModifiedEventScope = function () {
        this._checkoutView.pushNotificationDelayScope();
      };
      /**
       * Reenables notifications when popModifiedEventScope has been called the same number of times as
       * pushModifiedEventScope.
       */
      Workspace.prototype.popModifiedEventScope = function () {
        this._checkoutView.popNotificationDelayScope();
      };
      /**
       *
       * Insert a property at the root of the PropertySet that the workspace has checked out.
       * NOTE: This will throw an error if the workspace is not at the HEAD of the checked out branch
       *       or if the repository hasn't been checked out yet.
       *
       * @param {String} [in_id] - The id under which the property is inserted. This parameter is
       *                           optional. If it is not supplied, the id already stored in
       *                           the property object is used. Otherwise it is overwritten
       *                           with the supplied id
       * @param {LYNX.Property.BaseProperty} in_property - The property to insert
       */
      Workspace.prototype.insert = function (in_id, in_property) {
        console.assert(this.isCheckedOut(), 'Cannot insert. ' + MSG.NO_REPOSITORY_CHECKED_OUT);
        this._checkoutView.getRoot().insert(in_id, in_property);
      };
      /**
       * Remove a property from the root of the PropertySet that the workspace has checked out.
       * NOTE: This will throw an error if the workspace is not at the HEAD of the checked out branch
       *       or if the repository hasn't been checked out yet.
       *
       * @param {string|LYNX.Property.BaseProperty} in_property - Property to be removed
       *                                                          (either its id or the whole property).
       */
      Workspace.prototype.remove = function (in_property) {
        console.assert(this.isCheckedOut(), 'Cannot remove. ' + MSG.NO_REPOSITORY_CHECKED_OUT);
        this._checkoutView.getRoot().remove(in_property);
      };
      /**
       * Returns the name of all the properties at the root of the PropertySet that
       * the workspace has checked out.
       *
       * @return {Array.<string>} An array of property ids
       */
      Workspace.prototype.getIds = function () {
        console.assert(this.isCheckedOut(), 'Cannot get ids. ' + MSG.NO_REPOSITORY_CHECKED_OUT);
        return this._checkoutView.getRoot().getIds();
      };
      /**
       * Returns the property having the given name at the root of the PropertySet
       * that the workspace has checked out.
       *
       * @param  {string} in_id the ID of the property
       * @return {LYNX.Property.BaseProperty|undefined} The property you seek
       */
      Workspace.prototype.get = function (in_id) {
        console.assert(this.isCheckedOut(), 'Cannot get a property. ' + MSG.NO_REPOSITORY_CHECKED_OUT);
        return this._checkoutView.getRoot().get(in_id);
      };
      /**
       * Checks whether a property with the given name exists at the root of the
       * PropertySet that the workspace has checked out.
       *
       * @param {string} in_id - Name of the property
       * @return {boolean} Does the property exist?
       */
      Workspace.prototype.has = function (in_id) {
        console.assert(this.isCheckedOut(), 'Cannot test for property existence. ' + MSG.NO_REPOSITORY_CHECKED_OUT);
        return this._checkoutView.getRoot().has(in_id);
      };
      /**
       * Get all properties at the root of the PropertySet that the workspace has checked out.
       * Caller MUST NOT modify the properties.
       *
       * @return {Object.<LYNX.Property.BaseProperty>|null} An object containing all the properties or null on error
       */
      Workspace.prototype.getEntriesReadOnly = function () {
        console.assert(this.isCheckedOut(), 'Cannot get entries. ' + MSG.NO_REPOSITORY_CHECKED_OUT);
        return this._checkoutView.getRoot().getEntriesReadOnly();
      };
      /**
       * returns a count if the local branch is ahead of the remote branch.
       * This is the case where the application hasn't pushed it's local changes to the remote branch
       * @return {Number} the number of commits by which your workspace is ahead.
       */
      Workspace.prototype.getAheadCount = function () {
        var lcaPaths = [];
        var repository = this.getActiveRepository();
        var branch = this.getActiveBranch();
        var remoteBranch = branch.getRemoteBranch();
        if (!remoteBranch) {
          console.warn('remote branch does not exist');
          return 0;
        }
        repository._getCommitGraph().lowestCommonAncestor(branch.getHead(), remoteBranch.getHead(), { out_paths: lcaPaths });
        var aheadCommits = lcaPaths.pop().pathFromA;
        // Ignore the first element in the array (LCA)
        aheadCommits.shift();
        return aheadCommits.length;
      };
      /**
       * returns a count if the local branch is behind the remote branch
       * This is the case where the application has not pulled remote changes into the local branch
       *
       * @return {Number} The number of commits by which your workspace is behind.
       */
      Workspace.prototype.getBehindCount = function () {
        var lcaPaths = [];
        var repository = this.getActiveRepository();
        var branch = this.getActiveBranch();
        var remoteBranch = branch.getRemoteBranch();
        if (!remoteBranch) {
          console.warn('remote branch does not exist');
          return 0;
        }
        repository._getCommitGraph().lowestCommonAncestor(remoteBranch.getHead(), branch.getHead(), { out_paths: lcaPaths });
        var behindCommits = lcaPaths.pop().pathFromA;
        behindCommits.shift();
        return behindCommits.length;
      };
      /**
       * Get the root property of the workspace. This is effectively the PropertySet that the workspace has checked out.
       * @return {Object}  The root repository that has been checked out.
       */
      Workspace.prototype._getRoot = function () {
        return this._checkoutView.getRoot();
      };
      /**
       * If this flag is set to true, the Workspace will generate a merged changeset for the modified events
       * that contains all changes to referenced repositories. If it is set to false, the modified event only contains the
       * changes to the root. If an application uses the modifiedPerRepository events instead of the modified,
       * disabling the creation of merged changesets for the modified events avoids unnecessary computations.
       *
       * @param {Boolean} in_enabled - Are merged Changesets enabled
       */
      Workspace.prototype._enableMergedOnModifiedChangeset = function (in_enabled) {
        this._checkoutView._enableMergedOnModifiedChangeset(in_enabled);
      };
      /**
       * Initialize bindings to HFDM and checkout view.
       */
      Workspace.prototype._initBindings = function () {
        var that = this;
        var key;
        key = this._checkoutView.register('modified', function (checkoutView, changeSet) {
          that.trigger('modified', that, [changeSet]);
        });
        this._bindings.push({
          object: this._checkoutView,
          event: 'modified',
          key: key
        });
        key = this._checkoutView.register('modifiedPerRepository', function (checkoutView, modifications) {
          that.trigger('modifiedPerRepository', that, [modifications]);
        });
        this._bindings.push({
          object: this._checkoutView,
          event: 'modifiedPerRepository',
          key: key
        });
        key = this._HFDM.register('branchMoved', function (repository, branchNode, commitNode) {
          if (that.isCheckedOut() && !that.isDetachedHead() && that.getActiveBranch().isEqual(branchNode)) {
            that.trigger('branchMoved', that, [
              commitNode,
              branchNode
            ]);
            if (branchNode.isRemoteBranch()) {
              that.trigger('newRemoteCommit', that, [commitNode]);
            }
            if (!that._currentlyUpdatingRemote) {
              // we've got a notification that the remote branch has moved
              that._currentlyUpdatingRemote = true;
              switch (that._syncMode) {
              case that.SYNC_MODE.SYNCHRONIZE:
                // pull, rebase, push
                that.synchronize(null).then(function (success) {
                  that._currentlyUpdatingRemote = false;
                }).catch(function (error) {
                  console.error('HFDM "branchMoved" event error:', error);
                  that._currentlyUpdatingRemote = false;
                });
                break;
              case that.SYNC_MODE.PULL:
                // just pull, but don't push
                that.pull();
                break;
              case that.SYNC_MODE.PUSH:
              case that.SYNC_MODE.MANUAL:
              default:
                // nothing to do (we don't automatically sync to remote changes in these settings)
                break;
              }
              that._currentlyUpdatingRemote = false;
            }
          }
        });
        this._bindings.push({
          object: this._HFDM,
          event: 'branchMoved',
          key: key
        });
        this._checkoutView.register('onReferenceHeadMoved', function () {
          throw new Error(MSG.NOT_IMPLEMENTED);
        });
        this._bindings.push({
          object: this._checkoutView,
          event: 'onReferenceHeadMoved',
          key: key
        });
        this._checkoutView.register('onReferenceRebase', function () {
          throw new Error(MSG.NOT_IMPLEMENTED);
        });
        this._bindings.push({
          object: this._checkoutView,
          event: 'onReferenceRebase',
          key: key
        });
        this._checkoutView.register('onReferenceLoaded', function () {
          throw new Error(MSG.NOT_IMPLEMENTED);
        });
        this._bindings.push({
          object: this._checkoutView,
          event: 'onReferenceLoaded',
          key: key
        });
        this._checkoutView.register('onAllReferencesLoaded', function () {
          this.trigger('onAllReferencesLoaded', that, []);
        });
        this._bindings.push({
          object: this._checkoutView,
          event: 'onAllReferencesLoaded',
          key: key
        });
      };
      /**
       * Destroy the workspace by removing all its bindings.
       * NOTE: The workspace will no longer be usable after calling this function
       * @private
       */
      Workspace.prototype._destroy = function () {
        _.each(this._bindings, function (entry) {
          entry.object.unregister(entry.event, entry.key);
        });
        this.trigger('destroyed');
        var that = this;
        _.each(Object.getOwnPropertyNames(Workspace.prototype), function (funcName) {
          if (_.isFunction(that[funcName])) {
            that[funcName] = function () {
              console.error('Workspace has been destroyed. Please create a new Workspace');
            };
          }
        });
      };
      /**
       * Leaves the branch if working on an active branch then destroys the workspace
       * then destroys the workspace by removing all its bindings.
       * NOTE: The workspace will no longer be usable after calling this function
       * @return {Promise} - Promise
       */
      Workspace.prototype.destroy = function () {
        return this._HFDM.destroyWorkspace(this);
      };
      /**
       * Returns the internal CheckoutView of this workspace
       * @return {LYNX.Property.CheckoutView} The checkout view
       */
      Workspace.prototype.getCheckoutView = function () {
        return this._checkoutView;
      };
      /**
       * Repeatedly calls back the given function with human-readable string representations
       * of the root property and of its sub-properties. By default it logs to the console.
       *
       * @param {function} [printFct=console.log] - Function to call for printing each property
       */
      Workspace.prototype.prettyPrint = function (printFct) {
        if (this.isCheckedOut()) {
          this._checkoutView.getRoot().prettyPrint(printFct);
        } else {
          if (typeof printFct !== 'function') {
            printFct = console.log;
          }
          printFct(MSG.NO_REPOSITORY_CHECKED_OUT);
        }
      };
      exportModule('LYNX.Property.Workspace', Workspace);
    }());
    (function () {
      var EventEmitter = include('LYNX.Utils.Events.EventEmitter');
      var EMPTY_FUNCTION = include('LYNX.Utils.emptyFunction');
      var Repository = include('LYNX.Property.Repository');
      var CheckoutView = include('LYNX.Property.CheckoutView');
      var _ = include('underscore');
      var async = include('LYNX.Utils.ThirdParty.async');
      var MSG = include('LYNX.Property._constants.MSG');
      var Environment = include('LYNX.Utils.Environment');
      var Workspace = include('LYNX.Property.Workspace');
      var UrnUtils = include('LYNX.Utils.UrnUtils');
      /**
       * Encapsulates lower level Repository management logic such as:
       * updating repositories, creating repositories, collaboration, etc.
       * @constructor
       * @alias LYNX.Property.HFDM
       */
      var Hfdm = function () {
        /**
         * Map Repository GUID to its branch GUIDs and commitGUIDs and local: boolean
         * @type {Object}
         */
        this._repositoryGuidRepositoryMap = {};
        /**
         * Map branch GUID to Repository
         * @type {Object}
         */
        this._branchGuidRepositoryMap = {};
        /**
         * Map commit GUID to Repository
         * @type {Object}
         */
        this._commitGuidRepositoryMap = {};
        /**
         * references counter, increments and decrements when joining and leaving a branch
         * @type {Object}
         */
        this._referenceCounter = {};
        /**
         * map branch guid to checkout counter
         * @type {object}
         */
        this._checkoutCounter = {};
        /**
         * Are the checkout counters disabled?
         * @type {Boolean}
         */
        this._checkoutCounterDisabled = false;
        // property sets client reference used to connect to the backend
        // set in the connect() method
        this._propertySetsClient = undefined;
        // Set of locks
        this._locks = {
          // Lock used to track the commit guid that is being pushed to the server
          pushCommits: new Set()
        };
        EventEmitter.call(this);
      };
      Hfdm.prototype = Object.create(EventEmitter.prototype);
      /**
       * The getBearerToken callback which the user-supplied function will invoke with the
       * user's bearer token.
       * @callback getBearerTokenCallback
       * @private
       * @param {Error} err An error object
       * @param {string} bearerToken The OAuth2 bearer token representing the user
       */
      /**
       * A user-supplied function to provide the user's bearer token.
       * @function getBearerTokenFn
       * @private
       * @param {getBearerTokenCallback} cb The callback function which should be passed an error or
       *   the OAuth2 bearer token representing the user
       */
      /**
       * Connect to the PropertySetsServer
       * @param {object} in_options connection options
       * @param {string=} [in_params.serverUrl] The url pointing to the PropertySetsServer
       * @param {getBearerTokenFn=} [in_params.getBearerToken] Function that accepts a callback
       *     function that should be called with an error or the OAuth2 bearer token representing the user.
       * @param {object|undefined=} [in_params.headers] Headers to supply when making requests (used by tests)
       *
       * @return {Promise} - Resolved Promise with no value, else rejected Error
       */
      Hfdm.prototype.connect = function (in_options) {
        /*
         * The parameter propertySetsClient is only for internal use. It is intentionally not documented above in the
         * external API specification.
         *
         * @param {LYNX.PropertySetsServer.Client} [in_params.propertySetsClient] The property sets client instance (this
         *     can be supplied instead of a server URL to use an already existing propertySetsClient for the connection)
         */
        var that = this;
        in_options = in_options || {};
        return new Promise(function (resolve, reject) {
          if (in_options.serverUrl && !_.isString(in_options.serverUrl)) {
            reject(new Error(MSG.SERVER_URL_NOT_STRING));
            return;
          }
          if (in_options.getBearerToken && !_.isFunction(in_options.getBearerToken)) {
            reject(new Error(MSG.GETBEARERTOKEN_NOT_FUNCTION));
            return;
          }
          if (in_options.headers && !_.isObject(in_options.headers)) {
            reject(new Error(MSG.HEADERS_NOT_OBJECT));
            return;
          }
          if (that.isConnected()) {
            reject(new Error(MSG.ALREADY_CONNECTED));
            return;
          }
          if (that._isConnecting()) {
            reject(new Error(MSG.CONNECTING));
            return;
          }
          if (in_options.propertySetsClient) {
            // Use the supplied PropertySets client
            that._propertySetsClient = in_options.propertySetsClient;
          } else {
            // Create new PropertySetsServer client
            if (Environment.isBrowser) {
              var BrowserClient = include('LYNX.PropertySetsServer.BrowserClient');
              that._propertySetsClient = new BrowserClient(in_options);
            } else {
              var NodeClient = include('LYNX.PropertySetsServer.NodeClient');
              that._propertySetsClient = new NodeClient(in_options);
            }
          }
          that._propertySetsClient.once('connected', function () {
            that._propertySetsClient.registerToUpdates(function (message) {
              var params = _.extend({ updateRemoteBranch: true }, message.data);
              that._addCommitNode(params);
            });
          });
          that._propertySetsClient.on('connected', function () {
            that.trigger('connected', that);
          });
          that._propertySetsClient.on('reconnect', function (attemptNumber) {
            // We send another connected message upon a reconnect
            that.trigger('connected', that);
          });
          that._propertySetsClient.on('disconnected', function (reason) {
            that.trigger('disconnected', that, [reason]);
          });
          that._propertySetsClient.connect(in_options, function (error) {
            if (error) {
              reject(error);
            } else {
              resolve();
            }
          });
        });
      };
      /**
       * Disconnect from the backend
       * This method first leaves all joined branches then disconnects from the backend
       * @return {Promise} - Resolved Promise with no value, else rejected Error
       */
      Hfdm.prototype.disconnect = function () {
        var that = this;
        return new Promise(function (resolve, reject) {
          if (!that.isConnected()) {
            reject(new Error('Cannot disconnect. ' + MSG.NOT_CONNECTED));
            return;
          }
          var joinedRooms = that._propertySetsClient.getRooms();
          async.eachLimit(joinedRooms, 5, function (room, asyncCallback) {
            if (!that._propertySetsClient.hasJoined(room.name)) {
              asyncCallback();
            } else {
              that._leave(room.name, null).then(function (repository) {
                asyncCallback();
              }).catch(function (error) {
                asyncCallback(error);
              });
            }
          }, function (error) {
            that._propertySetsClient.disconnect();
            delete that._propertySetsClient;
            if (error) {
              reject(error);
            } else {
              resolve();
            }
          });
        });
      };
      /**
       * Update the Repository commit graph by adding a new commit node.
       * @param {object} in_params List of options
       * @param {object|undefined} [in_params.commit] object describing a commit
       * @param {Array<object>|undefined} [in_params.commits] Array of objects describing the individual commits
       *  within a batch. NOTE: If this parameter is not specified then "commit" param must be specified
       * @param {LYNX.Property.ChangeSet} in_params.changeSet ChangeSet coupled with the commit object
       * @param {object} in_params.branch The branch information
       * @param {string} in_params.branch.guid The guid of the branch that the commit belongs to
       *  Note: This will change soon as commits may belong to multiple branches
       * @private
       */
      Hfdm.prototype._addCommitNode = function (in_params) {
        var repository = this._branchGuidRepositoryMap[in_params.branch.guid];
        if (in_params.commits) {
          _.each(in_params.commits, function (commit) {
            repository._addCommitNode(commit, in_params.branch.guid, { updateRemoteBranch: in_params.updateRemoteBranch });
          });
        } else {
          repository._addCommitNode(in_params.commit, in_params.branch.guid, { updateRemoteBranch: in_params.updateRemoteBranch });
        }
      };
      /**
       * Return the Repository specified by the given Urn
       * @param {string} in_urn Urn representing a repository, branch or commit Urn
       * @return {LYNX.Property.Repository} The Repository or undefined if not found
       */
      Hfdm.prototype.getRepository = function (in_urn) {
        var guid = UrnUtils.toGuid(in_urn);
        if (UrnUtils.isRepoUrn(in_urn)) {
          return this._repositoryGuidRepositoryMap[guid] && this._repositoryGuidRepositoryMap[guid].repository;
        }
        if (UrnUtils.isBranchUrn(in_urn)) {
          return this._branchGuidRepositoryMap[guid];
        }
        if (UrnUtils.isCommitUrn(in_urn)) {
          // Commit guids can also include an optional branch, which is unused here
          guid = UrnUtils.commitToGuids(in_urn);
          return this._commitGuidRepositoryMap[guid.commitGuid];
        }
        // in_urn was not a recognizable Urn.
        return undefined;
      };
      /**
       * Method used to check whether the HFDM client is connected to the backend
       * @return {boolean} Returns true if we are connected to the backend
       */
      Hfdm.prototype.isConnected = function () {
        return !!(this._propertySetsClient && this._propertySetsClient.isConnected());
      };
      /**
       * Method used to check whether the HFDM client is connecting to the backend
       * @return {boolean} Returns true if we are in the process of connecting to the backend
       * @private
       */
      Hfdm.prototype._isConnecting = function () {
        return !!(this._propertySetsClient && this._propertySetsClient.isConnecting());
      };
      /**
       * Internal method to return the internally created PropertySetsServer client
       * @return {LYNX.PropertySetsServer.Client} Returns the PropertySetsServer client which was created internally.
       * @private
       */
      Hfdm.prototype._getBackendAdapter = function () {
        return this._propertySetsClient;
      };
      /**
       * Join a branch session specified by the branch guid
       * @param {string} in_branchGuid The branch guid to join on
       * @param {Object} in_options List of options for the join call of the propertySetsClient
       * @return {Promise.<{
       *   repository: LYNX.Property.Repository,
       *   branch: LYNX.Property.BranchNode
       * }>}
       *     On resolve, the promise returns the repository and the branch we have joined.
       * @private
       */
      Hfdm.prototype._join = function (in_branchGuid, in_options) {
        var that = this;
        return new Promise(function (resolve, reject) {
          if (!that.isConnected()) {
            reject(new Error('Cannot join. ' + MSG.NOT_CONNECTED));
            return;
          }
          in_options = in_options || {};
          var repository = that._branchGuidRepositoryMap[in_branchGuid];
          that._referenceCounter[in_branchGuid] = that._referenceCounter[in_branchGuid] || 0;
          // Make join idempotent
          if (that._referenceCounter[in_branchGuid] > 0) {
            console.assert(that._propertySetsClient.hasJoined(in_branchGuid), 'Invalid state, join counter should be > 0');
            that._referenceCounter[in_branchGuid]++;
            var branch = repository.getBranch(in_branchGuid);
            resolve({
              repository: repository,
              branch: branch
            });
            return;
          }
          console.assert(!that._propertySetsClient.hasJoined(in_branchGuid), 'Invalid state, join counter should be 0');
          if (!repository) {
            in_options.rootCommit = true;
            in_options.flatten = true;
            in_options.containerInfo = true;
            that._propertySetsClient.joinBranchSession(in_branchGuid, in_options, function (error, repositoryJSON) {
              if (error) {
                reject(error);
                return;
              }
              that._referenceCounter[in_branchGuid]++;
              var repositoryJSON = _.defaults(repositoryJSON, { flatten: in_options.flatten });
              repository = that._createRemoteRepository(repositoryJSON);
              var joinedBranch = repository.getBranch(in_branchGuid);
              resolve({
                repository: repository,
                branch: joinedBranch
              });
            });
          } else {
            in_options.flatten = true;
            that._propertySetsClient.joinBranchSession(in_branchGuid, in_options, function (error, repositoryJSON) {
              if (error) {
                reject(error);
                return;
              }
              that._referenceCounter[in_branchGuid]++;
              var joinedBranch = repository.getBranch(in_branchGuid);
              if (joinedBranch && joinedBranch.getHead().getGuid() === _.last(repositoryJSON.commits).guid) {
                // If we already have the correct branch in the graph, we reuse the existing
                // branch node
                resolve({
                  repository: repository,
                  branch: joinedBranch
                });
                return;
              } else {
                // Delete the old branch
                repository._removeBranch(in_branchGuid);
                // And reinitialize from the server response
                var repositoryJSON = _.defaults(repositoryJSON, { flatten: in_options.flatten });
                that._updateRepository(repository, repositoryJSON);
                joinedBranch = repository.getBranch(in_branchGuid);
                resolve({
                  repository: repository,
                  branch: joinedBranch
                });
              }
            });
          }
        });
      };
      /**
       * Leave a branch session specified by the branch guid
       * @param {string} in_branchGuid The branch guid to leave
       * @param {object} in_options - Options to be passed to leaveBranchSession
       * @return {Promise} - Promise that gets resolved once the leave operation succeeded
       * @private
       */
      Hfdm.prototype._leave = function (in_branchGuid, in_options) {
        var that = this;
        return new Promise(function (resolve, reject) {
          if (!that.isConnected()) {
            reject(new Error('Cannot leave. ' + MSG.NOT_CONNECTED));
            return;
          }
          that._referenceCounter[in_branchGuid] = that._referenceCounter[in_branchGuid] || 0;
          if (that._referenceCounter[in_branchGuid] === 0) {
            reject(new Error('Cannot leave. ' + MSG.NOT_JOINED + ' ' + in_branchGuid));
            delete that._referenceCounter[in_branchGuid];
            return;
          }
          if (that._referenceCounter[in_branchGuid] > 1) {
            that._referenceCounter[in_branchGuid]--;
            console.assert(that._propertySetsClient.hasJoined(in_branchGuid), 'Invalid state, join counter should be 0');
            resolve();
            return;
          }
          console.assert(that._propertySetsClient.hasJoined(in_branchGuid), 'Invalid state, join counter should be 1');
          console.assert(_.isString(in_branchGuid), 'in_branchGuid must be a GUID');
          var repository = that._branchGuidRepositoryMap[in_branchGuid];
          if (repository) {
            var repositoryState = that._repositoryGuidRepositoryMap[repository.getGuid()];
            if (repositoryState.local) {
              reject(new Error('Cannot call leave on a local branch'));
            } else {
              // TBD: How do we handle nested repositories (repositories references)?
              // What should happen when you leave a branch? Do we delete it from the tree?
              // Do we reinitialize it when we call join again?
              that._propertySetsClient.leaveBranchSession(in_branchGuid, in_options, function (error, response) {
                if (error) {
                  reject(error);
                } else {
                  that._referenceCounter[in_branchGuid]--;
                  if (that._referenceCounter[in_branchGuid] === 0) {
                    that._removeBranch(repository, in_branchGuid);
                    delete that._referenceCounter[in_branchGuid];
                  }
                  resolve();
                }
              });
            }
          } else {
            var error = new Error(MSG.UNKNOWN_REPOSITORY + in_branchGuid);
            reject(error);
          }
        });
      };
      /**
       * Remove the branch from the Repository and from our local state
       * @param  {LYNX.Property.Repository} in_repository - The Repository in which the branch is removed
       * @param  {string} in_branchGuid - The GUID of the branch to remove
       * @private
       */
      Hfdm.prototype._removeBranch = function (in_repository, in_branchGuid) {
        console.assert(_.isString(in_branchGuid), 'in_branchGuid must be a GUID');
        console.assert(in_repository instanceof Repository);
        var that = this;
        var otherBranches = _.filter(that._branchGuidRepositoryMap, function (otherRepository, branchGuid) {
          return branchGuid !== in_branchGuid && otherRepository.getGuid() === in_repository.getGuid();
        });
        delete that._branchGuidRepositoryMap[in_branchGuid];
        if (!otherBranches) {
          var repositoryState = that._repositoryGuidRepositoryMap[in_repository.getGuid()];
          _.each(repositoryState.branches, function (ignore, branchGuid) {
            delete that._branchGuidRepositoryMap[branchGuid];
          });
          _.each(repositoryState.commits, function (ignore, commitGuid) {
            delete that._commitGuidRepositoryMap[commitGuid];
          });
          _.each(repositoryState.callbacks, function (callback, event) {
            in_repository.off(event, callback);
          });
        } else {
          var commitGUIDs = _.keys(in_repository._getCommitGraph().getCommitNodes());
          _.each(commitGUIDs, function (commitGUID) {
            delete that._commitGuidRepositoryMap[commitGUID];
          });
        }
        in_repository._removeBranch(in_branchGuid);
      };
      /**
       * Update the given Repository with the given commits for a given branch
       * 1. Only to be used for a set of commits from a single branch
       * 2. Flatten will only be true if there is just a single commit. This is used when pulling a
       *   set of many commits from the server as a single squashed commit. This is done for efficiency.
       * @param {LYNX.Property.Repository} in_repository - The Repository to update
       * @param {object} in_params The list of parameters
       * @param {Array.<object>} in_params.commits The list of commit objects
       *                                           Includes the guid of the commit plus all metadata
       * @param {object} in_params.branch The branch object parameter
       *                                  Includes the guid of the branch plus all metadata
       * @param {boolean} in_params.flatten flag used to indicate that the commit history is flattened
       *                                    This flag is generally set to true when loading a branch that doesn't
       *                                    exist locally. Defaults to false.
       * @private
       */
      Hfdm.prototype._updateRepository = function (in_repository, in_params) {
        console.assert(in_params.commits && in_params.branch);
        _.each(in_params.commits, function (commit) {
          var commitNode = in_repository._addCommitNode(commit);
          // Only true for a Repository pulled from a server
          if (in_params.flatten) {
            console.assert(in_params.commits.length === 1, 'Flatten is set to true but we have more than one commit');
            var rootNode = in_repository.getRootNode();
            console.assert(!!rootNode, 'Commit node is normalized but the rootNode is not set');
            // If the Repository is an existing one (not a new one that was just created or just fetched)
            // then it could be that we already have the commit
            var hasParent = !!commitNode.getFirstParent();
            if (!hasParent) {
              commitNode._setNormalized(true);
              // normalized == squashed == flatten(ed)
              if (rootNode !== commitNode) {
                commitNode._addParent(rootNode);
              }
            }
            in_repository._branch(in_params.branch, commitNode.getGuid(), { trackRemoteBranch: true });
          }
        });
      };
      /**
       * Create a remote Repository
       * Remote repositories exist on a server and can exist with other collaborators
       * @param  {object}   in_params The list of parameters
       * @param  {object=}  in_params.changeSet The flattened change set to
       *                                        be set as the root commit.
       * @param  {object}   in_params.repository The repository object parameter
       *                                        Includes the guid of the repository plus all metadata
       * @param  {string}   in_params.repository.guid - GUID of the repository
       * @param  {string=}  in_params.repository.creatorId The Oxygen user ID of the Repository creator
       * @param  {string}   in_params.rootCommit - The root commit object to add to the Repository
       * @param  {string}   in_params.rootCommit.guid Guid representing the root commit of the Repository
       *                                             This is generally set when loading a Repository for the first time
       * @return {LYNX.Property.Repository} The created Repository
       */
      Hfdm.prototype._createRemoteRepository = function (in_params) {
        console.assert(in_params.rootCommit.guid, 'An initial commit must be specified');
        // TODO: Currently, the repository information is still under the old container namespace
        // Change this to in_params.repository once we have fixed the return value of HFDMServer
        // see LYNXDEV-257
        var repository = new Repository(in_params.container);
        var callbacks = this._bindRepositoryListeners(repository);
        this._repositoryGuidRepositoryMap[repository.getGuid()] = {
          repository: repository,
          local: false,
          commits: {},
          branches: {},
          callbacks: callbacks
        };
        // We already have a root commit
        // because either the backend created it or we joined an existing branch
        var rootNode = repository._addCommitNode({
          guid: in_params.rootCommit.guid,
          meta: { name: 'Root commit' },
          changeSet: in_params.changeSet || {}
        });
        repository._setRootNode(rootNode);
        this._updateRepository(repository, in_params);
        this._registerBranches(repository);
        return repository;
      };
      /**
       * Take all of the branches of the Repository and add them to our accelerated data structures
       * @param  {LYNX.Property.Repository} in_repository - The Repository to register
       */
      Hfdm.prototype._registerBranches = function (in_repository) {
        var brancheGUIDs = _.keys(in_repository._getCommitGraph().getBranchNodes());
        var that = this;
        _.each(brancheGUIDs, function (brancheGUID) {
          that._branchGuidRepositoryMap[brancheGUID] = in_repository;
        });
      };
      /**
       * Create a local Repository
       * Local repositories do not exist anywhere else
       *
       * @param {String} [in_guid] - The GUID of the repository
       * @return {Repository} The created Repository
       */
      Hfdm.prototype._createLocalRepository = function (in_guid) {
        var repository = new Repository({ guid: in_guid });
        this._bindRepositoryListeners(repository);
        this._repositoryGuidRepositoryMap[repository.getGuid()] = {
          repository: repository,
          local: true,
          commits: {},
          branches: {}
        };
        // We do not have a initial commit
        repository._initMaster();
        this._registerBranches(repository);
        return repository;
      };
      /**
       * Bind to Repository events.
       * Some events will be bubbled up and triggered on the HFDM such as `branchMoved`.
       * @param {LYNX.Property.Repository} in_repository The Repository to bind listeners to.
       *
       * @return {{branchMoved: Function,
       *           onBranch: Function,
       *           onAddCommitNode: Function,
       *           onCommitEnd: Function,
       *           onCommitBegin: Function,
       *           onBranchReset: Function
       *         }} The callbacks that were bound
       * @private
       */
      Hfdm.prototype._bindRepositoryListeners = function (in_repository) {
        var that = this;
        var callbacks = {
          branchMoved: function () {
            that.trigger('branchMoved', that, _.values(arguments));
          },
          onBranch: function (branch) {
            that._repositoryGuidRepositoryMap[in_repository.getGuid()].branches[branch.getGuid()] = true;
            that._branchGuidRepositoryMap[branch.getGuid()] = in_repository;
          },
          onAddCommitNode: function (commit) {
            that._repositoryGuidRepositoryMap[in_repository.getGuid()].commits[commit.getGuid()] = true;
            that._commitGuidRepositoryMap[commit.getGuid()] = in_repository;
          },
          onCommitEnd: function () {
            that.trigger('onCommitEnd', that, _.values(arguments));
          },
          onCommitBegin: function () {
            that.trigger('onCommitBegin', that, _.values(arguments));
          },
          onBranchReset: function () {
            that.trigger('onBranchReset', that, _.values(arguments));
          }
        };
        _.each(callbacks, function (callback, event) {
          in_repository.on(event, callback);
        });
        return callbacks;
      };
      /**
       * Create a single Repository
       * @param {object} in_params list of options passed to createRepositories
       * @return {Promise.<LYNX.Property.Repository, LYNX.PropertyGraph.Nodes.BranchNode,
       *  LYNX.PropertyGraph.Nodes.CommitNode>} - Resolved repository, else rejected Error
       * @private
       */
      Hfdm.prototype._createRepository = function (in_params) {
        var that = this;
        in_params = in_params || {};
        var params = _.extend(in_params, { count: 1 });
        return new Promise(function (resolve, reject) {
          that._createRepositories(params).then(function (results) {
            var result = _.first(results);
            resolve({
              repository: result.repository,
              branch: result.branchNode,
              commit: result.commitNode
            });
          }).catch(function (error) {
            reject(error);
          });
        });
      };
      /**
       * Create a series of repositories.
       *  Note: If the HFDM is not connected to the backend, the repository will be created locally
       * @param {object=} [in_options] Additional options
       * @param {number=} [in_options.count] Number of repositories to create (takes precedence over in_options.qs.count)
       * @param {object=} [in_options.headers] Objects to be appended as headers in the request
       * @param {boolean=} [in_options.local] Flag used to create local repository
       * @param {object=} [in_options.qs] Parameters appended to the query string
       * @param {number=} [in_options.qs.count] Number of repositories to create (if in_options.count is not present)
       * @return {Promise.Array<LYNX.Property.Repository, LYNX.PropertyGraph.Nodes.BranchNode,
       *  LYNX.PropertyGraph.Nodes.CommitNode>} - Resolved repositories, else rejected Error
       * @private
       */
      Hfdm.prototype._createRepositories = function (in_options) {
        var that = this;
        in_options = in_options || {};
        in_options.qs = in_options.qs || {};
        // in_options.count takes precedence over in_options.qs.count
        in_options.qs.count = in_options.count || in_options.qs.count || 1;
        return new Promise(function (resolve, reject) {
          if (in_options.local || !that.isConnected()) {
            var localRepositories = [];
            _.times(in_options.qs.count, function () {
              var repository = that._createLocalRepository();
              var branchNode = _.first(repository.getBranchNodes({ array: true }));
              var commitNode = branchNode.getHead();
              localRepositories.push({
                repository: repository,
                branchNode: branchNode,
                commitNode: commitNode
              });
            });
            resolve(localRepositories);
          } else {
            that._propertySetsClient.createContainers(in_options, function (error, repositoriesJSON) {
              if (error) {
                reject(error);
                return;
              }
              var remoteRepositories = _.map(repositoriesJSON, function (repositoryJSON) {
                // We just created the Repository, therefore there is only a single commit (the rootCommit)
                // that the backend created
                repositoryJSON.rootCommit = repositoryJSON.commits[0];
                repositoryJSON.flatten = true;
                var repository = that._createRemoteRepository(repositoryJSON);
                var branchNode = _.first(repository.getBranchNodes({ array: true }));
                var commitNode = branchNode.getHead();
                return {
                  repository: repository,
                  branchNode: branchNode,
                  commitNode: commitNode
                };
              });
              resolve(remoteRepositories);
            });
          }
        });
      };
      /**
       * Performs a rebase of a branch onto another branch.
       *
       * This will search for the first common parent commit between two supplied branch tips (only along the direct parents
       * not via merge branches). Once this common parent is found, the changes between this parent and the rebase branch
       * will be reapplied at the tip of the onto branch. This involves a rebase transformation of the ChangeSet to take the
       * changes in the onto branch into account. For the baseProperties this will be done automatically, for more complex
       * transformations, the user can supply a callback.
       *
       * @param {LYNX.Property.BranchIdentifier|LYNX.Property.CheckoutView} in_rebaseBranchOrView -
       *     The branch or view that will be rebased. If only a branch is supplied it will be rebased on the supplied
       *     onto-branch. If a CheckoutView is supplied, first its branch will be rebased, the new rebased branch will be
       *     checked out and then then the view's pending changes will be rebased and reapplied as modified
       * @param {LYNX.Property.CommitOrBranchIdentifier} in_ontoCommitOrBranch -
       *     The commit onto which the branch should be rebased
       *
       * @param {Object} [in_options] - Options
       * @param {Function} [in_options.rebaseCallback] -
       *     A callback that is invoked to perform the rebase operation. It will be invoked separately for each commit that
       *     is rebased, and then finally again for the pending changes if applicable. The function indicates via its
       *     return value, whether the rebase was successful. If true is returned the rebase will continue, if false is
       *     returned, it will be aborted (and no changes will occur). Furthermore, the function can modify the ChangeSet
       *     in the parameter transformedChangeSet to adapt the changes to the changes in the onto-branch.
       *
       *     It will be passed an Object with these members:
       *     * {LYNX.Property.SerializedChangeSet}  transformedChangeSet - The ChangeSet that resulted from performing the
       *                                                                   rebase operation on the base types. This
       *                                                                   ChangeSet can be modified to adapt the changes
       *                                                                   to the changes in the onto-branch.
       *     * {LYNX.Property.SerializedChangeSet}  originalChangeSet    - The original ChangeSet before the rebase
       *     * {LYNX.Property.SerializedChangeSet}  ontoBranchChangeSet  - The changes between the common parent commit and
       *                                                                   the tip of the onto branch
       *     * {LYNX.Property.SerializedChangeSet}  [currentState]       - The normalized ChangeSet for the whole Repository
       *                                                                   before the application of the
       *                                                                   transformedChangeSet. It will only be supplied
       *                                                                   when in_options.trackState===true, since
       *                                                                   computing this state can be expensive.
       *     * {Array.<LYNX.Property.ConflictInfo>} conflicts            - List with the conflicts that occurred during the
       *                                                                   rebase
       *     * {LYNX.PropertyGraph.Nodes.CommitNode} [commitNode]        - The commit node that is rebased. This is
       *                                                                   undefined, when the pending changes are rebased
       * @param {boolean} [in_options.trackState = true] -
       *     Enable tracking of the normalized ChangeSet during the rebase operation. This can be disabled, since the
       *     computation of the normalized ChangeSet incurs additional costs and should only be done when it is needed
       *     by the rebase function.
       * @param {boolean} [in_options.push=true] - Flag used to indicate that any local commits that have been rebased
       *                                      onto the new commit will not be pushed to the the server.
       * @param {boolean} [in_options.squash=true] - Flag used to squash the commits during the rebase.
       *  TODO: Right now, we only support squashing the commits when rebasing. This is due to the fact
       *  that the OT logic does not yet support multi commit rebase.
       * @param {function(?Error=,?Array.<CommitNode>=)} [in_options.pushCallback] - Callback function invoked when
       * the push process has completed called with an error or an array of commits that
       * were pushed to the server ordered frorm oldest to newest
       *
       * @return {boolean} - Returns a flag indicating success. The rebase callback can cancel the rebase. In that case
       *     false will be returned.
       * @private
       */
      Hfdm.prototype._rebase = function (in_rebaseBranchOrView, in_ontoCommitOrBranch, in_options) {
        in_options = in_options || {};
        in_options.pushCallback = in_options.pushCallback || EMPTY_FUNCTION;
        in_options.push = _.isBoolean(in_options.push) ? in_options.push : true;
        var ontoCommitOrBranchGuid = _.isString(in_ontoCommitOrBranch) ? in_ontoCommitOrBranch : in_ontoCommitOrBranch.getGuid();
        var repository = this._branchGuidRepositoryMap[ontoCommitOrBranchGuid];
        if (!repository) {
          repository = this._commitGuidRepositoryMap[ontoCommitOrBranchGuid];
        }
        console.assert(repository, 'Could not find Branch or Commit with GUID: ' + in_ontoCommitOrBranch);
        if (in_rebaseBranchOrView instanceof CheckoutView) {
          in_rebaseBranchOrView.pushNotificationDelayScope();
        }
        // TODO: Right now, we only support squashing the commits when rebasing. This is due to the fact
        // that the OT logic does not yet support multi commit rebase.
        in_options.squash = true;
        var toReturn = repository._rebase(in_rebaseBranchOrView, in_ontoCommitOrBranch, in_options);
        // Check that the head of the remote branch matches the parent of the first new commit
        // (fast forward merge)
        // In that case, push newCommitNodes to the server
        // otherwise, throw error.
        if (in_rebaseBranchOrView instanceof CheckoutView) {
          in_rebaseBranchOrView.popNotificationDelayScope();
        }
        var canPush = this.isConnected() && in_options.push && toReturn === true && !this._repositoryGuidRepositoryMap[repository.getGuid()].local;
        if (canPush) {
          this._push(in_rebaseBranchOrView, null).then(function (obj) {
            in_options.pushCallback(null, obj);
          }).catch(function (error) {
            in_options.pushCallback(error);
            console.log(error.message);
          });
        }
        return toReturn;
      };
      /**
       * Push local commits to the backend if the remote branch is tracked on the server.
       * @param {LYNX.Property.BranchIdentifier|LYNX.Property.CheckoutView} in_branchOrCheckoutView -
       *  The branch to gather the local commits from in order to push to the server. If a CheckoutView
       *  is supplied, then we'll get the branch from getActiveBranch()
       * @param {object} [in_options] - Set of options. NOTE: options are not supported yet
       * @return {Promise} - Commit node on success, else rejected
       * @private
       */
      Hfdm.prototype._push = function (in_branchOrCheckoutView, in_options) {
        // Check whether the branch or checkout view can be pushed (fast forward)
        var branch = in_branchOrCheckoutView instanceof CheckoutView ? in_branchOrCheckoutView.getActiveBranch() : this.getBranch(UrnUtils.branchUrn(in_branchOrCheckoutView));
        var remoteBranch = branch.getRemoteBranch();
        var that = this;
        return new Promise(function (resolve, reject) {
          if (remoteBranch) {
            if (remoteBranch.getHead().isEqual(branch.getHead())) {
              // Already up to date
              resolve();
              return;
            }
            var repository = that._branchGuidRepositoryMap[branch.getGuid()];
            var lcaPaths = [];
            var lca = repository._getCommitGraph().lowestCommonAncestor(branch.getHead(), remoteBranch.getHead(), { out_paths: lcaPaths });
            if (lca.isEqual(remoteBranch.getHead()) && !_.isEmpty(lcaPaths)) {
              console.assert(lcaPaths.length === 1, 'There should only be one path to lca in this case');
              var pathFromA = lcaPaths.pop().pathFromA;
              // Ignore the first element in the array (LCA)
              pathFromA.shift();
              var commitsToPush = [];
              _.each(pathFromA, function (entry, i) {
                if (that._locks.pushCommits.has(entry.node.getGuid())) {
                  return;
                }
                var commitPayLoad = {
                  guid: entry.node.getGuid(),
                  base: { guid: undefined },
                  changeSet: entry.node.getChangeSetReadOnly()
                };
                if (i === 0) {
                  commitPayLoad.base.guid = remoteBranch.getHead().getGuid();
                } else {
                  commitPayLoad.base.guid = pathFromA[i - 1].node.getGuid();
                }
                that._locks.pushCommits.add(entry.node.getGuid());
                commitsToPush.push(commitPayLoad);
              });
              if (_.isEmpty(commitsToPush)) {
                // We're in the process of pushing the commits
                resolve();
                return;
              }
              var params;
              if (commitsToPush.length === 1) {
                params = {
                  branch: { guid: branch.getGuid() },
                  commit: commitsToPush[0]
                };
              } else {
                params = {
                  branch: { guid: branch.getGuid() },
                  commits: commitsToPush
                };
              }
              // Get the commit before committing or else the repo might be deleted by the time the commit
              // finishes if the user leaves before the commit finishes
              var lastCommitGuid = _.last(commitsToPush).guid;
              var commit = repository.getCommit(lastCommitGuid);
              that._propertySetsClient.commit(params, in_options, function (error) {
                if (!error) {
                  repository._resetBranch(remoteBranch, commit);
                } else {
                  error = new Error(MSG.FAILED_PUSH + error.message);
                }
                _.each(commitsToPush, function (commitPayload) {
                  that._locks.pushCommits.delete(commitPayload.guid);
                });
                if (error) {
                  reject(error);
                } else {
                  resolve(_.pluck(pathFromA, 'node'));
                  return;
                }
              });
            } else {
              reject(new Error(MSG.REBASE_NEEDED));
            }
          } else {
            reject(new Error(MSG.REMOTE_BRANCH_NOT_TRACKED));
          }
        });
      };
      /**
       * Checks out the given Repository into the supplied checkout view
       *
       * @param {string} in_commitOrBranchGuid               - GUID identifying the commit or branch to check out
       * @param {LYNX.Property.CheckoutView} in_checkoutView - The checkout view the commit is checked out into
       * @return {Promise}                                   - Resolved Promise
       * @private
       */
      Hfdm.prototype._checkout = function (in_commitOrBranchGuid, in_checkoutView) {
        // The CheckoutView needs to knows its container manager
        in_checkoutView._setHFDMObject(this);
        if (in_checkoutView.isCheckedOut() && !in_checkoutView.isDetachedHead() && in_checkoutView.getActiveBranch().getGuid() !== in_commitOrBranchGuid) {
          var previousBranchGuid = in_checkoutView.getActiveBranch().getGuid();
        }
        // We push the notification scope to prevent notifications during the checkout
        in_checkoutView.pushNotificationDelayScope();
        // We delay repository reference updates until the new parent repository has been set
        in_checkoutView._pushRepositoryReferenceUpdatesDelayScope();
        var repository = this._checkoutSingleRepository(in_commitOrBranchGuid, in_checkoutView);
        in_checkoutView._setParentRepository(repository.getGuid(), repository.getCommit(in_commitOrBranchGuid).getGuid());
        // Now that the new head has been registered, we can update the other repositories
        in_checkoutView._popRepositoryReferenceUpdatesDelayScope(true);
        in_checkoutView.popNotificationDelayScope();
        var branchExists = !!this._branchGuidRepositoryMap[in_commitOrBranchGuid];
        if (branchExists) {
          this._checkoutCounter[in_commitOrBranchGuid] = this._checkoutCounter[in_commitOrBranchGuid] || 0;
          this._checkoutCounter[in_commitOrBranchGuid]++;
        }
        if (previousBranchGuid) {
          this._checkoutCounter[previousBranchGuid]--;
          if (this._checkoutCounter[previousBranchGuid] === 0 && this.isConnected() && this._propertySetsClient.hasJoined(previousBranchGuid)) {
            var that = this;
            return this._leave(previousBranchGuid).catch(function (error) {
              that._checkoutCounter[previousBranchGuid]++;
              console.error('[checkout] Failed to leave previous checkedout branch - ' + previousBranchGuid);
              console.error(error);
              throw error;
            });
          }
        }
        return Promise.resolve();
      };
      /**
       * Attempts to checkout the given URN into the supplied checkoutView. In the case of a branch urn,we only
       * allow 1 checkout per branch. If the URN does not exist locally, we fetch the squashed commit from the server.
       * @param {string} in_commitOrBranchUrn - Urn identifying the commit or branch to check out
       * @param {LYNX.Property.CheckoutView} in_checkoutView - The checkout view the commit is checked out into
       * @return {Promise}
       * @private
       */
      Hfdm.prototype._checkoutAttempt = function (in_commitOrBranchUrn, in_checkoutView) {
        var that = this;
        return new Promise(function (resolve, reject) {
          var repo, commitGuid, branchGuid;
          if (UrnUtils.isCommitUrn(in_commitOrBranchUrn)) {
            commitGuid = UrnUtils.commitToGuids(in_commitOrBranchUrn).commitGuid;
            repo = that.getRepository(in_commitOrBranchUrn);
          } else if (UrnUtils.isBranchUrn(in_commitOrBranchUrn)) {
            branchGuid = UrnUtils.toGuid(in_commitOrBranchUrn);
            if (!that._checkoutCounterDisabled && that._checkoutCounter[branchGuid] && that._checkoutCounter[branchGuid] > 0) {
              reject(new Error('Cannot checkout a branch more than once - ' + in_commitOrBranchUrn));
              return;
            }
            repo = that.getRepository(in_commitOrBranchUrn);
          } else {
            reject(new Error('Not a valid URN. Must specify a branch or commit URN - ' + in_commitOrBranchUrn));
            return;
          }
          if (repo) {
            if (branchGuid) {
              var branch = that.getBranch(in_commitOrBranchUrn);
              if (!branch.getRemoteBranch()) {
                that._checkout(branchGuid, in_checkoutView).then(function () {
                  resolve();
                }).catch(function (error) {
                  reject(error);
                });
                return;
              }
            } else if (commitGuid) {
              that._checkout(commitGuid, in_checkoutView).then(function () {
                resolve();
              }).catch(function (error) {
                reject(error);
              });
              return;
            }
          } else if (!that.isConnected()) {
            reject(new Error('Cannot checkout a remote commit/branch - Not Connected'));
            return;
          }
          if (branchGuid) {
            that._join(branchGuid, null).then(function (params) {
              that._checkout(branchGuid, in_checkoutView).then(function () {
                resolve();
              }).catch(function (error) {
                reject(error);
              });
            }).catch(function (error) {
              reject(error);
            });
          } else {
            var commitGuid = UrnUtils.commitToGuids(in_commitOrBranchUrn).commitGuid;
            var branchGuid = UrnUtils.commitToGuids(in_commitOrBranchUrn).branchGuid;
            if (!branchGuid) {
              reject(new Error('Missing branch guid in remote commit urn.'));
            }
            var options = {
              rootCommit: true,
              containerInfo: true,
              flatten: true,
              commit: { guid: commitGuid }
            };
            that._propertySetsClient.squashedCommitHistory(branchGuid, options, function (error, repositoryJSON) {
              if (error) {
                reject(error);
                return;
              }
              var repository = that.getRepository(UrnUtils.repoUrn(repositoryJSON.container.guid));
              if (repository) {
                that._updateRepository(repository, repositoryJSON);
              } else {
                that._createRemoteRepository(repositoryJSON);
              }
              that._checkout(commitGuid, in_checkoutView).then(function () {
                resolve();
              }).catch(function (err) {
                reject(err);
              });
            });
          }
        });
      };
      /**
       * Checks out a specific repository into the CheckoutView
       *
       * @param {string} in_commitOrBranchGuid - GUID identifying the commit or branch to check out
       * @param {LYNX.Property.CheckoutView} in_checkoutView - The checkout view the commit is checked out into
       * @param {LYNX.Property.CheckoutView~CheckedOutRepositoryInfo}   [in_checkedOutRepositoryInfo] -
       *     The checkedOut repository info into which the checkout will happen. If none is given, it will be chosen based
       *     on the commit GUID
       * @param {string} [in_checkoutGroupID] - Defines a scope which is used to determine a group of repository
       *     references which share a checked out repositories
       *
       * @return {LYNX.Property.Repository|undefined} The repository that was identified by the given commit or undefined
       *                                              if the commit could not be found
       * @private
       */
      Hfdm.prototype._checkoutSingleRepository = function (in_commitOrBranchGuid, in_checkoutView, in_checkedOutRepositoryInfo, in_checkoutGroupID) {
        // We push the notification scope to prevent notifications during the checkout
        in_checkoutView.pushNotificationDelayScope();
        // Get the repository from the supplied GUID
        var repository = this._branchGuidRepositoryMap[in_commitOrBranchGuid];
        if (!repository) {
          repository = this._commitGuidRepositoryMap[in_commitOrBranchGuid];
        }
        // If no repository could be found, we return undefined
        if (!repository) {
          in_checkoutView.popNotificationDelayScope();
          return undefined;
        }
        // Check, whether the requested commit exists
        var commit = repository.getCommit(in_commitOrBranchGuid);
        if (!commit) {
          in_checkoutView.popNotificationDelayScope();
          return undefined;
        }
        // Perform the checkout
        repository._checkout(in_commitOrBranchGuid, in_checkoutView, in_checkedOutRepositoryInfo, in_checkoutGroupID);
        in_checkoutView.popNotificationDelayScope();
        return repository;
      };
      /**
       * Commit the current pending changes. The new commit node will be
       * the new head of the current branch.
       * This only works if you are at the tip of a branch – no commits on a detached head!
       * If the HFDM is connected to the backend, the new commit will also be persisted and broadcasted.
       * @param {object} in_commitMetadata - Object containing the commit meta data
       * @param {LYNX.Property.CheckoutView} in_checkoutView - The view to commit
       * @param {object=} [in_options] Additional options
       * @param {object=} [in_options.headers] Objects to be appended as headers in the request
       * @param {boolean=} [in_options.local] Flag used to create local repositories
       * @param {object=} [in_options.qs] Parameters appended to the query string
       * @return {Promise.<LYNX.PropertyGraph.Nodes.CommitNode>|undefined} On resolve, the Promise returns the commitNode
       * @private
       */
      Hfdm.prototype._commit = function (in_commitMetadata, in_checkoutView, in_options) {
        var that = this;
        in_commitMetadata = in_commitMetadata || {};
        return new Promise(function (resolve, reject) {
          var activeBranch = in_checkoutView.getActiveBranch();
          if (!activeBranch) {
            reject(new Error(MSG.MISSING_BRANCH));
            return;
          }
          if (!in_checkoutView.hasPendingChanges()) {
            // Nothing to commit
            // This is not an error, but there is no new commit node
            resolve(undefined);
            return;
          }
          in_options = in_options || {};
          var repository = that._branchGuidRepositoryMap[activeBranch.getGuid()];
          // Here we check that we are connected to the backend AND the operation is not local AND the Repository
          // is not local
          if (that.isConnected() && !in_options.local && !that._repositoryGuidRepositoryMap[repository.getGuid()].local) {
            repository.once('onCommitEnd', function (comittedRepository, commitGuid, commit, branch) {
              that._push(in_checkoutView, in_options).then(function (commits) {
                resolve(commit);
              }).catch(function (error) {
                reject(error);
              });
            });
            in_checkoutView._pushRepositoryReferenceUpdatesDelayScope();
            repository._commit(in_commitMetadata.name, in_checkoutView);
            in_checkoutView._popRepositoryReferenceUpdatesDelayScope(true);
          } else {
            in_checkoutView._pushRepositoryReferenceUpdatesDelayScope();
            var commitNode = repository._commit(in_commitMetadata.name, in_checkoutView);
            in_checkoutView._popRepositoryReferenceUpdatesDelayScope(true);
            resolve(commitNode);
          }
        });
      };
      /**
       * Fetches a specific commit into the supplied repository.
       *
       * @param {String} in_commitGUID     - GUID of the commit
       * @param {String} in_branchGUID     - GUID of the branch containing the commit
       * @param {Object} in_options        - Additional options
       * @param {function(?Error=, (?LYNX.PropertyGraph.Nodes.CommitNode|String)=)} in_callback -
       *     Callback function invoked with an error if the commit could not be retrieved
       *     or the newly created commit node (if it could not be created the GUID of the commit will be passed
       *     as second parameter)
       */
      Hfdm.prototype._fetchCommit = function (in_commitGUID, in_branchGUID, in_options, in_callback) {
        var that = this;
        // Check whether the commit is already available
        var repository = this._branchGuidRepositoryMap[in_branchGUID] && this._branchGuidRepositoryMap[in_branchGUID].repository;
        if (repository) {
          var existingCommit = repository.getCommit(in_commitGUID);
          if (existingCommit) {
            in_callback(null, existingCommit);
            return;
          }
        }
        if (!this.isConnected()) {
          in_callback(new Error(new Error(MSG.CANNOT_FETCH_COMMIT + MSG.NOT_CONNECTED)), in_commitGUID);
          return;
        }
        this._propertySetsClient.fetchCommit(in_commitGUID, in_branchGUID, in_options, function (in_error, in_commitInfo) {
          if (in_error) {
            in_callback(in_error, in_commitGUID);
            return;
          }
          if (!repository) {
            // The repository does not yet exist, we initialize it as an empty repository
            repository = that._createLocalRepository(in_commitInfo.repository.guid);
          }
          var commit = repository._addCommitNode(in_commitInfo);
          in_callback(null, commit);
        });
      };
      /**
       * Create a branch from in_parentCommitGuid
       * @param {string} in_parentCommitGuid Parameter used to specify which commit to branch from
       * @param {object=} [in_options] Additional options
       * @param {object=} [in_options.headers] Objects to be appended as headers in the request
       * @param {boolean=} [in_options.local] Flag used to create local repositories
       * @param {object=} [in_options.qs] Parameters appended to the query string
       * @param {object} [in_options.metaData] The branch metadata
       * @param {string} [in_options.metaData.name] The human readable name of the branch.
       *  If not specified, it defaults to the guid.
       * @param {string} [in_options.metaData.guid] The guid of the branch. If not specified, a guid will be generated
       * @return {Promise.<LYNX.PropertyGraph.Nodes.BranchNode>} - Resolved branch, else rejected Error
       * @private
       */
      Hfdm.prototype._branch = function (in_parentCommitGuid, in_options) {
        in_options = in_options || {};
        in_options.metadata = in_options.metadata || {};
        var repository = this._commitGuidRepositoryMap[in_parentCommitGuid];
        var that = this;
        return new Promise(function (resolve, reject) {
          if (!repository) {
            reject(new Error(MSG.NO_PARENT_COMMIT));
            return;
          }
          // Here we check that we are connected to the backend AND the operation is not local AND the Repository is not local
          var createRemoteBranch = that.isConnected() && !in_options.local && !that._repositoryGuidRepositoryMap[repository.getGuid()].local;
          if (createRemoteBranch) {
            that._propertySetsClient.branch({
              branch: {
                // TODO: There is an inconsistency between the repository.branch function and the format expected by the
                //       HFDM server, in regard to the place where they expect the branch GUID. For that reason, we currently
                //       copy the GUID.
                guid: in_options.metadata.guid,
                meta: _.omit(in_options.metadata, 'guid')
              },
              commit: { guid: in_parentCommitGuid },
              container: { guid: repository.getGuid() }
            }, in_options, function (error, results) {
              if (error) {
                error = new Error(MSG.FAILED_TO_PERSIST + error.message);
                reject(error);
                return;
              }
              var branchNode = repository._branch(results.branch, in_parentCommitGuid, { trackRemoteBranch: true });
              resolve(branchNode);
            });
          } else {
            resolve(repository._branch(in_options.metadata, in_parentCommitGuid));
          }
        });
      };
      /**
       * Returns the branch node that corresponds to the Urn identifier.
       * @param {string} in_urn - Identifies the branch to return as a Urn.
       * @return {LYNX.PropertyGraph.Nodes.BranchNode} - The resolved branch node
       */
      Hfdm.prototype.getBranch = function (in_urn) {
        console.assert(UrnUtils.isBranchUrn(in_urn), 'in_urn must be a branch Urn');
        var branchGuid = UrnUtils.toGuid(in_urn);
        var repository = this._branchGuidRepositoryMap[branchGuid];
        if (!repository) {
          return undefined;
        }
        return repository.getBranch(branchGuid);
      };
      /**
       * Returns the commit node that corresponds to the Urn identifier.
       * @param {string} in_urn - Identifies the commit to return as a Urn.
       * @return {LYNX.PropertyGraph.Nodes.CommitNode} - The resolved commit node
       */
      Hfdm.prototype.getCommit = function (in_urn) {
        console.assert(UrnUtils.isCommitUrn(in_urn), 'in_urn must be a commit Urn');
        var commitGuid = UrnUtils.toGuid(in_urn);
        var repository = this._commitGuidRepositoryMap[commitGuid];
        if (!repository) {
          return undefined;
        }
        return repository.getCommit(commitGuid);
      };
      /**
       * Creates a new Workspace object associated with this HFDM instance.
       * HFDM keeps track of all workspaces that have been initialized/checked out.
       * @return {LYNX.Property.Workspace} - The created workspace object
       */
      Hfdm.prototype.createWorkspace = function () {
        return new Workspace(this);
      };
      /**
       * Leaves the branch if working on an active branch then decrements its the checkoutCounter
       * @param {LYNX.Property.Workspace} in_workspace - the workspace to be destroyed
       * @return {Promise}
       * @private
       */
      Hfdm.prototype.destroyWorkspace = function (in_workspace) {
        var that = this;
        return new Promise(function (resolve, reject) {
          if (in_workspace.isCheckedOut() && !in_workspace.isDetachedHead() && that._getBackendAdapter() && that._getBackendAdapter().hasJoined(in_workspace.getActiveBranch().getGuid())) {
            that._leave(in_workspace.getActiveBranch().getGuid()).then(function () {
              that._checkoutCounter[in_workspace.getActiveBranch().getGuid()]--;
              in_workspace._destroy();
              resolve();
            }).catch(function (e) {
              reject(e);
            });
          } else {
            try {
              in_workspace._destroy();
              resolve();
            } catch (e) {
              reject(e);
            }
          }
        });
      };
      /**
       * Share resources (URN's) with given user(s)
       * @param {Array<string>} in_urns - The URNs for the resources
       * @param {Array<string>} in_userIds - The oxygen user ids which will get policies assigned
       * @param {Array<string>} in_groupIds - The oxygen group ids which will get policies assigned
       * @param {object=} in_options - Optional parameters
       * @param {Array<string>=} in_options.actions - The actions to be added on the URNs to the oxygen ids
       *   Defaults to ['read', 'write', 'delete']
       * @return {Promise} - The result of all of that, either resolve or fail
       * @private
       */
      Hfdm.prototype.share = function (in_urns, in_userIds, in_groupIds, in_options) {
        return this._share(true, in_urns, in_userIds, in_groupIds, in_options);
      };
      /**
       * UnShare resources (URN's) with given user(s)
       * @param {Array<string>} in_urns - The URNs for the resources
       * @param {Array<string>} in_userIds - The oxygen user ids which will get policies unassigned
       * @param {Array<string>} in_groupIds - The oxygen group ids which will get policies unassigned
       * @param {object=} in_options - Optional parameters
       * @param {Array<string>=} in_options.actions - The actions to be removed on the URNs to the oxygen ids
       *   Defaults to ['read', 'write', 'delete']
       * @return {Promise} - The result of all of that, either resolve or fail
       * @private
       */
      Hfdm.prototype.unshare = function (in_urns, in_userIds, in_groupIds, in_options) {
        return this._share(false, in_urns, in_userIds, in_groupIds, in_options);
      };
      /**
       * Share/UnShare resources Implementation
       * @param {bool} share Should we call share() or unshare()?
       * @param {Array<string>} in_urns - The URNs for the resources
       * @param {Array<string>} in_userIds - The oxygen user ids which will get policies assigned/unassigned
       * @param {Array<string>} in_groupIds - The oxygen group ids which will get policies assigned/unassigned
       * @param {object=} in_options - Optional parameters
       * @param {Array<string>=} in_options.actions - The actions to be added/removed on the URNs to the oxygen ids
       *   Defaults to ['read', 'write', 'delete']
       * @return {Promise} - The result of all of that, either resolve or fail
       * @private
       */
      Hfdm.prototype._share = function (share, in_urns, in_userIds, in_groupIds, in_options) {
        var that = this;
        return new Promise(function (resolve, reject) {
          if (!that.isConnected()) {
            reject(new Error('Cannot ' + (share ? 'share' : 'unshare') + '. ' + MSG.NOT_CONNECTED));
            return;
          }
          var shareFcn = share ? that._propertySetsClient.share : that._propertySetsClient.unshare, params = {
              urns: in_urns,
              userIds: in_userIds,
              groupIds: in_groupIds
            };
          _.defaults(params, in_options);
          // Fold in 'actions' if present
          shareFcn.call(that._propertySetsClient, params, undefined, function (error, success) {
            if (error) {
              return reject(error);
            } else {
              return resolve(success);
            }
          });
        });
      };
      /**
       * Disable the checkout counters
       *
       * WARNING: This is only used for internal tests!
       *
       * @param {Boolean} in_disabled - Are the counters disabled?
       * @private
       */
      Hfdm.prototype._setCheckoutCounterDisabled = function (in_disabled) {
        this._checkoutCounterDisabled = in_disabled;
      };
      exportModule('LYNX.Property.HFDM', Hfdm);
    }());
    (function () {
      /**
       * This namespace contains helper functions which can be used to debug code. They are not intended to be used
       * in client code, as they might not be fully supported on all target platforms.
       * @namespace LYNX.Debug
       */
      /**
       * Starts a download of the supplied blob
       *
       * Note: This function is only provided for debugging purposes. It is not supported on all browsers. Currently,
       *       it only works on Chrome and Firefox. On Chrome, it is only possible to open a text document in a new tab,
       *       downloading binary data is not supported (probably for security reasons).
       *
       * @param {String} in_filename - the filename the downloaded file will have
       * @param {Blob} in_blob - The object to download
       *
       * @alias LYNX.Debug.downloadBlob
       */
      var downloadBlob = function (in_filename, in_blob) {
        var link = URL.createObjectURL(in_blob);
        var requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
        if (navigator.userAgent.indexOf('Chrome') !== -1 || navigator.userAgent.indexOf('Safari') !== -1 || navigator.userAgent.indexOf('Firefox') !== -1) {
          // This seems to work more reliably on chrome (even from the console during
          // debugging)
          var e = document.createEvent('MouseEvents'), a = document.createElement('a');
          a.download = in_filename;
          a.href = link;
          // In Safari, we cannot download a file, but at least we can open
          // an ascii document in a separate tab
          if (navigator.userAgent.indexOf('Safari')) {
            a.target = '_blank';
          }
          a.dataset.downloadurl = [
            'application/octet-stream',
            a.download,
            a.href
          ].join(':');
          e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
          a.dispatchEvent(e);
        } else if (requestFileSystem) {
          // Try to perform the download via a filesystem object
          var failure = function () {
            console.log('Writing file failed!');
          };
          requestFileSystem(window.TEMPORARY, in_blob.size, function (filesystem) {
            filesystem.root.getFile(in_filename, { create: true }, function (entry) {
              entry.createWriter(function (writer) {
                writer.onwriteend = function (ev) {
                  window.location.href = entry.toURL();
                };
                writer.write(in_blob);
              }, failure);
            }, failure);
          }, failure);
        } else {
          // Fallback if no request file system is possible
          window.location.href = URL.createObjectURL(in_blob);
        }
      };
      /**
       * Starts a download of the supplied string
       *
       * Note: This function is only provided for debugging purposes. It is not supported on all browsers. Currently,
       *       it only works on Chrome and Firefox. On Chrome, it is only possible to open a text document in a new tab,
       *       downloading binary data is not supported (probably for security reasons).
       *
       * @param {String} in_filename             - the filename the downloaded file will have
       * @param {Blob}   in_str                  - The string to download
       * @param {String} [in_type='text/plain']  - The mime type of the supplied string
       *
       * @alias LYNX.Debug.downloadString
       */
      var downloadString = function (in_filename, in_str, in_type) {
        var in_type = in_type || 'text/plain';
        var blob = new Blob([in_str], { type: in_type });
        downloadBlob(in_filename, blob);
      };
      /**
       * Shows the user a file open dialog to select a file. The callback will be invoked with the contents of the file.
       *
       * Warning: This function is provided for debugging purposes. It is not reliably supported on all browsers. On
       *          Firefox we currently cannot reliably detect the cancel event.
       *
       * @param {Window}                in_window   -
       *     Window object that is used to create the file open dialog (by adding an input element to the body)
       * @param {function((Error|undefined), String, File)} in_callback -
       *     The callback that will be invoked, when the file open dialog has been closed. It will be invoked with the
       *     content of the file as a string and the File object. If loading the file failed (or the dialog was canceled
       *     by the user, an Error object will be passed). We currently cannot distinguish between canceling and other
       *     types of errors. Detection of Cancel is not supported reliably on Firefox at the moment.
       *
       * @alias LYNX.Debug.uploadFileViaDialog
       */
      var uploadFileViaDialog = function (in_window, in_callback) {
        var document = in_window.document;
        // To trigger the download dialog, we create an input element,
        // add it to the document, and trigger a click event to show the dialog
        var importFileInput = document.createElement('input');
        importFileInput.style.visibility = 'hidden';
        importFileInput.type = 'file';
        importFileInput.focus();
        document.body.appendChild(importFileInput);
        // Function to process the result of the selection
        var alreadyProcessed = false;
        var processDialog = function () {
          // Prevent multiple invocations of the callback
          if (alreadyProcessed) {
            return;
          }
          alreadyProcessed = true;
          if (importFileInput.files.length === 1) {
            // Read the file via a FileReader
            var reader = new FileReader();
            reader.onloadend = function () {
              document.body.removeChild(importFileInput);
              in_callback(null, reader.result);
            };
            reader.readAsText(importFileInput.files[0]);
          } else {
            // Report the failure to the caller
            document.body.removeChild(importFileInput);
            in_callback(new Error('Loading file failed.'), undefined);
          }
        };
        // This event listener is used to detect cancel events
        var focusEventListener = function () {
          window.setTimeout(function () {
            processDialog();
            in_window.removeEventListener('focus', focusEventListener);
          }, 100);
        };
        window.setTimeout(function () {
          in_window.addEventListener('focus', focusEventListener);
        }, 100);
        // Detect the selection of a file
        importFileInput.onchange = processDialog;
        importFileInput.onblur = processDialog;
        // Finally perform the click event to start the dialog
        importFileInput.click();
      };
      exportModule('LYNX.Debug.downloadBlob', downloadBlob);
      exportModule('LYNX.Debug.downloadString', downloadString);
      exportModule('LYNX.Debug.uploadFileViaDialog', uploadFileViaDialog);
    }());

  }


  /**
   * Defines what is ultimately exported by the single-file representation of
   * this module.
   * @param {function} include The 'include' function to be used to get whatever
   *   object/class that needs to be exported in the window object ( if running in
   *   the browser ), or the module.exports object ( if running in NodeJS )
   */
  function defineExports(include) {

    /**
     * @fileoverview This files defines the SDK that will be exported in the
     * distribution file.
     */
    (function() {
      /**
       * Copy the exported content to the appropriate global object.
       * @param  {object} in_exports The content to be exported.
       */
      function makeExports(in_exports) {
        if (typeof window !== 'undefined') {
          window.Forge = window.Forge || {};
          window.Forge.HFDM = in_exports;
        } else if (typeof require !== 'undefined') {
          module.exports = in_exports;
        }
      }

      // Defines what this module exports publicly.
      makeExports({
        HFDM: include('LYNX.Property.HFDM'),
        PropertyFactory: include('LYNX.Property.PropertyFactory'),
        CONSTANTS: include('LYNX.CollaborationServerAPI.CONSTANTS')
      });
    })();


  }


  main();
})();
