<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.
*/

/*
* You can test webapp commands get loaded by xplat by following steps:
* a. Copy the folder to '<repository root>\lib\commands\arm'
* b. Under <repository root>, run 'node bin/azure config mode arm'
* c. Run 'node bin/azure', you should see 'appserviceplan' listed as a command set
* d. Run 'node bin/azure', you should see 'create', "delete", etc
      showing up in the help text
*/

'use strict';

var util = require('util');

var profile = require('../../../util/profile');
var utils = require('../../../util/utils');
var appserviceplanUtils = require('../webapp/webappUtils');

var $ = utils.getLocaleString;

exports.init = function (cli) {
  var log = cli.output;

  var tierInfo = [ '\n\teg:\n\t\t Free, Shared',
                   '\n\t\t B1 (Basic Small),    B2 (Basic Medium),    B3 (Basic Large)',
                   '\n\t\t S1 (Standard Small), S2 (Standard Medium), S3 (Standard Large)',
                   '\n\t\t P1 (Premium Small),  P2 (Premium Medium),  P3 (Premium Large)\n'
                 ];

  var appserviceplan = cli.category('appserviceplan')
    .description($('Commands to manage your Azure appserviceplans'));

  appserviceplan.command('create [resource-group] [name] [location] [sku]')
    .description($('Create an App Service Plan'))
    .usage('[options] <resource-group> <name> <location> <sku>')
    .option('-g --resource-group <resource-group>', $('Name of the resource group'))
    .option('-n --name <name>', $('the name of the App Service Plan to create'))
    .option('-l --location <location>', $('the geographic region to create the App Service Plan, \n\t\t\t\t\t   locations found at https://azure.microsoft.com/en-us/status/'))
    .option('-k --sku <sku>', $('sku of the App Service Plan' + tierInfo.toString()))
    .option('-i --instances <numberofinstances>', $('the number of instances in the App Service Plan, Default is 1'))
    .option('-s --subscription <id>', $('the subscription identifier'))
    .option('-t --tier <tier>', $('to be deprecated, please use --sku option'))
    .execute(function(resourceGroup, name, location, sku, options, _) {
      var subscription = profile.current.getSubscription(options.subscription);
      var client = appserviceplanUtils.createWebappManagementClient(subscription);
      var numOfInstances;
      if (!resourceGroup) {
        cli.missingArgument('resource-group');
      }
      if (!name) {
        return cli.missingArgument('name');
      }
      if (!location) {
        return cli.missingArgument('location');
      }
      if (!sku) {
        return cli.missingArgument('sku');
      }
      if('tier' in options) {
          skuInfo = translateTier(options.tier);
          envelope.sku = skuInfo;
      }
      if('instances' in options) {
        numOfInstances = Number(options.instances);
      }
      else {
        numOfInstances = 1; //Default is 1
      }
      var progress = cli.interaction.progress(util.format($('Creating appserviceplan %s'), name));
      var skuInfo = translateTier(sku);
      skuInfo.capacity = numOfInstances;
      var envelope = {
        location: location,
        name: name,
	      sku: skuInfo
      };
      var result;
      try {
        result = client.serverFarms.createOrUpdateServerFarm(resourceGroup, name, envelope, options , _);
      } finally {
        progress.end();
      }
      log.info('App Service Plan ' + name + ' has been created ');
    });

  appserviceplan.command('delete [resource-group] [name]')
    .description($('Delete an App Service Plan'))
    .usage('[options] <resource-group> <name>')
    .option('-g --resource-group <resource-group>', $('Name of the resource group'))
    .option('-n --name <name>', $('the name of the App Service Plan to delete'))
    .option('-q, --quiet', $('quiet mode, do not ask for delete confirmation'))
    .option('-s --subscription <subscription>', $('the subscription identifier'))
    .execute(function (resourceGroup, name, options, _) {
      var subscription = profile.current.getSubscription(options.subscription);
      var client = appserviceplanUtils.createWebappManagementClient(subscription);
      if (!resourceGroup) {
        cli.missingArgument('resource-group');
      }
      if (!name) {
        return cli.missingArgument('name');
      }
      if (!options.quiet) {
        if (!cli.interaction.confirm(util.format('Delete App Service Plan %s? [y/n] ', name), _)) {
          return;
        }
      }
      var progress = cli.interaction.progress(util.format($('Deleting App Service Plan %s'), name));
      var result;
      try {
        result = client.serverFarms.deleteServerFarm(resourceGroup, name, _);
      } finally {
        progress.end();
      }
      log.info(util.format($('appserviceplan %s has been deleted'), name));
    });

  appserviceplan.command('list [resource-group]')
    .description($('List your App Service Plans'))
    .usage('[options] <resource-group>')
    .option('-g --resource-group <resource-group>', $('the name of the resource group'))
    .option('-s --subscription <subscription>', $('the subscription identifier'))
    .execute(function (resourceGroup, options, _) {
      if (!resourceGroup) {
        cli.missingArgument('resource-group');
      }
      var subscription = profile.current.getSubscription(options.subscription);
      var client = appserviceplanUtils.createWebappManagementClient(subscription);
      var progress = cli.interaction.progress($('Listing App Service Plans'));
      var result;
      try {
        result = client.serverFarms.getServerFarms(resourceGroup, _);
      } finally {
        progress.end();
      }
      cli.interaction.formatOutput(result.value, function (data) {
        if (data.length > 0) {
          log.table(data, function (row, item) {
            row.cell($('Name '), item.name);
            row.cell($('Location '), item.location);
            row.cell($('SKU '), item.sku.name);
            row.cell($('Number Of Workers '), item.sku.capacity);
            row.cell($('Worker Size '), translateWorkerSize(item.sku.name));
          });
        } else {
          log.info(util.format($('No app service plans found.')));
        }
      });
    });

  appserviceplan.command('show [resource-group] [name]')
    .description($('Get an App Service Plan'))
    .usage('[options] <resource-group> <name>')
    .option('-g --resource-group <resource-group>', $('the name of the resource group'))
    .option('-n --name <name>', $('the name of the App Service Plan to show'))
    .option('-s --subscription <subscription>', $('the subscription identifier'))
    .execute(function (resourceGroup, name, options, _) {
      if (!resourceGroup) {
        cli.missingArgument('resource-group');
      }
      if (!name) {
        return cli.missingArgument('name');
      }
      var subscription = profile.current.getSubscription(options.subscription);
      var client = appserviceplanUtils.createWebappManagementClient(subscription);
      var progress = cli.interaction.progress($('Getting App Service Plan'));
      var result;
      try {
        result = client.serverFarms.getServerFarm(resourceGroup, name, _);
      } finally {
        progress.end();
      }
      cli.interaction.formatOutput(result, function (data) {
        if (!data) {
          log.info($('No App Service Plan information available'));
        } else {
          log.data($('App Service Plan Name  :'), data.name);
          log.data($('Location           :'), data.location);
          log.data($('SKU                :'), data.sku.name);
          log.data($('Number of workers  :'), data.sku.capacity);
          log.data($('Worker Size        :'), translateWorkerSize(data.sku.name));
          log.data('');
        }
      });
    });

  appserviceplan.command('set [resource-group] [name]')
    .description($('Update App Service Plan Configurations'))
    .usage('[options] <resource-group> <name>')
    .option('-g --resource-group <resource-group>', $('Name of the resource group'))
    .option('-n --name <name>', $('the name of the App Service Plan to update'))
    .option('-l --location <location>', $('the geographic region to create the App Service Plan, \n\t\t\t\t\t   locations found at https://azure.microsoft.com/en-us/status/'))
    .option('-k --sku <sku>', $('sku of the App Service Plan' + tierInfo.toString()))
    .option('-i --instances <numberofinstances>', $('the number of instances in the App Service Plan, Default is 1'))
    .option('-s --subscription <id>', $('the subscription identifier'))
    .option('-t --tier <tier>', $('to be deprecated, please use --sku option'))
    .execute(function(resourceGroup, name, options, _) {
      var subscription = profile.current.getSubscription(options.subscription);
      var client = appserviceplanUtils.createWebappManagementClient(subscription);
      var progress = cli.interaction.progress($('Updating App Service Plan'));
      if (!resourceGroup) {
        cli.missingArgument('resource-group');
      }
      if (!name) {
        return cli.missingArgument('name');
      }
      var result;
      try {
        result = client.serverFarms.getServerFarm(resourceGroup, name, _);
      } finally {
        progress.end();
      }
      if(result === undefined) {
        log.info('App Service Plan ' + name + ' does not exist! ');
      }
      else {
        var envelope = {
          name: name
        };
        var skuInfo = result.sku;
        if('tier' in options) {
          skuInfo = translateTier(options.tier);
          envelope.sku = skuInfo;
        }
        else if('sku' in options) {
          skuInfo = translateTier(options.sku);
          envelope.sku = skuInfo;
        }
        if('instances' in options) {
          var numOfInstances;
          numOfInstances = Number(options.instances);
          skuInfo.capacity = numOfInstances;
        }
        if('location' in options) {
          envelope.location = options.location;
        }
        else {
          envelope.location = result.location;
        }
        var progress = cli.interaction.progress(util.format($('Updating appserviceplan %s'), name));
        try {
          result = client.serverFarms.createOrUpdateServerFarm(resourceGroup, name, envelope, options , _);
        } finally {
          progress.end();
        }
        log.info('App Service Plan ' + name + ' has been updated ');
      }
    });

  function translateWorkerSize(skuName) {
    var workerSize = skuName.charAt(1);
    var workerSizeNum = Number(workerSize) - 1;
    var workerSizeLiteral = -1;
	  switch (workerSizeNum) {
	  	case 0:
        workerSizeLiteral = 'Small';
        break;
	    case 1:
	  	  workerSizeLiteral = 'Medium';
	      break;
	  	case 2:
	  	  workerSizeLiteral = 'Large';
	      break;
	  	default:
	      workerSizeLiteral = 'Undefined';
	  }
	  return workerSizeLiteral;
	}

  function translateTier(tier) {
    var tierUpper = tier.toUpperCase();
    var workerSize = -1;
    var sku = 'invalid';
    switch(tierUpper) {
      case 'FREE':
        tier = 'F1';
        workerSize = 0;
        break;
      case 'SHARED':
        tier = 'D1';
        workerSize = 0;
        break;
      case 'B1':
      case 'B2':
      case 'B3':
        sku = 'Basic';
        workerSize = tier.charAt(1) - 1;
        break;
      case 'S1':
      case 'S2':
      case 'S3':
        sku = 'Standard';
        workerSize = tier.charAt(1) - 1;
        break;
      case 'P1':
      case 'P2':
      case 'P3':
        sku = 'Premium';
        workerSize = tier.charAt(1) - 1;
        break;
      default:
        log.info($('Invalid Tier'));
      }
    var skuInfo = {name: tier, tier: sku, family: tier.charAt(0)};
    return skuInfo;
	}
};
<commit_msg>Update appserviceplan._js<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.
*/

/*
* You can test webapp commands get loaded by xplat by following steps:
* a. Copy the folder to '<repository root>\lib\commands\arm'
* b. Under <repository root>, run 'node bin/azure config mode arm'
* c. Run 'node bin/azure', you should see 'appserviceplan' listed as a command set
* d. Run 'node bin/azure', you should see 'create', "delete", etc
      showing up in the help text
*/

'use strict';

var util = require('util');

var profile = require('../../../util/profile');
var utils = require('../../../util/utils');
var appserviceplanUtils = require('../webapp/webappUtils');

var $ = utils.getLocaleString;

exports.init = function (cli) {
  var log = cli.output;

  var tierInfo = [ '\n\teg:\n\t\t Free, Shared',
                   '\n\t\t B1 (Basic Small),    B2 (Basic Medium),    B3 (Basic Large)',
                   '\n\t\t S1 (Standard Small), S2 (Standard Medium), S3 (Standard Large)',
                   '\n\t\t P1 (Premium Small),  P2 (Premium Medium),  P3 (Premium Large)\n'
                 ];

  var appserviceplan = cli.category('appserviceplan')
    .description($('Commands to manage your Azure appserviceplans'));

  appserviceplan.command('create [resource-group] [name] [location] [sku]')
    .description($('Create an App Service Plan'))
    .usage('[options] <resource-group> <name> <location> <sku>')
    .option('-g --resource-group <resource-group>', $('Name of the resource group'))
    .option('-n --name <name>', $('the name of the App Service Plan to create'))
    .option('-l --location <location>', $('the geographic region to create the App Service Plan, \n\t\t\t\t\t   locations found at https://azure.microsoft.com/en-us/status/'))
    .option('-k --sku <sku>', $('sku of the App Service Plan' + tierInfo.toString()))
    .option('-i --instances <numberofinstances>', $('the number of instances in the App Service Plan, Default is 1'))
    .option('-s --subscription <id>', $('the subscription identifier'))
    .option('-t --tier <tier>', $('to be deprecated, please use --sku option'))
    .execute(function(resourceGroup, name, location, sku, options, _) {
      var subscription = profile.current.getSubscription(options.subscription);
      var client = appserviceplanUtils.createWebappManagementClient(subscription);
      var numOfInstances;
      if (!resourceGroup) {
        cli.missingArgument('resource-group');
      }
      if (!name) {
        return cli.missingArgument('name');
      }
      if (!location) {
        return cli.missingArgument('location');
      }
      if (!sku) {
        return cli.missingArgument('sku');
      }
      if('tier' in options) {
          skuInfo = translateTier(options.tier);
          envelope.sku = skuInfo;
      }
      if('instances' in options) {
        numOfInstances = Number(options.instances);
      }
      else {
        numOfInstances = 1; //Default is 1
      }
      var progress = cli.interaction.progress(util.format($('Creating appserviceplan %s'), name));
      var skuInfo = translateTier(sku);
      skuInfo.capacity = numOfInstances;
      var envelope = {
        location: location,
        name: name,
	      sku: skuInfo
      };
      var result;
      try {
        result = client.serverFarms.createOrUpdateServerFarm(resourceGroup, name, envelope, options , _);
      } finally {
        progress.end();
      }
      log.info('App Service Plan ' + name + ' has been created ');
    });

  appserviceplan.command('delete [resource-group] [name]')
    .description($('Delete an App Service Plan'))
    .usage('[options] <resource-group> <name>')
    .option('-g --resource-group <resource-group>', $('Name of the resource group'))
    .option('-n --name <name>', $('the name of the App Service Plan to delete'))
    .option('-q, --quiet', $('quiet mode, do not ask for delete confirmation'))
    .option('-s --subscription <subscription>', $('the subscription identifier'))
    .execute(function (resourceGroup, name, options, _) {
      var subscription = profile.current.getSubscription(options.subscription);
      var client = appserviceplanUtils.createWebappManagementClient(subscription);
      if (!resourceGroup) {
        cli.missingArgument('resource-group');
      }
      if (!name) {
        return cli.missingArgument('name');
      }
      if (!options.quiet) {
        if (!cli.interaction.confirm(util.format('Delete App Service Plan %s? [y/n] ', name), _)) {
          return;
        }
      }
      var progress = cli.interaction.progress(util.format($('Deleting App Service Plan %s'), name));
      var result;
      try {
        result = client.serverFarms.deleteServerFarm(resourceGroup, name, _);
      } finally {
        progress.end();
      }
      log.info(util.format($('appserviceplan %s has been deleted'), name));
    });

  appserviceplan.command('list [resource-group]')
    .description($('List your App Service Plans'))
    .usage('[options] <resource-group>')
    .option('-g --resource-group <resource-group>', $('the name of the resource group'))
    .option('-s --subscription <subscription>', $('the subscription identifier'))
    .execute(function (resourceGroup, options, _) {
      if (!resourceGroup) {
        cli.missingArgument('resource-group');
      }
      var subscription = profile.current.getSubscription(options.subscription);
      var client = appserviceplanUtils.createWebappManagementClient(subscription);
      var progress = cli.interaction.progress($('Listing App Service Plans'));
      var result;
      try {
        result = client.serverFarms.getServerFarms(resourceGroup, _);
      } finally {
        progress.end();
      }
      cli.interaction.formatOutput(result.value, function (data) {
        if (data.length > 0) {
          log.table(data, function (row, item) {
            row.cell($('Name '), item.name);
            row.cell($('Location '), item.location);
            row.cell($('SKU '), item.sku.name);
            row.cell($('Number Of Workers '), item.sku.capacity);
            row.cell($('Worker Size '), translateWorkerSize(item.sku.name));
          });
        } else {
          log.info(util.format($('No app service plans found.')));
        }
      });
    });

  appserviceplan.command('show [resource-group] [name]')
    .description($('Get an App Service Plan'))
    .usage('[options] <resource-group> <name>')
    .option('-g --resource-group <resource-group>', $('the name of the resource group'))
    .option('-n --name <name>', $('the name of the App Service Plan to show'))
    .option('-s --subscription <subscription>', $('the subscription identifier'))
    .execute(function (resourceGroup, name, options, _) {
      if (!resourceGroup) {
        cli.missingArgument('resource-group');
      }
      if (!name) {
        return cli.missingArgument('name');
      }
      var subscription = profile.current.getSubscription(options.subscription);
      var client = appserviceplanUtils.createWebappManagementClient(subscription);
      var progress = cli.interaction.progress($('Getting App Service Plan'));
      var result;
      try {
        result = client.serverFarms.getServerFarm(resourceGroup, name, _);
      } finally {
        progress.end();
      }
      cli.interaction.formatOutput(result, function (data) {
        if (!data) {
          log.info($('No App Service Plan information available'));
        } else {
          log.data($('App Service Plan Name  :'), data.name);
          log.data($('Location           :'), data.location);
          log.data($('SKU                :'), data.sku.name);
          log.data($('Number of workers  :'), data.sku.capacity);
          log.data($('Worker Size        :'), translateWorkerSize(data.sku.name));
          log.data('');
        }
      });
    });

  appserviceplan.command('set [resource-group] [name]')
    .description($('Update App Service Plan Configurations'))
    .usage('[options] <resource-group> <name>')
    .option('-g --resource-group <resource-group>', $('Name of the resource group'))
    .option('-n --name <name>', $('the name of the App Service Plan to update'))
    .option('-l --location <location>', $('the geographic region to create the App Service Plan, \n\t\t\t\t\t   locations found at https://azure.microsoft.com/en-us/status/'))
    .option('-k --sku <sku>', $('sku of the App Service Plan' + tierInfo.toString()))
    .option('-i --instances <numberofinstances>', $('the number of instances in the App Service Plan, Default is 1'))
    .option('-s --subscription <id>', $('the subscription identifier'))
    .option('-t --tier <tier>', $('to be deprecated, please use --sku option'))
    .execute(function(resourceGroup, name, options, _) {
      var subscription = profile.current.getSubscription(options.subscription);
      var client = appserviceplanUtils.createWebappManagementClient(subscription);
      var progress = cli.interaction.progress($('Updating App Service Plan'));
      if (!resourceGroup) {
        cli.missingArgument('resource-group');
      }
      if (!name) {
        return cli.missingArgument('name');
      }
      var result;
      try {
        result = client.serverFarms.getServerFarm(resourceGroup, name, _);
      } finally {
        progress.end();
      }
      if(result === undefined) {
        log.info('App Service Plan ' + name + ' does not exist! ');
      }
      else {
        var envelope = {
          name: name
        };
        var skuInfo = result.sku;
        if('tier' in options) {
          skuInfo = translateTier(options.tier);
          envelope.sku = skuInfo;
        }
        else if('sku' in options) {
          skuInfo = translateTier(options.sku);
          envelope.sku = skuInfo;
        }
        if('instances' in options) {
          var numOfInstances;
          numOfInstances = Number(options.instances);
          skuInfo.capacity = numOfInstances;
        }
        if('location' in options) {
          envelope.location = options.location;
        }
        else {
          envelope.location = result.location;
        }
        try {
          result = client.serverFarms.createOrUpdateServerFarm(resourceGroup, name, envelope, options , _);
        } finally {
          progress.end();
        }
        log.info('App Service Plan ' + name + ' has been updated ');
      }
    });

  function translateWorkerSize(skuName) {
    var workerSize = skuName.charAt(1);
    var workerSizeNum = Number(workerSize) - 1;
    var workerSizeLiteral = -1;
	  switch (workerSizeNum) {
	  	case 0:
        workerSizeLiteral = 'Small';
        break;
	    case 1:
	  	  workerSizeLiteral = 'Medium';
	      break;
	  	case 2:
	  	  workerSizeLiteral = 'Large';
	      break;
	  	default:
	      workerSizeLiteral = 'Undefined';
	  }
	  return workerSizeLiteral;
	}

  function translateTier(tier) {
    var tierUpper = tier.toUpperCase();
    var workerSize = -1;
    var sku = 'invalid';
    switch(tierUpper) {
      case 'FREE':
        tier = 'F1';
        workerSize = 0;
        break;
      case 'SHARED':
        tier = 'D1';
        workerSize = 0;
        break;
      case 'B1':
      case 'B2':
      case 'B3':
        sku = 'Basic';
        workerSize = tier.charAt(1) - 1;
        break;
      case 'S1':
      case 'S2':
      case 'S3':
        sku = 'Standard';
        workerSize = tier.charAt(1) - 1;
        break;
      case 'P1':
      case 'P2':
      case 'P3':
        sku = 'Premium';
        workerSize = tier.charAt(1) - 1;
        break;
      default:
        log.info($('Invalid Tier'));
      }
    var skuInfo = {name: tier, tier: sku, family: tier.charAt(0)};
    return skuInfo;
	}
};
<|endoftext|>


<commit_before>const Util = require('../../util');
const PolarLabels = require('./polar-labels');
const PathUtil = require('../util/path');
const Global = require('../../global');
const MARGIN = 5;

function getEndPoint(center, angle, r) {
  return {
    x: center.x + r * Math.cos(angle),
    y: center.y + r * Math.sin(angle)
  };
}

function antiCollision(labels, lineHeight, plotRange, center, isRight) {
  // adjust y position of labels to avoid overlapping
  let overlapping = true;
  const start = plotRange.start;
  const end = plotRange.end;
  const startY = Math.min(start.y, end.y);
  let totalHeight = Math.abs(start.y - end.y);
  let i;

  let maxY = 0;
  let minY = Number.MIN_VALUE;
  const boxes = labels.map(function(label) {
    if (label.y > maxY) {
      maxY = label.y;
    }
    if (label.y < minY) {
      minY = label.y;
    }
    return {
      size: lineHeight,
      targets: [ label.y - startY ]
    };
  });
  minY -= startY;
  if ((maxY - startY) > totalHeight) {
    totalHeight = maxY - startY;
  }

  while (overlapping) {
    /* eslint no-loop-func: 0 */
    boxes.forEach(function(box) {
      const target = (Math.min.apply(minY, box.targets) + Math.max.apply(minY, box.targets)) / 2;
      box.pos = Math.min(Math.max(minY, target - box.size / 2), totalHeight - box.size);
      // box.pos = Math.max(0, target - box.size / 2);
    });

    // detect overlapping and join boxes
    overlapping = false;
    i = boxes.length;
    while (i--) {
      if (i > 0) {
        const previousBox = boxes[i - 1];
        const box = boxes[i];
        if (previousBox.pos + previousBox.size > box.pos) { // overlapping
          previousBox.size += box.size;
          previousBox.targets = previousBox.targets.concat(box.targets);

          // overflow, shift up
          if (previousBox.pos + previousBox.size > totalHeight) {
            previousBox.pos = totalHeight - previousBox.size;
          }
          boxes.splice(i, 1); // removing box
          overlapping = true;
        }
      }
    }
  }

  i = 0;
  // step 4: normalize y and adjust x
  boxes.forEach(function(b) {
    let posInCompositeBox = startY + lineHeight / 2; // middle of the label
    b.targets.forEach(function() {
      labels[i].y = b.pos + posInCompositeBox;
      posInCompositeBox += lineHeight;
      i++;
    });
  });

  // (x - cx)^2 + (y - cy)^2 = totalR^2
  labels.forEach(function(label) {
    const rPow2 = label.r * label.r;
    const dyPow2 = Math.pow(Math.abs(label.y - center.y), 2);
    if (rPow2 < dyPow2) {
      label.x = center.x;
    } else {
      const dx = Math.sqrt(rPow2 - dyPow2);
      if (!isRight) { // left
        label.x = center.x - dx;
      } else { // right
        label.x = center.x + dx;
      }
    }
  });
}

class PieLabels extends PolarLabels {
  getDefaultCfg() {
    return {
      label: Global.thetaLabels
    };
  }

  getDefaultOffset() {
    const labelCfg = this.get('label');
    let offset = labelCfg.offset || 0;
    if (!Util.isArray(offset)) {
      offset = [ 0, offset ];
    }
    return offset;
  }

  /**
   * @protected
   * to avoid overlapping
   * @param {Array} items labels to be placed
   * @return {Array} items
   */
  adjustItems(items) {
    const self = this;
    const offset = self.getDefaultOffset();
    if (offset[1] > 0) {
      items = self._distribute(items, offset);
    }

    return items;
  }

  /**
   * @private
   * distribute labels
   * @param {Array} labels labels
   * @param {Number} offset offset
   * @return {Array} labels
   */
  _distribute(labels, offset) {
    const self = this;
    const coord = self.get('coord');
    const radius = coord.getRadius();
    const lineHeight = self.get('label').labelHeight;
    const center = coord.getCenter();
    const totalR = radius + offset[1];
    const totalHeight = totalR * 2 + lineHeight * 2;
    let plotRange = {
      start: coord.start,
      end: coord.end
    };
    const geom = self.get('geom');
    if (geom) {
      const view = geom.get('view');
      plotRange = view.getViewRegion();
    }

    // step 1: separate labels
    const halves = [
      [], // left
      [] // right
    ];
    labels.forEach(function(label) {
      if (label.textAlign === 'right') { // left
        halves[0].push(label);
      } else { // right or center will be put on the right side
        halves[1].push(label);
      }
    });

    halves.forEach(function(half, index) {
      // step 2: reduce labels
      const maxLabelsCountForOneSide = parseInt(totalHeight / lineHeight, 10);
      if (half.length > maxLabelsCountForOneSide) {
        half.sort(function(a, b) { // sort by percentage DESC
          return b['..percent'] - a['..percent'];
        });
        half.splice(maxLabelsCountForOneSide, half.length - maxLabelsCountForOneSide);
      }

      // step 3: distribute position (x and y)
      half.sort(function(a, b) { // sort by y ASC
        return a.y - b.y;
      });
      antiCollision(half, lineHeight, plotRange, center, index);
    });

    return halves[0].concat(halves[1]);
  }

  // 连接线
  lineToLabel(label, labelLine) {
    const self = this;
    const coord = self.get('coord');
    const r = coord.getRadius();
    const distance = self.getDefaultOffset()[1];
    const angle = label.orignAngle || label.angle;
    const center = coord.getCenter();
    const start = getEndPoint(center, angle, r + MARGIN / 2);
    const inner = getEndPoint(center, angle, r + distance / 2);
    let lineGroup = self.get('lineGroup');
    // var lineShape;
    if (!lineGroup) {
      lineGroup = self.addGroup({
        elCls: 'x-line-group'
      });
      self.set('lineGroup', lineGroup);
    }
    // lineShape =
    const lineShape = lineGroup.addShape('path', {
      attrs: Util.mix({
        path: [ 'M' + start.x, start.y + ' Q' + inner.x, inner.y + ' ' + label.x, label.y ].join(','),
        fill: null,
        stroke: label.color
      }, labelLine)
    });
    // label 对应线的动画关闭
    lineShape.name = 'labelLine';
    lineShape._id = label._id && label._id.replace('glabel', 'glabelline'); // generate labelLine id according to label id
    lineShape.set('coord', coord);
  }

  /**
   * @protected
   * get rotation for label
   * @param {Number} angle angle
   * @param {Number} offset offset
   * @return {Number} rotate
   */
  getLabelRotate(angle, offset) {
    let rotate;
    if (offset[1] < 0) {
      rotate = angle * 180 / Math.PI;
      if (rotate > 90) {
        rotate = rotate - 180;
      }
      if (rotate < -90) {
        rotate = rotate + 180;
      }
    }
    return rotate / 180 * Math.PI;
  }

  /**
   * @protected
   * get text align for label
   * @param {Object} point point
   * @return {String} align
   */
  getLabelAlign(point) {
    const self = this;
    const coord = self.get('coord');
    const center = coord.getCenter();

    let align;
    if (point.angle <= Math.PI / 2 && point.x >= center.x) {
      align = 'left';
    } else {
      align = 'right';
    }

    const offset = self.getDefaultOffset()[1];
    if (offset <= 0) {
      if (align === 'right') {
        align = 'left';
      } else {
        align = 'right';
      }
    }
    return align;
  }

  getArcPoint(point) {
    return point;
  }

  getPointAngle(point) {
    const self = this;
    const coord = self.get('coord');
    const startPoint = {
      x: Util.isArray(point.x) ? point.x[0] : point.x,
      y: point.y[0]
    };
    self.transLabelPoint(startPoint); // 转换到画布坐标，如果坐标系发生改变
    const endPoint = {
      x: Util.isArray(point.x) ? point.x[1] : point.x,
      y: point.y[1]
    };
    self.transLabelPoint(endPoint); // 转换到画布坐标，如果坐标系发生改变
    let angle;
    const startAngle = PathUtil.getPointAngle(coord, startPoint);
    if (point.points && point.points[0].y === point.points[1].y) {
      angle = startAngle;
    } else {
      let endAngle = PathUtil.getPointAngle(coord, endPoint);
      if (startAngle >= endAngle) { // 100% pie slice
        endAngle = endAngle + Math.PI * 2;
      }
      angle = startAngle + (endAngle - startAngle) / 2;
    }
    return angle;
  }

  getCirclePoint(angle, offset) {
    const self = this;
    const coord = self.get('coord');
    const center = coord.getCenter();
    const r = coord.getRadius() + offset[1];
    const point = getEndPoint(center, angle, r);
    const appendAngle = Math.asin(offset[0] / (2 * r));
    angle = angle + appendAngle * 2;
    point.angle = angle;
    point.r = r;
    return point;
  }
}

module.exports = PieLabels;
<commit_msg>refactor(pie-label): modify pieLabel accordingly<commit_after>const Util = require('../../util');
const PolarLabels = require('./polar-labels');
const PathUtil = require('../util/path');
const Global = require('../../global');
const MARGIN = 5;

function getEndPoint(center, angle, r) {
  return {
    x: center.x + r * Math.cos(angle),
    y: center.y + r * Math.sin(angle)
  };
}

function antiCollision(labels, lineHeight, plotRange, center, isRight) {
  // adjust y position of labels to avoid overlapping
  let overlapping = true;
  const start = plotRange.start;
  const end = plotRange.end;
  const startY = Math.min(start.y, end.y);
  let totalHeight = Math.abs(start.y - end.y);
  let i;

  let maxY = 0;
  let minY = Number.MIN_VALUE;
  const boxes = labels.map(function(label) {
    if (label.y > maxY) {
      maxY = label.y;
    }
    if (label.y < minY) {
      minY = label.y;
    }
    return {
      size: lineHeight,
      targets: [ label.y - startY ]
    };
  });
  minY -= startY;
  if ((maxY - startY) > totalHeight) {
    totalHeight = maxY - startY;
  }

  while (overlapping) {
    /* eslint no-loop-func: 0 */
    boxes.forEach(function(box) {
      const target = (Math.min.apply(minY, box.targets) + Math.max.apply(minY, box.targets)) / 2;
      box.pos = Math.min(Math.max(minY, target - box.size / 2), totalHeight - box.size);
      // box.pos = Math.max(0, target - box.size / 2);
    });

    // detect overlapping and join boxes
    overlapping = false;
    i = boxes.length;
    while (i--) {
      if (i > 0) {
        const previousBox = boxes[i - 1];
        const box = boxes[i];
        if (previousBox.pos + previousBox.size > box.pos) { // overlapping
          previousBox.size += box.size;
          previousBox.targets = previousBox.targets.concat(box.targets);

          // overflow, shift up
          if (previousBox.pos + previousBox.size > totalHeight) {
            previousBox.pos = totalHeight - previousBox.size;
          }
          boxes.splice(i, 1); // removing box
          overlapping = true;
        }
      }
    }
  }

  i = 0;
  // step 4: normalize y and adjust x
  boxes.forEach(function(b) {
    let posInCompositeBox = startY + lineHeight / 2; // middle of the label
    b.targets.forEach(function() {
      labels[i].y = b.pos + posInCompositeBox;
      posInCompositeBox += lineHeight;
      i++;
    });
  });

  // (x - cx)^2 + (y - cy)^2 = totalR^2
  labels.forEach(function(label) {
    const rPow2 = label.r * label.r;
    const dyPow2 = Math.pow(Math.abs(label.y - center.y), 2);
    if (rPow2 < dyPow2) {
      label.x = center.x;
    } else {
      const dx = Math.sqrt(rPow2 - dyPow2);
      if (!isRight) { // left
        label.x = center.x - dx;
      } else { // right
        label.x = center.x + dx;
      }
    }
  });
}

class PieLabels extends PolarLabels {
  getDefaultCfg() {
    return {
      label: Global.thetaLabels
    };
  }

  getDefaultOffset(point) {
    let offset = point.offset || [ 0, 0 ];
    if (!Util.isArray(offset)) {
      offset = [ 0, offset ];
    }
    return offset;
  }

  /**
   * @protected
   * to avoid overlapping
   * @param {Array} items labels to be placed
   * @return {Array} items
   */
  adjustItems(items) {
    const self = this;
    const offset = items[0].offset[1];
    if (offset > 0) {
      items = self._distribute(items, offset);
    }
    return items;
  }

  /**
   * @private
   * distribute labels
   * @param {Array} labels labels
   * @param {Number} offset offset
   * @return {Array} labels
   */
  _distribute(labels, offset) {
    const self = this;
    const coord = self.get('coord');
    const radius = coord.getRadius();
    const lineHeight = self.get('label').labelHeight;
    const center = coord.getCenter();
    const totalR = radius + offset[1];
    const totalHeight = totalR * 2 + lineHeight * 2;
    let plotRange = {
      start: coord.start,
      end: coord.end
    };
    const geom = self.get('geom');
    if (geom) {
      const view = geom.get('view');
      plotRange = view.getViewRegion();
    }

    // step 1: separate labels
    const halves = [
      [], // left
      [] // right
    ];
    labels.forEach(function(label) {
      if (label.textAlign === 'right') { // left
        halves[0].push(label);
      } else { // right or center will be put on the right side
        halves[1].push(label);
      }
    });

    halves.forEach(function(half, index) {
      // step 2: reduce labels
      const maxLabelsCountForOneSide = parseInt(totalHeight / lineHeight, 10);
      if (half.length > maxLabelsCountForOneSide) {
        half.sort(function(a, b) { // sort by percentage DESC
          return b['..percent'] - a['..percent'];
        });
        half.splice(maxLabelsCountForOneSide, half.length - maxLabelsCountForOneSide);
      }

      // step 3: distribute position (x and y)
      half.sort(function(a, b) { // sort by y ASC
        return a.y - b.y;
      });
      antiCollision(half, lineHeight, plotRange, center, index);
    });

    return halves[0].concat(halves[1]);
  }

  // 连接线
  lineToLabel(label) {
    const self = this;
    const coord = self.get('coord');
    const r = coord.getRadius() + label.offset[0];
    const distance = label.offset[1];
    const angle = label.orignAngle || label.angle;
    const center = coord.getCenter();
    const start = getEndPoint(center, angle, r + MARGIN / 2);
    const inner = getEndPoint(center, angle, r + distance / 2);
    let lineGroup = self.get('lineGroup');
    // var lineShape;
    if (!lineGroup) {
      lineGroup = self.addGroup({
        elCls: 'x-line-group'
      });
      self.set('lineGroup', lineGroup);
    }
    // lineShape =
    const lineShape = lineGroup.addShape('path', {
      attrs: Util.mix({
        path: [ 'M' + start.x, start.y + ' Q' + inner.x, inner.y + ' ' + label.x, label.y ].join(','),
        fill: null,
        stroke: label.color
      }, label.labelLine)
    });
    // label 对应线的动画关闭
    lineShape.name = 'labelLine';
    lineShape._id = label._id && label._id.replace('glabel', 'glabelline'); // generate labelLine id according to label id
    lineShape.set('coord', coord);
  }

  /**
   * @protected
   * get rotation for label
   * @param {Number} angle angle
   * @param {Number} offset offset
   * @return {Number} rotate
   */
  getLabelRotate(angle, offset) {
    let rotate;
    if (offset[1] < 0) {
      rotate = angle * 180 / Math.PI;
      if (rotate > 90) {
        rotate = rotate - 180;
      }
      if (rotate < -90) {
        rotate = rotate + 180;
      }
    }
    return rotate / 180 * Math.PI;
  }

  /**
   * @protected
   * get text align for label
   * @param {Object} point point
   * @return {String} align
   */
  getLabelAlign(point) {
    const self = this;
    const coord = self.get('coord');
    const center = coord.getCenter();

    let align;
    if (point.angle <= Math.PI / 2 && point.x >= center.x) {
      align = 'left';
    } else {
      align = 'right';
    }

    const offset = point.offset[1];
    if (offset <= 0) {
      if (align === 'right') {
        align = 'left';
      } else {
        align = 'right';
      }
    }
    return align;
  }

  getArcPoint(point) {
    return point;
  }

  getPointAngle(point) {
    const self = this;
    const coord = self.get('coord');
    const startPoint = {
      x: Util.isArray(point.x) ? point.x[0] : point.x,
      y: point.y[0]
    };
    self.transLabelPoint(startPoint); // 转换到画布坐标，如果坐标系发生改变
    const endPoint = {
      x: Util.isArray(point.x) ? point.x[1] : point.x,
      y: point.y[1]
    };
    self.transLabelPoint(endPoint); // 转换到画布坐标，如果坐标系发生改变
    let angle;
    const startAngle = PathUtil.getPointAngle(coord, startPoint);
    if (point.points && point.points[0].y === point.points[1].y) {
      angle = startAngle;
    } else {
      let endAngle = PathUtil.getPointAngle(coord, endPoint);
      if (startAngle >= endAngle) { // 100% pie slice
        endAngle = endAngle + Math.PI * 2;
      }
      angle = startAngle + (endAngle - startAngle) / 2;
    }
    return angle;
  }

  getCirclePoint(angle, offset) {
    const self = this;
    const coord = self.get('coord');
    const center = coord.getCenter();
    const r = coord.getRadius() + offset[1];
    const point = getEndPoint(center, angle, r);
    const appendAngle = Math.asin(offset[0] / (2 * r));
    angle = angle + appendAngle * 2;
    point.angle = angle;
    point.r = r;
    return point;
  }
}

module.exports = PieLabels;
<|endoftext|>


<commit_before>/**
* @license Apache-2.0
*
* Copyright (c) 2018 The Stdlib Authors.
*
* 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.
*/

'use strict';

/*
* When adding modules to the namespace, ensure that they are added in alphabetical order according to module name.
*/

// MODULES //

var setReadOnly = require( '@stdlib/utils/define-read-only-property' );

// MAIN //

/**
* Top-level namespace.
*
* @namespace ns
*/
var ns = {};

/**
* @name assert
* @memberof ns
* @readonly
* @type {Namespace}
* @see {@link module:@stdlib/ndarray/base/assert}
*/
setReadOnly( ns, 'assert', require( '@stdlib/ndarray/base/assert' ) );

/**
* @name bind2vind
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/bind2vind}
*/
setReadOnly( ns, 'bind2vind', require( '@stdlib/ndarray/base/bind2vind' ) );

/**
* @name buffer
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/buffer}
*/
setReadOnly( ns, 'buffer', require( '@stdlib/ndarray/base/buffer' ) );

/**
* @name bufferCtors
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/buffer-ctors}
*/
setReadOnly( ns, 'bufferCtors', require( '@stdlib/ndarray/base/buffer-ctors' ) );

/**
* @name bufferDataType
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/buffer-dtype}
*/
setReadOnly( ns, 'bufferDataType', require( '@stdlib/ndarray/base/buffer-dtype' ) );

/**
* @name bytesPerElement
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/bytes-per-element}
*/
setReadOnly( ns, 'bytesPerElement', require( '@stdlib/ndarray/base/bytes-per-element' ) );

/**
* @name clampIndex
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/clamp-index}
*/
setReadOnly( ns, 'clampIndex', require( '@stdlib/ndarray/base/clamp-index' ) );

/**
* @name ndarray
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/ctor}
*/
setReadOnly( ns, 'ndarray', require( '@stdlib/ndarray/base/ctor' ) );

/**
* @name dtypeChar
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/dtype-char}
*/
setReadOnly( ns, 'dtypeChar', require( '@stdlib/ndarray/base/dtype-char' ) );

/**
* @name ind
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/ind}
*/
setReadOnly( ns, 'ind', require( '@stdlib/ndarray/base/ind' ) );

/**
* @name ind2sub
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/ind2sub}
*/
setReadOnly( ns, 'ind2sub', require( '@stdlib/ndarray/base/ind2sub' ) );

/**
* @name iterationOrder
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/iteration-order}
*/
setReadOnly( ns, 'iterationOrder', require( '@stdlib/ndarray/base/iteration-order' ) );

/**
* @name maxViewBufferIndex
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/max-view-buffer-index}
*/
setReadOnly( ns, 'maxViewBufferIndex', require( '@stdlib/ndarray/base/max-view-buffer-index' ) );

/**
* @name memoizedCtor
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/memoized-ctor}
*/
setReadOnly( ns, 'memoizedCtor', require( '@stdlib/ndarray/base/memoized-ctor' ) );

/**
* @name minViewBufferIndex
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/min-view-buffer-index}
*/
setReadOnly( ns, 'minViewBufferIndex', require( '@stdlib/ndarray/base/min-view-buffer-index' ) );

/**
* @name minmaxViewBufferIndex
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/minmax-view-buffer-index}
*/
setReadOnly( ns, 'minmaxViewBufferIndex', require( '@stdlib/ndarray/base/minmax-view-buffer-index' ) );

/**
* @name numel
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/numel}
*/
setReadOnly( ns, 'numel', require( '@stdlib/ndarray/base/numel' ) );

/**
* @name shape2strides
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/shape2strides}
*/
setReadOnly( ns, 'shape2strides', require( '@stdlib/ndarray/base/shape2strides' ) );

/**
* @name strides2offset
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/strides2offset}
*/
setReadOnly( ns, 'strides2offset', require( '@stdlib/ndarray/base/strides2offset' ) );

/**
* @name strides2order
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/strides2order}
*/
setReadOnly( ns, 'strides2order', require( '@stdlib/ndarray/base/strides2order' ) );

/**
* @name sub2ind
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/sub2ind}
*/
setReadOnly( ns, 'sub2ind', require( '@stdlib/ndarray/base/sub2ind' ) );

/**
* @name ndarray2array
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/to-array}
*/
setReadOnly( ns, 'ndarray2array', require( '@stdlib/ndarray/base/to-array' ) );

/**
* @name vind2bind
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/vind2bind}
*/
setReadOnly( ns, 'vind2bind', require( '@stdlib/ndarray/base/vind2bind' ) );

/**
* @name wrapIndex
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/wrap-index}
*/
setReadOnly( ns, 'wrapIndex', require( '@stdlib/ndarray/base/wrap-index' ) );

// EXPORTS //

module.exports = ns;
<commit_msg>Update alias to be consistent with parent namespace<commit_after>/**
* @license Apache-2.0
*
* Copyright (c) 2018 The Stdlib Authors.
*
* 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.
*/

'use strict';

/*
* When adding modules to the namespace, ensure that they are added in alphabetical order according to module name.
*/

// MODULES //

var setReadOnly = require( '@stdlib/utils/define-read-only-property' );

// MAIN //

/**
* Top-level namespace.
*
* @namespace ns
*/
var ns = {};

/**
* @name assert
* @memberof ns
* @readonly
* @type {Namespace}
* @see {@link module:@stdlib/ndarray/base/assert}
*/
setReadOnly( ns, 'assert', require( '@stdlib/ndarray/base/assert' ) );

/**
* @name bind2vind
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/bind2vind}
*/
setReadOnly( ns, 'bind2vind', require( '@stdlib/ndarray/base/bind2vind' ) );

/**
* @name buffer
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/buffer}
*/
setReadOnly( ns, 'buffer', require( '@stdlib/ndarray/base/buffer' ) );

/**
* @name bufferCtors
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/buffer-ctors}
*/
setReadOnly( ns, 'bufferCtors', require( '@stdlib/ndarray/base/buffer-ctors' ) );

/**
* @name bufferDataType
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/buffer-dtype}
*/
setReadOnly( ns, 'bufferDataType', require( '@stdlib/ndarray/base/buffer-dtype' ) );

/**
* @name bytesPerElement
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/bytes-per-element}
*/
setReadOnly( ns, 'bytesPerElement', require( '@stdlib/ndarray/base/bytes-per-element' ) );

/**
* @name clampIndex
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/clamp-index}
*/
setReadOnly( ns, 'clampIndex', require( '@stdlib/ndarray/base/clamp-index' ) );

/**
* @name ndarray
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/ctor}
*/
setReadOnly( ns, 'ndarray', require( '@stdlib/ndarray/base/ctor' ) );

/**
* @name dtypeChar
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/dtype-char}
*/
setReadOnly( ns, 'dtypeChar', require( '@stdlib/ndarray/base/dtype-char' ) );

/**
* @name ind
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/ind}
*/
setReadOnly( ns, 'ind', require( '@stdlib/ndarray/base/ind' ) );

/**
* @name ind2sub
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/ind2sub}
*/
setReadOnly( ns, 'ind2sub', require( '@stdlib/ndarray/base/ind2sub' ) );

/**
* @name iterationOrder
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/iteration-order}
*/
setReadOnly( ns, 'iterationOrder', require( '@stdlib/ndarray/base/iteration-order' ) );

/**
* @name maxViewBufferIndex
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/max-view-buffer-index}
*/
setReadOnly( ns, 'maxViewBufferIndex', require( '@stdlib/ndarray/base/max-view-buffer-index' ) );

/**
* @name ndarrayMemoized
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/memoized-ctor}
*/
setReadOnly( ns, 'ndarrayMemoized', require( '@stdlib/ndarray/base/memoized-ctor' ) );

/**
* @name minViewBufferIndex
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/min-view-buffer-index}
*/
setReadOnly( ns, 'minViewBufferIndex', require( '@stdlib/ndarray/base/min-view-buffer-index' ) );

/**
* @name minmaxViewBufferIndex
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/minmax-view-buffer-index}
*/
setReadOnly( ns, 'minmaxViewBufferIndex', require( '@stdlib/ndarray/base/minmax-view-buffer-index' ) );

/**
* @name numel
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/numel}
*/
setReadOnly( ns, 'numel', require( '@stdlib/ndarray/base/numel' ) );

/**
* @name shape2strides
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/shape2strides}
*/
setReadOnly( ns, 'shape2strides', require( '@stdlib/ndarray/base/shape2strides' ) );

/**
* @name strides2offset
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/strides2offset}
*/
setReadOnly( ns, 'strides2offset', require( '@stdlib/ndarray/base/strides2offset' ) );

/**
* @name strides2order
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/strides2order}
*/
setReadOnly( ns, 'strides2order', require( '@stdlib/ndarray/base/strides2order' ) );

/**
* @name sub2ind
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/sub2ind}
*/
setReadOnly( ns, 'sub2ind', require( '@stdlib/ndarray/base/sub2ind' ) );

/**
* @name ndarray2array
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/to-array}
*/
setReadOnly( ns, 'ndarray2array', require( '@stdlib/ndarray/base/to-array' ) );

/**
* @name vind2bind
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/vind2bind}
*/
setReadOnly( ns, 'vind2bind', require( '@stdlib/ndarray/base/vind2bind' ) );

/**
* @name wrapIndex
* @memberof ns
* @readonly
* @type {Function}
* @see {@link module:@stdlib/ndarray/base/wrap-index}
*/
setReadOnly( ns, 'wrapIndex', require( '@stdlib/ndarray/base/wrap-index' ) );

// EXPORTS //

module.exports = ns;
<|endoftext|>


<commit_before>const { KubeConfig } = require('kubernetes-client')
const Client = require('kubernetes-client').Client
const Request = require('kubernetes-client/backends/request')

const kubeconfig = new KubeConfig()
var consoleLogger = require('../../util/logger.js')
var LRU = require('lru-cache')
var podCache = new LRU({
  max: 2000,
  maxAge: 1000 * 60 * 60
})

var digestRegEx = /sha256:.+/i
var client = null
const FALSE_REGEX = /false/i

if (process.env.KUBERNETES_PORT_443_TCP !== undefined) {
  kubeconfig.loadFromCluster()
} else {
  kubeconfig.loadFromDefault()
}
client = new Client({ backend: new Request({ kubeconfig }) })
client.loadSpec().catch(error => {
  consoleLogger.error('Error in k8s client', error)
})

function kubernetesOutputFilter (context, config, eventEmitter, data, callback) {
  // we use the config object to track state of each plugin instance
  if (!config.client) {
    initKubernetesClient(config)
  } else {
    enrichLogs(context, config, eventEmitter, data, callback)
  }
}

function initKubernetesClient (config) {
  // do we run in k8s cluster?
  config.client = client
  config.getPodSpec = function (namespace, podName, cb) {
    this.client.api.v1.namespaces(namespace).pods(podName).get(cb)
  }.bind(config)
}

async function getPodSpec (config, namespace, podName, cb) {
  if (config.client && config.client.api) {
    var spec = await config.client.api.v1.namespaces(namespace).pods(podName).get()
    cb(null, spec.body)
  } else {
    cb(new Error('Kuberntes API not ready'))
  }
}

function removeFields (pod, data) {
  if (pod && pod.stRemoveFields === false) {
    return
  }
  var annotations = pod.metadata.annotations
  var removeFields = annotations['REMOVE_FIELDS'] || annotations['sematext.com/logs-remove-fields']
  if (removeFields) {
    var fieldNames = removeFields.split(',')
    for (var i = 0; i < fieldNames.length; i++) {
      delete data[fieldNames[i]]
    }
  } else {
    pod.stRemoveFields = false
  }
}

function checkLogsEnabled (pod, data, context) {
  if (pod.stLogEnabled === false) {
    data.stLogEnabled = false
    return
  }
  var annotations = pod.metadata.annotations
  if (annotations) {
    var logsEnabled = annotations['sematext.com/logs-enabled']
    if (FALSE_REGEX.test(logsEnabled)) {
      data.stLogEnabled = false
      pod.stLogEnabled = false
      if (process.env.DEBUG) {
        consoleLogger.error('stLogEnabled = false', key)
      }
    } else {
      pod.stLogEnabled = true
    }
  }
}

function addLogsIndex (pod, data) {
  if (pod.stLogsTokenSet === false) {
    return
  }
  // get ST Logs Token from pod annotation
  var index = pod.metadata.annotations['sematext.com/logs-token']
  // get comma separated list of fields from pod annotation
  if (index) {
    data._index = index
  } else {
    pod.stLogsTokenSet = false
  }
}

function replaceDockerImageName (pod, data) {
  if (data.kubernetes && data.container && pod.stImageCache) {
    var containerName = data.kubernetes.pod.container.name
    var imageInfo = pod.stImageCache[data.kubernetes.pod.name + containerName]
    if (!imageInfo) {
      return
    }
    if (data.container.image.name === 'sha256') {
      data.container.image.digest = 'sha256:' + data.container.image.tag
    }
    data.container.image.name = imageInfo.name
    data.container.image.tag = imageInfo.tag
  } else {
    pod.stLogsTokenSet = false
  }
}

function processAnnotations (data, context) {
  var pod = podCache.get(data.kubernetes.namespace + '/' + data.kubernetes.pod.name)
  if (pod && pod.metadata) {
    checkLogsEnabled(pod, data, context)
    if (data.stLogEnabled === false) {
      // logs will be droped, so no need for other processing
      return
    }
    replaceDockerImageName(pod, data)
    removeFields(pod, data)
    addLogsIndex(pod, data)
  }
}

function enrichLogs (context, config, eventEmitter, data, callback) {
  if (!(data.kubernetes && data.kubernetes.pod)) {
    return callback(null, data)
  }

  if (podCache.get(data.kubernetes.namespace + '/' + data.kubernetes.pod.name)) {
    processAnnotations(data, context)
    if (data.stLogEnabled === false) {
      // allow input plugins to close the input stream, e.g. input/docker/docker.js
      eventEmitter.emit('dropLogsRequest', context, data)
      if (config.debug === true) {
        consoleLogger.log('logs dropped ' + data.kubernetes.namespace + '/' + data.kubernetes.pod.name, data.message)
      }
      return callback()
    } else {
      return callback(null, data)
    }
  } else {
    getPodSpec(
      config,
      data.kubernetes.namespace,
      data.kubernetes.pod.name,
      function (err, pod) {
        if (err) {
          consoleLogger.log(err.message)
          return callback(null, data)
        }
        podCache.set(data.kubernetes.namespace + '/' + data.kubernetes.pod.name, pod)
        // create hashtable with containerName -> imageInformation
        pod.stImageCache = {}
        if (pod.spec && pod.spec.containers) {
          var podContainers = pod.spec.containers
          for (var i = 0; i < podContainers.length; i++) {
            var container = podContainers[i]
            // split imageName:version
            var imageInfo = container.image.split(':')
            // split registry/imageName
            var imageRegistryInfo = container.image.split('/')
            var imageKey = pod.metadata.name + container.name
            pod.stImageCache[imageKey] = {
              image: container.image,
              name: imageInfo[0],
              registry: imageRegistryInfo[0]
            }
            if (imageInfo.length > 1) {
              pod.stImageCache[imageKey].tag = imageInfo[1]
            }
            if (imageRegistryInfo.length > 1) {
              pod.stImageCache[imageKey].plainImageName = imageRegistryInfo[1]
            }
          }
        }
        if (config.debug) {
          consoleLogger.log('pod spec: ' + JSON.stringify(pod, null, ' '))
        }
        processAnnotations(data, context)
        if (data.stLogEnabled === false) {
          // allow input plugins to close the input stream, e.g. input/docker/docker.js
          eventEmitter.emit('dropLogsRequest', context, data)
          return callback()
        } else {
          return callback(null, data)
        }
      }
    )
  }
}

module.exports = kubernetesOutputFilter
<commit_msg>fix 'Unhandled Rejection' in kuberntes-enrichment plugin<commit_after>const { KubeConfig } = require('kubernetes-client')
const Client = require('kubernetes-client').Client
const Request = require('kubernetes-client/backends/request')

const kubeconfig = new KubeConfig()
var consoleLogger = require('../../util/logger.js')
var LRU = require('lru-cache')
var podCache = new LRU({
  max: 2000,
  maxAge: 1000 * 60 * 60
})

var digestRegEx = /sha256:.+/i
var client = null
const FALSE_REGEX = /false/i

if (process.env.KUBERNETES_PORT_443_TCP !== undefined) {
  kubeconfig.loadFromCluster()
} else {
  kubeconfig.loadFromDefault()
}
client = new Client({ backend: new Request({ kubeconfig }) })
client.loadSpec().catch(error => {
  consoleLogger.error('Error in k8s client', error)
})

function kubernetesOutputFilter (context, config, eventEmitter, data, callback) {
  // we use the config object to track state of each plugin instance
  if (!config.client) {
    initKubernetesClient(config)
  } else {
    enrichLogs(context, config, eventEmitter, data, callback)
  }
}

function initKubernetesClient (config) {
  // do we run in k8s cluster?
  config.client = client
  config.getPodSpec = function (namespace, podName, cb) {
    this.client.api.v1.namespaces(namespace).pods(podName).get(cb)
  }.bind(config)
}

async function getPodSpec (config, namespace, podName, cb) {
  if (config.client && config.client.api) {
    var spec = await config.client.api.v1.namespaces(namespace).pods(podName).get()
    cb(null, spec.body)
  } else {
    cb(new Error('Kuberntes API not ready'))
  }
}

function removeFields (pod, data) {
  if (pod && pod.stRemoveFields === false) {
    return
  }
  var annotations = pod.metadata.annotations
  if (!annotations) {
    pod.stRemoveFields = false
    return
  }
  var removeFields = annotations['REMOVE_FIELDS'] || annotations['sematext.com/logs-remove-fields']
  if (removeFields) {
    var fieldNames = removeFields.split(',')
    for (var i = 0; i < fieldNames.length; i++) {
      delete data[fieldNames[i]]
    }
  } else {
    pod.stRemoveFields = false
  }
}

function checkLogsEnabled (pod, data, context) {
  if (pod.stLogEnabled === false) {
    data.stLogEnabled = false
    return
  }
  var annotations = pod.metadata.annotations
  if (annotations) {
    var logsEnabled = annotations['sematext.com/logs-enabled']
    if (FALSE_REGEX.test(logsEnabled)) {
      data.stLogEnabled = false
      pod.stLogEnabled = false
      if (process.env.DEBUG) {
        consoleLogger.error('stLogEnabled = false', key)
      }
    } else {
      pod.stLogEnabled = true
    }
  }
}

function addLogsIndex (pod, data) {
  if (pod.stLogsTokenSet === false) {
    return
  }
  // get ST Logs Token from pod annotation
  var index = pod.metadata.annotations['sematext.com/logs-token']
  // get comma separated list of fields from pod annotation
  if (index) {
    data._index = index
  } else {
    pod.stLogsTokenSet = false
  }
}

function replaceDockerImageName (pod, data) {
  if (data.kubernetes && data.container && pod.stImageCache) {
    var containerName = data.kubernetes.pod.container.name
    var imageInfo = pod.stImageCache[data.kubernetes.pod.name + containerName]
    if (!imageInfo) {
      return
    }
    if (data.container.image.name === 'sha256') {
      data.container.image.digest = 'sha256:' + data.container.image.tag
    }
    data.container.image.name = imageInfo.name
    data.container.image.tag = imageInfo.tag
  } else {
    pod.stLogsTokenSet = false
  }
}

function processAnnotations (data, context) {
  var pod = podCache.get(data.kubernetes.namespace + '/' + data.kubernetes.pod.name)
  if (pod && pod.metadata) {
    checkLogsEnabled(pod, data, context)
    if (data.stLogEnabled === false) {
      // logs will be droped, so no need for other processing
      return
    }
    replaceDockerImageName(pod, data)
    removeFields(pod, data)
    addLogsIndex(pod, data)
  }
}

function enrichLogs (context, config, eventEmitter, data, callback) {
  if (!(data.kubernetes && data.kubernetes.pod)) {
    return callback(null, data)
  }

  if (podCache.get(data.kubernetes.namespace + '/' + data.kubernetes.pod.name)) {
    processAnnotations(data, context)
    if (data.stLogEnabled === false) {
      // allow input plugins to close the input stream, e.g. input/docker/docker.js
      eventEmitter.emit('dropLogsRequest', context, data)
      if (config.debug === true) {
        consoleLogger.log('logs dropped ' + data.kubernetes.namespace + '/' + data.kubernetes.pod.name, data.message)
      }
      return callback()
    } else {
      return callback(null, data)
    }
  } else {
    getPodSpec(
      config,
      data.kubernetes.namespace,
      data.kubernetes.pod.name,
      function (err, pod) {
        if (err) {
          consoleLogger.log(err.message)
          return callback(null, data)
        }
        podCache.set(data.kubernetes.namespace + '/' + data.kubernetes.pod.name, pod)
        // create hashtable with containerName -> imageInformation
        pod.stImageCache = {}
        if (pod.spec && pod.spec.containers) {
          var podContainers = pod.spec.containers
          for (var i = 0; i < podContainers.length; i++) {
            var container = podContainers[i]
            // split imageName:version
            var imageInfo = container.image.split(':')
            // split registry/imageName
            var imageRegistryInfo = container.image.split('/')
            var imageKey = pod.metadata.name + container.name
            pod.stImageCache[imageKey] = {
              image: container.image,
              name: imageInfo[0],
              registry: imageRegistryInfo[0]
            }
            if (imageInfo.length > 1) {
              pod.stImageCache[imageKey].tag = imageInfo[1]
            }
            if (imageRegistryInfo.length > 1) {
              pod.stImageCache[imageKey].plainImageName = imageRegistryInfo[1]
            }
          }
        }
        if (config.debug) {
          consoleLogger.log('pod spec: ' + JSON.stringify(pod, null, ' '))
        }
        processAnnotations(data, context)
        if (data.stLogEnabled === false) {
          // allow input plugins to close the input stream, e.g. input/docker/docker.js
          eventEmitter.emit('dropLogsRequest', context, data)
          return callback()
        } else {
          return callback(null, data)
        }
      }
    )
  }
}

module.exports = kubernetesOutputFilter
<|endoftext|>


<commit_before>'use strict';

// Remove element
var removeElement = function(element) {
  element.parentNode.removeChild(element);
};

// Home page
var linkList = document.getElementById('gbzc');
var moreLinks = document.getElementById('gbmm');
var expandOnHomePage = function() {
  removeElement(linkList.lastChild);

  var gbtb2;
  var gbts;
  var option;
  var optionLink;
  while (moreLinks.childNodes.length > 0) {
    option = moreLinks.firstChild;
    optionLink = option.firstChild;

    if (optionLink.textContent) {
      option.className = 'gbt';

      if (optionLink.className.indexOf('gbp1') > -1) {
        optionLink.className = 'gbzt gbz0l gbp1';
      } else {
        optionLink.className = 'gbzt';
      }

      gbtb2 = document.createElement('span');
      gbtb2.className = 'gbtb2';

      gbts = document.createElement('span');
      gbts.className = 'gbts';
      gbts.textContent = optionLink.textContent;
      optionLink.text = '';

      optionLink.appendChild(gbtb2);
      optionLink.appendChild(gbts);
      linkList.appendChild(option);
    } else {
      removeElement(option);
    }
  }
};

// Search page
var searchOptionMenu = document.getElementById('hdtb-msb');
var searchMore = document.getElementById('hdtb-more');
var searchMoreItems = document.getElementById('hdtb-more-mn');
var searchTool = document.getElementById('hdtb-tls');
var expandOnSearchPage = function() {
  var searchFilters = document.getElementsByClassName('hdtbU');
  var rcnt = document.getElementById('rcnt');
  var slimAppbar = document.getElementById('slim_appbar');
  var centerCol = document.getElementById('center_col');
  var footcnt = document.getElementById('footcnt');
  var nycp = document.getElementById('nycp');
  var nyccur = document.getElementById('nyccur');
  var rhs = document.getElementById('rhs');
  var i;
  var j;
  var item;
  var itemLink;
  var leftCol;
  var searchFilter;

  while (searchMoreItems.childNodes.length > 0) {
    item = searchMoreItems.firstChild;
    searchOptionMenu.insertBefore(item, searchTool);
  }

  for (i of [searchOptionMenu.firstChild, slimAppbar, footcnt]) {
    i.style.marginLeft = '200px';
  }

  centerCol.style.marginLeft = '200px';

  if (nycp && nyccur) {
    for (i of [nycp, nyccur]) {
      i.style.marginLeft = '113px';
    }
  }

  rhs.style.marginLeft = '782px';

  leftCol = document.createElement('div');
  leftCol.style.className = 'col';
  leftCol.style.width = '200px';
  leftCol.style.float = 'left';
  leftCol.style.position = 'absolute';

  for (i = 0; i < searchFilters.length; ++i) {
    searchFilter = searchFilters.item(i).cloneNode(true);
    searchFilter.className = '';
    for (j = 0; j < searchFilter.childNodes.length; ++j) {
      searchFilter.childNodes[j].className = '';
    }
    leftCol.appendChild(searchFilter);
  }

  rcnt.insertBefore(leftCol, rcnt.firstChild);

  removeElement(searchMore);
  removeElement(searchMoreItems);
  // removeElement(searchTool);
};

// Main
(function main() {
  if (linkList && moreLinks) {
    expandOnHomePage();
  }

  if (searchOptionMenu && searchMore && searchTool) {
    expandOnSearchPage();
  }
})();
<commit_msg>Styling search filters in the left column<commit_after>'use strict';

// Remove element
var removeElement = function(element) {
  element.parentNode.removeChild(element);
};

// Home page
var linkList = document.getElementById('gbzc');
var moreLinks = document.getElementById('gbmm');
var expandOnHomePage = function() {
  removeElement(linkList.lastChild);

  var gbtb2;
  var gbts;
  var option;
  var optionLink;
  while (moreLinks.childNodes.length > 0) {
    option = moreLinks.firstChild;
    optionLink = option.firstChild;

    if (optionLink.textContent) {
      option.className = 'gbt';

      if (optionLink.className.indexOf('gbp1') > -1) {
        optionLink.className = 'gbzt gbz0l gbp1';
      } else {
        optionLink.className = 'gbzt';
      }

      gbtb2 = document.createElement('span');
      gbtb2.className = 'gbtb2';

      gbts = document.createElement('span');
      gbts.className = 'gbts';
      gbts.textContent = optionLink.textContent;
      optionLink.text = '';

      optionLink.appendChild(gbtb2);
      optionLink.appendChild(gbts);
      linkList.appendChild(option);
    } else {
      removeElement(option);
    }
  }
};

// Search page
var searchOptionMenu = document.getElementById('hdtb-msb');
var searchMore = document.getElementById('hdtb-more');
var searchMoreItems = document.getElementById('hdtb-more-mn');
var searchTool = document.getElementById('hdtb-tls');
var expandOnSearchPage = function() {
  var searchFilters = document.getElementsByClassName('hdtbU');
  var searchToolDropMenu = document.getElementById('hdtbMenus');
  var rcnt = document.getElementById('rcnt');
  var slimAppbar = document.getElementById('slim_appbar');
  var centerCol = document.getElementById('center_col');
  var footcnt = document.getElementById('footcnt');
  var nycp = document.getElementById('nycp');
  var nyccur = document.getElementById('nyccur');
  var rhs = document.getElementById('rhs');
  var i;
  var j;
  var k;
  var nd;
  var ndLinks;
  var ndLink;
  var item;
  var itemLink;
  var leftCol;
  var searchFilter;

  while (searchMoreItems.childNodes.length > 0) {
    item = searchMoreItems.firstChild;
    searchOptionMenu.insertBefore(item, searchTool);
  }

  for (i of [searchOptionMenu.firstChild, slimAppbar, footcnt]) {
    i.style.marginLeft = '200px';
  }

  centerCol.style.marginLeft = '200px';

  if (nycp && nyccur) {
    for (i of [nycp, nyccur]) {
      i.style.marginLeft = '113px';
    }
  }

  rhs.style.marginLeft = '782px';

  leftCol = document.createElement('div');
  leftCol.className = 'geo-leftcol';
  leftCol.style.width = '200px';
  leftCol.style.float = 'left';
  leftCol.style.position = 'absolute';

  for (i = 0; i < searchFilters.length; ++i) {
    searchFilter = searchFilters.item(i).cloneNode(true);
    searchFilter.className = 'geo-ul';
    searchFilter.style.marginBottom = '20px';
    for (j = 0; j < searchFilter.childNodes.length; ++j) {
      nd = searchFilter.childNodes[j];
      nd.className = 'geo-li';

      ndLinks = nd.getElementsByTagName('a');
      if (ndLinks.length) {
        for (k = 0; k < ndLinks.length; ++k) {
          ndLink = ndLinks.item(k);
          ndLink.style.lineHeight = '17px';
          ndLink.style.display = 'block';
          ndLink.style.color = '#777';
          ndLink.style.padding = '6px 44px 6px 30px';
          ndLink.style.textDecoration = 'none';
          ndLink.style.backgroundColor = 'inherit';
          ndLink.onmouseover = function(e) {
            e.target.style.backgroundColor = '#f1f1f1';
          };
          ndLink.onmouseout = function(e) {
            e.target.style.backgroundColor = '#fff';
          };
        }
      } else {
        nd.style.lineHeight = '17px';
        nd.style.display = 'block';
        nd.style.padding = '6px 44px 6px 30px';
        nd.style.textDecoration = 'none';
        nd.style.color = '#DD4B39';
        nd.style.fontWeight = 'bold';
      }
    }
    leftCol.appendChild(searchFilter);
  }

  rcnt.insertBefore(leftCol, rcnt.firstChild);

  removeElement(searchMore);
  removeElement(searchMoreItems);
  removeElement(searchTool);
  removeElement(searchToolDropMenu);
};

// Main
(function main() {
  if (linkList && moreLinks) {
    expandOnHomePage();
  }

  if (searchOptionMenu && searchMore && searchTool) {
    expandOnSearchPage();
  }
})();
<|endoftext|>


<commit_before>import warning from 'warning';
import PropTypes from 'prop-types';
import React from 'react';
import ReactDOM from 'react-dom';
import window from 'window-or-global';

/**
 * The structure for the position of floating menu.
 * @typedef {Object} FloatingMenu~position
 * @property {number} left The left position.
 * @property {number} top The top position.
 * @property {number} right The right position.
 * @property {number} bottom The bottom position.
 */

/**
 * The structure for the size of floating menu.
 * @typedef {Object} FloatingMenu~size
 * @property {number} width The width.
 * @property {number} height The height.
 */

/**
 * The structure for the position offset of floating menu.
 * @typedef {Object} FloatingMenu~offset
 * @property {number} top The top position.
 * @property {number} left The left position.
 */

export const DIRECTION_LEFT = 'left';
export const DIRECTION_TOP = 'top';
export const DIRECTION_RIGHT = 'right';
export const DIRECTION_BOTTOM = 'bottom';

const hasCreatePortal = typeof ReactDOM.createPortal === 'function';

/**
 * @param {FloatingMenu~offset} [oldMenuOffset={}] The old value.
 * @param {FloatingMenu~offset} [menuOffset={}] The new value.
 * @returns `true` if the parent component wants to change in the adjustment of the floating menu position.
 * @private
 */
const hasChangeInOffset = (oldMenuOffset = {}, menuOffset = {}) => {
  if (typeof oldMenuOffset !== typeof menuOffset) {
    return true;
  } else if (
    Object(menuOffset) === menuOffset &&
    typeof menuOffset !== 'function'
  ) {
    return (
      oldMenuOffset.top !== menuOffset.top ||
      oldMenuOffset.left !== menuOffset.left
    );
  }
  return oldMenuOffset !== menuOffset;
};

/**
 * @param {Object} params The parameters.
 * @param {FloatingMenu~size} params.menuSize The size of the menu.
 * @param {FloatingMenu~position} params.refPosition The position of the triggering element.
 * @param {FloatingMenu~offset} [params.offset={ left: 0, top: 0 }] The position offset of the menu.
 * @param {string} [params.direction=bottom] The menu direction.
 * @param {number} [params.scrollY=0] The scroll position of the viewport.
 * @returns {FloatingMenu~offset} The position of the menu, relative to the top-left corner of the viewport.
 * @private
 */
const getFloatingPosition = ({
  menuSize,
  refPosition,
  offset = {},
  direction = DIRECTION_BOTTOM,
  scrollY = 0,
}) => {
  const {
    left: refLeft = 0,
    top: refTop = 0,
    right: refRight = 0,
    bottom: refBottom = 0,
  } = refPosition;

  const { width, height } = menuSize;
  const { top = 0, left = 0 } = offset;
  const refCenterHorizontal = (refLeft + refRight) / 2;
  const refCenterVertical = (refTop + refBottom) / 2;

  return {
    [DIRECTION_LEFT]: () => ({
      left: refLeft - width - left,
      top: refCenterVertical - height / 2 + scrollY + top,
    }),
    [DIRECTION_TOP]: () => ({
      left: refCenterHorizontal - width / 2 + left,
      top: refTop - height + scrollY - top,
    }),
    [DIRECTION_RIGHT]: () => ({
      left: refRight + left,
      top: refCenterVertical - height / 2 + scrollY + top,
    }),
    [DIRECTION_BOTTOM]: () => ({
      left: refCenterHorizontal - width / 2 + left,
      top: refBottom + scrollY + top,
    }),
  }[direction]();
};

/**
 * A menu that is detached from the triggering element.
 * Useful when the container of the triggering element cannot have `overflow:visible` style, etc.
 */
class FloatingMenu extends React.Component {
  static propTypes = {
    /**
     * Contents to put into the floating menu.
     */
    children: PropTypes.object,

    /**
     * The position in the viewport of the trigger button.
     */
    menuPosition: PropTypes.shape({
      top: PropTypes.number,
      right: PropTypes.number,
      bottom: PropTypes.number,
      left: PropTypes.number,
    }),

    /**
     * Where to put the tooltip, relative to the trigger button.
     */
    menuDirection: PropTypes.oneOf([
      DIRECTION_LEFT,
      DIRECTION_TOP,
      DIRECTION_RIGHT,
      DIRECTION_BOTTOM,
    ]),

    /**
     * The adjustment of the floating menu position, considering the position of dropdown arrow, etc.
     */
    menuOffset: PropTypes.oneOfType([
      PropTypes.shape({
        top: PropTypes.number,
        left: PropTypes.number,
      }),
      PropTypes.func,
    ]),

    /**
     * The additional styles to put to the floating menu.
     */
    styles: PropTypes.object,
  };

  static defaultProps = {
    menuPosition: {},
    menuOffset: {},
    menuDirection: DIRECTION_BOTTOM,
  };

  state = {
    /**
     * The position of the menu, relative to the top-left corner of the viewport.
     * @type {FloatingMenu~offset}
     */
    floatingPosition: undefined,
  };

  /**
   * The cached refernce to the menu container.
   * Only used if React portal API is not available.
   * @type {Element}
   * @private
   */
  _menuContainer = null;

  /**
   * The cached refernce to the menu body.
   * @type {Element}
   * @private
   */
  _menuBody = null;

  /**
   * A callback called when a new menu ref is available.
   * @private
   */
  _onNewMenuRef = menu => {
    if (hasCreatePortal) {
      this._menuBody = menu && menu.firstChild;
    }
  };

  /**
   * Calculates the position in the viewport of floating menu,
   * once this component is mounted or updated upon change in the following props:
   *
   * * `menuPosition` (The position in the viewport of the trigger button)
   * * `menuOffset` (The adjustment that should be applied to the calculated floating menu's position)
   * * `menuDirection` (Where the floating menu menu should be placed relative to the trigger button)
   *
   * @private
   */
  _updateMenuSize = (prevProps = {}) => {
    const menuBody = this._menuBody;
    warning(
      menuBody,
      'The DOM node for menu body for calculating its position is not available. Skipping...'
    );
    if (!menuBody) {
      return;
    }

    const {
      menuPosition: oldRefPosition = {},
      menuOffset: oldMenuOffset = {},
      menuDirection: oldMenuDirection,
    } = prevProps;
    const {
      menuPosition: refPosition = {},
      menuOffset = {},
      menuDirection,
    } = this.props;

    if (
      oldRefPosition.top !== refPosition.top ||
      oldRefPosition.right !== refPosition.right ||
      oldRefPosition.bottom !== refPosition.bottom ||
      oldRefPosition.left !== refPosition.left ||
      hasChangeInOffset(oldMenuOffset, menuOffset) ||
      oldMenuDirection !== menuDirection
    ) {
      const menuSize = menuBody.getBoundingClientRect();
      const offset =
        typeof menuOffset !== 'function'
          ? menuOffset
          : menuOffset(menuBody, menuDirection);
      // Skips if either in the following condition:
      // a) Menu body has `display:none`
      // b) `menuOffset` as a callback returns `undefined` (The callback saw that it couldn't calculate the value)
      if ((menuSize.width > 0 && menuSize.height > 0) || !offset) {
        this.setState({
          floatingPosition: getFloatingPosition({
            menuSize,
            refPosition,
            direction: menuDirection,
            offset,
            scrollY: window.scrollY,
          }),
        });
      }
    }
  };

  componentDidUpdate(prevProps) {
    if (!hasCreatePortal) {
      ReactDOM.render(this._getChildrenWithProps(), this._menuContainer);
    } else {
      this._updateMenuSize(prevProps);
    }
  }

  componentDidMount() {
    if (!hasCreatePortal) {
      this._menuContainer = document.createElement('div');
      document.body.appendChild(this._menuContainer);
      const style = {
        display: 'block',
        opacity: 0,
      };
      const childrenWithProps = React.cloneElement(this.props.children, {
        style,
      });
      ReactDOM.render(childrenWithProps, this._menuContainer, () => {
        this._menuBody = this._menuContainer.firstChild;
        this._updateMenuSize();
        ReactDOM.render(this._getChildrenWithProps(), this._menuContainer);
      });
    } else {
      this._updateMenuSize();
    }
  }

  componentWillUnmount() {
    if (!hasCreatePortal) {
      const menuContainer = this._menuContainer;
      ReactDOM.unmountComponentAtNode(menuContainer);
      if (menuContainer && menuContainer.parentNode) {
        menuContainer.parentNode.removeChild(menuContainer);
      }
      this._menuContainer = null;
    }
  }

  /**
   * @returns The child nodes, with styles containing the floating menu position.
   * @private
   */
  _getChildrenWithProps = () => {
    const { styles, children } = this.props;
    const { floatingPosition: pos } = this.state;
    return !pos
      ? children
      : React.cloneElement(children, {
          style: Object.assign(
            {
              left: `${pos.left}px`,
              top: `${pos.top}px`,
              position: 'absolute',
              right: 'auto',
              margin: 0,
              opacity: 1,
            },
            styles
          ),
        });
  };

  render() {
    if (typeof document !== 'undefined' && hasCreatePortal) {
      return ReactDOM.createPortal(

{this._getChildrenWithProps()}
, // Add wrapper `<div>` to align to React15 version
        document.body
      );
    }
    return null;
  }
}

export default FloatingMenu;
<commit_msg>fix(FloatingMenu): remove wrapper div for portal (#647)<commit_after>import warning from 'warning';
import PropTypes from 'prop-types';
import React from 'react';
import ReactDOM from 'react-dom';
import window from 'window-or-global';

/**
 * The structure for the position of floating menu.
 * @typedef {Object} FloatingMenu~position
 * @property {number} left The left position.
 * @property {number} top The top position.
 * @property {number} right The right position.
 * @property {number} bottom The bottom position.
 */

/**
 * The structure for the size of floating menu.
 * @typedef {Object} FloatingMenu~size
 * @property {number} width The width.
 * @property {number} height The height.
 */

/**
 * The structure for the position offset of floating menu.
 * @typedef {Object} FloatingMenu~offset
 * @property {number} top The top position.
 * @property {number} left The left position.
 */

export const DIRECTION_LEFT = 'left';
export const DIRECTION_TOP = 'top';
export const DIRECTION_RIGHT = 'right';
export const DIRECTION_BOTTOM = 'bottom';

const hasCreatePortal = typeof ReactDOM.createPortal === 'function';

/**
 * @param {FloatingMenu~offset} [oldMenuOffset={}] The old value.
 * @param {FloatingMenu~offset} [menuOffset={}] The new value.
 * @returns `true` if the parent component wants to change in the adjustment of the floating menu position.
 * @private
 */
const hasChangeInOffset = (oldMenuOffset = {}, menuOffset = {}) => {
  if (typeof oldMenuOffset !== typeof menuOffset) {
    return true;
  } else if (
    Object(menuOffset) === menuOffset &&
    typeof menuOffset !== 'function'
  ) {
    return (
      oldMenuOffset.top !== menuOffset.top ||
      oldMenuOffset.left !== menuOffset.left
    );
  }
  return oldMenuOffset !== menuOffset;
};

/**
 * @param {Object} params The parameters.
 * @param {FloatingMenu~size} params.menuSize The size of the menu.
 * @param {FloatingMenu~position} params.refPosition The position of the triggering element.
 * @param {FloatingMenu~offset} [params.offset={ left: 0, top: 0 }] The position offset of the menu.
 * @param {string} [params.direction=bottom] The menu direction.
 * @param {number} [params.scrollY=0] The scroll position of the viewport.
 * @returns {FloatingMenu~offset} The position of the menu, relative to the top-left corner of the viewport.
 * @private
 */
const getFloatingPosition = ({
  menuSize,
  refPosition,
  offset = {},
  direction = DIRECTION_BOTTOM,
  scrollY = 0,
}) => {
  const {
    left: refLeft = 0,
    top: refTop = 0,
    right: refRight = 0,
    bottom: refBottom = 0,
  } = refPosition;

  const { width, height } = menuSize;
  const { top = 0, left = 0 } = offset;
  const refCenterHorizontal = (refLeft + refRight) / 2;
  const refCenterVertical = (refTop + refBottom) / 2;

  return {
    [DIRECTION_LEFT]: () => ({
      left: refLeft - width - left,
      top: refCenterVertical - height / 2 + scrollY + top,
    }),
    [DIRECTION_TOP]: () => ({
      left: refCenterHorizontal - width / 2 + left,
      top: refTop - height + scrollY - top,
    }),
    [DIRECTION_RIGHT]: () => ({
      left: refRight + left,
      top: refCenterVertical - height / 2 + scrollY + top,
    }),
    [DIRECTION_BOTTOM]: () => ({
      left: refCenterHorizontal - width / 2 + left,
      top: refBottom + scrollY + top,
    }),
  }[direction]();
};

/**
 * A menu that is detached from the triggering element.
 * Useful when the container of the triggering element cannot have `overflow:visible` style, etc.
 */
class FloatingMenu extends React.Component {
  static propTypes = {
    /**
     * Contents to put into the floating menu.
     */
    children: PropTypes.object,

    /**
     * The position in the viewport of the trigger button.
     */
    menuPosition: PropTypes.shape({
      top: PropTypes.number,
      right: PropTypes.number,
      bottom: PropTypes.number,
      left: PropTypes.number,
    }),

    /**
     * Where to put the tooltip, relative to the trigger button.
     */
    menuDirection: PropTypes.oneOf([
      DIRECTION_LEFT,
      DIRECTION_TOP,
      DIRECTION_RIGHT,
      DIRECTION_BOTTOM,
    ]),

    /**
     * The adjustment of the floating menu position, considering the position of dropdown arrow, etc.
     */
    menuOffset: PropTypes.oneOfType([
      PropTypes.shape({
        top: PropTypes.number,
        left: PropTypes.number,
      }),
      PropTypes.func,
    ]),

    /**
     * The additional styles to put to the floating menu.
     */
    styles: PropTypes.object,
  };

  static defaultProps = {
    menuPosition: {},
    menuOffset: {},
    menuDirection: DIRECTION_BOTTOM,
  };

  state = {
    /**
     * The position of the menu, relative to the top-left corner of the viewport.
     * @type {FloatingMenu~offset}
     */
    floatingPosition: undefined,
  };

  /**
   * The cached refernce to the menu container.
   * Only used if React portal API is not available.
   * @type {Element}
   * @private
   */
  _menuContainer = null;

  /**
   * The cached refernce to the menu body.
   * @type {Element}
   * @private
   */
  _menuBody = null;

  constructor(props) {
    super(props);
    if (typeof document !== 'undefined' && hasCreatePortal) {
      this.el = document.createElement('div');
    }
  }

  /**
   * A callback called when a new menu ref is available.
   * @private
   */
  _onNewMenuRef = menu => {
    if (hasCreatePortal) {
      this._menuBody = menu && menu.firstChild;
    }
  };

  /**
   * Calculates the position in the viewport of floating menu,
   * once this component is mounted or updated upon change in the following props:
   *
   * * `menuPosition` (The position in the viewport of the trigger button)
   * * `menuOffset` (The adjustment that should be applied to the calculated floating menu's position)
   * * `menuDirection` (Where the floating menu menu should be placed relative to the trigger button)
   *
   * @private
   */
  _updateMenuSize = (prevProps = {}) => {
    const menuBody = this._menuBody;
    warning(
      menuBody,
      'The DOM node for menu body for calculating its position is not available. Skipping...'
    );
    if (!menuBody) {
      return;
    }

    const {
      menuPosition: oldRefPosition = {},
      menuOffset: oldMenuOffset = {},
      menuDirection: oldMenuDirection,
    } = prevProps;
    const {
      menuPosition: refPosition = {},
      menuOffset = {},
      menuDirection,
    } = this.props;

    if (
      oldRefPosition.top !== refPosition.top ||
      oldRefPosition.right !== refPosition.right ||
      oldRefPosition.bottom !== refPosition.bottom ||
      oldRefPosition.left !== refPosition.left ||
      hasChangeInOffset(oldMenuOffset, menuOffset) ||
      oldMenuDirection !== menuDirection
    ) {
      const menuSize = menuBody.getBoundingClientRect();
      const offset =
        typeof menuOffset !== 'function'
          ? menuOffset
          : menuOffset(menuBody, menuDirection);
      // Skips if either in the following condition:
      // a) Menu body has `display:none`
      // b) `menuOffset` as a callback returns `undefined` (The callback saw that it couldn't calculate the value)
      if ((menuSize.width > 0 && menuSize.height > 0) || !offset) {
        this.setState({
          floatingPosition: getFloatingPosition({
            menuSize,
            refPosition,
            direction: menuDirection,
            offset,
            scrollY: window.scrollY,
          }),
        });
      }
    }
  };

  componentDidUpdate(prevProps) {
    if (!hasCreatePortal) {
      ReactDOM.render(this._getChildrenWithProps(), this._menuContainer);
    } else {
      this._updateMenuSize(prevProps);
    }
  }

  componentDidMount() {
    if (!hasCreatePortal) {
      this._menuContainer = document.createElement('div');
      document.body.appendChild(this._menuContainer);
      const style = {
        display: 'block',
        opacity: 0,
      };
      const childrenWithProps = React.cloneElement(this.props.children, {
        style,
      });
      ReactDOM.render(childrenWithProps, this._menuContainer, () => {
        this._menuBody = this._menuContainer.firstChild;
        this._updateMenuSize();
        ReactDOM.render(this._getChildrenWithProps(), this._menuContainer);
      });
    } else {
      if (this.el && this.el.firstChild) {
        this._menuBody = this.el.firstChild;
        document.body.appendChild(this._menuBody);
      }
      this._updateMenuSize();
    }
  }

  componentWillUnmount() {
    if (!hasCreatePortal) {
      const menuContainer = this._menuContainer;
      ReactDOM.unmountComponentAtNode(menuContainer);
      if (menuContainer && menuContainer.parentNode) {
        menuContainer.parentNode.removeChild(menuContainer);
      }
      this._menuContainer = null;
    } else if (this._menuBody) {
      // Moves the menu body back to the portal container so that React unmount code does not crash
      this.el.appendChild(this._menuBody);
    }
  }

  /**
   * @returns The child nodes, with styles containing the floating menu position.
   * @private
   */
  _getChildrenWithProps = () => {
    const { styles, children } = this.props;
    const { floatingPosition: pos } = this.state;
    return !pos
      ? children
      : React.cloneElement(children, {
          style: Object.assign(
            {
              left: `${pos.left}px`,
              top: `${pos.top}px`,
              position: 'absolute',
              right: 'auto',
              margin: 0,
              opacity: 1,
            },
            styles
          ),
        });
  };

  render() {
    if (typeof document !== 'undefined' && hasCreatePortal) {
      return ReactDOM.createPortal(this._getChildrenWithProps(), this.el);
    }
    return null;
  }
}

export default FloatingMenu;
<|endoftext|>