'use strict';

const _ = require('lodash');
const IsArray = require('./IsArray');
const asMatcher = require('../utils/asMatcher');

// TODO: Make promise agnostic

function ConsumingMatcher(matchers) {
	return _.create({}, {
		unmatchedMatchers: _.clone(matchers),
		matches: function (actual) {
			let matched = false;
			_.forEach(this.unmatchedMatchers, (matcher, index) => {
				if (matcher.matches(actual)) {
					matched = true;
					this.unmatchedMatchers.splice(index, 1);
					return false;
				}
			}, this);
			return matched;
		}
	});
}

const IsArrayContainingInAnyOrder = function IsArrayContainingInAnyOrder(itemsOrMatchers) {
	const matchers = _.map(itemsOrMatchers, asMatcher);
	return _.create(new IsArray(), {
		matchesSafely: function (actual) {
			if (actual.length !== matchers.length) {
				return false;
			}
			const matcher = new ConsumingMatcher(matchers);
			_.forEach(actual, (item) => {
				if (!matcher.matches(item)) {
					return false;
				}
			});
			return matcher.unmatchedMatchers.length === 0;
		},
		describeTo: function (description) {
			description
				.appendList('[', ', ', ']', matchers)
				.append(' in any order');
		},
		describeMismatchSafely: function (actual, description) {
			const matcher = new ConsumingMatcher(matchers);
			const unmatchedItems = [];
			_.forEach(actual, (item) => {
				if (!matcher.matches(item)) {
					unmatchedItems.push(item);
				}
			});
			if (matcher.unmatchedMatchers.length !== 0) {
				description
					.append('no item in ')
					.appendValue(actual)
					.indented(() => description.appendList(' matches:\n', ',\n', '', matcher.unmatchedMatchers));
			}
			else if (unmatchedItems.length !== 0) {
				description
					.indented(() => description.appendList('not matched:\n', ',\n', '', unmatchedItems))
					.append('\nfrom ')
					.appendValue(actual);
			}
		}
	});
};

IsArrayContainingInAnyOrder.containsInAnyOrder = function () {
	return new IsArrayContainingInAnyOrder(arguments);
};

module.exports = IsArrayContainingInAnyOrder;


'use strict';

/**
 * Traverse the specified AST.
 * @param {{ type: NodeType }} node AST to traverse.
 * @param {function({ type: NodeType })?} opt_onEnter Callback for onEnter.
 * @param {function({ type: NodeType })?} opt_onLeave Callback for onLeave.
 */
function traverse(node, opt_onEnter, opt_onLeave) {
  if (opt_onEnter) opt_onEnter(node);

  var childNodes = _collectChildNodes(node);
  childNodes.forEach(function(childNode) {
    traverse(childNode, opt_onEnter, opt_onLeave);
  });

  if (opt_onLeave) opt_onLeave(node);
}

/**
 * @enum {number}
 * @private
 */
var _PropertyAccessor = {
  NODE: function(fn, node) {
    fn(node);
  },
  NODE_LIST: function(fn, nodes) {
    nodes.forEach(function(node) {
      fn(node);
    });
  },
  NULLABLE_NODE: function(fn, opt_node) {
    if (opt_node) fn(opt_node);
  },
};

/** @private */
var _childNodesMap = {
  NAME: {},
  NAMED_PARAMETER: {
    typeName: _PropertyAccessor.NULLABLE_NODE,
  },
  MEMBER: {
    owner: _PropertyAccessor.NODE,
  },
  UNION: {
    left: _PropertyAccessor.NODE,
    right: _PropertyAccessor.NODE,
  },
  VARIADIC: {
    value: _PropertyAccessor.NODE,
  },
  RECORD: {
    entries: _PropertyAccessor.NODE_LIST,
  },
  RECORD_ENTRY: {
    value: _PropertyAccessor.NULLABLE_NODE,
  },
  GENERIC: {
    subject: _PropertyAccessor.NODE,
    objects: _PropertyAccessor.NODE_LIST,
  },
  MODULE: {
    value: _PropertyAccessor.NODE,
  },
  OPTIONAL: {
    value: _PropertyAccessor.NODE,
  },
  NULLABLE: {
    value: _PropertyAccessor.NODE,
  },
  NOT_NULLABLE: {
    value: _PropertyAccessor.NODE,
  },
  FUNCTION: {
    params: _PropertyAccessor.NODE_LIST,
    returns: _PropertyAccessor.NULLABLE_NODE,
    this: _PropertyAccessor.NULLABLE_NODE,
    new: _PropertyAccessor.NULLABLE_NODE,
  },
  ARROW: {
    params: _PropertyAccessor.NODE_LIST,
    returns: _PropertyAccessor.NULLABLE_NODE,
    new: _PropertyAccessor.NULLABLE_NODE,
  },
  ANY: {},
  UNKNOWN: {},
  INNER_MEMBER: {
    owner: _PropertyAccessor.NODE,
  },
  INSTANCE_MEMBER: {
    owner: _PropertyAccessor.NODE,
  },
  STRING_VALUE: {},
  NUMBER_VALUE: {},
  EXTERNAL: {
    value: _PropertyAccessor.NODE,
  },
  FILE_PATH: {},
  PARENTHESIS: {
    value: _PropertyAccessor.NODE,
  },
  TYPE_QUERY: {
    name: _PropertyAccessor.NODE,
  },
  IMPORT: {
    path: _PropertyAccessor.NODE,
  },
};

/** @private */
function _collectChildNodes(node) {
  var childNodes = [];
  var propAccessorMap = _childNodesMap[node.type];

  Object.keys(propAccessorMap).forEach(function(propName) {
    var propAccessor = propAccessorMap[propName];
    propAccessor(childNodes.push.bind(childNodes), node[propName]);
  });

  return childNodes;
}

module.exports = {
  traverse: traverse,
};


'use strict';

var Base = require('./base');
var Util = require('util');
var logger = require('./logger');
var assert = require('assert').ok;
var uuid = require('uuid').v4;
/**

 * json-rpc-ws module
 *
 * @param {Object} WebSocket object to use
 * @returns {Object} Client - json-rpc-ws client
 */
module.exports = function JsonRpcWs(WebSocket) {
  /**
   * json-rpc-ws client
   *
   * @param {Object} Parent object, used to find method handlers.
   * @param {Socket} WebSocket connection.
   * @extends {Base}
   * @constructor
   */
  var Client = function Client() {

    logger('new Client');
    this.type = 'client';
    this.id = uuid();
    this.browser = (WebSocket.Server === undefined);
    Base.call(this);
  };

  Util.inherits(Client, Base);

  /**
   * Connect to a json-rpc-ws server
   *
   * @param {String} address - url to connect to i.e. `ws://foo.com/`.
   * @param {function} callback - optional callback to call once socket is connected
   * @public
   */
  Client.prototype.connect = function connect(address, callback) {

    logger('Client connect %s', address);
    assert(!this.isConnected(), 'Already connected');
    var self = this;
    var opened = false;
    this.socket = new WebSocket(address);
    if (this.browser) {
      this.socket.onerror = function onerror(err) {

        if (!opened && callback) {
          self.socket.onopen = undefined;
          callback(err);
        }
      };
      this.socket.onopen = function onopen() {

        opened = true;
        self.socket.onopen = undefined;
        self.connected(this);
        if (callback) {
          callback();
        }
      };
    }
    else {
      this.socket.once('open', function clientConnected() {

        // The client connected handler runs scoped as the socket so we can pass
        // it into our connected method like thisk
        self.connected(this);
      });
      if (callback) {
        this.socket.once('open', function socketOpen() {

          opened = true;
          callback.apply(this, arguments);
        });
        this.socket.once('error', function socketError(e) {

          if (!opened) {
            callback.apply(this, arguments);
          }
        });
      }
    }
  };

  /**
   * Test whether we have a connection or not
   *
   * @returns {Boolean} whether or not we have a connection
   * @public
   */
  Client.prototype.isConnected = function isConnected() {

    if (Object.keys(this.connections).length === 0) {
      return false;
    }
    return true;
  };

  /**
   * Return the current connection (there can be only one)
   *
   * @returns {Object} current connection
   * @public
   */
  Client.prototype.getConnection = function getConnection() {

    var ids = Object.keys(this.connections);
    return this.connections[ids[0]];
  };

  /**
   * Close the current connection
   *
   * @param {function} callback - called when the connection has been closed
   * @public
   */
  Client.prototype.disconnect = function disconnect(callback) {

    assert(this.isConnected(), 'Not connected');
    var connection = this.getConnection();
    connection.hangup(callback);
  };

  /**
   * Send a method request
   *
   * @param {String} method - name of method
   * @param {Array} params - optional parameters for method
   * @param {function} callback - optional reply handler
   * @public
   * @todo allow for empty params aka arguments.length === 2
   */
  Client.prototype.send = function send(method, params, callback) {

    logger('send %s', method);
    assert(this.isConnected(), 'Not connected');
    var connection = this.getConnection();
    connection.sendMethod(method, params, callback);
  };

  return Client;

};


'use strict';

var uuid = require('uuid').v4;
var logger = require('./logger');
var Errors = require('./errors');
var Assert = require('assert');

/**
 * Quarantined JSON.parse try/catch block in its own function
 *
 * @param {String} data - json data to be parsed
 * @returns {Object} Parsed json data
 */
var jsonParse = function jsonParse(data) {

  var payload;
  try {
    payload = JSON.parse(data);
  }
  catch (error) {
    logger(error);
    payload = null;
  }
  return payload;
};

/**
 * JSON spec requires a reply for every request, but our lib doesn't require a
 * callback for every sendMethod. We need a dummy callback to throw into responseHandlers
 * for when the user doesn't supply callback to sendMethod
 */
var emptyCallback = function emptyCallback() {

  logger('emptycallback');
};

/**
 * Socket onclose handler for browser WebSocket
 */
var socketClosed = function socketClosed() {

  this.socket.onclose = undefined;
  this.close();
};

/**
 * Socket onerror handler for browser WebSocket
 */
var socketError = function socketError() {

  this.socket.onerror = undefined;
  this.close();
};

/**
 * json-rpc-ws connection
 *
 * @constructor
 * @param {Socket} socket - web socket for this connection
 * @param {Object} parent - parent that controls this connection
 */
var Connection = function Connection(socket, parent) {

  logger('new Connection to %s', parent.type);
  this.id = uuid();
  this.socket = socket;
  this.parent = parent;
  this.responseHandlers = {};
  if (this.parent.browser) {
    this.socket.onmessage = this.message.bind(this);
    this.socket.onclose = socketClosed.bind(this);
    this.socket.onerror = socketError.bind(this);
  }
  else {
    this.socket.on('message', this.message.bind(this));
    this.socket.once('close', this.close.bind(this));
    this.socket.once('error', this.close.bind(this));
  }
};

/**
 * Send json payload to the socket connection
 *
 * @param {Object} payload - data to be stringified
 * @private
 * @todo validate payload
 * @todo make sure this.connection exists, is connected
 * @todo if we're not connected look up the response handler from payload.id
 */
Connection.prototype.sendRaw = function sendRaw(payload) {

  payload.jsonrpc = '2.0';
  this.socket.send(JSON.stringify(payload));
};

/**
 * Validate payload as valid jsonrpc 2.0
 * http://www.jsonrpc.org/specification
 * Reply or delegate as needed
 *
 * @param {Object} payload - payload coming in to be validated
 * @returns {void}
 */
Connection.prototype.processPayload = function processPayload(payload) {

  var version = payload.jsonrpc;
  var id = payload.id;
  var method = payload.method;
  var params = payload.params;
  var result = payload.result;
  var error = payload.error;
  if (version !== '2.0') {
    return this.sendError('invalidRequest', id, { info: 'jsonrpc must be exactly "2.0"' });
  }
  //Will either have a method (request), or result or error (response)
  if (typeof method === 'string') {
    var handler = this.parent.getHandler(method);
    if (!handler) {
      return this.sendError('methodNotFound', id, { info: 'no handler found for method ' + method });
    }
    if (id !== undefined && id !== null && typeof id !== 'string' && typeof id !== 'number') {
      return this.sendError('invalidRequest', id, { info: 'id, if provided, must be one of: null, string, number' });
    }
    if (params !== undefined && params !== null && typeof params !== 'object') {
      return this.sendError('invalidRequest', id, { info: 'params, if provided, must be one of: null, object, array' });
    }
    logger('message method %s', payload.method);
    if (id === null || id === undefined) {
      return handler.call(this, params, emptyCallback);
    }
    var handlerCallback = function handlerCallback(err, reply) {

      logger('handler got callback %j, %j', err, reply);
      return this.sendResult(id, err, reply);
    };
    return handler.call(this, params, handlerCallback.bind(this));
  }
  // needs a result or error at this point
  if (result === undefined && error === undefined) {
    return this.sendError('invalidRequest', id, { info: 'replies must have either a result or error' });
  }
  if (typeof id === 'string' || typeof id === 'number') {
    logger('message id %s result %j error %j', id, result, error);
    var responseHandler = this.responseHandlers[payload.id];
    if (!responseHandler) {
      return this.sendError('invalidRequest', id, { info: 'no response handler for id ' + id });
    }
    delete this.responseHandlers[payload.id];
    return responseHandler.call(this, error, result);
  }
};

/**
 * Send a result message
 *
 * @param {String} id - id for the message
 * @param {Object} error - error for the message
 * @param {String|Object|Array|Number} result - result for the message
 * @public
 *
 */
Connection.prototype.sendResult = function sendResult(id, error, result) {

  logger('sendResult %s %j %j', id, error, result);
  Assert(error || result, 'Must have an error or a result.');
  Assert( !( error && result ), 'Cannot have both an error and a result');

  var response = { id: id };

  if (result) {
    response.result = result;
  }
  else {
    response.error = error;
  }

  this.sendRaw(response);
};

/**
 * Send a method message
 *
 * @param {String} method - method for the message
 * @param {Array|Object|null} params  - params for the message
 * @param {function} callback - optional callback for a reply from the message
 * @public
 */
Connection.prototype.sendMethod = function sendMethod(method, params, callback) {

  var id = uuid();
  Assert((typeof method === 'string') && (method.length > 0), 'method must be a non-empty string');
  Assert(params === null || params === undefined || params instanceof Object, 'params, if provided,  must be an array, object or null');
  logger('sendMethod %s', method, id);
  if (callback) {
    this.responseHandlers[id] = callback;
  }
  else {
    this.responseHandlers[id] = emptyCallback;
  }
  var request = {
    id: id,
    method: method
  };

  if (params) {
    request.params = params;
  }

  this.sendRaw(request);
};

/**
 * Send an error message
 *
 * @param {Object} error - json-rpc error object (See Connection.errors)
 * @param {String|Number|null} id - Optional id for reply
 * @param {Any} data - Optional value for data portion of reply
 * @public
 */
Connection.prototype.sendError = function sendError(error, id, data) {

  logger('sendError %s', error);
  //TODO if id matches a responseHandler, we should dump it right?
  this.sendRaw(Errors(error, id, data));
};

/**
 * Called when socket gets 'close' event
 *
 * @param {Error} error - optional error object of close wasn't expected
 * @private
 */
Connection.prototype.close = function close(error) {

  logger('close');
  if (this.parent.browser && error !== 1000) {
    logger('close error %s', error);
  }
  else if (!this.parent.browser && error) {
    logger('close error %s', error.stack || error);
  }
  this.parent.disconnected(this); //Tell parent what went on so it can track connections
  delete this.socket;
};

/**
 * Hang up the current socket
 *
 * @param {function} callback - called when socket has been closed
 * @public
 */
Connection.prototype.hangup = function hangup(callback) {

  logger('hangup');
  Assert(this.socket, 'Not connected');
  if (typeof callback === 'function') {
    if (this.parent.browser) {
      this.socket.onerror = function socketHangupError() {

        this.socket.onerror = undefined;
        callback();
      }.bind(this);
      this.socket.onclose = function socketHangupClose() {

        this.socket.onclose = undefined;
        callback();
      }.bind(this);
    }
    else {
      this.socket.once('error', callback);
      this.socket.once('close', callback);
    }
  }
  this.socket.close();
};

/**
 * Incoming message handler
 *
 * @param {String} data - message from the websocket
 * @returns {void}
 * @private
 */
Connection.prototype.message = function message(data) {

  //Validate as json first, easy reply if it's not
  //If it's an array iterate and handle
  //If it's an object handle
  //name of handle function ?!?!?
  logger('message %j', data);
  var payload;
  if (this.parent.browser) {
    payload = jsonParse(data.data);
  }
  else {
    payload = jsonParse(data);
  }

  if (payload === null) {
    return Errors('parseError');
  }
  //Object or array
  if (payload instanceof Array) {
    payload.forEach(this.processPayload, this);
  }
  else {
    this.processPayload(payload);
  }
};

module.exports = Connection;


/*
 * Javascript Diff Algorithm
 *  By John Resig (http://ejohn.org/)
 *  Modified by Chu Alan "sprite"
 *
 * Released under the MIT license.
 *
 * More Info:
 *  http://ejohn.org/projects/javascript-diff-algorithm/
 */

function escapeJSDiff(s) {
    var n = s;
    n = n.replace(/&/g, "&");
    n = n.replace(/</g, "<");
    n = n.replace(/>/g, ">");
    n = n.replace(/"/g, """);

    return n;
}

function diffString( o, n ) {
  o = o.replace(/\s+$/, '');
  n = n.replace(/\s+$/, '');

  var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/) );
  var str = "";

  var oSpace = o.match(/\s+/g);
  if (oSpace == null) {
    oSpace = ["\n"];
  } else {
    oSpace.push("\n");
  }
  var nSpace = n.match(/\s+/g);
  if (nSpace == null) {
    nSpace = ["\n"];
  } else {
    nSpace.push("\n");
  }

  if (out.n.length == 0) {
      for (var i = 0; i < out.o.length; i++) {
        str += '' + escapeJSDiff(out.o[i]) + oSpace[i] + "";
      }
  } else {
    if (out.n[0].text == null) {
      for (n = 0; n < out.o.length && out.o[n].text == null; n++) {
        str += '' + escapeJSDiff(out.o[n]) + oSpace[n] + "";
      }
    }

    for ( var i = 0; i < out.n.length; i++ ) {
      if (out.n[i].text == null) {
        str += '' + escapeJSDiff(out.n[i]) + nSpace[i] + "";
      } else {
        var pre = "";

        for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {
          pre += '' + escapeJSDiff(out.o[n]) + oSpace[n] + "";
        }
        str += " " + out.n[i].text + nSpace[i] + pre;
      }
    }
  }

  return str;
}

function randomColor() {
    return "rgb(" + (Math.random() * 100) + "%, " +
                    (Math.random() * 100) + "%, " +
                    (Math.random() * 100) + "%)";
}
function diffString2( o, n ) {
  o = o.replace(/\s+$/, '');
  n = n.replace(/\s+$/, '');

  var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/) );

  var oSpace = o.match(/\s+/g);
  if (oSpace == null) {
    oSpace = ["\n"];
  } else {
    oSpace.push("\n");
  }
  var nSpace = n.match(/\s+/g);
  if (nSpace == null) {
    nSpace = ["\n"];
  } else {
    nSpace.push("\n");
  }

  var os = "";
  var colors = new Array();
  for (var i = 0; i < out.o.length; i++) {
      colors[i] = randomColor();

      if (out.o[i].text != null) {
          os += '' +
                escapeJSDiff(out.o[i].text) + oSpace[i] + "";
      } else {
          os += "" + escapeJSDiff(out.o[i]) + oSpace[i] + "";
      }
  }

  var ns = "";
  for (var i = 0; i < out.n.length; i++) {
      if (out.n[i].text != null) {
          ns += '' +
                escapeJSDiff(out.n[i].text) + nSpace[i] + "";
      } else {
          ns += "" + escapeJSDiff(out.n[i]) + nSpace[i] + "";
      }
  }

  return { o : os , n : ns };
}

function diff( o, n ) {
  var ns = new Object();
  var os = new Object();

  for ( var i = 0; i < n.length; i++ ) {
    if ( ns[ n[i] ] == null )
      ns[ n[i] ] = { rows: new Array(), o: null };
    ns[ n[i] ].rows.push( i );
  }

  for ( var i = 0; i < o.length; i++ ) {
    if ( os[ o[i] ] == null )
      os[ o[i] ] = { rows: new Array(), n: null };
    os[ o[i] ].rows.push( i );
  }

  for ( var i in ns ) {
    if ( ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1 ) {
      n[ ns[i].rows[0] ] = { text: n[ ns[i].rows[0] ], row: os[i].rows[0] };
      o[ os[i].rows[0] ] = { text: o[ os[i].rows[0] ], row: ns[i].rows[0] };
    }
  }

  for ( var i = 0; i < n.length - 1; i++ ) {
    if ( n[i].text != null && n[i+1].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&
         n[i+1] == o[ n[i].row + 1 ] ) {
      n[i+1] = { text: n[i+1], row: n[i].row + 1 };
      o[n[i].row+1] = { text: o[n[i].row+1], row: i + 1 };
    }
  }

  for ( var i = n.length - 1; i > 0; i-- ) {
    if ( n[i].text != null && n[i-1].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&
         n[i-1] == o[ n[i].row - 1 ] ) {
      n[i-1] = { text: n[i-1], row: n[i].row - 1 };
      o[n[i].row-1] = { text: o[n[i].row-1], row: i - 1 };
    }
  }

  return { o: o, n: n };
}


// The `$dialogProvider` can be used to configure global defaults for your
// `$dialog` service.
var dialogModule = angular.module('hawtio-compat.dialog', ['hawtio-compat.transition']);

dialogModule.controller('MessageBoxController', ['$scope', 'dialog', 'model', function($scope, dialog, model){
  $scope.title = model.title;
  $scope.message = model.message;
  $scope.buttons = model.buttons;
  $scope.close = function(res){
    dialog.close(res);
  };
}]);

dialogModule.provider("$dialog", function(){

  // The default options for all dialogs.
  var defaults = {
    backdrop: true,
    dialogClass: 'modal',
    backdropClass: 'modal-backdrop',
    transitionClass: 'fade',
    triggerClass: 'in',
    resolve:{},
    backdropFade: false,
    dialogFade:false,
    keyboard: true, // close with esc key
    backdropClick: true // only in conjunction with backdrop=true
    /* other options: template, templateUrl, controller */
	};

	var globalOptions = {};

  var activeBackdrops = {value : 0};

  // The `options({})` allows global configuration of all dialogs in the application.
  //
  //      var app = angular.module('App', ['ui.bootstrap.dialog'], function($dialogProvider){
  //        // don't close dialog when backdrop is clicked by default
  //        $dialogProvider.options({backdropClick: false});
  //      });
	this.options = function(value){
		globalOptions = value;
	};

  // Returns the actual `$dialog` service that is injected in controllers
	this.$get = ["$http", "$document", "$compile", "$rootScope", "$controller", "$templateCache", "$q", "$transition", "$injector",
  function ($http, $document, $compile, $rootScope, $controller, $templateCache, $q, $transition, $injector) {

		var body = $document.find('body');

		function createElement(clazz) {
			var el = angular.element("<div>");
			el.addClass(clazz);
			return el;
		}

    // The `Dialog` class represents a modal dialog. The dialog class can be invoked by providing an options object
    // containing at lest template or templateUrl and controller:
    //
    //     var d = new Dialog({templateUrl: 'foo.html', controller: 'BarController'});
    //
    // Dialogs can also be created using templateUrl and controller as distinct arguments:
    //
    //     var d = new Dialog('path/to/dialog.html', MyDialogController);
		function Dialog(opts) {

      var self = this, options = this.options = angular.extend({}, defaults, globalOptions, opts);
      this._open = false;

      this.backdropEl = createElement(options.backdropClass);
      if(options.backdropFade){
        this.backdropEl.addClass(options.transitionClass);
        this.backdropEl.removeClass(options.triggerClass);
      }

      this.modalEl = createElement(options.dialogClass);
      if(options.dialogFade){
        this.modalEl.addClass(options.transitionClass);
        this.modalEl.removeClass(options.triggerClass);
      }

      this.handledEscapeKey = function(e) {
        if (e.which === 27) {
          self.close();
          e.preventDefault();
          self.$scope.$apply();
        }
      };

      this.handleBackDropClick = function(e) {
        self.close();
        e.preventDefault();
        self.$scope.$apply();
      };

      this.handleLocationChange = function() {
        self.close();
      };
    }

    // The `isOpen()` method returns wether the dialog is currently visible.
    Dialog.prototype.isOpen = function(){
      return this._open;
    };

    // The `open(templateUrl, controller)` method opens the dialog.
    // Use the `templateUrl` and `controller` arguments if specifying them at dialog creation time is not desired.
    Dialog.prototype.open = function(templateUrl, controller){
      var self = this, options = this.options;

      if(templateUrl){
        options.templateUrl = templateUrl;
      }
      if(controller){
        options.controller = controller;
      }

      if(!(options.template || options.templateUrl)) {
        throw new Error('Dialog.open expected template or templateUrl, neither found. Use options or open method to specify them.');
      }

      this._loadResolves().then(function(locals) {
        var $scope = locals.$scope = self.$scope = locals.$scope ? locals.$scope : $rootScope.$new();

        self.modalEl.html(locals.$template);

        if (self.options.controller) {
          var ctrl = $controller(self.options.controller, locals);
          self.modalEl.children().data('ngControllerController', ctrl);
        }

        $compile(self.modalEl)($scope);
        self._addElementsToDom();

        // trigger tranisitions
        setTimeout(function(){
          if(self.options.dialogFade){ self.modalEl.addClass(self.options.triggerClass); }
          if(self.options.backdropFade){ self.backdropEl.addClass(self.options.triggerClass); }
        });

        self._bindEvents();
      });

      this.deferred = $q.defer();
      return this.deferred.promise;
    };

    // closes the dialog and resolves the promise returned by the `open` method with the specified result.
    Dialog.prototype.close = function(result){
      var self = this;
      var fadingElements = this._getFadingElements();

      if(fadingElements.length > 0){
        for (var i = fadingElements.length - 1; i >= 0; i--) {
          $transition(fadingElements[i], removeTriggerClass).then(onCloseComplete);
        }
        return;
      }

      this._onCloseComplete(result);

      function removeTriggerClass(el){
        el.removeClass(self.options.triggerClass);
      }

      function onCloseComplete(){
        if(self._open){
          self._onCloseComplete(result);
        }
      }
    };

    Dialog.prototype._getFadingElements = function(){
      var elements = [];
      if(this.options.dialogFade){
        elements.push(this.modalEl);
      }
      if(this.options.backdropFade){
        elements.push(this.backdropEl);
      }

      return elements;
    };

    Dialog.prototype._bindEvents = function() {
      if(this.options.keyboard){ body.bind('keydown', this.handledEscapeKey); }
      if(this.options.backdrop && this.options.backdropClick){ this.backdropEl.bind('click', this.handleBackDropClick); }
    };

    Dialog.prototype._unbindEvents = function() {
      if(this.options.keyboard){ body.unbind('keydown', this.handledEscapeKey); }
      if(this.options.backdrop && this.options.backdropClick){ this.backdropEl.unbind('click', this.handleBackDropClick); }
    };

    Dialog.prototype._onCloseComplete = function(result) {
      this._removeElementsFromDom();
      this._unbindEvents();

      this.deferred.resolve(result);
    };

    Dialog.prototype._addElementsToDom = function(){
      body.append(this.modalEl);

      if(this.options.backdrop) {
        if (activeBackdrops.value === 0) {
          body.append(this.backdropEl);
        }
        activeBackdrops.value++;
      }

      this._open = true;
    };

    Dialog.prototype._removeElementsFromDom = function(){
      this.modalEl.remove();

      if(this.options.backdrop) {
        activeBackdrops.value--;
        if (activeBackdrops.value === 0) {
          this.backdropEl.remove();
        }
      }
      this._open = false;
    };

    // Loads all `options.resolve` members to be used as locals for the controller associated with the dialog.
    Dialog.prototype._loadResolves = function(){
      var values = [], keys = [], templatePromise, self = this;

      if (this.options.template) {
        templatePromise = $q.when(this.options.template);
      } else if (this.options.templateUrl) {
        templatePromise = $http.get(this.options.templateUrl, {cache:$templateCache})
        .then(function(response) { return response.data; });
      }

      angular.forEach(this.options.resolve || [], function(value, key) {
        keys.push(key);
        values.push(angular.isString(value) ? $injector.get(value) : $injector.invoke(value));
      });

      keys.push('$template');
      values.push(templatePromise);

      return $q.all(values).then(function(values) {
        var locals = {};
        angular.forEach(values, function(value, index) {
          locals[keys[index]] = value;
        });
        locals.dialog = self;
        return locals;
      });
    };

    // The actual `$dialog` service that is injected in controllers.
    return {
      // Creates a new `Dialog` with the specified options.
      dialog: function(opts){
        return new Dialog(opts);
      },
      // creates a new `Dialog` tied to the default message box template and controller.
      //
      // Arguments `title` and `message` are rendered in the modal header and body sections respectively.
      // The `buttons` array holds an object with the following members for each button to include in the
      // modal footer section:
      //
      // * `result`: the result to pass to the `close` method of the dialog when the button is clicked
      // * `label`: the label of the button
      // * `cssClass`: additional css class(es) to apply to the button for styling
      messageBox: function(title, message, buttons){
        return new Dialog({templateUrl: 'plugins/ui-bootstrap/html/message.html', controller: 'MessageBoxController', resolve:
          {model: function() {
            return {
              title: title,
              message: message,
              buttons: buttons
            };
          }
        }});
      }
    };
  }];
});


import {Node} from "prosemirror-model"

const noTag = Node.prototype.tag = Object.create(null)

function flatten(schema, children, f) {
  let result = [], pos = 0, tag = noTag

  for (let i = 0; i < children.length; i++) {
    let child = children[i]
    if (child.tag && child.tag != Node.prototype.tag) {
      if (tag == noTag) tag = Object.create(null)
      for (let id in child.tag)
        tag[id] = child.tag[id] + (child.flat || child.isText ? 0 : 1) + pos
    }

    if (typeof child == "string") {
      let re = /<(\w+)>/g, m, at = 0, out = ""
      while (m = re.exec(child)) {
        out += child.slice(at, m.index)
        pos += m.index - at
        at = m.index + m[0].length
        if (tag == noTag) tag = Object.create(null)
        tag[m[1]] = pos
      }
      out += child.slice(at)
      pos += child.length - at
      if (out) result.push(f(schema.text(out)))
    } else if (child.flat) {
      for (let j = 0; j < child.flat.length; j++) {
        let node = f(child.flat[j])
        pos += node.nodeSize
        result.push(node)
      }
    } else {
      let node = f(child)
      pos += node.nodeSize
      result.push(node)
    }
  }
  return {nodes: result, tag}
}

function id(x) { return x }

function takeAttrs(attrs, args) {
  let a0 = args[0]
  if (!args.length || (a0 && (typeof a0 == "string" || a0 instanceof Node || a0.flat)))
    return attrs

  args.shift()
  if (!attrs) return a0
  if (!a0) return attrs
  let result = {}
  for (let prop in attrs) result[prop] = attrs[prop]
  for (let prop in a0) result[prop] = a0[prop]
  return result
}

// : (string, ?Object) → (...content: [union<string, Node>]) → Node
// Create a builder function for nodes with content.
function block(type, attrs) {
  let result = function(...args) {
    let myAttrs = takeAttrs(attrs, args)
    let {nodes, tag} = flatten(type.schema, args, id)
    let node = type.create(myAttrs, nodes)
    if (tag != noTag) node.tag = tag
    return node
  }
  if (type.isLeaf) try { result.flat = [type.create(attrs)] } catch(_) {}
  return result
}

// Create a builder function for marks.
function mark(type, attrs) {
  return function(...args) {
    let mark = type.create(takeAttrs(attrs, args))
    let {nodes, tag} = flatten(type.schema, args, n => mark.type.isInSet(n.marks) ? n : n.mark(mark.addToSet(n.marks)))
    return {flat: nodes, tag}
  }
}

export default function(schema, names) {
  let result = {schema}
  for (let name in schema.nodes) result[name] = block(schema.nodes[name], {})
  for (let name in schema.marks) result[name] = mark(schema.marks[name], {})

  if (names) for (let name in names) {
    let value = names[name], typeName = value.nodeType || value.markType || name, type
    if (type = schema.nodes[typeName]) result[name] = block(type, value)
    else if (type = schema.marks[typeName]) result[name] = mark(type, value)
  }
  return result
}


import {Provider} from "react-redux";
import React from 'react';
import {combineEpics} from 'redux-observable';
import UTILS from 'mi-js-utils';
import {combineReducers} from "redux";
import storeFactory from "./store/storeFactory";
import createReducer from "./createReducer";
import {createAction} from './createAction';
import getEpicMiddleware from "./getEpicMiddleware";

let _epics = [];
let _reducersObj = {};
let _plugins = {};
let _middlewares = [];

let _configureStore = UTILS.common.noop;

function _addPlugins(key, plugin) {
  _plugins[key] = plugin;
}

function _addMiddleware(middlewares) {
  if (Array.isArray(middlewares)) {
    _middlewares = _middlewares.concat(middlewares);
  } else {
    _middlewares.push(middlewares);
  }
}

function _addEpic(epics) {
  if (epics) {
    _epics = _epics.concat(Object.values(epics));
  }
}

function _addReducer(namespace, initState, handles) {
  _reducersObj[namespace] = createReducer(initState, handles);
}

function _addModel(model) {
  const {namespace, state, epics, reducers} = model;

  if (epics) {
    _addEpic(epics);
  }
  if (reducers) {
    _addReducer(namespace, state || {}, reducers);
  }
}

/**
 * create configure store method
 * @private
 */
function _createConfigureStore(...args){
  _configureStore = storeFactory(...args);
}

/**
 * create store
 * @private
 */
function _createStore() {
  //start to create store
  const rootEpic = combineEpics(..._epics);
  const _trueReducers = combineReducers(_reducersObj);

  //TODO extra epic inject plugin
  const epicMiddleware = getEpicMiddleware(rootEpic, _plugins);

  _middlewares.push(epicMiddleware);

  return _configureStore(_trueReducers, _middlewares);
}

function _addOriginReducer(key, reducer) {
  _reducersObj[key] = reducer;
}

function _addOriginEpic(epics) {
  if (Array.isArray(epics)) {
    _epics = _epics.concat(epics);
  } else {
    _epics.push(epics);
  }
}

export default {
  addModel: _addModel,
  createAction,
  addReducer: _addOriginReducer,
  addEpic: _addOriginEpic,
  addPlugin: _addPlugins,
  addMiddleware: _addMiddleware,

  createConfigureStore: _createConfigureStore,
  createStore: _createStore,
}


(function (angular, io) {
'use strict'/*global angular */
angular.module('ngSails', ['ng']);

/*global angular, io */
(function(angular, io) {
    'use strict';
    if(io.sails){
      io.sails.autoConnect = false;
    }

    // copied from angular
    function parseHeaders(headers) {
        var parsed = {},
            key, val, i;
        if (!headers) return parsed;
        angular.forEach(headers.split('\n'), function(line) {
            i = line.indexOf(':');
            key = lowercase(trim(line.substr(0, i)));
            val = trim(line.substr(i + 1));
            if (key) {
                parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
            }
        });

        return parsed;
    }

    function trim(value) {
        return angular.isString(value) ? value.trim() : value;
    }

    function isPromiseLike (obj){
        return obj && angular.isFunction(obj.then);
    }

    // copied from angular
    function headersGetter(headers) {
        var headersObj = angular.isObject(headers) ? headers : undefined;
        return function(name) {
            if (!headersObj) headersObj = parseHeaders(headers);
            if (name) {
                var value = headersObj[lowercase(name)];
                if (value === void 0) {
                    value = null;
                }
                return value;
            }
            return headersObj;
        };
    }

    angular.module('ngSails').provider('$sails', function() {
        var provider = this;

        this.httpVerbs = ['get', 'post', 'put', 'delete'];

        this.eventNames = ['on', 'off'];

        this.url = undefined;

        this.urlPrefix = '';

        this.config = {
            transports: ['websocket', 'polling'],
            useCORSRouteToGetCookie: false
        };

        this.debug = false;

        // like https://docs.angularjs.org/api/ng/service/$http#interceptors
        // but with sails.io arguments
        var interceptorFactories = this.interceptors = [
            /*function($injectables) {
                return {
                    request: function(config) {},
                    response: function(response) {},
                    requestError: function(rejection) {},
                    responseError: function(rejection) {}
                };
            }*/
        ];

        /*@ngInject*/
        this.$get = ["$q", "$injector", "$rootScope", "$log", "$timeout", function($q, $injector, $rootScope, $log, $timeout) {
            var socket = (io.sails && io.sails.connect || io.connect)(provider.url, provider.config);

            socket.connect = function(opts){
                if(!socket.isConnected()){
                    var _opts = opts||{};
                    _opts = angular.extend({},provider.config,opts);

                    // These are the options sails.io.js actually sets when making the connection.
                    socket.useCORSRouteToGetCookie = _opts.useCORSRouteToGetCookie;
                    socket.url = _opts.url || provider.url;
                    socket.multiplex = _opts.multiplex;

                    socket._connect();
                }
                return socket;
            };

            // TODO: separate out interceptors into its own file (and provider?).
            // build interceptor chain
            var reversedInterceptors = [];
            angular.forEach(interceptorFactories, function(interceptorFactory) {
                reversedInterceptors.unshift(
                    angular.isString(interceptorFactory) ?
                    $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory)
                );
            });

            // Send the request using the socket
            function serverRequest(config) {
                var defer = $q.defer();
                if (provider.debug) $log.info('$sails ' + config.method + ' ' + config.url, config.data || '');

                if (config.timeout > 0) {
                    $timeout(timeoutRequest, config.timeout);
                } else if (isPromiseLike(config.timeout)) {
                    config.timeout.then(timeoutRequest);
                }

                socket['legacy_' + config.method.toLowerCase()](config.url, config.data, serverResponse);

                function timeoutRequest(){
                    serverResponse(null);
                }

                function serverResponse(result, jwr) {

                    if (!jwr) {
                        jwr = {
                            body: result,
                            headers: result.headers || {},
                            statusCode: result.statusCode || result.status || 0,
                            error: (function() {
                                if (this.statusCode < 200 || this.statusCode >= 400) {
                                    return this.body || this.statusCode;
                                }
                            })()
                        };
                    }

                    jwr.data = jwr.body; // $http compat
                    jwr.status = jwr.statusCode; // $http compat
                    jwr.socket = socket;
                    jwr.url = config.url;
                    jwr.method = config.method;
                    jwr.config = config.config;
                    if (jwr.error) {
                        if (provider.debug) $log.warn('$sails response ' + jwr.statusCode + ' ' + config.url, jwr);
                        defer.reject(jwr);
                    } else {
                        if (provider.debug) $log.info('$sails response ' + config.url, jwr);
                        defer.resolve(jwr);
                    }
                }

                return defer.promise;
            }

            function promisify(methodName) {
                socket['legacy_' + methodName] = socket[methodName];

                socket[methodName] = function(url, data, config) {

                    var chain = [serverRequest, undefined];

                    //TODO: more compatible with $http methods and config

                    var promise = $q.when({
                        url: provider.urlPrefix + url,
                        data: data,
                        socket: socket,
                        config: config || {},
                        method: methodName.toUpperCase()
                    });

                    // apply interceptors
                    angular.forEach(reversedInterceptors, function(interceptor) {
                        if (interceptor.request || interceptor.requestError) {
                            chain.unshift(interceptor.request, interceptor.requestError);
                        }
                        if (interceptor.response || interceptor.responseError) {
                            chain.push(interceptor.response, interceptor.responseError);
                        }
                    });

                    while (chain.length) {
                        var thenFn = chain.shift();
                        var rejectFn = chain.shift();

                        promise = promise.then(thenFn, rejectFn);
                    }

                    // be $http compatible
                    promise.success = function(fn) {
                        promise.then(function(jwr) {
                            fn(jwr.body, jwr.statusCode, headersGetter(jwr.headers), jwr);
                        });
                        return promise;
                    };
                    promise.error = function(fn) {
                        promise.then(null, function(jwr) {
                            fn(jwr.body, jwr.statusCode, headersGetter(jwr.headers), jwr);
                        });
                        return promise;
                    };

                    return promise;
                };
            }

            function wrapEvent(eventName) {
                if(socket[eventName] || socket._raw && socket._raw[eventName]) {
                    socket['legacy_' + eventName] = socket[eventName] || socket._raw[eventName];
                    socket[eventName] = function(event, cb) {
                    	var wrapEventFn = null;
                        if (eventName == 'off') {
                            return socket['legacy_' + eventName](event, cb);
                        }else if (cb !== null && angular.isFunction(cb)) {
                            socket['legacy_' + eventName](event, wrapEventFn = function(result) {
                                $rootScope.$evalAsync(cb.bind(socket, result));
                            });
                        }
                        return wrapEventFn;
                    };
                }
            }

            // sails.io.js doesn't have `once`, need to access it through `._raw`
            socket.once = function(event, cb){
              if (cb !== null && angular.isFunction(cb)) {
                if(socket._raw){
                  socket._raw.once(event, function(result) {
                      $rootScope.$evalAsync(cb.bind(socket, result));
                  });
                }
              }
            };

            angular.forEach(provider.httpVerbs, promisify);
            angular.forEach(provider.eventNames, wrapEvent);

            /**
             * Update a model on sails pushes
             * @param {String} name       Sails model name
             * @param {Array} models      Array with model objects
             */
            socket.$modelUpdater = function(name, models) {

                var update = function(message) {

                    $rootScope.$evalAsync(function(){
                        var i;

                        switch (message.verb) {

                            case "created":
                                // create new model item
                                models.push(message.data);
                                break;

                            case "updated":
                                var obj;
                                for (i = 0; i < models.length; i++) {
                                    if (models[i].id === message.id) {
                                        obj = models[i];
                                        break;
                                    }
                                }

                                // cant update if the angular-model does not have the item and the
                                // sails message does not give us the previous record
                                if (!obj && !message.previous) return;

                                if (!obj) {
                                    // sails has given us the previous record, create it in our model
                                    obj = message.previous;
                                    models.push(obj);
                                }

                                // update the model item
                                angular.extend(obj, message.data);
                                break;

                            case "destroyed":
                                for (i = 0; i < models.length; i++) {
                                    if (models[i].id === message.id) {
                                        models.splice(i, 1);
                                        break;
                                    }
                                }
                                break;
                        }
                    });
                };

                socket.legacy_on(name, update);

                return function(){
                    socket.legacy_off(name, update);
                };
            };

            return socket;
        }];
        this.$get.$inject = ["$q", "$injector", "$rootScope", "$log", "$timeout"];
    });
}(angular, io));
}(angular, io));


// -----------------------------------------------------------
// version 1.09
// -----------------------------------------------------------
"use strict";

var debugging = false;

var fs = require('fs');
var cp = require('child_process');
var path = require('path');

var isRanFromNativeScript = fs.existsSync("../../app/App_Resources");
var hasNativeScript = fs.existsSync("../../nativescript");

// Figure out angular Seed Path for symlink...
var angularSeedPath = '../../src/';
var seeds = ['client', 'app'];
var seedId = 0;

// Search for the actual seed, depending on where we are run from
for (var i=0;i<seeds.length;i++) {
    if (fs.existsSync(angularSeedPath+seeds[i])) {
        seedId = i; break;
    }
}
angularSeedPath += seeds[seedId] + "/";
var nativescriptClientPath = '../../nativescript/app/' + seeds[seedId] + "/";

if (debugging) {
    console.log("Path is:", angularSeedPath, nativescriptClientPath);
}

// Root SymLink Code for Windows
if (process.argv.length > 2) {
    if (process.argv[2] === 'symlink') {
        createRootSymLink();
        console.log("Created Symlink");
    }
    return 0;
}

if (!hasNativeScript && !isRanFromNativeScript) {
    console.log("Installing NativeScript Angular 2 Template...");
    cp.execSync('tns create nativescript --template  https://github.com/NativeScript/template-hello-world-ng', {cwd: '../..'});
    console.log("Installing NativeScript support files...");
    cp.execSync('npm install', {cwd: '../../nativescript'});

    console.log("Installing support files");
    if (process.platform === 'darwin') {
        try {
            cp.execSync('npm install image-to-ascii-cli', {cwd: '../..'});
        }
        catch (Err) {
            console.log("Install Error", Err);
        }
    }

    console.log("Configuring...");
    if (debugging) {
        cp.execSync('tns plugin add ../node_modules/nativescript-ng2-magic', {cwd: '../../nativescript'});
    } else {
        cp.execSync('tns plugin add nativescript-ng2-magic', {cwd: '../../nativescript'});
    }

    // remove sample component
    if (fs.existsSync('../../nativescript/app/app.component.ts')) {
        console.log("Removing sample component");
        fs.unlinkSync('../../nativescript/app/app.component.ts');
    }
  if (fs.existsSync('../../nativescript/app/app.component.html')) {
        console.log("Removing sample component html");
        fs.unlinkSync('../../nativescript/app/app.component.html');
    }

    // We need to create a symlink
    try {
        createSymLink();
    } catch (err) {
        if (debugging) {
            console.log("Symlink error: ", err);
        }
        // Failed, which means they weren't running root; so lets try to get root
        AttemptRootSymlink();
    }

    // Might silent fail on OSX, so we have to see if it exists
    if (!fs.existsSync(nativescriptClientPath)) {
        AttemptRootSymlink();
    }

    // This does not look good on windows; windows ansi support in node sucks...  So we aren't going to do this in windows
    if (process.platform === "darwin") {
        // image to ascii uses GM which may not be installed, so if it isn't installed; don't print the error message
		try {
          var ascii = cp.execSync('image-to-ascii -i https://cdn.filestackcontent.com/XXMT4f8S8OGngNsJj0pr', {cwd: '../image-to-ascii-cli/bin/'}).toString();
          if (ascii.length > 30) {
            console.log(ascii);
          }
	    }
		catch (err) {
			// Do Nothing; if the site can't be resolved; we don't want to fail the script
		}
    }

    displayFinalHelp();

    if (!fs.existsSync(nativescriptClientPath)) {
        console.log("We were unable to create a symlink  - from -");
        console.log("  ", resolve(angularSeedPath), "    - to - ");
		console.log("  ", resolve(nativescriptClientPath));
        console.log("If you don't create this symlink, you will have to manually copy the code each time you change it.");
    }

}

if (isRanFromNativeScript) {
    if (!fs.existsSync('installed.ns') ) {
        fs.writeFileSync('installed.ns', 'installed');
        fixTsConfig();
        fixRefFile();
        fixNativeScriptPackage();
        fixAngularPackage();
        fixMainFile( figureOutRootComponent());

        // when being run from inside {N} app, the directory is different
        var srcRoot = '../../../src/';
        for (var i=0;i<seeds.length;i++) {
            if (fs.existsSync(srcRoot + seeds[i])) {
                seedId = i; break;
            }
        }
        fixGitIgnore('nativescript/app/' + seeds[seedId]);
        fixGitIgnore('nativescript/app/node_modules');
        fixGitIgnore('nativescript/app/platforms');
        console.log("Completed Install");
    } else {
        console.log("We have already been installed in the NativeScript app as a plugin.");
        return 0;
    }
}

return 0;

/**
 * Searches for the bootstrap file until it finds one....
 * @returns {string}
 */
function figureOutRootComponent() {
    var rootComponents = ['../../../src/app/app.module.ts', '../../../src/app.ts', '../../../boot.ts', '../../../src/main.ts'];
    for (var i=0;i<rootComponents.length; i++) {
        if (fs.existsSync(rootComponents[i])) {
            var result = processBootStrap(rootComponents[i]);
            if (result) { return result; }
        }
    }
    // Return a default component, if we can't find one...
    return {
      name: 'AppComponent',
      path: './client/components/app.component'
    };
}

/**
 * Parses the bootstrap to figure out the default bootstrap component
 * @param file
 * @returns {*}
 */
function processBootStrap(file) {
    var data = fs.readFileSync(file).toString();
    var idx = data.indexOf('bootstrap(');
    if (idx === -1) { return null; }
    else { idx+=10; }

    var odx1 = data.indexOf(',', idx);
    var odx2 = data.indexOf(')', idx);
    if (odx2 < odx1 && odx2 !== -1 || odx1 === -1) { odx1 = odx2; }
    if (odx1 === -1) { return null; }
    var componentRef = data.substring(idx, odx1);
    var exp = "import\\s+\\{("+componentRef+")\\}\\s+from+\\s+[\'|\"](\\S+)[\'|\"][;?]";
    if (debugging) {
        console.log("Searching for", exp);
    }

    var result = function (r) {
      return {
        name: r[1],
        path: r[r.length - 1]
      };
    };
    //noinspection JSPotentiallyInvalidConstructorUsage
    var r = RegExp(exp, 'i').exec(data);
    if (r === null || r.length <= 1) {
      // check if using current style guide with spaces
      exp = "import\\s+\\{\\s+("+componentRef+")\\,\\s+([A-Z]{0,300})\\w+\\s+\\}\\s+from+\\s+[\'|\"](\\S+)[\'|\"][;?]";
      if (debugging) {
        console.log("Searching for", exp);
      }
      r = RegExp(exp, 'i').exec(data);
      if (r === null || r.length <= 1) {
        // try just spaces with no angular cli style (, environment) etc.
        exp = "import\\s+\\{\\s+(" + componentRef + ")\\s+\\}\\s+from+\\s+[\'|\"](\\S+)[\'|\"][;?]";
        if (debugging) {
          console.log("Searching for", exp);
        }
        r = RegExp(exp, 'i').exec(data);
        if (r !== null && r.length > 1) {
          return result(r);
        }
      } else {
        // angular cli
        return result(r);
      }
      return null;
    }
    return result(r);
}

/**
 * This will attempt to run the install script as root to make a symlink
 *
 */
function AttemptRootSymlink() {

    if (process.platform === 'win32') {
        var curPath = resolve("./");
        if (debugging) {
            console.log("RootSymlink Base path is", curPath);
        }
        cp.execSync("powershell -Command \"Start-Process 'node' -ArgumentList '"+curPath+"/install.js symlink' -verb runas\"");
    } else {
		console.log("To automatically create a SymLink between your web app and NativeScript, we need root for a second.");
		cp.execSync("sudo "+process.argv[0] + " " + process.argv[1] +" symlink");
    }
}

/**
 * Create the symlink when running as root
 */
function createRootSymLink() {
    var li1 = process.argv[1].lastIndexOf('\\'), li2 = process.argv[1].lastIndexOf('/');
    if (li2 > li1) { li1 = li2; }
    var AppPath = process.argv[1].substring(0,li1);
    var p1 = resolve(AppPath + "/" + nativescriptClientPath);
    var p2 = resolve(AppPath + "/" + angularSeedPath);
    if (debugging) {
        console.log("Path: ", p1, p2);
    }
    fs.symlinkSync(p2,p1,'junction');
}

/**
 * Create Symlink
 */
function createSymLink() {
    if (debugging) {
        console.log("Attempting to Symlink", angularSeedPath, nativescriptClientPath);
    }
    fs.symlinkSync(resolve(angularSeedPath),resolve(nativescriptClientPath),'junction');
}

/**
 * This fixes the TS Config file in the nativescript folder
 */
function fixTsConfig() {
    var tsConfig={}, tsFile = '../../tsconfig.json';
    if (fs.existsSync(tsFile)) {
        tsConfig = require(tsFile);
    }
    if (!tsConfig.compilerOptions || !tsConfig.compilerOptions.typeRoots) {
        tsConfig.compilerOptions = {
          target: "es5",
          module: "commonjs",
          declaration: false,
          removeComments: true,
          noLib: false,
          emitDecoratorMetadata: true,
          experimentalDecorators: true,
          lib: [
            "dom"
          ],
          sourceMap: true,
          pretty: true,
          allowUnreachableCode: false,
          allowUnusedLabels: false,
          noImplicitAny: false,
          noImplicitReturns: true,
          noImplicitUseStrict: false,
          noFallthroughCasesInSwitch: true,
          typeRoots: [
              "node_modules/@types",
              "node_modules"
          ],
          types: [
              "jasmine"
          ]
        };
    }

    // See: https://github.com/NativeScript/nativescript-angular/issues/205
    // tsConfig.compilerOptions.noEmitHelpers = false;
    // tsConfig.compilerOptions.noEmitOnError = false;

    if (!tsConfig.exclude) {
        tsConfig.exclude = [];
    }
    if (tsConfig.exclude.indexOf('node_modules') === -1) {
        tsConfig.exclude.push('node_modules');
    }
    if (tsConfig.exclude.indexOf('platforms') === -1) {
        tsConfig.exclude.push('platforms');
    }

    fs.writeFileSync(tsFile, JSON.stringify(tsConfig, null, 4), 'utf8');
}

/**
 * This fixes the references file to work with TS 2.0 in the nativescript folder
 */
function fixRefFile() {
    var existingRef='', refFile = '../../references.d.ts';
    if (fs.existsSync(refFile)) {
        existingRef = fs.readFileSync(refFile).toString();
    }

  if (existingRef.indexOf('typescript/lib/lib.d.ts') === -1) {
    // has not been previously modified
    var fix = '/// <reference path="./node_modules/tns-core-modules/tns-core-modules.d.ts" />\n' +
      '/// <reference path="./node_modules/typescript/lib/lib.d.ts" />\n';

    fs.writeFileSync(refFile, fix, 'utf8');
  }
}

/**
 * Fix the NativeScript Package file
 */
function fixNativeScriptPackage() {
    var packageJSON = {}, packageFile = '../../package.json';
    packageJSON.name = "NativeScriptApp";
    packageJSON.version = "0.0.0";

    // var AngularJSON = {};
    if (fs.existsSync(packageFile)) {
        packageJSON = require(packageFile);
    } else {
        console.log("This should not happen, your are missing your package.json file!");
        return;
    }
    // if (fs.existsSync('../angular2/package.json')) {
    //     AngularJSON = require('../angular2/package.json');
    // } else {
    //     // Copied from the Angular2.0.0-beta-16 package.json, this is a fall back
    //     AngularJSON.peerDependencies = {
    //         "es6-shim": "^0.35.0",
    //         "reflect-metadata": "0.1.2",
    //         "rxjs": "5.0.0-beta.6",
    //         "zone.js": "^0.6.12"
    //     };
    // }

    packageJSON.nativescript['tns-ios'] = { version: "2.3.0" };
    packageJSON.nativescript['tns-android'] = {version: "2.3.0" };

    // Copy over all the Peer Dependencies
    // for (var key in AngularJSON.peerDependencies) {
    //     if (AngularJSON.peerDependencies.hasOwnProperty(key)) {
    //         packageJSON.dependencies[key] = AngularJSON.peerDependencies[key];
    //     }
    // }

    // TODO: Can we get these from somewhere rather than hardcoding them, maybe need to pull/download the package.json from the default template?
    if (!packageJSON.devDependencies) {
        packageJSON.devDependencies = {};
    }
    packageJSON.devDependencies["@types/jasmine"] = "^2.5.35";
    packageJSON.devDependencies["babel-traverse"] = "6.12.0";
    packageJSON.devDependencies["babel-types"] = "6.11.1";
    packageJSON.devDependencies.babylon = "6.8.4";
    packageJSON.devDependencies.filewalker = "0.1.2";
    packageJSON.devDependencies.lazy = "1.0.11";
    // packageJSON.devDependencies["nativescript-dev-typescript"] = "^0.3.2";
    packageJSON.devDependencies.typescript = "^2.0.2";

    fs.writeFileSync(packageFile, JSON.stringify(packageJSON, null, 4), 'utf8');
}

/**
 * Fix the Angular Package
 */
function fixAngularPackage() {
    var packageJSON = {}, packageFile = '../../../package.json';
    if (fs.existsSync(packageFile)) {
        packageJSON = require(packageFile);
    } else {
        console.log("This should not happen, your are missing your main package.json file!");
        return;
    }

    if (!packageJSON.scripts) {
        packageJSON.scripts = {};
    }

    packageJSON.scripts["start.ios"] = "cd nativescript && tns emulate ios";
    packageJSON.scripts["start.livesync.ios"] = "cd nativescript && tns livesync ios --emulator --watch";
    packageJSON.scripts["start.android"] = "cd nativescript && tns emulate android";
    packageJSON.scripts["start.livesync.android"] = "cd nativescript && tns livesync android --emulator --watch";

    fs.writeFileSync(packageFile, JSON.stringify(packageJSON, null, 4), 'utf8');
}

/**
 * Fix the Main NativeScript File
 * @param component
 */
function fixMainFile(component) {
  var mainTS = '', mainFile = '../../app/main.ts';
  if (fs.existsSync(mainFile)) {
    mainTS = fs.readFileSync(mainFile).toString();
  }

  if (mainTS.indexOf('MagicService') === -1) {
    // has not been previously modified
    var fix = '// this import should be first in order to load some required settings (like globals and reflect-metadata)\n' +
      'import { platformNativeScriptDynamic, NativeScriptModule } from "nativescript-angular/platform";\n' +
      'import { NgModule } from "@angular/core";\n' +
      'import { AppComponent } from "./app/app.component";\n' +
      '\n' +
      '@NgModule({\n' +
      '  declarations: [AppComponent],\n' +
      '  bootstrap: [AppComponent],\n' +
      '   imports: [NativeScriptModule],\n' +
      '})\n' +
      'class AppComponentModule {}\n\n' +
      'platformNativeScriptDynamic().bootstrapModule(AppComponentModule);';

    fs.writeFileSync(mainFile, fix, 'utf8');
  }
}

/**
 * Fix .gitignore
 * @param path
 */
function fixGitIgnore(ignorePattern) {
  var fileString = '', ignoreFile = '../../../.gitignore';
  if (fs.existsSync(ignoreFile)) {
    fileString = fs.readFileSync(ignoreFile).toString();
  }

  if (fileString.indexOf(ignorePattern) === -1) {
    // has not been previously modified
    var fix = fileString +
      '\n' +
      ignorePattern;

    fs.writeFileSync(ignoreFile, fix, 'utf8');
  }
}

/**
 * Display final help screen!
 */
function displayFinalHelp()
{
    console.log("-------------- Welcome to the Magical World of NativeScript -----------------------------");
    console.log("To finish, follow this guide https://github.com/NathanWalker/nativescript-ng2-magic#usage");
    console.log("After you have completed the steps in the usage guide, you can then:");
    console.log("");
    console.log("Run your app in the iOS Simulator with these options:");
    console.log("  npm run start.ios");
    console.log("  npm run start.livesync.ios");
    console.log("");
    console.log("Run your app in an Android emulator with these options:");
    console.log("  npm run start.android");
    console.log("  npm run start.livesync.android");
    console.log("-----------------------------------------------------------------------------------------");
    console.log("");
}

function splitPath(v) {
    var x;
    if (v.indexOf('/') !== -1) {
        x = v.split('/');
    } else {
        x = v.split("\\");
    }
    return x;
}

function resolve(v) {
    var cwdPath = splitPath(process.argv[1]);
    // Kill the Script name
    cwdPath.length = cwdPath.length - 1;

    var resolvePath = splitPath(v);

    // Eliminate a trailing slash/backslash
    if (cwdPath[cwdPath.length-1] === "") { cwdPath.pop(); }

    if (v[0] === '/' || v[0] === "\\") { cwdPath = []; }
    for (var i=0;i<resolvePath.length;i++) {
        if (resolvePath[i] === '.' || resolvePath[i] === "") { continue; }
        if (resolvePath[i] === '..') { cwdPath.pop(); }
        else { cwdPath.push(resolvePath[i]); }
    }
    if (process.platform === 'win32') {
        var winResult = cwdPath.join("\\");
        if (winResult[winResult.length-1] === "\\") { winResult = winResult.substring(0, winResult.length - 1); }
        return winResult;
    } else {
		var result = cwdPath.join('/');
		if (result[0] !== '/') { result = '/' + result; }
		if (result[result.length-1] === '/') { result = result.substring(0, result.length - 1); }
        return result;
    }

}