<commit_before>var core_services = [
	{
		"name": "dashboard",
		"extKeyRequired": true,
		"port": 4003,
		"requestTimeout": 30,
		"requestTimeoutRenewal": 5,
		"apis": [
			{
				'l': "List Environments",
				'v': "/environment/list",
				"group": "Environment",
				"groupMain": true
			},
			{
				'l': "Add Environment",
				'v': "/environment/add",
				"group": "Environment"
			},
			{
				'l': "Delete Environment",
				'v': "/environment/delete",
				"group": "Environment"
			},
			{
				'l': "Update Environment",
				'v': "/environment/update",
				"group": "Environment"
			},
			{
				'l': 'List Environment Databases',
				'v': '/environment/dbs/list',
				'group': 'Environment'
			},
			{
				'l': 'Add Environment Database',
				'v': '/environment/dbs/add',
				'group': 'Environment'
			},
			{
				'l': 'Update Environment Database',
				'v': '/environment/dbs/update',
				'group': 'Environment'
			},
			{
				'l': 'Delete Environment Database',
				'v': '/environment/dbs/delete',
				'group': 'Environment'
			},
			{
				'l': 'Update Environment Databases Prefix',
				'v': '/environment/dbs/updatePrefix',
				'group': 'Environment'
			},
			{
				'l': 'List Environment Database Clusters',
				'v': '/environment/clusters/list',
				'group': 'Environment'
			},
			{
				'l': 'Add Environment Database Cluster',
				'v': '/environment/clusters/add',
				'group': 'Environment'
			},
			{
				'l': 'Update Environment Database Cluster',
				'v': '/environment/clusters/update',
				'group': 'Environment'
			},
			{
				'l': 'Delete Environment Database Cluster',
				'v': '/environment/clusters/delete',
				'group': 'Environment'
			},
			{
				'l': "Update Product",
				'v': "/product/update",
				"group": "Product"
			},
			{
				'l': "Add Product",
				'v': "/product/add",
				"group": "Product"
			},
			{
				'l': "Delete Product",
				'v': "/product/delete",
				"group": "Product"
			},
			{
				'l': "List Products",
				'v': "/product/list",
				"group": "Product",
				"groupMain": true
			},
			{
				'l': "Get Product",
				'v': "/product/get",
				"group": "Product"
			},
			{
				'l': "Add Product Package",
				'v': "/product/packages/add",
				"group": "Product"
			},
			{
				'l': "Get Product Package",
				'v': "/product/packages/get",
				"group": "Product"
			},
			{
				"l": "Delete Product Package",
				"group": "Product",
				'v': "/product/packages/delete"
			},
			{
				"l": "List Product Packages",
				"group": "Product",
				'v': "/product/packages/list"
			},
			{
				"l": "Update Product Package",
				"group": "Product",
				'v': "/product/packages/update"
			},
			{
				"l": "List Tenants",
				"group": "Tenant",
				"groupMain": true,
				'v': "/tenant/list"
			},
			{
				"l": "Get Tenant Info",
				"group": "Tenant",
				'v': "/tenant/get"
			},
			{
				"l": "Add Tenant",
				"group": "Tenant",
				'v': "/tenant/add"
			},
			{
				"l": "Update Tenant",
				"group": "Tenant",
				'v': "/tenant/update"
			},
			{
				"l": "List Tenant Applications",
				"group": "Tenant Application",
				'v': "/tenant/application/list",
				"groupMain": true
			},
			{
				"l": "Add Tenant Application",
				"group": "Tenant Application",
				'v': "/tenant/application/add"
			},
			{
				"l": "Update Tenant Application",
				"group": "Tenant Application",
				'v': "/tenant/application/update"
			},
			{
				"l": "Delete Tenant",
				"group": "Tenant",
				'v': "/tenant/delete"
			},
			{
				"l": "Delete Tenant Application",
				"group": "Tenant Application",
				'v': "/tenant/application/delete"
			},
			{
				"v": "/tenant/application/acl/get",
				"l": "Get Tenant Application By External Key",
				"group": "Tenant Application"
			},
			{
				"l": "Add Tenant Application Key",
				"group": "Tenant Application",
				'v': "/tenant/application/key/add"
			},
			{
				"l": "List Application Key Configuration",
				"group": "Tenant Application",
				'v': "/tenant/application/key/config/list"
			},
			{
				"l": "Update Application Key Configuration",
				"group": "Tenant Application",
				'v': "/tenant/application/key/config/update"
			},
			{
				"l": "Delete Application Key",
				"group": "Tenant Application",
				'v': "/tenant/application/key/delete"
			},
			{
				"l": "Add Application External Key",
				"group": "Tenant Application",
				'v': "/tenant/application/key/ext/add"
			},
			{
				"l": "Delete Application External Key",
				"group": "Tenant Application",
				'v': "/tenant/application/key/ext/delete"
			},
			{
				"l": "List Application External Keys",
				"group": "Tenant Application",
				'v': "/tenant/application/key/ext/list"
			},
			{
				"l": "Update Application External Key",
				"group": "Tenant Application",
				'v': "/tenant/application/key/ext/update"
			},
			{
				"l": "List Application Keys",
				"group": "Tenant Application",
				'v': "/tenant/application/key/list"
			},
			{
				"l": "List Tenant oAuth Users",
				"group": "Tenant oAuth",
				"groupMain": true,
				'v': "/tenant/oauth/users/list"
			},
			{
				"l": "Add Tenant oAuth Configuration",
				"group": "Tenant oAuth",
				'v': "/tenant/oauth/add"
			},
			{
				"l": "Delete Tenant oAuth Configuration",
				"group": "Tenant oAuth",
				'v': "/tenant/oauth/delete"
			},
			{
				"l": "Get Tenant oAuth Configuration",
				"group": "Tenant oAuth",
				'v': "/tenant/oauth/list"
			},
			{
				"l": "Update Tenant oAuth Configuration",
				"group": "Tenant oAuth",
				'v': "/tenant/oauth/update"
			},
			{
				"l": "Add Tenant oAuth User",
				"group": "Tenant oAuth",
				'v': "/tenant/oauth/users/add"
			},
			{
				"l": "Delete Tenant oAuth User",
				"group": "Tenant oAuth",
				'v': "/tenant/oauth/users/delete"
			},
			{
				"l": "Update Tenant oAuth User",
				"group": "Tenant oAuth",
				'v': "/tenant/oauth/users/update"
			},
			{
				"l": "List Hosts",
				"group": "Hosts",
				'v': "/hosts/list",
				"groupMain": true
			},
			{
				"v":"/hosts/delete",
				"l": "Delete Hosts",
				"group": "Hosts"
			},
			{
				"v":"/hosts/maintenanceOperation",
				"l": "Perform Maintenance Operation",
				"group": "Hosts"
			},
			{
				"l": "List Services",
				"group": "Services",
				'v': "/services/list",
				"groupMain": true
			},
			{
				"l": "Get Tenant Security Permissions",
				"group": "Tenant",
				'v': "/tenant/permissions/get"
			}
		]
	},
	{
		"name": "urac",
		"extKeyRequired": true,
		"port": 4001,
		"requestTimeout": 30,
		"requestTimeoutRenewal": 5,
		"apis": []
	}
];

var core_hosts = [
	{
		"env": "dev",
		"name": "controller",
		"ip": "127.0.0.1"
	},
	{
		"env": "dev",
		"name": "dashboard",
		"ip": "127.0.0.1"
	},
	{
		"env": "dev",
		"name": "urac",
		"ip": "127.0.0.1"
	}
];<commit_msg>Updates to core services, added controller<commit_after>var core_services = [
	{
		"name": "controller",
		"port": 4000,
		"src": {
			"provider": "github",
			"owner": "soajs",
			"repo": "soajs.controller"
		}
	},
	{
		"name": "dashboard",
		"extKeyRequired": true,
		"port": 4003,
		"requestTimeout": 30,
		"requestTimeoutRenewal": 5,
		"apis": [
			{
				'l': "List Environments",
				'v': "/environment/list",
				"group": "Environment",
				"groupMain": true
			},
			{
				'l': "Add Environment",
				'v': "/environment/add",
				"group": "Environment"
			},
			{
				'l': "Delete Environment",
				'v': "/environment/delete",
				"group": "Environment"
			},
			{
				'l': "Update Environment",
				'v': "/environment/update",
				"group": "Environment"
			},
			{
				'l': 'List Environment Databases',
				'v': '/environment/dbs/list',
				'group': 'Environment'
			},
			{
				'l': 'Add Environment Database',
				'v': '/environment/dbs/add',
				'group': 'Environment'
			},
			{
				'l': 'Update Environment Database',
				'v': '/environment/dbs/update',
				'group': 'Environment'
			},
			{
				'l': 'Delete Environment Database',
				'v': '/environment/dbs/delete',
				'group': 'Environment'
			},
			{
				'l': 'Update Environment Databases Prefix',
				'v': '/environment/dbs/updatePrefix',
				'group': 'Environment'
			},
			{
				'l': 'List Environment Database Clusters',
				'v': '/environment/clusters/list',
				'group': 'Environment'
			},
			{
				'l': 'Add Environment Database Cluster',
				'v': '/environment/clusters/add',
				'group': 'Environment'
			},
			{
				'l': 'Update Environment Database Cluster',
				'v': '/environment/clusters/update',
				'group': 'Environment'
			},
			{
				'l': 'Delete Environment Database Cluster',
				'v': '/environment/clusters/delete',
				'group': 'Environment'
			},
			{
				'l': "Update Product",
				'v': "/product/update",
				"group": "Product"
			},
			{
				'l': "Add Product",
				'v': "/product/add",
				"group": "Product"
			},
			{
				'l': "Delete Product",
				'v': "/product/delete",
				"group": "Product"
			},
			{
				'l': "List Products",
				'v': "/product/list",
				"group": "Product",
				"groupMain": true
			},
			{
				'l': "Get Product",
				'v': "/product/get",
				"group": "Product"
			},
			{
				'l': "Add Product Package",
				'v': "/product/packages/add",
				"group": "Product"
			},
			{
				'l': "Get Product Package",
				'v': "/product/packages/get",
				"group": "Product"
			},
			{
				"l": "Delete Product Package",
				"group": "Product",
				'v': "/product/packages/delete"
			},
			{
				"l": "List Product Packages",
				"group": "Product",
				'v': "/product/packages/list"
			},
			{
				"l": "Update Product Package",
				"group": "Product",
				'v': "/product/packages/update"
			},
			{
				"l": "List Tenants",
				"group": "Tenant",
				"groupMain": true,
				'v': "/tenant/list"
			},
			{
				"l": "Get Tenant Info",
				"group": "Tenant",
				'v': "/tenant/get"
			},
			{
				"l": "Add Tenant",
				"group": "Tenant",
				'v': "/tenant/add"
			},
			{
				"l": "Update Tenant",
				"group": "Tenant",
				'v': "/tenant/update"
			},
			{
				"l": "List Tenant Applications",
				"group": "Tenant Application",
				'v': "/tenant/application/list",
				"groupMain": true
			},
			{
				"l": "Add Tenant Application",
				"group": "Tenant Application",
				'v': "/tenant/application/add"
			},
			{
				"l": "Update Tenant Application",
				"group": "Tenant Application",
				'v': "/tenant/application/update"
			},
			{
				"l": "Delete Tenant",
				"group": "Tenant",
				'v': "/tenant/delete"
			},
			{
				"l": "Delete Tenant Application",
				"group": "Tenant Application",
				'v': "/tenant/application/delete"
			},
			{
				"v": "/tenant/application/acl/get",
				"l": "Get Tenant Application By External Key",
				"group": "Tenant Application"
			},
			{
				"l": "Add Tenant Application Key",
				"group": "Tenant Application",
				'v': "/tenant/application/key/add"
			},
			{
				"l": "List Application Key Configuration",
				"group": "Tenant Application",
				'v': "/tenant/application/key/config/list"
			},
			{
				"l": "Update Application Key Configuration",
				"group": "Tenant Application",
				'v': "/tenant/application/key/config/update"
			},
			{
				"l": "Delete Application Key",
				"group": "Tenant Application",
				'v': "/tenant/application/key/delete"
			},
			{
				"l": "Add Application External Key",
				"group": "Tenant Application",
				'v': "/tenant/application/key/ext/add"
			},
			{
				"l": "Delete Application External Key",
				"group": "Tenant Application",
				'v': "/tenant/application/key/ext/delete"
			},
			{
				"l": "List Application External Keys",
				"group": "Tenant Application",
				'v': "/tenant/application/key/ext/list"
			},
			{
				"l": "Update Application External Key",
				"group": "Tenant Application",
				'v': "/tenant/application/key/ext/update"
			},
			{
				"l": "List Application Keys",
				"group": "Tenant Application",
				'v': "/tenant/application/key/list"
			},
			{
				"l": "List Tenant oAuth Users",
				"group": "Tenant oAuth",
				"groupMain": true,
				'v': "/tenant/oauth/users/list"
			},
			{
				"l": "Add Tenant oAuth Configuration",
				"group": "Tenant oAuth",
				'v': "/tenant/oauth/add"
			},
			{
				"l": "Delete Tenant oAuth Configuration",
				"group": "Tenant oAuth",
				'v': "/tenant/oauth/delete"
			},
			{
				"l": "Get Tenant oAuth Configuration",
				"group": "Tenant oAuth",
				'v': "/tenant/oauth/list"
			},
			{
				"l": "Update Tenant oAuth Configuration",
				"group": "Tenant oAuth",
				'v': "/tenant/oauth/update"
			},
			{
				"l": "Add Tenant oAuth User",
				"group": "Tenant oAuth",
				'v': "/tenant/oauth/users/add"
			},
			{
				"l": "Delete Tenant oAuth User",
				"group": "Tenant oAuth",
				'v': "/tenant/oauth/users/delete"
			},
			{
				"l": "Update Tenant oAuth User",
				"group": "Tenant oAuth",
				'v': "/tenant/oauth/users/update"
			},
			{
				"l": "List Hosts",
				"group": "Hosts",
				'v': "/hosts/list",
				"groupMain": true
			},
			{
				"v":"/hosts/delete",
				"l": "Delete Hosts",
				"group": "Hosts"
			},
			{
				"v":"/hosts/maintenanceOperation",
				"l": "Perform Maintenance Operation",
				"group": "Hosts"
			},
			{
				"l": "List Services",
				"group": "Services",
				'v': "/services/list",
				"groupMain": true
			},
			{
				"l": "Get Tenant Security Permissions",
				"group": "Tenant",
				'v': "/tenant/permissions/get"
			}
		]
	},
	{
		"name": "urac",
		"extKeyRequired": true,
		"port": 4001,
		"requestTimeout": 30,
		"requestTimeoutRenewal": 5,
		"apis": []
	}
];

var core_hosts = [
	{
		"env": "dev",
		"name": "controller",
		"ip": "127.0.0.1"
	},
	{
		"env": "dev",
		"name": "dashboard",
		"ip": "127.0.0.1"
	},
	{
		"env": "dev",
		"name": "urac",
		"ip": "127.0.0.1"
	}
];<|endoftext|>


<commit_before>export { Base };
export { BasePool };
export { Concrete };
export { ConcretePool };
export { Aggregate };
export { AggregatePool };

import * as Pool from "../util/Pool.js";

/**
 * The base class for representing valuePercentage as number berween [0, 100] inclusive. Acceptable by Receiver.Base.
 *
 * The maxPercentage always returns 100. The valuePercentage returns number berween [0, 100] inclusive.
 *
 * @member {Percentage.Base} parent The direct parent Percentage.Base of this Percentage.Base.
 */
class Base {

  /**
   *
   */
  constructor() {
    this.setAsConstructor();
  }

  /**
   * @return {Base}
   *   Return the this object.
   */
  setAsConstructor() {
    this.parent = null;
    return this;
  }

  /**
   * Sub-class should override this method (and call super.disposeResources() before return).
   */
  disposeResources() {
  }

  /**
   * After calling this method, this object should be viewed as disposed and should not be operated again.
   */
  disposeResources_and_recycleToPool() {
    this.disposeResources();
    BasePool.Singleton.recycle( this );
  }

//!!! (2020/12/12 Remarked) This object is tended to be created new every time. There is no need to reset.
//   /** Dummy. Do nothing. Sub-class should override this method. */
//   resetValue() {
//   }

  /**
   * @return {Percentage.Base} The root Percentage.Base of the whole Percentage hierarchy. The root's valuePercentage represents the whole percentage.
   */
  getRoot() {
    if ( this.parent )
      return this.parent.getRoot();
    return this; // If no parent, this is the root.
  }

  /**
   * Dummy.
   * @return {number} Always 0. Sub-class should override this method.
   */
  get valuePercentage() {
    return 0;
  }

  /** @return {number} Always 100. Sub-class should NOT override this method. */
  get maxPercentage() {
    return 100;
  }
}

/**
 * Providing ValueMax.Base
 *
 */
class BasePool extends Pool.Root {

  constructor() {
    super( Base, Base.setAsConstructor );
  }

}

/**
 * Used as default ValueMax.Base provider.
 */
BasePool.Singleton = new BasePool();

/**
 * Collect value and max and represents them as percentage.
 *
 * The Concrete.maxPercentage always returns 100. The Concrete.valuePercentage returns number berween [0, 100] inclusive.
 *
 * @member {number} value A positive number between [ 0, max ]. Usually, caller will increase it.
 * @member {number} max   A positive number indicates the maximum value of this.value.
 */
class Concrete extends Base {

  /**
   * @param {number} max
   *   The possible maximum value of this.value. If negative, indicates not initialized. This is different from maxPercentage.
   * The maxPercentage is always 100. The this.max, however, could be zero or any positive value. If max is negative, the
   * the valuePercentage will always be 0 (to avoid Aggregate.valuePercentage immediately 100). If max is zero, the
   * valuePercentage will always be 100 (to avoid divide by zero and avoid Aggregate.valuePercentage never 100).
   */
  constructor( max = -1 ) {
    super();
    this.setAsConstructor( max );
  }

  /**
   * @param {number} max
   *   The possible maximum value of this.value. If negative, indicates not initialized. This is different from maxPercentage.
   * The maxPercentage is always 100. The this.max, however, could be zero or any positive value. If max is negative, the
   * the valuePercentage will always be 0 (to avoid Aggregate.valuePercentage immediately 100). If max is zero, the
   * valuePercentage will always be 100 (to avoid divide by zero and avoid Aggregate.valuePercentage never 100).
   *
   * @return {Concrete}
   *   Return the this object.
   */
  setAsConstructor( max = -1 ) {
    super.setAsConstructor();
    this.value = 0;
    this.max = max; // Negative indicates not initialized.
    return this;
  }

  /**
   * Sub-class should override this method (and call super.disposeResources() before return).
   */
  disposeResources() {
    super.disposeResources();
  }

  /**
   * After calling this method, this object should be viewed as disposed and should not be operated again.
   */
  disposeResources_and_recycleToPool() {
    this.disposeResources();
    ConcretePool.Singleton.recycle( this );
  }

//!!! (2020/12/12 Remarked) This object is tended to be created new every time. There is no need to reset.
//   /** Reset this.value to 0. */
//   resetValue() {
//     this.value = 0;
//   }

  /**
   * @return {number}
   *   The progress as number between [0, 100] inclusive.
   *   Always 0, if this.max is negative.
   *   Always 100, if this.max is zero.
   *   Otherwise, return the ratio of ( this.value / this.max ).
   */
  get valuePercentage() {
    if (this.max < 0)
      return 0;   // If max is negative (i.e. not initialized), return 0 (to avoid Aggregate.valuePercentage immediately 100).
    if (this.max == 0)
      return 100; // If max is indeed zero, return 100 (to avoid divide by zero and avoid Aggregate.valuePercentage never 100).

    let value = Math.max( 0, Math.min( this.value, this.max ) ); // Restrict between [0, total].
    let percentage = ( value / this.max ) * 100;
    return percentage;
  }
}

/**
 * Providing ValueMax.Concrete
 *
 */
class ConcretePool extends Pool.Root {

  constructor() {
    super( Concrete, Concrete.setAsConstructor );
  }

}

/**
 * Used as default ValueMax.Concrete provider.
 */
ConcretePool.Singleton = new ConcretePool();

/**
 * Aggregate all children ( valuePercentage / maxPercentage ) and represents them as percentage.
 */
class Aggregate extends Base {

  /**
   * @param {Percentage.Base[]} children
   *   An array of Percentage.Base which will be aggregated. Their parent will be set to this Percentage.Aggregate.
   */
  constructor( children = Pool.Array.Singleton.get_or_create_by( 0 ) ) {
    super();
    this.setAsConstructor( children );
  }

  /**
   * @param {Percentage.Base[]} children
   *   An array of Percentage.Base which will be aggregated. Their parent will be set to this Percentage.Aggregate.
   *
   * @return {Aggregate}
   *   Return the this object.
   */
  setAsConstructor( children = Pool.Array.Singleton.get_or_create_by( 0 ) ) {
    super.setAsConstructor();

    this.children = children;

    for ( let i = 0; i < this.children.length; ++i ) {
      let child = this.children[ i ];
      if ( child )
        child.parent = this;
    }
    return this;
  }

  /**
   * Sub-class should override this method (and call super.disposeResources() before return).
   */
  disposeResources() {
    if ( this.children ) {

      for ( let i = 0; i < this.children.length; ++i ) {
        let child = this.children[ i ];
        if ( child )
          child.disposeResources_and_recycleToPool();
      }

      Pool.Array.Singleton.recycle( this.children );
      this.children = null;
    }
    super.disposeResources();
  }

  /**
   * After calling this method, this object should be viewed as disposed and should not be operated again.
   */
  disposeResources_and_recycleToPool() {
    this.disposeResources();
    AggregatePool.Singleton.recycle( this );
  }

  /**
   * @param {Percentage.Base} child
   *   Another Percentage.Base object. Its parent will be set to this object.
   *
   * @return {Percentage.Base} Return the child for cascading easily.
   */
  addChild( child ) {
    if ( !child )
      return null;
    this.children.push( child );
    child.parent = this;
    return child;
  }

//!!! (2020/12/12 Remarked) This object is tended to be created new every time. There is no need to reset.
//   /** Reset all children's this.value to 0. */
//   resetValue() {
//     for ( let i = 0; i < this.children.length; ++i ) {
//       let child = this.children[ i ];
//       if ( child ) {
//         child.resetValue();
//       }
//     }
//   }

  /**
   * @return {number} The sum of all children's ( valuePercentage / maxPercentage ) as number between [0, 100] inclusive.
   */
  get valuePercentage() {
    let valueSum = 0, maxSum = 0;

    // Use integer array index is faster than iterator.
    //for (let child of this.children) {
    for ( let i = 0; i < this.children.length; ++i ) {
      let child = this.children[ i ];
      if ( !child )
        continue;

      let partMax = child.maxPercentage;
      if ( partMax <= 0 )
        continue; // Skip illegal progress. (This is impossible because maxPercentage is always 100.)

      let partValue = child.valuePercentage;
      partValue = Math.max( 0, Math.min( partValue, partMax ) ); // Restrict between [0, partMax].

      valueSum += partValue;
      maxSum += partMax;
    }

    if ( maxSum <= 0 )
      return 0; // Return zero if the total max is illegal. (to avoid divide by zero.)

    let percentage = ( valueSum / maxSum ) * 100;
    return percentage;
  }

}

/**
 * Providing ValueMax.Aggregate
 *
 */
class AggregatePool extends Pool.Root {

  constructor() {
    super( Aggregate, Aggregate.setAsConstructor );
  }

}

/**
 * Used as default ValueMax.Aggregate provider.
 */
AggregatePool.Singleton = new AggregatePool();

<commit_msg>Update Percentage.js<commit_after>export { Base };
export { BasePool };
export { Concrete };
export { ConcretePool };
export { Aggregate };
export { AggregatePool };

import * as Pool from "../util/Pool.js";

/**
 * The base class for representing valuePercentage as number berween [0, 100] inclusive. Acceptable by Receiver.Base.
 *
 * The maxPercentage always returns 100. The valuePercentage returns number berween [0, 100] inclusive.
 *
 * @member {Percentage.Base} parent The direct parent Percentage.Base of this Percentage.Base.
 */
class Base {

  /**
   *
   */
  constructor() {
    this.setAsConstructor();
  }

  /**
   * @return {Base}
   *   Return the this object.
   */
  setAsConstructor() {
    this.parent = null;
    return this;
  }

  /**
   * Sub-class should override this method (and call super.disposeResources() before return).
   */
  disposeResources() {
  }

  /**
   * After calling this method, this object should be viewed as disposed and should not be operated again.
   */
  disposeResources_and_recycleToPool() {
    this.disposeResources();
    BasePool.Singleton.recycle( this );
  }

//!!! (2020/12/12 Remarked) This object is tended to be created new every time. There is no need to reset.
//   /** Dummy. Do nothing. Sub-class should override this method. */
//   resetValue() {
//   }

  /**
   * @return {Percentage.Base} The root Percentage.Base of the whole Percentage hierarchy. The root's valuePercentage represents the whole percentage.
   */
  getRoot() {
    if ( this.parent )
      return this.parent.getRoot();
    return this; // If no parent, this is the root.
  }

  /**
   * Dummy.
   * @return {number} Always 0. Sub-class should override this method.
   */
  get valuePercentage() {
    return 0;
  }

  /** @return {number} Always 100. Sub-class should NOT override this method. */
  get maxPercentage() {
    return 100;
  }
}

/**
 * Providing ValueMax.Base
 *
 */
class BasePool extends Pool.Root {

  constructor() {
    super( Base, Base.setAsConstructor );
  }

}

/**
 * Used as default ValueMax.Base provider.
 */
BasePool.Singleton = new BasePool();

/**
 * Collect value and max and represents them as percentage.
 *
 * The Concrete.maxPercentage always returns 100. The Concrete.valuePercentage returns number berween [0, 100] inclusive.
 *
 * @member {number} value A positive number between [ 0, max ]. Usually, caller will increase it.
 * @member {number} max   A positive number indicates the maximum value of this.value.
 */
class Concrete extends Base {

  /**
   * @param {number} max
   *   The possible maximum value of this.value. If negative, indicates not initialized. This is different from maxPercentage.
   * The maxPercentage is always 100. The this.max, however, could be zero or any positive value. If max is negative, the
   * the valuePercentage will always be 0 (to avoid Aggregate.valuePercentage immediately 100). If max is zero, the
   * valuePercentage will always be 100 (to avoid divide by zero and avoid Aggregate.valuePercentage never 100).
   */
  constructor( max = -1 ) {
    super();
    this.setAsConstructor( max );
  }

  /**
   * @param {number} max
   *   The possible maximum value of this.value. If negative, indicates not initialized. This is different from maxPercentage.
   * The maxPercentage is always 100. The this.max, however, could be zero or any positive value. If max is negative, the
   * the valuePercentage will always be 0 (to avoid Aggregate.valuePercentage immediately 100). If max is zero, the
   * valuePercentage will always be 100 (to avoid divide by zero and avoid Aggregate.valuePercentage never 100).
   *
   * @return {Concrete}
   *   Return the this object.
   */
  setAsConstructor( max = -1 ) {
    super.setAsConstructor();
    this.value = 0;
    this.max = max; // Negative indicates not initialized.
    return this;
  }

  /**
   * Sub-class should override this method (and call super.disposeResources() before return).
   */
  disposeResources() {
    super.disposeResources();
  }

  /**
   * After calling this method, this object should be viewed as disposed and should not be operated again.
   */
  disposeResources_and_recycleToPool() {
    this.disposeResources();
    ConcretePool.Singleton.recycle( this );
  }

//!!! (2020/12/12 Remarked) This object is tended to be created new every time. There is no need to reset.
//   /** Reset this.value to 0. */
//   resetValue() {
//     this.value = 0;
//   }

  /**
   * @return {number}
   *   The progress as number between [0, 100] inclusive.
   *   Always 0, if this.max is negative.
   *   Always 100, if this.max is zero.
   *   Otherwise, return the ratio of ( this.value / this.max ).
   */
  get valuePercentage() {
    if (this.max < 0)
      return 0;   // If max is negative (i.e. not initialized), return 0 (to avoid Aggregate.valuePercentage immediately 100).
    if (this.max == 0)
      return 100; // If max is indeed zero, return 100 (to avoid divide by zero and avoid Aggregate.valuePercentage never 100).

    let value = Math.max( 0, Math.min( this.value, this.max ) ); // Restrict between [0, total].
    let percentage = ( value / this.max ) * 100;
    return percentage;
  }
}

/**
 * Providing ValueMax.Concrete
 *
 */
class ConcretePool extends Pool.Root {

  constructor() {
    super( Concrete, Concrete.setAsConstructor );
  }

}

/**
 * Used as default ValueMax.Concrete provider.
 */
ConcretePool.Singleton = new ConcretePool();

/**
 * Aggregate all children ( valuePercentage / maxPercentage ) and represents them as percentage.
 */
class Aggregate extends Base {

  /**
   * @param {Percentage.Base[]} children
   *   An array of Percentage.Base which will be aggregated. Their parent will be set to this Percentage.Aggregate.
   */
  constructor( children = Pool.Array.Singleton.get_or_create_by( 0 ) ) {
    super();
    this.setAsConstructor( children );
  }

  /**
   * @param {Percentage.Base[]} children
   *   An array of Percentage.Base which will be aggregated. Their parent will be set to this Percentage.Aggregate.
   *
   * @return {Aggregate}
   *   Return the this object.
   */
  setAsConstructor( children = Pool.Array.Singleton.get_or_create_by( 0 ) ) {
    super.setAsConstructor();

    this.children = children;

    for ( let i = 0; i < this.children.length; ++i ) {
      let child = this.children[ i ];
      if ( child )
        child.parent = this;
    }
    return this;
  }

  /**
   * Sub-class should override this method (and call super.disposeResources() before return).
   */
  disposeResources() {
    if ( this.children ) {

      for ( let i = 0; i < this.children.length; ++i ) {
        let child = this.children[ i ];
        if ( child )
          child.disposeResources_and_recycleToPool();
      }

      this.children.length = 0;
      Pool.Array.Singleton.recycle( this.children );
      this.children = null;
    }
    super.disposeResources();
  }

  /**
   * After calling this method, this object should be viewed as disposed and should not be operated again.
   */
  disposeResources_and_recycleToPool() {
    this.disposeResources();
    AggregatePool.Singleton.recycle( this );
  }

  /**
   * @param {Percentage.Base} child
   *   Another Percentage.Base object. Its parent will be set to this object.
   *
   * @return {Percentage.Base} Return the child for cascading easily.
   */
  addChild( child ) {
    if ( !child )
      return null;
    this.children.push( child );
    child.parent = this;
    return child;
  }

//!!! (2020/12/12 Remarked) This object is tended to be created new every time. There is no need to reset.
//   /** Reset all children's this.value to 0. */
//   resetValue() {
//     for ( let i = 0; i < this.children.length; ++i ) {
//       let child = this.children[ i ];
//       if ( child ) {
//         child.resetValue();
//       }
//     }
//   }

  /**
   * @return {number} The sum of all children's ( valuePercentage / maxPercentage ) as number between [0, 100] inclusive.
   */
  get valuePercentage() {
    let valueSum = 0, maxSum = 0;

    // Use integer array index is faster than iterator.
    //for (let child of this.children) {
    for ( let i = 0; i < this.children.length; ++i ) {
      let child = this.children[ i ];
      if ( !child )
        continue;

      let partMax = child.maxPercentage;
      if ( partMax <= 0 )
        continue; // Skip illegal progress. (This is impossible because maxPercentage is always 100.)

      let partValue = child.valuePercentage;
      partValue = Math.max( 0, Math.min( partValue, partMax ) ); // Restrict between [0, partMax].

      valueSum += partValue;
      maxSum += partMax;
    }

    if ( maxSum <= 0 )
      return 0; // Return zero if the total max is illegal. (to avoid divide by zero.)

    let percentage = ( valueSum / maxSum ) * 100;
    return percentage;
  }

}

/**
 * Providing ValueMax.Aggregate
 *
 */
class AggregatePool extends Pool.Root {

  constructor() {
    super( Aggregate, Aggregate.setAsConstructor );
  }

}

/**
 * Used as default ValueMax.Aggregate provider.
 */
AggregatePool.Singleton = new AggregatePool();

<|endoftext|>


<commit_before>/*
 *   Copyright 2015 OSBI
 *
 *   Created by Andreas Verleysen
 */

/**
 * Changes the locale of the cube
 */
var ChangeLocale = Backbone.View.extend({

    initialize: function (args) {
        this.visible = false;

        this.workspace = args.workspace;

        // Create a unique ID for use as the CSS selector
        this.id = _.uniqueId("changeLocale_");
        $(this.el).attr({ id: this.id });

        // Bind table rendering to query result event
        _.bindAll(this, "render", "show", "handleClick", "processDatasourcesWithoutAddingNewTab", "toggleLocaleScreen", "setUserFeedback");

        this.datasources = new Connections({}, { dialog: this });

        // Add change locale button to saiku toolbar
        this.add_button();
        this.workspace.toolbar.changeLocale = this.show;

        // Create locale screen
        this.localeOptionsScreen = $("
  " +
            "
" +
            "
         <button class='button' id='en_US' > English </button>
 " +
            "
   	    <button class='button' id='nl_BE' > Dutch </button>
 " +
            "
   	    <button class='button' id='fr_FR' > French </button>
" +
            "
" +
            "
 " +
            " Choose your language ")
        ;

        // attach event handler
        this.localeOptionsScreen.find('button').click(this.handleClick);

        //function for menu
        $("#nav ul ").css({display: "none"}); // Opera Fix
        $("#nav li").hover(function () {
            $(this).find('ul:first').css({visibility: "visible", display: "none"}).show(400);
        }, function () {
            $(this).find('ul:first').css({visibility: "hidden"});
        });

        // Append chart to workspace
        $(this.workspace.el).find('.workspace_results')
            .prepend($(this.el).hide())
            .prepend(this.localeOptionsScreen.hide());

    },

    add_button: function () {
        var $chart_button =
            $('')
                .css({  'background-image': "url('js/saiku/plugins/ChangeLocale/images/change_locale.png')",
                    'background-repeat': 'no-repeat',
                    'background-position': '7px 7px'
                });

        var $chart_li = $('

').append($chart_button);
        $(this.workspace.toolbar.el).find("ul").append($chart_li);
    },

    show: function (event, ui) {
        this.toggleWorkspaceWithLocaleScreen();
//        if ($(event.target).hasClass('on')) {
//            this.render();
//        } else {
//            this.workspace.table.render({ data: this.workspace.query.result.lastresult() });
//        }
    },

    toggleWorkspaceWithLocaleScreen: function () {
        this.toggleWorkSpace();
        this.toggleLocaleScreen();
    },

    toggleWorkSpace: function () {
        $(this.workspace.table.el).toggle();
        $(this.el).toggle();
    },

    toggleLocaleScreen: function () {
        if (this.visible) {
            this.setUserFeedback("Choose language");
            $(this.workspace.el).find('#languageOptions').addClass('hide');
            $(this.workspace.el).find('#feedback').addClass('hide');
            this.workspace.toolbar.run_query();
        }
        else {
            $(this.workspace.el).find('#languageOptions').removeClass('hide').show();
            $(this.workspace.el).find('#feedback').removeClass('hide').show();
        }
        $(event.target).toggleClass('on');
        this.visible = !this.visible;
    },

    handleClick: function (event) {
        // Keep a reference to the main plugin object.
        var this_p = this;
        this_p.setUserFeedback("Changing locale...");
        // get selected locale
        var newLocale = $(event.target).attr('id');

        // get current selected connection name (without URL data)
        var selectedCube = $(".cubes option:selected").val();
        var selectedConnectionName = selectedCube.substring(0, selectedCube.indexOf("/"));

        // Get all connections from back-end
        var getUrl = Settings.REST_URL + "admin" + "/datasources";
        $.get(getUrl, function (data) {
            var allConnections = data;
            // match
            var selectedConnection = _.find(allConnections, function (connection) {
                return connection.connectionname == selectedConnectionName
            });

            var selectedDataSource = new DataSource(selectedConnection);

            this_p.setUserFeedback("Persisting locale...");
            this_p.persistLocaleOfDataSource(selectedDataSource, newLocale);
            this_p.setUserFeedback("Refreshing data source with new locale...");
            this_p.refreshDatasources();

        });
        return false;
    },

    persistLocaleOfDataSource: function (selectedDataSource, newLocale) {
        putUrl = Settings.REST_URL + "admin/datasources/" + selectedDataSource.id + "/locale";

        var xhr = new XMLHttpRequest();
        xhr.open('PUT', putUrl);
        xhr.setRequestHeader('Content-Type', 'application/json');
        xhr.send(newLocale);
    },

    refreshDatasources: function () {
        // keep reference to this plugin
        var this_plugin = this;
        if (typeof localStorage !== "undefined" && localStorage) {
            localStorage.clear();
        }

        Saiku.session.sessionworkspace.clear();

        if (typeof localStorage !== "undefined" && localStorage) {
            localStorage.setItem('saiku-version', Settings.VERSION);
        }

        Saiku.session.sessionworkspace.fetch({success: this_plugin.processDatasourcesWithoutAddingNewTab}, {});
    },

    processDatasourcesWithoutAddingNewTab: function (model, response) {
        // Save session in localStorage for other tabs to use
        if (typeof localStorage !== "undefined" && localStorage && localStorage.getItem('session') === null) {
            localStorage.setItem('session', JSON.stringify(response));

            // Set expiration on localStorage to one day in the future
            var expires = (new Date()).getTime() + Settings.LOCALSTORAGE_EXPIRATION;
            if (typeof localStorage !== "undefined" && localStorage) {
                localStorage.setItem('expiration', expires);
            }
        }

        // Generate cube navigation for reuse
        Saiku.session.sessionworkspace.cube_navigation = _.template($("#template-cubes").html())({
            connections: response
        });

        // Create cube objects
        Saiku.session.sessionworkspace.cube = {};
        Saiku.session.sessionworkspace.connections = response;
        _.delay(Saiku.session.sessionworkspace.prefetch_dimensions, 20);

        this.show();
    },

    setUserFeedback: function (msg) {
        $("#feedback").html(msg);
    }
});

var DataSource = Backbone.Model.extend({
//    url: "admin/datasources",
    urlRoot: "admin/datasources",
    refresh: function () {
        $.ajax({
            type: 'GET',
            url: Settings.REST_URL + "admin" + "/datasources/" + this.get("connectionname") + "/refresh"
        });
    }
});

/**
 * Start Plugin
 */
Saiku.events.bind('session:new', function (session) {

    function new_workspace(args) {
        // Add stats element
        if (typeof args.workspace.changeLocale == "undefined") {
            args.workspace.changeLocale = new ChangeLocale({ workspace: args.workspace });
        }
    };

    function clear_workspace(args) {
        if (typeof args.workspace.changeLocale != "undefined") {
            $(args.workspace.changeLocale.localeOptionsScreen).hide();
            $(args.workspace.changeLocale.el).parents().find('.workspace_results table').show();
            $(args.workspace.changeLocale.el).hide();
        }
    };

    // Attach stats to existing tabs
    for (var i = 0; i < Saiku.tabs._tabs.length; i++) {
        var tab = Saiku.tabs._tabs[i];
        new_workspace({
            workspace: tab.content
        });
    }
    ;

    // Attach stats to future tabs
    Saiku.session.bind("workspace:new", new_workspace);
    Saiku.session.bind("workspace:clear", clear_workspace);
});
<commit_msg>ChangeLocale: toggle button bug fix<commit_after>/*
 *   Copyright 2015 OSBI
 *
 *   Created by Andreas Verleysen
 */

/**
 * Changes the locale of the cube
 */
var ChangeLocale = Backbone.View.extend({

    initialize: function (args) {
        this.visible = false;

        this.workspace = args.workspace;

        // Create a unique ID for use as the CSS selector
        this.id = _.uniqueId("changeLocale_");
        $(this.el).attr({ id: this.id });

        // Bind table rendering to query result event
        _.bindAll(this, "render", "show", "handleClick", "processDatasourcesWithoutAddingNewTab", "toggleLocaleScreen", "setUserFeedback");

        this.datasources = new Connections({}, { dialog: this });

        // Add change locale button to saiku toolbar
        this.add_button();
        this.workspace.toolbar.changeLocale = this.show;

        // Create locale screen
        this.localeOptionsScreen = $("
  " +
            "
" +
            "
         <button class='button' id='en_US' > English </button>
 " +
            "
   	    <button class='button' id='nl_BE' > Dutch </button>
 " +
            "
   	    <button class='button' id='fr_FR' > French </button>
" +
            "
" +
            "
 " +
            " Choose your language ")
        ;

        // attach event handler
        this.localeOptionsScreen.find('button').click(this.handleClick);

        //function for menu
        $("#nav ul ").css({display: "none"}); // Opera Fix
        $("#nav li").hover(function () {
            $(this).find('ul:first').css({visibility: "visible", display: "none"}).show(400);
        }, function () {
            $(this).find('ul:first').css({visibility: "hidden"});
        });

        // Append chart to workspace
        $(this.workspace.el).find('.workspace_results')
            .prepend($(this.el).hide())
            .prepend(this.localeOptionsScreen.hide());

    },

    add_button: function () {
        var $chart_button =
            $('')
                .css({  'background-image': "url('js/saiku/plugins/ChangeLocale/images/change_locale.png')",
                    'background-repeat': 'no-repeat',
                    'background-position': '7px 7px'
                });

        var $chart_li = $('

').append($chart_button);
        $(this.workspace.toolbar.el).find("ul").append($chart_li);
    },

    show: function (event, ui) {
        this.toggleWorkspaceWithLocaleScreen();
//        if ($(event.target).hasClass('on')) {
//            this.render();
//        } else {
//            this.workspace.table.render({ data: this.workspace.query.result.lastresult() });
//        }
    },

    toggleWorkspaceWithLocaleScreen: function () {
        this.toggleWorkSpace();
        this.toggleLocaleScreen();
    },

    toggleWorkSpace: function () {
        $(this.workspace.table.el).toggle();
        $(this.el).toggle();
    },

    toggleLocaleScreen: function () {
        if (this.visible) {
            this.setUserFeedback("Choose language");
            $(this.workspace.el).find('#languageOptions').addClass('hide');
            $(this.workspace.el).find('#feedback').addClass('hide');
            this.workspace.toolbar.run_query();
        }
        else {
            $(this.workspace.el).find('#languageOptions').removeClass('hide').show();
            $(this.workspace.el).find('#feedback').removeClass('hide').show();
        }
        $("#changeLocaleBtn").toggleClass('on');
        this.visible = !this.visible;
    },

    handleClick: function (event) {
        // Keep a reference to the main plugin object.
        var this_p = this;
        this_p.setUserFeedback("Changing locale...");
        // get selected locale
        var newLocale = $(event.target).attr('id');

        // get current selected connection name (without URL data)
        var selectedCube = $(".cubes option:selected").val();
        var selectedConnectionName = selectedCube.substring(0, selectedCube.indexOf("/"));

        // Get all connections from back-end
        var getUrl = Settings.REST_URL + "admin" + "/datasources";
        $.get(getUrl, function (data) {
            var allConnections = data;
            // match
            var selectedConnection = _.find(allConnections, function (connection) {
                return connection.connectionname == selectedConnectionName
            });

            var selectedDataSource = new DataSource(selectedConnection);

            this_p.setUserFeedback("Persisting locale...");
            this_p.persistLocaleOfDataSource(selectedDataSource, newLocale);
            this_p.setUserFeedback("Refreshing data source with new locale...");
            this_p.refreshDatasources();

        });
        return false;
    },

    persistLocaleOfDataSource: function (selectedDataSource, newLocale) {
        putUrl = Settings.REST_URL + "admin/datasources/" + selectedDataSource.id + "/locale";

        var xhr = new XMLHttpRequest();
        xhr.open('PUT', putUrl);
        xhr.setRequestHeader('Content-Type', 'application/json');
        xhr.send(newLocale);
    },

    refreshDatasources: function () {
        // keep reference to this plugin
        var this_plugin = this;
        if (typeof localStorage !== "undefined" && localStorage) {
            localStorage.clear();
        }

        Saiku.session.sessionworkspace.clear();

        if (typeof localStorage !== "undefined" && localStorage) {
            localStorage.setItem('saiku-version', Settings.VERSION);
        }

        Saiku.session.sessionworkspace.fetch({success: this_plugin.processDatasourcesWithoutAddingNewTab}, {});
    },

    processDatasourcesWithoutAddingNewTab: function (model, response) {
        // Save session in localStorage for other tabs to use
        if (typeof localStorage !== "undefined" && localStorage && localStorage.getItem('session') === null) {
            localStorage.setItem('session', JSON.stringify(response));

            // Set expiration on localStorage to one day in the future
            var expires = (new Date()).getTime() + Settings.LOCALSTORAGE_EXPIRATION;
            if (typeof localStorage !== "undefined" && localStorage) {
                localStorage.setItem('expiration', expires);
            }
        }

        // Generate cube navigation for reuse
        Saiku.session.sessionworkspace.cube_navigation = _.template($("#template-cubes").html())({
            connections: response
        });

        // Create cube objects
        Saiku.session.sessionworkspace.cube = {};
        Saiku.session.sessionworkspace.connections = response;
        _.delay(Saiku.session.sessionworkspace.prefetch_dimensions, 20);

        this.show();
    },

    setUserFeedback: function (msg) {
        $("#feedback").html(msg);
    }
});

var DataSource = Backbone.Model.extend({
//    url: "admin/datasources",
    urlRoot: "admin/datasources",
    refresh: function () {
        $.ajax({
            type: 'GET',
            url: Settings.REST_URL + "admin" + "/datasources/" + this.get("connectionname") + "/refresh"
        });
    }
});

/**
 * Start Plugin
 */
Saiku.events.bind('session:new', function (session) {

    function new_workspace(args) {
        // Add stats element
        if (typeof args.workspace.changeLocale == "undefined") {
            args.workspace.changeLocale = new ChangeLocale({ workspace: args.workspace });
        }
    };

    function clear_workspace(args) {
        if (typeof args.workspace.changeLocale != "undefined") {
            $(args.workspace.changeLocale.localeOptionsScreen).hide();
            $(args.workspace.changeLocale.el).parents().find('.workspace_results table').show();
            $(args.workspace.changeLocale.el).hide();
        }
    };

    // Attach stats to existing tabs
    for (var i = 0; i < Saiku.tabs._tabs.length; i++) {
        var tab = Saiku.tabs._tabs[i];
        new_workspace({
            workspace: tab.content
        });
    }
    ;

    // Attach stats to future tabs
    Saiku.session.bind("workspace:new", new_workspace);
    Saiku.session.bind("workspace:clear", clear_workspace);
});
<|endoftext|>


<commit_before>/*global define*/
define([
        '../Core/clone',
        '../Core/defaultValue',
        '../Core/defined',
        '../Core/defineProperties',
        '../Renderer/createShaderSource',
        './BlendingState',
        './CullFace'
    ], function(
        clone,
        defaultValue,
        defined,
        defineProperties,
        createShaderSource,
        BlendingState,
        CullFace) {
    "use strict";

    /**
     * An appearance defines the full GLSL vertex and fragment shaders and the
     * render state used to draw a {@link Primitive}.  All appearances implement
     * this base Appearance interface.
     *
     * @alias Appearance
     * @constructor
     *
     * @see MaterialAppearance
     * @see EllipsoidSurfaceAppearance
     * @see PerInstanceColorAppearance
     * @see DebugAppearance
     * @see PolylineColorAppearance
     * @see PolylineMaterialAppearance
     */
    var Appearance = function(options) {
        options = defaultValue(options, defaultValue.EMPTY_OBJECT);

        /**
         * The material used to determine the fragment color.  Unlike other {@link Appearance}
         * properties, this is not read-only, so an appearance's material can change on the fly.
         *
         * @type Material
         *
         * @see {@link https://github.com/AnalyticalGraphicsInc/cesium/wiki/Fabric|Fabric}
         */
        this.material = options.material;

        /**
         * When true, the geometry is expected to appear translucent.
         *
         * @type {Boolean}
         *
         * @default true
         */
        this.translucent = defaultValue(options.translucent, true);

        this._vertexShaderSource = options.vertexShaderSource;
        this._fragmentShaderSource = options.fragmentShaderSource;
        this._renderState = options.renderState;
        this._closed = defaultValue(options.closed, false);
    };

    defineProperties(Appearance.prototype, {
        /**
         * The GLSL source code for the vertex shader.
         *
         * @memberof Appearance.prototype
         *
         * @type {String}
         * @readonly
         */
        vertexShaderSource : {
            get : function() {
                return this._vertexShaderSource;
            }
        },

        /**
         * The GLSL source code for the fragment shader.  The full fragment shader
         * source is built procedurally taking into account the {@link Appearance#material}.
         * Use {@link Appearance#getFragmentShaderSource} to get the full source.
         *
         * @memberof Appearance.prototype
         *
         * @type {String}
         * @readonly
         */
        fragmentShaderSource : {
            get : function() {
                return this._fragmentShaderSource;
            }
        },

        /**
         * The WebGL fixed-function state to use when rendering the geometry.
         *
         * @memberof Appearance.prototype
         *
         * @type {Object}
         * @readonly
         */
        renderState : {
            get : function() {
                return this._renderState;
            }
        },

        /**
         * When true, the geometry is expected to be closed.
         *
         * @memberof Appearance.prototype
         *
         * @type {Boolean}
         * @readonly
         *
         * @default false
         */
        closed : {
            get : function() {
                return this._closed;
            }
        }
    });

    /**
     * Procedurally creates the full GLSL fragment shader source for this appearance
     * taking into account {@link Appearance#fragmentShaderSource} and {@link Appearance#material}.
     *
     * @returns {String} The full GLSL fragment shader source.
     */
    Appearance.prototype.getFragmentShaderSource = function() {
        return createShaderSource({
            defines : [this.flat ? 'FLAT' : '', this.faceForward ? 'FACE_FORWARD' : ''],
            sources : [defined(this.material) ? this.material.shaderSource : '', this.fragmentShaderSource]
        });
    };

    /**
     * Determines if the geometry is translucent based on {@link Appearance#translucent} and {@link Material#isTranslucent}.
     *
     * @returns {Boolean} true if the appearance is translucent.
     */
    Appearance.prototype.isTranslucent = function() {
        return (defined(this.material) && this.material.isTranslucent()) || (!defined(this.material) && this.translucent);
    };

    /**
     * Creates a render state.  This is not the final {@link RenderState} instance; instead,
     * it can contain a subset of render state properties identical to renderState
     * passed to {@link Context#createRenderState}.
     *
     * @returns {Object} The render state.
     */
    Appearance.prototype.getRenderState = function() {
        var translucent = this.isTranslucent();
        var rs = clone(this.renderState, false);
        if (translucent) {
            rs.depthMask = false;
            rs.blending = BlendingState.ALPHA_BLEND;
        } else {
            rs.depthMask = true;
        }
        return rs;
    };

    /**
     * @private
     */
    Appearance.getDefaultRenderState = function(translucent, closed) {
        var rs = {
            depthTest : {
                enabled : true
            }
        };

        if (translucent) {
            rs.depthMask = false;
            rs.blending = BlendingState.ALPHA_BLEND;
        }

        if (closed) {
            rs.cull = {
                enabled : true,
                face : CullFace.BACK
            };
        }

        return rs;
    };

    return Appearance;
});<commit_msg>Reference doc for Appearance options<commit_after>/*global define*/
define([
        '../Core/clone',
        '../Core/defaultValue',
        '../Core/defined',
        '../Core/defineProperties',
        '../Renderer/createShaderSource',
        './BlendingState',
        './CullFace'
    ], function(
        clone,
        defaultValue,
        defined,
        defineProperties,
        createShaderSource,
        BlendingState,
        CullFace) {
    "use strict";

    /**
     * An appearance defines the full GLSL vertex and fragment shaders and the
     * render state used to draw a {@link Primitive}.  All appearances implement
     * this base Appearance interface.
     *
     * @alias Appearance
     * @constructor
     *
     * @param {Object} [options] Object with the following properties:
     * @param {Boolean} [options.translucent=true] When true, the geometry is expected to appear translucent so {@link Appearance#renderState} has alpha blending enabled.
     * @param {Boolean} [options.closed=false] When true, the geometry is expected to be closed so {@link lAppearance#renderState} has backface culling enabled.
     * @param {Material} [options.material=Material.ColorType] The material used to determine the fragment color.
     * @param {String} [options.vertexShaderSource] Optional GLSL vertex shader source to override the default vertex shader.
     * @param {String} [options.fragmentShaderSource] Optional GLSL fragment shader source to override the default fragment shader.
     * @param {RenderState} [options.renderState] Optional render state to override the default render state.
     *
     * @see MaterialAppearance
     * @see EllipsoidSurfaceAppearance
     * @see PerInstanceColorAppearance
     * @see DebugAppearance
     * @see PolylineColorAppearance
     * @see PolylineMaterialAppearance
     */
    var Appearance = function(options) {
        options = defaultValue(options, defaultValue.EMPTY_OBJECT);

        /**
         * The material used to determine the fragment color.  Unlike other {@link Appearance}
         * properties, this is not read-only, so an appearance's material can change on the fly.
         *
         * @type Material
         *
         * @see {@link https://github.com/AnalyticalGraphicsInc/cesium/wiki/Fabric|Fabric}
         */
        this.material = options.material;

        /**
         * When true, the geometry is expected to appear translucent.
         *
         * @type {Boolean}
         *
         * @default true
         */
        this.translucent = defaultValue(options.translucent, true);

        this._vertexShaderSource = options.vertexShaderSource;
        this._fragmentShaderSource = options.fragmentShaderSource;
        this._renderState = options.renderState;
        this._closed = defaultValue(options.closed, false);
    };

    defineProperties(Appearance.prototype, {
        /**
         * The GLSL source code for the vertex shader.
         *
         * @memberof Appearance.prototype
         *
         * @type {String}
         * @readonly
         */
        vertexShaderSource : {
            get : function() {
                return this._vertexShaderSource;
            }
        },

        /**
         * The GLSL source code for the fragment shader.  The full fragment shader
         * source is built procedurally taking into account the {@link Appearance#material}.
         * Use {@link Appearance#getFragmentShaderSource} to get the full source.
         *
         * @memberof Appearance.prototype
         *
         * @type {String}
         * @readonly
         */
        fragmentShaderSource : {
            get : function() {
                return this._fragmentShaderSource;
            }
        },

        /**
         * The WebGL fixed-function state to use when rendering the geometry.
         *
         * @memberof Appearance.prototype
         *
         * @type {Object}
         * @readonly
         */
        renderState : {
            get : function() {
                return this._renderState;
            }
        },

        /**
         * When true, the geometry is expected to be closed.
         *
         * @memberof Appearance.prototype
         *
         * @type {Boolean}
         * @readonly
         *
         * @default false
         */
        closed : {
            get : function() {
                return this._closed;
            }
        }
    });

    /**
     * Procedurally creates the full GLSL fragment shader source for this appearance
     * taking into account {@link Appearance#fragmentShaderSource} and {@link Appearance#material}.
     *
     * @returns {String} The full GLSL fragment shader source.
     */
    Appearance.prototype.getFragmentShaderSource = function() {
        return createShaderSource({
            defines : [this.flat ? 'FLAT' : '', this.faceForward ? 'FACE_FORWARD' : ''],
            sources : [defined(this.material) ? this.material.shaderSource : '', this.fragmentShaderSource]
        });
    };

    /**
     * Determines if the geometry is translucent based on {@link Appearance#translucent} and {@link Material#isTranslucent}.
     *
     * @returns {Boolean} true if the appearance is translucent.
     */
    Appearance.prototype.isTranslucent = function() {
        return (defined(this.material) && this.material.isTranslucent()) || (!defined(this.material) && this.translucent);
    };

    /**
     * Creates a render state.  This is not the final {@link RenderState} instance; instead,
     * it can contain a subset of render state properties identical to renderState
     * passed to {@link Context#createRenderState}.
     *
     * @returns {Object} The render state.
     */
    Appearance.prototype.getRenderState = function() {
        var translucent = this.isTranslucent();
        var rs = clone(this.renderState, false);
        if (translucent) {
            rs.depthMask = false;
            rs.blending = BlendingState.ALPHA_BLEND;
        } else {
            rs.depthMask = true;
        }
        return rs;
    };

    /**
     * @private
     */
    Appearance.getDefaultRenderState = function(translucent, closed) {
        var rs = {
            depthTest : {
                enabled : true
            }
        };

        if (translucent) {
            rs.depthMask = false;
            rs.blending = BlendingState.ALPHA_BLEND;
        }

        if (closed) {
            rs.cull = {
                enabled : true,
                face : CullFace.BACK
            };
        }

        return rs;
    };

    return Appearance;
});<|endoftext|>


<commit_before>ManageIQ.angular.app.component('mainCustomButtonForm', {
  bindings: {
    genericObjectDefinitionRecordId: '@?',
    customButtonGroupRecordId: '@?',
    customButtonRecordId: '@?',
    redirectUrl: '@',
  },
  controllerAs: 'vm',
  controller: mainCustomButtonFormController,
  templateUrl: '/static/generic_object/main_custom_button_form.html.haml',
});

mainCustomButtonFormController.$inject = ['API', 'miqService', '$q', '$http'];

function mainCustomButtonFormController(API, miqService, $q, $http) {
  var vm = this;

  vm.$onInit = function() {
    vm.entity = __('Custom Button');
    vm.saveable = miqService.saveable;
    vm.afterGet = false;

    vm.customButtonModel = {
      button_type: 'default',
      name: '',
      description: '',
      display: true,
      button_icon: '',
      button_color: '#4d5258',
      options: {},
      resource_action: {},
      visibility: {},
      dialog_id: undefined,
      display_for: 'single',
      submit_how: 'one',
      ae_instance: 'Request',
      ae_message: 'create',
      request: '',
      roles: ['_ALL_'],
      attribute_names: [],
      attribute_values: [],
      attributeValuesTableChanged: false,
      current_visibility: 'all',
      available_roles: [],
      uri_attributes: {"request": "", service_template: null, hosts: null},
    };

    vm.dialogs = [];
    vm.button_types = [];
    vm.ae_instances = [];
    vm.templates = [];
    vm.inventory = 'localhost';

    vm.attributeValueTableHeaders = [__('Name'), __('Value'), __('Actions')];

    vm.display_for = [
      {id: 'single', name: __('Single Entity')},
      {id: 'list', name: __('List')},
      {id: 'both', name: __('Single and List')},
    ];

    vm.submit_how = [
      {id: 'all', name: __('Submit all')},
      {id: 'one', name: __('One by one')},
    ];

    vm.visibilities = [
      {id: 'all', name: '<' + __('To All') + '>'},
      {id: 'role', name: '<' + __('By Role') + '>'},
    ];

    miqService.sparkleOn();
    var optionsPromise = API.options('/api/custom_buttons')
      .then(function(response) {
        _.forEach(response.data.custom_button_types, function(name, id) {
          vm.button_types.push({id: id, name: name});
        });
      });
    var dataPromise;
    if (vm.customButtonRecordId) {
      vm.newRecord = false;
      dataPromise = API.get('/api/custom_buttons/' + vm.customButtonRecordId + '?attributes=resource_action,uri_attributes')
        .then(getCustomButtonFormData)
        .catch(miqService.handleFailure);
    } else {
      vm.newRecord = true;
      vm.modelCopy = angular.copy( vm.customButtonModel );
    }

    var serviceDialogsPromise = API.get('/api/service_dialogs?expand=resources&attributes=label')
      .then(function(response) {
        _.forEach(response.resources, function(item) {
          vm.dialogs.push({id: item.id, label: item.label});
        });
      });

    var rolesPromise = API.get('/api/roles?expand=resources&attributes=name')
      .then(function(response) {
        _.forEach(response.resources, function(item) {
          vm.customButtonModel.available_roles.push({name: item.name, value: false});
        });
      });

    var instancesPromise = $http.get('/generic_object_definition/retrieve_distinct_instances_across_domains')
      .then(function(response) {
        _.forEach(response.data.distinct_instances_across_domains, function(item) {
          vm.ae_instances.push({id: item, name: item});
        });
      });

    var serviceTemplateAnsiblePlaybooks = $http.get('/generic_object_definition/service_template_ansible_playbooks')
      .then(function(response) {
        vm.templates = response.data.templates;
      });

    $q.all([optionsPromise, dataPromise, serviceDialogsPromise, rolesPromise, instancesPromise, serviceTemplateAnsiblePlaybooks])
      .then(function() {
        vm.afterGet = true;
        miqService.sparkleOff();
      })
      .catch(miqService.handleFailure);
  };

  vm.cancelClicked = function() {
    miqService.sparkleOn();
    if (vm.newRecord) {
      miqService.redirectBack(sprintf(__('Creation of new %s was canceled by the user.'), vm.entity), 'warning', vm.redirectUrl);
    } else {
      miqService.redirectBack(sprintf(__('Edit of %s "%s" was canceled by the user.'), vm.entity, vm.customButtonModel.name), 'warning', vm.redirectUrl);
    }
  };

  vm.resetClicked = function(angularForm) {
    vm.customButtonModel = angular.element.extend(true, {}, vm.modelCopy);

    assignAllObjectsToKeyValueArrays(true);

    angularForm.$setUntouched(true);
    angularForm.$setPristine(true);

    miqService.miqFlash('warn', __('All changes have been reset'));
  };

  vm.saveClicked = function() {
    miqService.sparkleOn();
    miqService.miqFlashClear(); // remove previous messages
    var saveMsg = sprintf(__('%s "%s" has been successfully saved.'), vm.entity, vm.customButtonModel.name);
    return API.put('/api/custom_buttons/' + vm.customButtonRecordId, vm.prepSaveObject(), {skipErrors: [400]})
      .then(function() {
        miqService.redirectBack(saveMsg, 'success', vm.redirectUrl);
      })
      .catch(handleErrorMessages);
  };

  vm.addClicked = function() {
    miqService.sparkleOn();
    miqService.miqFlashClear(); // remove previous messages
    var saveMsg = sprintf(__('%s "%s" has been successfully added.'), vm.entity, vm.customButtonModel.name);
    return API.post('/api/custom_buttons/', vm.prepSaveObject(), {skipErrors: [400]})
      .then(function(response) {
        if (vm.customButtonGroupRecordId) {
          var saveMsgBtnInGrp = sprintf(__('%s "%s" has been successfully added under the selected button group.'), vm.entity, vm.customButtonModel.name);
          return $http.post('/generic_object_definition/add_button_in_group/' + vm.customButtonGroupRecordId + '?button_id=' + response.results[0].id)
            .then(function() {
              miqService.redirectBack(saveMsgBtnInGrp, 'success', vm.redirectUrl);
            })
            .catch(miqService.handleFailure);
        } else {
          miqService.redirectBack(saveMsg, 'success', vm.redirectUrl);
        }
      })
      .catch(handleErrorMessages);
  };

  vm.prepSaveObject = function() {
    vm.customButtonModel.options = {};
    vm.customButtonModel.resource_action = {};
    vm.customButtonModel.visibility = {};

    vm.customButtonModel.options = {
      button_icon: vm.customButtonModel.button_icon,
      button_color: vm.customButtonModel.button_color,
      button_type: vm.customButtonModel.button_type,
      display: vm.customButtonModel.display,
      display_for: vm.customButtonModel.display_for,
      submit_how: vm.customButtonModel.submit_how,
    };

    vm.customButtonModel.resource_action.ae_attributes = _.zipObject(
      vm.customButtonModel.attribute_names,
      vm.customButtonModel.attribute_values);
    vm.customButtonModel.resource_action.ae_attributes.request = vm.customButtonModel.request;
    vm.customButtonModel.resource_action.ae_attributes.service_template = vm.customButtonModel.uri_attributes.service_template;

    vm.customButtonModel.resource_action = {
      id : vm.customButtonModel.resource_id,
      dialog_id: vm.customButtonModel.dialog_id,
      ae_namespace: 'SYSTEM',
      ae_class: 'PROCESS',
      ae_instance: vm.customButtonModel.ae_instance,
      ae_message: vm.customButtonModel.ae_message,
      ae_attributes: vm.customButtonModel.resource_action.ae_attributes,
    };

    if (vm.customButtonModel.current_visibility === 'role') {
      vm.customButtonModel.roles = _.map(_.filter(vm.customButtonModel.available_roles, function(role) {
        return role.value === true;
      }), 'name');
    }
    // set uri_attributes to default for non-Ansible button
    if (vm.customButtonModel.button_type === "default") {
      vm.customButtonModel.uri_attributes = {"request": vm.customButtonModel.request, service_template: null, hosts: null};
    };

    vm.customButtonModel.visibility = {
      roles: vm.customButtonModel.roles.length === 0 ? ['_ALL_'] : vm.customButtonModel.roles,
    };

    return {
      name: vm.customButtonModel.name,
      description: vm.customButtonModel.description,
      applies_to_class: 'GenericObjectDefinition',
      applies_to_id: vm.genericObjectDefinitionRecordId,
      options: vm.customButtonModel.options,
      resource_action: vm.customButtonModel.resource_action,
      visibility: vm.customButtonModel.visibility,
      uri_attributes: vm.customButtonModel.uri_attributes,
    };
  };

  // private functions
  function handleErrorMessages(error) {
    miqSparkleOff();
    if (error.status === 400) {
      var errorMessages = error.data.error.message.split(',');
      errorMessages.forEach(function(message) {
        if (message.includes("Name has already been taken")) {
          add_flash(__("Name has already been taken"), "error");
          return Promise.reject();
        } else if (message.includes("Description has already been taken")) {
          add_flash(__("Description has already been taken"), "error");
          return Promise.reject();
        } else {
          return miqService.handleFailure();
        }
      });
    } else {
      return miqService.handleFailure();
    }
  }

  function getCustomButtonFormData(response) {
    Object.assign(vm.customButtonModel, response);

    vm.customButtonModel.button_icon = response.options.button_icon;
    vm.customButtonModel.button_color = response.options.button_color;
    vm.customButtonModel.button_type = response.options.button_type;
    vm.customButtonModel.display = response.options.display;
    vm.customButtonModel.display_for = response.options.display_for;
    vm.customButtonModel.submit_how = response.options.submit_how;

    vm.customButtonModel.resource_id = response.resource_action.id;
    vm.customButtonModel.dialog_id = response.resource_action.dialog_id;
    vm.customButtonModel.ae_instance = response.resource_action.ae_instance;
    vm.customButtonModel.ae_message = response.resource_action.ae_message;
    vm.customButtonModel.request = response.resource_action.ae_attributes.request;

    vm.customButtonModel.current_visibility = response.visibility.roles[0] === '_ALL_' || response.visibility.roles.length === 0 ? 'all' : 'role';

    vm.genericObjectDefinitionRecordId = response.applies_to_id;

    vm.customButtonModel.uri_attributes = response.uri_attributes;

    if (vm.customButtonModel.current_visibility === 'role') {
      _.forEach(vm.customButtonModel.available_roles, function(role, index) {
        if (_.includes(response.visibility.roles, role.name)) {
          vm.customButtonModel.available_roles[index].value = true;
        }
      });
    }

    delete vm.customButtonModel.resource_action.ae_attributes.request;
    delete vm.customButtonModel.resource_action.ae_attributes.service_template;
    delete vm.customButtonModel.resource_action.ae_attributes.hosts;

    vm.customButtonModel.attribute_values = Object.values(vm.customButtonModel.resource_action.ae_attributes);
    vm.customButtonModel.attribute_names = Object.keys(vm.customButtonModel.resource_action.ae_attributes);
    vm.customButtonModel.noOfAttributeValueRows = vm.customButtonModel.attribute_names.length;

    if (!vm.customButtonModel.uri_attributes.hosts || vm.customButtonModel.uri_attributes.hosts === "localhost") {
      vm.inventory = "localhost";
    } else if (vm.customButtonModel.uri_attributes.hosts === "vmdb_object") {
      vm.inventory = "vmdb_object";
    } else {
      vm.inventory = "manual";
    }
    vm.modelCopy = angular.element.extend(true, {}, vm.customButtonModel);
  }

  function assignAllObjectsToKeyValueArrays(purge) {
    if (purge) {
      vm.customButtonModel.attribute_names = [];
      vm.customButtonModel.attribute_values = [];
    }

    vm.customButtonModel.attribute_values = Object.values(vm.customButtonModel.resource_action.ae_attributes);
    vm.customButtonModel.attribute_names = Object.keys(vm.customButtonModel.resource_action.ae_attributes);
    vm.customButtonModel.noOfAttributeValueRows = vm.customButtonModel.attribute_names.length;

    vm.modelCopy = angular.element.extend(true, {}, vm.customButtonModel);
  }
}
<commit_msg>Use uri_attributes in CustomButton instead of ae_attributes in ResourceAction to make sure it's filtered correctly<commit_after>ManageIQ.angular.app.component('mainCustomButtonForm', {
  bindings: {
    genericObjectDefinitionRecordId: '@?',
    customButtonGroupRecordId: '@?',
    customButtonRecordId: '@?',
    redirectUrl: '@',
  },
  controllerAs: 'vm',
  controller: mainCustomButtonFormController,
  templateUrl: '/static/generic_object/main_custom_button_form.html.haml',
});

mainCustomButtonFormController.$inject = ['API', 'miqService', '$q', '$http'];

function mainCustomButtonFormController(API, miqService, $q, $http) {
  var vm = this;

  vm.$onInit = function() {
    vm.entity = __('Custom Button');
    vm.saveable = miqService.saveable;
    vm.afterGet = false;

    vm.customButtonModel = {
      button_type: 'default',
      name: '',
      description: '',
      display: true,
      button_icon: '',
      button_color: '#4d5258',
      options: {},
      resource_action: {},
      visibility: {},
      dialog_id: undefined,
      display_for: 'single',
      submit_how: 'one',
      ae_instance: 'Request',
      ae_message: 'create',
      request: '',
      roles: ['_ALL_'],
      attribute_names: [],
      attribute_values: [],
      attributeValuesTableChanged: false,
      current_visibility: 'all',
      available_roles: [],
      uri_attributes: {"request": "", service_template: null, hosts: null},
    };

    vm.dialogs = [];
    vm.button_types = [];
    vm.ae_instances = [];
    vm.templates = [];
    vm.inventory = 'localhost';

    vm.attributeValueTableHeaders = [__('Name'), __('Value'), __('Actions')];

    vm.display_for = [
      {id: 'single', name: __('Single Entity')},
      {id: 'list', name: __('List')},
      {id: 'both', name: __('Single and List')},
    ];

    vm.submit_how = [
      {id: 'all', name: __('Submit all')},
      {id: 'one', name: __('One by one')},
    ];

    vm.visibilities = [
      {id: 'all', name: '<' + __('To All') + '>'},
      {id: 'role', name: '<' + __('By Role') + '>'},
    ];

    miqService.sparkleOn();
    var optionsPromise = API.options('/api/custom_buttons')
      .then(function(response) {
        _.forEach(response.data.custom_button_types, function(name, id) {
          vm.button_types.push({id: id, name: name});
        });
      });
    var dataPromise;
    if (vm.customButtonRecordId) {
      vm.newRecord = false;
      dataPromise = API.get('/api/custom_buttons/' + vm.customButtonRecordId + '?attributes=resource_action,uri_attributes')
        .then(getCustomButtonFormData)
        .catch(miqService.handleFailure);
    } else {
      vm.newRecord = true;
      vm.modelCopy = angular.copy( vm.customButtonModel );
    }

    var serviceDialogsPromise = API.get('/api/service_dialogs?expand=resources&attributes=label')
      .then(function(response) {
        _.forEach(response.resources, function(item) {
          vm.dialogs.push({id: item.id, label: item.label});
        });
      });

    var rolesPromise = API.get('/api/roles?expand=resources&attributes=name')
      .then(function(response) {
        _.forEach(response.resources, function(item) {
          vm.customButtonModel.available_roles.push({name: item.name, value: false});
        });
      });

    var instancesPromise = $http.get('/generic_object_definition/retrieve_distinct_instances_across_domains')
      .then(function(response) {
        _.forEach(response.data.distinct_instances_across_domains, function(item) {
          vm.ae_instances.push({id: item, name: item});
        });
      });

    var serviceTemplateAnsiblePlaybooks = $http.get('/generic_object_definition/service_template_ansible_playbooks')
      .then(function(response) {
        vm.templates = response.data.templates;
      });

    $q.all([optionsPromise, dataPromise, serviceDialogsPromise, rolesPromise, instancesPromise, serviceTemplateAnsiblePlaybooks])
      .then(function() {
        vm.afterGet = true;
        miqService.sparkleOff();
      })
      .catch(miqService.handleFailure);
  };

  vm.cancelClicked = function() {
    miqService.sparkleOn();
    if (vm.newRecord) {
      miqService.redirectBack(sprintf(__('Creation of new %s was canceled by the user.'), vm.entity), 'warning', vm.redirectUrl);
    } else {
      miqService.redirectBack(sprintf(__('Edit of %s "%s" was canceled by the user.'), vm.entity, vm.customButtonModel.name), 'warning', vm.redirectUrl);
    }
  };

  vm.resetClicked = function(angularForm) {
    vm.customButtonModel = angular.element.extend(true, {}, vm.modelCopy);

    assignAllObjectsToKeyValueArrays(true);

    angularForm.$setUntouched(true);
    angularForm.$setPristine(true);

    miqService.miqFlash('warn', __('All changes have been reset'));
  };

  vm.saveClicked = function() {
    miqService.sparkleOn();
    miqService.miqFlashClear(); // remove previous messages
    var saveMsg = sprintf(__('%s "%s" has been successfully saved.'), vm.entity, vm.customButtonModel.name);
    return API.put('/api/custom_buttons/' + vm.customButtonRecordId, vm.prepSaveObject(), {skipErrors: [400]})
      .then(function() {
        miqService.redirectBack(saveMsg, 'success', vm.redirectUrl);
      })
      .catch(handleErrorMessages);
  };

  vm.addClicked = function() {
    miqService.sparkleOn();
    miqService.miqFlashClear(); // remove previous messages
    var saveMsg = sprintf(__('%s "%s" has been successfully added.'), vm.entity, vm.customButtonModel.name);
    return API.post('/api/custom_buttons/', vm.prepSaveObject(), {skipErrors: [400]})
      .then(function(response) {
        if (vm.customButtonGroupRecordId) {
          var saveMsgBtnInGrp = sprintf(__('%s "%s" has been successfully added under the selected button group.'), vm.entity, vm.customButtonModel.name);
          return $http.post('/generic_object_definition/add_button_in_group/' + vm.customButtonGroupRecordId + '?button_id=' + response.results[0].id)
            .then(function() {
              miqService.redirectBack(saveMsgBtnInGrp, 'success', vm.redirectUrl);
            })
            .catch(miqService.handleFailure);
        } else {
          miqService.redirectBack(saveMsg, 'success', vm.redirectUrl);
        }
      })
      .catch(handleErrorMessages);
  };

  vm.prepSaveObject = function() {
    vm.customButtonModel.options = {};
    vm.customButtonModel.resource_action = {};
    vm.customButtonModel.visibility = {};

    vm.customButtonModel.options = {
      button_icon: vm.customButtonModel.button_icon,
      button_color: vm.customButtonModel.button_color,
      button_type: vm.customButtonModel.button_type,
      display: vm.customButtonModel.display,
      display_for: vm.customButtonModel.display_for,
      submit_how: vm.customButtonModel.submit_how,
    };

    vm.customButtonModel.uri_attributes = _.zipObject(
      vm.customButtonModel.attribute_names,
      vm.customButtonModel.attribute_values);
    vm.customButtonModel.uri_attributes.request = vm.customButtonModel.request;
    vm.customButtonModel.uri_attributes.service_template = vm.customButtonModel.uri_attributes.service_template;

    vm.customButtonModel.resource_action = {
      id : vm.customButtonModel.resource_id,
      dialog_id: vm.customButtonModel.dialog_id,
      ae_namespace: 'SYSTEM',
      ae_class: 'PROCESS',
      ae_instance: vm.customButtonModel.ae_instance,
      ae_message: vm.customButtonModel.ae_message,
    };

    if (vm.customButtonModel.current_visibility === 'role') {
      vm.customButtonModel.roles = _.map(_.filter(vm.customButtonModel.available_roles, function(role) {
        return role.value === true;
      }), 'name');
    }
    // set uri_attributes to default for non-Ansible button
    if (vm.customButtonModel.button_type === "default") {
      Object.assign(vm.customButtonModel.uri_attributes, {"request": vm.customButtonModel.request, service_template: null, hosts: null});
    };

    vm.customButtonModel.visibility = {
      roles: vm.customButtonModel.roles.length === 0 ? ['_ALL_'] : vm.customButtonModel.roles,
    };

    return {
      name: vm.customButtonModel.name,
      description: vm.customButtonModel.description,
      applies_to_class: 'GenericObjectDefinition',
      applies_to_id: vm.genericObjectDefinitionRecordId,
      options: vm.customButtonModel.options,
      resource_action: vm.customButtonModel.resource_action,
      visibility: vm.customButtonModel.visibility,
      uri_attributes: vm.customButtonModel.uri_attributes,
    };
  };

  // private functions
  function handleErrorMessages(error) {
    miqSparkleOff();
    if (error.status === 400) {
      var errorMessages = error.data.error.message.split(',');
      errorMessages.forEach(function(message) {
        if (message.includes("Name has already been taken")) {
          add_flash(__("Name has already been taken"), "error");
          return Promise.reject();
        } else if (message.includes("Description has already been taken")) {
          add_flash(__("Description has already been taken"), "error");
          return Promise.reject();
        } else {
          return miqService.handleFailure();
        }
      });
    } else {
      return miqService.handleFailure();
    }
  }

  function getCustomButtonFormData(response) {
    Object.assign(vm.customButtonModel, response);

    vm.customButtonModel.button_icon = response.options.button_icon;
    vm.customButtonModel.button_color = response.options.button_color;
    vm.customButtonModel.button_type = response.options.button_type;
    vm.customButtonModel.display = response.options.display;
    vm.customButtonModel.display_for = response.options.display_for;
    vm.customButtonModel.submit_how = response.options.submit_how;

    vm.customButtonModel.resource_id = response.resource_action.id;
    vm.customButtonModel.dialog_id = response.resource_action.dialog_id;
    vm.customButtonModel.ae_instance = response.resource_action.ae_instance;
    vm.customButtonModel.ae_message = response.resource_action.ae_message;
    vm.customButtonModel.request = response.resource_action.ae_attributes.request;

    vm.customButtonModel.current_visibility = response.visibility.roles[0] === '_ALL_' || response.visibility.roles.length === 0 ? 'all' : 'role';

    vm.genericObjectDefinitionRecordId = response.applies_to_id;

    vm.customButtonModel.uri_attributes = response.uri_attributes;

    if (vm.customButtonModel.current_visibility === 'role') {
      _.forEach(vm.customButtonModel.available_roles, function(role, index) {
        if (_.includes(response.visibility.roles, role.name)) {
          vm.customButtonModel.available_roles[index].value = true;
        }
      });
    }

    delete vm.customButtonModel.resource_action.ae_attributes.request;
    delete vm.customButtonModel.resource_action.ae_attributes.service_template;
    delete vm.customButtonModel.resource_action.ae_attributes.hosts;

    vm.customButtonModel.attribute_values = Object.values(vm.customButtonModel.resource_action.ae_attributes);
    vm.customButtonModel.attribute_names = Object.keys(vm.customButtonModel.resource_action.ae_attributes);
    vm.customButtonModel.noOfAttributeValueRows = vm.customButtonModel.attribute_names.length;

    if (!vm.customButtonModel.uri_attributes.hosts || vm.customButtonModel.uri_attributes.hosts === "localhost") {
      vm.inventory = "localhost";
    } else if (vm.customButtonModel.uri_attributes.hosts === "vmdb_object") {
      vm.inventory = "vmdb_object";
    } else {
      vm.inventory = "manual";
    }
    vm.modelCopy = angular.element.extend(true, {}, vm.customButtonModel);
  }

  function assignAllObjectsToKeyValueArrays(purge) {
    if (purge) {
      vm.customButtonModel.attribute_names = [];
      vm.customButtonModel.attribute_values = [];
    }

    vm.customButtonModel.attribute_values = Object.values(vm.customButtonModel.uri_attributes);
    vm.customButtonModel.attribute_names = Object.keys(vm.customButtonModel.uri_attributes);
    vm.customButtonModel.noOfAttributeValueRows = vm.customButtonModel.attribute_names.length;

    vm.modelCopy = angular.element.extend(true, {}, vm.customButtonModel);
  }
}
<|endoftext|>


<commit_before>function setup() {
  var cnv = createCanvas(500,500);
  var w=(windowWidth - width)/2;
  var h=(windowHeight - height)/2;
  cnv.position(w,h);
  background(255);
}

function draw(){
  background(255);
	var x=0;
  var y=0;
  var planeWidth=300;
	//uncomment this code and play around
	// push();
  // rotate( -map(mouseX,0,width,0,TWO_PI));
  beginShape();
  for(var i=0; i< planeWidth; i++){

    vertex(x,y);
    vertex(x,planeWidth);
    x+=3;

    vertex(x+20, y+100);
    vertex(x+30, planeWidth);
    y+=3;
  }
  endShape();
  //uncomment this code and play around
	// pop();

	//try uncommenting this code and playing around
	//push();
  //rotate(map(mouseX,0,width,0,TWO_PI));
  beginShape(LINES);
  var a=0, b=0;
  for(var j=0; j<height;j++){
    vertex(a,b);
    vertex(width,b);
    vertex(a,b);
    vertex(width,b);
    b+=3;
  }
  endShape();
	//try uncommenting this code
	//pop();
}
<commit_msg>Update sketch_1.js<commit_after>function setup() {
  createCanvas(windowWidth,windowHeight);
  background(255);

  //try uncommenting this code and playing around
	push();
  rotate(map(mouseX,0,width,0,TWO_PI));
  beginShape(LINES);
  var a=0, b=0;
  for(var j=0; j<height;j++){
    vertex(a,b);
    vertex(width,b);
    vertex(a,b);
    vertex(width,b);
    b+=3;
  }
  endShape();
	//try uncommenting this code
	pop();
}

function draw(){
  translate(width/2, height/2);
	var x=0;
  var y=0;
  var planeWidth=300;

	//uncomment this code and play around
	push();
  rotate(-map(mouseX,0,width,0,TWO_PI));
  beginShape();
  for(var i=0; i< planeWidth; i+=5){
    vertex(x,y);
    vertex(x,planeWidth);
    x+=3;

    vertex(x+100, y+100);
    vertex(x+200, planeWidth);
    y+=3;
  }
  endShape();
  //uncomment this code and play around
	pop();

}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import charts from 'utils/charts';
import SVGIcon from 'utils/svgIcon';
import { urls } from 'js/config';
import Measure from 'react-measure';

export default class VegaChart extends Component {
  constructor(props) {
    super(props);

    this.state = {
      isError: false,
      errorMsg: null,
      showDownloadOptions: false,
      downloadOptions: [],
      chartDownloadTitle: 'analysis.png',
      chartImgDownloadUrl: null,
      chartName: '',
      toggle: false,
      dimensions: {
        width: 500,
        height: 500
      },
      description: ''
    };
  }

  handleError(errorMsg) {
    this.setState({ isError: true, errorMsg });
    if (this.props.setLoading) {
      this.props.setLoading();
    }
  }

  componentDidMount() {
    if (this.props.results.hasOwnProperty('error')) {
      this.handleError();
    } else {
      const config = this.props.results.data.attributes.widgetConfig;
      const widthSignal = {
        name: "width",
        value: "",
        on: [
          {
            events: {
              source: "window",
              type: "resize"
            },
            update: "containerSize()[0]*0.95"
          }
        ]
      };
      const heightSignal = {
        name: "height",
        value: "",
        on: [
          {
            events: {
              source: "window",
              type: "resize"
            },
            update: "containerSize()[1]*0.95"
          }
        ]
      };
      config.width = this.state.dimensions.width;
      config.height = this.state.dimensions.height;
      config.autosize = {type: 'fit', resize: true};
      config.signals.push(widthSignal);
      config.signals.push(heightSignal);
      const {setLoading, language, results} = this.props;

      if (config.data[0].url.indexOf('?&') > -1){
        const urlPieces = config.data[0].url.split('?&');
        config.data[0].url = `${urlPieces[0]}?${urlPieces[1]}`;
      }

      const dataset = this.props.results.data.attributes.dataset;
      const id = this.props.results.data.id;

      if (this.props.component === 'Report'){
        fetch(`https://production-api.globalforestwatch.org/v1/dataset/${dataset}/widget/${id}/metadata?language=${language}`).then(res => {
          res.json().then(json => {
            if (res.status !== 200) {
              this.setState({
                description: 'Error retrieving description'
              });
            } else {
            this.setState({
              description: json.data[0].attributes.description
            });
            }
          });
        });
      }

      fetch(config.data[0].url).then(res => {
        if (res.status !== 200) {
          this.handleError('Error creating analysis.');
        } else {
          res.json().then(json => {
            charts.makeVegaChart(this.chart, config, language, setLoading, this.addChartDownload);
            const downloadOptions = [];
            if (json.data && json.data.attributes && json.data.attributes.downloadUrls && !config.title) {
              const downloadUrls = json.data.attributes.downloadUrls;
              const label = 'csv';
              downloadOptions.push({label, url: downloadUrls[label]});
            }
            const chartDownloadTitle = json.data && json.data.type ? json.data.type + '-analysis.png' : 'analysis.png';
            this.setState({ downloadOptions, chartDownloadTitle });
          });
        }
      })
      .catch(() => this.handleError('Error creating analysis.'));
    }
  }

  componentDidUpdate(){
    const config = this.props.results.data.attributes.widgetConfig;
    config.width = this.state.dimensions.width;
  }

  addChartDownload = (url) => {
    this.setState({ chartImgDownloadUrl: url });
  };

  renderdownloadOptions = (option, i) => {
    const baseUrl = urls.analysisDataBaseUrl;
    return (
      <a href={option.url.includes('cartodb') ? option.url : baseUrl + option.url} target="_blank" download key={`option-${i}`}>
        Download Alerts as .CSV

    );
  };

  toggleChart = () => {
    this.setState({
      toggle: !this.state.toggle
    });
  }

  render() {
    const { isError, errorMsg, showDownloadOptions, downloadOptions, chartDownloadTitle, chartImgDownloadUrl, toggle, description } = this.state;
    const {width, height} = this.state.dimensions;
    const { results, component, reportLabel } = this.props;
    if (isError) {
      return (

{results.message || errorMsg}

      );
    } else {
      return (

          { showDownloadOptions &&

 this.setState({ showDownloadOptions: false })}>

          }
          {component === 'Report' ?

            {/*
{results.data.attributes.name}
 */}

{reportLabel}

 console.log('clicked')}>
                <SVGIcon className="vega-chart-menu-icon" id={'icon-gear'} />
                {/* SETTINGS */}

 this.setState({showDownloadOptions: !showDownloadOptions})}>
                <SVGIcon className="vega-chart-menu-icon" id={'icon-download-grey'} />

              <div className={`vega-chart-toggle-${toggle}`} onClick={this.toggleChart}>

 :

{reportLabel}

 this.setState({showDownloadOptions: !showDownloadOptions})}>
                <SVGIcon className="vega-chart-menu-icon" id={'icon-download-grey'} />

          }
          { showDownloadOptions &&

 this.setState({showDownloadOptions: !showDownloadOptions})}>
              {downloadOptions.map(this.renderdownloadOptions)}
              {this.chart &&

                  Download PNG

              }

          }
          <Measure
                bounds
                onResize={contentRect => {
                this.setState({ dimensions: contentRect.bounds });
                }}
              >
                {({ measureRef }) => (

                    <div width={width} height={height} className={`vega-chart ${toggle && 'vega-chart-hide'}`} id='AnalysisVegaChart' ref={(chart) => { this.chart = chart; }}>

                )}
          </Measure>
          {component === 'Report' &&

              <div className={`vega-chart-info-container ${toggle && 'vega-chart-hide'}`}>

                    {description}

          }

      );
    }
  }
}
<commit_msg>Clean up code<commit_after>import React, { Component } from 'react';
import charts from 'utils/charts';
import SVGIcon from 'utils/svgIcon';
import { urls } from 'js/config';
import Measure from 'react-measure';

export default class VegaChart extends Component {
  constructor(props) {
    super(props);

    this.state = {
      isError: false,
      errorMsg: null,
      showDownloadOptions: false,
      downloadOptions: [],
      chartDownloadTitle: 'analysis.png',
      chartImgDownloadUrl: null,
      chartName: '',
      toggle: false,
      dimensions: {
        width: 500,
        height: 500
      },
      description: ''
    };
  }

  handleError(errorMsg) {
    this.setState({ isError: true, errorMsg });
    if (this.props.setLoading) {
      this.props.setLoading();
    }
  }

  componentDidMount() {
    if (this.props.results.hasOwnProperty('error')) {
      this.handleError();
    } else {
      const config = this.props.results.data.attributes.widgetConfig;
      const widthSignal = {
        name: "width",
        value: "",
        on: [
          {
            events: {
              source: "window",
              type: "resize"
            },
            update: "containerSize()[0]*0.95"
          }
        ]
      };
      const heightSignal = {
        name: "height",
        value: "",
        on: [
          {
            events: {
              source: "window",
              type: "resize"
            },
            update: "containerSize()[1]*0.95"
          }
        ]
      };
      config.width = this.state.dimensions.width;
      config.height = this.state.dimensions.height;
      config.autosize = {type: 'fit', resize: true};
      config.signals.push(widthSignal);
      config.signals.push(heightSignal);
      const {setLoading, language, results} = this.props;

      if (config.data[0].url.indexOf('?&') > -1){
        const urlPieces = config.data[0].url.split('?&');
        config.data[0].url = `${urlPieces[0]}?${urlPieces[1]}`;
      }

      const dataset = this.props.results.data.attributes.dataset;
      const id = this.props.results.data.id;

      if (this.props.component === 'Report'){
        fetch(`https://production-api.globalforestwatch.org/v1/dataset/${dataset}/widget/${id}/metadata?language=${language}`).then(res => {
          res.json().then(json => {
            if (res.status !== 200) {
              this.setState({
                description: 'Error retrieving description'
              });
            } else {
            this.setState({
              description: json.data[0].attributes.description
            });
            }
          });
        });
      }

      fetch(config.data[0].url).then(res => {
        if (res.status !== 200) {
          this.handleError('Error creating analysis.');
        } else {
          res.json().then(json => {
            charts.makeVegaChart(this.chart, config, language, setLoading, this.addChartDownload);
            const downloadOptions = [];
            if (json.data && json.data.attributes && json.data.attributes.downloadUrls && !config.title) {
              const downloadUrls = json.data.attributes.downloadUrls;
              const label = 'csv';
              downloadOptions.push({label, url: downloadUrls[label]});
            }
            const chartDownloadTitle = json.data && json.data.type ? json.data.type + '-analysis.png' : 'analysis.png';
            this.setState({ downloadOptions, chartDownloadTitle });
          });
        }
      })
      .catch(() => this.handleError('Error creating analysis.'));
    }
  }

  componentDidUpdate(){
    const config = this.props.results.data.attributes.widgetConfig;
    config.width = this.state.dimensions.width;
  }

  addChartDownload = (url) => {
    this.setState({ chartImgDownloadUrl: url });
  };

  renderdownloadOptions = (option, i) => {
    const baseUrl = urls.analysisDataBaseUrl;
    return (
      <a href={option.url.includes('cartodb') ? option.url : baseUrl + option.url} target="_blank" download key={`option-${i}`}>
        Download Alerts as .CSV

    );
  };

  toggleChart = () => {
    this.setState({
      toggle: !this.state.toggle
    });
  }

  render() {
    const { isError, errorMsg, showDownloadOptions, downloadOptions, chartDownloadTitle, chartImgDownloadUrl, toggle, description } = this.state;
    const {width, height} = this.state.dimensions;
    const { results, component, reportLabel } = this.props;
    if (isError) {
      return (

{results.message || errorMsg}

      );
    } else {
      return (

          { showDownloadOptions &&

 this.setState({ showDownloadOptions: false })}>

          }
          {component === 'Report' ?

            {/*
{results.data.attributes.name}
 */}

{reportLabel}

 console.log('clicked')}>
                <SVGIcon className="vega-chart-menu-icon" id={'icon-gear'} />
                {/* SETTINGS */}

 this.setState({showDownloadOptions: !showDownloadOptions})}>
                <SVGIcon className="vega-chart-menu-icon" id={'icon-download-grey'} />

              <div className={`vega-chart-toggle-${toggle}`} onClick={this.toggleChart}>

 :

{reportLabel}

 this.setState({showDownloadOptions: !showDownloadOptions})}>
                <SVGIcon className="vega-chart-menu-icon" id={'icon-download-grey'} />

          }
          { showDownloadOptions &&

 this.setState({showDownloadOptions: !showDownloadOptions})}>
              {downloadOptions.map(this.renderdownloadOptions)}
              {this.chart &&

                  Download PNG

              }

          }
          <Measure
            bounds
            onResize={contentRect => {
            this.setState({ dimensions: contentRect.bounds });
            }}
          >
            {({ measureRef }) => (

                <div width={width} height={height} className={`vega-chart ${toggle && 'vega-chart-hide'}`} id='AnalysisVegaChart' ref={(chart) => { this.chart = chart; }}>

            )}
          </Measure>
          {component === 'Report' &&

              <div className={`vega-chart-info-container ${toggle && 'vega-chart-hide'}`}>

                    {description}

          }

      );
    }
  }
}
<|endoftext|>