<commit_before>
const debug = require(`debug`)(`tooling:version`);
const _ = require(`lodash`);
const {getDistTag} = require(`./npm`);
const {list} = require(`./package`);
const {exec} = require(`./async`);
const {read, write} = require(`../util/package`);
const updated = require(`./updated`);
const git = require(`./git`);

/* eslint-disable complexity */

/**
 * Determines the latest published package version for the repo
 * @returns {Promise<string>}
 */
exports.last = async function last() {
  const packages = Array.from(await list());
  const version = _(await Promise.all(packages
    // TODO stop omitting eslint config once it's fully removed from the repo
    .filter((p) => p !== `@ciscospark/eslint-config`)
    .map(getDistTag)))
    .sort()
    .map((v) => v.trim())
    // TODO stop omitting v2 packages once the last once is unpublished
    .filter((v) => !v.startsWith(`2.`))
    .last()
    .trim();

  const tag = `v${version}`;
  debug(`last version tag is ${version}`);
  return tag;
};

/**
 * Determines the next appropriate version to publish
 * @returns {Promise<string>}
 */
exports.next = async function next({always}) {
  const version = await checkLastCommit();
  if (version) {
    debug(`found ${version} in last commit message`);
    return version.replace(`v`, ``);
  }

  const currentVersion = (await exports.last()).replace(`v`, ``);
  debug(`current versoin is ${currentVersion}`);

  if (await hasBreakingChange()) {
    debug(`detected breaking changes`);
    return increment(`major`, currentVersion);
  }

  debug(`no breaking changes detected`);

  const type = await getChangeType();
  if (!type) {
    debug(`no changes to make`);
    if (always) {
      const nextVersion = increment(`patch`, currentVersion);
      debug(`next version is ${version}`);
      return nextVersion;
    }
    return currentVersion;
  }

  const nextVersion = increment(type, currentVersion);
  debug(`next version is ${version}`);
  return nextVersion;
};

exports.set = async function set(version, {all, lastLog}) {
  if (lastLog) {
    const log = await git.lastLog();
    if (log.includes(`#force-publish`)) {
      all = true;
    }
  }

  // reminder, can't destructure updated because it's a circular dependency
  const packages = Array.from(all ? await list() : await updated.updated({dependents: true}))
    .filter((p) => ![`docs`, `legacy`, `tooling`].includes(p));

  if (packages.length === 0) {
    // eslint-disable-next-line no-console
    console.info(`no packages to update`);
    return;
  }

  for (const packageName of packages) {
    debug(`updating ${packageName} to ${version}`);
    try {
      const pkg = await read(packageName);
      debug(`${packageName} was at ${pkg.version}`);
      pkg.version = version;
      await write(packageName, pkg);
    }
    catch (err) {
      if (err.code !== `ENOENT`) {
        throw err;
      }
    }
  }
};

/**
 * Determines if the last commit specified an explicit version to set
 * @returns {Promise<string>}
 */
async function checkLastCommit() {
  debug(`checking if the last commit message has explicit release instructions`);
  const summary = await git.lastLog();
  const re = /^#release v(.+?)(\s.+)?$/;
  const match = summary.match(re);
  if (match) {
    const version = match[1];
    if (version) {
      return version;
    }
  }
  return undefined;
}

/**
 * Determines if there are any breaking changes between HEAD and upstream/master
 * @returns {boolean}
 */
async function hasBreakingChange() {
  debug(`checking for breaking changes between HEAD and upstream/master`);
  const bodies = await exec(`git log upstream/master.. --format=%b`);
  if (/^BREAKING CHANGE:/.test(bodies)) {
    debug(`found breaking change`);
    return true;
  }
  debug(`no breaking changes detected`);
  return false;
}

/**
 * Checks commit messages to determine change type
 * @returns {Promise<boolean>}
 */
async function getChangeType() {
  const subjects = await exec(`git log upstream/master.. --format=%s`);
  for (const subject of subjects.split(`\n`)) {
    if (subject.startsWith(`feat`)) {
      return `minor`;
    }

    if (subject.startsWith(`fix`) || subject.startsWith(`perf`) || subject.startsWith(`refactor`)) {
      return `patch`;
    }
  }

  return undefined;
}

/* eslint-disable complexity */
/**
 * Increments a semver
 * @param {string} type
 * @param {string} version
 * @returns {string}
 */
function increment(type, version) {
  let [major, minor, patch] = version
    .replace(`v`, ``)
    .split(`.`)
    .map((v) => parseInt(v, 10));

  if (major === 0) {
    switch (type) {
    case `major`:
      minor += 1;
      patch = 0;
      break;
    case `minor`:
      patch += 1;
      break;
    case `patch`:
      patch += 1;
      break;
    default:
      throw new Error(`unrecognized change type`);
    }
  }
  else {
    switch (type) {
    case `major`:
      major += 1;
      minor = 0;
      patch = 0;
      break;
    case `minor`:
      minor += 1;
      patch = 0;
      break;
    case `patch`:
      patch += 1;
      break;
    default:
      throw new Error(`unrecognized change type`);
    }
  }

  return `${major}.${minor}.${patch}`;
}
<commit_msg>chore(tooling): handle packages without disttags<commit_after>
const debug = require(`debug`)(`tooling:version`);
const _ = require(`lodash`);
const {getDistTag} = require(`./npm`);
const {list} = require(`./package`);
const {exec} = require(`./async`);
const {read, write} = require(`../util/package`);
const updated = require(`./updated`);
const git = require(`./git`);

/* eslint-disable complexity */

/**
 * Determines the latest published package version for the repo
 * @returns {Promise<string>}
 */
exports.last = async function last() {
  const packages = Array.from(await list());
  const version = _(await Promise.all(packages
    // TODO stop omitting eslint config once it's fully removed from the repo
    .filter((p) => p !== `@ciscospark/eslint-config`)
    .map(getDistTag)))
    .sort()
    .filter()
    .map((v) => v.trim())
    // TODO stop omitting v2 packages once the last once is unpublished
    .filter((v) => !v.startsWith(`2.`))
    .last()
    .trim();

  const tag = `v${version}`;
  debug(`last version tag is ${version}`);
  return tag;
};

/**
 * Determines the next appropriate version to publish
 * @returns {Promise<string>}
 */
exports.next = async function next({always}) {
  const version = await checkLastCommit();
  if (version) {
    debug(`found ${version} in last commit message`);
    return version.replace(`v`, ``);
  }

  const currentVersion = (await exports.last()).replace(`v`, ``);
  debug(`current versoin is ${currentVersion}`);

  if (await hasBreakingChange()) {
    debug(`detected breaking changes`);
    return increment(`major`, currentVersion);
  }

  debug(`no breaking changes detected`);

  const type = await getChangeType();
  if (!type) {
    debug(`no changes to make`);
    if (always) {
      const nextVersion = increment(`patch`, currentVersion);
      debug(`next version is ${version}`);
      return nextVersion;
    }
    return currentVersion;
  }

  const nextVersion = increment(type, currentVersion);
  debug(`next version is ${version}`);
  return nextVersion;
};

exports.set = async function set(version, {all, lastLog}) {
  if (lastLog) {
    const log = await git.lastLog();
    if (log.includes(`#force-publish`)) {
      all = true;
    }
  }

  // reminder, can't destructure updated because it's a circular dependency
  const packages = Array.from(all ? await list() : await updated.updated({dependents: true}))
    .filter((p) => ![`docs`, `legacy`, `tooling`].includes(p));

  if (packages.length === 0) {
    // eslint-disable-next-line no-console
    console.info(`no packages to update`);
    return;
  }

  for (const packageName of packages) {
    debug(`updating ${packageName} to ${version}`);
    try {
      const pkg = await read(packageName);
      debug(`${packageName} was at ${pkg.version}`);
      pkg.version = version;
      await write(packageName, pkg);
    }
    catch (err) {
      if (err.code !== `ENOENT`) {
        throw err;
      }
    }
  }
};

/**
 * Determines if the last commit specified an explicit version to set
 * @returns {Promise<string>}
 */
async function checkLastCommit() {
  debug(`checking if the last commit message has explicit release instructions`);
  const summary = await git.lastLog();
  const re = /^#release v(.+?)(\s.+)?$/;
  const match = summary.match(re);
  if (match) {
    const version = match[1];
    if (version) {
      return version;
    }
  }
  return undefined;
}

/**
 * Determines if there are any breaking changes between HEAD and upstream/master
 * @returns {boolean}
 */
async function hasBreakingChange() {
  debug(`checking for breaking changes between HEAD and upstream/master`);
  const bodies = await exec(`git log upstream/master.. --format=%b`);
  if (/^BREAKING CHANGE:/.test(bodies)) {
    debug(`found breaking change`);
    return true;
  }
  debug(`no breaking changes detected`);
  return false;
}

/**
 * Checks commit messages to determine change type
 * @returns {Promise<boolean>}
 */
async function getChangeType() {
  const subjects = await exec(`git log upstream/master.. --format=%s`);
  for (const subject of subjects.split(`\n`)) {
    if (subject.startsWith(`feat`)) {
      return `minor`;
    }

    if (subject.startsWith(`fix`) || subject.startsWith(`perf`) || subject.startsWith(`refactor`)) {
      return `patch`;
    }
  }

  return undefined;
}

/* eslint-disable complexity */
/**
 * Increments a semver
 * @param {string} type
 * @param {string} version
 * @returns {string}
 */
function increment(type, version) {
  let [major, minor, patch] = version
    .replace(`v`, ``)
    .split(`.`)
    .map((v) => parseInt(v, 10));

  if (major === 0) {
    switch (type) {
    case `major`:
      minor += 1;
      patch = 0;
      break;
    case `minor`:
      patch += 1;
      break;
    case `patch`:
      patch += 1;
      break;
    default:
      throw new Error(`unrecognized change type`);
    }
  }
  else {
    switch (type) {
    case `major`:
      major += 1;
      minor = 0;
      patch = 0;
      break;
    case `minor`:
      minor += 1;
      patch = 0;
      break;
    case `patch`:
      patch += 1;
      break;
    default:
      throw new Error(`unrecognized change type`);
    }
  }

  return `${major}.${minor}.${patch}`;
}
<|endoftext|>


<commit_before>var redis = require('../libs/redis-client');
var approvedEnvironment = require('../libs/schemas/validator').approvedEnvironment;
var models = require('../models');
var fs = require('fs');
var log = require('czagenda-log').from(__filename);
var path = require('path');

var SCHEMAS = [{
	"name" : "base",
	"final" : false,
	"file" : "base-abstract.json"
}, {
	"name" : "who",
	"final" : false,
	"file" : "who-abstract.json"
}, {
	"name" : "who",
	"final" : true,
	"file" : "who.json"
}, {
	"name" : "geo",
	"final" : false,
	"file" : "geo-abstract.json"
}, {
	"name" : "geo",
	"final" : true,
	"file" : "geo.json"
}, {
	"name" : "localization",
	"final" : false,
	"file" : "localization-abstract.json"
}, {
	"name" : "localization",
	"final" : true,
	"file" : "localization.json"
}, {
	"name" : "event",
	"final" : false,
	"file" : "event-abstract.json"
}, {
	"name" : "event",
	"final" : true,
	"file" : "event.json"
}, {
	"name" : "entity",
	"final" : false,
	"file" : "entity-abstract.json"
}, {
	"name" : "organization",
	"final" : false,
	"file" : "organization-abstract.json"
}, {
	"name" : "organization",
	"final" : true,
	"file" : "organization.json"
}, {
	"name" : "person",
	"final" : false,
	"file" : "person-abstract.json"
}, {
	"name" : "person",
	"final" : true,
	"file" : "person.json"
},{
	"name" : "event-europe",
	"final" : false,
	"file" : "event-europe-abstract.json"
},{
	"name" : "event-europe",
	"final" : true,
	"file" : "event-europe.json"
}]

var createSchema = function() {

	var data = SCHEMAS.shift();

	log.notice("Try to create " + data.name);

	var schema = new models.Schema();
	schema.name = data.name;
	schema.author = "/user/jh.pinson";
	schema['final'] = data['final'];
	schema.status = 'APPROVED';
	schema.schema = JSON.parse(fs.readFileSync("./schemas/" + data.file, 'utf8'));

	if (path.existsSync('./schemas/template/' + data.name + '.tpl') === true) {
		schema.template = fs.readFileSync("./schemas/template/" + data.name + '.tpl', 'utf8');
	}

	if (path.existsSync('./schemas/sample/' + data.name + '.json') === true) {
		schema.sample = fs.readFileSync("./schemas/sample/" + data.name + '.json', 'utf8');
	}

	schema.save(function(err, obj) {

		if(err === null || err instanceof models.errors.ObjectAlreadyExists) {

			if( err instanceof models.errors.ObjectAlreadyExists) {
				console.log('ObjectAlreadyExists', data.name);

			} else {
				log.notice("Succesfully created " + data.name, obj.id);
			}

			if(SCHEMAS.length > 0) {
				setTimeout(createSchema, 3000);
			} else {
				log.notice("All done");
				setTimeout(process.exit, 1000);
			}

		} else {

			 if( err instanceof models.errors.ValidationError) {
				console.log(obj.validationErrors, data.name);

			} else {
				console.log('Internal error', data.name)
			}
		}

	});
}

approvedEnvironment.load(function(err, success) {
	createSchema();
})<commit_msg>bugfix createSchema<commit_after>var redis = require('../libs/redis-client');
var approvedEnvironment = require('../libs/schemas/validator').approvedEnvironment;
var models = require('../models');
var fs = require('fs');
var log = require('czagenda-log').from(__filename);
var path = require('path');

var SCHEMAS = [{
	"name" : "base",
	"final" : false,
	"file" : "base-abstract.json"
}, {
	"name" : "who",
	"final" : false,
	"file" : "who-abstract.json"
}, {
	"name" : "who",
	"final" : true,
	"file" : "who.json"
}, {
	"name" : "geo",
	"final" : false,
	"file" : "geo-abstract.json"
}, {
	"name" : "geo",
	"final" : true,
	"file" : "geo.json"
}, {
	"name" : "localization",
	"final" : false,
	"file" : "localization-abstract.json"
}, {
	"name" : "localization",
	"final" : true,
	"file" : "localization.json"
}, {
	"name" : "event",
	"final" : false,
	"file" : "event-abstract.json"
}, {
	"name" : "event",
	"final" : true,
	"file" : "event.json"
}, {
	"name" : "entity",
	"final" : false,
	"file" : "entity-abstract.json"
}, {
	"name" : "organization",
	"final" : false,
	"file" : "organization-abstract.json"
}, {
	"name" : "organization",
	"final" : true,
	"file" : "organization.json"
}, {
	"name" : "person",
	"final" : false,
	"file" : "person-abstract.json"
}, {
	"name" : "person",
	"final" : true,
	"file" : "person.json"
},{
	"name" : "event-europe",
	"final" : false,
	"file" : "event-europe-abstract.json"
},{
	"name" : "event-europe",
	"final" : true,
	"file" : "event-europe.json"
}]

var createSchema = function() {

	var data = SCHEMAS.shift();

	log.notice("Try to create " + data.name);

	var schema = new models.Schema();
	schema.name = data.name;
	schema.author = "/user/jh.pinson";
	schema['final'] = data['final'];
	schema.status = 'APPROVED';
	schema.schema = JSON.parse(fs.readFileSync("./schemas/" + data.file, 'utf8'));

	if (path.existsSync('./schemas/template/' + data.name + '.tpl') === true) {
		schema.template = fs.readFileSync("./schemas/template/" + data.name + '.tpl', 'utf8');
	}

	if (path.existsSync('./schemas/sample/' + data.name + '.json') === true) {
		schema.sample = fs.readFileSync("./schemas/sample/" + data.name + '.json', 'utf8');
	}

	schema.save(function(err, obj) {

		if(err === null || err instanceof models.errors.ObjectAlreadyExists) {

			if( err instanceof models.errors.ObjectAlreadyExists) {
				console.log('ObjectAlreadyExists', data.name);

			} else {
				log.notice("Succesfully created " + data.name, obj.id);
			}

			if(SCHEMAS.length > 0) {
				setTimeout(createSchema, 3000);
			} else {
				log.notice("All done");
				setTimeout(function () {process.exit(1)}, 1000);
			}

		} else {

			 if( err instanceof models.errors.ValidationError) {
				console.log(obj.validationErrors, data.name);

			} else {
				console.log('Internal error', data.name)
			}
			setTimeout(function () {process.exit(0)}, 1000);
		}

	});
}

approvedEnvironment.load(function(err, success) {
	createSchema();
})<|endoftext|>


<commit_before>export default function persistState(sessionId, stateDeserializer = null, actionDeserializer = null) {
  if (!sessionId) {
    return next => (...args) => next(...args);
  }

  function deserializeState(fullState) {
    if (!fullState || typeof stateDeserializer !== 'function') {
      return fullState;
    }
    return {
      ...fullState,
      committedState: stateDeserializer(fullState.committedState),
      computedStates: fullState.computedStates.map((computedState) => {
        return {
          ...computedState,
          state: stateDeserializer(computedState.state)
        };
      })
    };
  }

  function deserializeActions(fullState) {
    if (!fullState || typeof actionDeserializer !== 'function') {
      return fullState;
    }
    return {
      ...fullState,
      stagedActions: fullState.stagedActions.map((action) => {
        return actionDeserializer(action);
      })
    };
  }

  return next => (reducer, initialState) => {
    const key = `redux-dev-session-${sessionId}`;

    let finalInitialState;
    try {
      finalInitialState = deserializeActions(deserializeState(JSON.parse(localStorage.getItem(key)))) || initialState;
      next(reducer, initialState);
    } catch (e) {
      console.warn('Could not read debug session from localStorage:', e);
      try {
        localStorage.removeItem(key);
      } finally {
        finalInitialState = undefined;
      }
    }

    const store = next(reducer, finalInitialState);

    return {
      ...store,
      dispatch(action) {
        store.dispatch(action);

        try {
          localStorage.setItem(key, JSON.stringify(store.getState()));
        } catch (e) {
          console.warn('Could not write debug session from localStorage:', e);
        }

        return action;
      }
    };
  };
}
<commit_msg>refactor deserialization in persistState<commit_after>export default function persistState(sessionId, stateDeserializer = null, actionDeserializer = null) {
  if (!sessionId) {
    return next => (...args) => next(...args);
  }

  function deserializeState(fullState) {
    return {
      ...fullState,
      committedState: stateDeserializer(fullState.committedState),
      computedStates: fullState.computedStates.map((computedState) => {
        return {
          ...computedState,
          state: stateDeserializer(computedState.state)
        };
      })
    };
  }

  function deserializeActions(fullState) {
    return {
      ...fullState,
      stagedActions: fullState.stagedActions.map((action) => {
        return actionDeserializer(action);
      })
    };
  }

  function deserialize(fullState) {
    if (!fullState) {
      return fullState;
    }
    let deserializedState = fullState;
    if (typeof stateDeserializer === 'function') {
      deserializedState = deserializeState(deserializedState);
    }
    if (typeof actionDeserializer === 'function') {
      deserializedState = deserializeActions(deserializedState);
    }
    return deserializedState;
  }

  return next => (reducer, initialState) => {
    const key = `redux-dev-session-${sessionId}`;

    let finalInitialState;
    try {
      finalInitialState = deserialize(JSON.parse(localStorage.getItem(key))) || initialState;
      next(reducer, initialState);
    } catch (e) {
      console.warn('Could not read debug session from localStorage:', e);
      try {
        localStorage.removeItem(key);
      } finally {
        finalInitialState = undefined;
      }
    }

    const store = next(reducer, finalInitialState);

    return {
      ...store,
      dispatch(action) {
        store.dispatch(action);

        try {
          localStorage.setItem(key, JSON.stringify(store.getState()));
        } catch (e) {
          console.warn('Could not write debug session from localStorage:', e);
        }

        return action;
      }
    };
  };
}
<|endoftext|>


<commit_before>/*!
 * jQuery UI Selectmenu @VERSION
 * http://jqueryui.com
 *
 * Copyright 2012 jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Selectmenu
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *	jquery.ui.position.js
 *	jquery.ui.menu.js
 */
(function( $, undefined ) {

$.widget( "ui.selectmenu", {
	version: "@VERSION",
	defaultElement: "<select>",
	options: {
		dropdown: true,
		appendTo: "body",
		position: {
			my: "left top",
			at: "left bottom",
			collision: "none"
		},
		// callbacks
		open: null,
		focus: null,
		select: null,
		close: null,
		change: null
	},

	_create: function() {
		// make / set unique id
		var selectmenuId = this.element.uniqueId().attr( 'id' );

		// array of button and menu id's
		this.ids = { id: selectmenuId, button: selectmenuId + '-button', menu: selectmenuId + '-menu' };

		this._drawButton();
		this._on( this.button, this._buttonEvents );
		this._hoverable( this.button );
		this._focusable( this.button );

		this._drawMenu();

		// document click closes menu
		this._on( document, {
			click: function( event ) {
				if ( this.isOpen && !$( event.target ).closest( "li.ui-state-disabled, li.ui-selectmenu-optgroup, #" + this.ids.button ).length ) {
					this.close( event );
				}
			}
		});

		if ( this.options.disabled ) {
			this.disable();
		}
	},

	_drawButton: function() {
		var tabindex = this.element.attr( 'tabindex' );

		// fix existing label
		this.label = $( "label[for='" + this.ids.id + "']" ).attr( "for", this.ids.button );
		// catch click event of the label
		this._on( this.label, {
			'click':  function( event ) {
				this.button.focus();
				event.preventDefault();
			}
		});

		// hide original select tag
		this.element.hide();

		// create button
		this.button = $( '', {
			'class': 'ui-button ui-widget ui-state-default ui-corner-all',
			href: '#' + this.ids.id,
			tabindex: ( tabindex ? tabindex : this.options.disabled ? -1 : 0 ),
			id: this.ids.button,
			width: this.element.outerWidth(),
			role: 'combobox',
			'aria-expanded': false,
			'aria-autocomplete': 'list',
			'aria-owns': this.ids.menu,
			'aria-haspopup': true
		});

		this.button.prepend( $( '' ) );

		this.buttonText = $( '', {
				'class': 'ui-selectmenu-text' ,
				html: this.element.find( "option:selected" ).text() || ' '
			})
			.appendTo( this.button );

		// wrap and insert new button
		this.buttonWrap = $( '', {
				'class': 'ui-selectmenu-button'
			})
			.append( this.button )
			.insertAfter( this.element );
	},

	_drawMenu: function() {
		var menuInstance,
			that = this;

		// create menu portion, append to body
		this.menu = $( '
', {
			'aria-hidden': true,
			'aria-labelledby': this.ids.button,
			id: this.ids.menu
		});

		// wrap menu
		this.menuWrap = $( '
', {
				'class': 'ui-selectmenu-menu',
				width: ( this.options.dropdown ) ? this.button.outerWidth() : this.buttonText.width() + parseFloat( this.buttonText.css( "padding-left" ) ) || 0 + parseFloat( this.buttonText.css( "margin-left" ) ) || 0
			})
			.append( this.menu )
			.appendTo( this.options.appendTo );

		// init menu widget
		menuInstance = this.menu.menu({
			select: function( event, ui ) {
				var item = ui.item.data( "ui-selectmenu-item" );

				that._select( item, event );

				if ( that.isOpen ) {
					event.preventDefault();
					that.close( event );
				}
			},
			focus: function( event, ui ) {
				var item = ui.item.data( "ui-selectmenu-item" );

				if ( that.focus !== undefined ) {
					if ( item.index !== that.focus ) {
						that._trigger( "focus", event, { item: item } );
						if ( !that.isOpen ) {
							that._select( item, event );
						}
					}
				}
				that.focus = item.index;

				// Set ARIA active decendent
				that.button.attr( "aria-activedescendant", that.menuItems.eq( item.index ).find( "a" ).attr( "id" ) );

			},
			// set ARIA role
			role: 'listbox'
		})
		.data( "ui-menu" );

		// change menu styles?
		if ( this.options.dropdown ) {
			this.menu.addClass( 'ui-corner-bottom' ).removeClass( 'ui-corner-all' );
		}

		// unbind uneeded Menu events
		menuInstance._off( this.menu, "mouseleave" );
	},

	refresh: function() {
		this.menu.empty();

		var item,
			options = this.element.find( 'option' );
		if ( options.length ) {
			this._readOptions( options );
			this._renderMenu( this.menu, this.items );

			this.menu.menu( "refresh" );
			this.menuItems = this.menu.find( "li" ).not( '.ui-selectmenu-optgroup' );

			// select current item
			item = this._getSelectedItem();
			this.menu.menu( "focus", null, item );
			this._setSelected( item.data( "ui-selectmenu-item" ) );

			// set disabled state
			this._setOption( "disabled", this._getCreateOptions().disabled );
		}
	},

	open: function( event ) {
		if ( !this.options.disabled ) {
			// make sure menu is refreshed on first init (needed at least for IE9)
			if ( this.isOpen === undefined ) {
				this.button.trigger( "focus" );
			}

			this.isOpen = true;
			this._toggleAttr();

			if ( this.items && !this.options.dropdown ) {
				var currentItem = this._getSelectedItem();
				// center current item
				if ( this.menu.outerHeight() < this.menu.prop( "scrollHeight" ) ) {
					this.menuWrap.css( "left" , -10000 );
					this.menu.scrollTop( this.menu.scrollTop() + currentItem.position().top - this.menu.outerHeight() / 2 + currentItem.outerHeight() / 2 );
					this.menuWrap.css( "left" , "auto" );
				}

				$.extend( this.options.position, {
					my: "left top",
					at: "left top",
					// calculate offset
					offset: "0 " + ( this.menu.offset().top  - currentItem.offset().top + ( this.button.outerHeight() - currentItem.outerHeight() ) / 2 )
				});
			}

			this.options.position.of = this.button;
			this.menuWrap
				.zIndex( this.element.zIndex() + 1 )
				.position( this.options.position );

			this._trigger( "open", event );
		}
	},

	close: function( event ) {
		if ( this.isOpen ) {
			this.isOpen = false;
			this._toggleAttr();
			this._trigger( "close", event );
		}
	},

	widget: function() {
		return this.button;
	},

	menuWidget: function() {
		return this.menu;
	},

	_renderMenu: function( ul, items ) {
		var that = this,
			currentOptgroup = "";

		$.each( items, function( index, item ) {
			if ( item.optgroup !== currentOptgroup ) {
				$( '
', {
					'class': 'ui-selectmenu-optgroup' + ( item.element.parent( "optgroup" ).attr( "disabled" ) ? ' ui-state-disabled' : '' ),
					html: item.optgroup
				}).appendTo( ul );
				currentOptgroup = item.optgroup;
			}
			that._renderItem( ul, item );
		});
	},

	_renderItem: function( ul, item ) {
		var li = $( "
" ).data( "ui-selectmenu-item", item );
		if ( item.disabled ) {
			li.addClass( 'ui-state-disabled' );
		}
		li.append( $( "", {
				html: item.label,
				href: '#'
			})
		);

		return li.appendTo( ul );
	},

	_move: function( direction, event ) {
		if ( direction === "first" || direction === "last" ) {
			// set focus manually for first or last item
			this.menu.menu( "focus", event, this.menuItems[ direction ]() );
		} else {
			// move to and focus next or prev item
			this.menu.menu( direction, event );
		}
	},

	_getSelectedItem: function() {
		return this.menuItems.eq( this.element[ 0 ].selectedIndex );
	},

	_toggle: function( event ) {
		if ( this.isOpen ) {
			this.close( event );
		} else {
			this.open( event );
		}
	},

	_buttonEvents: {
		focus: function( event ) {
			// init Menu on first focus
			this.refresh();
			this._off( this.button, "focus" );
		},
		click: function( event ) {
			this._toggle( event );
			event.preventDefault();
		},
		keydown: function( event ) {
			var prevDef = true;
			switch ( event.keyCode ) {
				case $.ui.keyCode.TAB:
				case $.ui.keyCode.ESCAPE:
					if ( this.isOpen ) {
						this.close( event );
					}
					prevDef = false;
					break;
				case $.ui.keyCode.ENTER:
					if ( this.isOpen ) {
						this.menu.menu( "select", event );
					}
					break;
				case $.ui.keyCode.UP:
					if ( event.altKey ) {
						this._toggle( event );
					} else {
						this._move( "previous", event );
					}
					break;
				case $.ui.keyCode.DOWN:
					if ( event.altKey ) {
						this._toggle( event );
					} else {
						this._move( "next", event );
					}
					break;
				case $.ui.keyCode.SPACE:
					this._toggle( event );
					break;
				case $.ui.keyCode.LEFT:
					this._move( "previous", event );
					break;
				case $.ui.keyCode.RIGHT:
					this._move( "next", event );
					break;
				case $.ui.keyCode.HOME:
				case $.ui.keyCode.PAGE_UP:
					this._move( "first", event );
					break;
				case $.ui.keyCode.END:
				case $.ui.keyCode.PAGE_DOWN:
					this._move( "last", event );
					break;
				default:
					this.menu.trigger( event );
					prevDef = false;
			}
			if ( prevDef ) {
				event.preventDefault();
			}
		}
	},

	_select: function( item, event ) {
		var oldIndex = this.element[ 0 ].selectedIndex;
		// change native select element
		this.element[ 0 ].selectedIndex = item.index;
		this._setSelected( item );
		this._trigger( "select", event, { item: item } );

		if ( item.index !== oldIndex ) {
			this._trigger( "change", event, { item: item } );
		}
	},

	_setSelected: function( item ) {
		var itemId = this.menuItems.eq( item.index ).find( "a" ).attr( "id" );
		// update button text
		this.buttonText.html( item.label );
		// change ARIA attr
		this.menuItems.find( "a" ).attr( "aria-selected", false );
		this._getSelectedItem().find( "a" ).attr( "aria-selected", true );
		this.button.attr({
			"aria-activedescendant": itemId,
			"aria-labelledby": itemId
		});
	},

	_setOption: function( key, value ) {
		this._super( key, value );

		if ( key === "appendTo" ) {
			this.menuWrap.appendTo( $( value || "body", this.element[ 0 ].ownerDocument )[ 0 ] );
		}
		if ( key === "disabled" ) {
			this.menu.menu( "option", "disabled", value );
			if ( value ) {
				this.element.attr( "disabled", "disabled" );
				this.button.attr( "tabindex", -1 );
				this.close();
			} else {
				this.element.removeAttr( "disabled" );
				this.button.attr( "tabindex", 0 );
			}
		}
	},

	_toggleAttr: function(){
		if ( this.options.dropdown ) {
			this.button.toggleClass( 'ui-corner-top', this.isOpen ).toggleClass( 'ui-corner-all', !this.isOpen );
		}
		this.menuWrap.toggleClass( 'ui-selectmenu-open', this.isOpen );
		this.menu.attr("aria-hidden", !this.isOpen);
		this.button.attr("aria-expanded", this.isOpen);
	},

	_getCreateOptions: function() {
		return { disabled: !!this.element.attr( 'disabled' ) };
	},

	_readOptions: function( options ) {
		var data = [];
		$.each( options, function( index, item ) {
			var option = $( item ),
				optgroup = option.parent( "optgroup" );
			data.push({
				element: option,
				index: index,
				value: option.attr( 'value' ),
				label: option.text() || ' ',
				optgroup: optgroup.attr( "label" ) || "",
				disabled: optgroup.attr( "disabled" ) || option.attr( "disabled" )
			});
		});
		this.items = data;
	},

	_destroy: function() {
		this.menuWrap.remove();
		this.buttonWrap.remove();
		this.element.show();
		this.element.removeUniqueId();
		this.label.attr( "for", this.ids.id );
	}
});

}( jQuery ));
<commit_msg>Selectmenu: reset aria-activedescendant to selected item when menu is closed<commit_after>/*!
 * jQuery UI Selectmenu @VERSION
 * http://jqueryui.com
 *
 * Copyright 2012 jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Selectmenu
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *	jquery.ui.position.js
 *	jquery.ui.menu.js
 */
(function( $, undefined ) {

$.widget( "ui.selectmenu", {
	version: "@VERSION",
	defaultElement: "<select>",
	options: {
		dropdown: true,
		appendTo: "body",
		position: {
			my: "left top",
			at: "left bottom",
			collision: "none"
		},
		// callbacks
		open: null,
		focus: null,
		select: null,
		close: null,
		change: null
	},

	_create: function() {
		// make / set unique id
		var selectmenuId = this.element.uniqueId().attr( 'id' );

		// array of button and menu id's
		this.ids = { id: selectmenuId, button: selectmenuId + '-button', menu: selectmenuId + '-menu' };

		this._drawButton();
		this._on( this.button, this._buttonEvents );
		this._hoverable( this.button );
		this._focusable( this.button );

		this._drawMenu();

		// document click closes menu
		this._on( document, {
			click: function( event ) {
				if ( this.isOpen && !$( event.target ).closest( "li.ui-state-disabled, li.ui-selectmenu-optgroup, #" + this.ids.button ).length ) {
					this.close( event );
				}
			}
		});

		if ( this.options.disabled ) {
			this.disable();
		}
	},

	_drawButton: function() {
		var tabindex = this.element.attr( 'tabindex' );

		// fix existing label
		this.label = $( "label[for='" + this.ids.id + "']" ).attr( "for", this.ids.button );
		// catch click event of the label
		this._on( this.label, {
			'click':  function( event ) {
				this.button.focus();
				event.preventDefault();
			}
		});

		// hide original select tag
		this.element.hide();

		// create button
		this.button = $( '', {
			'class': 'ui-button ui-widget ui-state-default ui-corner-all',
			href: '#' + this.ids.id,
			tabindex: ( tabindex ? tabindex : this.options.disabled ? -1 : 0 ),
			id: this.ids.button,
			width: this.element.outerWidth(),
			role: 'combobox',
			'aria-expanded': false,
			'aria-autocomplete': 'list',
			'aria-owns': this.ids.menu,
			'aria-haspopup': true
		});

		this.button.prepend( $( '' ) );

		this.buttonText = $( '', {
				'class': 'ui-selectmenu-text' ,
				html: this.element.find( "option:selected" ).text() || ' '
			})
			.appendTo( this.button );

		// wrap and insert new button
		this.buttonWrap = $( '', {
				'class': 'ui-selectmenu-button'
			})
			.append( this.button )
			.insertAfter( this.element );
	},

	_drawMenu: function() {
		var menuInstance,
			that = this;

		// create menu portion, append to body
		this.menu = $( '
', {
			'aria-hidden': true,
			'aria-labelledby': this.ids.button,
			id: this.ids.menu
		});

		// wrap menu
		this.menuWrap = $( '
', {
				'class': 'ui-selectmenu-menu',
				width: ( this.options.dropdown ) ? this.button.outerWidth() : this.buttonText.width() + parseFloat( this.buttonText.css( "padding-left" ) ) || 0 + parseFloat( this.buttonText.css( "margin-left" ) ) || 0
			})
			.append( this.menu )
			.appendTo( this.options.appendTo );

		// init menu widget
		menuInstance = this.menu.menu({
			select: function( event, ui ) {
				var item = ui.item.data( "ui-selectmenu-item" );

				that._select( item, event );

				if ( that.isOpen ) {
					event.preventDefault();
					that.close( event );
				}
			},
			focus: function( event, ui ) {
				var item = ui.item.data( "ui-selectmenu-item" );

				if ( that.focus !== undefined ) {
					if ( item.index !== that.focus ) {
						that._trigger( "focus", event, { item: item } );
						if ( !that.isOpen ) {
							that._select( item, event );
						}
					}
				}
				that.focus = item.index;

				// Set ARIA active decendent
				that.button.attr( "aria-activedescendant", that.menuItems.eq( item.index ).find( "a" ).attr( "id" ) );
			},
			// set ARIA role
			role: 'listbox'
		})
		.data( "ui-menu" );

		// change menu styles?
		if ( this.options.dropdown ) {
			this.menu.addClass( 'ui-corner-bottom' ).removeClass( 'ui-corner-all' );
		}

		// unbind uneeded Menu events
		menuInstance._off( this.menu, "mouseleave" );
	},

	refresh: function() {
		this.menu.empty();

		var item,
			options = this.element.find( 'option' );
		if ( options.length ) {
			this._readOptions( options );
			this._renderMenu( this.menu, this.items );

			this.menu.menu( "refresh" );
			this.menuItems = this.menu.find( "li" ).not( '.ui-selectmenu-optgroup' );

			// select current item
			item = this._getSelectedItem();
			this.menu.menu( "focus", null, item );
			this._setSelected( item.data( "ui-selectmenu-item" ) );

			// set disabled state
			this._setOption( "disabled", this._getCreateOptions().disabled );
		}
	},

	open: function( event ) {
		if ( !this.options.disabled ) {
			// make sure menu is refreshed on first init (needed at least for IE9)
			if ( this.isOpen === undefined ) {
				this.button.trigger( "focus" );
			}

			this.isOpen = true;
			this._toggleAttr();

			if ( this.items && !this.options.dropdown ) {
				var currentItem = this._getSelectedItem();
				// center current item
				if ( this.menu.outerHeight() < this.menu.prop( "scrollHeight" ) ) {
					this.menuWrap.css( "left" , -10000 );
					this.menu.scrollTop( this.menu.scrollTop() + currentItem.position().top - this.menu.outerHeight() / 2 + currentItem.outerHeight() / 2 );
					this.menuWrap.css( "left" , "auto" );
				}

				$.extend( this.options.position, {
					my: "left top",
					at: "left top",
					// calculate offset
					offset: "0 " + ( this.menu.offset().top  - currentItem.offset().top + ( this.button.outerHeight() - currentItem.outerHeight() ) / 2 )
				});
			}

			this.options.position.of = this.button;
			this.menuWrap
				.zIndex( this.element.zIndex() + 1 )
				.position( this.options.position );

			this._trigger( "open", event );
		}
	},

	close: function( event ) {
		if ( this.isOpen ) {
			this.isOpen = false;
			this._toggleAttr();
			this.button.attr( "aria-activedescendant", this._getSelectedItem().find( "a" ).attr( "id" ) );
			this._trigger( "close", event );
		}
	},

	widget: function() {
		return this.button;
	},

	menuWidget: function() {
		return this.menu;
	},

	_renderMenu: function( ul, items ) {
		var that = this,
			currentOptgroup = "";

		$.each( items, function( index, item ) {
			if ( item.optgroup !== currentOptgroup ) {
				$( '
', {
					'class': 'ui-selectmenu-optgroup' + ( item.element.parent( "optgroup" ).attr( "disabled" ) ? ' ui-state-disabled' : '' ),
					html: item.optgroup
				}).appendTo( ul );
				currentOptgroup = item.optgroup;
			}
			that._renderItem( ul, item );
		});
	},

	_renderItem: function( ul, item ) {
		var li = $( "
" ).data( "ui-selectmenu-item", item );
		if ( item.disabled ) {
			li.addClass( 'ui-state-disabled' );
		}
		li.append( $( "", {
				html: item.label,
				href: '#'
			})
		);

		return li.appendTo( ul );
	},

	_move: function( direction, event ) {
		if ( direction === "first" || direction === "last" ) {
			// set focus manually for first or last item
			this.menu.menu( "focus", event, this.menuItems[ direction ]() );
		} else {
			// move to and focus next or prev item
			this.menu.menu( direction, event );
		}
	},

	_getSelectedItem: function() {
		return this.menuItems.eq( this.element[ 0 ].selectedIndex );
	},

	_toggle: function( event ) {
		if ( this.isOpen ) {
			this.close( event );
		} else {
			this.open( event );
		}
	},

	_buttonEvents: {
		focus: function( event ) {
			// init Menu on first focus
			this.refresh();
			this._off( this.button, "focus" );
		},
		click: function( event ) {
			this._toggle( event );
			event.preventDefault();
		},
		keydown: function( event ) {
			var prevDef = true;
			switch ( event.keyCode ) {
				case $.ui.keyCode.TAB:
				case $.ui.keyCode.ESCAPE:
					if ( this.isOpen ) {
						this.close( event );
					}
					prevDef = false;
					break;
				case $.ui.keyCode.ENTER:
					if ( this.isOpen ) {
						this.menu.menu( "select", event );
					}
					break;
				case $.ui.keyCode.UP:
					if ( event.altKey ) {
						this._toggle( event );
					} else {
						this._move( "previous", event );
					}
					break;
				case $.ui.keyCode.DOWN:
					if ( event.altKey ) {
						this._toggle( event );
					} else {
						this._move( "next", event );
					}
					break;
				case $.ui.keyCode.SPACE:
					this._toggle( event );
					break;
				case $.ui.keyCode.LEFT:
					this._move( "previous", event );
					break;
				case $.ui.keyCode.RIGHT:
					this._move( "next", event );
					break;
				case $.ui.keyCode.HOME:
				case $.ui.keyCode.PAGE_UP:
					this._move( "first", event );
					break;
				case $.ui.keyCode.END:
				case $.ui.keyCode.PAGE_DOWN:
					this._move( "last", event );
					break;
				default:
					this.menu.trigger( event );
					prevDef = false;
			}
			if ( prevDef ) {
				event.preventDefault();
			}
		}
	},

	_select: function( item, event ) {
		var oldIndex = this.element[ 0 ].selectedIndex;
		// change native select element
		this.element[ 0 ].selectedIndex = item.index;
		this._setSelected( item );
		this._trigger( "select", event, { item: item } );

		if ( item.index !== oldIndex ) {
			this._trigger( "change", event, { item: item } );
		}
	},

	_setSelected: function( item ) {
		// update button text
		this.buttonText.html( item.label );
		// change ARIA attr
		this.menuItems.find( "a" ).attr( "aria-selected", false );
		this._getSelectedItem().find( "a" ).attr( "aria-selected", true );
		this.button.attr( "aria-labelledby", this.menuItems.eq( item.index ).find( "a" ).attr( "id" ) );
	},

	_setOption: function( key, value ) {
		this._super( key, value );

		if ( key === "appendTo" ) {
			this.menuWrap.appendTo( $( value || "body", this.element[ 0 ].ownerDocument )[ 0 ] );
		}
		if ( key === "disabled" ) {
			this.menu.menu( "option", "disabled", value );
			if ( value ) {
				this.element.attr( "disabled", "disabled" );
				this.button.attr( "tabindex", -1 );
				this.close();
			} else {
				this.element.removeAttr( "disabled" );
				this.button.attr( "tabindex", 0 );
			}
		}
	},

	_toggleAttr: function(){
		if ( this.options.dropdown ) {
			this.button.toggleClass( 'ui-corner-top', this.isOpen ).toggleClass( 'ui-corner-all', !this.isOpen );
		}
		this.menuWrap.toggleClass( 'ui-selectmenu-open', this.isOpen );
		this.menu.attr("aria-hidden", !this.isOpen);
		this.button.attr("aria-expanded", this.isOpen);
	},

	_getCreateOptions: function() {
		return { disabled: !!this.element.attr( 'disabled' ) };
	},

	_readOptions: function( options ) {
		var data = [];
		$.each( options, function( index, item ) {
			var option = $( item ),
				optgroup = option.parent( "optgroup" );
			data.push({
				element: option,
				index: index,
				value: option.attr( 'value' ),
				label: option.text() || ' ',
				optgroup: optgroup.attr( "label" ) || "",
				disabled: optgroup.attr( "disabled" ) || option.attr( "disabled" )
			});
		});
		this.items = data;
	},

	_destroy: function() {
		this.menuWrap.remove();
		this.buttonWrap.remove();
		this.element.show();
		this.element.removeUniqueId();
		this.label.attr( "for", this.ids.id );
	}
});

}( jQuery ));
<|endoftext|>


<commit_before>/*
 * jQuery UI Selectmenu @VERSION
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Selectmenu
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *	jquery.ui.position.js
 *	jquery.ui.menu.js
 *	jquery.ui.button.js
 */
(function( $, undefined ) {

$.widget( "ui.selectmenu", {
	version: "@VERSION",
	defaultElement: "<select>",
	options: {
		dropdown: true,
		appendTo: "body",
		position: {
			my: "left top",
			at: "left bottom",
			collision: "none"
		},
		value: null,
		// callbacks
		open: null,
		focus: null,
		select: null,
		close: null,
		change: null
	},

	_create: function() {
		// set a default id value, generate a new random one if not set by developer
		var selectmenuId = this.element.attr( 'id' ) || 'ui-selectmenu-' + Math.random().toString( 16 ).slice( 2, 10 );

		// quick array of button and menu id's
		this.ids = [ selectmenuId, selectmenuId + '-button', selectmenuId + '-menu' ];

		// set current value
		if ( this.options.value ) {
			this.element[0].value = this.options.value;
		} else {
			this.options.value = this.element[0].value;
		}

		// catch click event of the label
		this._bind({
			'click':  function( event ) {
				this.button.focus();
				event.preventDefault();
			}
		});

		this._drawButton();
		this._bind( this.button, this._buttonEvents );

		this._drawMenu();
		this.refresh();
	},

	_drawButton: function() {
		var tabindex = this.element.attr( 'tabindex' );

		// hide original select tag
		this.element.hide();

		// create button
		this.button = $( '', {
				href: '#' + this.ids[ 0 ],
				tabindex: ( tabindex ? tabindex : this.element.attr( 'disabled' ) ? -1 : 0 ),
				id: this.ids[ 1 ],
				css: {
					width: this.element.outerWidth()
				},
				'aria-disabled': this.options.disabled,
				'aria-owns': this.ids[ 2 ],
				'aria-haspopup': true
			})
			.button({
				label: this.element.find( "option:selected" ).text(),
				icons: {
					primary: ( this.options.dropdown ? 'ui-icon-triangle-1-s' : 'ui-icon-triangle-2-n-s' )
				}
			});

		// wrap and insert new button
		this.buttonWrap = $( '' )
			.addClass( 'ui-selectmenu-button' )
			.append( this.button )
			.insertAfter( this.element );
	},

	_drawMenu: function() {
		var that = this;

		// create menu portion, append to body
		this.menu = $( '
', {
			'class': 'ui-widget ui-widget-content',
			'aria-hidden': true,
			'aria-labelledby': this.ids[1],
			role: 'menubox',
			id: this.ids[2]
		});

		// set width
		if ( this.options.dropdown ) {
			var setWidth = this.button.outerWidth();
		} else {
			var text = this.button.find( "span.ui-button-text");
			var setWidth = text.width() + parseFloat( text.css( "padding-left" ) ) + parseFloat( text.css( "margin-left" ) );
		}

		// wrap menu
		this.menuWrap = $( '
' )
			.addClass( 'ui-selectmenu-menu' )
			.width( setWidth )
			.append( this.menu )
			.appendTo( this.options.appendTo );

		// init menu widget
		this.menu
			.data( 'element.selectelemenu', this.element )
			.menu({
				select: function( event, ui ) {
					var flag = false,
						item = ui.item.data( "item.selectmenu" );

					if ( item.index != that.element[0].selectedIndex ) flag = true;

					that._setOption( "value", item.value );
					that._trigger( "select", event, { item: item } );

					if ( flag ) that._trigger( "change", event, { item: item } );

					if ( that.opened ) {
						event.preventDefault();
						that.close( event, true);
					}
				},
				focus: function( event, ui ) {
					var item = ui.item.data( "item.selectmenu" );
					if ( that.focus !== undefined && item.index != that.focus ) that._trigger( "focus", event, { item: item } );
					that.focus = item.index;
				}
			});

		// document click closes menu
		this._bind( document, {
			'mousedown': function( event ) {
				if ( this.opened && !$( event.target ).is( this.menu ) ) {
					window.setTimeout( function() {
						this.close( event );
					}, 200 );
				}
			}
		});
	},

	refresh: function() {
		this.menu.empty();

		this._readOptions();
		this._renderMenu( this.menu, this.items );

		this.menu.menu( "refresh" );

		// adjust ARIA
		this.menu.find( "li" ).not( '.ui-selectmenu-optgroup' ).find( 'a' ).attr( 'role', 'option' );

		if ( this.options.dropdown ) {
			this.menu
				.addClass( 'ui-corner-bottom' )
				.removeClass( 'ui-corner-all' );
		}

		// transfer disabled state
		if ( this.element.attr( 'disabled' ) ) {
			this.disable();
		} else {
			this.enable()
		}
	},

	open: function( event ) {
		var currentItem = this._getSelectedItem();

		if ( !this.options.disabled ) {
			// close all other selectmenus
			$( '.ui-selectmenu-open' ).not( this.button ).each( function() {
				$( this ).children( 'ul.ui-menu' ).data( 'element.selectelemenu' ).selectmenu( 'close' );
			});

			if ( this.options.dropdown ) {
				this.button
					.addClass( 'ui-corner-top' )
					.removeClass( 'ui-corner-all' );
			}

			this.menuWrap.addClass( 'ui-selectmenu-open' );
			this.menu.menu( "focus", null, currentItem );

			if ( !this.options.dropdown ) {
				// center current item
				if ( this.menu.css("overflow") == "auto" ) {
					this.menu.scrollTop( this.menu.scrollTop() + currentItem.position().top - this.menu.outerHeight()/2 + currentItem.outerHeight()/2 );
				}
				// calculate offset
				var _offset = ( this.menu.offset().top  - currentItem.offset().top + ( this.button.outerHeight() - currentItem.outerHeight() ) / 2);
				$.extend( this.options.position, {
					my: "left top",
					at: "left top",
					offset: "0 " + _offset
				});
			}

			this.menuWrap
				.zIndex( this.element.zIndex() + 1 )
				.position( $.extend({
					of: this.button
				}, this.options.position ));

			this.opened = true;
			this._trigger( "open", event );
		}
	},

	close: function( event, focus ) {
		if ( this.opened ) {
			if ( this.options.dropdown ) {
				this.button
					.addClass( 'ui-corner-all' )
					.removeClass( 'ui-corner-top' );
			}

			this.menuWrap.removeClass( 'ui-selectmenu-open' );
			this.opened = false;

			if (focus) this.button.focus();

			this._trigger( "close", event );
		}
	},

	_renderMenu: function( ul, items ) {
		var that = this,
			currentOptgroup = "";

		$.each( items, function( index, item ) {
			if ( item.optgroup != currentOptgroup ) {
				var optgroup = $( '
' + item.optgroup + '
' );
				if ( item.element.parent( "optgroup" ).attr( "disabled" ) ) optgroup.addClass( 'ui-state-disabled' );
				ul.append( optgroup );
				currentOptgroup = item.optgroup;
			}
			that._renderItem( ul, item );
		});
	},

	_renderItem: function( ul, item) {
		var li = $( "
" ).data( "item.selectmenu", item );
		if ( item.disabled ) {
			li.addClass( 'ui-state-disabled' ).text( item.label );
		} else {
			li.append( $( "", {
					text: item.label,
					href: '#'
				})
			);
		}

		return li.appendTo( ul );
	},

	_move: function( key, event ) {
		if ( !this.opened )	this.menu.menu( "focus", event, this._getSelectedItem() );
		this.menu.menu( key, event );
		if ( !this.opened ) this.menu.menu( "select", event  );
	},

	_getSelectedItem: function() {
		return this.menu.find( "li" ).not( '.ui-selectmenu-optgroup' ).eq( this.element[0].selectedIndex );
	},

	_toggle: function( event ) {
		if ( this.opened ) {
			this.close( event );
		} else {
			this.open( event );
		}
	},

	_buttonEvents: {
		mousedown: function( event ) {
			this._toggle( event );
			event.stopImmediatePropagation();
		},
		click: function( event ) {
			// return false needed to prevent browser from following the anchor
			return false;
		},
		keydown: function( event ) {
			switch (event.keyCode) {
				case $.ui.keyCode.TAB:
					if ( this.opened ) this.close( event );
					break;
				case $.ui.keyCode.ENTER:
					if ( this.opened ) {
						this.menu.menu( "select", this._getSelectedItem() );
						event.preventDefault();
					}
					break;
				case $.ui.keyCode.SPACE:
					this._toggle(event);
					event.preventDefault();
					break;
				case $.ui.keyCode.UP:
					if ( event.altKey ) {
						this._toggle( event );
					} else {
						this._move( "previous", event );
					}
					event.preventDefault();
					break;
				case $.ui.keyCode.DOWN:
					if ( event.altKey ) {
						this._toggle( event );
					} else {
						this._move( "next", event );
					}
					event.preventDefault();
					break;
				case $.ui.keyCode.LEFT:
					this._move( "previous", event );
					event.preventDefault();
					break;
				case $.ui.keyCode.RIGHT:
					this._move( "next", event );
					event.preventDefault();
					break;
				default:
					this.menu.trigger( event );
			}
		}
	},

	_setOption: function( key, value ) {
		this._super( "_setOption", key, value );

		if ( key === "appendTo" ) {
			this.menuWrap.appendTo( $( value || "body", this.element[0].ownerDocument )[0] );
		}
		if ( key === "value" && value !== undefined ) {
			this.element[0].value = value;
			this.button.children( '.ui-button-text' ).text( this.items[ this.element[0].selectedIndex ].label );
		}
		if ( key === "disabled" ) {
			this.button.button( "option", "disabled", value );
			if ( value ) {
				this.element.attr( "disabled", "disabled" );
				this.button.attr( "tabindex", -1 );
			} else {
				this.element.removeAttr( "disabled" );
				this.button.attr( "tabindex", 1 );
			}
			this.menu.attr( "aria-disabled", value );
			this.close();
		}
	},

	widget: function() {
		return this.buttonWrap.add( this.menuWrap );
	},

	_readOptions: function() {
		var data = [];
		$.each( this.element.find( 'option' ), function( index, item ) {
			var option = $( item ),
				optgroup = option.parent( "optgroup" );
			data.push({
				element: option,
				index: index,
				value: option.attr( 'value' ),
				label: option.text(),
				optgroup: optgroup.attr( "label" ) || false,
				disabled: optgroup.attr( "disabled" ) || option.attr( "disabled" )
			});
		});
		this.items = data;
	},

	_destroy: function() {
		this.menuWrap.remove();
		this.buttonWrap.remove();
		this.element.show();
	}
});

}( jQuery ));
<commit_msg>Selectmenu: changed to _delay<commit_after>/*
 * jQuery UI Selectmenu @VERSION
 *
 * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
 * Dual licensed under the MIT or GPL Version 2 licenses.
 * http://jquery.org/license
 *
 * http://docs.jquery.com/UI/Selectmenu
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *	jquery.ui.position.js
 *	jquery.ui.menu.js
 *	jquery.ui.button.js
 */
(function( $, undefined ) {

$.widget( "ui.selectmenu", {
	version: "@VERSION",
	defaultElement: "<select>",
	options: {
		dropdown: true,
		appendTo: "body",
		position: {
			my: "left top",
			at: "left bottom",
			collision: "none"
		},
		value: null,
		// callbacks
		open: null,
		focus: null,
		select: null,
		close: null,
		change: null
	},

	_create: function() {
		// set a default id value, generate a new random one if not set by developer
		var selectmenuId = this.element.attr( 'id' ) || 'ui-selectmenu-' + Math.random().toString( 16 ).slice( 2, 10 );

		// quick array of button and menu id's
		this.ids = [ selectmenuId, selectmenuId + '-button', selectmenuId + '-menu' ];

		// set current value
		if ( this.options.value ) {
			this.element[0].value = this.options.value;
		} else {
			this.options.value = this.element[0].value;
		}

		// catch click event of the label
		this._bind({
			'click':  function( event ) {
				this.button.focus();
				event.preventDefault();
			}
		});

		this._drawButton();
		this._bind( this.button, this._buttonEvents );

		this._drawMenu();
		this.refresh();
	},

	_drawButton: function() {
		var tabindex = this.element.attr( 'tabindex' );

		// hide original select tag
		this.element.hide();

		// create button
		this.button = $( '', {
				href: '#' + this.ids[ 0 ],
				tabindex: ( tabindex ? tabindex : this.element.attr( 'disabled' ) ? -1 : 0 ),
				id: this.ids[ 1 ],
				css: {
					width: this.element.outerWidth()
				},
				'aria-disabled': this.options.disabled,
				'aria-owns': this.ids[ 2 ],
				'aria-haspopup': true
			})
			.button({
				label: this.element.find( "option:selected" ).text(),
				icons: {
					primary: ( this.options.dropdown ? 'ui-icon-triangle-1-s' : 'ui-icon-triangle-2-n-s' )
				}
			});

		// wrap and insert new button
		this.buttonWrap = $( '' )
			.addClass( 'ui-selectmenu-button' )
			.append( this.button )
			.insertAfter( this.element );
	},

	_drawMenu: function() {
		var that = this;

		// create menu portion, append to body
		this.menu = $( '
', {
			'class': 'ui-widget ui-widget-content',
			'aria-hidden': true,
			'aria-labelledby': this.ids[1],
			role: 'menubox',
			id: this.ids[2]
		});

		// set width
		if ( this.options.dropdown ) {
			var setWidth = this.button.outerWidth();
		} else {
			var text = this.button.find( "span.ui-button-text");
			var setWidth = text.width() + parseFloat( text.css( "padding-left" ) ) + parseFloat( text.css( "margin-left" ) );
		}

		// wrap menu
		this.menuWrap = $( '
' )
			.addClass( 'ui-selectmenu-menu' )
			.width( setWidth )
			.append( this.menu )
			.appendTo( this.options.appendTo );

		// init menu widget
		this.menu
			.data( 'element.selectelemenu', this.element )
			.menu({
				select: function( event, ui ) {
					var flag = false,
						item = ui.item.data( "item.selectmenu" );

					if ( item.index != that.element[0].selectedIndex ) flag = true;

					that._setOption( "value", item.value );
					that._trigger( "select", event, { item: item } );

					if ( flag ) that._trigger( "change", event, { item: item } );

					if ( that.opened ) {
						event.preventDefault();
						that.close( event, true);
					}
				},
				focus: function( event, ui ) {
					var item = ui.item.data( "item.selectmenu" );
					if ( that.focus !== undefined && item.index != that.focus ) that._trigger( "focus", event, { item: item } );
					that.focus = item.index;
				}
			});

		// document click closes menu
		this._bind( document, {
			'mousedown': function( event ) {
				if ( this.opened && !$( event.target ).is( this.menu ) ) {
					this._delay( function() {
						this.close( event );
					}, 200);
				}
			}
		});
	},

	refresh: function() {
		this.menu.empty();

		this._readOptions();
		this._renderMenu( this.menu, this.items );

		this.menu.menu( "refresh" );

		// adjust ARIA
		this.menu.find( "li" ).not( '.ui-selectmenu-optgroup' ).find( 'a' ).attr( 'role', 'option' );

		if ( this.options.dropdown ) {
			this.menu
				.addClass( 'ui-corner-bottom' )
				.removeClass( 'ui-corner-all' );
		}

		// transfer disabled state
		if ( this.element.attr( 'disabled' ) ) {
			this.disable();
		} else {
			this.enable()
		}
	},

	open: function( event ) {
		var currentItem = this._getSelectedItem();

		if ( !this.options.disabled ) {
			// close all other selectmenus
			$( '.ui-selectmenu-open' ).not( this.button ).each( function() {
				$( this ).children( 'ul.ui-menu' ).data( 'element.selectelemenu' ).selectmenu( 'close' );
			});

			if ( this.options.dropdown ) {
				this.button
					.addClass( 'ui-corner-top' )
					.removeClass( 'ui-corner-all' );
			}

			this.menuWrap.addClass( 'ui-selectmenu-open' );
			this.menu.menu( "focus", null, currentItem );

			if ( !this.options.dropdown ) {
				// center current item
				if ( this.menu.css("overflow") == "auto" ) {
					this.menu.scrollTop( this.menu.scrollTop() + currentItem.position().top - this.menu.outerHeight()/2 + currentItem.outerHeight()/2 );
				}
				// calculate offset
				var _offset = ( this.menu.offset().top  - currentItem.offset().top + ( this.button.outerHeight() - currentItem.outerHeight() ) / 2);
				$.extend( this.options.position, {
					my: "left top",
					at: "left top",
					offset: "0 " + _offset
				});
			}

			this.menuWrap
				.zIndex( this.element.zIndex() + 1 )
				.position( $.extend({
					of: this.button
				}, this.options.position ));

			this.opened = true;
			this._trigger( "open", event );
		}
	},

	close: function( event, focus ) {
		if ( this.opened ) {
			if ( this.options.dropdown ) {
				this.button
					.addClass( 'ui-corner-all' )
					.removeClass( 'ui-corner-top' );
			}

			this.menuWrap.removeClass( 'ui-selectmenu-open' );
			this.opened = false;

			if (focus) this.button.focus();

			this._trigger( "close", event );
		}
	},

	_renderMenu: function( ul, items ) {
		var that = this,
			currentOptgroup = "";

		$.each( items, function( index, item ) {
			if ( item.optgroup != currentOptgroup ) {
				var optgroup = $( '
' + item.optgroup + '
' );
				if ( item.element.parent( "optgroup" ).attr( "disabled" ) ) optgroup.addClass( 'ui-state-disabled' );
				ul.append( optgroup );
				currentOptgroup = item.optgroup;
			}
			that._renderItem( ul, item );
		});
	},

	_renderItem: function( ul, item) {
		var li = $( "
" ).data( "item.selectmenu", item );
		if ( item.disabled ) {
			li.addClass( 'ui-state-disabled' ).text( item.label );
		} else {
			li.append( $( "", {
					text: item.label,
					href: '#'
				})
			);
		}

		return li.appendTo( ul );
	},

	_move: function( key, event ) {
		if ( !this.opened )	this.menu.menu( "focus", event, this._getSelectedItem() );
		this.menu.menu( key, event );
		if ( !this.opened ) this.menu.menu( "select", event  );
	},

	_getSelectedItem: function() {
		return this.menu.find( "li" ).not( '.ui-selectmenu-optgroup' ).eq( this.element[0].selectedIndex );
	},

	_toggle: function( event ) {
		if ( this.opened ) {
			this.close( event );
		} else {
			this.open( event );
		}
	},

	_buttonEvents: {
		mousedown: function( event ) {
			this._toggle( event );
			event.stopImmediatePropagation();
		},
		click: function( event ) {
			// return false needed to prevent browser from following the anchor
			return false;
		},
		keydown: function( event ) {
			switch (event.keyCode) {
				case $.ui.keyCode.TAB:
					if ( this.opened ) this.close( event );
					break;
				case $.ui.keyCode.ENTER:
					if ( this.opened ) {
						this.menu.menu( "select", this._getSelectedItem() );
						event.preventDefault();
					}
					break;
				case $.ui.keyCode.SPACE:
					this._toggle(event);
					event.preventDefault();
					break;
				case $.ui.keyCode.UP:
					if ( event.altKey ) {
						this._toggle( event );
					} else {
						this._move( "previous", event );
					}
					event.preventDefault();
					break;
				case $.ui.keyCode.DOWN:
					if ( event.altKey ) {
						this._toggle( event );
					} else {
						this._move( "next", event );
					}
					event.preventDefault();
					break;
				case $.ui.keyCode.LEFT:
					this._move( "previous", event );
					event.preventDefault();
					break;
				case $.ui.keyCode.RIGHT:
					this._move( "next", event );
					event.preventDefault();
					break;
				default:
					this.menu.trigger( event );
			}
		}
	},

	_setOption: function( key, value ) {
		this._super( "_setOption", key, value );

		if ( key === "appendTo" ) {
			this.menuWrap.appendTo( $( value || "body", this.element[0].ownerDocument )[0] );
		}
		if ( key === "value" && value !== undefined ) {
			this.element[0].value = value;
			this.button.children( '.ui-button-text' ).text( this.items[ this.element[0].selectedIndex ].label );
		}
		if ( key === "disabled" ) {
			this.button.button( "option", "disabled", value );
			if ( value ) {
				this.element.attr( "disabled", "disabled" );
				this.button.attr( "tabindex", -1 );
			} else {
				this.element.removeAttr( "disabled" );
				this.button.attr( "tabindex", 1 );
			}
			this.menu.attr( "aria-disabled", value );
			this.close();
		}
	},

	widget: function() {
		return this.buttonWrap.add( this.menuWrap );
	},

	_readOptions: function() {
		var data = [];
		$.each( this.element.find( 'option' ), function( index, item ) {
			var option = $( item ),
				optgroup = option.parent( "optgroup" );
			data.push({
				element: option,
				index: index,
				value: option.attr( 'value' ),
				label: option.text(),
				optgroup: optgroup.attr( "label" ) || false,
				disabled: optgroup.attr( "disabled" ) || option.attr( "disabled" )
			});
		});
		this.items = data;
	},

	_destroy: function() {
		this.menuWrap.remove();
		this.buttonWrap.remove();
		this.element.show();
	}
});

}( jQuery ));
<|endoftext|>


<commit_before>import {proxy} from 'utils/queryUrlGenerator'
import replaceTemplate from 'src/tempVars/utils/replace'
import AJAX from 'utils/ajax'
import {
  linksFromHosts,
  updateActiveHostLink,
} from 'src/hosts/utils/hostsSwitcherLinks'
import _ from 'lodash'

export const getCpuAndLoadForHosts = (
  proxyLink,
  telegrafDB,
  telegrafSystemInterval,
  tempVars
) => {
  const query = replaceTemplate(
    `SELECT mean("usage_user") FROM \":db:\".\":rp:\".\"cpu\" WHERE "cpu" = 'cpu-total' AND time > now() - 10m GROUP BY host;
      SELECT mean("load1") FROM \":db:\".\":rp:\".\"system\" WHERE time > now() - 10m GROUP BY host;
      SELECT non_negative_derivative(mean(uptime)) AS deltaUptime FROM \":db:\".\":rp:\".\"system\" WHERE time > now() - ${telegrafSystemInterval} * 10 GROUP BY host, time(${telegrafSystemInterval}) fill(0);
      SELECT mean("Percent_Processor_Time") FROM \":db:\".\":rp:\".\"win_cpu\" WHERE time > now() - 10m GROUP BY host;
      SELECT mean("Processor_Queue_Length") FROM \":db:\".\":rp:\".\"win_system\" WHERE time > now() - 10s GROUP BY host;
      SELECT non_negative_derivative(mean("System_Up_Time")) AS winDeltaUptime FROM \":db:\".\":rp:\".\"win_system\" WHERE time > now() - ${telegrafSystemInterval} * 10 GROUP BY host, time(${telegrafSystemInterval}) fill(0);
      SHOW TAG VALUES WITH KEY = "host";`,
    tempVars
  )

  return proxy({
    source: proxyLink,
    query,
    db: telegrafDB,
    tempVars,
  }).then(resp => {
    const hosts = {}
    const precision = 100
    const cpuSeries = _.get(resp, ['data', 'results', '0', 'series'], [])
    const loadSeries = _.get(resp, ['data', 'results', '1', 'series'], [])
    const uptimeSeries = _.get(resp, ['data', 'results', '2', 'series'], [])
    const winCPUSeries = _.get(resp, ['data', 'results', '3', 'series'], [])
    const winLoadSeries = _.get(resp, ['data', 'results', '4', 'series'], [])
    const winUptimeSeries = _.get(resp, ['data', 'results', '5', 'series'], [])
    const allHostsSeries = _.get(resp, ['data', 'results', '6', 'series'], [])

    allHostsSeries.forEach(s => {
      const hostnameIndex = s.columns.findIndex(col => col === 'value')
      s.values.forEach(v => {
        const hostname = v[hostnameIndex]
        hosts[hostname] = {
          name: hostname,
          deltaUptime: -1,
          cpu: 0.0,
          load: 0.0,
        }
      })
    })

    cpuSeries.forEach(s => {
      const meanIndex = s.columns.findIndex(col => col === 'mean')
      hosts[s.tags.host] = {
        name: s.tags.host,
        cpu: Math.round(s.values[0][meanIndex] * precision) / precision,
      }
    })

    loadSeries.forEach(s => {
      const meanIndex = s.columns.findIndex(col => col === 'mean')
      hosts[s.tags.host].load =
        Math.round(s.values[0][meanIndex] * precision) / precision
    })

    uptimeSeries.forEach(s => {
      const uptimeIndex = s.columns.findIndex(col => col === 'deltaUptime')
      hosts[s.tags.host].deltaUptime =
        s.values[s.values.length - 1][uptimeIndex]
    })

    winCPUSeries.forEach(s => {
      const meanIndex = s.columns.findIndex(col => col === 'mean')
      hosts[s.tags.host] = {
        name: s.tags.host,
        cpu: Math.round(s.values[0][meanIndex] * precision) / precision,
      }
    })

    winLoadSeries.forEach(s => {
      const meanIndex = s.columns.findIndex(col => col === 'mean')
      hosts[s.tags.host].load =
        Math.round(s.values[0][meanIndex] * precision) / precision
    })

    winUptimeSeries.forEach(s => {
      const winUptimeIndex = s.columns.findIndex(
        col => col === 'winDeltaUptime'
      )
      hosts[s.tags.host].winDeltaUptime =
        s.values[s.values.length - 1][winUptimeIndex]
    })

    return hosts
  })
}

async function getAllHosts(source) {
  const {
    telegrafDB,
    links: {proxy: proxyLink},
  } = source

  try {
    const resp = await proxy({
      source: proxyLink,
      query: 'show tag values with key = "host"',
      db: telegrafDB,
    })
    const hosts = {}
    const allHostsSeries = _.get(resp, ['data', 'results', '0', 'series'], [])

    allHostsSeries.forEach(s => {
      const hostnameIndex = s.columns.findIndex(col => col === 'value')
      s.values.forEach(v => {
        const hostname = v[hostnameIndex]
        hosts[hostname] = {
          name: hostname,
        }
      })
    })

    return hosts
  } catch (error) {
    console.error(error) // eslint-disable-line no-console
    throw error
  }
}

export const loadHostsLinks = async (
  source,
  {activeHost = {}, getHostNamesAJAX = getAllHosts} = {}
) => {
  const hostNames = await getHostNamesAJAX(source)
  const allLinks = linksFromHosts(hostNames, source)

  return updateActiveHostLink(allLinks, activeHost)
}

export const getLayouts = () =>
  AJAX({
    method: 'GET',
    resource: 'layouts',
  })

export const getAppsForHost = (proxyLink, host, appLayouts, telegrafDB) => {
  const measurements = appLayouts.map(m => `^${m.measurement}$`).join('|')
  const measurementsToApps = _.zipObject(
    appLayouts.map(m => m.measurement),
    appLayouts.map(({app}) => app)
  )

  return proxy({
    source: proxyLink,
    query: `show series from /${measurements}/ where host = '${host}'`,
    db: telegrafDB,
  }).then(resp => {
    const result = {apps: [], tags: {}}
    const allSeries = _.get(resp, 'data.results.0.series.0.values', [])

    allSeries.forEach(([series]) => {
      const seriesObj = parseSeries(series)
      const measurement = seriesObj.measurement

      result.apps = _.uniq(result.apps.concat(measurementsToApps[measurement]))
      _.assign(result.tags, seriesObj.tags)
    })

    return result
  })
}

export const getAppsForHosts = (proxyLink, hosts, appLayouts, telegrafDB) => {
  const measurements = appLayouts.map(m => `^${m.measurement}$`).join('|')
  const measurementsToApps = _.zipObject(
    appLayouts.map(m => m.measurement),
    appLayouts.map(({app}) => app)
  )

  return proxy({
    source: proxyLink,
    query: `show series from /${measurements}/`,
    db: telegrafDB,
  }).then(resp => {
    const newHosts = Object.assign({}, hosts)
    const allSeries = _.get(
      resp,
      ['data', 'results', '0', 'series', '0', 'values'],
      []
    )

    allSeries.forEach(([series]) => {
      const seriesObj = parseSeries(series)
      const measurement = seriesObj.measurement
      const host = _.get(seriesObj, ['tags', 'host'], '')

      if (!newHosts[host]) {
        return
      }

      if (!newHosts[host].apps) {
        newHosts[host].apps = []
      }

      if (!newHosts[host].tags) {
        newHosts[host].tags = {}
      }

      newHosts[host].apps = _.uniq(
        newHosts[host].apps.concat(measurementsToApps[measurement])
      )
      _.assign(newHosts[host].tags, seriesObj.tags)
    })

    return newHosts
  })
}

export function getMeasurementsForHost(source, host) {
  return proxy({
    source: source.links.proxy,
    query: `SHOW MEASUREMENTS WHERE "host" = '${host}'`,
    db: source.telegraf,
  }).then(({data}) => {
    if (_isEmpty(data) || _hasError(data)) {
      return []
    }

    const series = _.get(data, ['results', '0', 'series', '0'])
    return series.values.map(measurement => {
      return measurement[0]
    })
  })
}

function parseSeries(series) {
  const ident = /\w+/
  const tag = /,?([^=]+)=([^,]+)/

  function parseMeasurement(s, obj) {
    const match = ident.exec(s)
    const measurement = match[0]
    if (measurement) {
      obj.measurement = measurement
    }
    return s.slice(match.index + measurement.length)
  }

  function parseTag(s, obj) {
    const match = tag.exec(s)

    if (match) {
      const kv = match[0]
      const key = match[1]
      const value = match[2]

      if (key) {
        if (!obj.tags) {
          obj.tags = {}
        }
        obj.tags[key] = value
      }
      return s.slice(match.index + kv.length)
    }

    return ''
  }

  let workStr = series.slice()
  const out = {}

  // Consume measurement
  workStr = parseMeasurement(workStr, out)

  // Consume tags
  while (workStr.length > 0) {
    workStr = parseTag(workStr, out)
  }

  return out
}

function _isEmpty(resp) {
  return !resp.results[0].series
}

function _hasError(resp) {
  return !!resp.results[0].error
}
<commit_msg>Supply database to request for hosts<commit_after>import {proxy} from 'utils/queryUrlGenerator'
import replaceTemplate from 'src/tempVars/utils/replace'
import AJAX from 'utils/ajax'
import {
  linksFromHosts,
  updateActiveHostLink,
} from 'src/hosts/utils/hostsSwitcherLinks'
import _ from 'lodash'

export const getCpuAndLoadForHosts = (
  proxyLink,
  telegrafDB,
  telegrafSystemInterval,
  tempVars
) => {
  const query = replaceTemplate(
    `SELECT mean("usage_user") FROM \":db:\".\":rp:\".\"cpu\" WHERE "cpu" = 'cpu-total' AND time > now() - 10m GROUP BY host;
      SELECT mean("load1") FROM \":db:\".\":rp:\".\"system\" WHERE time > now() - 10m GROUP BY host;
      SELECT non_negative_derivative(mean(uptime)) AS deltaUptime FROM \":db:\".\":rp:\".\"system\" WHERE time > now() - ${telegrafSystemInterval} * 10 GROUP BY host, time(${telegrafSystemInterval}) fill(0);
      SELECT mean("Percent_Processor_Time") FROM \":db:\".\":rp:\".\"win_cpu\" WHERE time > now() - 10m GROUP BY host;
      SELECT mean("Processor_Queue_Length") FROM \":db:\".\":rp:\".\"win_system\" WHERE time > now() - 10s GROUP BY host;
      SELECT non_negative_derivative(mean("System_Up_Time")) AS winDeltaUptime FROM \":db:\".\":rp:\".\"win_system\" WHERE time > now() - ${telegrafSystemInterval} * 10 GROUP BY host, time(${telegrafSystemInterval}) fill(0);
      SHOW TAG VALUES WITH KEY = "host";`,
    tempVars
  )

  return proxy({
    source: proxyLink,
    query,
    db: telegrafDB,
    tempVars,
  }).then(resp => {
    const hosts = {}
    const precision = 100
    const cpuSeries = _.get(resp, ['data', 'results', '0', 'series'], [])
    const loadSeries = _.get(resp, ['data', 'results', '1', 'series'], [])
    const uptimeSeries = _.get(resp, ['data', 'results', '2', 'series'], [])
    const winCPUSeries = _.get(resp, ['data', 'results', '3', 'series'], [])
    const winLoadSeries = _.get(resp, ['data', 'results', '4', 'series'], [])
    const winUptimeSeries = _.get(resp, ['data', 'results', '5', 'series'], [])
    const allHostsSeries = _.get(resp, ['data', 'results', '6', 'series'], [])

    allHostsSeries.forEach(s => {
      const hostnameIndex = s.columns.findIndex(col => col === 'value')
      s.values.forEach(v => {
        const hostname = v[hostnameIndex]
        hosts[hostname] = {
          name: hostname,
          deltaUptime: -1,
          cpu: 0.0,
          load: 0.0,
        }
      })
    })

    cpuSeries.forEach(s => {
      const meanIndex = s.columns.findIndex(col => col === 'mean')
      hosts[s.tags.host] = {
        name: s.tags.host,
        cpu: Math.round(s.values[0][meanIndex] * precision) / precision,
      }
    })

    loadSeries.forEach(s => {
      const meanIndex = s.columns.findIndex(col => col === 'mean')
      hosts[s.tags.host].load =
        Math.round(s.values[0][meanIndex] * precision) / precision
    })

    uptimeSeries.forEach(s => {
      const uptimeIndex = s.columns.findIndex(col => col === 'deltaUptime')
      hosts[s.tags.host].deltaUptime =
        s.values[s.values.length - 1][uptimeIndex]
    })

    winCPUSeries.forEach(s => {
      const meanIndex = s.columns.findIndex(col => col === 'mean')
      hosts[s.tags.host] = {
        name: s.tags.host,
        cpu: Math.round(s.values[0][meanIndex] * precision) / precision,
      }
    })

    winLoadSeries.forEach(s => {
      const meanIndex = s.columns.findIndex(col => col === 'mean')
      hosts[s.tags.host].load =
        Math.round(s.values[0][meanIndex] * precision) / precision
    })

    winUptimeSeries.forEach(s => {
      const winUptimeIndex = s.columns.findIndex(
        col => col === 'winDeltaUptime'
      )
      hosts[s.tags.host].winDeltaUptime =
        s.values[s.values.length - 1][winUptimeIndex]
    })

    return hosts
  })
}

async function getAllHosts(source) {
  const {
    telegraf,
    links: {proxy: proxyLink},
  } = source

  try {
    const resp = await proxy({
      source: proxyLink,
      query: 'show tag values with key = "host"',
      db: telegraf,
    })

    const hosts = {}
    const allHostsSeries = _.get(resp, ['data', 'results', '0', 'series'], [])

    allHostsSeries.forEach(s => {
      const hostnameIndex = s.columns.findIndex(col => col === 'value')
      s.values.forEach(v => {
        const hostname = v[hostnameIndex]
        hosts[hostname] = {
          name: hostname,
        }
      })
    })

    return hosts
  } catch (error) {
    console.error(error) // eslint-disable-line no-console
    throw error
  }
}

export const loadHostsLinks = async (
  source,
  {activeHost = {}, getHostNamesAJAX = getAllHosts} = {}
) => {
  const hostNames = await getHostNamesAJAX(source)
  const allLinks = linksFromHosts(hostNames, source)

  return updateActiveHostLink(allLinks, activeHost)
}

export const getLayouts = () =>
  AJAX({
    method: 'GET',
    resource: 'layouts',
  })

export const getAppsForHost = (proxyLink, host, appLayouts, telegrafDB) => {
  const measurements = appLayouts.map(m => `^${m.measurement}$`).join('|')
  const measurementsToApps = _.zipObject(
    appLayouts.map(m => m.measurement),
    appLayouts.map(({app}) => app)
  )

  return proxy({
    source: proxyLink,
    query: `show series from /${measurements}/ where host = '${host}'`,
    db: telegrafDB,
  }).then(resp => {
    const result = {apps: [], tags: {}}
    const allSeries = _.get(resp, 'data.results.0.series.0.values', [])

    allSeries.forEach(([series]) => {
      const seriesObj = parseSeries(series)
      const measurement = seriesObj.measurement

      result.apps = _.uniq(result.apps.concat(measurementsToApps[measurement]))
      _.assign(result.tags, seriesObj.tags)
    })

    return result
  })
}

export const getAppsForHosts = (proxyLink, hosts, appLayouts, telegrafDB) => {
  const measurements = appLayouts.map(m => `^${m.measurement}$`).join('|')
  const measurementsToApps = _.zipObject(
    appLayouts.map(m => m.measurement),
    appLayouts.map(({app}) => app)
  )

  return proxy({
    source: proxyLink,
    query: `show series from /${measurements}/`,
    db: telegrafDB,
  }).then(resp => {
    const newHosts = Object.assign({}, hosts)
    const allSeries = _.get(
      resp,
      ['data', 'results', '0', 'series', '0', 'values'],
      []
    )

    allSeries.forEach(([series]) => {
      const seriesObj = parseSeries(series)
      const measurement = seriesObj.measurement
      const host = _.get(seriesObj, ['tags', 'host'], '')

      if (!newHosts[host]) {
        return
      }

      if (!newHosts[host].apps) {
        newHosts[host].apps = []
      }

      if (!newHosts[host].tags) {
        newHosts[host].tags = {}
      }

      newHosts[host].apps = _.uniq(
        newHosts[host].apps.concat(measurementsToApps[measurement])
      )
      _.assign(newHosts[host].tags, seriesObj.tags)
    })

    return newHosts
  })
}

export function getMeasurementsForHost(source, host) {
  return proxy({
    source: source.links.proxy,
    query: `SHOW MEASUREMENTS WHERE "host" = '${host}'`,
    db: source.telegraf,
  }).then(({data}) => {
    if (_isEmpty(data) || _hasError(data)) {
      return []
    }

    const series = _.get(data, ['results', '0', 'series', '0'])
    return series.values.map(measurement => {
      return measurement[0]
    })
  })
}

function parseSeries(series) {
  const ident = /\w+/
  const tag = /,?([^=]+)=([^,]+)/

  function parseMeasurement(s, obj) {
    const match = ident.exec(s)
    const measurement = match[0]
    if (measurement) {
      obj.measurement = measurement
    }
    return s.slice(match.index + measurement.length)
  }

  function parseTag(s, obj) {
    const match = tag.exec(s)

    if (match) {
      const kv = match[0]
      const key = match[1]
      const value = match[2]

      if (key) {
        if (!obj.tags) {
          obj.tags = {}
        }
        obj.tags[key] = value
      }
      return s.slice(match.index + kv.length)
    }

    return ''
  }

  let workStr = series.slice()
  const out = {}

  // Consume measurement
  workStr = parseMeasurement(workStr, out)

  // Consume tags
  while (workStr.length > 0) {
    workStr = parseTag(workStr, out)
  }

  return out
}

function _isEmpty(resp) {
  return !resp.results[0].series
}

function _hasError(resp) {
  return !!resp.results[0].error
}
<|endoftext|>


<commit_before>/**
 * Helpers for working with map/filter/reduce
 *
 * Should play nice with Node/RequireJS/VanillaJS
 */

/*globals module:true, define:true*/
(function(root, factory) {
    "use strict";

    root.f = factory.call({});

    if (typeof define === "function" && define.amd) {
        define(root.f.always(root.f));
    } else if (typeof module === "object" && "exports" in module) {
        module.exports = root.f;
    }
}(this, function() {
    "use strict";

    function toarray(a) {
        return Array.prototype.slice.call(a, 0);
    }

    /**
     * Determines if an input is an odd number
     */
    function odd(n) {
        return n % 2 !== 0;
    }
    this.odd = odd;

    /**
     * Determines if an input is an even number
     */
    function even(n) {
        return n % 2 === 0;
    }
    this.even = even;

    /**
     * Multiplies the input
     */
    function mul(a, b) {
        return Number(a) * Number(b);
    }
    this.mul = mul;

    /**
     * Adds the input
     */
    function add(a, b) {
        return Number(a) + Number(b);
    }
    this.add = add;

    /**
     * Subtracts the input
     */
    function sub(a, b) {
        return Number(a) - Number(b);
    }
    this.sub = sub;

    /**
     * Divides the input
     */
    function div(a, b) {
        return Number(a) / Number(b);
    }
    this.div = div;

    /**
     * Always returns the original input
     */
    function always(x) {
        return function () {
            return x;
        };
    }
    this.always = always;

    /**
     * Extracts the head from array/string
     */
    function head(a, until) {
        until = until || 1;
        return a.slice(0, until);
    }
    this.head = head;

    /**
     * Extracts the tail from an array/string
     */
    function tail(a, from) {
        from = from || 1;
        return a.slice(from);
    }
    this.tail = tail;

    /**
     * Returns the first element of an array
     */
    function first(a) {
        return a[0];
    }
    this.first = first;

    /**
     * Returns the last element of an array
     */
    function last(a) {
        return a[a.length -1];
    }
    this.last = last;

    /**
     * Returns the sign of input
     */
    function sign(n) {
        n = Number(n);
        return isNaN(n) || n === 0 ? 0 : Math.abs(n) / n;
    }
    this.sign = sign;

    /**
     * Inverts a boolean input
     */
    function inverse(b) {
        return !b;
    }
    this.inverse = inverse;

    /**
     * Returns true for an empty string/array
     */
    function empty(s) {
        return s.length === 0;
    }
    this.empty = empty;

    /**
     * Trim a string
     */
    function trim(s) {
        return s.replace(/^\s+|\s+$/g, "");
    }
    this.trim = trim;

    /**
     * Constrains a number within defined boundaries. Inclusive.
     */
    function constrain(lower, upper) {
        return function (v) {
            return Math.min(upper, Math.max(lower, v));
        };
    }
    this.constrain = constrain;

    /**
     * Check whether a number is between set boundaries. Inclusive.
     */
    function between(lower, upper) {
        var c = constrain(lower, upper);
        return function (v) {
            return c(v) === v;
        };
    }
    this.between = between;

    /**
     * Check if a property exists
     */
    function has(pname) {
        return function (o) {
            return Object.hasOwnProperty.call(o, pname);
        };
    }
    this.has = has;

    /**
     * Returns a property key from input
     */
    function prop(pname) {
        return function (o) {
            return o[pname];
        };
    }
    this.prop = prop;
    this.pluck = prop;

    /**
     * Invokes a function fn on input
     */
    function func(fn) {
        return function (o) {
            return o[fn]();
        };
    }
    this.func = func;

    /**
     * Match a value against a regex
     */
    function match(re) {
        if (! (re instanceof RegExp)) {
            re = new RegExp("^" + re + "$");
        }
        return function (v) {
            return re.test(v);
        };
    }
    this.match = match;

    /**
     * Shorthand for an inverted matcher
     */
    function omit(re) {
        return compose(inverse, match(re));
    }
    this.omit = omit;

    /**
     * Splits a string on a separator to limit parts
     * The sum of the returned parts is always the
     * original input.
     */
    function split(sep, limit) {
        limit = limit && Math.max(limit, 1) || false;
        return function (s) {
            var p = s.split(sep);
            var r = p.splice(limit);
            if (limit && r.length) {
                r.unshift(p.pop());
                p.push(r.join(sep));
            }
            return p;
        };
    }
    this.split = split;

    /**
     * Maps an array to an object
     */
    function object(keys) {
        return function (a) {
            var ret = {};
            var k = keys.slice(0);
            while (k.length > 0) {
                ret[k.shift()] = a.shift();
            }
            return ret;
        };
    }
    this.object = object;

    /**
     * Returns a partial application
     */
    function partial() {
        var outerargs = toarray(arguments);
        var func = outerargs.shift();
        var thisp;

        if (typeof(func) !== "function") {
            thisp = func;
            func = outerargs.shift();
        }

        return function partialinner() {
            var args = outerargs.concat(toarray(arguments));
            return func.apply(thisp || this, args);
        };
    }
    this.partial = partial;

    /**
     * Composes a function-stack to pass a value through.
     * Evaluates right-to-left, i.e. the final returned value
     * is returned by the first argument
     */
    function compose() {
        var fstack = toarray(arguments).reverse();
        var thisp;

        if (typeof(last(fstack)) !== "function") {
            thisp = fstack.pop();
        }

        return function composeinner() {
            var r = arguments;
            fstack.forEach(function (f) {
                r = [f.apply(thisp || this, r)];
            });
            return r[0];
        };
    }
    this.compose = compose;

    return this;
}));
<commit_msg>Removed number wrappers<commit_after>/**
 * Helpers for working with map/filter/reduce
 *
 * Should play nice with Node/RequireJS/VanillaJS
 */

/*globals module:true, define:true*/
(function(root, factory) {
    "use strict";

    root.f = factory.call({});

    if (typeof define === "function" && define.amd) {
        define(root.f.always(root.f));
    } else if (typeof module === "object" && "exports" in module) {
        module.exports = root.f;
    }
}(this, function() {
    "use strict";

    function toarray(a) {
        return Array.prototype.slice.call(a, 0);
    }

    /**
     * Determines if an input is an odd number
     */
    function odd(n) {
        return n % 2 !== 0;
    }
    this.odd = odd;

    /**
     * Determines if an input is an even number
     */
    function even(n) {
        return n % 2 === 0;
    }
    this.even = even;

    /**
     * Multiplies the input
     */
    function mul(a, b) {
        return a * b;
    }
    this.mul = mul;

    /**
     * Adds the input
     */
    function add(a, b) {
        return a + b;
    }
    this.add = add;

    /**
     * Subtracts the input
     */
    function sub(a, b) {
        return a - b;
    }
    this.sub = sub;

    /**
     * Divides the input
     */
    function div(a, b) {
        return a / b;
    }
    this.div = div;

    /**
     * Always returns the original input
     */
    function always(x) {
        return function () {
            return x;
        };
    }
    this.always = always;

    /**
     * Extracts the head from array/string
     */
    function head(a, until) {
        until = until || 1;
        return a.slice(0, until);
    }
    this.head = head;

    /**
     * Extracts the tail from an array/string
     */
    function tail(a, from) {
        from = from || 1;
        return a.slice(from);
    }
    this.tail = tail;

    /**
     * Returns the first element of an array
     */
    function first(a) {
        return a[0];
    }
    this.first = first;

    /**
     * Returns the last element of an array
     */
    function last(a) {
        return a[a.length -1];
    }
    this.last = last;

    /**
     * Returns the sign of input
     */
    function sign(n) {
        n = Number(n);
        return isNaN(n) || n === 0 ? 0 : Math.abs(n) / n;
    }
    this.sign = sign;

    /**
     * Inverts a boolean input
     */
    function inverse(b) {
        return !b;
    }
    this.inverse = inverse;

    /**
     * Returns true for an empty string/array
     */
    function empty(s) {
        return s.length === 0;
    }
    this.empty = empty;

    /**
     * Trim a string
     */
    function trim(s) {
        return s.replace(/^\s+|\s+$/g, "");
    }
    this.trim = trim;

    /**
     * Constrains a number within defined boundaries. Inclusive.
     */
    function constrain(lower, upper) {
        return function (v) {
            return Math.min(upper, Math.max(lower, v));
        };
    }
    this.constrain = constrain;

    /**
     * Check whether a number is between set boundaries. Inclusive.
     */
    function between(lower, upper) {
        var c = constrain(lower, upper);
        return function (v) {
            return c(v) === v;
        };
    }
    this.between = between;

    /**
     * Check if a property exists
     */
    function has(pname) {
        return function (o) {
            return Object.hasOwnProperty.call(o, pname);
        };
    }
    this.has = has;

    /**
     * Returns a property key from input
     */
    function prop(pname) {
        return function (o) {
            return o[pname];
        };
    }
    this.prop = prop;
    this.pluck = prop;

    /**
     * Invokes a function fn on input
     */
    function func(fn) {
        return function (o) {
            return o[fn]();
        };
    }
    this.func = func;

    /**
     * Match a value against a regex
     */
    function match(re) {
        if (! (re instanceof RegExp)) {
            re = new RegExp("^" + re + "$");
        }
        return function (v) {
            return re.test(v);
        };
    }
    this.match = match;

    /**
     * Shorthand for an inverted matcher
     */
    function omit(re) {
        return compose(inverse, match(re));
    }
    this.omit = omit;

    /**
     * Splits a string on a separator to limit parts
     * The sum of the returned parts is always the
     * original input.
     */
    function split(sep, limit) {
        limit = limit && Math.max(limit, 1) || false;
        return function (s) {
            var p = s.split(sep);
            var r = p.splice(limit);
            if (limit && r.length) {
                r.unshift(p.pop());
                p.push(r.join(sep));
            }
            return p;
        };
    }
    this.split = split;

    /**
     * Maps an array to an object
     */
    function object(keys) {
        return function (a) {
            var ret = {};
            var k = keys.slice(0);
            while (k.length > 0) {
                ret[k.shift()] = a.shift();
            }
            return ret;
        };
    }
    this.object = object;

    /**
     * Returns a partial application
     */
    function partial() {
        var outerargs = toarray(arguments);
        var func = outerargs.shift();
        var thisp;

        if (typeof(func) !== "function") {
            thisp = func;
            func = outerargs.shift();
        }

        return function partialinner() {
            var args = outerargs.concat(toarray(arguments));
            return func.apply(thisp || this, args);
        };
    }
    this.partial = partial;

    /**
     * Composes a function-stack to pass a value through.
     * Evaluates right-to-left, i.e. the final returned value
     * is returned by the first argument
     */
    function compose() {
        var fstack = toarray(arguments).reverse();
        var thisp;

        if (typeof(last(fstack)) !== "function") {
            thisp = fstack.pop();
        }

        return function composeinner() {
            var r = arguments;
            fstack.forEach(function (f) {
                r = [f.apply(thisp || this, r)];
            });
            return r[0];
        };
    }
    this.compose = compose;

    return this;
}));
<|endoftext|>