<commit_before>/**
 * All azerite essences go in here.
 * You need to do this manually, usually an easy way to do this is by opening a WCL report and clicking the icons of spells to open the relevant Wowhead pages, here you can get the icon name by clicking the icon, copy the name of the spell and the ID is in the URL.
 * You can access these entries like other entries in the spells files by importing `common/SPELLS` and using the assigned property on the SPELLS object. Please try to avoid abbreviating properties.
 */

 // Trait IDs
  //   2 = "Azeroth's Undying Gift"
  //   3 = "Sphere of Suppression"
  //   4 = "Worldvein Resonance"
  //   5 = "Essence of the Focusing Iris"
  //   6 = "Purification Protocol"
  //   7 = "Anima of Life and Death"
  //  12 = "The Crucible of Flame"
  //  13 = "Nullification Dynamo"
  //  14 = "Condensed Life-Force"
  //  15 = "Ripple in Space"
  //  17 = "The Ever-Rising Tide"
  //  18 = "Artifice of Time"
  //  19 = "The Well of Existence"
  //  20 = "Life-Binder's Invocation"
  //  21 = "Vitality Conduit"
  //  22 = "Vision of Perfection"
  //  23 = "Blood of the Enemy"
  //  25 = "Aegis of the Deep"
  //  27 = "Memory of Lucid Dreams"
  //  28 = "The Unbound Force"
  //  32 = "Conflict and Strife"

export default {
  //The Ever-Rising Tide
  EVER_RISING_TIDE: {
    id: 299879,
    traitId: 17,
    name: 'The Ever-Rising Tide',
    icon: 'inv_elemental_mote_mana',
  },
  EVER_RISING_TIDE_MAJOR: {
    id: 299876,
    name: 'Overcharge Mana',
    icon: 'spell_azerite_essence09',
  },
  EVER_RISING_TIDE_HEALING_BUFF: {
    id: 299624,
    name: 'Overcharge Mana',
    icon: 'spell_azerite_essence09',
  },
  EVER_RISING_TIDE_CHARGING_BUFF: {
    id: 296072,
    name: 'Overcharge Mana',
    icon: 'spell_azerite_essence09',
  },
  EVER_RISING_TIDE_STAT_BUFF: {
    id: 296059,
    name: 'The Ever-Rising Tide',
    icon: 'inv_elemental_mote_mana',
  },
  EVER_RISING_TIDE_ENERGIZE: {
    id: 296065,
    name: 'The Ever-Rising Tide',
    icon: 'inv_elemental_mote_mana',
  },
  WELL_OF_EXISTENCE: {
    id: 299936,
    traitId: 19,
    name: 'The Well Of Existence',
    icon: 'creatureportrait_twilightshammer_summoningportal_water01',
  },
  WELL_OF_EXISTENCE_MAJOR: {
    id: 299933,
    name: 'Refreshment',
    icon: 'creatureportrait_twilightshammer_summoningportal_water01',
  },
  WELL_OF_EXISTENCE_MAJOR_ABILITY: {
    id: 296197,
    name: 'Refreshment',
    icon: 'creatureportrait_twilightshammer_summoningportal_water01',
  },
  WELL_OF_EXISTENCE_DOUBLE_ABSORBTION: {
    id: 296193,
    name: 'The Well of Existence',
    icon: 'creatureportrait_twilightshammer_summoningportal_water01',
  },
  WELL_OF_EXISTENCE_HEAL: {
    id: 296184,
    name: 'The Well of Existence',
    icon: 'creatureportrait_twilightshammer_summoningportal_water01',
  },
  //The Crucible of Flame
  CONCENTRATED_FLAME: {
    id: 299349,
    name: 'Concentrated Flame',
    icon: 'spell_azerite_essence_15',
  },
  ANCIENT_FLAME: {
    id: 299348,
    traitId: 12,
    name: 'Ancient Flame',
    icon: 'inv_radientazeritematrix',
  },
  CONCENTRATED_FLAME_CAST: {
    id: 295373,
    name: 'Concentrated Flame',
    icon: 'spell_azerite_essence_15',
  },
  CONCENTRATED_FLAME_CAST_HEAL: {
    id: 295375,
    name: 'Concentrated Flame',
    icon: 'spell_azerite_essence_15',
  },
  CONCENTRATED_FLAME_CAST_DAMAGE: {
    id: 295374,
    name: 'Concentrated Flame',
    icon: 'spell_azerite_essence_15',
  },
  CONCENTRATED_FLAME_DOT_DAMAGE: {
    id: 295368,
    name: 'Concentrated Flame',
    icon: 'spell_azerite_essence_15',
  },
  ANCIENT_FLAME_DOT_HEAL: {
    id: 303380,
    name: 'Ancient Flame',
    icon: 'spell_azerite_essence_15',
  },
  ANCIENT_FLAME_DOT_DAMAGE: {
    id: 295367,
    name: 'Ancient Flame',
    icon: 'spell_azerite_essence_15',
  },
//general conflict (the major part of conflict and stife)
  CONFLICT: {
    id: 304088,
    traitId: 32,
    name: 'Conflict',
    icon: 'spell_azerite_essence_17',
  },
  //Worldvein Resonance
  WORLDVEIN_RESONANCE: {
    id: 295186,
    traitId: 4,
    name: 'Worldvein Resonance',
    icon: 'inv_misc_azerite_01',
  },
  LIFEBLOOD_BUFF: {
    // buff given when stood near shards with the minor essence, stacking up to 4
    id: 295137,
    name: 'Lifeblood',
    icon: 'inv_glowingazeritespire',
  },
  LIFEBLOOD_SHARD_SUMMON: {
    // id for 'summon' event of shards being created, either randomly from the minor or in response to activating the major
    id: 295114,
    name: 'Lifeblood Shard',
    icon: 'inv_radientazeritematrix',
  },
  NULL_DYNAMO: {
    traitId: 13,
    // major cast
    id: 295746,
    name: 'Nullification Dynamo',
    icon: 'spell_azerite_essence_16',
  },
  NULL_DYNAMO_SHIELD_MINOR: {
    id: 295842,
    name: 'Null Barrier',
    icon: 'spell_azerite_essence_16',
  },
  NULL_DYNAMO_DAMAGE: {
    id: 296061,
    name: 'Null Barrier',
    icon: 'spell_azerite_essence_16',
  },
  // Memory of Lucid Dreams
  LUCID_DREAMS: {
    id: 299374,
    traitId: 27,
    name: 'Memory of Lucid Dreams',
    icon: 'spell_azerite_essence05',
  },
  LUCID_DREAMS_MAJOR: {
    id: 298357,
    name: 'Memory of Lucid Dreams',
    icon: 'spell_azerite_essence05',
  },
  LUCID_DREAMS_MINOR: {
    id: 299373,
    name: 'Lucid Dreams',
    icon: 'inv_radientazeritematrix',
  },
  LUCID_DREAMS_MINOR_STAT_BUFF: {
    id: 298343,
    name: 'Lucid Dreams',
    icon: 'spell_azerite_essence05',
  },
  LUCID_DREAMS_HEAL: {
    id: 298318,
    name: 'Lucid Dreams',
    icon: 'inv_radientazeritematrix',
  },
  LUCID_DREAMS_MINOR_RESOURCE_REFUND: {
    id: 298324,
    name: 'Lucid Dreams',
    icon: 'inv_radientazeritematrix',
  },
  LUCID_DREAMS_MINOR_RESOURCE_REFUND_WARRIOR: {
    id: 298321,
    name: 'Lucid Dreams',
    icon: 'inv_radientazeritematrix',
  },
  //Vision of Perfection
  VISION_OF_PERFECTION: {
    id: 296325,
    traitId: 22,
    name: 'Vision of Perfection',
    icon: 'spell_azerite_essence_18',
  },
  VISION_OF_PERFECTION_HASTE_CALC: {
    id: 303342,
    name: 'Vision of Perfection',
    icon: 'spell_azerite_essence_18',
  },
  VISION_OF_PERFECTION_HASTE_BUFF_SELF: {
    id: 303344,
    name: 'Vision of Perfection',
    icon: 'spell_azerite_essence_18',
  },
  VISION_OF_PERFECTION_HASTE_BUFF_OTHERS: {
    id: 303345,
    name: 'Vision of Perfection',
    icon: 'spell_azerite_essence_18',
  },
  STRIVE_FOR_PERFECTION: {
    id: 296320,
    name: 'Strive for Perfection',
    icon: 'inv_radientazeritematrix',
  },
  STRIVE_FOR_PERFECTION_HEAL: { // 5% heal on activation
    id: 296330,
    name: 'Strive for Perfection',
    icon: 'spell_azerite_essence_18',
  },
  STRIVE_FOR_PERFECTION_VERSATILITY: {
    id: 296322,
    name: 'Strive for Perfection',
    icon: 'inv_radientazeritematrix',
  },
};
<commit_msg>Rename. Will add all the others later as I have lots of changes on this branch<commit_after>/**
 * All azerite essences go in here.
 * You need to do this manually, usually an easy way to do this is by opening a WCL report and clicking the icons of spells to open the relevant Wowhead pages, here you can get the icon name by clicking the icon, copy the name of the spell and the ID is in the URL.
 * You can access these entries like other entries in the spells files by importing `common/SPELLS` and using the assigned property on the SPELLS object. Please try to avoid abbreviating properties.
 */

 // Trait IDs
  //   2 = "Azeroth's Undying Gift"
  //   3 = "Sphere of Suppression"
  //   4 = "Worldvein Resonance"
  //   5 = "Essence of the Focusing Iris"
  //   6 = "Purification Protocol"
  //   7 = "Anima of Life and Death"
  //  12 = "The Crucible of Flame"
  //  13 = "Nullification Dynamo"
  //  14 = "Condensed Life-Force"
  //  15 = "Ripple in Space"
  //  17 = "The Ever-Rising Tide"
  //  18 = "Artifice of Time"
  //  19 = "The Well of Existence"
  //  20 = "Life-Binder's Invocation"
  //  21 = "Vitality Conduit"
  //  22 = "Vision of Perfection"
  //  23 = "Blood of the Enemy"
  //  25 = "Aegis of the Deep"
  //  27 = "Memory of Lucid Dreams"
  //  28 = "The Unbound Force"
  //  32 = "Conflict and Strife"

export default {
  //The Ever-Rising Tide
  EVER_RISING_TIDE: {
    id: 299879,
    traitId: 17,
    name: 'The Ever-Rising Tide',
    icon: 'inv_elemental_mote_mana',
  },
  EVER_RISING_TIDE_MAJOR: {
    id: 299876,
    name: 'Overcharge Mana',
    icon: 'spell_azerite_essence09',
  },
  EVER_RISING_TIDE_HEALING_BUFF: {
    id: 299624,
    name: 'Overcharge Mana',
    icon: 'spell_azerite_essence09',
  },
  EVER_RISING_TIDE_CHARGING_BUFF: {
    id: 296072,
    name: 'Overcharge Mana',
    icon: 'spell_azerite_essence09',
  },
  EVER_RISING_TIDE_STAT_BUFF: {
    id: 296059,
    name: 'The Ever-Rising Tide',
    icon: 'inv_elemental_mote_mana',
  },
  EVER_RISING_TIDE_ENERGIZE: {
    id: 296065,
    name: 'The Ever-Rising Tide',
    icon: 'inv_elemental_mote_mana',
  },
  WELL_OF_EXISTENCE: {
    id: 299936,
    traitId: 19,
    name: 'The Well Of Existence',
    icon: 'creatureportrait_twilightshammer_summoningportal_water01',
  },
  WELL_OF_EXISTENCE_MAJOR: {
    id: 299933,
    name: 'Refreshment',
    icon: 'creatureportrait_twilightshammer_summoningportal_water01',
  },
  WELL_OF_EXISTENCE_MAJOR_ABILITY: {
    id: 296197,
    name: 'Refreshment',
    icon: 'creatureportrait_twilightshammer_summoningportal_water01',
  },
  WELL_OF_EXISTENCE_DOUBLE_ABSORBTION: {
    id: 296193,
    name: 'The Well of Existence',
    icon: 'creatureportrait_twilightshammer_summoningportal_water01',
  },
  WELL_OF_EXISTENCE_HEAL: {
    id: 296184,
    name: 'The Well of Existence',
    icon: 'creatureportrait_twilightshammer_summoningportal_water01',
  },
  //The Crucible of Flame
  CONCENTRATED_FLAME: {
    id: 299349,
    name: 'Concentrated Flame',
    icon: 'spell_azerite_essence_15',
  },
  ANCIENT_FLAME: {
    id: 299348,
    traitId: 12,
    name: 'Ancient Flame',
    icon: 'inv_radientazeritematrix',
  },
  CONCENTRATED_FLAME_CAST: {
    id: 295373,
    name: 'Concentrated Flame',
    icon: 'spell_azerite_essence_15',
  },
  CONCENTRATED_FLAME_CAST_HEAL: {
    id: 295375,
    name: 'Concentrated Flame',
    icon: 'spell_azerite_essence_15',
  },
  CONCENTRATED_FLAME_CAST_DAMAGE: {
    id: 295374,
    name: 'Concentrated Flame',
    icon: 'spell_azerite_essence_15',
  },
  CONCENTRATED_FLAME_DOT_DAMAGE: {
    id: 295368,
    name: 'Concentrated Flame',
    icon: 'spell_azerite_essence_15',
  },
  ANCIENT_FLAME_DOT_HEAL: {
    id: 303380,
    name: 'Ancient Flame',
    icon: 'spell_azerite_essence_15',
  },
  ANCIENT_FLAME_DOT_DAMAGE: {
    id: 295367,
    name: 'Ancient Flame',
    icon: 'spell_azerite_essence_15',
  },
//general conflict (the major part of conflict and stife)
  CONFLICT: {
    id: 304088,
    traitId: 32,
    name: 'Conflict',
    icon: 'spell_azerite_essence_17',
  },
  //Worldvein Resonance
  WORLDVEIN_RESONANCE: {
    id: 295186,
    traitId: 4,
    name: 'Worldvein Resonance',
    icon: 'inv_misc_azerite_01',
  },
  LIFEBLOOD_BUFF: {
    // buff given when stood near shards with the minor essence, stacking up to 4
    id: 295137,
    name: 'Lifeblood',
    icon: 'inv_glowingazeritespire',
  },
  LIFEBLOOD_SHARD_SUMMON: {
    // id for 'summon' event of shards being created, either randomly from the minor or in response to activating the major
    id: 295114,
    name: 'Lifeblood Shard',
    icon: 'inv_radientazeritematrix',
  },
  NULL_DYNAMO: {
    traitId: 13,
    // major cast
    id: 295746,
    name: 'Nullification Dynamo',
    icon: 'spell_azerite_essence_16',
  },
  NULL_DYNAMO_SHIELD_MINOR: {
    id: 295842,
    name: 'Null Barrier',
    icon: 'spell_azerite_essence_16',
  },
  NULL_DYNAMO_DAMAGE: {
    id: 296061,
    name: 'Null Barrier',
    icon: 'spell_azerite_essence_16',
  },
  // Memory of Lucid Dreams
  LUCID_DREAMS: {
    id: 299374,
    traitId: 27,
    name: 'Memory of Lucid Dreams',
    icon: 'spell_azerite_essence05',
  },
  LUCID_DREAMS_MAJOR: {
    id: 298357,
    name: 'Memory of Lucid Dreams',
    icon: 'spell_azerite_essence05',
  },
  LUCID_DREAMS_MINOR: {
    id: 299373,
    name: 'Lucid Dreams',
    icon: 'inv_radientazeritematrix',
  },
  LUCID_DREAMS_MINOR_STAT_BUFF: {
    id: 298343,
    name: 'Lucid Dreams',
    icon: 'spell_azerite_essence05',
  },
  LUCID_DREAMS_HEAL: {
    id: 298318,
    name: 'Lucid Dreams',
    icon: 'inv_radientazeritematrix',
  },
  LUCID_DREAMS_MINOR_RESOURCE_REFUND: {
    id: 298324,
    name: 'Lucid Dreams',
    icon: 'inv_radientazeritematrix',
  },
   LUCID_DREAMS_MINOR_RESOURCE_REFUND_RAGE : {
    id: 298321,
    name: 'Lucid Dreams',
    icon: 'inv_radientazeritematrix',
  },
  //Vision of Perfection
  VISION_OF_PERFECTION: {
    id: 296325,
    traitId: 22,
    name: 'Vision of Perfection',
    icon: 'spell_azerite_essence_18',
  },
  VISION_OF_PERFECTION_HASTE_CALC: {
    id: 303342,
    name: 'Vision of Perfection',
    icon: 'spell_azerite_essence_18',
  },
  VISION_OF_PERFECTION_HASTE_BUFF_SELF: {
    id: 303344,
    name: 'Vision of Perfection',
    icon: 'spell_azerite_essence_18',
  },
  VISION_OF_PERFECTION_HASTE_BUFF_OTHERS: {
    id: 303345,
    name: 'Vision of Perfection',
    icon: 'spell_azerite_essence_18',
  },
  STRIVE_FOR_PERFECTION: {
    id: 296320,
    name: 'Strive for Perfection',
    icon: 'inv_radientazeritematrix',
  },
  STRIVE_FOR_PERFECTION_HEAL: { // 5% heal on activation
    id: 296330,
    name: 'Strive for Perfection',
    icon: 'spell_azerite_essence_18',
  },
  STRIVE_FOR_PERFECTION_VERSATILITY: {
    id: 296322,
    name: 'Strive for Perfection',
    icon: 'inv_radientazeritematrix',
  },
};
<|endoftext|>


<commit_before>/*
 R/a/dio API handler
 */

var common = require('../common'),
	hook =require('../util/hooks').hook,
	OK = require('./okyaku'),
	request = require('request');

var json,
	songMap = [
		[/Girls,? Be Ambitious/i, 'Joe'],
		[/Super Special/i, 'Super Special'],
		[/Turning Japanese/i, '\u540D\u7121\u3057'],
		[/Make a Man Out of You|Be a Man/i, 'Cute Little Girl']
	];

exports.name = null;

function parse(main) {
	let data = {
		np: main.np || '',
		listeners: main.listeners,
		dj: main.dj && main.dj.djname
	};
	// Stringify new object, so it can be compared to the old one
	let newJSON = JSON.stringify(data);
	if (newJSON != json) {
		json = newJSON;
		// Push new radio info to clients
		OK.push([0, common.RADIO, json]);
	}

	// Test song name against regex
	let name;
	for (let i = 0, l = songMap.length; i < l; i++) {
		let song = songMap[i];
		if (!song[0].test(data.np))
			continue;
		// Assign name replacement
		name = song[1];
		break;
	}

	// Build song queue
	var queue = '';
	for (let i = 0, l = main.queue.length; i < l; i++) {
		if (i > 0)
			queue += ' / ';
		queue +=  main.queue[i].meta;
	}

	exports.name = name;
	exports.queue = queue;
}

// Send r/a/dio banner on client sync
hook('clientSynced', function(info, cb) {
	info.client.send([0, common.RADIO, json]);
	cb(null);
});

// Send data to client on request
OK.dispatcher[common.RADIO] = function(msg, client) {
	client.send([0, common.RADIO, json]);
	return true;
};

function fetch() {
	// Query r/a/dio API
	request.get({
			url: 'https://r-a-d.io/api',
			json: true
		},
		function (err, resp, json) {
			if (err || resp.statusCode != 200 || !json || !json.main) {
				exports.name = exports.queue = null;
				return again();
			}
			parse(json.main);
			again();
		}
	);
}

function again() {
	setTimeout(fetch, 10000);
}

fetch();
<commit_msg>Fix: r/a/dio banner not updating after prolonged operation<commit_after>/*
 R/a/dio API handler
 */

var common = require('../common'),
	hook =require('../util/hooks').hook,
	OK = require('./okyaku'),
	request = require('request');

var json,
	songMap = [
		[/Girls,? Be Ambitious/i, 'Joe'],
		[/Super Special/i, 'Super Special'],
		[/Turning Japanese/i, '\u540D\u7121\u3057'],
		[/Make a Man Out of You|Be a Man/i, 'Cute Little Girl']
	];

exports.name = null;

function parse(main) {
	let data = {
		np: main.np || '',
		listeners: main.listeners,
		dj: main.dj && main.dj.djname
	};
	// Stringify new object, so it can be compared to the old one
	let newJSON = JSON.stringify(data);
	if (newJSON != json) {
		json = newJSON;
		// Push new radio info to clients
		OK.push([0, common.RADIO, json]);
	}

	// Test song name against regex
	let name;
	for (let i = 0, l = songMap.length; i < l; i++) {
		let song = songMap[i];
		if (!song[0].test(data.np))
			continue;
		// Assign name replacement
		name = song[1];
		break;
	}

	// Build song queue
	var queue = '';
	for (let i = 0, l = main.queue.length; i < l; i++) {
		if (i > 0)
			queue += ' / ';
		queue +=  main.queue[i].meta;
	}

	exports.name = name;
	exports.queue = queue;
}

// Send r/a/dio banner on client sync
hook('clientSynced', function(info, cb) {
	info.client.send([0, common.RADIO, json]);
	cb(null);
});

// Send data to client on request
OK.dispatcher[common.RADIO] = function(msg, client) {
	client.send([0, common.RADIO, json]);
	return true;
};

function fetch() {
	// Query r/a/dio API
	request.get({
			url: 'https://r-a-d.io/api',
			json: true
		},
		function (err, resp, json) {
			if (err || resp.statusCode != 200 || !json || !json.main)
				return exports.name = exports.queue = null;
			parse(json.main);
		}
	);
}
fetch()
setInterval(fetch, 10000)
<|endoftext|>


<commit_before>'use strict';

describe('io.dennis.contextmenu', function() {
  var mock = angular.mock;
  var ae = angular.element;

  describe('directive:contextmenu', function() {
    var $injector, $compile, $rootScope;
    var $scope;

    beforeEach(mock.module('io.dennis.contextmenu'));
    beforeEach(inject(function(_$injector_, _$compile_, _$rootScope_) {
      $injector = _$injector_;
      $compile = _$compile_;
      $rootScope = _$rootScope_;

      var configService = $injector.get('$contextmenu');
      configService.set('DEBOUNCE_BROADCAST_TIME', 0);

      $scope = $rootScope.$new();
    }));

    it('should register $window.(click|contextmenu|scroll)', function() {
      var $window = $injector.get('$window');
      var $windowElementStub = jasmine.createSpyObj('we', ['on']);

      var onEventFn;
      $windowElementStub.on.and.callFake(function(event, fn) {
        onEventFn = fn;
      });

      spyOn(angular, 'element').and.callFake(function(_element) {
        return (_element === $window) ?
          $windowElementStub : ae(_element);
      });

      var html = '

';
      var element = angular.element(html);
      $compile(element)($scope);

      expect($windowElementStub.on).toHaveBeenCalledWith(
        'contextmenu scroll click', jasmine.any(Function)
      );
    });

    it('should broadcast contextmenu.close on <event>', function(done) {
      var $window = $injector.get('$window');
      var $windowElementStub = jasmine.createSpyObj('we', ['on']);

      $windowElementStub.on.and.callFake(onRegisterEvent);

      spyOn(angular, 'element').and.callFake(function(_element) {
        return (_element === $window) ?
          $windowElementStub : ae(_element);
      });

      var broadcastSpy = spyOn($rootScope, '$broadcast');

      var html = '

';
      var element = angular.element(html);
      $compile(element)($scope);

      function onRegisterEvent(event, fn) {
        fn();
        expect(broadcastSpy).toHaveBeenCalledWith('contextmenu.close');
        done();
      }

    });

    it('should be able to register two independant menus', function() {
      var html = '

';
      html += '

';

      var element = angular.element(html);
      $compile(element)($scope);

      $rootScope.$apply();
      expect($scope.some.menu).not.toEqual($scope.other.menu);
    });

    it('should close other menus before opening current', function(done) {
      var html = '

';
      html += '

';

      var element = angular.element(html);
      var compiled = $compile(element)($scope);
      $rootScope.$apply();

      var first = ae(compiled['0']);
      var second = ae(compiled['1']);

      first.controller('contextmenu').open();

      setTimeout(function() {
        second.controller('contextmenu').open();

        expect(first.hasClass('ng-hide')).toEqual(true);
        done();
      }, 1);
    });

  });

});
<commit_msg>test(contextmenu): remove angular.element spy after test<commit_after>'use strict';

describe('io.dennis.contextmenu', function() {
  var mock = angular.mock;
  var ae = angular.element;

  describe('directive:contextmenu', function() {
    var $injector, $compile, $rootScope;
    var $scope;
    var elementSpy;

    beforeEach(mock.module('io.dennis.contextmenu'));
    beforeEach(inject(function(_$injector_, _$compile_, _$rootScope_) {
      $injector = _$injector_;
      $compile = _$compile_;
      $rootScope = _$rootScope_;

      var configService = $injector.get('$contextmenu');
      configService.set('DEBOUNCE_BROADCAST_TIME', 0);

      $scope = $rootScope.$new();
    }));

    describe('with angular.element() spy', function() {

      beforeEach(function() {
        elementSpy = spyOn(angular, 'element');
      });

      afterEach(function() {
        elementSpy.and.callThrough();
      });

      it('should register $window.(click|contextmenu|scroll)', function() {
        var $window = $injector.get('$window');
        var $windowElementStub = jasmine.createSpyObj('we', ['on']);

        var onEventFn;
        $windowElementStub.on.and.callFake(function(event, fn) {
          onEventFn = fn;
        });

        elementSpy.and.callFake(function(_element) {
          return (_element === $window) ?
            $windowElementStub : ae(_element);
        });

        var html = '

';
        var element = angular.element(html);
        $compile(element)($scope);

        expect($windowElementStub.on).toHaveBeenCalledWith(
          'click contextmenu scroll', jasmine.any(Function)
        );

      });

      it('should broadcast contextmenu.close on <event>', function(done) {
        var $window = $injector.get('$window');
        var $windowElementStub = jasmine.createSpyObj('we', ['on']);

        $windowElementStub.on.and.callFake(onRegisterEvent);

        elementSpy.and.callFake(function(_element) {
          return (_element === $window) ?
            $windowElementStub : ae(_element);
        });

        var broadcastSpy = spyOn($rootScope, '$broadcast');

        var html = '

';
        var element = angular.element(html);
        $compile(element)($scope);

        function onRegisterEvent(event, fn) {
          fn();
          expect(broadcastSpy).toHaveBeenCalledWith('contextmenu.close');
          done();
        }

      });

    });

    it('should be able to register two independant menus', function() {
      var html = '

';
      html += '

';

      var element = angular.element(html);
      $compile(element)($scope);

      $rootScope.$apply();
      expect($scope.some.menu).not.toEqual($scope.other.menu);
    });

    it('should close other menus before opening current', function(done) {
      var html = '

';
      html += '

';

      var element = angular.element(html);
      var compiled = $compile(element)($scope);
      $rootScope.$apply();

      var first = ae(compiled['0']);
      var second = ae(compiled['1']);

      first.controller('contextmenu').open();

      setTimeout(function() {
        second.controller('contextmenu').open();

        expect(first.hasClass('ng-hide')).toEqual(true);
        done();
      }, 1);
    });

  });

});
<|endoftext|>


<commit_before>/*
    The Conflict of Interest (COI) module of Kuali Research
    Copyright © 2005-2016 Kuali, Inc.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

import {getUserInfo, getAuthToken} from '../services/auth-service/auth-service';
import Log from '../log';
import {UNAUTHORIZED} from '../../http-status-codes';

export default function authentication(req, res, next) {
  let authToken = getAuthToken(req.headers.authorization);
  if (req.url.startsWith('/coi/files') && req.method === 'GET' && !authToken) {
    authToken = req.cookies.authToken;
  }

  getUserInfo(req.dbInfo, req.hostname, authToken)
    .then(userInfo => {
      if (!userInfo) {
        let ipAddresses;
        if (req.ips.length > 0) {
          ipAddresses = req.ips.toString();
        } else {
          ipAddresses = req.ip;
        }
        Log.error(`unauthenticated request from ${ipAddresses}`, req);
        res.sendStatus(UNAUTHORIZED);
      } else {
        req.userInfo = userInfo;
        next();
      }
    }).catch(err => {
      Log.error(err);
      next(err);
    });
}
<commit_msg>Don't flag unauthenticated requests as errors<commit_after>/*
    The Conflict of Interest (COI) module of Kuali Research
    Copyright © 2005-2016 Kuali, Inc.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as
    published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

import {getUserInfo, getAuthToken} from '../services/auth-service/auth-service';
import Log from '../log';
import {UNAUTHORIZED} from '../../http-status-codes';

export default function authentication(req, res, next) {
  let authToken = getAuthToken(req.headers.authorization);
  if (req.url.startsWith('/coi/files') && req.method === 'GET' && !authToken) {
    authToken = req.cookies.authToken;
  }

  getUserInfo(req.dbInfo, req.hostname, authToken)
    .then(userInfo => {
      if (!userInfo) {
        let ipAddresses;
        if (req.ips.length > 0) {
          ipAddresses = req.ips.toString();
        } else {
          ipAddresses = req.ip;
        }
        Log.warn(`unauthenticated request from ${ipAddresses}`, req);
        res.sendStatus(UNAUTHORIZED);
      } else {
        req.userInfo = userInfo;
        next();
      }
    }).catch(err => {
      Log.error(err);
      next(err);
    });
}
<|endoftext|>


<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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';

KylinApp.controller('CubeMeasuresCtrl', function ($scope, $modal,MetaModel,cubesManager,CubeDescModel,SweetAlert) {
  $scope.num=0;
  $scope.convertedColumns=[];
  $scope.groupby=[];
  $scope.initUpdateMeasureStatus = function(){
    $scope.updateMeasureStatus = {
      isEdit:false,
      editIndex:-1
    }
  };
  $scope.initUpdateMeasureStatus();

  $scope.addNewMeasure = function (measure, index) {
    if(measure&&index>=0){
      $scope.updateMeasureStatus.isEdit = true;
      $scope.updateMeasureStatus.editIndex = index;
    }
    $scope.nextParameters = [];
    $scope.newMeasure = (!!measure)? jQuery.extend(true, {},measure):CubeDescModel.createMeasure();
    if(!!measure && measure.function.parameter.next_parameter){
      $scope.nextPara.value = measure.function.parameter.next_parameter.value;
    }
    if($scope.newMeasure.function.expression=="TOP_N"){
      $scope.convertedColumns=[];
      for(var configuration in $scope.newMeasure.function.configuration) {
        var _name=configuration.slice(14);
        var item=$scope.newMeasure.function.configuration[configuration];
        var _isFixedLength = item.substring(0,12) === "fixed_length"?"true":"false";//fixed_length:12
        var _isIntLength = item.substring(0,3) === "int"?"true":"false";//fixed_length:12
        var _encoding = item;
        var _valueLength = 0 ;
        if(_isFixedLength !=="false"){
          _valueLength = item.substring(13,item.length);
          _encoding = "fixed_length";
        }
        if(_isIntLength!="false"){
          _valueLength = item.substring(4,item.length);
          _encoding = "int";
        }
        $scope.GroupBy = {
          name:_name,
          encoding:_encoding,
          valueLength:_valueLength,
        }
        $scope.convertedColumns.push($scope.GroupBy);
      };
    }
  };

  $scope.updateNextParameter = function(){
    for(var i= 0;i<$scope.nextParameters.length-1;i++){
      $scope.nextParameters[i].next_parameter=$scope.nextParameters[i+1];
    }
    $scope.newMeasure.function.parameter.next_parameter = $scope.nextParameters[0];
    console.log($scope.newMeasure.function.parameter);
  }

  $scope.editNextParameter = function(parameter){
    $scope.openParameterModal(parameter);

  }

  $scope.addNextParameter = function(){
    $scope.openParameterModal();
  }

  $scope.removeParameter = function(parameters,index){
    if(index>-1){
      parameters.splice(index,1);
    }
    $scope.updateNextParameter();
  }

  $scope.nextPara = {
    "type":"column",
    "value":"",
    "next_parameter":null
  }

  $scope.openParameterModal = function (parameter) {
    $modal.open({
      templateUrl: 'nextParameter.html',
      controller: NextParameterModalCtrl,
      resolve: {
        scope: function () {
          return $scope;
        },
        para:function(){
          return parameter;
        }
      }
    });
  };
  $scope.nextParameters =[];

  $scope.removeElement = function (arr, element) {
    var index = arr.indexOf(element);
    if (index > -1) {
      arr.splice(index, 1);
    }
  };

  $scope.clearNewMeasure = function () {
    $scope.newMeasure = null;
    $scope.initUpdateMeasureStatus();
    $scope.nextParameterInit();
  };

  $scope.saveNewMeasure = function () {

    if ($scope.newMeasure.function.expression === 'TOP_N' ) {
      if($scope.newMeasure.function.parameter.value == ""){
        SweetAlert.swal('', '[TOP_N] ORDER|SUM by Column  is required', 'warning');
        return false;
      }
      if($scope.convertedColumns.length<1){
        SweetAlert.swal('', '[TOP_N] Group by Column is required', 'warning');
        return false;
      }

      var hasExisted = [];

      for (var column in $scope.convertedColumns){
        if(hasExisted.indexOf($scope.convertedColumns[column].name)==-1){
          hasExisted.push($scope.convertedColumns[column].name);
        }
        else{
          SweetAlert.swal('', 'The column named ['+$scope.convertedColumns[column].name+'] already exits.', 'warning');
          return false;
        }
        if ($scope.convertedColumns[column].encoding == 'int' && ($scope.convertedColumns[column].valueLength < 1 || $scope.convertedColumns[column].valueLength > 8)) {
          SweetAlert.swal('', 'int encoding column length should between 1 and 8.', 'warning');
          return false;
        }
      }
        $scope.nextPara.next_parameter={};
        $scope.newMeasure.function.configuration={};
        $scope.groupby($scope.nextPara);
        angular.forEach($scope.convertedColumns,function(item){
          var a='topn.encoding.'+item.name;
          var encoding="";
          if(item.encoding!=="dict" && item.encoding!=="date"&& item.encoding!=="time"){
            if(item.encoding=="fixed_length" && item.valueLength){
              encoding = "fixed_length:"+item.valueLength;
            }
            else if(item.encoding=="int" && item.valueLength){
              encoding = "int:"+item.valueLength;
            }else{
              encoding = item.encoding;
            }
          }else{
            encoding = item.encoding;
            item.valueLength=0;
          }
          $scope.newMeasure.function.configuration[a]=encoding;
          });
    }

    if ($scope.isNameDuplicated($scope.cubeMetaFrame.measures, $scope.newMeasure) == true) {
      SweetAlert.swal('', 'The measure name: ' + $scope.newMeasure.name + ' is duplicated', 'warning');
      return false;
    }

    if($scope.nextPara.value!=="" && ($scope.newMeasure.function.expression == 'EXTENDED_COLUMN' || $scope.newMeasure.function.expression == 'TOP_N')){
      $scope.newMeasure.function.parameter.next_parameter = jQuery.extend(true,{},$scope.nextPara);
    }

    if($scope.updateMeasureStatus.isEdit == true){
      $scope.cubeMetaFrame.measures[$scope.updateMeasureStatus.editIndex] = $scope.newMeasure;
    }
    else {
      $scope.cubeMetaFrame.measures.push($scope.newMeasure);
    }

    $scope.newMeasure = null;
    $scope.initUpdateMeasureStatus();
    $scope.nextParameterInit();
    return true;
  };

  $scope.isNameDuplicated = function (measures, newMeasure) {
    var names = [];
    for(var i = 0;i < measures.length; i++){
        names.push(measures[i].name);
    }
    var index = names.indexOf(newMeasure.name);
    return index > -1;
  }

  $scope.nextParameterInit = function(){
    $scope.nextPara = {
      "type":"column",
      "value":"",
      "next_parameter":{}
    }
    if($scope.newMeasure){
      $scope.newMeasure.function.parameter.next_parameter = null;
    }
  }

  $scope.addNewGroupByColumn = function () {
    $scope.nextGroupBy = {
      name:null,
      encoding:"dict",
      valueLength:0,
    }
    $scope.convertedColumns.push($scope.nextGroupBy);

  };

  $scope.removeColumn = function(arr,index){
    if (index > -1) {
      arr.splice(index, 1);
    }
  };
  $scope.refreshGroupBy=function (list,index,item) {
    var encoding;
    var name = item.name;
    if(item.encoding=="dict" || item.encoding=="date"|| item.encoding=="time"){
      item.valueLength=0;
    }
  };

  $scope.groupby= function (next_parameter){
    if($scope.num<$scope.convertedColumns.length-1){
      next_parameter.value=$scope.convertedColumns[$scope.num].name;
      next_parameter.type="column";
      next_parameter.next_parameter={};
      $scope.num++;
      $scope.groupby(next_parameter.next_parameter);
    }
    else{
      next_parameter.value=$scope.convertedColumns[$scope.num].name;
      next_parameter.type="column";
      next_parameter.next_parameter=null;
      $scope.num=0;
      return false;
    }
  }
  $scope.converted = function (next_parameter) {
    if (next_parameter != null) {
      $scope.groupby.push(next_parameter.value);
      converted(next_parameter.next_parameter)
    }
    else {
      $scope.groupby.push(next_parameter.value);
      return false;
    }
  }
  //map right return type for param
  $scope.measureReturnTypeUpdate = function(){

    if($scope.newMeasure.function.expression == 'TOP_N'){
      $scope.convertedColumns=[];
      $scope.newMeasure.function.parameter.type= 'column';
      $scope.newMeasure.function.returntype = "topn(100)";
      return;
    }else if($scope.newMeasure.function.expression == 'EXTENDED_COLUMN'){
      $scope.newMeasure.function.parameter.type= 'column';
      $scope.newMeasure.function.returntype = "extendedcolumn(100)";
      return;
    }else{
      $scope.nextParameterInit();
    }

    if($scope.newMeasure.function.expression == 'COUNT'){
      $scope.newMeasure.function.parameter.type= 'constant';
    }

    if($scope.newMeasure.function.parameter.type=="constant"&&$scope.newMeasure.function.expression!=="COUNT_DISTINCT"){
      switch($scope.newMeasure.function.expression){
        case "SUM":
        case "COUNT":
          $scope.newMeasure.function.returntype = "bigint";
          break;
        default:
          $scope.newMeasure.function.returntype = "";
          break;
      }
    }
    if($scope.newMeasure.function.parameter.type=="column"&&$scope.newMeasure.function.expression!=="COUNT_DISTINCT"){

      var column = $scope.newMeasure.function.parameter.value;
      var colType = $scope.getColumnType(column, $scope.metaModel.model.fact_table); // $scope.getColumnType defined in cubeEdit.js

      if(colType==""||!colType){
        $scope.newMeasure.function.returntype = "";
        return;
      }

      switch($scope.newMeasure.function.expression){
        case "SUM":
          if(colType==="smallint"||colType==="int"||colType==="bigint"||colType==="integer"){
            $scope.newMeasure.function.returntype= 'bigint';
          }else{
            if(colType.indexOf('decimal')!=-1){
              $scope.newMeasure.function.returntype= colType;
            }else{
              $scope.newMeasure.function.returntype= 'decimal';
            }
          }
          break;
        case "MIN":
        case "MAX":
          $scope.newMeasure.function.returntype = colType;
          break;
        case "RAW":
          $scope.newMeasure.function.returntype = "raw";
          break;
        case "COUNT":
          $scope.newMeasure.function.returntype = "bigint";
          break;
        default:
          $scope.newMeasure.function.returntype = "";
          break;
      }
    }
  }

});

var NextParameterModalCtrl = function ($scope, scope,para,$modalInstance,cubeConfig, CubeService, MessageService, $location, SweetAlert,ProjectModel, loadingRequest,ModelService) {

  $scope.newmea={
    "measure":scope.newMeasure
  }

  $scope.cubeConfig = cubeConfig;
  $scope.cancel = function () {
    $modalInstance.dismiss('cancel');
  };

  $scope.getCommonMetricColumns = function(measure){
    return scope.getCommonMetricColumns(measure);
  }

  $scope.nextPara = {
    "type":"",
    "value":"",
    "next_parameter":null
  }

  var _index = scope.nextParameters.indexOf(para);
  if(para){
    $scope.nextPara = para;
  }

  $scope.ok = function(){
    if(_index!=-1){
      scope.nextParameters[_index] = $scope.nextPara;
    }
    else{
      scope.nextParameters.push($scope.nextPara);
    }

    scope.updateNextParameter();
    $modalInstance.dismiss('cancel');
  }

}
<commit_msg>KYLIN-1965: bug fix<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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';

KylinApp.controller('CubeMeasuresCtrl', function ($scope, $modal,MetaModel,cubesManager,CubeDescModel,SweetAlert) {
  $scope.num=0;
  $scope.convertedColumns=[];
  $scope.groupby=[];
  $scope.initUpdateMeasureStatus = function(){
    $scope.updateMeasureStatus = {
      isEdit:false,
      editIndex:-1
    }
  };
  $scope.initUpdateMeasureStatus();

  $scope.addNewMeasure = function (measure, index) {
    if(measure&&index>=0){
      $scope.updateMeasureStatus.isEdit = true;
      $scope.updateMeasureStatus.editIndex = index;
    }
    $scope.nextParameters = [];
    $scope.newMeasure = (!!measure)? jQuery.extend(true, {},measure):CubeDescModel.createMeasure();
    if(!!measure && measure.function.parameter.next_parameter){
      $scope.nextPara.value = measure.function.parameter.next_parameter.value;
    }
    if($scope.newMeasure.function.expression=="TOP_N"){
      $scope.convertedColumns=[];
      for(var configuration in $scope.newMeasure.function.configuration) {
        var _name=configuration.slice(14);
        var item=$scope.newMeasure.function.configuration[configuration];
        var _isFixedLength = item.substring(0,12) === "fixed_length"?"true":"false";//fixed_length:12
        var _isIntLength = item.substring(0,3) === "int"?"true":"false";//fixed_length:12
        var _encoding = item;
        var _valueLength = 0 ;
        if(_isFixedLength !=="false"){
          _valueLength = item.substring(13,item.length);
          _encoding = "fixed_length";
        }
        if(_isIntLength!="false"){
          _valueLength = item.substring(4,item.length);
          _encoding = "int";
        }
        $scope.GroupBy = {
          name:_name,
          encoding:_encoding,
          valueLength:_valueLength,
        }
        $scope.convertedColumns.push($scope.GroupBy);
      };
    }
  };

  $scope.updateNextParameter = function(){
    for(var i= 0;i<$scope.nextParameters.length-1;i++){
      $scope.nextParameters[i].next_parameter=$scope.nextParameters[i+1];
    }
    $scope.newMeasure.function.parameter.next_parameter = $scope.nextParameters[0];
    console.log($scope.newMeasure.function.parameter);
  }

  $scope.editNextParameter = function(parameter){
    $scope.openParameterModal(parameter);

  }

  $scope.addNextParameter = function(){
    $scope.openParameterModal();
  }

  $scope.removeParameter = function(parameters,index){
    if(index>-1){
      parameters.splice(index,1);
    }
    $scope.updateNextParameter();
  }

  $scope.nextPara = {
    "type":"column",
    "value":"",
    "next_parameter":null
  }

  $scope.openParameterModal = function (parameter) {
    $modal.open({
      templateUrl: 'nextParameter.html',
      controller: NextParameterModalCtrl,
      resolve: {
        scope: function () {
          return $scope;
        },
        para:function(){
          return parameter;
        }
      }
    });
  };
  $scope.nextParameters =[];

  $scope.removeElement = function (arr, element) {
    var index = arr.indexOf(element);
    if (index > -1) {
      arr.splice(index, 1);
    }
  };

  $scope.clearNewMeasure = function () {
    $scope.newMeasure = null;
    $scope.initUpdateMeasureStatus();
    $scope.nextParameterInit();
  };

  $scope.saveNewMeasure = function () {

    if ($scope.newMeasure.function.expression === 'TOP_N' ) {
      if($scope.newMeasure.function.parameter.value == ""){
        SweetAlert.swal('', '[TOP_N] ORDER|SUM by Column  is required', 'warning');
        return false;
      }
      if($scope.convertedColumns.length<1){
        SweetAlert.swal('', '[TOP_N] Group by Column is required', 'warning');
        return false;
      }

      var hasExisted = [];

      for (var column in $scope.convertedColumns){
        if(hasExisted.indexOf($scope.convertedColumns[column].name)==-1){
          hasExisted.push($scope.convertedColumns[column].name);
        }
        else{
          SweetAlert.swal('', 'The column named ['+$scope.convertedColumns[column].name+'] already exits.', 'warning');
          return false;
        }
        if ($scope.convertedColumns[column].encoding == 'int' && ($scope.convertedColumns[column].valueLength < 1 || $scope.convertedColumns[column].valueLength > 8)) {
          SweetAlert.swal('', 'int encoding column length should between 1 and 8.', 'warning');
          return false;
        }
      }
        $scope.nextPara.next_parameter={};
        $scope.newMeasure.function.configuration={};
        $scope.groupby($scope.nextPara);
        angular.forEach($scope.convertedColumns,function(item){
          var a='topn.encoding.'+item.name;
          var encoding="";
          if(item.encoding!=="dict" && item.encoding!=="date"&& item.encoding!=="time"){
            if(item.encoding=="fixed_length" && item.valueLength){
              encoding = "fixed_length:"+item.valueLength;
            }
            else if(item.encoding=="int" && item.valueLength){
              encoding = "int:"+item.valueLength;
            }else{
              encoding = item.encoding;
            }
          }else{
            encoding = item.encoding;
            item.valueLength=0;
          }
          $scope.newMeasure.function.configuration[a]=encoding;
          });
    }

    if ($scope.isNameDuplicated($scope.cubeMetaFrame.measures, $scope.newMeasure) == true) {
      SweetAlert.swal('', 'The measure name: ' + $scope.newMeasure.name + ' is duplicated', 'warning');
      return false;
    }

    if($scope.nextPara.value!=="" && ($scope.newMeasure.function.expression == 'EXTENDED_COLUMN' || $scope.newMeasure.function.expression == 'TOP_N')){
      $scope.newMeasure.function.parameter.next_parameter = jQuery.extend(true,{},$scope.nextPara);
    }

    if($scope.updateMeasureStatus.isEdit == true){
      $scope.cubeMetaFrame.measures[$scope.updateMeasureStatus.editIndex] = $scope.newMeasure;
    }
    else {
      $scope.cubeMetaFrame.measures.push($scope.newMeasure);
    }

    $scope.newMeasure = null;
    $scope.initUpdateMeasureStatus();
    $scope.nextParameterInit();
    return true;
  };

  $scope.isNameDuplicated = function (measures, newMeasure) {
    var names = [];
    for(var i = 0;i < measures.length; i++){
        names.push(measures[i].name);
    }
    var index = names.indexOf(newMeasure.name);
    return (index > -1 && index != $scope.updateMeasureStatus.editIndex);
  }

  $scope.nextParameterInit = function(){
    $scope.nextPara = {
      "type":"column",
      "value":"",
      "next_parameter":{}
    }
    if($scope.newMeasure){
      $scope.newMeasure.function.parameter.next_parameter = null;
    }
  }

  $scope.addNewGroupByColumn = function () {
    $scope.nextGroupBy = {
      name:null,
      encoding:"dict",
      valueLength:0,
    }
    $scope.convertedColumns.push($scope.nextGroupBy);

  };

  $scope.removeColumn = function(arr,index){
    if (index > -1) {
      arr.splice(index, 1);
    }
  };
  $scope.refreshGroupBy=function (list,index,item) {
    var encoding;
    var name = item.name;
    if(item.encoding=="dict" || item.encoding=="date"|| item.encoding=="time"){
      item.valueLength=0;
    }
  };

  $scope.groupby= function (next_parameter){
    if($scope.num<$scope.convertedColumns.length-1){
      next_parameter.value=$scope.convertedColumns[$scope.num].name;
      next_parameter.type="column";
      next_parameter.next_parameter={};
      $scope.num++;
      $scope.groupby(next_parameter.next_parameter);
    }
    else{
      next_parameter.value=$scope.convertedColumns[$scope.num].name;
      next_parameter.type="column";
      next_parameter.next_parameter=null;
      $scope.num=0;
      return false;
    }
  }
  $scope.converted = function (next_parameter) {
    if (next_parameter != null) {
      $scope.groupby.push(next_parameter.value);
      converted(next_parameter.next_parameter)
    }
    else {
      $scope.groupby.push(next_parameter.value);
      return false;
    }
  }
  //map right return type for param
  $scope.measureReturnTypeUpdate = function(){

    if($scope.newMeasure.function.expression == 'TOP_N'){
      $scope.convertedColumns=[];
      $scope.newMeasure.function.parameter.type= 'column';
      $scope.newMeasure.function.returntype = "topn(100)";
      return;
    }else if($scope.newMeasure.function.expression == 'EXTENDED_COLUMN'){
      $scope.newMeasure.function.parameter.type= 'column';
      $scope.newMeasure.function.returntype = "extendedcolumn(100)";
      return;
    }else{
      $scope.nextParameterInit();
    }

    if($scope.newMeasure.function.expression == 'COUNT'){
      $scope.newMeasure.function.parameter.type= 'constant';
    }

    if($scope.newMeasure.function.parameter.type=="constant"&&$scope.newMeasure.function.expression!=="COUNT_DISTINCT"){
      switch($scope.newMeasure.function.expression){
        case "SUM":
        case "COUNT":
          $scope.newMeasure.function.returntype = "bigint";
          break;
        default:
          $scope.newMeasure.function.returntype = "";
          break;
      }
    }
    if($scope.newMeasure.function.parameter.type=="column"&&$scope.newMeasure.function.expression!=="COUNT_DISTINCT"){

      var column = $scope.newMeasure.function.parameter.value;
      var colType = $scope.getColumnType(column, $scope.metaModel.model.fact_table); // $scope.getColumnType defined in cubeEdit.js

      if(colType==""||!colType){
        $scope.newMeasure.function.returntype = "";
        return;
      }

      switch($scope.newMeasure.function.expression){
        case "SUM":
          if(colType==="smallint"||colType==="int"||colType==="bigint"||colType==="integer"){
            $scope.newMeasure.function.returntype= 'bigint';
          }else{
            if(colType.indexOf('decimal')!=-1){
              $scope.newMeasure.function.returntype= colType;
            }else{
              $scope.newMeasure.function.returntype= 'decimal';
            }
          }
          break;
        case "MIN":
        case "MAX":
          $scope.newMeasure.function.returntype = colType;
          break;
        case "RAW":
          $scope.newMeasure.function.returntype = "raw";
          break;
        case "COUNT":
          $scope.newMeasure.function.returntype = "bigint";
          break;
        default:
          $scope.newMeasure.function.returntype = "";
          break;
      }
    }
  }

});

var NextParameterModalCtrl = function ($scope, scope,para,$modalInstance,cubeConfig, CubeService, MessageService, $location, SweetAlert,ProjectModel, loadingRequest,ModelService) {

  $scope.newmea={
    "measure":scope.newMeasure
  }

  $scope.cubeConfig = cubeConfig;
  $scope.cancel = function () {
    $modalInstance.dismiss('cancel');
  };

  $scope.getCommonMetricColumns = function(measure){
    return scope.getCommonMetricColumns(measure);
  }

  $scope.nextPara = {
    "type":"",
    "value":"",
    "next_parameter":null
  }

  var _index = scope.nextParameters.indexOf(para);
  if(para){
    $scope.nextPara = para;
  }

  $scope.ok = function(){
    if(_index!=-1){
      scope.nextParameters[_index] = $scope.nextPara;
    }
    else{
      scope.nextParameters.push($scope.nextPara);
    }

    scope.updateNextParameter();
    $modalInstance.dismiss('cancel');
  }

}
<|endoftext|>


<commit_before>import { connect } from 'react-redux'
import { navigate, updateSettings } from '../../actions.js'
import { setViewportWidth } from '../actions.js'
import BaseStyle from '../base-style.js'
import debounce from 'lodash.debounce'
import getViewportWidth from '../get-viewport-width.js'
import Horizontal from '../../blocks/horizontal.js'
import Knocking from '../../blocks/knocking.js'
import React, { Component } from 'react'
import Route from '../../route.js'
import Vertical from '../../blocks/vertical.js'

const DEBOUNCE = 250
const LOADING_SIZE = 100
const LOADING_OFFSET = LOADING_SIZE / -2
const REBOUND = 500

export class LaunchpadRuntime extends Component {
  componentDidMount() {
    window.addEventListener('resize', this.setViewportWidth, false)
    window.addEventListener('orientationchange', this.setViewportWidth, false)
    document.addEventListener('visibilitychange', this.onVisibilityChange)

    this.ensureDefault()
  }

  componentDidUpdate(prevProps) {
    const { props } = this

    if (props.focusPanel) {
      window.document.title = props.focusPanel.title || props.focusPanel.type
    }

    this.ensureDefault()
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.setViewportWidth)
    window.removeEventListener('orientationchange', this.setViewportWidth)
  }

  ensureDefault() {
    const { props } = this
    if (props.launchpadPanel && !props.mainPanel) {
      props.navigate(`${props.launchpadRoute.context}/${props.launchpadPanel.default}`)
    }
  }

  onVisibilityChange = () => {
    if (document.visibilityState === 'hidden') {
      window.removeEventListener('resize', this.setViewportWidth)
      window.removeEventListener('orientationchange', this.setViewportWidth)
    } else {
      setTimeout(() => {
        window.addEventListener('resize', this.setViewportWidth, false)
        window.addEventListener('orientationchange', this.setViewportWidth, false)
      }, REBOUND)
    }
  }

  render() {
    const {
      dockedApp, dockedPanel, dockedRoute, dockedRouteIndex,
      mainApp, mainPanel, mainRoute,
      launchpadApp, launchpadPanel, launchpadRoute,
      navigate,
      router,
      runtime,
      updateSettings
    } = this.props

    const docked = dockedPanel && (
      <Route
        key={`docked-${dockedPanel.dockLeft ? 'left' : 'right'}`}
        navigate={navigate}
        panel={dockedPanel}
        route={dockedRoute}
        routeIndex={dockedRouteIndex}
        router={router}
        store={dockedApp.store}
        Type={dockedApp.types[dockedPanel.type]}
        updateSettings={updateSettings}
        width={dockedRoute.width}
      />
    )

    const mainWidth = runtime.viewportWidth - ((dockedRoute && dockedRoute.width) || 0)

    return (
      <Vertical
        ref={$e => this.$runtime = $e}
        style={{
          height: '100%',
          overflow: 'hidden',
          width: '100%'
        }}
      >
        <BaseStyle />

        {launchpadPanel && (
          <Route
            navigate={navigate}
            panel={launchpadPanel}
            route={launchpadRoute}
            routeIndex={0}
            router={router}
            store={launchpadApp.store}
            style={{
              height: launchpadPanel.height || 0,
              ...launchpadPanel.style
            }}
            Type={launchpadApp.types[launchpadPanel.type]}
            updateSettings={updateSettings}
            width={runtime.viewportWidth}
          />
        )}

        <Horizontal
          style={{
            flexDirection: 'row',
            height: `calc(100% - ${(launchpadPanel && launchpadPanel.height) || 0}px)`,
            overflow: 'hidden',
            width: '100vw'
          }}
        >
          {dockedPanel && dockedPanel.dockLeft && docked}

          {mainPanel && (
            <Route
              key={'main'}
              navigate={navigate}
              panel={mainPanel}
              route={mainRoute}
              routeIndex={1}
              router={router}
              store={mainApp.store}
              Type={mainApp.types[mainPanel.type]}
              updateSettings={updateSettings}
              width={mainWidth}
            />
          )}

          {dockedPanel && !dockedPanel.dockLeft && docked}
        </Horizontal>

        {router.isLoading ? (
          <Vertical
            style={{
              justifyContent: 'center',
              position: 'absolute',
              left: LOADING_OFFSET,
              top: LOADING_OFFSET
            }}
          >
            <Knocking size={LOADING_SIZE} />
          </Vertical>
          ) : null}
      </Vertical>
    )
  }

  setViewportWidth = debounce(() => this.props.setViewportWidth(getViewportWidth()), DEBOUNCE)
}

function mapStateToProps({ apps, panels, runtime, router }, props) {
  const launchpadRoute = router.routes.byContext[
    router.routes.items[0]
  ]
  const mainRoute = router.routes.byContext[
    router.routes.items[1]
  ]
  let dockedRoute
  let dockedRouteIndex
  if (router.routes.items.length > 2) {
    dockedRouteIndex = router.routes.items.length - 1
    dockedRoute = router.routes.byContext[
      router.routes.items[dockedRouteIndex]
    ]
  }

  const dockedPanel = dockedRoute && panels.byId[dockedRoute.panelId]
  const mainPanel = mainRoute && panels.byId[mainRoute.panelId]

  return {
    dockedApp: dockedRoute && apps.byName[dockedRoute.app],
    dockedPanel,
    dockedRoute,
    dockedRouteIndex,

    focusPanel: dockedPanel || mainPanel, // <-- TODO will change with what's open & focused, e.g. TOC

    mainApp: mainRoute && apps.byName[mainRoute.app],
    mainPanel,
    mainRoute,

    launchpadApp: launchpadRoute && apps.byName[launchpadRoute.app],
    launchpadPanel: launchpadRoute && panels.byId[launchpadRoute.panelId],
    launchpadRoute,

    router,
    runtime
  }
}

const mapDispatchToProps = {
  navigate,
  setViewportWidth,
  updateSettings
}
export default connect(mapStateToProps, mapDispatchToProps)(LaunchpadRuntime)
<commit_msg>fix: change launchpad wrapper width on visibility change<commit_after>import { connect } from 'react-redux'
import { navigate, updateSettings } from '../../actions.js'
import { setViewportWidth } from '../actions.js'
import BaseStyle from '../base-style.js'
import debounce from 'lodash.debounce'
import getViewportWidth from '../get-viewport-width.js'
import Horizontal from '../../blocks/horizontal.js'
import Knocking from '../../blocks/knocking.js'
import React, { Component } from 'react'
import Route from '../../route.js'
import Vertical from '../../blocks/vertical.js'

const DEBOUNCE = 250
const LOADING_SIZE = 100
const LOADING_OFFSET = LOADING_SIZE / -2
const REBOUND = 500

export class LaunchpadRuntime extends Component {
  componentDidMount() {
    window.addEventListener('resize', this.setViewportWidth, false)
    window.addEventListener('orientationchange', this.setViewportWidth, false)
    document.addEventListener('visibilitychange', this.onVisibilityChange)

    this.ensureDefault()
  }

  componentDidUpdate(prevProps) {
    const { props } = this

    if (props.focusPanel) {
      window.document.title = props.focusPanel.title || props.focusPanel.type
    }

    this.ensureDefault()
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.setViewportWidth)
    window.removeEventListener('orientationchange', this.setViewportWidth)
  }

  ensureDefault() {
    const { props } = this
    if (props.launchpadPanel && !props.mainPanel) {
      props.navigate(`${props.launchpadRoute.context}/${props.launchpadPanel.default}`)
    }
  }

  onVisibilityChange = () => {
    if (document.visibilityState === 'hidden') {
      window.removeEventListener('resize', this.setViewportWidth)
      window.removeEventListener('orientationchange', this.setViewportWidth)
    } else {
      setTimeout(() => {
        this.setViewportWidth()
        window.addEventListener('resize', this.setViewportWidth, false)
        window.addEventListener('orientationchange', this.setViewportWidth, false)
      }, REBOUND)
    }
  }

  render() {
    const {
      dockedApp, dockedPanel, dockedRoute, dockedRouteIndex,
      mainApp, mainPanel, mainRoute,
      launchpadApp, launchpadPanel, launchpadRoute,
      navigate,
      router,
      runtime,
      updateSettings
    } = this.props

    const docked = dockedPanel && (
      <Route
        key={`docked-${dockedPanel.dockLeft ? 'left' : 'right'}`}
        navigate={navigate}
        panel={dockedPanel}
        route={dockedRoute}
        routeIndex={dockedRouteIndex}
        router={router}
        store={dockedApp.store}
        Type={dockedApp.types[dockedPanel.type]}
        updateSettings={updateSettings}
        width={dockedRoute.width}
      />
    )

    const mainWidth = runtime.viewportWidth - ((dockedRoute && dockedRoute.width) || 0)

    return (
      <Vertical
        ref={$e => this.$runtime = $e}
        style={{
          height: '100%',
          overflow: 'hidden',
          width: '100%'
        }}
      >
        <BaseStyle />

        {launchpadPanel && (
          <Route
            navigate={navigate}
            panel={launchpadPanel}
            route={launchpadRoute}
            routeIndex={0}
            router={router}
            store={launchpadApp.store}
            style={{
              height: launchpadPanel.height || 0,
              ...launchpadPanel.style
            }}
            Type={launchpadApp.types[launchpadPanel.type]}
            updateSettings={updateSettings}
            width={runtime.viewportWidth}
          />
        )}

        <Horizontal
          style={{
            flexDirection: 'row',
            height: `calc(100% - ${(launchpadPanel && launchpadPanel.height) || 0}px)`,
            overflow: 'hidden',
            width: '100vw'
          }}
        >
          {dockedPanel && dockedPanel.dockLeft && docked}

          {mainPanel && (
            <Route
              key={'main'}
              navigate={navigate}
              panel={mainPanel}
              route={mainRoute}
              routeIndex={1}
              router={router}
              store={mainApp.store}
              Type={mainApp.types[mainPanel.type]}
              updateSettings={updateSettings}
              width={mainWidth}
            />
          )}

          {dockedPanel && !dockedPanel.dockLeft && docked}
        </Horizontal>

        {router.isLoading ? (
          <Vertical
            style={{
              justifyContent: 'center',
              position: 'absolute',
              left: LOADING_OFFSET,
              top: LOADING_OFFSET
            }}
          >
            <Knocking size={LOADING_SIZE} />
          </Vertical>
          ) : null}
      </Vertical>
    )
  }

  setViewportWidth = debounce(() => this.props.setViewportWidth(getViewportWidth()), DEBOUNCE)
}

function mapStateToProps({ apps, panels, runtime, router }, props) {
  const launchpadRoute = router.routes.byContext[
    router.routes.items[0]
  ]
  const mainRoute = router.routes.byContext[
    router.routes.items[1]
  ]
  let dockedRoute
  let dockedRouteIndex
  if (router.routes.items.length > 2) {
    dockedRouteIndex = router.routes.items.length - 1
    dockedRoute = router.routes.byContext[
      router.routes.items[dockedRouteIndex]
    ]
  }

  const dockedPanel = dockedRoute && panels.byId[dockedRoute.panelId]
  const mainPanel = mainRoute && panels.byId[mainRoute.panelId]

  return {
    dockedApp: dockedRoute && apps.byName[dockedRoute.app],
    dockedPanel,
    dockedRoute,
    dockedRouteIndex,

    focusPanel: dockedPanel || mainPanel, // <-- TODO will change with what's open & focused, e.g. TOC

    mainApp: mainRoute && apps.byName[mainRoute.app],
    mainPanel,
    mainRoute,

    launchpadApp: launchpadRoute && apps.byName[launchpadRoute.app],
    launchpadPanel: launchpadRoute && panels.byId[launchpadRoute.panelId],
    launchpadRoute,

    router,
    runtime
  }
}

const mapDispatchToProps = {
  navigate,
  setViewportWidth,
  updateSettings
}
export default connect(mapStateToProps, mapDispatchToProps)(LaunchpadRuntime)
<|endoftext|>


<commit_before>let message = require('focus-core').message;
let changeBehaviourMixin = {
    /**
     * Display a message when there is a change on a store property resulting from a component action call.
     * @param  {object} changeInfos - An object containing all the event informations, without the data.
     * @return {function} - An override function can be called.
     */
    _displayMessageOnChange: function displayMessageOnChange(changeInfos) {
        if (this.displayMessageOnChange) {
            return this.displayMessageOnChange(changeInfos);
        }
        if (changeInfos && changeInfos.status && changeInfos.status.name) {
            switch (changeInfos.status.name) {
                /* case 'loading':
                 Focus.message.addInformationMessage('detail.loading');
                 break;
                 case 'loaded':
                 Focus.message.addSuccessMessage('detail.loaded');
                 break;
                 case 'saving':
                 Focus.message.addInformationMessage('detail.saving');
                 break;*/
                case 'saved':
                    //Maybe the action result or the event should have a caller notion.
                    message.addSuccessMessage('detail.saved');
                    //Change the page mode as edit
                    this.setState({isEdit: false});
                    break;
                default:
                    break;
            }
        }
    },
    /**
     * After change informations.
     * You can override this method using afterChange function.
     * @param {object} changeInfos - All informations relative to the change.
     * @returns {undefined} -  The return value is the callback.
     */
    _afterChange: function afterChangeForm(changeInfos) {
        if (this.afterChange) {
            return this.afterChange(changeInfos);
        }
        //If there is no callerId in the event, the display message does not have any sens.
        //Other component responding to the store property change does not need to react on it.
        if(changeInfos && changeInfos.informations && changeInfos.informations.callerId && this._identifier === changeInfos.informations.callerId){
            return this._displayMessageOnChange(changeInfos);
        }

    },
    /**
     * Event handler for 'change' events coming from the stores
     * @param {object} changeInfos - The changing informations.
     */
    _onChange: function onFormStoreChangeHandler(changeInfos) {
        let onChange = this.props.onChange || this.onChange;
        if (onChange) {
            onChange.call(this, changeInfos);
        }
        this.setState(this._getStateFromStores(), () => this._afterChange(changeInfos));
    },
    /**
     * Event handler for 'error' events coming from the stores.
     */
    _onError: function onFormErrorHandler(changeInfos) {
        const errorState = this._getErrorStateFromStores();
        for (let key in errorState) {
            if (this.refs[key]) {
                this.refs[key].setError(errorState[key]);
            }
        }
    },
    /**
     * Read
     * @param  {[type]} changeInfos [description]
     * @return {[type]}             [description]
     */
    _onStatus(changeInfos) {
        if (this._getEntity) {
            this.setState({...this._getEntity(), ...this._getLoadingStateFromStores()});
        } else {
            this.setState(this._getLoadingStateFromStores());
        }

    }
};
module.exports = changeBehaviourMixin;
<commit_msg>[store change] Update status after error on a node<commit_after>let message = require('focus-core').message;
let changeBehaviourMixin = {
    /**
     * Display a message when there is a change on a store property resulting from a component action call.
     * @param  {object} changeInfos - An object containing all the event informations, without the data.
     * @return {function} - An override function can be called.
     */
    _displayMessageOnChange: function displayMessageOnChange(changeInfos) {
        if (this.displayMessageOnChange) {
            return this.displayMessageOnChange(changeInfos);
        }
        if (changeInfos && changeInfos.status && changeInfos.status.name) {
            switch (changeInfos.status.name) {
                /* case 'loading':
                 Focus.message.addInformationMessage('detail.loading');
                 break;
                 case 'loaded':
                 Focus.message.addSuccessMessage('detail.loaded');
                 break;
                 case 'saving':
                 Focus.message.addInformationMessage('detail.saving');
                 break;*/
                case 'saved':
                    //Maybe the action result or the event should have a caller notion.
                    message.addSuccessMessage('detail.saved');
                    //Change the page mode as edit
                    this.setState({isEdit: false});
                    break;
                default:
                    break;
            }
        }
    },
    /**
     * After change informations.
     * You can override this method using afterChange function.
     * @param {object} changeInfos - All informations relative to the change.
     * @returns {undefined} -  The return value is the callback.
     */
    _afterChange: function afterChangeForm(changeInfos) {
        if (this.afterChange) {
            return this.afterChange(changeInfos);
        }
        //If there is no callerId in the event, the display message does not have any sens.
        //Other component responding to the store property change does not need to react on it.
        if(changeInfos && changeInfos.informations && changeInfos.informations.callerId && this._identifier === changeInfos.informations.callerId){
            return this._displayMessageOnChange(changeInfos);
        }

    },
    /**
     * Event handler for 'change' events coming from the stores
     * @param {object} changeInfos - The changing informations.
     */
    _onChange: function onFormStoreChangeHandler(changeInfos) {
        let onChange = this.props.onChange || this.onChange;
        if (onChange) {
            onChange.call(this, changeInfos);
        }
        this.setState(this._getStateFromStores(), () => this._afterChange(changeInfos));
    },
    /**
     * Event handler for 'error' events coming from the stores.
     */
    _onError: function onFormErrorHandler(changeInfos) {
        const errorState = this._getErrorStateFromStores();
        for (let key in errorState) {
            if (this.refs[key]) {
                this.refs[key].setError(errorState[key]);
            }
        }
        this.setState(this._getLoadingStateFromStores()); // update status after errors
    },
    /**
     * Read
     * @param  {[type]} changeInfos [description]
     * @return {[type]}             [description]
     */
    _onStatus(changeInfos) {
        if (this._getEntity) {
            this.setState({...this._getEntity(), ...this._getLoadingStateFromStores()});
        } else {
            this.setState(this._getLoadingStateFromStores());
        }

    }
};
module.exports = changeBehaviourMixin;
<|endoftext|>


<commit_before>var formSubmitActions = {};

formSubmitActions.sendEmailTo = {
	description: {
		label: "Send email to",
		methodName: "formSubmitActions.sendEmailTo",
		parameters: [
			{
				label: "Recipient",
				type: "text"
			},
			{
				label: "Subject",
				type: "text"
			},
			{
				label: "Body",
				type: "textarea"
			}
		]
	},
	method: function (form, recipient, subject, body) {
		var templateSettings = {
			interpolate: /\{\{(.+?)\}\}/g
		};
		if (/[@;]/g.test(recipient)) {
			throw new Meteor.Error("Bad request");
		}
		var email = {
			from: "form@pscanf.com",
			to: recipient + "@pscanf.com",
			subject: subject,
			text: _.template(body, form, templateSetting)
		};
		Email.send(email);
	}
};

Meteor.methods({
	// Define all methods
	"formSubmitActions.sendEmailTo": formSubmitActions.sendEmailTo.method,
	// API for listing available actions
	getFormSubmitActions: function () {
		return _.map(formSubmitActions, function (action) {
			return action.description;
		});
	}
});
<commit_msg>Fixed typo<commit_after>var formSubmitActions = {};

formSubmitActions.sendEmailTo = {
	description: {
		label: "Send email to",
		methodName: "formSubmitActions.sendEmailTo",
		parameters: [
			{
				label: "Recipient",
				type: "text"
			},
			{
				label: "Subject",
				type: "text"
			},
			{
				label: "Body",
				type: "textarea"
			}
		]
	},
	method: function (form, recipient, subject, body) {
		var templateSettings = {
			interpolate: /\{\{(.+?)\}\}/g
		};
		if (/[@;]/g.test(recipient)) {
			throw new Meteor.Error("Bad request");
		}
		var email = {
			from: "form@pscanf.com",
			to: recipient + "@pscanf.com",
			subject: subject,
			text: _.template(body, form, templateSettings)
		};
		Email.send(email);
	}
};

Meteor.methods({
	// Define all methods
	"formSubmitActions.sendEmailTo": formSubmitActions.sendEmailTo.method,
	// API for listing available actions
	getFormSubmitActions: function () {
		return _.map(formSubmitActions, function (action) {
			return action.description;
		});
	}
});
<|endoftext|>


<commit_before>// @flow

import * as React from 'react';
import get from 'lodash/get';
import isEqual from 'lodash/isEqual';
import classNames from 'classnames';
import createChainedFunction from './utils/createChainedFunction';
import prefix, { globalKey } from './utils/prefix';
import DropdownMenuItem from './DropdownMenuItem';
import Icon from './Icon';
import ReactChildren from './utils/ReactChildren';

type Props = {
  activeKey?: any,
  className?: string,
  children?: React.ChildrenArray<any>,
  classPrefix?: string,
  pullRight?: boolean,
  onSelect?: Function,
  title?: React.Node,
  direction?: 'left' | 'right'
}

class DorpdownMenu extends React.Component<Props> {
  static displayName = 'DropdownMenu';
  static defaultProps = {
    classPrefix: `${globalKey}dropdown-menu`
  }

  addPrefix(name: string) {
    const { classPrefix } = this.props;
    return prefix(classPrefix)(name);
  }
  isActive(props: Object, activeKey: any) {
    if (
      props.active ||
      (activeKey !== null && isEqual(props.eventKey, activeKey))
    ) {
      return true;
    }

    if (ReactChildren.some(props.children, child => (
      this.isActive(child.props, activeKey)
    ))) {
      return true;
    }

    return props.active;
  }

  renderMenuItems(children?: React.ChildrenArray<any>): React.ChildrenArray<any> {

    const { activeKey, onSelect, title } = this.props;
    const items = React.Children.map(children, (item, index) => {
      let displayName = get(item, ['type', 'displayName']);

      if (displayName === 'DropdownMenuItem' || displayName === 'NavItem') {
        let { onSelect: onItemSelect } = item.props;
        return React.cloneElement(item, {
          key: index,
          active: this.isActive(item.props, activeKey),
          onSelect: createChainedFunction(onSelect, onItemSelect)
        });
      } else if (displayName === 'DropdownMenu') {
        return (
          <DropdownMenuItem
            active={this.isActive(item.props, activeKey)}
            componentClass="div"
            submenu
          >

              {title}
              <Icon icon="angle-right" />

              {this.renderMenuItems(item.props.children)}

          </DropdownMenuItem>
        );
      }
      return item;
    });

    return items;
  }
  render() {
    const {
      pullRight,
      children,
      className,
      onSelect,
      classPrefix,
      activeKey,
      ...props
    } = this.props;

    const classes = classNames(classPrefix, {
      [this.addPrefix('right')]: pullRight
    }, className);

    const items = this.renderMenuItems(children);

    return (

        {items}

    );
  }
}

export default DorpdownMenu;
<commit_msg>Update DropdownMenu<commit_after>// @flow

import * as React from 'react';
import get from 'lodash/get';
import isEqual from 'lodash/isEqual';
import classNames from 'classnames';
import createChainedFunction from './utils/createChainedFunction';
import prefix, { globalKey } from './utils/prefix';
import DropdownMenuItem from './DropdownMenuItem';
import Icon from './Icon';
import ReactChildren from './utils/ReactChildren';

type Props = {
  activeKey?: any,
  className?: string,
  children?: React.ChildrenArray<any>,
  classPrefix?: string,
  pullLeft?: boolean,
  onSelect?: Function,
  title?: React.Node
}

class DorpdownMenu extends React.Component<Props> {
  static displayName = 'DropdownMenu';
  static defaultProps = {
    classPrefix: `${globalKey}dropdown-menu`
  }

  addPrefix(name: string) {
    const { classPrefix } = this.props;
    return prefix(classPrefix)(name);
  }
  isActive(props: Object, activeKey: any) {
    if (
      props.active ||
      (activeKey !== null && isEqual(props.eventKey, activeKey))
    ) {
      return true;
    }

    if (ReactChildren.some(props.children, child => (
      this.isActive(child.props, activeKey)
    ))) {
      return true;
    }

    return props.active;
  }

  renderMenuItems(children?: React.ChildrenArray<any>): React.ChildrenArray<any> {

    const { activeKey, onSelect } = this.props;
    const items = React.Children.map(children, (item, index) => {
      let displayName = get(item, ['type', 'displayName']);

      if (displayName === 'DropdownMenuItem' || displayName === 'NavItem') {
        let { onSelect: onItemSelect } = item.props;
        return React.cloneElement(item, {
          key: index,
          active: this.isActive(item.props, activeKey),
          onSelect: createChainedFunction(onSelect, onItemSelect)
        });
      } else if (displayName === 'DropdownMenu') {

        return (
          <DropdownMenuItem
            active={this.isActive(item.props, activeKey)}
            componentClass="div"
            className={this.addPrefix(`pull-${item.props.pullLeft ? 'left' : 'right'}`)}
            submenu
          >

              {item.props.title}
              <Icon icon="angle-right" />

              {this.renderMenuItems(item.props.children)}

          </DropdownMenuItem>
        );
      }
      return item;
    });

    return items;
  }
  render() {
    const {
      pullLeft,
      children,
      className,
      onSelect,
      classPrefix,
      activeKey,
      ...props
    } = this.props;

    const classes = classNames(classPrefix, className);
    const items = this.renderMenuItems(children);

    return (

        {items}

    );
  }
}

export default DorpdownMenu;
<|endoftext|>