<commit_before>'use strict';

var debug   = require('debug')('kiss.io:socket');
var parser  = require('socket.io-parser');
var url     = require('url');
var hasBin  = require('has-binary');

var Router  = require('./router');
var Plugin  = require('./plugin');

/**
 * A socket is a representation of a singular socket connection that
 * ..might be part of a larger connection pool, represented by `Client`.
 * Socket is specific to one namespace of one server.
 *
 * @constructs Socket
 * @access public
 *
 * @param {Namespace} nsp: the namespace that the socket is connected to.
 * @param {Client} client: the client instance that owns the socket.
 * @param {Object} query: the query params of the request.
 * @returns {Socket}
 */
function Socket(nsp, client, query)
{
  if(!(this instanceof Socket))
  {
    return new Socket(nsp, client, query);
  }

  this.nsp = nsp;
  this.id = nsp.name !== '/' ? nsp.name + '#' + client.id : client.id;
  this.opts = this.nsp.opts || {};
  this.client = client;
  this.conn = client.conn;
  this.request = this.conn.request;
  this.acks = {};
  this.connected = true;
  this.handshake = this.buildHandshake(query);
  this.router = new Router(this.nsp.opts);
}

/**
 * Built-in, emitted by default events.
 */
Socket.events =
[
  'error',
  'pre-setup',
  'connect',
  'connection',
  'pre-disconnect',
  'pre-disconnection',
  'disconnect',
  'disconnection',
  'newListener',
  'removeListener'
];

/**
 * Set/get a temporary flag.
 *
 * @access public
 *
 * @param {String} key: the name of the flag.
 * @param {*} [value]
 * @returns {*|undefined} value
 */
Socket.prototype.flag = function(key, value)
{
  this._flags = this._flags || {};

  if(arguments.length == 2)
  {
    this._flags[key] = value;
  }

  return this._flags;
};

/**
 * Reset temporary flags
 *
 * @access public
 */
Socket.prototype.reset = function()
{
  delete this._flags;
};

/**
 * Builds the `handshake` BC object
 *
 * @access private
 *
 * @param {Object} [query]: if not provided, uses Socket.request query.
 * @returns {Object} handshake
 */
Socket.prototype.buildHandshake = function(query)
{
  var requestQuery = url.parse(this.request.url, true).query || {};

  query = query || requestQuery;
  query.t = requestQuery.t;
  query.EIO = requestQuery.EIO;
  query.transport = requestQuery.transport;

  return {
    headers: this.request.headers,
    time: (new Date) + '',
    address: this.conn.remoteAddress,
    xdomain: !!this.request.headers.origin,
    secure: !!this.request.connection.encrypted,
    issued: +(new Date),
    url: this.request.url,
    query: query
  };
};

/**
 * Bind a router to this socket and merege it into the main
 * ..socket router.
 *
 * @access public
 *
 * @param {Router} router
 * @returns {Socket} self
 */
Socket.prototype.merge = function(router)
{
  var context =
  {
    nsp: this.nsp,
    socket: this
  };

  router.forRoute(function bind(route)
  {
    route.bindContext(context);
  });

  this.router.merge(router);
  return this;
};

Socket.prototype.plug =
Socket.prototype.plugin = function(plugin)
{
  var exports = plugin.exports.socket || {};

  for(let key of Object.keys(exports))
  {
    let value = exports[key];

    if(typeof value == 'function')
    {
      value = value.bind(plugin, this);
    }
    else if(typeof value == 'object')
    {
      value = Object.create(value);
    }

    this[key] = value;
  }

  this.merge(plugin.exports.router);
  return this;
};

/**
 * Shortcut for `Socket.router.on`.
 * @see Router.on
 *
 * @access public
 *
 * @param {String} event
 * @param {Array<Function>} [handlers]
 * @returns {Route}
 */
Socket.prototype.on =
Socket.prototype.event = function(event, handler)
{
  return this.router.on.apply(this.router, arguments);
};

Socket.prototype.once =
Socket.prototype.eventOnce = function(event, handler)
{
  return this.router.once.apply(this.router, arguments);
};

/**
 * Trigger an inside event for Socket.
 *
 * @access private
 *
 * @param {String} ev
 */
Socket.prototype.trigger = function(ev)
{
  this.router.trigger.apply(this.router, arguments);
};

/**
 * Emits an event to the owning `Client`.
 * If event not in the reserved event list (Socket.events),
 * ..then send a packet to the socket with the given arguments.
 *
 * @access public
 *
 * @param {String} ev: event
 * @return {Socket} self
 */
Socket.prototype.emit = function(ev)
{
  if (Socket.events.indexOf(ev) !== -1)
  {
    this.trigger.apply(this, arguments);
  }
  else
  {
    var args = Array.prototype.slice.call(arguments);
    var packet = {};

    packet.type = hasBin(args) ? parser.BINARY_EVENT : parser.EVENT;
    packet.data = args;

    // access last argument to see if it's an ACK callback
    if ('function' == typeof args[args.length - 1])
    {
      debug('emitting packet with ack id %d', this.nsp.ids);

      this.acks[this.nsp.ids] = args.pop();
      packet.id = this.nsp.ids++;
    }

    // dispatch packet
    this.packet(packet, {
      volatile: this.flag('volatile'),
      compress: this.flag('compress')
    });

    // reset flags
    this.reset();
  }

  return this;
};

/**
 * Broadcasts a packet to every socket on the same namespace of this socket,
 * ..except this socket.
 *
 * @access public
 *
 * @param {String} ev: event
 */
Socket.prototype.broadcast = function(ev)
{
  this.nsp.broadcast.apply(this.nsp.except(this.id), arguments);
};

/**
 * Sends a `message` event to the socket.
 * (fits the engine.io api).
 *
 * @alias {Socket.write}
 * @access public
 *
 * @params {*} params to emit with the message event.
 */
Socket.prototype.send =
Socket.prototype.write = function()
{
  var args = Array.prototype.slice.call(arguments);
  args.unshift('message');
  this.emit.apply(this, args);
};

/**
 * Writes a packet to the socket.
 *
 * @see Client.packet
 *
 * @access private
 *
 * @param {Object} packet
 * @param {Object} [opts]
 */
Socket.prototype.packet = function(packet, opts)
{
  packet.nsp = this.nsp.name;

  opts = opts || {};
  opts.compress = false !== opts.compress;

  this.client.packet(packet, opts);
};

/**
 * Send a CONNECT packet to the socket. Called by `Namespace` upon success.
 *
 * @access private (used publicly only by `Namespace`)
 */
Socket.prototype.onConnect = function()
{
  debug('socket connected - writing packet');

  this.packet({ type: parser.CONNECT });
};

/**
 * Called with each packet. Called by `Client`.
 *
 * @access private (used publicly only by `Client`)
 *
 * @param {Object} packet
 */
Socket.prototype.onPacket = function(packet)
{
  debug('got packet %j', packet);

  switch (packet.type)
  {
    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.onError(packet.data);
      break;
  }
};

/**
 * Parses packet and emits to this socket. Called upon event packet.
 *
 * @access private
 *
 * @param {Object} packet
 */
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));
  }

  this.trigger.apply(this, args);
};

/**
 * Produces an ack callback to emit with an event.
 *
 * @access private
 *
 * @param {Number} id: packet id
 */
Socket.prototype.ack = function(id)
{
  var self = this;
  var sent = false;

  return function()
  {
    // prevent double callbacks
    if (sent) return;

    var args = Array.prototype.slice.call(arguments);
    var type = hasBin(args) ? parser.BINARY_ACK : parser.ACK;

    debug('sending ack %j', args);

    self.packet({
      id: id,
      type: type,
      data: args
    });

    sent = true;
  };
};

/**
 * Called upon ack packet.
 *
 * @access 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);
  }
};

/**
 * Closes connection. Called upon client disconnect packet.
 *
 * @access private
 */
Socket.prototype.onDisconnect = function()
{
  debug('got disconnect packet');
  this.disconnect('client namespace disconnect');
};

/**
 * Handles a client error.
 *
 * @access private
 */
Socket.prototype.onError = function(err)
{
  if (this.router.listensOn('error'))
  {
    this.trigger('error', err);
  }
  else
  {
    console.error('Missing error handler on `socket`.');
    console.error(err.stack);
  }
};

/**
 * Called upon closing. Called by `Client`.
 *
 * @access private (used publicly only by `Client`)
 *
 * @param {String} [reason]
 */
Socket.prototype.onClose = function(reason)
{
  debug('closing socket - reason %s', reason);
  this.disconnect(reason);
};

/**
 * Produces and sends an `error` packet to the socket.
 *
 * @access public
 *
 * @param {Error} err: error object
 */
Socket.prototype.error = function(err)
{
  this.packet({ type: parser.ERROR, data: err });
};

/**
 * Disconnects this client.
 *
 * @access public
 *
 * @param {String} [reason]
 * @return {Socket} self
 */
Socket.prototype.disconnect = function(reason)
{
  if (!this.connected) return this;

  debug('closing socket - reason %s', reason);

  this.trigger('pre-disconnect', this, reason);
  this.trigger('pre-disconnection', this, reason);

  this.packet({ type: parser.DISCONNECT });

  this.nsp.remove(this);
  this.client.remove(this);
  this.connected = false;

  setImmediate(function()
  {
    this.trigger('disconnect', this, reason);
    this.trigger('disconnection', this, reason);
  }.bind(this));

  return this;
};

/**
 * Sets the compress flag.
 *
 * @access public
 *
 * @param {Boolean} [compress=true]: If `true`, compresses the sending data.
 * @return {Socket} self
 */
Socket.prototype.compress = function(compress)
{
  this.flag('compress', compress);
  return this;
};

/*!
 * Module exports.
 */
module.exports = Socket;
<commit_msg>Plugin no longer dependency of Socket<commit_after>'use strict';

var debug   = require('debug')('kiss.io:socket');
var parser  = require('socket.io-parser');
var url     = require('url');
var hasBin  = require('has-binary');

var Router  = require('./router');

/**
 * A socket is a representation of a singular socket connection that
 * ..might be part of a larger connection pool, represented by `Client`.
 * Socket is specific to one namespace of one server.
 *
 * @constructs Socket
 * @access public
 *
 * @param {Namespace} nsp: the namespace that the socket is connected to.
 * @param {Client} client: the client instance that owns the socket.
 * @param {Object} query: the query params of the request.
 * @returns {Socket}
 */
function Socket(nsp, client, query)
{
  if(!(this instanceof Socket))
  {
    return new Socket(nsp, client, query);
  }

  this.nsp = nsp;
  this.id = nsp.name !== '/' ? nsp.name + '#' + client.id : client.id;
  this.opts = this.nsp.opts || {};
  this.client = client;
  this.conn = client.conn;
  this.request = this.conn.request;
  this.acks = {};
  this.connected = true;
  this.handshake = this.buildHandshake(query);
  this.router = new Router(this.nsp.opts);
}

/**
 * Built-in, emitted by default events.
 */
Socket.events =
[
  'error',
  'pre-setup',
  'connect',
  'connection',
  'pre-disconnect',
  'pre-disconnection',
  'disconnect',
  'disconnection',
  'newListener',
  'removeListener'
];

/**
 * Set/get a temporary flag.
 *
 * @access public
 *
 * @param {String} key: the name of the flag.
 * @param {*} [value]
 * @returns {*|undefined} value
 */
Socket.prototype.flag = function(key, value)
{
  this._flags = this._flags || {};

  if(arguments.length == 2)
  {
    this._flags[key] = value;
  }

  return this._flags;
};

/**
 * Reset temporary flags
 *
 * @access public
 */
Socket.prototype.reset = function()
{
  delete this._flags;
};

/**
 * Builds the `handshake` BC object
 *
 * @access private
 *
 * @param {Object} [query]: if not provided, uses Socket.request query.
 * @returns {Object} handshake
 */
Socket.prototype.buildHandshake = function(query)
{
  var requestQuery = url.parse(this.request.url, true).query || {};

  query = query || requestQuery;
  query.t = requestQuery.t;
  query.EIO = requestQuery.EIO;
  query.transport = requestQuery.transport;

  return {
    headers: this.request.headers,
    time: (new Date) + '',
    address: this.conn.remoteAddress,
    xdomain: !!this.request.headers.origin,
    secure: !!this.request.connection.encrypted,
    issued: +(new Date),
    url: this.request.url,
    query: query
  };
};

/**
 * Bind a router to this socket and merege it into the main
 * ..socket router.
 *
 * @access public
 *
 * @param {Router} router
 * @returns {Socket} self
 */
Socket.prototype.merge = function(router)
{
  var context =
  {
    nsp: this.nsp,
    socket: this
  };

  router.forRoute(function bind(route)
  {
    route.bindContext(context);
  });

  this.router.merge(router);
  return this;
};

Socket.prototype.plug =
Socket.prototype.plugin = function(plugin)
{
  var exports = plugin.exports.socket || {};

  for(let key of Object.keys(exports))
  {
    let value = exports[key];

    if(typeof value == 'function')
    {
      value = value.bind(plugin, this);
    }
    else if(typeof value == 'object')
    {
      value = Object.create(value);
    }

    this[key] = value;
  }

  this.merge(plugin.exports.router);
  return this;
};

/**
 * Shortcut for `Socket.router.on`.
 * @see Router.on
 *
 * @access public
 *
 * @param {String} event
 * @param {Array<Function>} [handlers]
 * @returns {Route}
 */
Socket.prototype.on =
Socket.prototype.event = function(event, handler)
{
  return this.router.on.apply(this.router, arguments);
};

Socket.prototype.once =
Socket.prototype.eventOnce = function(event, handler)
{
  return this.router.once.apply(this.router, arguments);
};

/**
 * Trigger an inside event for Socket.
 *
 * @access private
 *
 * @param {String} ev
 */
Socket.prototype.trigger = function(ev)
{
  this.router.trigger.apply(this.router, arguments);
};

/**
 * Emits an event to the owning `Client`.
 * If event not in the reserved event list (Socket.events),
 * ..then send a packet to the socket with the given arguments.
 *
 * @access public
 *
 * @param {String} ev: event
 * @return {Socket} self
 */
Socket.prototype.emit = function(ev)
{
  if (Socket.events.indexOf(ev) !== -1)
  {
    this.trigger.apply(this, arguments);
  }
  else
  {
    var args = Array.prototype.slice.call(arguments);
    var packet = {};

    packet.type = hasBin(args) ? parser.BINARY_EVENT : parser.EVENT;
    packet.data = args;

    // access last argument to see if it's an ACK callback
    if ('function' == typeof args[args.length - 1])
    {
      debug('emitting packet with ack id %d', this.nsp.ids);

      this.acks[this.nsp.ids] = args.pop();
      packet.id = this.nsp.ids++;
    }

    // dispatch packet
    this.packet(packet, {
      volatile: this.flag('volatile'),
      compress: this.flag('compress')
    });

    // reset flags
    this.reset();
  }

  return this;
};

/**
 * Broadcasts a packet to every socket on the same namespace of this socket,
 * ..except this socket.
 *
 * @access public
 *
 * @param {String} ev: event
 */
Socket.prototype.broadcast = function(ev)
{
  this.nsp.broadcast.apply(this.nsp.except(this.id), arguments);
};

/**
 * Sends a `message` event to the socket.
 * (fits the engine.io api).
 *
 * @alias {Socket.write}
 * @access public
 *
 * @params {*} params to emit with the message event.
 */
Socket.prototype.send =
Socket.prototype.write = function()
{
  var args = Array.prototype.slice.call(arguments);
  args.unshift('message');
  this.emit.apply(this, args);
};

/**
 * Writes a packet to the socket.
 *
 * @see Client.packet
 *
 * @access private
 *
 * @param {Object} packet
 * @param {Object} [opts]
 */
Socket.prototype.packet = function(packet, opts)
{
  packet.nsp = this.nsp.name;

  opts = opts || {};
  opts.compress = false !== opts.compress;

  this.client.packet(packet, opts);
};

/**
 * Send a CONNECT packet to the socket. Called by `Namespace` upon success.
 *
 * @access private (used publicly only by `Namespace`)
 */
Socket.prototype.onConnect = function()
{
  debug('socket connected - writing packet');

  this.packet({ type: parser.CONNECT });
};

/**
 * Called with each packet. Called by `Client`.
 *
 * @access private (used publicly only by `Client`)
 *
 * @param {Object} packet
 */
Socket.prototype.onPacket = function(packet)
{
  debug('got packet %j', packet);

  switch (packet.type)
  {
    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.onError(packet.data);
      break;
  }
};

/**
 * Parses packet and emits to this socket. Called upon event packet.
 *
 * @access private
 *
 * @param {Object} packet
 */
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));
  }

  this.trigger.apply(this, args);
};

/**
 * Produces an ack callback to emit with an event.
 *
 * @access private
 *
 * @param {Number} id: packet id
 */
Socket.prototype.ack = function(id)
{
  var self = this;
  var sent = false;

  return function()
  {
    // prevent double callbacks
    if (sent) return;

    var args = Array.prototype.slice.call(arguments);
    var type = hasBin(args) ? parser.BINARY_ACK : parser.ACK;

    debug('sending ack %j', args);

    self.packet({
      id: id,
      type: type,
      data: args
    });

    sent = true;
  };
};

/**
 * Called upon ack packet.
 *
 * @access 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);
  }
};

/**
 * Closes connection. Called upon client disconnect packet.
 *
 * @access private
 */
Socket.prototype.onDisconnect = function()
{
  debug('got disconnect packet');
  this.disconnect('client namespace disconnect');
};

/**
 * Handles a client error.
 *
 * @access private
 */
Socket.prototype.onError = function(err)
{
  if (this.router.listensOn('error'))
  {
    this.trigger('error', err);
  }
  else
  {
    console.error('Missing error handler on `socket`.');
    console.error(err.stack);
  }
};

/**
 * Called upon closing. Called by `Client`.
 *
 * @access private (used publicly only by `Client`)
 *
 * @param {String} [reason]
 */
Socket.prototype.onClose = function(reason)
{
  debug('closing socket - reason %s', reason);
  this.disconnect(reason);
};

/**
 * Produces and sends an `error` packet to the socket.
 *
 * @access public
 *
 * @param {Error} err: error object
 */
Socket.prototype.error = function(err)
{
  this.packet({ type: parser.ERROR, data: err });
};

/**
 * Disconnects this client.
 *
 * @access public
 *
 * @param {String} [reason]
 * @return {Socket} self
 */
Socket.prototype.disconnect = function(reason)
{
  if (!this.connected) return this;

  debug('closing socket - reason %s', reason);

  this.trigger('pre-disconnect', this, reason);
  this.trigger('pre-disconnection', this, reason);

  this.packet({ type: parser.DISCONNECT });

  this.nsp.remove(this);
  this.client.remove(this);
  this.connected = false;

  setImmediate(function()
  {
    this.trigger('disconnect', this, reason);
    this.trigger('disconnection', this, reason);
  }.bind(this));

  return this;
};

/**
 * Sets the compress flag.
 *
 * @access public
 *
 * @param {Boolean} [compress=true]: If `true`, compresses the sending data.
 * @return {Socket} self
 */
Socket.prototype.compress = function(compress)
{
  this.flag('compress', compress);
  return this;
};

/*!
 * Module exports.
 */
module.exports = Socket;
<|endoftext|>


<commit_before>/**
 * Copyright (c) Microsoft.  All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var __ = require('underscore');
var util = require('util');
var utils = require('../../../util/utils');
var $ = utils.getLocaleString;

function ReservedIp(cli, networkManagementClient) {
  this.networkManagementClient = networkManagementClient;
  this.output = cli.output;
  this.interaction = cli.interaction;
}

__.extend(ReservedIp.prototype, {
  create: function (name, location, options, _) {
    var self = this;

    var params = {
      name: name,
      location: location,
    };

    if (options.label) {
      params.label = options.label;
    }

    if (options.serviceName) {
      params.serviceName = options.serviceName;
    }

    var progress = self.interaction.progress($('Creating reserved IP address'));
    try {
      self.networkManagementClient.reservedIPs.create(params, _);
    } finally {
      progress.end();
    }
  },

  list: function (options, _) {
    var self = this;

    var progress = self.interaction.progress($('Getting reserved IP addresses'));
    var reservedIPs;
    try {
      reservedIPs = self.networkManagementClient.reservedIPs.list(_);
    } finally {
      progress.end();
    }

    self.interaction.formatOutput(reservedIPs.reservedIPs, function (data) {
      if (data.length === 0) {
        self.output.warn($('No reserved IP addresses found'));
      } else {
        self.output.table(data, function (row, ip) {
          row.cell($('Name'), ip.name);
          row.cell($('Location'), ip.location);
          row.cell($('Address'), ip.address);
          row.cell($('Label'), ip.label || '');
          row.cell($('Service Name'), ip.serviceName || '');
          row.cell($('State'), ip.state);
        });
      }
    });
  },

  show: function (name, options, _) {
    var self = this;
    var reservedIP = self.get(name, _);

    if (reservedIP) {
      self.interaction.formatOutput(reservedIP, function (ip) {
        self.output.nameValue($('Name'), ip.name);
        self.output.nameValue($('Location'), ip.location);
        self.output.nameValue($('Address'), ip.address);
        self.output.nameValue($('Label'), ip.label || '');
        self.output.nameValue($('Service Name'), ip.serviceName || '');
        self.output.nameValue($('State'), ip.state);
      });
    } else {
      if (self.output.format().json) {
        self.output.json({});
      } else {
        self.output.warn(util.format($('A reserved ip address with name "%s" not found'), name));
      }
    }
  },

  delete: function (name, options, _) {
    var self = this;

    var progress = self.interaction.progress($('Looking up reserved IP address'));
    try {
      if (!options.quiet) {
        // Ensure the reserved IP address exists before prompting for confirmation
        self.networkManagementClient.reservedIPs.get(name, _);
        if (!self.interaction.confirm(util.format($('Delete reserved IP address %s? [y/n] '), name), _))
          return;
      }

      progress = self.interaction.progress($('Deleting reserved IP address'));
      self.networkManagementClient.reservedIPs.deleteMethod(name, _);
    } finally {
      progress.end();
    }
  },

  get: function (name, _) {
    var self = this;
    var progress = self.interaction.progress(util.format($('Looking up the reserved ip "%s"'), name));
    try {
      var reservedIP = self.networkManagementClient.reservedIPs.get(name, _);
      return reservedIP;
    } catch (e) {
      if (e.statusCode === 404) {
        return null;
      }
      throw e;
    } finally {
      progress.end();
    }
  },

  associate: function(name, serviceName, _) {
    var self = this;

    var params = {
      name: name,
      serviceName: serviceName
    };

    var progress = self.interaction.progress($('Setting reserved IP address and cloud service association'));
    try {
      self.networkManagementClient.reservedIPs.associate(name, params, _);
    } finally {
      progress.end();
    }
  },

  disassociate: function(name, serviceName, _) {
    var self = this;

    var params = {
      name: name,
      serviceName: serviceName
    };

    var progress = self.interaction.progress($('Removing reserved IP address and cloud service association'));
    try {
      self.networkManagementClient.reservedIPs.disassociate(name, params, _);
    } finally {
      progress.end();
    }
  }
});

module.exports = ReservedIp;
<commit_msg>removed extra-commas<commit_after>/**
 * Copyright (c) Microsoft.  All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var __ = require('underscore');
var util = require('util');
var utils = require('../../../util/utils');
var $ = utils.getLocaleString;

function ReservedIp(cli, networkManagementClient) {
  this.networkManagementClient = networkManagementClient;
  this.output = cli.output;
  this.interaction = cli.interaction;
}

__.extend(ReservedIp.prototype, {
  create: function (name, location, options, _) {
    var self = this;

    var params = {
      name: name,
      location: location
    };

    if (options.label) {
      params.label = options.label;
    }

    if (options.serviceName) {
      params.serviceName = options.serviceName;
    }

    var progress = self.interaction.progress($('Creating reserved IP address'));
    try {
      self.networkManagementClient.reservedIPs.create(params, _);
    } finally {
      progress.end();
    }
  },

  list: function (options, _) {
    var self = this;

    var progress = self.interaction.progress($('Getting reserved IP addresses'));
    var reservedIPs;
    try {
      reservedIPs = self.networkManagementClient.reservedIPs.list(_);
    } finally {
      progress.end();
    }

    self.interaction.formatOutput(reservedIPs.reservedIPs, function (data) {
      if (data.length === 0) {
        self.output.warn($('No reserved IP addresses found'));
      } else {
        self.output.table(data, function (row, ip) {
          row.cell($('Name'), ip.name);
          row.cell($('Location'), ip.location);
          row.cell($('Address'), ip.address);
          row.cell($('Label'), ip.label || '');
          row.cell($('Service Name'), ip.serviceName || '');
          row.cell($('State'), ip.state);
        });
      }
    });
  },

  show: function (name, options, _) {
    var self = this;
    var reservedIP = self.get(name, _);

    if (reservedIP) {
      self.interaction.formatOutput(reservedIP, function (ip) {
        self.output.nameValue($('Name'), ip.name);
        self.output.nameValue($('Location'), ip.location);
        self.output.nameValue($('Address'), ip.address);
        self.output.nameValue($('Label'), ip.label || '');
        self.output.nameValue($('Service Name'), ip.serviceName || '');
        self.output.nameValue($('State'), ip.state);
      });
    } else {
      if (self.output.format().json) {
        self.output.json({});
      } else {
        self.output.warn(util.format($('A reserved ip address with name "%s" not found'), name));
      }
    }
  },

  delete: function (name, options, _) {
    var self = this;

    var progress = self.interaction.progress($('Looking up reserved IP address'));
    try {
      if (!options.quiet) {
        // Ensure the reserved IP address exists before prompting for confirmation
        self.networkManagementClient.reservedIPs.get(name, _);
        if (!self.interaction.confirm(util.format($('Delete reserved IP address %s? [y/n] '), name), _))
          return;
      }

      progress = self.interaction.progress($('Deleting reserved IP address'));
      self.networkManagementClient.reservedIPs.deleteMethod(name, _);
    } finally {
      progress.end();
    }
  },

  get: function (name, _) {
    var self = this;
    var progress = self.interaction.progress(util.format($('Looking up the reserved ip "%s"'), name));
    try {
      var reservedIP = self.networkManagementClient.reservedIPs.get(name, _);
      return reservedIP;
    } catch (e) {
      if (e.statusCode === 404) {
        return null;
      }
      throw e;
    } finally {
      progress.end();
    }
  },

  associate: function(name, serviceName, _) {
    var self = this;

    var params = {
      name: name,
      serviceName: serviceName
    };

    var progress = self.interaction.progress($('Setting reserved IP address and cloud service association'));
    try {
      self.networkManagementClient.reservedIPs.associate(name, params, _);
    } finally {
      progress.end();
    }
  },

  disassociate: function(name, serviceName, _) {
    var self = this;

    var params = {
      name: name,
      serviceName: serviceName
    };

    var progress = self.interaction.progress($('Removing reserved IP address and cloud service association'));
    try {
      self.networkManagementClient.reservedIPs.disassociate(name, params, _);
    } finally {
      progress.end();
    }
  }
});

module.exports = ReservedIp;
<|endoftext|>


<commit_before>const getBuildProperty = require('./getBuildProperty');
const plistParser = require('plist');
const path = require('path');

/**
 * Returns Info.plist located in the iOS project and removes
 * xcode $(SRCROOT) and other implementation specific decorators, so that
 * return value can be easily used in e.g. path.join calls
 *
 * Returns `null` if INFOPLIST_FILE is not specified.
 */
module.exports = function getPlistPath(project, sourceDir) {
  const plistFile = getBuildProperty(project, 'INFOPLIST_FILE');

  if (!plistFile) {
    return null;
  }

  const plistPath = path.join(
    sourceDir,
    plistFile.replace(/"/g, '').replace('$(SRCROOT)', '')
  );

  if (!fs.existsSync(plistPath)) {
    return null;
  }

  return plistParser.parse(
    fs.readFileSync(plistPath, 'utf-8')
  );
};
<commit_msg>Update comments<commit_after>const getBuildProperty = require('./getBuildProperty');
const plistParser = require('plist');
const path = require('path');

/**
 * Returns Info.plist located in the iOS project
 *
 * Returns `null` if INFOPLIST_FILE is not specified.
 */
module.exports = function getPlistPath(project, sourceDir) {
  const plistFile = getBuildProperty(project, 'INFOPLIST_FILE');

  if (!plistFile) {
    return null;
  }

  const plistPath = path.join(
    sourceDir,
    plistFile.replace(/"/g, '').replace('$(SRCROOT)', '')
  );

  if (!fs.existsSync(plistPath)) {
    return null;
  }

  return plistParser.parse(
    fs.readFileSync(plistPath, 'utf-8')
  );
};
<|endoftext|>


<commit_before>var _ = require('underscore');
var carto = require('carto');
var crypto = require('crypto');
var fs = require('fs');
var path = require('path');
var url = require('url');
var mkdirp = require('mkdirp');
var sm = new (require('sphericalmercator'));
var yaml = require('js-yaml');
var tm = require('./tm');
var Bridge = require('tilelive-bridge');
var TileJSON = require('tilejson');
var tilelive = require('tilelive');
var style;
var MAPBOX_API = 'http://a.tiles.mapbox.com/v3'
var CachingTileJSON = require('./cache')(TileJSON, tm.config().cache);
var CachingBridge = require('./cache')(Bridge, tm.config().cache);
var mapnik = require('mapnik');
var mapnikref = require('mapnik-reference').version.latest;

var defaults = {
    name:'',
    description:'',
    attribution:'',
    mtime:+new Date,
    minzoom:0,
    maxzoom:6,
    center:[0,0,3],
    Layer:[],
    _prefs: {
        saveCenter: true,
        disabled: [],
        inspector: false
    }
};
var deflayer = {
    id:'',
    srs:'',
    description:'',
    fields: {},
    Datasource: {},
    properties: {
        minzoom:0,
        maxzoom:22,
        'buffer-size':0
    }
};

var cache = {};

module.exports = source;
tilelive.protocols['mapbox:'] = source;
tilelive.protocols['tmsource:'] = source;

source.defaults = defaults;
source.deflayer = deflayer;

function source(arg, callback) {
    if ('string' !== typeof arg) {
        var id = url.format(arg);
        var uri = arg;
    } else {
        var id = arg;
        var uri = url.parse(arg);
    }

    if (uri.protocol !== 'tmsource:' && uri.protocol !== 'mapbox:')
        return callback(new Error('Invalid source protocol'));

    if (cache[id]) return callback(null, cache[id]);

    source.info(id, function(err, data) {
        if (err) return callback(err);
        data = source.normalize(data);
        source.toXML(data, function(err, xml) {
            if (err) return callback(err);
            source.refresh(data, xml, callback);
        });
    });
};

// Load or refresh the relevant source using specified data + xml.
source.refresh = function(data, xml, callback) {
    var id = data.id;
    var uri = url.parse(data.id);
    var done = function(err, p) {
        if (err) return callback(err);
        cache[id] = cache[id] || p;
        cache[id].data = data;

        var interactivity_layer;
        var template = '';
        if (data.vector_layers.length && data._prefs.inspector) {
            var inspector = _(data.vector_layers).find(function(l) {
                return l.id === data._prefs.inspector;
            });
            if (inspector && inspector.fields) {
                interactivity_layer = inspector.id;
                template = tm.templates.xraytemplate(Object.keys(inspector.fields));
            }
        }
        style = style || require('./style');
        style.save({
            id: style.tmpid(id, true),
            styles: [tm.templates.xraystyles(data)],
            source: id,
            center: data.center,
            interactivity_layer: interactivity_layer,
            template: template
        }, function(err, source) {
            if (err) return callback(err);
            cache[id].style = source;
            cache[id].data._style = style.tmpid(id, true);
            cache[id].data._template = template;
            return callback(null, cache[id]);
        });
    };
    if (xml) {
        var opts = {};
        opts.xml = xml;
        opts.base = !source.tmpid(id) && uri.pathname;
        return cache[id] ? cache[id].update(opts, done) : new CachingBridge(opts, done);
    } else {
        return cache[id] ? done(null, cache[id]) : new CachingTileJSON({data:data}, done);
    }
};

// Writing.
source.save = function(data, callback) {
    var id = data.id;
    var uri = url.parse(data.id);
    var perm = !source.tmpid(id);
    var remote = uri.protocol === 'mapbox:';

    // "Soft" write for remote sources.
    if (remote) return source.info(id, function(err, info) {
        if (err) return callback(err);
        info.mtime = +new Date;
        info._prefs = data._prefs || info._prefs;
        info = source.normalize(info);
        source.refresh(info, null, callback);
    });

    data = _(data).defaults(defaults);
    data._tmp = source.tmpid(id);
    data.mtime = +new Date;
    data = source.normalize(data);

    source.toXML(data, function(err, xml) {
        if (err) return callback(err);
        if (!perm) return source.refresh(data, xml, callback);

        var files = [];
        var filtered = tm.filterkeys(data, defaults);
        filtered.Layer = filtered.Layer.map(function(l) { return tm.filterkeys(l, deflayer) });
        files.push({
            basename: 'data.yml',
            data: yaml.dump(tm.sortkeys(filtered), null, 2)
        });
        files.push({ basename: 'data.xml', data: xml });

        tm.writefiles(uri.pathname, files, function(err) {
            if (err) return callback(err);
            source.refresh(data, xml, function(err, p) {
                if (err) return callback(err);
                style.thumbSave(style.tmpid(id,true), path.join(uri.pathname,'.thumb.png'));
                callback(null, p);
            });
        });
    });
};

// Helper for tm.tmpid().
source.tmpid = function(id, md5) { return tm.tmpid('tmsource:', id, md5) };

source.toXML = function(data, callback) {
    if (data.tiles) return callback();

    // Include params to be written to XML.
    var opts = [
        'name',
        'description',
        'attribution',
        'center',
        'minzoom',
        'maxzoom'
    ].reduce(function(memo, key) {
        if (key in data) memo[key] = data[key];
        return memo;
    }, {});
    opts.srs = tm.srs['900913'];
    opts.Layer = data.Layer.map(function(l) {
        l.srs = l.srs || tm.srs['900913'];
        l.name = l.id;
        return l;
    });
    opts.json = JSON.stringify({ vector_layers: data.vector_layers });

    new carto.Renderer().render(tm.sortkeys(opts), callback);
};

// Autodetect the extent (bounds) of a source.
source.extent = function(data) {
    var extent = (data.Layer||[]).reduce(function(memo, l) {
        // If SRS is not one of the known convertibles, skip.
        var srsname = tm.srsname[l.srs];
        if (['900913','WGS84'].indexOf(srsname) === -1) return memo;

        // Leave out any explicit extents. We want to try to detect here.
        var opts = _(l.Datasource).reduce(function(memo, val, key) {
            if (key !== 'extent') memo[key] = val;
            return memo;
        }, {});

        try {
            var extent = new mapnik.Datasource(opts).extent();
            if (srsname === '900913') extent = sm.convert(extent, 'WGS84');
            memo[0] = Math.max(-180, Math.min(extent[0], memo[0]));
            memo[1] = Math.max(-85.0511, Math.min(extent[1], memo[1]));
            memo[2] = Math.min(180, Math.max(extent[2], memo[2]));
            memo[3] = Math.min(85.0511, Math.max(extent[3], memo[3]));
        } catch(err) { console.error(err); }
        return memo;
    }, [Infinity,Infinity,-Infinity,-Infinity]);
    return extent[0] === Infinity ? [-180,-85.0511,180,85.0511] : extent;
};

// Initialize defaults and derived properties on source data.
source.normalize = function(data) {
    data = _(data).defaults(defaults);
    // Initialize deep defaults for _prefs, layers.
    data._prefs = _(data._prefs).defaults(defaults._prefs);
    data.Layer = data.Layer.map(function(l) {
        l = _(l).defaults(deflayer);
        // @TODO mapnikref doesn't distinguish between keys that belong in
        // layer properties vs. attributes...
        l.properties = _(l.properties).defaults(deflayer.properties);
        // Ensure datasource keys are valid.
        l.Datasource = _(l.Datasource).reduce(function(memo, val, key) {
            if (!mapnikref.datasources[l.Datasource.type]) return memo;
            if (key === 'type') memo[key] = val;
            if (key in mapnikref.datasources[l.Datasource.type]) memo[key] = val;
            // Set a default extent value for postgis based on the SRS.
            if (l.Datasource.type === 'postgis' && key === 'extent' && !val) {
                _(tm.srs).each(function(srs, id) {
                    if (l.srs !== srs) return;
                    memo[key] = tm.extent[id];
                });
            }
            return memo
        }, {});
        return l;
    });
    // Construct vector_layers info from layer properties if necessary.
    data.vector_layers = url.parse(data.id).protocol === 'tmsource:'
        ? data.Layer.map(function(l) {
            var info = {};
            info.id = l.id;
            if ('description' in l) info.description = l.description;
            if ('minzoom' in l.properties) info.minzoom = l.properties.minzoom;
            if ('maxzoom' in l.properties) info.maxzoom = l.properties.maxzoom;
            info.fields = [];
            try {
                var fields = new mapnik.Datasource(l.Datasource).describe().fields;
                info.fields = _(fields).reduce(function(memo, type, field) {
                    memo[field] = l.fields[field] || type;
                    return memo;
                }, {});
            } catch(err) {
                console.error(err);
            }
            return info;
        })
        : data.vector_layers;
    return data;
};

// Light read of style info.
source.info = function(id, callback) {
    var uri = url.parse(id);

    if (uri.protocol !== 'tmsource:' && uri.protocol !== 'mapbox:')
        return callback(new Error('Invalid source protocol'));

    var load = function(data, callback) {
        data.id = id;
        return callback(null, data);
    };
    switch (uri.protocol) {
    case 'mapbox:':
        var loaded = false;
        if (tm.remote(id)) {
            load(_({}).defaults(tm.remote(id)), callback);
            loaded = true;
        }
        new TileJSON.get({
            uri: MAPBOX_API + uri.pathname + '.json',
            timeout: 5000
        }).asBuffer(function(err, data) {
            if (err) return callback(err);
            try { data = JSON.parse(data); }
            catch(err) { return callback(err); }
            if (!data.vector_layers) {
                tm.remote(id, undefined);
                return loaded || callback(new Error('Source ' + id + ' does not contain "vector_layers" key'));
            } else {
                tm.remote(id, data);
                return loaded || load(data, callback);
            }
        });
        break;
    case 'tmsource:':
        fs.readFile(path.join(uri.pathname,'data.yml'), 'utf8', function(err, data) {
            if (err) return callback(err);
            try { data = yaml.load(data); }
            catch(err) { return callback(err); }
            return load(data, callback);
        });
        break;
    default:
        callback(new Error('Unsupported source protocol'));
        break;
    }
};

// Invalidate a source from the cache.
source.invalidate = function(id, callback) {
    if (!cache[id] || !cache[id]._mbtiles) return callback();
    cache[id]._mbtiles._db.exec('PRAGMA synchronous=OFF; DELETE FROM map; DELETE FROM images;', callback);
};

// Generate a hash based on source pertinent source info.
// Hash changes when significant portions of the source have changed.
source.toHash = function(id, callback) {
    source.info(id, function(err, info) {
        if (err) return callback(err);
        info = _(info).reduce(function(memo, val, key) {
            if (key === 'mtime') return memo;
            if (key === '_prefs') return memo;
            memo[key] = val;
            return memo;
        }, {});
        callback(null, crypto.createHash('md5')
            .update(JSON.stringify(tm.sortkeys(info)))
            .digest('hex')
            .substr(0,8));
    });
};

// Grab mbtiles export info for a source. If no export exists and
// no export is in progress this will also kick off the export process.
source.mbtiles = function(id, force, callback) {
    source.toHash(id, function(err, hash) {
        if (err) return callback(err);
        var file = tm.config().cache + '/' + 'export-' + hash + '.mbtiles';
        if (force) return fs.unlink(file, function(err) {
            if (err && err.code !== 'ENOENT') return callback(err);
            tm.cleartask(function(err) {
                if (err) return callback(err);
                tm.copytask(id, 'mbtiles://' + file, callback);
            });
        });
        tm.copytask(id, 'mbtiles://' + file, callback);
    });
};

// Stream exported MBTiles on disk to dest.
source.toMBTiles = function(id, dest, callback) {
    callback = callback || function() {};
    if (!id) return callback(new Error('id is required.'));
    if (!dest) return callback(new Error('dest stream is required.'));

    source.toHash(id, function(err, hash) {
        if (err) return callback(err);

        // If dest is an HTTP response object, set an appropriate header.
        if (dest.writable && dest.setHeader) {
            var basename = path.basename(id, '.tm2');
            dest.setHeader('content-disposition', 'attachment; filename="'+basename+'.mbtiles"');
            dest.setHeader('content-type', 'application/x-sqlite3');
        }

        var file = tm.config().cache + '/' + 'export-' + hash + '.mbtiles';
        try {
            var read = fs.createReadStream(file, { flags:'r', autoClose:true });
            read.pipe(dest);
            read.on('error', callback);
            read.on('end', callback);
            dest.on('error', callback);
        } catch(err) { callback(err); }
    });
};
<commit_msg>Fix relative paths for datasource handling.<commit_after>var _ = require('underscore');
var carto = require('carto');
var crypto = require('crypto');
var fs = require('fs');
var path = require('path');
var url = require('url');
var mkdirp = require('mkdirp');
var sm = new (require('sphericalmercator'));
var yaml = require('js-yaml');
var tm = require('./tm');
var Bridge = require('tilelive-bridge');
var TileJSON = require('tilejson');
var tilelive = require('tilelive');
var style;
var MAPBOX_API = 'http://a.tiles.mapbox.com/v3'
var CachingTileJSON = require('./cache')(TileJSON, tm.config().cache);
var CachingBridge = require('./cache')(Bridge, tm.config().cache);
var mapnik = require('mapnik');
var mapnikref = require('mapnik-reference').version.latest;

var defaults = {
    name:'',
    description:'',
    attribution:'',
    mtime:+new Date,
    minzoom:0,
    maxzoom:6,
    center:[0,0,3],
    Layer:[],
    _prefs: {
        saveCenter: true,
        disabled: [],
        inspector: false
    }
};
var deflayer = {
    id:'',
    srs:'',
    description:'',
    fields: {},
    Datasource: {},
    properties: {
        minzoom:0,
        maxzoom:22,
        'buffer-size':0
    }
};

var cache = {};

module.exports = source;
tilelive.protocols['mapbox:'] = source;
tilelive.protocols['tmsource:'] = source;

source.defaults = defaults;
source.deflayer = deflayer;

function source(arg, callback) {
    if ('string' !== typeof arg) {
        var id = url.format(arg);
        var uri = arg;
    } else {
        var id = arg;
        var uri = url.parse(arg);
    }

    if (uri.protocol !== 'tmsource:' && uri.protocol !== 'mapbox:')
        return callback(new Error('Invalid source protocol'));

    if (cache[id]) return callback(null, cache[id]);

    source.info(id, function(err, data) {
        if (err) return callback(err);
        data = source.normalize(data);
        source.toXML(data, function(err, xml) {
            if (err) return callback(err);
            source.refresh(data, xml, callback);
        });
    });
};

// Load or refresh the relevant source using specified data + xml.
source.refresh = function(data, xml, callback) {
    var id = data.id;
    var uri = url.parse(data.id);
    var done = function(err, p) {
        if (err) return callback(err);
        cache[id] = cache[id] || p;
        cache[id].data = data;

        var interactivity_layer;
        var template = '';
        if (data.vector_layers.length && data._prefs.inspector) {
            var inspector = _(data.vector_layers).find(function(l) {
                return l.id === data._prefs.inspector;
            });
            if (inspector && inspector.fields) {
                interactivity_layer = inspector.id;
                template = tm.templates.xraytemplate(Object.keys(inspector.fields));
            }
        }
        style = style || require('./style');
        style.save({
            id: style.tmpid(id, true),
            styles: [tm.templates.xraystyles(data)],
            source: id,
            center: data.center,
            interactivity_layer: interactivity_layer,
            template: template
        }, function(err, source) {
            if (err) return callback(err);
            cache[id].style = source;
            cache[id].data._style = style.tmpid(id, true);
            cache[id].data._template = template;
            return callback(null, cache[id]);
        });
    };
    if (xml) {
        var opts = {};
        opts.xml = xml;
        opts.base = !source.tmpid(id) && uri.pathname;
        return cache[id] ? cache[id].update(opts, done) : new CachingBridge(opts, done);
    } else {
        return cache[id] ? done(null, cache[id]) : new CachingTileJSON({data:data}, done);
    }
};

// Writing.
source.save = function(data, callback) {
    var id = data.id;
    var uri = url.parse(data.id);
    var perm = !source.tmpid(id);
    var remote = uri.protocol === 'mapbox:';

    // "Soft" write for remote sources.
    if (remote) return source.info(id, function(err, info) {
        if (err) return callback(err);
        info.mtime = +new Date;
        info._prefs = data._prefs || info._prefs;
        info = source.normalize(info);
        source.refresh(info, null, callback);
    });

    data = _(data).defaults(defaults);
    data._tmp = source.tmpid(id);
    data.mtime = +new Date;
    data = source.normalize(data);

    source.toXML(data, function(err, xml) {
        if (err) return callback(err);
        if (!perm) return source.refresh(data, xml, callback);

        var files = [];
        var filtered = tm.filterkeys(data, defaults);
        filtered.Layer = filtered.Layer.map(function(l) { return tm.filterkeys(l, deflayer) });
        files.push({
            basename: 'data.yml',
            data: yaml.dump(tm.sortkeys(filtered), null, 2)
        });
        files.push({ basename: 'data.xml', data: xml });

        tm.writefiles(uri.pathname, files, function(err) {
            if (err) return callback(err);
            source.refresh(data, xml, function(err, p) {
                if (err) return callback(err);
                style.thumbSave(style.tmpid(id,true), path.join(uri.pathname,'.thumb.png'));
                callback(null, p);
            });
        });
    });
};

// Helper for tm.tmpid().
source.tmpid = function(id, md5) { return tm.tmpid('tmsource:', id, md5) };

source.toXML = function(data, callback) {
    if (data.tiles) return callback();

    // Include params to be written to XML.
    var opts = [
        'name',
        'description',
        'attribution',
        'center',
        'minzoom',
        'maxzoom'
    ].reduce(function(memo, key) {
        if (key in data) memo[key] = data[key];
        return memo;
    }, {});
    opts.srs = tm.srs['900913'];
    opts.Layer = data.Layer.map(function(l) {
        l.srs = l.srs || tm.srs['900913'];
        l.name = l.id;
        return l;
    });
    opts.json = JSON.stringify({ vector_layers: data.vector_layers });

    new carto.Renderer().render(tm.sortkeys(opts), callback);
};

// Autodetect the extent (bounds) of a source.
source.extent = function(data) {
    var extent = (data.Layer||[]).reduce(function(memo, l) {
        // If SRS is not one of the known convertibles, skip.
        var srsname = tm.srsname[l.srs];
        if (['900913','WGS84'].indexOf(srsname) === -1) return memo;

        // Leave out any explicit extents. We want to try to detect here.
        var opts = _(l.Datasource).reduce(function(memo, val, key) {
            if (key !== 'extent') memo[key] = val;
            return memo;
        }, {});

        // Ugh. Also, Windows.
        if (opts.file && opts.file.charAt(0) !== '/') opts.base = url.parse(data.id).pathname;

        try {
            var extent = new mapnik.Datasource(opts).extent();
            if (srsname === '900913') extent = sm.convert(extent, 'WGS84');
            memo[0] = Math.max(-180, Math.min(extent[0], memo[0]));
            memo[1] = Math.max(-85.0511, Math.min(extent[1], memo[1]));
            memo[2] = Math.min(180, Math.max(extent[2], memo[2]));
            memo[3] = Math.min(85.0511, Math.max(extent[3], memo[3]));
        } catch(err) { console.error(err); }
        return memo;
    }, [Infinity,Infinity,-Infinity,-Infinity]);
    return extent[0] === Infinity ? [-180,-85.0511,180,85.0511] : extent;
};

// Initialize defaults and derived properties on source data.
source.normalize = function(data) {
    data = _(data).defaults(defaults);
    // Initialize deep defaults for _prefs, layers.
    data._prefs = _(data._prefs).defaults(defaults._prefs);
    data.Layer = data.Layer.map(function(l) {
        l = _(l).defaults(deflayer);
        // @TODO mapnikref doesn't distinguish between keys that belong in
        // layer properties vs. attributes...
        l.properties = _(l.properties).defaults(deflayer.properties);
        // Ensure datasource keys are valid.
        l.Datasource = _(l.Datasource).reduce(function(memo, val, key) {
            if (!mapnikref.datasources[l.Datasource.type]) return memo;
            if (key === 'type') memo[key] = val;
            if (key in mapnikref.datasources[l.Datasource.type]) memo[key] = val;
            // Set a default extent value for postgis based on the SRS.
            if (l.Datasource.type === 'postgis' && key === 'extent' && !val) {
                _(tm.srs).each(function(srs, id) {
                    if (l.srs !== srs) return;
                    memo[key] = tm.extent[id];
                });
            }
            return memo
        }, {});
        return l;
    });
    // Construct vector_layers info from layer properties if necessary.
    data.vector_layers = url.parse(data.id).protocol === 'tmsource:'
        ? data.Layer.map(function(l) {
            var info = {};
            info.id = l.id;
            if ('description' in l) info.description = l.description;
            if ('minzoom' in l.properties) info.minzoom = l.properties.minzoom;
            if ('maxzoom' in l.properties) info.maxzoom = l.properties.maxzoom;
            info.fields = [];
            try {
                var opts = _(l.Datasource).clone();

                // Ugh. Also, Windows.
                if (opts.file && opts.file.charAt(0) !== '/') opts.base = url.parse(data.id).pathname;

                var fields = new mapnik.Datasource(opts).describe().fields;
                info.fields = _(fields).reduce(function(memo, type, field) {
                    memo[field] = l.fields[field] || type;
                    return memo;
                }, {});
            } catch(err) {
                console.error(err);
            }
            return info;
        })
        : data.vector_layers;
    return data;
};

// Light read of style info.
source.info = function(id, callback) {
    var uri = url.parse(id);

    if (uri.protocol !== 'tmsource:' && uri.protocol !== 'mapbox:')
        return callback(new Error('Invalid source protocol'));

    var load = function(data, callback) {
        data.id = id;
        return callback(null, data);
    };
    switch (uri.protocol) {
    case 'mapbox:':
        var loaded = false;
        if (tm.remote(id)) {
            load(_({}).defaults(tm.remote(id)), callback);
            loaded = true;
        }
        new TileJSON.get({
            uri: MAPBOX_API + uri.pathname + '.json',
            timeout: 5000
        }).asBuffer(function(err, data) {
            if (err) return callback(err);
            try { data = JSON.parse(data); }
            catch(err) { return callback(err); }
            if (!data.vector_layers) {
                tm.remote(id, undefined);
                return loaded || callback(new Error('Source ' + id + ' does not contain "vector_layers" key'));
            } else {
                tm.remote(id, data);
                return loaded || load(data, callback);
            }
        });
        break;
    case 'tmsource:':
        fs.readFile(path.join(uri.pathname,'data.yml'), 'utf8', function(err, data) {
            if (err) return callback(err);
            try { data = yaml.load(data); }
            catch(err) { return callback(err); }
            return load(data, callback);
        });
        break;
    default:
        callback(new Error('Unsupported source protocol'));
        break;
    }
};

// Invalidate a source from the cache.
source.invalidate = function(id, callback) {
    if (!cache[id] || !cache[id]._mbtiles) return callback();
    cache[id]._mbtiles._db.exec('PRAGMA synchronous=OFF; DELETE FROM map; DELETE FROM images;', callback);
};

// Generate a hash based on source pertinent source info.
// Hash changes when significant portions of the source have changed.
source.toHash = function(id, callback) {
    source.info(id, function(err, info) {
        if (err) return callback(err);
        info = _(info).reduce(function(memo, val, key) {
            if (key === 'mtime') return memo;
            if (key === '_prefs') return memo;
            memo[key] = val;
            return memo;
        }, {});
        callback(null, crypto.createHash('md5')
            .update(JSON.stringify(tm.sortkeys(info)))
            .digest('hex')
            .substr(0,8));
    });
};

// Grab mbtiles export info for a source. If no export exists and
// no export is in progress this will also kick off the export process.
source.mbtiles = function(id, force, callback) {
    source.toHash(id, function(err, hash) {
        if (err) return callback(err);
        var file = tm.config().cache + '/' + 'export-' + hash + '.mbtiles';
        if (force) return fs.unlink(file, function(err) {
            if (err && err.code !== 'ENOENT') return callback(err);
            tm.cleartask(function(err) {
                if (err) return callback(err);
                tm.copytask(id, 'mbtiles://' + file, callback);
            });
        });
        tm.copytask(id, 'mbtiles://' + file, callback);
    });
};

// Stream exported MBTiles on disk to dest.
source.toMBTiles = function(id, dest, callback) {
    callback = callback || function() {};
    if (!id) return callback(new Error('id is required.'));
    if (!dest) return callback(new Error('dest stream is required.'));

    source.toHash(id, function(err, hash) {
        if (err) return callback(err);

        // If dest is an HTTP response object, set an appropriate header.
        if (dest.writable && dest.setHeader) {
            var basename = path.basename(id, '.tm2');
            dest.setHeader('content-disposition', 'attachment; filename="'+basename+'.mbtiles"');
            dest.setHeader('content-type', 'application/x-sqlite3');
        }

        var file = tm.config().cache + '/' + 'export-' + hash + '.mbtiles';
        try {
            var read = fs.createReadStream(file, { flags:'r', autoClose:true });
            read.pipe(dest);
            read.on('error', callback);
            read.on('end', callback);
            dest.on('error', callback);
        } catch(err) { callback(err); }
    });
};
<|endoftext|>


<commit_before>/**
 * @ngdoc module
 * @name material.components.tooltip
 */
angular
    .module('material.components.tooltip', [ 'material.core' ])
    .directive('mdTooltip', MdTooltipDirective);

/**
 * @ngdoc directive
 * @name mdTooltip
 * @module material.components.tooltip
 * @description
 * Tooltips are used to describe elements that are interactive and primarily graphical (not textual).
 *
 * Place a `<md-tooltip>` as a child of the element it describes.
 *
 * A tooltip will activate when the user focuses, hovers over, or touches the parent.
 *
 * @usage
 * <hljs lang="html">
 * <md-button class="md-fab md-accent" aria-label="Play">
 *   <md-tooltip>
 *     Play Music
 *   </md-tooltip>
 *   <md-icon icon="img/icons/ic_play_arrow_24px.svg"></md-icon>
 * </md-button>
 * </hljs>
 *
 * @param {expression=} md-visible Boolean bound to whether the tooltip is currently visible.
 * @param {number=} md-delay How many milliseconds to wait to show the tooltip after the user focuses, hovers, or touches the parent. Defaults to 0ms.
 * @param {boolean=} md-autohide If present or provided with a boolean value, the tooltip will hide on mouse leave, regardless of focus
 * @param {string=} md-direction Which direction would you like the tooltip to go?  Supports left, right, top, and bottom.  Defaults to bottom.
 */
function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdTheming, $rootElement,
                            $animate, $q) {

  var TOOLTIP_SHOW_DELAY = 0;
  var TOOLTIP_WINDOW_EDGE_SPACE = 8;

  return {
    restrict: 'E',
    transclude: true,
    priority:210, // Before ngAria
    template: '

',
    scope: {
      delay: '=?mdDelay',
      visible: '=?mdVisible',
      autohide: '=?mdAutohide',
      direction: '@?mdDirection'    // only expect raw or interpolated string value; not expression
    },
    link: postLink
  };

  function postLink(scope, element, attr) {

    $mdTheming(element);

    var parent        = $mdUtil.getParentWithPointerEvents(element),
        content       = angular.element(element[0].getElementsByClassName('_md-content')[0]),
        tooltipParent = angular.element(document.body),
        debouncedOnResize = $$rAF.throttle(function () { updatePosition(); });

    if ($animate.pin) $animate.pin(element, parent);

    // Initialize element

    setDefaults();
    manipulateElement();
    bindEvents();

    // Default origin transform point is 'center top'
    // positionTooltip() is always relative to center top
    updateContentOrigin();

    configureWatchers();
    addAriaLabel();

    function setDefaults () {
      scope.delay = scope.delay || TOOLTIP_SHOW_DELAY;
    }

    function updateContentOrigin() {
      var origin = 'center top';
      switch (scope.direction) {
        case 'left'  : origin =  'right center';  break;
        case 'right' : origin =  'left center';   break;
        case 'top'   : origin =  'center bottom'; break;
        case 'bottom': origin =  'center top';    break;
      }
      content.css('transform-origin', origin);
    }

    function configureWatchers () {
      scope.$on('$destroy', function() {
        scope.visible = false;
        element.remove();
        angular.element($window).off('resize', debouncedOnResize);
      });

      scope.$watch('visible', function (isVisible) {
        if (isVisible) showTooltip();
        else hideTooltip();
      });

      scope.$watch('direction', updatePosition );
    }

    function addAriaLabel () {
      if (!parent.attr('aria-label') && !parent.text().trim()) {
        parent.attr('aria-label', element.text().trim());
      }
    }

    function manipulateElement () {
      element.detach();
      element.attr('role', 'tooltip');
    }

    function bindEvents () {
      var mouseActive = false;

      var ngWindow = angular.element($window);

      // add an mutationObserver when there is support for it
      // and the need for it in the form of viable host(parent[0])
      if (parent[0] && 'MutationObserver' in $window) {
        // use an mutationObserver to tackle #2602
        var attributeObserver = new MutationObserver(function(mutations) {
          mutations.forEach(function (mutation) {
            if (mutation.attributeName === 'disabled' && parent[0].disabled) {
              setVisible(false);
              scope.$digest(); // make sure the elements gets updated
            }
          });
        });

        attributeObserver.observe(parent[0], { attributes: true});
      }

      // Store whether the element was focused when the window loses focus.
      var windowBlurHandler = function() {
        elementFocusedOnWindowBlur = document.activeElement === parent[0];
      };
      var elementFocusedOnWindowBlur = false;

      function windowScrollHandler() {
        setVisible(false);
      }

      ngWindow.on('blur', windowBlurHandler);
      ngWindow.on('resize', debouncedOnResize);
      document.addEventListener('scroll', windowScrollHandler, true);
      scope.$on('$destroy', function() {
        ngWindow.off('blur', windowBlurHandler);
        ngWindow.off('resize', debouncedOnResize);
        document.removeEventListener('scroll', windowScrollHandler, true);
        attributeObserver && attributeObserver.disconnect();
      });

      var enterHandler = function(e) {
        // Prevent the tooltip from showing when the window is receiving focus.
        if (e.type === 'focus' && elementFocusedOnWindowBlur) {
          elementFocusedOnWindowBlur = false;
          return;
        }
        parent.on('blur mouseleave touchend touchcancel', leaveHandler );
        setVisible(true);
      };
      var leaveHandler = function () {
        var autohide = scope.hasOwnProperty('autohide') ? scope.autohide : attr.hasOwnProperty('mdAutohide');
        if (autohide || mouseActive || ($document[0].activeElement !== parent[0]) ) {
          parent.off('blur mouseleave touchend touchcancel', leaveHandler );
          parent.triggerHandler("blur");
          setVisible(false);
        }
        mouseActive = false;
      };

      // to avoid `synthetic clicks` we listen to mousedown instead of `click`
      parent.on('mousedown', function() { mouseActive = true; });
      parent.on('focus mouseenter touchstart', enterHandler );

    }

    function setVisible (value) {
      // break if passed value is already in queue or there is no queue and passed value is current in the scope
      if (setVisible.queued && setVisible.visible === !!value || scope.visible === !!value) return;

      setVisible.value = !!value;
      if (!setVisible.queued) {
        if (value) {
          setVisible.queued = true;
          $timeout(function() {
            scope.visible = setVisible.value;
            setVisible.queued = false;
          }, scope.delay);
        } else {
          $mdUtil.nextTick(function() { scope.visible = false; });
        }
      }
    }

    function showTooltip() {
      // Insert the element before positioning it, so we can get the position
      // and check if we should display it
      tooltipParent.append(element);

      // Check if we should display it or not.
      // This handles hide-* and show-* along with any user defined css
      if ( $mdUtil.hasComputedStyle(element, 'display', 'none')) {
        scope.visible = false;
        element.detach();
        return;
      }

      updatePosition();

      angular.forEach([element, content], function (element) {
        $animate.addClass(element, '_md-show');
      });
    }

    function hideTooltip() {
        var promises = [];
        angular.forEach([element, content], function (it) {
          if (it.parent() && it.hasClass('_md-show')) {
            promises.push($animate.removeClass(it, '_md-show'));
          }
        });

        $q.all(promises)
          .then(function () {
            if (!scope.visible) element.detach();
          });
    }

    function updatePosition() {
      if ( !scope.visible ) return;

      updateContentOrigin();
      positionTooltip();
    }

    function positionTooltip() {
      var tipRect = $mdUtil.offsetRect(element, tooltipParent);
      var parentRect = $mdUtil.offsetRect(parent, tooltipParent);
      var newPosition = getPosition(scope.direction);
      var offsetParent = element.prop('offsetParent');

      // If the user provided a direction, just nudge the tooltip onto the screen
      // Otherwise, recalculate based on 'top' since default is 'bottom'
      if (scope.direction) {
        newPosition = fitInParent(newPosition);
      } else if (offsetParent && newPosition.top > offsetParent.scrollHeight - tipRect.height - TOOLTIP_WINDOW_EDGE_SPACE) {
        newPosition = fitInParent(getPosition('top'));
      }

      element.css({
        left: newPosition.left + 'px',
        top: newPosition.top + 'px'
      });

      function fitInParent (pos) {
        var newPosition = { left: pos.left, top: pos.top };
        newPosition.left = Math.min( newPosition.left, tooltipParent.prop('scrollWidth') - tipRect.width - TOOLTIP_WINDOW_EDGE_SPACE );
        newPosition.left = Math.max( newPosition.left, TOOLTIP_WINDOW_EDGE_SPACE );
        newPosition.top  = Math.min( newPosition.top,  tooltipParent.prop('scrollHeight') - tipRect.height - TOOLTIP_WINDOW_EDGE_SPACE );
        newPosition.top  = Math.max( newPosition.top,  TOOLTIP_WINDOW_EDGE_SPACE );
        return newPosition;
      }

      function getPosition (dir) {
        return dir === 'left'
          ? { left: parentRect.left - tipRect.width - TOOLTIP_WINDOW_EDGE_SPACE,
              top: parentRect.top + parentRect.height / 2 - tipRect.height / 2 }
          : dir === 'right'
          ? { left: parentRect.left + parentRect.width + TOOLTIP_WINDOW_EDGE_SPACE,
              top: parentRect.top + parentRect.height / 2 - tipRect.height / 2 }
          : dir === 'top'
          ? { left: parentRect.left + parentRect.width / 2 - tipRect.width / 2,
              top: parentRect.top - tipRect.height - TOOLTIP_WINDOW_EDGE_SPACE }
          : { left: parentRect.left + parentRect.width / 2 - tipRect.width / 2,
              top: parentRect.top + parentRect.height + TOOLTIP_WINDOW_EDGE_SPACE };
      }
    }

  }

}
<commit_msg>perf(md-tooltip) use MutationObserver instead of watcher if available use watcher on mdVisible only if being used<commit_after>/**
 * @ngdoc module
 * @name material.components.tooltip
 */
angular
    .module('material.components.tooltip', [ 'material.core' ])
    .directive('mdTooltip', MdTooltipDirective);

/**
 * @ngdoc directive
 * @name mdTooltip
 * @module material.components.tooltip
 * @description
 * Tooltips are used to describe elements that are interactive and primarily graphical (not textual).
 *
 * Place a `<md-tooltip>` as a child of the element it describes.
 *
 * A tooltip will activate when the user focuses, hovers over, or touches the parent.
 *
 * @usage
 * <hljs lang="html">
 * <md-button class="md-fab md-accent" aria-label="Play">
 *   <md-tooltip>
 *     Play Music
 *   </md-tooltip>
 *   <md-icon icon="img/icons/ic_play_arrow_24px.svg"></md-icon>
 * </md-button>
 * </hljs>
 *
 * @param {expression=} md-visible Boolean bound to whether the tooltip is currently visible.
 * @param {number=} md-delay How many milliseconds to wait to show the tooltip after the user focuses, hovers, or touches the parent. Defaults to 0ms.
 * @param {boolean=} md-autohide If present or provided with a boolean value, the tooltip will hide on mouse leave, regardless of focus
 * @param {string=} md-direction Which direction would you like the tooltip to go?  Supports left, right, top, and bottom.  Defaults to bottom.
 */
function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdTheming, $rootElement,
                            $animate, $q) {

  var TOOLTIP_SHOW_DELAY = 0;
  var TOOLTIP_WINDOW_EDGE_SPACE = 8;

  return {
    restrict: 'E',
    transclude: true,
    priority:210, // Before ngAria
    template: '

',
    scope: {
      delay: '=?mdDelay',
      visible: '=?mdVisible',
      autohide: '=?mdAutohide',
      direction: '@?mdDirection'    // only expect raw or interpolated string value; not expression
    },
    link: postLink
  };

  function postLink(scope, element, attr) {

    $mdTheming(element);

    var parent        = $mdUtil.getParentWithPointerEvents(element),
        content       = angular.element(element[0].getElementsByClassName('_md-content')[0]),
        tooltipParent = angular.element(document.body),
        debouncedOnResize = $$rAF.throttle(function () { updatePosition(); });

    if ($animate.pin) $animate.pin(element, parent);

    // Initialize element

    setDefaults();
    manipulateElement();
    bindEvents();

    // Default origin transform point is 'center top'
    // positionTooltip() is always relative to center top
    updateContentOrigin();

    configureWatchers();
    addAriaLabel();

    function setDefaults () {
      scope.delay = scope.delay || TOOLTIP_SHOW_DELAY;
    }

    function updateContentOrigin() {
      var origin = 'center top';
      switch (scope.direction) {
        case 'left'  : origin =  'right center';  break;
        case 'right' : origin =  'left center';   break;
        case 'top'   : origin =  'center bottom'; break;
        case 'bottom': origin =  'center top';    break;
      }
      content.css('transform-origin', origin);
    }

    function onVisibleChanged (isVisible) {
      if (isVisible) showTooltip();
      else hideTooltip();
    }

    function configureWatchers () {
      scope.$on('$destroy', function() {
        scope.visible = false;
        element.remove();
        angular.element($window).off('resize', debouncedOnResize);
      });

      if (element[0] && 'MutationObserver' in $window) {
        var attributeObserver = new MutationObserver(function(mutations) {
          mutations
            .forEach(function (mutation) {
              if (mutation.attributeName === 'md-visible') {
                if (!scope.visibleWatcher)
                  scope.visibleWatcher = scope.$watch('visible', onVisibleChanged );
              }
              if (mutation.attributeName === 'md-direction') {
                updatePosition(scope.direction);
              }
            });
        });

        attributeObserver.observe(element[0], { attributes: true});

        if (attr.hasOwnProperty('mdVisible')) // build watcher only if mdVisible is being used
          scope.visibleWatcher = scope.$watch('visible', onVisibleChanged );

      }
      else { // MutationObserver not supported
        scope.visibleWatcher = scope.$watch('visible', onVisibleChanged );
        scope.$watch('direction', updatePosition );
      }
    }

    function addAriaLabel () {
      if (!parent.attr('aria-label') && !parent.text().trim()) {
        parent.attr('aria-label', element.text().trim());
      }
    }

    function manipulateElement () {
      element.detach();
      element.attr('role', 'tooltip');
    }

    function bindEvents () {
      var mouseActive = false;

      var ngWindow = angular.element($window);

      // add an mutationObserver when there is support for it
      // and the need for it in the form of viable host(parent[0])
      if (parent[0] && 'MutationObserver' in $window) {
        // use an mutationObserver to tackle #2602
        var attributeObserver = new MutationObserver(function(mutations) {
          mutations.forEach(function (mutation) {
            if (mutation.attributeName === 'disabled' && parent[0].disabled) {
              setVisible(false);
              scope.$digest(); // make sure the elements gets updated
            }
          });
        });

        attributeObserver.observe(parent[0], { attributes: true});
      }

      // Store whether the element was focused when the window loses focus.
      var windowBlurHandler = function() {
        elementFocusedOnWindowBlur = document.activeElement === parent[0];
      };
      var elementFocusedOnWindowBlur = false;

      function windowScrollHandler() {
        setVisible(false);
      }

      ngWindow.on('blur', windowBlurHandler);
      ngWindow.on('resize', debouncedOnResize);
      document.addEventListener('scroll', windowScrollHandler, true);
      scope.$on('$destroy', function() {
        ngWindow.off('blur', windowBlurHandler);
        ngWindow.off('resize', debouncedOnResize);
        document.removeEventListener('scroll', windowScrollHandler, true);
        attributeObserver && attributeObserver.disconnect();
      });

      var enterHandler = function(e) {
        // Prevent the tooltip from showing when the window is receiving focus.
        if (e.type === 'focus' && elementFocusedOnWindowBlur) {
          elementFocusedOnWindowBlur = false;
          return;
        }
        parent.on('blur mouseleave touchend touchcancel', leaveHandler );
        setVisible(true);
      };
      var leaveHandler = function () {
        var autohide = scope.hasOwnProperty('autohide') ? scope.autohide : attr.hasOwnProperty('mdAutohide');
        if (autohide || mouseActive || ($document[0].activeElement !== parent[0]) ) {
          parent.off('blur mouseleave touchend touchcancel', leaveHandler );
          parent.triggerHandler("blur");
          setVisible(false);
        }
        mouseActive = false;
      };

      // to avoid `synthetic clicks` we listen to mousedown instead of `click`
      parent.on('mousedown', function() { mouseActive = true; });
      parent.on('focus mouseenter touchstart', enterHandler );

    }

    function setVisible (value) {
      // break if passed value is already in queue or there is no queue and passed value is current in the scope
      if (setVisible.queued && setVisible.visible === !!value || scope.visible === !!value) return;

      setVisible.value = !!value;
      if (!setVisible.queued) {
        if (value) {
          setVisible.queued = true;
          $timeout(function() {
            scope.visible = setVisible.value;
            setVisible.queued = false;
            if (!scope.visibleWatcher)
              onVisibleChanged(scope.visible);
          }, scope.delay);
        } else {
          $mdUtil.nextTick(function() {
            scope.visible = false;
            if (!scope.visibleWatcher)
              onVisibleChanged(false);
          });
        }
      }
    }

    function showTooltip() {
      // Insert the element before positioning it, so we can get the position
      // and check if we should display it
      tooltipParent.append(element);

      // Check if we should display it or not.
      // This handles hide-* and show-* along with any user defined css
      if ( $mdUtil.hasComputedStyle(element, 'display', 'none')) {
        scope.visible = false;
        element.detach();
        return;
      }

      updatePosition();

      angular.forEach([element, content], function (element) {
        $animate.addClass(element, '_md-show');
      });
    }

    function hideTooltip() {
        var promises = [];
        angular.forEach([element, content], function (it) {
          if (it.parent() && it.hasClass('_md-show')) {
            promises.push($animate.removeClass(it, '_md-show'));
          }
        });

        $q.all(promises)
          .then(function () {
            if (!scope.visible) element.detach();
          });
    }

    function updatePosition() {
      if ( !scope.visible ) return;

      updateContentOrigin();
      positionTooltip();
    }

    function positionTooltip() {
      var tipRect = $mdUtil.offsetRect(element, tooltipParent);
      var parentRect = $mdUtil.offsetRect(parent, tooltipParent);
      var newPosition = getPosition(scope.direction);
      var offsetParent = element.prop('offsetParent');

      // If the user provided a direction, just nudge the tooltip onto the screen
      // Otherwise, recalculate based on 'top' since default is 'bottom'
      if (scope.direction) {
        newPosition = fitInParent(newPosition);
      } else if (offsetParent && newPosition.top > offsetParent.scrollHeight - tipRect.height - TOOLTIP_WINDOW_EDGE_SPACE) {
        newPosition = fitInParent(getPosition('top'));
      }

      element.css({
        left: newPosition.left + 'px',
        top: newPosition.top + 'px'
      });

      function fitInParent (pos) {
        var newPosition = { left: pos.left, top: pos.top };
        newPosition.left = Math.min( newPosition.left, tooltipParent.prop('scrollWidth') - tipRect.width - TOOLTIP_WINDOW_EDGE_SPACE );
        newPosition.left = Math.max( newPosition.left, TOOLTIP_WINDOW_EDGE_SPACE );
        newPosition.top  = Math.min( newPosition.top,  tooltipParent.prop('scrollHeight') - tipRect.height - TOOLTIP_WINDOW_EDGE_SPACE );
        newPosition.top  = Math.max( newPosition.top,  TOOLTIP_WINDOW_EDGE_SPACE );
        return newPosition;
      }

      function getPosition (dir) {
        return dir === 'left'
          ? { left: parentRect.left - tipRect.width - TOOLTIP_WINDOW_EDGE_SPACE,
              top: parentRect.top + parentRect.height / 2 - tipRect.height / 2 }
          : dir === 'right'
          ? { left: parentRect.left + parentRect.width + TOOLTIP_WINDOW_EDGE_SPACE,
              top: parentRect.top + parentRect.height / 2 - tipRect.height / 2 }
          : dir === 'top'
          ? { left: parentRect.left + parentRect.width / 2 - tipRect.width / 2,
              top: parentRect.top - tipRect.height - TOOLTIP_WINDOW_EDGE_SPACE }
          : { left: parentRect.left + parentRect.width / 2 - tipRect.width / 2,
              top: parentRect.top + parentRect.height + TOOLTIP_WINDOW_EDGE_SPACE };
      }
    }

  }

}
<|endoftext|>


<commit_before>angular.module('ippon').controller('TeamsController',[
  '$scope',
  '$stateParams',
  '$location',
  '$resource',
  '$state',
  'Auth',
  function($scope, $stateParams, $location, $resource, $state, Auth){

    var teamResource = $resource("/teams/:teamId",
    {
      teamId: "@id",
      format: "json"
    }, {
      'save': {
        method: 'PUT'
      },
      'create': {
        method: 'POST'
      },
      'add_member' : {
        method: 'PUT',
        url: '/teams/:teamId/add_member/:playerId',
        params: {
          teamId: "@teamId",
          playerId: "@playerId"
        }
      },
      'delete_member' : {
        method: 'DELETE',
        url: '/teams/:teamId/delete_member/:playerId'
      }
    });

    var getTeam = function(teamId, next) {
      teamResource.get({
        teamId: teamId
      }, function(response) {
        $scope.team = response.team;
        $scope.members = response.players;
        $scope.is_admin = response.is_admin;
        if(next) {
          next();
        }
      }, function(httpResponse) {
        $scope.team = null;
        $scope.members = null;
        $scope.is_admin = false;
      });
    }

    var tournamentResource = $resource("/tournaments/:tournamentId",
    {
      tournamentId: "@id",
      format: "json"
    },{
      'participants' : {
        method: 'GET',
        isArray: true,
        url: "/tournaments/:tournamentId/participants"
      },
      'unassigned' : {
        method: 'GET',
        isArray: true,
        url: "/tournaments/:tournamentId/participants/unassigned",
        params: {
          tournamentId: "@tournamentId"
        }
      }
    });

    var getAvailablePlayers = function() {
      tournamentResource.unassigned({tournamentId: $scope.team.tournament_id}, function(results) {
        $scope.players = results;
      }, function(httpResponse) {
        $scope.players = null;
      });
    }

    if($stateParams.teamId && ($state.is('teams_show') || $state.is('teams_edit'))) {
      if($state.is('teams_edit')) {
        getTeam($stateParams.teamId, getAvailablePlayers);
      } else {
        getTeam($stateParams.teamId);
      }
    } else {
      $scope.team = {};
      $scope.tournament_id = $stateParams.tournament_id;
    }

    $scope.save = function() {
      var onError = function(_httpResponse) {
        //TODO flash.error
      };

      if($scope.team.id) {
        teamResource.save($scope.team, function() {
          $state.go('teams_show',{teamId: $scope.team.id});
        }, onError);
      } else {
        teamResource.create($scope.team, function(newTeam) {
          $state.go('teams_show',{teamId: newTeam.id});
        }, onError);
      }
    };

    $scope["delete"] = function() {
      teamResource.delete({teamId: $scope.team.id});
      $state.go('home');
    }

    $scope.add_member = function(playerId) {
      teamResource.add_member({teamId: $scope.team.id, playerId: playerId},
      function() {
        getTeam($scope.team.id, getAvailablePlayers);
      });
    };

    $scope.delete_member = function(playerId) {
      teamResource.delete_member({teamId: $scope.team.id, playerId: playerId},
      function() {
        getTeam($scope.team.id, getAvailablePlayers);
      });
    };

    $scope.edit = function() {
      $state.go('teams_edit',{teamId: $scope.team.id});
    };

    $scope.cancel = function() {
      if($scope.player.id) {
        $state.go('teams_show',{teamId: $scope.team.id});
      } else {
        $state.go('teams');
      }
    };
  }]);
<commit_msg>Fix cancel button in Teams edit view<commit_after>angular.module('ippon').controller('TeamsController',[
  '$scope',
  '$stateParams',
  '$location',
  '$resource',
  '$state',
  'Auth',
  function($scope, $stateParams, $location, $resource, $state, Auth){

    var teamResource = $resource("/teams/:teamId",
    {
      teamId: "@id",
      format: "json"
    }, {
      'save': {
        method: 'PUT'
      },
      'create': {
        method: 'POST'
      },
      'add_member' : {
        method: 'PUT',
        url: '/teams/:teamId/add_member/:playerId',
        params: {
          teamId: "@teamId",
          playerId: "@playerId"
        }
      },
      'delete_member' : {
        method: 'DELETE',
        url: '/teams/:teamId/delete_member/:playerId'
      }
    });

    var getTeam = function(teamId, next) {
      teamResource.get({
        teamId: teamId
      }, function(response) {
        $scope.team = response.team;
        $scope.members = response.players;
        $scope.is_admin = response.is_admin;
        if(next) {
          next();
        }
      }, function(httpResponse) {
        $scope.team = null;
        $scope.members = null;
        $scope.is_admin = false;
      });
    }

    var tournamentResource = $resource("/tournaments/:tournamentId",
    {
      tournamentId: "@id",
      format: "json"
    },{
      'participants' : {
        method: 'GET',
        isArray: true,
        url: "/tournaments/:tournamentId/participants"
      },
      'unassigned' : {
        method: 'GET',
        isArray: true,
        url: "/tournaments/:tournamentId/participants/unassigned",
        params: {
          tournamentId: "@tournamentId"
        }
      }
    });

    var getAvailablePlayers = function() {
      tournamentResource.unassigned({tournamentId: $scope.team.tournament_id}, function(results) {
        $scope.players = results;
      }, function(httpResponse) {
        $scope.players = null;
      });
    }

    if($stateParams.teamId && ($state.is('teams_show') || $state.is('teams_edit'))) {
      if($state.is('teams_edit')) {
        getTeam($stateParams.teamId, getAvailablePlayers);
      } else {
        getTeam($stateParams.teamId);
      }
    } else {
      $scope.team = {};
      $scope.tournament_id = $stateParams.tournament_id;
    }

    $scope.save = function() {
      var onError = function(_httpResponse) {
        //TODO flash.error
      };

      if($scope.team.id) {
        teamResource.save($scope.team, function() {
          $state.go('teams_show',{teamId: $scope.team.id});
        }, onError);
      } else {
        teamResource.create($scope.team, function(newTeam) {
          $state.go('teams_show',{teamId: newTeam.id});
        }, onError);
      }
    };

    $scope["delete"] = function() {
      teamResource.delete({teamId: $scope.team.id});
      $state.go('home');
    }

    $scope.add_member = function(playerId) {
      teamResource.add_member({teamId: $scope.team.id, playerId: playerId},
      function() {
        getTeam($scope.team.id, getAvailablePlayers);
      });
    };

    $scope.delete_member = function(playerId) {
      teamResource.delete_member({teamId: $scope.team.id, playerId: playerId},
      function() {
        getTeam($scope.team.id, getAvailablePlayers);
      });
    };

    $scope.edit = function() {
      $state.go('teams_edit',{teamId: $scope.team.id});
    };

    $scope.cancel = function() {
      if($scope.team.id) {
        $state.go('teams_show',{teamId: $scope.team.id});
      } else if ($scope.tournament.id) {
        $state.go('tournaments_edit', {tournament_id: $scope.tournament.id});
      } else {
        $state.go('home');
      }
    };
  }]);
<|endoftext|>


<commit_before>// ─────────────────────────────────────────────────────────────────────────────
// import
// ─────────────────────────────────────────────────────────────────────────────

import styled from 'styled-components';

// ─────────────────────────────────────────────────────────────────────────────
// component
// ─────────────────────────────────────────────────────────────────────────────

export default styled.p`
  font-family: ${({ fontFamily }) => fontFamily};
  font-size: ${({ fontSize }) => fontSize};
  font-weight: ${({ fontWeight }) => fontWeight};
  line-height: ${({ lineHeight }) => lineHeight};
  text-align: ${({ textAlign }) => textAlign};
  text-transform: ${({ textTransform }) => textTransform};
  letter-spacing: ${({ letterSpacing }) => letterSpacing};
  color: ${({ color }) => color};
  opacity: ${({ opacity }) => opacity};
  margin: ${({ margin }) => margin};
  padding: ${({ padding }) => padding};
  grid-column: ${({ gridColumn }) => gridColumn};
`;
<commit_msg>add content editing prop in dev<commit_after>// ─────────────────────────────────────────────────────────────────────────────
// import
// ─────────────────────────────────────────────────────────────────────────────

import styled from 'styled-components';

// ─────────────────────────────────────────────────────────────────────────────
// component
// ─────────────────────────────────────────────────────────────────────────────

export default styled.p.attrs({
  contentEditable: process.env.NODE_ENV === 'development',
})`
  font-family: ${({ fontFamily }) => fontFamily};
  font-size: ${({ fontSize }) => fontSize};
  font-weight: ${({ fontWeight }) => fontWeight};
  line-height: ${({ lineHeight }) => lineHeight};
  text-align: ${({ textAlign }) => textAlign};
  text-transform: ${({ textTransform }) => textTransform};
  letter-spacing: ${({ letterSpacing }) => letterSpacing};
  color: ${({ color }) => color};
  opacity: ${({ opacity }) => opacity};
  margin: ${({ margin }) => margin};
  padding: ${({ padding }) => padding};
  grid-column: ${({ gridColumn }) => gridColumn};
`;
<|endoftext|>