<commit_before>export { ValueMax_Percentage_Base as Base };
export { ValueMax_Percentage_Concrete as Concrete };
export { ValueMax_Percentage_Aggregate as Aggregate };

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

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

  /**
   * Used as default ValueMax.Percentage.Base provider for conforming to Recyclable interface.
   */
  static Pool = new Pool.Root( "ValueMax.Percentage.Base.Pool",
    ValueMax_Percentage_Base, ValueMax_Percentage_Base.setAsConstructor );

  /**
   *
   */
  constructor() {
    super();
    ValueMax_Percentage_Base.setAsConstructor_self.call( this );
  }

  /** @override */
  static setAsConstructor() {
    super.setAsConstructor();
    ValueMax_Percentage_Base.setAsConstructor_self.call( this );
    return this;
  }

  /** @override */
  static setAsConstructor_self() {
    this.parent = null;
    this.valuePercentage_cached = undefined;
  }

  /** @override */
  disposeResources() {

    // In theory, here should remove this child object from parent (i.e. Aggregate)
    // so that the parent will not dispose (and recycle) this child object once again.
    // In fact, however, this is an expensive action (because a linear search should
    // be done. So, the better choice is to dispose the whole tree from root object
    // by caller to avoid this problem.
    //
    //if ( this.parent ) {
    //  if ( this.parent instanceof ValueMax_Percentage_Aggregate ) {
    //    // Do nothing currently.
    //  }
    //}

    this.valuePercentage_cached = undefined;
    this.parent = undefined;
    super.disposeResources();
  }

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

  /**
   * Invalidate .valuePercentage_cached (i.e. let it become undefined). This method
   * will invalidate parent's .valuePercentage_cached, too.
   */
  valuePercentage_cached_invalidate() {
    this.valuePercentage_cached = undefined;
    if ( this.parent )
      this.parent.valuePercentage_cached_invalidate();
  }

  /**
   * 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;
  }
}

/**
 * Collect value and max and represents them as percentage.
 *
 * The Concrete.maxPercentage always returns 100. The Concrete.valuePercentage returns
 * number between [ 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 ValueMax_Percentage_Concrete extends ValueMax_Percentage_Base {

  /**
   * Used as default ValueMax.Percentage.Concrete provider for conforming to Recyclable interface.
   */
  static Pool = new Pool.Root( "ValueMax.Percentage.Concrete.Pool",
    ValueMax_Percentage_Concrete, ValueMax_Percentage_Concrete.setAsConstructor );

  /**
   * @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 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();
    ValueMax_Percentage_Concrete.setAsConstructor_self.call( this, max );
  }

  /** @override */
  static setAsConstructor( max = -1 ) {
    super.setAsConstructor();
    ValueMax_Percentage_Concrete.setAsConstructor_self.call( this, max );
    return this;
  }

  /** @override */
  static setAsConstructor_self( max = -1 ) {
    this.value = 0;
    this.max = max; // Negative indicates not initialized.
  }

  /** @override */
  disposeResources() {
    this.max = undefined;
    this.value = undefined;
    super.disposeResources();
  }

  /**
   * Add advancedValue to .value (and invalidate .valuePercentage_cached).
   *
   * @param {number} advancedValue
   *   Add how many to .value. default is 1.
   */
  value_advance( advancedValue = 1 ) {
    this.value += advancedValue;
    this.valuePercentage_cached_invalidate();
  }

  /**
   * @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.valuePercentage_cached != undefined )
      return this.valuePercentage_cached;

    // If max is negative (i.e. not initialized), return 0 (to avoid
    // Aggregate.valuePercentage become 100 immediately).
    if ( this.max < 0 )
      return 0;

    // If max is indeed zero, return 100 (to avoid divide by zero and avoid
    // Aggregate.valuePercentage never 100).
    if ( this.max == 0 )
      return 100;

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

/**
 * Aggregate all children ( valuePercentage / maxPercentage ) and represents them as
 * percentage.
 *
 * @member {Percentage.Base[]} children
 *   An array of Percentage.Base which will be aggregated. Their parent are set
 * to this Percentage.Aggregate.
 */
class ValueMax_Percentage_Aggregate extends ValueMax_Percentage_Base {

  /**
   * Used as default ValueMax.Percentage.Aggregate provider for conforming to Recyclable interface.
   */
  static Pool = new Pool.Root( "ValueMax.Percentage.Aggregate.Pool",
    ValueMax_Percentage_Aggregate, ValueMax_Percentage_Aggregate.setAsConstructor );

  /**
   */
  constructor() {
    super();
    ValueMax_Percentage_Aggregate.setAsConstructor_self.call( this );
  }

  /** @override */
  static setAsConstructor() {
    super.setAsConstructor();
    ValueMax_Percentage_Aggregate.setAsConstructor_self.call( this );
    return this;
  }

  /** @override */
  static setAsConstructor_self() {
    this.children = Recyclable.OwnerArray.Pool.get_or_create_by();
  }

  /** @override */
  disposeResources() {
    if ( this.children ) {
      this.children.disposeResources_and_recycleToPool();
      this.children = null;
    }
    super.disposeResources();
  }

  /**
   * Append child (and invalidate .valuePercentage_cached).
   *
   * @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.
   */
  child_add( child ) {
    if ( !child )
      return null;

    this.children.push( child );
    child.parent = this;

    this.valuePercentage_cached_invalidate();

    return child;
  }

  /**
   *   - Remove all children (clear their .parent, but not dispose them).
   *   - Invalidate .valuePercentage_cached.
   *
   */
  child_detachAll() {

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

        child.parent = null;
        this.children[ i ] = null;
      }

      this.children.length = 0;
    }

    this.valuePercentage_cached_invalidate();
  }

  /**
   *   - Remove all children (and dispose them).
   *   - Invalidate .valuePercentage_cached.
   *
   */
   child_clearAll() {
    this.children.clear();
    this.valuePercentage_cached_invalidate();
  }

  /**
   * @return {number} The sum of all children's ( valuePercentage / maxPercentage ) as number between [0, 100] inclusive.
   */
  get valuePercentage() {
    if ( this.valuePercentage_cached != undefined )
      return this.valuePercentage_cached;

    let valueSum = 0, maxSum = 0;

    // (Note: Use integer array index is faster than iterator (i.e. for-of)).
    //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. (Note: maxPercentage should always be 100.)

      let partValue = child.valuePercentage;

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

      valueSum += partValue;
      maxSum += partMax;
    }

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

    this.valuePercentage_cached = ( valueSum / maxSum ) * 100;
    return this.valuePercentage_cached;
  }

}
<commit_msg>child_disposeAll<commit_after>export { ValueMax_Percentage_Base as Base };
export { ValueMax_Percentage_Concrete as Concrete };
export { ValueMax_Percentage_Aggregate as Aggregate };

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

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

  /**
   * Used as default ValueMax.Percentage.Base provider for conforming to Recyclable interface.
   */
  static Pool = new Pool.Root( "ValueMax.Percentage.Base.Pool",
    ValueMax_Percentage_Base, ValueMax_Percentage_Base.setAsConstructor );

  /**
   *
   */
  constructor() {
    super();
    ValueMax_Percentage_Base.setAsConstructor_self.call( this );
  }

  /** @override */
  static setAsConstructor() {
    super.setAsConstructor();
    ValueMax_Percentage_Base.setAsConstructor_self.call( this );
    return this;
  }

  /** @override */
  static setAsConstructor_self() {
    this.parent = null;
    this.valuePercentage_cached = undefined;
  }

  /** @override */
  disposeResources() {

    // In theory, here should remove this child object from parent (i.e. Aggregate)
    // so that the parent will not dispose (and recycle) this child object once again.
    // In fact, however, this is an expensive action (because a linear search should
    // be done. So, the better choice is to dispose the whole tree from root object
    // by caller to avoid this problem.
    //
    //if ( this.parent ) {
    //  if ( this.parent instanceof ValueMax_Percentage_Aggregate ) {
    //    // Do nothing currently.
    //  }
    //}

    this.valuePercentage_cached = undefined;
    this.parent = undefined;
    super.disposeResources();
  }

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

  /**
   * Invalidate .valuePercentage_cached (i.e. let it become undefined). This method
   * will invalidate parent's .valuePercentage_cached, too.
   */
  valuePercentage_cached_invalidate() {
    this.valuePercentage_cached = undefined;
    if ( this.parent )
      this.parent.valuePercentage_cached_invalidate();
  }

  /**
   * 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;
  }
}

/**
 * Collect value and max and represents them as percentage.
 *
 * The Concrete.maxPercentage always returns 100. The Concrete.valuePercentage returns
 * number between [ 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 ValueMax_Percentage_Concrete extends ValueMax_Percentage_Base {

  /**
   * Used as default ValueMax.Percentage.Concrete provider for conforming to Recyclable interface.
   */
  static Pool = new Pool.Root( "ValueMax.Percentage.Concrete.Pool",
    ValueMax_Percentage_Concrete, ValueMax_Percentage_Concrete.setAsConstructor );

  /**
   * @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 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();
    ValueMax_Percentage_Concrete.setAsConstructor_self.call( this, max );
  }

  /** @override */
  static setAsConstructor( max = -1 ) {
    super.setAsConstructor();
    ValueMax_Percentage_Concrete.setAsConstructor_self.call( this, max );
    return this;
  }

  /** @override */
  static setAsConstructor_self( max = -1 ) {
    this.value = 0;
    this.max = max; // Negative indicates not initialized.
  }

  /** @override */
  disposeResources() {
    this.max = undefined;
    this.value = undefined;
    super.disposeResources();
  }

  /**
   * Add advancedValue to .value (and invalidate .valuePercentage_cached).
   *
   * @param {number} advancedValue
   *   Add how many to .value. default is 1.
   */
  value_advance( advancedValue = 1 ) {
    this.value += advancedValue;
    this.valuePercentage_cached_invalidate();
  }

  /**
   * @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.valuePercentage_cached != undefined )
      return this.valuePercentage_cached;

    // If max is negative (i.e. not initialized), return 0 (to avoid
    // Aggregate.valuePercentage become 100 immediately).
    if ( this.max < 0 )
      return 0;

    // If max is indeed zero, return 100 (to avoid divide by zero and avoid
    // Aggregate.valuePercentage never 100).
    if ( this.max == 0 )
      return 100;

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

/**
 * Aggregate all children ( valuePercentage / maxPercentage ) and represents them as
 * percentage.
 *
 * @member {Percentage.Base[]} children
 *   An array of Percentage.Base which will be aggregated. Their parent are set
 * to this Percentage.Aggregate.
 */
class ValueMax_Percentage_Aggregate extends ValueMax_Percentage_Base {

  /**
   * Used as default ValueMax.Percentage.Aggregate provider for conforming to Recyclable interface.
   */
  static Pool = new Pool.Root( "ValueMax.Percentage.Aggregate.Pool",
    ValueMax_Percentage_Aggregate, ValueMax_Percentage_Aggregate.setAsConstructor );

  /**
   */
  constructor() {
    super();
    ValueMax_Percentage_Aggregate.setAsConstructor_self.call( this );
  }

  /** @override */
  static setAsConstructor() {
    super.setAsConstructor();
    ValueMax_Percentage_Aggregate.setAsConstructor_self.call( this );
    return this;
  }

  /** @override */
  static setAsConstructor_self() {
    this.children = Recyclable.OwnerArray.Pool.get_or_create_by();
  }

  /** @override */
  disposeResources() {
    if ( this.children ) {
      this.children.disposeResources_and_recycleToPool();
      this.children = null;
    }
    super.disposeResources();
  }

  /**
   * Append child (and invalidate .valuePercentage_cached).
   *
   * @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.
   */
  child_add( child ) {
    if ( !child )
      return null;

    this.children.push( child );
    child.parent = this;

    this.valuePercentage_cached_invalidate();

    return child;
  }

  /**
   *   - Remove all children (clear their .parent, but not dispose them).
   *   - Invalidate .valuePercentage_cached.
   *
   */
  child_detachAll() {

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

        child.parent = null;
        this.children[ i ] = null;
      }

      this.children.length = 0;
    }

    this.valuePercentage_cached_invalidate();
  }

  /**
   *   - Remove all children (and dispose them).
   *   - Invalidate .valuePercentage_cached.
   *
   */
   child_disposeAll() {
    this.children.clear();
    this.valuePercentage_cached_invalidate();
  }

  /**
   * @return {number} The sum of all children's ( valuePercentage / maxPercentage ) as number between [0, 100] inclusive.
   */
  get valuePercentage() {
    if ( this.valuePercentage_cached != undefined )
      return this.valuePercentage_cached;

    let valueSum = 0, maxSum = 0;

    // (Note: Use integer array index is faster than iterator (i.e. for-of)).
    //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. (Note: maxPercentage should always be 100.)

      let partValue = child.valuePercentage;

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

      valueSum += partValue;
      maxSum += partMax;
    }

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

    this.valuePercentage_cached = ( valueSum / maxSum ) * 100;
    return this.valuePercentage_cached;
  }

}
<|endoftext|>


<commit_before>//Import dependencies
var fs = require('fs');
var path = require('path');

//Line break
var endl = '\n';

//Tabular character
var tab = ' ';

//Icons folder
var icons_folder = './svg/';

//Read the svg icons folder
fs.readdir(icons_folder, function(error, files)
{
  //Check the error
  if(error){ throw error; }

  //Initialize the unicode counter
  var icon_unicode = 57344;

  //Initialize the writable stream
  var writable = fs.createWriteStream('./icons.json', { encoding: 'utf8' });

  //Writable finish event
  writable.on('finish', function()
  {
    //Display done
    console.log('File created!');
  });

  //Initialize the output file
  writable.write('[' + endl);

  //For each file in the list
  files.forEach(function(file)
  {
    //Check the icon extension
    if(path.extname(file) !== '.svg'){ return; }

    //Check for first item
    if(counter > 0)
    {
      //Add the comma and a new line break
      writable.write(',' + endl);
    }

    //Get the icon id
    var icon_id = path.basename(file, '.svg');

    //Get the icon path
    var icon_path = path.join(icons_folder, file);

    //Write the file information
    writable.write(tab + '{ "id": "' + icon_id + '", "unicode": ' + icon_unicode + ', "path": "' + icon_path + '" }');

    //Increment the counter
    icon_unicode = icon_unicode + 1;
  });

  //End the writable file
  writable.end(endl + ']' + endl);
});<commit_msg>build-unicode.js: fixed bug<commit_after>//Import dependencies
var fs = require('fs');
var path = require('path');

//Line break
var endl = '\n';

//Tabular character
var tab = ' ';

//Icons folder
var icons_folder = './svg/';

//Unicode start
var unicode_start = 57344;

//Read the svg icons folder
fs.readdir(icons_folder, function(error, files)
{
  //Check the error
  if(error){ throw error; }

  //Initialize the unicode counter
  var icon_unicode = unicode_start;

  //Initialize the writable stream
  var writable = fs.createWriteStream('./icons.json', { encoding: 'utf8' });

  //Writable finish event
  writable.on('finish', function()
  {
    //Display done
    console.log('File created!');
  });

  //Initialize the output file
  writable.write('[' + endl);

  //For each file in the list
  files.forEach(function(file)
  {
    //Check the icon extension
    if(path.extname(file) !== '.svg'){ return; }

    //Check for first item
    if(icon_unicode > unicode_start)
    {
      //Add the comma and a new line break
      writable.write(',' + endl);
    }

    //Get the icon id
    var icon_id = path.basename(file, '.svg');

    //Get the icon path
    var icon_path = path.join(icons_folder, file);

    //Write the file information
    writable.write(tab + '{ "id": "' + icon_id + '", "unicode": ' + icon_unicode + ', "path": "' + icon_path + '" }');

    //Increment the counter
    icon_unicode = icon_unicode + 1;
  });

  //End the writable file
  writable.end(endl + ']' + endl);
});<|endoftext|>


<commit_before>/*global require, module */

const config = require('/etc/xray/config.json');
const pg = require('pg');
const logger = require('./logger.js');

const db_cfg = config.scraper.db;
db_cfg.max = 10;
db_cfg.idleTimeoutMillis = 30000;

//this initializes a connection pool
//it will keep idle connections open for 30 seconds
//and set a limit of maximum 10 idle clients
const pool = new pg.Pool(db_cfg);

pool.on('error', function(err) {
    logger.err('idle client error '+ err.message + err.stack);
});

//export the query method for passing queries to the pool
function query(text, values) {
    logger.debug('query:'+ text+ values);
    return pool.query(text, values);
}

// the pool also supports checking out a client for
// multiple operations, such as a transaction
function connect() {
    return pool.connect();
}

async function insertDev(dev) {
    var res = await query('SELECT id FROM developers WHERE $1 = ANY(email)', [dev.email]);
    if (res.rowCount > 0) {
        return res.rows[0].id;
    }

    // maybe dev id needs to be URL encoded?
    let store_site = 'https://play.google.com/store/apps/developer?id=' + dev.id;
    res = await query('INSERT INTO developers(email,name,store_site,site) VALUES ($1, $2, $3, $4) RETURNING id', [
        [dev.email], dev.name, store_site, dev.site
    ]);
    return res.rows[0].id;
}

module.exports = {
    insertPlayApp: async(app, region) => {

        var devId = await insertDev({
            name: app.developer,
            id: app.developerId,
            email: app.developerEmail,
            site: app.developerWebsite
        });

        var appExists = false,
            verExists = false;
        var verId;
        var res = await query('SELECT * FROM apps WHERE id = $1', [app.appId]);

        if (res.rowCount > 0) {
            appExists = true;
            // app exists in database, check if version does as well
            var res1 = await query(
                'SELECT id FROM app_versions WHERE app = $1 AND store = $2 AND region = $3 AND version = $4', [app.appId, 'play', region, app.version]);

            if (res1.rowCount > 0) {
                // app version is also in database
                verExists = true;
                verId = res1.rows[0].id;
            }
        }

        var client = await connect();
        logger.debug('Connected');

        await client.query('BEGIN'); // maybe this should be inside the try?
        try {
            if (!verExists) {
                if (!appExists) {
                    await client.query('INSERT INTO apps VALUES ($1, $2)', [app.appId, []]);
                }

                let res = await client.query(
                    'INSERT INTO app_versions(app, store, region, version, downloaded) VALUES ($1, $2, $3, $4, $5) RETURNING id',
                    [app.appId, 'play', region, app.version, app.isDownloaded]
                );
                verId = res.rows[0].id;

                await client.query('UPDATE apps SET versions=versions || $1 WHERE id = $2', [
                    [verId], app.appId
                ]);

            }

            await client.query(
                'INSERT INTO playstore_apps VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)', [
                    verId,
                    app.title,
                    app.summary,
                    app.description,
                    app.url,
                    app.price,
                    app.free,
                    app.score,
                    app.reviews,
                    app.genreId,
                    app.familyGenreId,
                    app.minInstalls,
                    app.maxInstalls,
                    devId,
                    app.updated,
                    app.androidVersion,
                    app.contentRating,
                    app.screenshots,
                    app.video,
                    app.recentChanges,
                    new Date().toDateString()
                ]);
            await client.query('COMMIT');
        } catch (e) {
            await client.query('ROLLBACK');
            throw e;
        } finally {
            client.release();
        }
        return verId;
    }
};

if (!module.parent)  {
    query('SELECT * FROM developers');
}
<commit_msg>update db.js to use old logging format<commit_after>'use strict';
/*global require, module */

const config = require('/etc/xray/config.json');
const pg = require('pg');
const logger = require('./logger.js');

const db_cfg = config.scraper.db;
db_cfg.max = 10;
db_cfg.idleTimeoutMillis = 30000;

//this initializes a connection pool
//it will keep idle connections open for 30 seconds
//and set a limit of maximum 10 idle clients
const pool = new pg.Pool(db_cfg);

pool.on('error', function(err) {
    logger.err('idle client error', err.message, err.stack);
});

//export the query method for passing queries to the pool
function query(text, values) {
    logger.debug('query:', text, values);
    return pool.query(text, values);
}

// the pool also supports checking out a client for
// multiple operations, such as a transaction
function connect() {
    return pool.connect();
}

async function insertDev(dev) {
    var res = await query('SELECT id FROM developers WHERE $1 = ANY(email)', [dev.email]);
    if (res.rowCount > 0) {
        return res.rows[0].id;
    }

    // maybe dev id needs to be URL encoded?
    let store_site = 'https://play.google.com/store/apps/developer?id=' + dev.id;
    res = await query('INSERT INTO developers(email,name,store_site,site) VALUES ($1, $2, $3, $4) RETURNING id', [
        [dev.email], dev.name, store_site, dev.site
    ]);
    return res.rows[0].id;
}

module.exports = {
    insertPlayApp: async(app, region) => {

        var devId = await insertDev({
            name: app.developer,
            id: app.developerId,
            email: app.developerEmail,
            site: app.developerWebsite
        });

        var appExists = false,
            verExists = false;
        var verId;
        var res = await query('SELECT * FROM apps WHERE id = $1', [app.appId]);

        if (res.rowCount > 0) {
            appExists = true;
            // app exists in database, check if version does as well
            var res1 = await query(
                'SELECT id FROM app_versions WHERE app = $1 AND store = $2 AND region = $3 AND version = $4', [app.appId, 'play', region, app.version]);

            if (res1.rowCount > 0) {
                // app version is also in database
                verExists = true;
                verId = res1.rows[0].id;
            }
        }

        var client = await connect();
        logger.debug('Connected');

        await client.query('BEGIN'); // maybe this should be inside the try?
        try {
            if (!verExists) {
                if (!appExists) {
                    await client.query('INSERT INTO apps VALUES ($1, $2)', [app.appId, []]);
                }

                let res = await client.query(
                    'INSERT INTO app_versions(app, store, region, version, downloaded) VALUES ($1, $2, $3, $4, $5) RETURNING id',
                    [app.appId, 'play', region, app.version, app.isDownloaded]
                );
                verId = res.rows[0].id;

                await client.query('UPDATE apps SET versions=versions || $1 WHERE id = $2', [
                    [verId], app.appId
                ]);

            }

            await client.query(
                'INSERT INTO playstore_apps VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21)', [
                    verId,
                    app.title,
                    app.summary,
                    app.description,
                    app.url,
                    app.price,
                    app.free,
                    app.score,
                    app.reviews,
                    app.genreId,
                    app.familyGenreId,
                    app.minInstalls,
                    app.maxInstalls,
                    devId,
                    app.updated,
                    app.androidVersion,
                    app.contentRating,
                    app.screenshots,
                    app.video,
                    app.recentChanges,
                    new Date().toDateString()
                ]);
            await client.query('COMMIT');
        } catch (e) {
            await client.query('ROLLBACK');
            throw e;
        } finally {
            client.release();
        }
        return verId;
    }
};

if (!module.parent)  {
    query('SELECT * FROM developers');
}
<|endoftext|>


<commit_before>function add_datepickers(that) {
  $('input#id_start_date', that).datepicker({dateFormat: 'd/m/yy', showOn: 'button' });
  $('input#id_request_date', that).datepicker({dateFormat: 'd/m/yy', showOn: 'button' });
  $('input#id_agree_date', that).datepicker({dateFormat: 'd/m/yy', showOn: 'button' });
  $('input#id_insist_date', that).datepicker({dateFormat: 'd/m/yy', showOn: 'button' });
  $('input#id_end_date', that).datepicker({dateFormat: 'd/m/yy', showOn: 'button' });
  $('input#id_date_selected', that).datepicker({dateFormat: 'd/m/yy', showOn: 'button' });
}

function state_dialog(url, state_title, state_type) {
    $('#change-record').load(url,
            function () {
                var patient_id = $(this).data('id');
                var service_id = $(this).data('service-id');
                function onsuccess(response, status, xhr, form) {
                    var parse = $(response);
                    if ($('.errorlist', parse).length != 0) {
                        $('#change-record').html(response);
                        $('#change-record form').ajaxForm({
                            success: onsuccess,
                            data: { patient_id: patient_id,  state_type: state_type, service_id: service_id }
                        });
                        console.log('error');
                    } else {
                        console.log('success');
                        window.location.reload(true);
                    }
                }
                $('input.date', this).datepicker({dateFormat: 'd/m/yy', showOn: 'button' });
                $('form', this).ajaxForm({
                    success: onsuccess,
                    data: { patient_id: patient_id,  state_type: state_type, service_id: service_id }
                });
                $(this).dialog({title: "Changement - " + state_title,
                    width: '500px',
                    buttons: [ { text: "Annuler",
                        click: function() { $(this).dialog("close"); } },
                    { text: "Valider",
                        click: function() { $("#change-record form").submit(); } }]});
            });
}

(function($) {
  $(function() {
    var $tabs = $('#tabs').tabs();

    $('.atabs').click(function() {
        location.hash = 'tab=' + $(this).data('id');
    });

    $('#btn_all_state').click(function() {
      $('.checkbox_state').attr('checked', true);
    });
    $('#btn_none_state').click(function() {
      $('.checkbox_state').attr('checked', false);
    });
    $('.checkbox_state').click(function() {
        $("#search").click();
    });
    $('#id_general-pause').click(function() {
        $('#general-form').submit();
    });
    $('#id_general-confidential').click(function() {
        $('#general-form').submit();
    });
    $('.policyholder-radio').click(function() {
        $("#policyholder-form").submit();
    });
    $('.pr-line').click(function() {
        window.location.href = $(this).data('link');
    });
    $('button#reset').click(function() {
        window.location.href = window.location.pathname;
        return false;
    });
    $('#print-button').click(function() { window.print(); });

    $('#close-patientrecord').click(function() {
        state_dialog('update-state', 'Clore', 'CLOS');
    });
    $('#reopen-patientrecord').click(function() {
        state_dialog('update-state', 'Réaccueil', 'ACCUEIL');
    });
    $('#diagnostic-patientrecord').click(function() {
        state_dialog('update-state', 'Diagnostic', 'DIAGNOSTIC');
    });
    $('#traitement-patientrecord').click(function() {
        state_dialog('update-state', 'Traitement', 'TRAITEMENT');
    });
    $('#finaccueil-patientrecord').click(function() {
        state_dialog('update-state', "Fin d'accueil", 'FIN_ACCUEIL');
    });
    $('#bilan-patientrecord').click(function() {
        state_dialog('update-state', 'Bilan', 'BILAN');
    });
    $('#surveillance-patientrecord').click(function() {
        state_dialog('update-state', 'Surveillance', 'SURVEILLANCE');
    });
    $('#suivi-patientrecord').click(function() {
        state_dialog('update-state', 'Suivi', 'SUIVI');
    });
    $('#patientrecord-history').click(function() {
      $('#dossier-histo-dlg').dialog({title: 'Historique dossier',
        width: '500px',
        buttons: [ { text: "Fermer",
          click: function() { $(this).dialog("close"); } }]}
        );
    });
    if (location.hash.indexOf('histo') != -1) {
      $('#patientrecord-history').click();
      location.hash = '';
    }

    $('#new-patientrecord').click(function() {
        generic_ajaxform_dialog('new', 'Nouveau dossier',
            '#dossier-dlg', '700px', 'Ajouter', false, function(that) {
                    $('input#id_date_selected', that).datepicker({dateFormat: 'd/m/yy', showOn: 'button' });
                    $(that).find('#id_last_name').keyup(function() {
                            var val = $(this).val();
                            if (val.length < 3) {
                               $(that).find('#last_name_matches').empty();
                               return;
                            }
                            $.ajax({
                               url: "/lookups/ajax_lookup/patientrecord?term=" + val,
                               success: function(json) {
                                  var list = $(that).find('#last_name_matches');
                                  list.empty();
                                  $(eval(json)).each(function(a, b) {
                                          list.append($('
' + b.value + '
'));
                                  });
                               }
                            });
                    });
            });
    });
    $('#patientrecord-delete').click(function() {
        generic_ajaxform_dialog('delete', 'Supprimer le dossier',
            '#ajax-dlg', '500px', 'Oui', '..');
    });

    $('#new-address-btn').click(function() {
        generic_ajaxform_dialog('address/new', 'Ajouter une adresse',
            '#ajax-dlg', '600px', 'Ajouter');
    });
    $('.update-address-btn').click(function() {
        generic_ajaxform_dialog('address/' + $(this).data('id') + '/update', 'Modifier une adresse',
            '#ajax-dlg', '600px', 'Modifier');
    });
    $('.del-address').click(function() {
        generic_ajaxform_dialog('address/' + $(this).data('id') + '/del', 'Supprimer une addresse',
            '#ajax-dlg', '500px', 'Supprimer');
    });

    $('#update-paper-id-btn').click(function() {
        generic_ajaxform_dialog('update/paper_id', 'Modifier le numéro du dossier papier',
            '#ajax-dlg', '500px', 'Modifier');
    });

    function nir_check(that) {
      $(that).find('#social-security-id input').keyup(function() {
        if ($(this).val().length < 13) {
             $('p#nir-key span').removeAttr('id')
             $('p#nir-key span').text('-');
         } else {
             $('p#nir-key span').attr('id', 'highlight')
             var nir = $(this).val();
             var minus = 0;
             if (nir.charAt(6) == 'A'){
               nir = nir.replace('A', '0');
               minus = 1000000;
             }
             if (nir.charAt(6) == 'B'){
               nir = nir.replace('B', '0');
               minus = 2000000;
             }
             nir = parseInt(nir, 10);
             nir = nir - minus;
             var key = 97 - (nir % 97);
             if (isNaN(key)) {
                 $('p#nir-key span').text('NIR invalide');
             } else {
                 $('p#nir-key span').text(key);
             }
         }
      });
    }

    $('#new-contact-btn').click(function() {
        generic_ajaxform_dialog('contact/new', 'Ajouter un contact',
            '#ajax-dlg', '800px', 'Ajouter', null, nir_check);
    });
    $('.update-contact-btn').click(function() {
        generic_ajaxform_dialog('contact/' + $(this).data('id') + '/update', 'Modifier un contact',
            '#ajax-dlg', '800px', 'Modifier', null, nir_check);
    });
    $('.del-contact').click(function() {
        generic_ajaxform_dialog('contact/' + $(this).data('id') + '/del?address=' + $(this).data('address-id'),
                'Supprimer un contact', '#ajax-dlg', '500px', 'Supprimer');
    });
    $('#new-socialisation-duration-btn').click(function() {
        generic_ajaxform_dialog('socialisation/new', 'Ajouter une période de socialisation',
            '#ajax-dlg', '800px', 'Ajouter', null, add_datepickers);
    });
    $('.update-duration-btn').click(function() {
        generic_ajaxform_dialog('socialisation/' + $(this).data('id') + '/update', 'Modifier une période de socialisation',
            '#ajax-dlg', '800px', 'Modifier', null, add_datepickers);
    });
    $('.del-duration').click(function() {
        generic_ajaxform_dialog('socialisation/' + $(this).data('id') + '/del', 'Supprimer une période de socialisation',
            '#ajax-dlg', '500px', 'Supprimer');
    });
    $('#new-mdph-request-btn').click(function() {
        generic_ajaxform_dialog('mdph_request/new', 'Ajouter une demande MDPH',
            '#ajax-dlg', '800px', 'Ajouter', null, add_datepickers);
    });
    $('.update-mdph-request-btn').click(function() {
        generic_ajaxform_dialog('mdph_request/' + $(this).data('id') + '/update', 'Modifier une demande MDPH',
            '#ajax-dlg', '800px', 'Modifier', null, add_datepickers);
    });
    $('.del-mdph-request').click(function() {
        generic_ajaxform_dialog('mdph_request/' + $(this).data('id') + '/del', 'Supprimer une demande MDPH',
            '#ajax-dlg', '500px', 'Supprimer');
    });
    $('#new-mdph-response-btn').click(function() {
        generic_ajaxform_dialog('mdph_response/new', 'Ajouter une réponse MDPH',
            '#ajax-dlg', '800px', 'Ajouter', null, add_datepickers);
    });
    $('.update-mdph-response-btn').click(function() {
        generic_ajaxform_dialog('mdph_response/' + $(this).data('id') + '/update', 'Modifier une réponse MDPH',
            '#ajax-dlg', '800px', 'Modifier', null, add_datepickers);
    });
    $('.del-mdph-response').click(function() {
        generic_ajaxform_dialog('mdph_response/' + $(this).data('id') + '/del', 'Supprimer une réponse MDPH',
            '#ajax-dlg', '500px', 'Supprimer');
    });
    $('#new-hctrait-btn').click(function() {
        generic_ajaxform_dialog('healthcare_treatment/new', 'Ajouter une prise en charge de traitement',
            '#ajax-dlg', '600px', 'Ajouter', null, add_datepickers);
    });
    $('#new-hcdiag-btn').click(function() {
        generic_ajaxform_dialog('healthcare_diagnostic/new', 'Ajouter une prise en charge de diagnostic',
            '#ajax-dlg', '600px', 'Ajouter', null, add_datepickers);
    });
    $('#new-notification-btn').click(function() {
        generic_ajaxform_dialog('healthcare_notification/new', 'Ajouter une notification',
            '#ajax-dlg', '600px', 'Ajouter', null, add_datepickers);
    });
    $('.update-hctrait-btn').click(function() {
        generic_ajaxform_dialog('healthcare_treatment/' + $(this).data('id') + '/update', 'Modifier une prise en charge de traitement',
            '#ajax-dlg', '800px', 'Modifier', null, add_datepickers);
    });
    $('.update-hcdiag-btn').click(function() {
        generic_ajaxform_dialog('healthcare_diagnostic/' + $(this).data('id') + '/update', 'Modifier une prise en charge de diagnostic',
            '#ajax-dlg', '800px', 'Modifier', null, add_datepickers);
    });
    $('.update-notification-btn').click(function() {
        generic_ajaxform_dialog('healthcare_notification/' + $(this).data('id') + '/update', 'Modifier une notification',
            '#ajax-dlg', '800px', 'Modifier', null, add_datepickers);
    });
    $('.del-hctrait').click(function() {
        generic_ajaxform_dialog('healthcare_treatment/' + $(this).data('id') + '/del', 'Supprimer une prise en charge de traitement',
            '#ajax-dlg', '500px', 'Supprimer');
    });
    $('.del-hcdiag').click(function() {
        generic_ajaxform_dialog('healthcare_diagnostic/' + $(this).data('id') + '/del', 'Supprimer une prise en charge de diagnostic',
            '#ajax-dlg', '500px', 'Supprimer');
    });
    $('.del-notification').click(function() {
        generic_ajaxform_dialog('healthcare_notification/' + $(this).data('id') + '/del', 'Supprimer une notification',
            '#ajax-dlg', '500px', 'Supprimer');
    });

    $('.update-patient-state-btn').click(function() {
        generic_ajaxform_dialog('state/' + $(this).data('id') + '/update', 'Modifier un état',
            '#ajax-dlg', '500px', 'Modifier', '#histo', add_datepickers);
    });
    $('.del-patient-state-btn').click(function() {
        generic_ajaxform_dialog('state/' + $(this).data('id') + '/del', 'Supprimer un état',
            '#ajax-dlg', '500px', 'Supprimer', '#histo');
    });

      $('.place_of_life').click(function() {
          if ((this.checked) == true) {
              var value = "true";
          } else {
              var value = "false";
          }
          $.ajax({
              url: '/api/v1/patientaddress/' + $(this).data("id") + '/?format=json',
              type: 'PATCH',
              contentType: 'application/json',
              data: '{"place_of_life": ' + value + '}',
              success: function(data) {
                  console.log('success');
              }
          });
      });

    $('input#id_id-birthdate', this).datepicker({dateFormat: 'd/m/yy', showOn: 'button' });

    $('button.blind').next().hide();
    $('button.blind').click(function() {
      $(this).next().toggle('blind');
    });
    var tabid = $.url($(location).attr('href')).fparam('tab');
    if (tabid) {
        $tabs.tabs('select',  parseInt(tabid));
    }
  });
})(window.jQuery)
<commit_msg>dossiers: extend new contact dialog to 900px<commit_after>function add_datepickers(that) {
  $('input#id_start_date', that).datepicker({dateFormat: 'd/m/yy', showOn: 'button' });
  $('input#id_request_date', that).datepicker({dateFormat: 'd/m/yy', showOn: 'button' });
  $('input#id_agree_date', that).datepicker({dateFormat: 'd/m/yy', showOn: 'button' });
  $('input#id_insist_date', that).datepicker({dateFormat: 'd/m/yy', showOn: 'button' });
  $('input#id_end_date', that).datepicker({dateFormat: 'd/m/yy', showOn: 'button' });
  $('input#id_date_selected', that).datepicker({dateFormat: 'd/m/yy', showOn: 'button' });
}

function state_dialog(url, state_title, state_type) {
    $('#change-record').load(url,
            function () {
                var patient_id = $(this).data('id');
                var service_id = $(this).data('service-id');
                function onsuccess(response, status, xhr, form) {
                    var parse = $(response);
                    if ($('.errorlist', parse).length != 0) {
                        $('#change-record').html(response);
                        $('#change-record form').ajaxForm({
                            success: onsuccess,
                            data: { patient_id: patient_id,  state_type: state_type, service_id: service_id }
                        });
                        console.log('error');
                    } else {
                        console.log('success');
                        window.location.reload(true);
                    }
                }
                $('input.date', this).datepicker({dateFormat: 'd/m/yy', showOn: 'button' });
                $('form', this).ajaxForm({
                    success: onsuccess,
                    data: { patient_id: patient_id,  state_type: state_type, service_id: service_id }
                });
                $(this).dialog({title: "Changement - " + state_title,
                    width: '500px',
                    buttons: [ { text: "Annuler",
                        click: function() { $(this).dialog("close"); } },
                    { text: "Valider",
                        click: function() { $("#change-record form").submit(); } }]});
            });
}

(function($) {
  $(function() {
    var $tabs = $('#tabs').tabs();

    $('.atabs').click(function() {
        location.hash = 'tab=' + $(this).data('id');
    });

    $('#btn_all_state').click(function() {
      $('.checkbox_state').attr('checked', true);
    });
    $('#btn_none_state').click(function() {
      $('.checkbox_state').attr('checked', false);
    });
    $('.checkbox_state').click(function() {
        $("#search").click();
    });
    $('#id_general-pause').click(function() {
        $('#general-form').submit();
    });
    $('#id_general-confidential').click(function() {
        $('#general-form').submit();
    });
    $('.policyholder-radio').click(function() {
        $("#policyholder-form").submit();
    });
    $('.pr-line').click(function() {
        window.location.href = $(this).data('link');
    });
    $('button#reset').click(function() {
        window.location.href = window.location.pathname;
        return false;
    });
    $('#print-button').click(function() { window.print(); });

    $('#close-patientrecord').click(function() {
        state_dialog('update-state', 'Clore', 'CLOS');
    });
    $('#reopen-patientrecord').click(function() {
        state_dialog('update-state', 'Réaccueil', 'ACCUEIL');
    });
    $('#diagnostic-patientrecord').click(function() {
        state_dialog('update-state', 'Diagnostic', 'DIAGNOSTIC');
    });
    $('#traitement-patientrecord').click(function() {
        state_dialog('update-state', 'Traitement', 'TRAITEMENT');
    });
    $('#finaccueil-patientrecord').click(function() {
        state_dialog('update-state', "Fin d'accueil", 'FIN_ACCUEIL');
    });
    $('#bilan-patientrecord').click(function() {
        state_dialog('update-state', 'Bilan', 'BILAN');
    });
    $('#surveillance-patientrecord').click(function() {
        state_dialog('update-state', 'Surveillance', 'SURVEILLANCE');
    });
    $('#suivi-patientrecord').click(function() {
        state_dialog('update-state', 'Suivi', 'SUIVI');
    });
    $('#patientrecord-history').click(function() {
      $('#dossier-histo-dlg').dialog({title: 'Historique dossier',
        width: '500px',
        buttons: [ { text: "Fermer",
          click: function() { $(this).dialog("close"); } }]}
        );
    });
    if (location.hash.indexOf('histo') != -1) {
      $('#patientrecord-history').click();
      location.hash = '';
    }

    $('#new-patientrecord').click(function() {
        generic_ajaxform_dialog('new', 'Nouveau dossier',
            '#dossier-dlg', '700px', 'Ajouter', false, function(that) {
                    $('input#id_date_selected', that).datepicker({dateFormat: 'd/m/yy', showOn: 'button' });
                    $(that).find('#id_last_name').keyup(function() {
                            var val = $(this).val();
                            if (val.length < 3) {
                               $(that).find('#last_name_matches').empty();
                               return;
                            }
                            $.ajax({
                               url: "/lookups/ajax_lookup/patientrecord?term=" + val,
                               success: function(json) {
                                  var list = $(that).find('#last_name_matches');
                                  list.empty();
                                  $(eval(json)).each(function(a, b) {
                                          list.append($('
' + b.value + '
'));
                                  });
                               }
                            });
                    });
            });
    });
    $('#patientrecord-delete').click(function() {
        generic_ajaxform_dialog('delete', 'Supprimer le dossier',
            '#ajax-dlg', '500px', 'Oui', '..');
    });

    $('#new-address-btn').click(function() {
        generic_ajaxform_dialog('address/new', 'Ajouter une adresse',
            '#ajax-dlg', '600px', 'Ajouter');
    });
    $('.update-address-btn').click(function() {
        generic_ajaxform_dialog('address/' + $(this).data('id') + '/update', 'Modifier une adresse',
            '#ajax-dlg', '600px', 'Modifier');
    });
    $('.del-address').click(function() {
        generic_ajaxform_dialog('address/' + $(this).data('id') + '/del', 'Supprimer une addresse',
            '#ajax-dlg', '500px', 'Supprimer');
    });

    $('#update-paper-id-btn').click(function() {
        generic_ajaxform_dialog('update/paper_id', 'Modifier le numéro du dossier papier',
            '#ajax-dlg', '500px', 'Modifier');
    });

    function nir_check(that) {
      $(that).find('#social-security-id input').keyup(function() {
        if ($(this).val().length < 13) {
             $('p#nir-key span').removeAttr('id')
             $('p#nir-key span').text('-');
         } else {
             $('p#nir-key span').attr('id', 'highlight')
             var nir = $(this).val();
             var minus = 0;
             if (nir.charAt(6) == 'A'){
               nir = nir.replace('A', '0');
               minus = 1000000;
             }
             if (nir.charAt(6) == 'B'){
               nir = nir.replace('B', '0');
               minus = 2000000;
             }
             nir = parseInt(nir, 10);
             nir = nir - minus;
             var key = 97 - (nir % 97);
             if (isNaN(key)) {
                 $('p#nir-key span').text('NIR invalide');
             } else {
                 $('p#nir-key span').text(key);
             }
         }
      });
    }

    $('#new-contact-btn').click(function() {
        generic_ajaxform_dialog('contact/new', 'Ajouter un contact',
            '#ajax-dlg', '900px', 'Ajouter', null, nir_check);
    });
    $('.update-contact-btn').click(function() {
        generic_ajaxform_dialog('contact/' + $(this).data('id') + '/update', 'Modifier un contact',
            '#ajax-dlg', '800px', 'Modifier', null, nir_check);
    });
    $('.del-contact').click(function() {
        generic_ajaxform_dialog('contact/' + $(this).data('id') + '/del?address=' + $(this).data('address-id'),
                'Supprimer un contact', '#ajax-dlg', '500px', 'Supprimer');
    });
    $('#new-socialisation-duration-btn').click(function() {
        generic_ajaxform_dialog('socialisation/new', 'Ajouter une période de socialisation',
            '#ajax-dlg', '800px', 'Ajouter', null, add_datepickers);
    });
    $('.update-duration-btn').click(function() {
        generic_ajaxform_dialog('socialisation/' + $(this).data('id') + '/update', 'Modifier une période de socialisation',
            '#ajax-dlg', '800px', 'Modifier', null, add_datepickers);
    });
    $('.del-duration').click(function() {
        generic_ajaxform_dialog('socialisation/' + $(this).data('id') + '/del', 'Supprimer une période de socialisation',
            '#ajax-dlg', '500px', 'Supprimer');
    });
    $('#new-mdph-request-btn').click(function() {
        generic_ajaxform_dialog('mdph_request/new', 'Ajouter une demande MDPH',
            '#ajax-dlg', '800px', 'Ajouter', null, add_datepickers);
    });
    $('.update-mdph-request-btn').click(function() {
        generic_ajaxform_dialog('mdph_request/' + $(this).data('id') + '/update', 'Modifier une demande MDPH',
            '#ajax-dlg', '800px', 'Modifier', null, add_datepickers);
    });
    $('.del-mdph-request').click(function() {
        generic_ajaxform_dialog('mdph_request/' + $(this).data('id') + '/del', 'Supprimer une demande MDPH',
            '#ajax-dlg', '500px', 'Supprimer');
    });
    $('#new-mdph-response-btn').click(function() {
        generic_ajaxform_dialog('mdph_response/new', 'Ajouter une réponse MDPH',
            '#ajax-dlg', '800px', 'Ajouter', null, add_datepickers);
    });
    $('.update-mdph-response-btn').click(function() {
        generic_ajaxform_dialog('mdph_response/' + $(this).data('id') + '/update', 'Modifier une réponse MDPH',
            '#ajax-dlg', '800px', 'Modifier', null, add_datepickers);
    });
    $('.del-mdph-response').click(function() {
        generic_ajaxform_dialog('mdph_response/' + $(this).data('id') + '/del', 'Supprimer une réponse MDPH',
            '#ajax-dlg', '500px', 'Supprimer');
    });
    $('#new-hctrait-btn').click(function() {
        generic_ajaxform_dialog('healthcare_treatment/new', 'Ajouter une prise en charge de traitement',
            '#ajax-dlg', '600px', 'Ajouter', null, add_datepickers);
    });
    $('#new-hcdiag-btn').click(function() {
        generic_ajaxform_dialog('healthcare_diagnostic/new', 'Ajouter une prise en charge de diagnostic',
            '#ajax-dlg', '600px', 'Ajouter', null, add_datepickers);
    });
    $('#new-notification-btn').click(function() {
        generic_ajaxform_dialog('healthcare_notification/new', 'Ajouter une notification',
            '#ajax-dlg', '600px', 'Ajouter', null, add_datepickers);
    });
    $('.update-hctrait-btn').click(function() {
        generic_ajaxform_dialog('healthcare_treatment/' + $(this).data('id') + '/update', 'Modifier une prise en charge de traitement',
            '#ajax-dlg', '800px', 'Modifier', null, add_datepickers);
    });
    $('.update-hcdiag-btn').click(function() {
        generic_ajaxform_dialog('healthcare_diagnostic/' + $(this).data('id') + '/update', 'Modifier une prise en charge de diagnostic',
            '#ajax-dlg', '800px', 'Modifier', null, add_datepickers);
    });
    $('.update-notification-btn').click(function() {
        generic_ajaxform_dialog('healthcare_notification/' + $(this).data('id') + '/update', 'Modifier une notification',
            '#ajax-dlg', '800px', 'Modifier', null, add_datepickers);
    });
    $('.del-hctrait').click(function() {
        generic_ajaxform_dialog('healthcare_treatment/' + $(this).data('id') + '/del', 'Supprimer une prise en charge de traitement',
            '#ajax-dlg', '500px', 'Supprimer');
    });
    $('.del-hcdiag').click(function() {
        generic_ajaxform_dialog('healthcare_diagnostic/' + $(this).data('id') + '/del', 'Supprimer une prise en charge de diagnostic',
            '#ajax-dlg', '500px', 'Supprimer');
    });
    $('.del-notification').click(function() {
        generic_ajaxform_dialog('healthcare_notification/' + $(this).data('id') + '/del', 'Supprimer une notification',
            '#ajax-dlg', '500px', 'Supprimer');
    });

    $('.update-patient-state-btn').click(function() {
        generic_ajaxform_dialog('state/' + $(this).data('id') + '/update', 'Modifier un état',
            '#ajax-dlg', '500px', 'Modifier', '#histo', add_datepickers);
    });
    $('.del-patient-state-btn').click(function() {
        generic_ajaxform_dialog('state/' + $(this).data('id') + '/del', 'Supprimer un état',
            '#ajax-dlg', '500px', 'Supprimer', '#histo');
    });

      $('.place_of_life').click(function() {
          if ((this.checked) == true) {
              var value = "true";
          } else {
              var value = "false";
          }
          $.ajax({
              url: '/api/v1/patientaddress/' + $(this).data("id") + '/?format=json',
              type: 'PATCH',
              contentType: 'application/json',
              data: '{"place_of_life": ' + value + '}',
              success: function(data) {
                  console.log('success');
              }
          });
      });

    $('input#id_id-birthdate', this).datepicker({dateFormat: 'd/m/yy', showOn: 'button' });

    $('button.blind').next().hide();
    $('button.blind').click(function() {
      $(this).next().toggle('blind');
    });
    var tabid = $.url($(location).attr('href')).fparam('tab');
    if (tabid) {
        $tabs.tabs('select',  parseInt(tabid));
    }
  });
})(window.jQuery)
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import {
  TouchableOpacity,
  ListView,
  StyleSheet,
  View,
  Text,
} from 'react-native'

import Icon from 'react-native-vector-icons/Ionicons'

export default class extends Component {
  constructor(props) {
    super(props)
    const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2})
    this.state = {
      refreshing: false,
      dataSource: ds.cloneWithRows(['row 1', 'row 2']),
    }
    this._renderRow = this._renderRow.bind(this)
  }

  _renderRow(rowData) {
    const tag = rowData
    const _onPress = () => {
      console.info('★', rowData.name, this.props)
      this.props.navigator.push({
        title: tag.name,
        screen: 'Chase.TagsTabScreen',
        passProps: { tag },
      })
    }
    return (
      <TouchableOpacity onPress={ _onPress } style={styles.row}>
        <View style={styles.rowLeft}>
          <Icon style={styles.tagIcon} name="ios-pricetag" size={20} />
          <Text>{ tag.name }</Text>
        </View>
        <View style={styles.rowRight}>
          <Text style={styles.badge}>{ tag.items.length }</Text>
          <Icon style={styles.chevronIcon} name="ios-arrow-forward" size={20} />
        </View>
      </TouchableOpacity>
    )
  }

  render() {
    return (
      <ListView
        dataSource={this.state.dataSource.cloneWithRows(this.props.tagsArr)}
        renderRow={this._renderRow}
      />
    )
  }
}

const styles = StyleSheet.create({
  row: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    backgroundColor: '#fff',
    borderBottomColor: '#ddd',
    borderBottomWidth: 1,
  },
  rowLeft: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  rowRight: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  tagIcon: {
    width: 40,
    padding: 10,
    alignItems: 'center',
    justifyContent: 'center',
    color: '#ddd'
  },
  chevronIcon: {
    width: 30,
    padding: 10,
    alignItems: 'center',
    justifyContent: 'center',
    color: '#ddd'
  },
  badge: {
    padding: 10,
    color: '#ccc'
  },
})
<commit_msg>:+1: styling<commit_after>import React, { Component } from 'react'
import {
  TouchableOpacity,
  ListView,
  StyleSheet,
  View,
  Text,
} from 'react-native'

import Icon from 'react-native-vector-icons/Ionicons'

export default class extends Component {
  constructor(props) {
    super(props)
    const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2})
    this.state = {
      refreshing: false,
      dataSource: ds.cloneWithRows(['row 1', 'row 2']),
    }
    this._renderRow = this._renderRow.bind(this)
  }

  _renderRow(rowData) {
    const tag = rowData
    const _onPress = () => {
      console.info('★', rowData.name, this.props)
      this.props.navigator.push({
        title: tag.name,
        screen: 'Chase.TagsTabScreen',
        passProps: { tag },
      })
    }
    return (
      <TouchableOpacity onPress={ _onPress } style={styles.row}>
        <View style={styles.rowLeft}>
          <Icon style={styles.tagIcon} name="ios-pricetag" size={20} />
          <Text>{ tag.name }</Text>
        </View>
        <View style={styles.rowRight}>
          <Text style={styles.badge}>{ tag.items.length }</Text>
          <Icon style={styles.chevronIcon} name="ios-arrow-forward" size={20} />
        </View>
      </TouchableOpacity>
    )
  }

  render() {
    return (
      <ListView
        dataSource={this.state.dataSource.cloneWithRows(this.props.tagsArr)}
        renderRow={this._renderRow}
      />
    )
  }
}

const styles = StyleSheet.create({
  row: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    backgroundColor: '#fff',
    borderBottomColor: '#ddd',
    borderBottomWidth: 1,
  },
  rowLeft: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  rowRight: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  tagIcon: {
    width: 40,
    padding: 10,
    alignItems: 'center',
    justifyContent: 'center',
    color: '#ddd'
  },
  chevronIcon: {
    width: 30,
    padding: 10,
    alignItems: 'center',
    justifyContent: 'center',
    color: '#ddd'
  },
  badge: {
    padding: 10,
    color: 'rgb(51, 123, 246)'
  },
})
<|endoftext|>


<commit_before>Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://cck2/CCK2.jsm");

(function () {
  var config = CCK2.getConfig();
  var temp = {};
  Components.utils.import("resource://cck2/Preferences.jsm", temp);
  var Preferences = temp.Preferences;
  let scope = {};
  Services.scriptloader.loadSubScript("chrome://cck2/content/util.js", scope);
  let {E, hide, disable, errorCritical} = scope;

  function paneload(event) {
    if (event.target.id == "panePrivacy" &&
	config.disablePrivateBrowsing) {
      hide(E("privateBrowsingAutoStart"));
      var privateBrowsingMenu = document.querySelector("menuitem[value='dontremember']");
      hide(privateBrowsingMenu);
    }
    if (event.target.id == "paneAdvanced" &&
	config.disableCrashReporter) {
      disable(E("submitCrashesBox"));
    }
    if (event.target.id == "paneAdvanced" &&
	Preferences.locked("datareporting.healthreport.uploadEnabled", false)) {
      disable(E("submitHealthReportBox"));
    }
    if (event.target.id == "paneSecurity" &&
	config.noMasterPassword == true) {
      hide(E("useMasterPassword"));
      hide(E("changeMasterPassword"));
    }
  }

  function startup()
  {
    try {
      window.removeEventListener("load", startup, false);
      if (!config.disableSync)
        return;
      var prefWindow = E("BrowserPreferences");
      var paneSyncRadio = document.getAnonymousElementByAttribute(prefWindow, "pane", "paneSync");
      hide(paneSyncRadio);
      var paneDeck = document.getAnonymousElementByAttribute(prefWindow, "anonid", "paneDeck");
      var paneSync = E("paneSync");
      paneSync.removeAttribute("helpTopic");
      var weavePrefsDeck = E("weavePrefsDeck");
      if (weavePrefsDeck)
        weavePrefsDeck.parentNode.removeChild(weavePrefsDeck);
      if (prefWindow.currentPane == E("paneSync"))
	prefWindow.showPane(E("paneMain"));
    } catch (e) {
      errorCritical(e);
    }
  }

  function shutdown()
  {
    window.removeEventListener("unload", shutdown, false);
    window.removeEventListener("paneload", paneload, false);
  }

  window.addEventListener("load", startup, false);
  window.addEventListener("unload", shutdown, false);
  window.addEventListener("paneload", paneload, false);
})();
<commit_msg>Hide pref elements<commit_after>Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://cck2/CCK2.jsm");

(function () {
  var config = CCK2.getConfig();
  var temp = {};
  Components.utils.import("resource://cck2/Preferences.jsm", temp);
  var Preferences = temp.Preferences;
  let scope = {};
  Services.scriptloader.loadSubScript("chrome://cck2/content/util.js", scope);
  let {E, hide, disable, errorCritical} = scope;

  function paneload(event) {
    if (config.hiddenUI) {
      for (var i=0; i < config.hiddenUI.length; i++) {
	var uiElement = document.querySelector(config.hiddenUI[i]);
	if (!uiElement)
	  continue;
	hide(uiElement);
      }
    }
    if (event.target.id == "panePrivacy" &&
	config.disablePrivateBrowsing) {
      hide(E("privateBrowsingAutoStart"));
      var privateBrowsingMenu = document.querySelector("menuitem[value='dontremember']");
      hide(privateBrowsingMenu);
    }
    if (event.target.id == "paneAdvanced" &&
	config.disableCrashReporter) {
      disable(E("submitCrashesBox"));
    }
    if (event.target.id == "paneAdvanced" &&
	Preferences.locked("datareporting.healthreport.uploadEnabled", false)) {
      disable(E("submitHealthReportBox"));
    }
    if (event.target.id == "paneSecurity" &&
	config.noMasterPassword == true) {
      hide(E("useMasterPassword"));
      hide(E("changeMasterPassword"));
    }
  }

  function startup()
  {
    try {
      window.removeEventListener("load", startup, false);
      if (!config.disableSync)
        return;
      var prefWindow = E("BrowserPreferences");
      var paneSyncRadio = document.getAnonymousElementByAttribute(prefWindow, "pane", "paneSync");
      hide(paneSyncRadio);
      var paneDeck = document.getAnonymousElementByAttribute(prefWindow, "anonid", "paneDeck");
      var paneSync = E("paneSync");
      paneSync.removeAttribute("helpTopic");
      var weavePrefsDeck = E("weavePrefsDeck");
      if (weavePrefsDeck)
        weavePrefsDeck.parentNode.removeChild(weavePrefsDeck);
      if (prefWindow.currentPane == E("paneSync"))
	prefWindow.showPane(E("paneMain"));
    } catch (e) {
      errorCritical(e);
    }
  }

  function shutdown()
  {
    window.removeEventListener("unload", shutdown, false);
    window.removeEventListener("paneload", paneload, false);
  }

  window.addEventListener("load", startup, false);
  window.addEventListener("unload", shutdown, false);
  window.addEventListener("paneload", paneload, false);
})();
<|endoftext|>


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

angular.module(PKG.name + '.commons')
  .controller('MyDAGController', function MyDAGController(jsPlumb, $scope, $timeout, MyDAGFactory, GLOBALS, NodesActionsFactory, $window, NodesStore, HydratorErrorFactory, $rootScope, HydratorService, $popover) {

    var vm = this;

    var endpoints = [];
    var sourceSettings = angular.copy(MyDAGFactory.getSettings(false).source);
    var sinkSettings = angular.copy(MyDAGFactory.getSettings(false).sink);
    var transformSourceSettings = angular.copy(MyDAGFactory.getSettings(false).transformSource);
    var transformSinkSettings = angular.copy(MyDAGFactory.getSettings(false).transformSink);

    var labels = [];

    var metricsLabel = [
      [ 'Custom', {
        create: function (label) {
          labels.push(label);
          return angular.element('3000');
        },
        location: [2, 0],
        id: 'metricLabel'
      }]
    ];

    if ($scope.showMetrics) {
      sourceSettings.overlays = metricsLabel;
      transformSourceSettings.overlays = metricsLabel;
    }

    var dragged = false;
    var canvasDragged = false;

    vm.isDisabled = $scope.isDisabled;

    var popovers = [];

    vm.scale = 1.0;

    vm.panning = {
      style: {
        'top': 0,
        'left': 0
      },
      top: 0,
      left: 0
    };

    /*
    FIXME: This should be fixed. Right now the assumption is to update
     the store before my-dag directive is rendered. What happens if we get the
     data after the rendering? The init function is never called or the DAG is not
     rendered based on the data.

     Right now there is a cycle which prevents us from listening to the NodeStore
     changes. The infinite recurrsion happens like this,
     Assuming we have this
     NodesStore.registerChangeListener(init);
     1. User adds a connection in view
     2. 'connection' event is fired by jsplumb
     3. On connection event we call 'formatConnections' function
     4. 'formatConnections' constructs the 'connections' array and sets it to NodesStore
     5. Now NodesStore fires an update to changelisteners.
     6. 'init' function gets called.
     6. 'init' function again programmatically connects all the nodes and sets it to NodesStore
     7. Control goes to step 5
     7. Hence the loop.

     We need to be able to separate render of graph from data and incremental user interactions.
     - Programmatically it should be possible to provide data and should be able to ask dag to render it at any time post-rendering of the directive
     - User should be able interact with the dag and add incremental changes.
    */
    function init() {
      $scope.nodes = NodesStore.getNodes();
      $scope.connections = NodesStore.getConnections();

      $timeout(function () {
        // centering DAG
        if ($scope.nodes.length) {
          var margins = $scope.getGraphMargins($scope.nodes);
          $timeout(function () { vm.instance.repaintEverything(); });

          vm.scale = margins.scale;
        }

        addEndpoints();

        angular.forEach($scope.connections, function (conn) {
          var sourceNode = $scope.nodes.filter( node => node.id === conn.from);
          var targetNode = $scope.nodes.filter( node => node.id === conn.to);
          if (!sourceNode.length || !targetNode.length) {
            return;
          }
          var sourceId = sourceNode[0].type === 'transform' ? 'Left' + conn.from : conn.from;
          var targetId = targetNode[0].type === 'transform' ? 'Right' + conn.to : conn.to;
          var connObj = {
            uuids: [sourceId, targetId]
          };
          if (vm.isDisabled) {
            connObj.detachable = false;
          }

          vm.instance.connect(connObj);
        });

        setZoom(vm.scale, vm.instance);

        if ($scope.showMetrics) {
          $scope.$watch('metricsData', function () {
            console.log('metrics', $scope.metricsData);

          }, true);
        }

      });

    }

    vm.zoomIn = function () {
      closeAllPopovers();
      vm.scale += 0.1;

      if (vm.scale >= 0.8) {
        angular.forEach(labels, function (label) {
          label.getOverlay('metricLabel').show();
        });
      }

      setZoom(vm.scale, vm.instance);
    };

    vm.zoomOut = function () {
      closeAllPopovers();
      if (vm.scale <= 0.2) { return; }

      if (vm.scale < 0.9) {
        angular.forEach(labels, function (label) {
          label.getOverlay('metricLabel').hide();
        });
      }

      vm.scale -= 0.1;
      setZoom(vm.scale, vm.instance);
    };

    /**
     * Utily function from jsPlumb
     * https://jsplumbtoolkit.com/community/doc/zooming.html
     *
     * slightly modified to fit our needs
     **/
    function setZoom(zoom, instance, transformOrigin, el) {
      if ($scope.nodes.length === 0) { return; }

      transformOrigin = transformOrigin || [ 0.5, 0.5 ];
      instance = instance || jsPlumb;
      el = el || instance.getContainer();
      var p = [ 'webkit', 'moz', 'ms', 'o' ],
          s = 'scale(' + zoom + ')',
          oString = (transformOrigin[0] * 100) + '% ' + (transformOrigin[1] * 100) + '%';

      for (var i = 0; i < p.length; i++) {
        el.style[p[i] + 'Transform'] = s;
        el.style[p[i] + 'TransformOrigin'] = oString;
      }

      el.style['transform'] = s;
      el.style['transformOrigin'] = oString;

      instance.setZoom(zoom);
    }

    function addEndpoints() {
      angular.forEach($scope.nodes, function (node) {
        if (endpoints.indexOf(node.id) !== -1) {
          return;
        }
        endpoints.push(node.id);

        var type = GLOBALS.pluginConvert[node.type];
        switch(type) {
          case 'source':
            vm.instance.addEndpoint(node.id, sourceSettings, {uuid: node.id});
            break;
          case 'sink':
            vm.instance.addEndpoint(node.id, sinkSettings, {uuid: node.id});
            break;
          case 'transform':
            // Need to id each end point so that it can be used later to make connections.
            vm.instance.addEndpoint(node.id, transformSourceSettings, {uuid: 'Left' + node.id});
            vm.instance.addEndpoint(node.id, transformSinkSettings, {uuid: 'Right' + node.id});
            break;
        }
      });
    }

    function transformCanvas (top, left) {
      vm.panning.top += top;
      vm.panning.left += left;

      vm.panning.style = {
        'top': vm.panning.top + 'px',
        'left': vm.panning.left + 'px'
      };
    }

    function formatConnections() {
      closeAllPopovers();
      var connections = [];
      angular.forEach(vm.instance.getConnections(), function (conn) {
        connections.push({
          from: conn.sourceId,
          to: conn.targetId
        });
      });
      NodesActionsFactory.setConnections(connections);
    }

    function connectionClick (connection) {
      if (!connection) {
        return;
      }

      var label = angular.element(connection.getOverlay('label').getElement());
      var scope = $rootScope.$new();

      scope.data = $scope.connectionPopoverData().call($scope.context, connection.sourceId, connection.targetId);

      var popover = $popover(label, {
        trigger: 'manual',
        placement: 'auto',
        target: label,
        templateUrl: $scope.templatePopover,
        container: 'main',
        scope: scope
      });

      popovers.push(popover);

      $timeout(function() {
        popover.show();
      });

      $scope.$on('$destroy', function () {
        scope.$destroy();
      });
    }

    function closeAllPopovers() {
      if (popovers.length === 0) { return; }

      angular.forEach(popovers, function (popover) {
        popover.hide();
      });
    }

    jsPlumb.ready(function() {
      var dagSettings = MyDAGFactory.getSettings().default;

      jsPlumb.setContainer('dag-container');
      vm.instance = jsPlumb.getInstance(dagSettings);

      init();

      // Making canvas draggable
      vm.secondInstance = jsPlumb.getInstance();
      vm.secondInstance.draggable('diagram-container', {
        stop: function (e) {
          e.el.style.left = '0px';
          e.el.style.top = '0px';
          transformCanvas(e.pos[1], e.pos[0]);
          NodesActionsFactory.resetPluginCount();
          NodesActionsFactory.setCanvasPanning(vm.panning);
        },
        start: function () {
          canvasDragged = true;
          closeAllPopovers();
        }
      });

      vm.instance.bind('connection', formatConnections);
      vm.instance.bind('connectionDetached', formatConnections);

      vm.instance.bind('click', connectionClick);

      // This should be removed once the node config is using FLUX
      $scope.$watch('nodes', function () {
        closeAllPopovers();

        $timeout(function () {
          var nodes = document.querySelectorAll('.box');
          addEndpoints();

          if (!vm.isDisabled) {
            vm.instance.draggable(nodes, {
              start: function () {
                dragged = true;
                closeAllPopovers();
              },
              stop: function (dragEndEvent) {
                var config = {
                  _uiPosition: {
                    top: dragEndEvent.el.style.top,
                    left: dragEndEvent.el.style.left
                  }
                };
                NodesActionsFactory.updateNode(dragEndEvent.el.id, config);
                $timeout(function () { vm.instance.repaintEverything(); });
              }
            });
          }
        });

        angular.forEach($scope.nodes, function (plugin) {
          plugin.requiredFieldCount = HydratorErrorFactory.countRequiredFields(plugin);
          if (plugin.requiredFieldCount > 0) {
            plugin.error = {
              message: GLOBALS.en.hydrator.studio.genericMissingRequiredFieldsError
            };
          } else {
            plugin.error = false;
          }
        });

      }, true);

      $scope.$watchCollection('connections', function () {
        console.log('ChangeConnection', $scope.connections);
      });

      // This is needed to redraw connections and endpoints on browser resize
      angular.element($window).on('resize', function() {
        vm.instance.repaintEverything();
      });

    });

    // var selectedNode = null;

    vm.clearNodeSelection = function () {
      if (canvasDragged) {
        canvasDragged = false;
        return;
      }

      vm.instance.clearDragSelection();
      angular.forEach($scope.nodes, function (node) {
        node.selected = false;
      });
    };

    function checkSelection() {
      vm.instance.clearDragSelection();

      var selected = [];
      angular.forEach($scope.nodes, function (node) {
        if (node.selected) {
          selected.push(node.id);
        }
      });

      vm.instance.addToDragSelection(selected);
    }

    vm.onNodeClick = function(event, node) {
      event.stopPropagation();

      if (dragged) {
        dragged = false;
        return;
      }

      if ((event.ctrlKey || event.metaKey)) {
        node.selected = !node.selected;
        NodesActionsFactory.resetSelectedNode();

        if (node.selected) {
          checkSelection();
        } else {
          vm.instance.removeFromDragSelection(node.id);
        }
      } else {
        vm.clearNodeSelection();
        node.selected = true;
        NodesActionsFactory.selectNode(node.id);
      }

      // $scope.nodeClick.call($scope.context, node);
    };

    vm.onNodeDelete = function (event, node) {
      event.stopPropagation();
      closeAllPopovers();
      NodesActionsFactory.removeNode(node.id);
      vm.instance.remove(node.id);
    };

    vm.cleanUpGraph = function () {
      if ($scope.nodes.length === 0) { return; }

      var graphNodes = MyDAGFactory.getGraphLayout($scope.nodes, $scope.connections)._nodes;

      angular.forEach($scope.nodes, function (node) {
        var location = graphNodes[node.id];
        node._uiPosition = {
          left: location.x + 'px',
          top: location.y + 'px'
        };
      });

      vm.panning.top = 0;
      vm.panning.left = 0;

      vm.panning.style = {
        'top': vm.panning.top + 'px',
        'left': vm.panning.left + 'px'
      };

      var margins = $scope.getGraphMargins($scope.nodes);
      vm.scale = margins.scale;
      $timeout(function () { vm.instance.repaintEverything(); });
      setZoom(vm.scale, vm.instance);

      NodesActionsFactory.resetPluginCount();
      NodesActionsFactory.setCanvasPanning(vm.panning);
    };

    vm.locateNodes = function () {
      var minLeft = null;
      var leftMostNode = null;

      angular.forEach($scope.nodes, function (node) {
        var left = parseInt(node._uiPosition.left, 10);

        if (node._uiPosition.left.includes('vw')) {
          left = parseInt(left, 10)/100 * document.documentElement.clientWidth;
          node._uiPosition.left = left + 'px';
        }

        if (minLeft === null || left < minLeft) {
          minLeft = left;
          leftMostNode = node;
        }
      });

      var offsetLeft = parseInt(leftMostNode._uiPosition.left, 10);
      var offsetTop = parseInt(leftMostNode._uiPosition.top, 10);

      angular.forEach($scope.nodes, function (node) {
        var left = parseInt(node._uiPosition.left, 10);
        var top = parseInt(node._uiPosition.top, 10);

        node._uiPosition = {
          left: (left - offsetLeft + 50) + 'px',
          top: (top - offsetTop + 150) + 'px'
        };
      });

      $timeout(function () { vm.instance.repaintEverything(); });

      vm.panning.top = 0;
      vm.panning.left = 0;

      vm.panning.style = {
        'top': vm.panning.top + 'px',
        'left': vm.panning.left + 'px'
      };

      NodesActionsFactory.resetPluginCount();
      NodesActionsFactory.setCanvasPanning(vm.panning);
    };

    $scope.$on('$destroy', function () {
      NodesActionsFactory.resetNodesAndConnections();
      NodesStore.reset();
    });

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

angular.module(PKG.name + '.commons')
  .controller('MyDAGController', function MyDAGController(jsPlumb, $scope, $timeout, MyDAGFactory, GLOBALS, NodesActionsFactory, $window, NodesStore, HydratorErrorFactory, $rootScope, HydratorService, $popover) {

    var vm = this;

    var endpoints = [];
    var sourceSettings = angular.copy(MyDAGFactory.getSettings(false).source);
    var sinkSettings = angular.copy(MyDAGFactory.getSettings(false).sink);
    var transformSourceSettings = angular.copy(MyDAGFactory.getSettings(false).transformSource);
    var transformSinkSettings = angular.copy(MyDAGFactory.getSettings(false).transformSink);

    var labels = [];

    var metricsLabel = [
      [ 'Custom', {
        create: function (label) {
          labels.push(label);
          return angular.element('3000');
        },
        location: [2, 0],
        id: 'metricLabel'
      }]
    ];

    if ($scope.showMetrics) {
      sourceSettings.overlays = metricsLabel;
      transformSourceSettings.overlays = metricsLabel;
    }

    var dragged = false;
    var canvasDragged = false;

    vm.isDisabled = $scope.isDisabled;

    var popovers = [];

    vm.scale = 1.0;

    vm.panning = {
      style: {
        'top': 0,
        'left': 0
      },
      top: 0,
      left: 0
    };

    /*
    FIXME: This should be fixed. Right now the assumption is to update
     the store before my-dag directive is rendered. What happens if we get the
     data after the rendering? The init function is never called or the DAG is not
     rendered based on the data.

     Right now there is a cycle which prevents us from listening to the NodeStore
     changes. The infinite recurrsion happens like this,
     Assuming we have this
     NodesStore.registerChangeListener(init);
     1. User adds a connection in view
     2. 'connection' event is fired by jsplumb
     3. On connection event we call 'formatConnections' function
     4. 'formatConnections' constructs the 'connections' array and sets it to NodesStore
     5. Now NodesStore fires an update to changelisteners.
     6. 'init' function gets called.
     6. 'init' function again programmatically connects all the nodes and sets it to NodesStore
     7. Control goes to step 5
     7. Hence the loop.

     We need to be able to separate render of graph from data and incremental user interactions.
     - Programmatically it should be possible to provide data and should be able to ask dag to render it at any time post-rendering of the directive
     - User should be able interact with the dag and add incremental changes.
    */
    function init() {
      $scope.nodes = NodesStore.getNodes();
      $scope.connections = NodesStore.getConnections();

      $timeout(function () {
        // centering DAG
        if ($scope.nodes.length) {
          var margins = $scope.getGraphMargins($scope.nodes);
          $timeout(function () { vm.instance.repaintEverything(); });

          vm.scale = margins.scale;
        }

        addEndpoints();

        angular.forEach($scope.connections, function (conn) {
          var sourceNode = $scope.nodes.filter( node => node.id === conn.from);
          var targetNode = $scope.nodes.filter( node => node.id === conn.to);
          if (!sourceNode.length || !targetNode.length) {
            return;
          }
          var sourceId = sourceNode[0].type === 'transform' ? 'Left' + conn.from : conn.from;
          var targetId = targetNode[0].type === 'transform' ? 'Right' + conn.to : conn.to;
          var connObj = {
            uuids: [sourceId, targetId]
          };
          if (vm.isDisabled) {
            connObj.detachable = false;
          }

          vm.instance.connect(connObj);
        });

        setZoom(vm.scale, vm.instance);

        if ($scope.showMetrics) {
          $scope.$watch('metricsData', function () {
            console.log('metrics', $scope.metricsData);

          }, true);
        }

      });

    }

    vm.zoomIn = function () {
      closeAllPopovers();
      vm.scale += 0.1;

      if (vm.scale >= 0.8) {
        angular.forEach(labels, function (label) {
          label.getOverlay('metricLabel').show();
        });
      }

      setZoom(vm.scale, vm.instance);
    };

    vm.zoomOut = function () {
      closeAllPopovers();
      if (vm.scale <= 0.2) { return; }

      if (vm.scale < 0.9) {
        angular.forEach(labels, function (label) {
          label.getOverlay('metricLabel').hide();
        });
      }

      vm.scale -= 0.1;
      setZoom(vm.scale, vm.instance);
    };

    /**
     * Utily function from jsPlumb
     * https://jsplumbtoolkit.com/community/doc/zooming.html
     *
     * slightly modified to fit our needs
     **/
    function setZoom(zoom, instance, transformOrigin, el) {
      if ($scope.nodes.length === 0) { return; }

      transformOrigin = transformOrigin || [ 0.5, 0.5 ];
      instance = instance || jsPlumb;
      el = el || instance.getContainer();
      var p = [ 'webkit', 'moz', 'ms', 'o' ],
          s = 'scale(' + zoom + ')',
          oString = (transformOrigin[0] * 100) + '% ' + (transformOrigin[1] * 100) + '%';

      for (var i = 0; i < p.length; i++) {
        el.style[p[i] + 'Transform'] = s;
        el.style[p[i] + 'TransformOrigin'] = oString;
      }

      el.style['transform'] = s;
      el.style['transformOrigin'] = oString;

      instance.setZoom(zoom);
    }

    function addEndpoints() {
      angular.forEach($scope.nodes, function (node) {
        if (endpoints.indexOf(node.id) !== -1) {
          return;
        }
        endpoints.push(node.id);

        var type = GLOBALS.pluginConvert[node.type];
        switch(type) {
          case 'source':
            vm.instance.addEndpoint(node.id, sourceSettings, {uuid: node.id});
            break;
          case 'sink':
            vm.instance.addEndpoint(node.id, sinkSettings, {uuid: node.id});
            break;
          case 'transform':
            // Need to id each end point so that it can be used later to make connections.
            vm.instance.addEndpoint(node.id, transformSourceSettings, {uuid: 'Left' + node.id});
            vm.instance.addEndpoint(node.id, transformSinkSettings, {uuid: 'Right' + node.id});
            break;
        }
      });
    }

    function transformCanvas (top, left) {
      vm.panning.top += top;
      vm.panning.left += left;

      vm.panning.style = {
        'top': vm.panning.top + 'px',
        'left': vm.panning.left + 'px'
      };
    }

    function formatConnections() {
      closeAllPopovers();
      var connections = [];
      angular.forEach(vm.instance.getConnections(), function (conn) {
        connections.push({
          from: conn.sourceId,
          to: conn.targetId
        });
      });
      NodesActionsFactory.setConnections(connections);
    }

    function addConnection (connectionObj) {
      console.log('conn', connection);
      var connection = connectionObj.connection;

      var label = angular.element(connection.getOverlay('label').getElement());
      var scope = $rootScope.$new();

      var popover = $popover(label, {
        trigger: 'manual',
        placement: 'auto',
        target: label,
        templateUrl: $scope.templatePopover,
        container: 'main',
        scope: scope
      });

      popovers.push(popover);

      connection.bind('click', function (conn, event) {
        event.stopPropagation();
        scope.data = $scope.connectionPopoverData().call($scope.context, connection.sourceId, connection.targetId);
        popover.show();
      });

      $scope.$on('$destroy', function () {
        scope.$destroy();
      });

      formatConnections();
    }

    // function connectionClick (connection) {
    //   if (!connection) {
    //     return;
    //   }

    //   var label = angular.element(connection.getOverlay('label').getElement());
    //   var scope = $rootScope.$new();

    //   scope.data = $scope.connectionPopoverData().call($scope.context, connection.sourceId, connection.targetId);

    //   var popover = $popover(label, {
    //     trigger: 'manual',
    //     placement: 'auto',
    //     target: label,
    //     templateUrl: $scope.templatePopover,
    //     container: 'main',
    //     scope: scope
    //   });

    //   popovers.push(popover);

    //   $timeout(function() {
    //     popover.show();
    //   });

    //   $scope.$on('$destroy', function () {
    //     scope.$destroy();
    //   });
    // }

    function closeAllPopovers() {
      if (popovers.length === 0) { return; }

      angular.forEach(popovers, function (popover) {
        popover.hide();
      });
    }

    jsPlumb.ready(function() {
      var dagSettings = MyDAGFactory.getSettings().default;

      jsPlumb.setContainer('dag-container');
      vm.instance = jsPlumb.getInstance(dagSettings);

      init();

      // Making canvas draggable
      vm.secondInstance = jsPlumb.getInstance();
      vm.secondInstance.draggable('diagram-container', {
        stop: function (e) {
          e.el.style.left = '0px';
          e.el.style.top = '0px';
          transformCanvas(e.pos[1], e.pos[0]);
          NodesActionsFactory.resetPluginCount();
          NodesActionsFactory.setCanvasPanning(vm.panning);
        },
        start: function () {
          canvasDragged = true;
          closeAllPopovers();
        }
      });

      vm.instance.bind('connection', addConnection);
      vm.instance.bind('connectionDetached', formatConnections);

      // vm.instance.bind('click', connectionClick);

      // This should be removed once the node config is using FLUX
      $scope.$watch('nodes', function () {
        closeAllPopovers();

        $timeout(function () {
          var nodes = document.querySelectorAll('.box');
          addEndpoints();

          if (!vm.isDisabled) {
            vm.instance.draggable(nodes, {
              start: function () {
                dragged = true;
                closeAllPopovers();
              },
              stop: function (dragEndEvent) {
                var config = {
                  _uiPosition: {
                    top: dragEndEvent.el.style.top,
                    left: dragEndEvent.el.style.left
                  }
                };
                NodesActionsFactory.updateNode(dragEndEvent.el.id, config);
                $timeout(function () { vm.instance.repaintEverything(); });
              }
            });
          }
        });

        angular.forEach($scope.nodes, function (plugin) {
          plugin.requiredFieldCount = HydratorErrorFactory.countRequiredFields(plugin);
          if (plugin.requiredFieldCount > 0) {
            plugin.error = {
              message: GLOBALS.en.hydrator.studio.genericMissingRequiredFieldsError
            };
          } else {
            plugin.error = false;
          }
        });

      }, true);

      $scope.$watchCollection('connections', function () {
        console.log('ChangeConnection', $scope.connections);
      });

      // This is needed to redraw connections and endpoints on browser resize
      angular.element($window).on('resize', function() {
        vm.instance.repaintEverything();
      });

    });

    // var selectedNode = null;

    vm.clearNodeSelection = function () {
      if (canvasDragged) {
        canvasDragged = false;
        return;
      }
      closeAllPopovers();
      vm.instance.clearDragSelection();
      angular.forEach($scope.nodes, function (node) {
        node.selected = false;
      });
    };

    function checkSelection() {
      vm.instance.clearDragSelection();

      var selected = [];
      angular.forEach($scope.nodes, function (node) {
        if (node.selected) {
          selected.push(node.id);
        }
      });

      vm.instance.addToDragSelection(selected);
    }

    vm.onNodeClick = function(event, node) {
      event.stopPropagation();

      if (dragged) {
        dragged = false;
        return;
      }

      if ((event.ctrlKey || event.metaKey)) {
        node.selected = !node.selected;
        NodesActionsFactory.resetSelectedNode();

        if (node.selected) {
          checkSelection();
        } else {
          vm.instance.removeFromDragSelection(node.id);
        }
      } else {
        vm.clearNodeSelection();
        node.selected = true;
        NodesActionsFactory.selectNode(node.id);
      }

      // $scope.nodeClick.call($scope.context, node);
    };

    vm.onNodeDelete = function (event, node) {
      event.stopPropagation();
      closeAllPopovers();
      NodesActionsFactory.removeNode(node.id);
      vm.instance.remove(node.id);
    };

    vm.cleanUpGraph = function () {
      if ($scope.nodes.length === 0) { return; }

      var graphNodes = MyDAGFactory.getGraphLayout($scope.nodes, $scope.connections)._nodes;

      angular.forEach($scope.nodes, function (node) {
        var location = graphNodes[node.id];
        node._uiPosition = {
          left: location.x + 'px',
          top: location.y + 'px'
        };
      });

      vm.panning.top = 0;
      vm.panning.left = 0;

      vm.panning.style = {
        'top': vm.panning.top + 'px',
        'left': vm.panning.left + 'px'
      };

      var margins = $scope.getGraphMargins($scope.nodes);
      vm.scale = margins.scale;
      $timeout(function () { vm.instance.repaintEverything(); });
      setZoom(vm.scale, vm.instance);

      NodesActionsFactory.resetPluginCount();
      NodesActionsFactory.setCanvasPanning(vm.panning);
    };

    vm.locateNodes = function () {
      var minLeft = null;
      var leftMostNode = null;

      angular.forEach($scope.nodes, function (node) {
        var left = parseInt(node._uiPosition.left, 10);

        if (node._uiPosition.left.includes('vw')) {
          left = parseInt(left, 10)/100 * document.documentElement.clientWidth;
          node._uiPosition.left = left + 'px';
        }

        if (minLeft === null || left < minLeft) {
          minLeft = left;
          leftMostNode = node;
        }
      });

      var offsetLeft = parseInt(leftMostNode._uiPosition.left, 10);
      var offsetTop = parseInt(leftMostNode._uiPosition.top, 10);

      angular.forEach($scope.nodes, function (node) {
        var left = parseInt(node._uiPosition.left, 10);
        var top = parseInt(node._uiPosition.top, 10);

        node._uiPosition = {
          left: (left - offsetLeft + 50) + 'px',
          top: (top - offsetTop + 150) + 'px'
        };
      });

      $timeout(function () { vm.instance.repaintEverything(); });

      vm.panning.top = 0;
      vm.panning.left = 0;

      vm.panning.style = {
        'top': vm.panning.top + 'px',
        'left': vm.panning.left + 'px'
      };

      NodesActionsFactory.resetPluginCount();
      NodesActionsFactory.setCanvasPanning(vm.panning);
    };

    $scope.$on('$destroy', function () {
      NodesActionsFactory.resetNodesAndConnections();
      NodesStore.reset();
    });

  });
<|endoftext|>


<commit_before>function setup() {
    createCanvas(400, 220);
}

function draw() {
    background('white')

    strokeWeight(3);
    line(135, 70, 175, 43);
    line(65, 70, 25, 43);
    ellipse(100, 110, 100, 100);
    ellipse(100, 80, 70, 70);
    ellipse(100, 50, 40, 40);
    strokeWeight(1);
    fill('#FF6600');;
    triangle(98,60,104,58,100+mouseX/20,70+mouseY/20);
    fill('white');
    ellipse(92,50,5,5);
    ellipse(108,50,5,5);
    textSize(24);
    fill('black')
    textFont('Proxima Nova')
    textStyle('italic')
    text("Do you want to build a me?",10,200);

}
<commit_msg>snowman!<commit_after>function setup() {
  createCanvas(400, 220)
}

function draw() {
  background('white')
  strokeWeight(3)
  line(135, 70, 175, 43)
  line(65, 70, 25, 43)
  ellipse(100, 110, 100, 100)
  ellipse(100, 80, 70, 70)
  ellipse(100, 50, 40, 40)
  strokeWeight(1)
  fill('#FF6600')
  triangle(98, 60, 104, 58, 100 + mouseX / 20, 70 + mouseY / 20)
  fill('white')
  ellipse(92, 50, 5, 5)
  ellipse(108, 50, 5, 5)
  textSize(24)
  fill('black')
  textFont('Proxima Nova')
  textStyle('italic')
  text("Do you want to build a me?", 10, 200)
    // Show my mouse coordinates on the screen, useful for drawing:
  textFont('monospace')
  textSize(10)
  textStyle('bold')
  text('Mouse X = ' + mouseX + " | Mouse Y = " + mouseY, 10, 10)
}<|endoftext|>