<commit_before>/**
 * Copyright DataStax, Inc.
 *
 * Please see the license for details:
 * http://www.datastax.com/terms/datastax-dse-driver-license-terms
 */

'use strict';

const os = require('os');
const path = require('path');
const fs = require('fs');
const utils = require('./utils');
const types = require('./types');
const requests = require('./requests');
const ExecutionOptions = require('./execution-options').ExecutionOptions;
const packageInfo = require('../package.json');
const VersionNumber = require('./types/version-number');

let kerberosModule;

try {
  // eslint-disable-next-line
  kerberosModule = require('kerberos');
}
catch (err) {
  // Kerberos is an optional dependency
}

const minDse6Version = new VersionNumber(6, 0, 5);
const minDse51Version = new VersionNumber(5, 1, 13);
const dse600Version = new VersionNumber(6, 0, 0);
const rpc = "CALL InsightsRpc.reportInsight(?)";
const maxStatusErrorLogs = 5;

/**
 * Contains methods and functionality to send events to DSE Insights.
 */
class InsightsClient {

  /**
   * Creates a new instance of the {@link InsightsClient} using the driver {@link Client}.
   * @param {Client} client
   * @param {Object} [options]
   * @param {Number} [options.statusEventDelay]
   * @param {Function} [options.errorCallback]
   */
  constructor(client, options) {
    this._client = client;
    this._sessionId = types.Uuid.random().toString();
    this._enabled = false;
    this._closed = false;
    this._firstTimeout = null;
    this._recurrentTimeout = null;
    this._statusErrorLogs = 0;

    options = options || {};

    this._statusEventDelay = options.statusEventDelay || 300000;
    this._errorCallback = options.errorCallback || utils.noop;
  }

  /**
   * Initializes the insights client in the background by sending the startup event and scheduling status events at
   * regular intervals.
   */
  init() {
    this._enabled = this._client.options.monitorReporting.enabled && this._dseSupportsInsights();
    if (!this._enabled) {
      return;
    }

    this._sendStartupEvent()
      .then(() => {

        if (this._closed) {
          // The client was shutdown
          return;
        }

        // Send the status event the first time with a delay containing some random portion
        // Initial delay should be statusEventDelay - (0 to 10%)
        const firstDelay = Math.floor(this._statusEventDelay - 0.1 * this._statusEventDelay * Math.random());

        // Schedule the first timer
        this._firstTimeout = setTimeout(() => {

          // Send the first status event, the promise will never be rejected
          this._sendStatusEvent();

          // The following status events are sent at regular intervals
          this._recurrentTimeout = setInterval(() => this._sendStatusEvent(), this._statusEventDelay);
        }, firstDelay);
      })
      .catch(err => {

        if (this._closed) {
          // Sending failed because the Client was shutdown
          return;
        }

        // We shouldn't try to recover
        this._client.log('error', `Insights startup message could not be sent (${err})`, err);
        this._errorCallback(err);
      });
  }

  /**
   * Sends the startup event.
   * @returns {Promise}
   * @private
   */
  _sendStartupEvent() {
    return this._getStartupMessage()
      .then(message => new Promise((resolve, reject) => {
        const request = new requests.QueryRequest(rpc, [ message ], ExecutionOptions.empty());

        this._client.controlConnection.query(request, false, err => {
          if (!err) {
            resolve();
          } else {
            reject(err);
          }
        });
      }));
  }

  /**
   * Sends the status event.
   * @returns {Promise} A promise that is never rejected.
   * @private
   */
  _sendStatusEvent() {
    const promise = new Promise((resolve, reject) => {
      const request = new requests.QueryRequest(rpc, [ this._getStatusEvent() ], ExecutionOptions.empty());

      this._client.controlConnection.query(request, false, err => {
        if (!err) {
          resolve();
        } else {
          reject(err);
        }
      });
    });

    return promise
      .catch(err => {
        if (this._closed) {
          // Sending failed because the Client was shutdown
          return;
        }

        if (this._statusErrorLogs < maxStatusErrorLogs) {
          this._client.log('warning', `Insights status message could not be sent (${err})`, err);
          this._statusErrorLogs++;
        }

        this._errorCallback(err);
      });
  }

  /**
   * Validates the minimum server version for all nodes in the cluster.
   * @private
   */
  _dseSupportsInsights() {
    if (this._client.hosts.length === 0) {
      return false;
    }

    return this._client.hosts.values().reduce((acc, host) => {
      if (!acc) {
        return acc;
      }

      // No spread operator in Node.js 4 :/
      const versionArr = host.getDseVersion();
      const version = new VersionNumber(versionArr[0], versionArr[1], versionArr[2]);

      return version.compare(minDse6Version) >= 0 ||
        (version.compare(dse600Version) < 0 && version.compare(minDse51Version) >= 0);

    }, true);
  }

  /**
   * @returns {Promise<String>} Returns a json string with the startup message.
   * @private
   */
  _getStartupMessage() {

    const cc = this._client.controlConnection;
    const options = this._client.options;

    return this._getAppInfo(options)
      .then(appInfo => {

        const message = {
          metadata: {
            name: 'driver.startup',
            insightMappingId: 'v1',
            insightType: 'EVENT',
            timestamp: Date.now(),
            tags: { language: 'nodejs' }
          },
          data: {
            driverName: packageInfo.description,
            driverVersion: packageInfo.version,
            clientId: options.id,
            sessionId: this._sessionId,
            applicationName: appInfo.applicationName,
            applicationVersion: appInfo.applicationVersion,
            applicationNameWasGenerated: appInfo.applicationNameWasGenerated,
            contactPoints: mapToObject(cc.getResolvedContactPoints()),
            dataCenters: this._getDataCenters(),
            initialControlConnection: cc.host ? cc.host.address : undefined,
            protocolVersion: cc.protocolVersion,
            localAddress: cc.getLocalAddress(),
            hostName: os.hostname(),
            executionProfiles: getExecutionProfiles(this._client),
            poolSizeByHostDistance: {
              local: options.pooling.coreConnectionsPerHost[types.distance.local],
              remote: options.pooling.coreConnectionsPerHost[types.distance.remote]
            },
            heartbeatInterval: options.pooling.heartBeatInterval,
            compression: 'NONE',
            reconnectionPolicy: getPolicyInfo(options.policies.reconnection),
            ssl: {
              enabled: !!options.sslOptions,
              certValidation: options.sslOptions ? !!options.sslOptions.rejectUnauthorized : undefined
            },
            authProvider: {
              type: getConstructor(options.authProvider),
            },
            otherOptions: {
              coalescingThreshold: options.socketOptions.coalescingThreshold,
            },
            platformInfo: {
              os: {
                name: os.platform(),
                version: os.release(),
                arch: os.arch()
              },
              cpus: {
                length: os.cpus().length,
                model: os.cpus()[0].model
              },
              runtime: {
                node: process.versions['node'],
                v8: process.versions['v8'],
                uv: process.versions['uv'],
                openssl: process.versions['openssl'],
                kerberos: kerberosModule ? kerberosModule.version : undefined
              }
            },
            configAntiPatterns: this._getConfigAntiPatterns(),
            periodicStatusInterval: Math.floor(this._statusEventDelay / 1000)
          }
        };

        return JSON.stringify(message);
      });
  }

  _getConfigAntiPatterns() {
    const options = this._client.options;
    const result = {};

    if (options.sslOptions && !options.sslOptions.rejectUnauthorized) {
      result.sslWithoutCertValidation =
        'Client-to-node encryption is enabled but server certificate validation is disabled';
    }

    return result;
  }

  /**
   * Gets an array of data centers the driver connects to.
   * Whether the driver connects to a certain host is determined by the host distance (local and remote hosts)
   * and the pooling options (whether connection length for remote hosts is greater than 0).
   * @returns {Array}
   * @private
   */
  _getDataCenters() {
    const remoteConnectionsLength = this._client.options.pooling.coreConnectionsPerHost[types.distance.remote];
    const dataCenters = new Set();

    this._client.hosts.values().forEach(h => {
      const distance = this._client.profileManager.getDistance(h);
      if (distance === types.distance.local || (distance === types.distance.remote && remoteConnectionsLength > 0)) {
        dataCenters.add(h.datacenter);
      }
    });

    return Array.from(dataCenters);
  }

  /**
   * Tries to obtain the application name and version from
   * @param {DseClientOptions} options
   * @returns {Promise}
   * @private
   */
  _getAppInfo(options) {
    if (typeof options.applicationName === 'string') {
      return Promise.resolve({
        applicationName: options.applicationName,
        applicationVersion: options.applicationVersion,
        applicationNameWasGenerated: false
      });
    }

    let readPromise = Promise.resolve();

    if (require.main && require.main.filename) {
      const packageInfoPath = path.dirname(require.main.filename);
      readPromise = this._readPackageInfoFile(packageInfoPath);
    }

    return readPromise.then(text => {
      let applicationName = 'Default Node.js Application';
      let applicationVersion;

      if (text) {
        try {
          const packageInfo = JSON.parse(text);

          if (packageInfo.name) {
            applicationName = packageInfo.name;
            applicationVersion = packageInfo.version;
          }
        } catch (err) {
          // The package.json file could not be parsed
          // Use the default name
        }
      }

      return {
        applicationName,
        applicationVersion,
        applicationNameWasGenerated: true
      };
    });
  }

  /**
   * @private
   * @returns {Promise} A Promise that will never be rejected
   */
  _readPackageInfoFile(packageInfoPath) {
    return new Promise(resolve => {
      fs.readFile(path.join(packageInfoPath, 'package.json'), 'utf8', (err, data) => {
        // Swallow error
        resolve(data);
      });
    });
  }

  /**
   * @returns {String} Returns a json string with the startup message.
   * @private
   */
  _getStatusEvent() {
    const cc = this._client.controlConnection;
    const options = this._client.options;
    const state = this._client.getState();
    const connectedNodes = {};

    state.getConnectedHosts().forEach(h => {
      connectedNodes[h.address] = {
        connections: state.getOpenConnections(h),
        inFlightQueries: state.getInFlightQueries(h)
      };
    });

    const message = {
      metadata: {
        name: 'driver.status',
        insightMappingId: 'v1',
        insightType: 'EVENT',
        timestamp: Date.now(),
        tags: { language: 'nodejs' }
      },
      data: {
        clientId: options.id,
        sessionId: this._sessionId,
        controlConnection: cc.host ? cc.host.address : undefined,
        connectedNodes
      }
    };

    return JSON.stringify(message);
  }

  /**
   * Cleans any timer used internally and sets the client as closed.
   */
  shutdown() {
    if (!this._enabled) {
      return;
    }

    this._closed = true;

    if (this._firstTimeout !== null) {
      clearTimeout(this._firstTimeout);
    }

    if (this._recurrentTimeout !== null) {
      clearInterval(this._recurrentTimeout);
    }
  }
}

module.exports = InsightsClient;

function mapToObject(map) {
  const result = {};
  map.forEach((value, key) => result[key] = value);
  return result;
}

function getPolicyInfo(policy) {
  if (!policy) {
    return undefined;
  }

  const options = policy.getOptions && policy.getOptions();

  return {
    type: policy.constructor.name,
    options: (options instanceof Map) ? mapToObject(options) : utils.emptyObject
  };
}

function getConsistencyString(c) {
  if (typeof c !== 'number') {
    return undefined;
  }

  return types.consistencyToString[c];
}

function getConstructor(instance) {
  return instance ? instance.constructor.name : undefined;
}

function getExecutionProfiles(client) {
  const executionProfiles = {};

  const defaultProfile = client.profileManager.getDefault();
  setExecutionProfileProperties(client, executionProfiles, defaultProfile, defaultProfile);

  client.profileManager.getAll()
    .filter(p => p !== defaultProfile)
    .forEach(profile => setExecutionProfileProperties(client, executionProfiles, profile, defaultProfile));

  return executionProfiles;
}

function setExecutionProfileProperties(client, parent, profile, defaultProfile) {
  const output = parent[profile.name] = {};
  setExecutionProfileItem(output, profile, defaultProfile, 'readTimeout');
  setExecutionProfileItem(output, profile, defaultProfile, 'loadBalancing', getPolicyInfo);
  setExecutionProfileItem(output, profile, defaultProfile, 'retry', getPolicyInfo);
  setExecutionProfileItem(output, profile, defaultProfile, 'consistency', getConsistencyString);
  setExecutionProfileItem(output, profile, defaultProfile, 'serialConsistency', getConsistencyString);

  if (profile === defaultProfile) {
    // Speculative execution policy is included in the profiles as some drivers support
    // different spec exec policy per profile, in this case is fixed for all profiles
    output.speculativeExecution = getPolicyInfo(client.options.policies.speculativeExecution);
  }

  if (profile.graphOptions) {
    output.graphOptions = {};
    const defaultGraphOptions = defaultProfile.graphOptions || utils.emptyObject;
    setExecutionProfileItem(output.graphOptions, profile.graphOptions, defaultGraphOptions, 'language');
    setExecutionProfileItem(output.graphOptions, profile.graphOptions, defaultGraphOptions, 'name');
    setExecutionProfileItem(output.graphOptions, profile.graphOptions, defaultGraphOptions, 'readConsistency',
      getConsistencyString);
    setExecutionProfileItem(output.graphOptions, profile.graphOptions, defaultGraphOptions, 'source');
    setExecutionProfileItem(output.graphOptions, profile.graphOptions, defaultGraphOptions, 'writeConsistency',
      getConsistencyString);

    if (Object.keys(output.graphOptions).length === 0) {
      // Properties that are undefined will not be included in the JSON
      output.graphOptions = undefined;
    }
  }
}

function setExecutionProfileItem(output, profile, defaultProfile, prop, valueGetter) {
  const value = profile[prop];
  valueGetter = valueGetter || (x => x);

  if ((profile === defaultProfile && value !== undefined) || value !== defaultProfile[prop]) {
    output[prop] = valueGetter(value);
  }
}<commit_msg>Lower log severity for Insights errors<commit_after>/**
 * Copyright DataStax, Inc.
 *
 * Please see the license for details:
 * http://www.datastax.com/terms/datastax-dse-driver-license-terms
 */

'use strict';

const os = require('os');
const path = require('path');
const fs = require('fs');
const utils = require('./utils');
const types = require('./types');
const requests = require('./requests');
const ExecutionOptions = require('./execution-options').ExecutionOptions;
const packageInfo = require('../package.json');
const VersionNumber = require('./types/version-number');

let kerberosModule;

try {
  // eslint-disable-next-line
  kerberosModule = require('kerberos');
}
catch (err) {
  // Kerberos is an optional dependency
}

const minDse6Version = new VersionNumber(6, 0, 5);
const minDse51Version = new VersionNumber(5, 1, 13);
const dse600Version = new VersionNumber(6, 0, 0);
const rpc = "CALL InsightsRpc.reportInsight(?)";
const maxStatusErrorLogs = 5;

/**
 * Contains methods and functionality to send events to DSE Insights.
 */
class InsightsClient {

  /**
   * Creates a new instance of the {@link InsightsClient} using the driver {@link Client}.
   * @param {Client} client
   * @param {Object} [options]
   * @param {Number} [options.statusEventDelay]
   * @param {Function} [options.errorCallback]
   */
  constructor(client, options) {
    this._client = client;
    this._sessionId = types.Uuid.random().toString();
    this._enabled = false;
    this._closed = false;
    this._firstTimeout = null;
    this._recurrentTimeout = null;
    this._statusErrorLogs = 0;

    options = options || {};

    this._statusEventDelay = options.statusEventDelay || 300000;
    this._errorCallback = options.errorCallback || utils.noop;
  }

  /**
   * Initializes the insights client in the background by sending the startup event and scheduling status events at
   * regular intervals.
   */
  init() {
    this._enabled = this._client.options.monitorReporting.enabled && this._dseSupportsInsights();
    if (!this._enabled) {
      return;
    }

    this._sendStartupEvent()
      .then(() => {

        if (this._closed) {
          // The client was shutdown
          return;
        }

        // Send the status event the first time with a delay containing some random portion
        // Initial delay should be statusEventDelay - (0 to 10%)
        const firstDelay = Math.floor(this._statusEventDelay - 0.1 * this._statusEventDelay * Math.random());

        // Schedule the first timer
        this._firstTimeout = setTimeout(() => {

          // Send the first status event, the promise will never be rejected
          this._sendStatusEvent();

          // The following status events are sent at regular intervals
          this._recurrentTimeout = setInterval(() => this._sendStatusEvent(), this._statusEventDelay);
        }, firstDelay);
      })
      .catch(err => {

        if (this._closed) {
          // Sending failed because the Client was shutdown
          return;
        }

        // We shouldn't try to recover
        this._client.log('verbose', `Insights startup message could not be sent (${err})`, err);
        this._errorCallback(err);
      });
  }

  /**
   * Sends the startup event.
   * @returns {Promise}
   * @private
   */
  _sendStartupEvent() {
    return this._getStartupMessage()
      .then(message => new Promise((resolve, reject) => {
        const request = new requests.QueryRequest(rpc, [ message ], ExecutionOptions.empty());

        this._client.controlConnection.query(request, false, err => {
          if (!err) {
            resolve();
          } else {
            reject(err);
          }
        });
      }));
  }

  /**
   * Sends the status event.
   * @returns {Promise} A promise that is never rejected.
   * @private
   */
  _sendStatusEvent() {
    const promise = new Promise((resolve, reject) => {
      const request = new requests.QueryRequest(rpc, [ this._getStatusEvent() ], ExecutionOptions.empty());

      this._client.controlConnection.query(request, false, err => {
        if (!err) {
          resolve();
        } else {
          reject(err);
        }
      });
    });

    return promise
      .catch(err => {
        if (this._closed) {
          // Sending failed because the Client was shutdown
          return;
        }

        if (this._statusErrorLogs < maxStatusErrorLogs) {
          this._client.log('warning', `Insights status message could not be sent (${err})`, err);
          this._statusErrorLogs++;
        }

        this._errorCallback(err);
      });
  }

  /**
   * Validates the minimum server version for all nodes in the cluster.
   * @private
   */
  _dseSupportsInsights() {
    if (this._client.hosts.length === 0) {
      return false;
    }

    return this._client.hosts.values().reduce((acc, host) => {
      if (!acc) {
        return acc;
      }

      // No spread operator in Node.js 4 :/
      const versionArr = host.getDseVersion();
      const version = new VersionNumber(versionArr[0], versionArr[1], versionArr[2]);

      return version.compare(minDse6Version) >= 0 ||
        (version.compare(dse600Version) < 0 && version.compare(minDse51Version) >= 0);

    }, true);
  }

  /**
   * @returns {Promise<String>} Returns a json string with the startup message.
   * @private
   */
  _getStartupMessage() {

    const cc = this._client.controlConnection;
    const options = this._client.options;

    return this._getAppInfo(options)
      .then(appInfo => {

        const message = {
          metadata: {
            name: 'driver.startup',
            insightMappingId: 'v1',
            insightType: 'EVENT',
            timestamp: Date.now(),
            tags: { language: 'nodejs' }
          },
          data: {
            driverName: packageInfo.description,
            driverVersion: packageInfo.version,
            clientId: options.id,
            sessionId: this._sessionId,
            applicationName: appInfo.applicationName,
            applicationVersion: appInfo.applicationVersion,
            applicationNameWasGenerated: appInfo.applicationNameWasGenerated,
            contactPoints: mapToObject(cc.getResolvedContactPoints()),
            dataCenters: this._getDataCenters(),
            initialControlConnection: cc.host ? cc.host.address : undefined,
            protocolVersion: cc.protocolVersion,
            localAddress: cc.getLocalAddress(),
            hostName: os.hostname(),
            executionProfiles: getExecutionProfiles(this._client),
            poolSizeByHostDistance: {
              local: options.pooling.coreConnectionsPerHost[types.distance.local],
              remote: options.pooling.coreConnectionsPerHost[types.distance.remote]
            },
            heartbeatInterval: options.pooling.heartBeatInterval,
            compression: 'NONE',
            reconnectionPolicy: getPolicyInfo(options.policies.reconnection),
            ssl: {
              enabled: !!options.sslOptions,
              certValidation: options.sslOptions ? !!options.sslOptions.rejectUnauthorized : undefined
            },
            authProvider: {
              type: getConstructor(options.authProvider),
            },
            otherOptions: {
              coalescingThreshold: options.socketOptions.coalescingThreshold,
            },
            platformInfo: {
              os: {
                name: os.platform(),
                version: os.release(),
                arch: os.arch()
              },
              cpus: {
                length: os.cpus().length,
                model: os.cpus()[0].model
              },
              runtime: {
                node: process.versions['node'],
                v8: process.versions['v8'],
                uv: process.versions['uv'],
                openssl: process.versions['openssl'],
                kerberos: kerberosModule ? kerberosModule.version : undefined
              }
            },
            configAntiPatterns: this._getConfigAntiPatterns(),
            periodicStatusInterval: Math.floor(this._statusEventDelay / 1000)
          }
        };

        return JSON.stringify(message);
      });
  }

  _getConfigAntiPatterns() {
    const options = this._client.options;
    const result = {};

    if (options.sslOptions && !options.sslOptions.rejectUnauthorized) {
      result.sslWithoutCertValidation =
        'Client-to-node encryption is enabled but server certificate validation is disabled';
    }

    return result;
  }

  /**
   * Gets an array of data centers the driver connects to.
   * Whether the driver connects to a certain host is determined by the host distance (local and remote hosts)
   * and the pooling options (whether connection length for remote hosts is greater than 0).
   * @returns {Array}
   * @private
   */
  _getDataCenters() {
    const remoteConnectionsLength = this._client.options.pooling.coreConnectionsPerHost[types.distance.remote];
    const dataCenters = new Set();

    this._client.hosts.values().forEach(h => {
      const distance = this._client.profileManager.getDistance(h);
      if (distance === types.distance.local || (distance === types.distance.remote && remoteConnectionsLength > 0)) {
        dataCenters.add(h.datacenter);
      }
    });

    return Array.from(dataCenters);
  }

  /**
   * Tries to obtain the application name and version from
   * @param {DseClientOptions} options
   * @returns {Promise}
   * @private
   */
  _getAppInfo(options) {
    if (typeof options.applicationName === 'string') {
      return Promise.resolve({
        applicationName: options.applicationName,
        applicationVersion: options.applicationVersion,
        applicationNameWasGenerated: false
      });
    }

    let readPromise = Promise.resolve();

    if (require.main && require.main.filename) {
      const packageInfoPath = path.dirname(require.main.filename);
      readPromise = this._readPackageInfoFile(packageInfoPath);
    }

    return readPromise.then(text => {
      let applicationName = 'Default Node.js Application';
      let applicationVersion;

      if (text) {
        try {
          const packageInfo = JSON.parse(text);

          if (packageInfo.name) {
            applicationName = packageInfo.name;
            applicationVersion = packageInfo.version;
          }
        } catch (err) {
          // The package.json file could not be parsed
          // Use the default name
        }
      }

      return {
        applicationName,
        applicationVersion,
        applicationNameWasGenerated: true
      };
    });
  }

  /**
   * @private
   * @returns {Promise} A Promise that will never be rejected
   */
  _readPackageInfoFile(packageInfoPath) {
    return new Promise(resolve => {
      fs.readFile(path.join(packageInfoPath, 'package.json'), 'utf8', (err, data) => {
        // Swallow error
        resolve(data);
      });
    });
  }

  /**
   * @returns {String} Returns a json string with the startup message.
   * @private
   */
  _getStatusEvent() {
    const cc = this._client.controlConnection;
    const options = this._client.options;
    const state = this._client.getState();
    const connectedNodes = {};

    state.getConnectedHosts().forEach(h => {
      connectedNodes[h.address] = {
        connections: state.getOpenConnections(h),
        inFlightQueries: state.getInFlightQueries(h)
      };
    });

    const message = {
      metadata: {
        name: 'driver.status',
        insightMappingId: 'v1',
        insightType: 'EVENT',
        timestamp: Date.now(),
        tags: { language: 'nodejs' }
      },
      data: {
        clientId: options.id,
        sessionId: this._sessionId,
        controlConnection: cc.host ? cc.host.address : undefined,
        connectedNodes
      }
    };

    return JSON.stringify(message);
  }

  /**
   * Cleans any timer used internally and sets the client as closed.
   */
  shutdown() {
    if (!this._enabled) {
      return;
    }

    this._closed = true;

    if (this._firstTimeout !== null) {
      clearTimeout(this._firstTimeout);
    }

    if (this._recurrentTimeout !== null) {
      clearInterval(this._recurrentTimeout);
    }
  }
}

module.exports = InsightsClient;

function mapToObject(map) {
  const result = {};
  map.forEach((value, key) => result[key] = value);
  return result;
}

function getPolicyInfo(policy) {
  if (!policy) {
    return undefined;
  }

  const options = policy.getOptions && policy.getOptions();

  return {
    type: policy.constructor.name,
    options: (options instanceof Map) ? mapToObject(options) : utils.emptyObject
  };
}

function getConsistencyString(c) {
  if (typeof c !== 'number') {
    return undefined;
  }

  return types.consistencyToString[c];
}

function getConstructor(instance) {
  return instance ? instance.constructor.name : undefined;
}

function getExecutionProfiles(client) {
  const executionProfiles = {};

  const defaultProfile = client.profileManager.getDefault();
  setExecutionProfileProperties(client, executionProfiles, defaultProfile, defaultProfile);

  client.profileManager.getAll()
    .filter(p => p !== defaultProfile)
    .forEach(profile => setExecutionProfileProperties(client, executionProfiles, profile, defaultProfile));

  return executionProfiles;
}

function setExecutionProfileProperties(client, parent, profile, defaultProfile) {
  const output = parent[profile.name] = {};
  setExecutionProfileItem(output, profile, defaultProfile, 'readTimeout');
  setExecutionProfileItem(output, profile, defaultProfile, 'loadBalancing', getPolicyInfo);
  setExecutionProfileItem(output, profile, defaultProfile, 'retry', getPolicyInfo);
  setExecutionProfileItem(output, profile, defaultProfile, 'consistency', getConsistencyString);
  setExecutionProfileItem(output, profile, defaultProfile, 'serialConsistency', getConsistencyString);

  if (profile === defaultProfile) {
    // Speculative execution policy is included in the profiles as some drivers support
    // different spec exec policy per profile, in this case is fixed for all profiles
    output.speculativeExecution = getPolicyInfo(client.options.policies.speculativeExecution);
  }

  if (profile.graphOptions) {
    output.graphOptions = {};
    const defaultGraphOptions = defaultProfile.graphOptions || utils.emptyObject;
    setExecutionProfileItem(output.graphOptions, profile.graphOptions, defaultGraphOptions, 'language');
    setExecutionProfileItem(output.graphOptions, profile.graphOptions, defaultGraphOptions, 'name');
    setExecutionProfileItem(output.graphOptions, profile.graphOptions, defaultGraphOptions, 'readConsistency',
      getConsistencyString);
    setExecutionProfileItem(output.graphOptions, profile.graphOptions, defaultGraphOptions, 'source');
    setExecutionProfileItem(output.graphOptions, profile.graphOptions, defaultGraphOptions, 'writeConsistency',
      getConsistencyString);

    if (Object.keys(output.graphOptions).length === 0) {
      // Properties that are undefined will not be included in the JSON
      output.graphOptions = undefined;
    }
  }
}

function setExecutionProfileItem(output, profile, defaultProfile, prop, valueGetter) {
  const value = profile[prop];
  valueGetter = valueGetter || (x => x);

  if ((profile === defaultProfile && value !== undefined) || value !== defaultProfile[prop]) {
    output[prop] = valueGetter(value);
  }
}<|endoftext|>


<commit_before>import isArrayLike from 'lodash/isArrayLike';
import noop from 'lodash/noop';

import once from './once';
import iterator from './iterator';

export default function _filter(eachfn, coll, iteratee, callback) {
    callback = once(callback || noop);
    var truthValues = isArrayLike(coll) ? new Array(coll.length) : {};
    eachfn(coll, function (x, index, callback) {
        iteratee(x, function (err, v) {
            truthValues[index] = !!v;
            callback(err);
        });
    }, function (err) {
        if (err) return callback(err);
        var result = [];
        var nextElem = iterator(coll);
        var elem;
        while ((elem = nextElem()) !== null) {
            if (truthValues[elem.key] === true) {
                result.push(elem.value);
            }
        }
        callback(null, result);
    });
}
<commit_msg>Revert back to previous filter implementation<commit_after>import arrayMap from 'lodash/_arrayMap';
import property from 'lodash/_baseProperty';
import noop from 'lodash/noop';
import once from './once';

export default function _filter(eachfn, arr, iteratee, callback) {
    callback = once(callback || noop);
    var results = [];
    eachfn(arr, function (x, index, callback) {
        iteratee(x, function (err, v) {
            if (err) {
                callback(err);
            } else {
                if (v) {
                    results.push({index: index, value: x});
                }
                callback();
            }
        });
    }, function (err) {
        if (err) {
            callback(err);
        } else {
            callback(null, arrayMap(results.sort(function (a, b) {
                return a.index - b.index;
            }), property('value')));
        }
    });
}
<|endoftext|>


<commit_before>// TODO split osm-pbf-leaflet into separate projects
//require('osm-pbf-leaflet');
require('../node_modules/osm-pbf-leaflet/lib/OSMReader.js');
require('../node_modules/osm-pbf-leaflet/lib/leaflet-osm.js');
require('leaflet-tilelayer-vector');
require('../lib/leaflet-plugins/Permalink.js');
require('../lib/leaflet-plugins/Permalink.Layer.js');
require('../lib/Leaflet.zoomslider/src/L.Control.Zoomslider.js');
require('./L.Control.Zoomslider-patch.js');
require('../lib/Leaflet.zoomdisplay/leaflet.zoomdisplay.js');
require('./Control.Progress');
//require('./Control.ZoomInfo.js');
require('./PbfWorker.js');
require('./Path._updatePathViewport.js');

var popup = require('./popup.js');
var miniMap = require('./minimap.js');

var map;
var baseLayer;
var emptyBaseLayer;
var vectorTileLayer;
var tileDebugLayer;
var layerControl;
var visibility = 'visible';
var baseLayerActive = null;
var landuse = true;

/**
 * Changes the visibility for all features (supporting hover only)
 *
 * @param {string} 'visible' | 'hidden'
 */
var updateVisibility = function(aVisibility) {
    var container = map.getRenderer(vectorTileLayer)._container;

    visibility = aVisibility;
    if (visibility === 'hidden') {
        activateBaseLayer();
    } else {
        restoreBaseLayer();
    }

    if (L.Path.CANVAS) {
        container.style.visibility = aVisibility;
    } else {
        // does not work as expected in Chrome (v26):
        // when svg root is hidden, setting visible on child has no effect
        //map._pathRoot.setAttribute('visibility', visibility);
        var children = container.childNodes;
        for (var i = 0; i < children.length; i++) {
            setPathVisibility(children[i], aVisibility);
        }
    }
};

var activateBaseLayer = function() {
    baseLayerActive = map.hasLayer(baseLayer);
    if (!baseLayerActive) {
        map.addLayer(baseLayer);
        map.removeLayer(emptyBaseLayer);
    }
};

var restoreBaseLayer = function() {
    if (baseLayerActive !== null && !baseLayerActive) {
        map.removeLayer(baseLayer);
        map.addLayer(emptyBaseLayer);
    }
};

var setPathVisibility = function(pathEle, aVisibility) {
    if (pathEle) {
        pathEle.setAttribute('visibility', aVisibility);
    }
};

var showLanduse = function(aLanduse) {
    landuse = aLanduse;
    vectorTileLayer.redraw();
};

function init() {

    // enlarge vector clip bounds, restricted by tile bounds,
    // see Path._updatePathViewport.js
    L.Path.CLIP_PADDING = 1;

    map = L.map('map', {
        minZoom: 5,
        maxZoom: 19,
        // disable keyboard, L.Map.Keyboard._onMouseDown slow (?)
        keyboard: false,
        // disable force/kinetic dragging, not recognizable with many vectors, sometimes too fast
        inertia: false,
        // ignored by MiniMap > reset setView in minimap.js
        //reset: true,
        //animate: false
    });
    map.setView([52.4859, -1.88935], 16);
    //map.addControl(new L.Control.ZoomInfo());

    emptyBaseLayer = L.layerGroup();
    baseLayer = new L.OSM.Mapnik({
        attribution: 'map © OpenStreetMap contributors'
    });

    var colors = {
        // Leaflet colors (http://leafletjs.com/examples/geojson.html)
        polygon: '#b0de5c',
        line: '#0033ff',
        point: '#ff7800',
        hover: 'red'
    };

    var pointStyle = {
        radius: 4,
        fillColor: colors.point,
        color: "#000",
        weight: 1,
        opacity: 1,
        fillOpacity: 0.8,
        pointerEvents: 'painted'
    };
    var lineStyle = {
        weight: 4,
        opacity: 0.7,
        color: colors.line,
        pointerEvents: 'painted'
    };
    var polygonStyle = {
        weight: 2,
        color: "#999",
        opacity: 1,
        fillColor: colors.polygon,
        fillOpacity: 0.5,
        pointerEvents: 'painted'
    };
    var styles = {
        node: pointStyle,
        way: lineStyle,
        area: polygonStyle
    };

    var getHoverStyle = function(layer) {
        return {
            weight: layer.options.weight + 1,
            color: colors.hover,
            fillColor: colors.hover
        };
    };
    var bindHover = function(feature, layer) {
        layer.on('mouseover', function(evt) {
            if (!this.defaultOptions) {
                this.defaultOptions = L.Util.extend({}, this.options);
            }
            this.setStyle(getHoverStyle(this));
            setPathVisibility(this._path, 'visible');

            // lazy label binding for better performance
            // (registering less events + creating less objects on tile load)
            var popupContent = popup.getFeatureInfoHtml(this.feature);
            if (!this._label) {
                this.bindLabel(popupContent);
                this._showLabel(evt);
            }
        }, layer);
        layer.on('mouseout', function(evt) {
            // TODO resetStyle for L.OSM.DataLayer? (has styles instead of style)
            //layer.resetStyle(evt.target);
            L.setOptions(this, this.defaultOptions);
            this.setStyle(this.options);
            setPathVisibility(this._path, visibility);
        }, layer);
    };

    var bindPopup = function(feature, layer) {
        layer.on('click', function(evt) {
            // lazy popup binding for better performance
            // (registering less events + creating less objects on tile load)
            var popupContent = popup.getFeatureInfoHtml(this.feature);
            this.bindPopup(popupContent, {offset:new L.Point(0,0)});
            this._openPopup(evt);
        }, layer);
    };

    function allKeys(tags, start) {
        for (key in tags) {
            if (!(start === key.substr(0, start.length))) {
                return false;
            }
        }
        return true;
    }

    // true keeps feature, false discards
    var filter = function(feature) {
        var tags = feature.tags,
            noTags = Object.keys(tags).length === 0;
        return (map.getZoom() >= 17
            || !(noTags || tags.building || allKeys(tags, 'building') || allKeys(tags, 'roof:')
                || allKeys(tags, 'addr:') || tags.natural === 'tree' || tags.highway === 'street_lamp'
                || tags.barrier === 'fence' || tags['building:demolished'] === 'yes'))
            && (map.getZoom() >= 15 || !(feature.type === 'node') || tags.place)
            && (landuse || !(feature.area && (tags.landuse || tags.natural || tags.leisure)));
    };

    var vectorOptions = {
        filter: filter,
        styles: styles,
        onEachFeature: function(feature, layer) {
            bindHover(feature, layer);
            bindPopup(feature, layer);
            layer.on('add', function(evt) {
                setPathVisibility(evt.target._path, visibility);
            });
        }
    };

    var tileOptions = {
        // remove tiles outside viewport
        unloadInvisibleTiles: true,
        // no tile loading while panning, too slow with large vector tiles
        updateWhenIdle: true,
        // unique feature key function, for filtering duplicate features contained in multiple tiles
        unique: function(feature) {
            return feature.type.substr(0,1) + feature.id;
        },
        // factory function for creating the actual vector layer, default is L.geoJson
        // PBF parsing done in worker, use default layer here to avoid dependency in main bundle
        //layerFactory: L.osmPbf
        layerFactory: function(data, options) {
            return new L.OSM.DataLayer(data, options);
        },
        ajax: L.Request.binaryGet,
        // web worker
        workerFactory: L.pbfWorker,
        //workerFactory: L.noWorker,
        // fixed zoom level 13 for Mapsplit tiles (resize all other levels)
        serverZooms: [13],
        minZoom: 13,
        maxZoom: 19,
        attribution: 'data © OpenStreetMap contributors, '
            + 'licensed under ODbL'
    };

    vectorTileLayer = new L.TileLayer.Vector.Unclipped("tiles/{x}_{y}.pbf", tileOptions, vectorOptions);
    /*
    vectorTileLayer.on('loading', function() {
        console.time('load');
        timer.reset();
    });
    vectorTileLayer.on('load', function() {
        console.timeEnd('load');
        timer.report();
        //var tilesKeys = Object.keys(vectorTileLayer._tiles);
        //console.log('_tiles (' + tilesKeys.length + '): ' + tilesKeys);
    });
    map.on('moveend', function() {
        console.log('----- moveend -----');
    });
    */

    // hack for Path_updatePathViewport.js
    map._vectorTileLayer = vectorTileLayer;

    map.addControl(new L.Control.Progress(vectorTileLayer, {div: 'progress-container'}));
    map.addLayer(vectorTileLayer);

    var progressLayer = new L.TileLayer.Progress(vectorTileLayer).addTo(map);
    var debugLayer = new L.TileLayer.Debug(vectorTileLayer);

    layerControl = L.control.layers({
        'OSM Mapnik': baseLayer,
        'no base layer': emptyBaseLayer
    }, {
        'Vector Tiles': vectorTileLayer,
        'Loading Tiles': progressLayer,
        'Debug': debugLayer
    }).addTo(map);
    baseLayer.addTo(map);

    map.addControl(new L.Control.Permalink({
        text: 'Permalink',
        layers: layerControl
    }));

    // MiniMap (overview map)
    miniMap.init(map);

    /*
    // debug layer, from:
    // http://blog.mathieu-leplatre.info/leaflet-tiles-in-lambert-93-projection-2154.html
    tileDebugLayer = L.tileLayer.canvas();
    tileDebugLayer.drawTile = function(canvas, tilePoint, zoom) {
        var ctx = canvas.getContext('2d');
        ctx.strokeStyle = ctx.fillStyle = "red";
        ctx.rect(0,0, 256,256);
        ctx.stroke();
        ctx.fillText('' + tilePoint.x + '_' + tilePoint.y,5,10);
    };
    map.addLayer(tileDebugLayer);
    */
}

init();

exports.updateVisibility = updateVisibility;
exports.showLanduse = showLanduse;
exports.map = map;
exports.activateBaseLayer = activateBaseLayer;
exports.restoreBaseLayer = restoreBaseLayer;
<commit_msg>workaround for MiniMap vector renderer issue<commit_after>// TODO split osm-pbf-leaflet into separate projects
//require('osm-pbf-leaflet');
require('../node_modules/osm-pbf-leaflet/lib/OSMReader.js');
require('../node_modules/osm-pbf-leaflet/lib/leaflet-osm.js');
require('leaflet-tilelayer-vector');
require('../lib/leaflet-plugins/Permalink.js');
require('../lib/leaflet-plugins/Permalink.Layer.js');
require('../lib/Leaflet.zoomslider/src/L.Control.Zoomslider.js');
require('./L.Control.Zoomslider-patch.js');
require('../lib/Leaflet.zoomdisplay/leaflet.zoomdisplay.js');
require('./Control.Progress');
//require('./Control.ZoomInfo.js');
require('./PbfWorker.js');
require('./Path._updatePathViewport.js');

var popup = require('./popup.js');
var miniMap = require('./minimap.js');

var map;
var baseLayer;
var emptyBaseLayer;
var vectorTileLayer;
var tileDebugLayer;
var layerControl;
var visibility = 'visible';
var baseLayerActive = null;
var landuse = true;

/**
 * Changes the visibility for all features (supporting hover only)
 *
 * @param {string} 'visible' | 'hidden'
 */
var updateVisibility = function(aVisibility) {
    var container = map.getRenderer(vectorTileLayer)._container;

    visibility = aVisibility;
    if (visibility === 'hidden') {
        activateBaseLayer();
    } else {
        restoreBaseLayer();
    }

    if (L.Path.CANVAS) {
        container.style.visibility = aVisibility;
    } else {
        // does not work as expected in Chrome (v26):
        // when svg root is hidden, setting visible on child has no effect
        //map._pathRoot.setAttribute('visibility', visibility);
        var children = container.childNodes;
        for (var i = 0; i < children.length; i++) {
            setPathVisibility(children[i], aVisibility);
        }
    }
};

var activateBaseLayer = function() {
    baseLayerActive = map.hasLayer(baseLayer);
    if (!baseLayerActive) {
        map.addLayer(baseLayer);
        map.removeLayer(emptyBaseLayer);
    }
};

var restoreBaseLayer = function() {
    if (baseLayerActive !== null && !baseLayerActive) {
        map.removeLayer(baseLayer);
        map.addLayer(emptyBaseLayer);
    }
};

var setPathVisibility = function(pathEle, aVisibility) {
    if (pathEle) {
        pathEle.setAttribute('visibility', aVisibility);
    }
};

var showLanduse = function(aLanduse) {
    landuse = aLanduse;
    vectorTileLayer.redraw();
};

function init() {

    // enlarge vector clip bounds, restricted by tile bounds,
    // see Path._updatePathViewport.js
    L.Path.CLIP_PADDING = 1;

    map = L.map('map', {
        minZoom: 5,
        maxZoom: 19,
        // disable keyboard, L.Map.Keyboard._onMouseDown slow (?)
        keyboard: false,
        // disable force/kinetic dragging, not recognizable with many vectors, sometimes too fast
        inertia: false,
        // ignored by MiniMap > reset setView in minimap.js
        //reset: true,
        //animate: false
        // TODO workaround: map and MiniMap cannot share singleton root container
        // (L.SVG.instance in Map.getRenderer in Renderer.js)
        renderer: L.svg()
    });
    map.setView([52.4859, -1.88935], 16);
    //map.addControl(new L.Control.ZoomInfo());

    emptyBaseLayer = L.layerGroup();
    baseLayer = new L.OSM.Mapnik({
        attribution: 'map © OpenStreetMap contributors'
    });

    var colors = {
        // Leaflet colors (http://leafletjs.com/examples/geojson.html)
        polygon: '#b0de5c',
        line: '#0033ff',
        point: '#ff7800',
        hover: 'red'
    };

    var pointStyle = {
        radius: 4,
        fillColor: colors.point,
        color: "#000",
        weight: 1,
        opacity: 1,
        fillOpacity: 0.8,
        pointerEvents: 'painted'
    };
    var lineStyle = {
        weight: 4,
        opacity: 0.7,
        color: colors.line,
        pointerEvents: 'painted'
    };
    var polygonStyle = {
        weight: 2,
        color: "#999",
        opacity: 1,
        fillColor: colors.polygon,
        fillOpacity: 0.5,
        pointerEvents: 'painted'
    };
    var styles = {
        node: pointStyle,
        way: lineStyle,
        area: polygonStyle
    };

    var getHoverStyle = function(layer) {
        return {
            weight: layer.options.weight + 1,
            color: colors.hover,
            fillColor: colors.hover
        };
    };
    var bindHover = function(feature, layer) {
        layer.on('mouseover', function(evt) {
            if (!this.defaultOptions) {
                this.defaultOptions = L.Util.extend({}, this.options);
            }
            this.setStyle(getHoverStyle(this));
            setPathVisibility(this._path, 'visible');

            // lazy label binding for better performance
            // (registering less events + creating less objects on tile load)
            var popupContent = popup.getFeatureInfoHtml(this.feature);
            if (!this._label) {
                this.bindLabel(popupContent);
                this._showLabel(evt);
            }
        }, layer);
        layer.on('mouseout', function(evt) {
            // TODO resetStyle for L.OSM.DataLayer? (has styles instead of style)
            //layer.resetStyle(evt.target);
            L.setOptions(this, this.defaultOptions);
            this.setStyle(this.options);
            setPathVisibility(this._path, visibility);
        }, layer);
    };

    var bindPopup = function(feature, layer) {
        layer.on('click', function(evt) {
            // lazy popup binding for better performance
            // (registering less events + creating less objects on tile load)
            var popupContent = popup.getFeatureInfoHtml(this.feature);
            this.bindPopup(popupContent, {offset:new L.Point(0,0)});
            this._openPopup(evt);
        }, layer);
    };

    function allKeys(tags, start) {
        for (key in tags) {
            if (!(start === key.substr(0, start.length))) {
                return false;
            }
        }
        return true;
    }

    // true keeps feature, false discards
    var filter = function(feature) {
        var tags = feature.tags,
            noTags = Object.keys(tags).length === 0;
        return (map.getZoom() >= 17
            || !(noTags || tags.building || allKeys(tags, 'building') || allKeys(tags, 'roof:')
                || allKeys(tags, 'addr:') || tags.natural === 'tree' || tags.highway === 'street_lamp'
                || tags.barrier === 'fence' || tags['building:demolished'] === 'yes'))
            && (map.getZoom() >= 15 || !(feature.type === 'node') || tags.place)
            && (landuse || !(feature.area && (tags.landuse || tags.natural || tags.leisure)));
    };

    var vectorOptions = {
        filter: filter,
        styles: styles,
        onEachFeature: function(feature, layer) {
            bindHover(feature, layer);
            bindPopup(feature, layer);
            layer.on('add', function(evt) {
                setPathVisibility(evt.target._path, visibility);
            });
        }
    };

    var tileOptions = {
        // remove tiles outside viewport
        unloadInvisibleTiles: true,
        // no tile loading while panning, too slow with large vector tiles
        updateWhenIdle: true,
        // unique feature key function, for filtering duplicate features contained in multiple tiles
        unique: function(feature) {
            return feature.type.substr(0,1) + feature.id;
        },
        // factory function for creating the actual vector layer, default is L.geoJson
        // PBF parsing done in worker, use default layer here to avoid dependency in main bundle
        //layerFactory: L.osmPbf
        layerFactory: function(data, options) {
            return new L.OSM.DataLayer(data, options);
        },
        ajax: L.Request.binaryGet,
        // web worker
        workerFactory: L.pbfWorker,
        //workerFactory: L.noWorker,
        // fixed zoom level 13 for Mapsplit tiles (resize all other levels)
        serverZooms: [13],
        minZoom: 13,
        maxZoom: 19,
        attribution: 'data © OpenStreetMap contributors, '
            + 'licensed under ODbL'
    };

    vectorTileLayer = new L.TileLayer.Vector.Unclipped("tiles/{x}_{y}.pbf", tileOptions, vectorOptions);
    /*
    vectorTileLayer.on('loading', function() {
        console.time('load');
        timer.reset();
    });
    vectorTileLayer.on('load', function() {
        console.timeEnd('load');
        timer.report();
        //var tilesKeys = Object.keys(vectorTileLayer._tiles);
        //console.log('_tiles (' + tilesKeys.length + '): ' + tilesKeys);
    });
    map.on('moveend', function() {
        console.log('----- moveend -----');
    });
    */

    // hack for Path_updatePathViewport.js
    map._vectorTileLayer = vectorTileLayer;

    map.addControl(new L.Control.Progress(vectorTileLayer, {div: 'progress-container'}));
    map.addLayer(vectorTileLayer);

    var progressLayer = new L.TileLayer.Progress(vectorTileLayer).addTo(map);
    var debugLayer = new L.TileLayer.Debug(vectorTileLayer);

    layerControl = L.control.layers({
        'OSM Mapnik': baseLayer,
        'no base layer': emptyBaseLayer
    }, {
        'Vector Tiles': vectorTileLayer,
        'Loading Tiles': progressLayer,
        'Debug': debugLayer
    }).addTo(map);
    baseLayer.addTo(map);

    map.addControl(new L.Control.Permalink({
        text: 'Permalink',
        layers: layerControl
    }));

    // MiniMap (overview map)
    miniMap.init(map);

    /*
    // debug layer, from:
    // http://blog.mathieu-leplatre.info/leaflet-tiles-in-lambert-93-projection-2154.html
    tileDebugLayer = L.tileLayer.canvas();
    tileDebugLayer.drawTile = function(canvas, tilePoint, zoom) {
        var ctx = canvas.getContext('2d');
        ctx.strokeStyle = ctx.fillStyle = "red";
        ctx.rect(0,0, 256,256);
        ctx.stroke();
        ctx.fillText('' + tilePoint.x + '_' + tilePoint.y,5,10);
    };
    map.addLayer(tileDebugLayer);
    */
}

init();

exports.updateVisibility = updateVisibility;
exports.showLanduse = showLanduse;
exports.map = map;
exports.activateBaseLayer = activateBaseLayer;
exports.restoreBaseLayer = restoreBaseLayer;
<|endoftext|>


<commit_before>/**
 * Created by dligtenb on 08.05.2015.
 * Modules for background map and overlay maps
 */
// module for adding the base maps
var baseMap = (function(){
    // creates leaflet createBounds from numerical coordinates
    function createBounds(){
        var p1 = new L.LatLng(config.swiss_bounds[0], config.swiss_bounds[1]),
            p2 = new L.LatLng(config.swiss_bounds[2], config.swiss_bounds[3]);
        return L.latLngBounds(p1, p2);
    }

    // initiates the actual map with default tiles in the background, fits the map to swiss createBounds
    function createMap(defaultMap, fogLayer, peaks, stops){

        var bounds = createBounds();

        return L.map('map',{
            maxBounds:bounds,
            layers:[defaultMap, fogLayer, peaks, stops]
        }).fitBounds(bounds);
    }

    // returns a tile layer wit predefined attributions
    function createTileLayer(source, attribution){
        return L.tileLayer(source, {
            attribution: attribution,
            minZoom:config.min_zoom,
            maxZoom:config.max_zoom
        });
    }

    // createMap a predefined attribution with a given url and text for the background map copyright
    function createAttribution (url, urlText){
        return "About this map | " +
            "Blog | " +
            "Weather © MeteoGroup | " +
            "Hillshade © NASA | " +
            "Map © " + urlText + "";
    }

    // createMap background layers
    function createLayer(tileSource, attributionSource, description ){
        var attribution = createAttribution(attributionSource, description);
        return createTileLayer(tileSource, attribution);
    }

    return{
        createMap:createMap,
        createLayer:createLayer,
        createBounds:createBounds
    };

})();

// module for updating the overlay fog
var fog = (function(){

    function updateFog(fogLayer){
        var day = FORECAST_DATE.getDate(),
            // month +1 because getMonth() returns a value starting at 0
            month = FORECAST_DATE.getMonth() + 1,
            hour = FORECAST_DATE.getHours(),
            year = FORECAST_DATE.getFullYear();

        var url = config.height_at_time_url +
                '?y=' + year + '&m=' + month + '&d=' + day + '&h=' + hour + '';
        // JQuery AJAX request for getting the height data, shows error if not successful
        $.ajax({
            url:url,
            dataType:'json',
            success:function(response){
                // round the fog to 20
                var rounded_height = (20 * Math.round(response.height / 20));

                // update the displayed fogheight in the bottom left corner
                $('#height').html('Hochnebelgrenze ' + FORECAST_DATE.toLocaleString() +
                ' Uhr, ' + rounded_height +' m ü. M. (+/-100m)');

                // load fog (set new URL) if its inside the displayable range, show error if not and set empty URL
                if (rounded_height <= 2000 &&rounded_height >= 500) {
                    var url = config.fog_tiles_url + '' + rounded_height + '/{z}/{x}/{y}.png';
                    fogLayer.setUrl(url);
                }
                else if(rounded_height > 2000){
                    fogLayer.setUrl('');
                    error.showError('Nebelgrenze über der anzeigbaren Höhe!');
                }
                else{
                    fogLayer.setUrl('');
                    error.showError('Nebelgrenze unter der anzeigbaren Höhe!');
                }
            },
            error:function(){
                // don't show any fog and fogheight if error occurs
                fogLayer.setUrl('');
                $('#height').html('');
                error.showError('Fehler beim Abrufen der Nebelgrenze!');
            }
        });
    }

    return{
        updateFog:updateFog
    }

})();
<commit_msg>Shortened forecast date in the bottom left corner<commit_after>/**
 * Created by dligtenb on 08.05.2015.
 * Modules for background map and overlay maps
 */
// module for adding the base maps
var baseMap = (function(){
    // creates leaflet createBounds from numerical coordinates
    function createBounds(){
        var p1 = new L.LatLng(config.swiss_bounds[0], config.swiss_bounds[1]),
            p2 = new L.LatLng(config.swiss_bounds[2], config.swiss_bounds[3]);
        return L.latLngBounds(p1, p2);
    }

    // initiates the actual map with default tiles in the background, fits the map to swiss createBounds
    function createMap(defaultMap, fogLayer, peaks, stops){

        var bounds = createBounds();

        return L.map('map',{
            maxBounds:bounds,
            layers:[defaultMap, fogLayer, peaks, stops]
        }).fitBounds(bounds);
    }

    // returns a tile layer wit predefined attributions
    function createTileLayer(source, attribution){
        return L.tileLayer(source, {
            attribution: attribution,
            minZoom:config.min_zoom,
            maxZoom:config.max_zoom
        });
    }

    // createMap a predefined attribution with a given url and text for the background map copyright
    function createAttribution (url, urlText){
        return "About this map | " +
            "Blog | " +
            "Weather © MeteoGroup | " +
            "Hillshade © NASA | " +
            "Map © " + urlText + "";
    }

    // createMap background layers
    function createLayer(tileSource, attributionSource, description ){
        var attribution = createAttribution(attributionSource, description);
        return createTileLayer(tileSource, attribution);
    }

    return{
        createMap:createMap,
        createLayer:createLayer,
        createBounds:createBounds
    };

})();

// module for updating the overlay fog
var fog = (function(){

    function updateFog(fogLayer){
        var day = FORECAST_DATE.getDate(),
            // month +1 because getMonth() returns a value starting at 0
            month = FORECAST_DATE.getMonth() + 1,
            hour = FORECAST_DATE.getHours(),
            year = FORECAST_DATE.getFullYear();

        var url = config.height_at_time_url +
                '?y=' + year + '&m=' + month + '&d=' + day + '&h=' + hour + '';
        // JQuery AJAX request for getting the height data, shows error if not successful
        $.ajax({
            url:url,
            dataType:'json',
            success:function(response){
                // round the fog to 20
                var rounded_height = (20 * Math.round(response.height / 20));

                // update the displayed fogheight in the bottom left corner
                $('#height').html('Hochnebelgrenze ' + FORECAST_DATE.getDay() + '.' + FORECAST_DATE.getMonth() + '.' +
                    FORECAST_DATE.getFullYear() + ', ' + FORECAST_DATE.getHours() + 'h, '
                    + rounded_height +' m ü. M. (+/-100m)');

                // load fog (set new URL) if its inside the displayable range, show error if not and set empty URL
                if (rounded_height <= 2000 &&rounded_height >= 500) {
                    var url = config.fog_tiles_url + '' + rounded_height + '/{z}/{x}/{y}.png';
                    fogLayer.setUrl(url);
                }
                else if(rounded_height > 2000){
                    fogLayer.setUrl('');
                    error.showError('Nebelgrenze über der anzeigbaren Höhe!');
                }
                else{
                    fogLayer.setUrl('');
                    error.showError('Nebelgrenze unter der anzeigbaren Höhe!');
                }
            },
            error:function(){
                // don't show any fog and fogheight if error occurs
                fogLayer.setUrl('');
                $('#height').html('');
                error.showError('Fehler beim Abrufen der Nebelgrenze!');
            }
        });
    }

    return{
        updateFog:updateFog
    }

})();
<|endoftext|>


<commit_before>var feature_flags = (function () {

var exports = {};

// Helpers
var special_24_hour_people= _.contains([],
    page_params.email);
var og_zuliper_emails = [];

var iceland = page_params.domain === 'customer8.invalid';

var customer4_realms = [
  'customer4.invalid',
  'users.customer4.invalid'
];
var is_customer4 = _.contains(customer4_realms, page_params.domain);

// Enterprise-related flags
exports.do_not_share_the_love = page_params.enterprise;

// Manually-flipped debugging flags
exports.log_send_times = false;
exports.collect_send_times = false;

// Permanent realm-specific stuff:
exports.disable_message_editing = _.contains(['mit.edu'], page_params.domain);
exports.is_og_zulip_user = _.contains(og_zuliper_emails, page_params.email);
exports.twenty_four_hour_time = special_24_hour_people || iceland;

exports.mandatory_topics = _.contains([
    'customer7.invalid'
    ],
    page_params.domain
);

exports.left_side_userlist = _.contains(['customer7.invalid'], page_params.domain);
//This was set as a page_params variable already, because it's also used in
//the settings.html django template. See zerver/views/__init__.py:home(request).
exports.show_autoscroll_forever_option = page_params.show_autoscroll_forever_option;

exports.enable_new_user_app_alerts = ! _.contains(['employees.customer16.invalid'], page_params.domain);

// Still very beta:

exports.full_width = false; //page_params.staging;
exports.cleanup_before_reload = page_params.staging || is_customer4;
exports.new_bot_ui = false; // page_params.staging;

// This list must match the list of domains in enqueue_digest_emails.
exports.dont_show_digest_email_setting = _.contains(["mit.edu", "customer29.invalid", "customer20.invalid"],
                                                    page_params.domain);

exports.fade_at_stream_granularity = page_params.staging;

// Still burning in...
exports.fade_users_when_composing = true;
exports.mark_read_at_bottom = true;
exports.propagate_topic_edits = true;
exports.clicking_notification_causes_narrow = true;
exports.use_socket = true;
exports.local_echo = true;
exports.negated_search = true;

// Ready for deprecation.
exports.collapsible = false;
exports.dropbox_integration = page_params.staging;

return exports;

}());
<commit_msg>Turn on cleanup_before_reload for everyone<commit_after>var feature_flags = (function () {

var exports = {};

// Helpers
var special_24_hour_people= _.contains([],
    page_params.email);
var og_zuliper_emails = [];

var iceland = page_params.domain === 'customer8.invalid';

var customer4_realms = [
  'customer4.invalid',
  'users.customer4.invalid'
];
var is_customer4 = _.contains(customer4_realms, page_params.domain);

// Enterprise-related flags
exports.do_not_share_the_love = page_params.enterprise;

// Manually-flipped debugging flags
exports.log_send_times = false;
exports.collect_send_times = false;

// Permanent realm-specific stuff:
exports.disable_message_editing = _.contains(['mit.edu'], page_params.domain);
exports.is_og_zulip_user = _.contains(og_zuliper_emails, page_params.email);
exports.twenty_four_hour_time = special_24_hour_people || iceland;

exports.mandatory_topics = _.contains([
    'customer7.invalid'
    ],
    page_params.domain
);

exports.left_side_userlist = _.contains(['customer7.invalid'], page_params.domain);
//This was set as a page_params variable already, because it's also used in
//the settings.html django template. See zerver/views/__init__.py:home(request).
exports.show_autoscroll_forever_option = page_params.show_autoscroll_forever_option;

exports.enable_new_user_app_alerts = ! _.contains(['employees.customer16.invalid'], page_params.domain);

// Still very beta:

exports.full_width = false; //page_params.staging;
exports.cleanup_before_reload = true;
exports.new_bot_ui = false; // page_params.staging;

// This list must match the list of domains in enqueue_digest_emails.
exports.dont_show_digest_email_setting = _.contains(["mit.edu", "customer29.invalid", "customer20.invalid"],
                                                    page_params.domain);

exports.fade_at_stream_granularity = page_params.staging;

// Still burning in...
exports.fade_users_when_composing = true;
exports.mark_read_at_bottom = true;
exports.propagate_topic_edits = true;
exports.clicking_notification_causes_narrow = true;
exports.use_socket = true;
exports.local_echo = true;
exports.negated_search = true;

// Ready for deprecation.
exports.collapsible = false;
exports.dropbox_integration = page_params.staging;

return exports;

}());
<|endoftext|>


<commit_before>(function(){
    var map;
    Array.range= function(a, b, step){
        var A = [];
        A[0] = a;
        step = step || 1;
        while(a+step <= b){
            A[A.length] = a+= step;
        }
        return A;
    }
    $(document).ready(function(){
        $('.full-height').height(window.innerHeight - 45);
        window.onresize = function(event){
            resize_junk();
        }
        map = L.mapbox.map('map', 'ericvanzanten.map-7b7muw9h').setView([41.83733944214672, -87.64171600341797], 11);
        load_map();
    })

    function resize_junk(){
        $('.full-height').height(window.innerHeight - 45);
        var offset = $('#overlay-top').height() + $('#crime-title').height() + 75;
        $('.hide-overflow').height(window.innerHeight - offset);
    }

    function load_map(){
        $('.full-height').height(window.innerHeight-45);
        var then = moment().subtract('days', 8);
        var y = then.format('YYYY');
        var m = then.format('M');
        var d = then.format('D');
        var date_str = then.format('MM-DD-YYYY')
        var url = 'data/' + y + '/' + m + '/' + d + '.json';
        fetch_and_load(url, date_str);
    }

    function fetch_and_load(url, date_str){
        var tpl = new EJS({url: 'js/views/dataTemplate.ejs'});
        $('#map').spin('large')
        $.getJSON(url, function(data){
            $('#map').spin(false);
            var marker_layer = L.mapbox.markerLayer(data.geojson).addTo(map);
            marker_layer.eachLayer(function(marker){
                bind_popup(marker);
            })
            data.date = date_str;
            var html = tpl.render(data);
            $('#overlay').html(html);
            $('.filter').on('change', function(e){
                filter_markers(marker_layer);
            });
            $('.date-select').unbind('change');
            $('.date-select').on('change', function(e){
                var day = $('#day').val();
                var month = $('#month').val();
                var year = $('#year').val();
                var date_str = moment(month + ' ' +  day + ' ' + year, 'M D YYYY').format('MM-DD-YYYY');
                var url = 'data/' + year + '/' + month + '/' + day + '.json';
                map.removeLayer(marker_layer);
                fetch_and_load(url, date_str);
            });
            resize_junk();
        }).fail(function(resp){
            var error_template = new EJS({url: 'js/views/errorTemplate.ejs'});
            var error_html = error_template.render({date: date_str});
            $('#overlay').html(error_html);
            $('.date-select').unbind('change');
            $('.date-select').on('change', function(e){
                var day = $('#day').val();
                var month = $('#month').val();
                var year = $('#year').val();
                var date_str = moment(month + ' ' +  day + ' ' + year, 'M D YYYY').format('MM-DD-YYYY');
                var url = 'data/' + year + '/' + month + '/' + day + '.json';
                fetch_and_load(url, date_str);
            });
        });
    }

    function filter_markers(marker_layer){
        var on = [];
        var checkboxes = $('.filter');
        $.each(checkboxes, function(i, checkbox){
            if($(checkbox).is(':checked')){
                on.push($(checkbox).attr('name'));
            }
        });
        marker_layer.setFilter(function(f){
            return on.indexOf(f.properties['key']) !== -1;
        });
        marker_layer.eachLayer(function(marker){
            bind_popup(marker);
        })
        return false;
    }

    function bind_popup(marker){
        var crime_template = new EJS({url: 'js/views/crimeTemplate.ejs'});
        var props = marker.feature.properties;
        var pop_content = crime_template.render(props);
        marker.bindPopup(pop_content, {
            closeButton: true,
            minWidth: 320
        })
    }

})()
<commit_msg>Fixed the spinner thing<commit_after>(function(){
    var map;
    Array.range= function(a, b, step){
        var A = [];
        A[0] = a;
        step = step || 1;
        while(a+step <= b){
            A[A.length] = a+= step;
        }
        return A;
    }
    $(document).ready(function(){
        $('.full-height').height(window.innerHeight - 45);
        window.onresize = function(event){
            resize_junk();
        }
        map = L.mapbox.map('map', 'ericvanzanten.map-7b7muw9h').setView([41.83733944214672, -87.64171600341797], 11);
        load_map();
    })

    function resize_junk(){
        $('.full-height').height(window.innerHeight - 45);
        var offset = $('#overlay-top').height() + $('#crime-title').height() + 75;
        $('.hide-overflow').height(window.innerHeight - offset);
    }

    function load_map(){
        $('.full-height').height(window.innerHeight-45);
        var then = moment().subtract('days', 8);
        var y = then.format('YYYY');
        var m = then.format('M');
        var d = then.format('D');
        var date_str = then.format('MM-DD-YYYY')
        var url = 'data/' + y + '/' + m + '/' + d + '.json';
        fetch_and_load(url, date_str);
    }

    function fetch_and_load(url, date_str){
        var tpl = new EJS({url: 'js/views/dataTemplate.ejs'});
        $('#map').spin('large')
        $.getJSON(url, function(data){
            $('#map').spin(false);
            var marker_layer = L.mapbox.markerLayer(data.geojson).addTo(map);
            marker_layer.eachLayer(function(marker){
                bind_popup(marker);
            })
            data.date = date_str;
            var html = tpl.render(data);
            $('#overlay').html(html);
            $('.filter').on('change', function(e){
                filter_markers(marker_layer);
            });
            $('.date-select').unbind('change');
            $('.date-select').on('change', function(e){
                var day = $('#day').val();
                var month = $('#month').val();
                var year = $('#year').val();
                var date_str = moment(month + ' ' +  day + ' ' + year, 'M D YYYY').format('MM-DD-YYYY');
                var url = 'data/' + year + '/' + month + '/' + day + '.json';
                map.removeLayer(marker_layer);
                fetch_and_load(url, date_str);
            });
            resize_junk();
        }).fail(function(resp){
            $('#map').spin(false);
            var error_template = new EJS({url: 'js/views/errorTemplate.ejs'});
            var error_html = error_template.render({date: date_str});
            $('#overlay').html(error_html);
            $('.date-select').unbind('change');
            $('.date-select').on('change', function(e){
                var day = $('#day').val();
                var month = $('#month').val();
                var year = $('#year').val();
                var date_str = moment(month + ' ' +  day + ' ' + year, 'M D YYYY').format('MM-DD-YYYY');
                var url = 'data/' + year + '/' + month + '/' + day + '.json';
                fetch_and_load(url, date_str);
            });
        });
    }

    function filter_markers(marker_layer){
        var on = [];
        var checkboxes = $('.filter');
        $.each(checkboxes, function(i, checkbox){
            if($(checkbox).is(':checked')){
                on.push($(checkbox).attr('name'));
            }
        });
        marker_layer.setFilter(function(f){
            return on.indexOf(f.properties['key']) !== -1;
        });
        marker_layer.eachLayer(function(marker){
            bind_popup(marker);
        })
        return false;
    }

    function bind_popup(marker){
        var crime_template = new EJS({url: 'js/views/crimeTemplate.ejs'});
        var props = marker.feature.properties;
        var pop_content = crime_template.render(props);
        marker.bindPopup(pop_content, {
            closeButton: true,
            minWidth: 320
        })
    }

})()
<|endoftext|>


<commit_before>/*
zoom for image comparison in darfur project,
when zoom before (or after) image, zoom the after (or before) image also
*/

var parallelZoom = {};
parallelZoom.init = function() {
	console.log($('.img-before.x1y1').attr('src'));
}

//Fixme: we enable zoom inside initLeaderboard() as we need to wait when all the images available on dom
//later when we can modify the template.html code then no need to do this
var darfurimagerycomparison_initLeaderboardTmp = darfurimagerycomparison.initLeaderboard;
darfurimagerycomparison.initLeaderboard = function(projectId) {
	darfurimagerycomparison_initLeaderboardTmp(projectId);

	parallelZoom.init();
}

<commit_msg>add zoom code<commit_after>/*
zoom for image comparison in darfur project,
when zoom before (or after) image, zoom the after (or before) image also
*/

var parallelZoom = {};
parallelZoom.zoomBeforeAfter = function(settings) {
	var beforeImageContainer = settings.beforeImageContainer;
	var afterImageContainer = settings.afterImageContainer;

	var beforeImageSources = [], afterImageSources = [];

	//get images source THEN hide them
	extractImageSource(beforeImageSources, beforeImageContainer);
	extractImageSource(afterImageSources, afterImageContainer);

	$(beforeImageContainer).find('img').hide();
	$(afterImageContainer).find('img').hide();

	//init canvas to render images
	var html = "
" +
			'<canvas id="smallBeforeCanvas">Your browser does not support the HTML5 canvas tag.</canvas>' +
			'<canvas id="bigBeforeCanvas" style="display:none">Your browser does not support the HTML5 canvas tag.</canvas>' +
		"
";
	$(beforeImageContainer).append(html);

		//squarec-container help to keep canvas in square shape during resize
	html = "
" +
			'<canvas id="smallAfterCanvas">Your browser does not support the HTML5 canvas tag.</canvas>' +
			'<canvas id="bigAfterCanvas" style="display:none">Your browser does not support the HTML5 canvas tag.</canvas>' +
		"
";
	$(afterImageContainer).append(html);

	var beforeImages = [];
	var bigBeforeCanvas = document.getElementById("bigBeforeCanvas");
	var bigBeforeCanvasContext = bigBeforeCanvas.getContext('2d');
	var smallBeforeCanvas = document.getElementById("smallBeforeCanvas");
	var smallBeforeCanvasContext = smallBeforeCanvas.getContext('2d');

	var afterImages = [];
	var bigAfterCanvas = document.getElementById("bigAfterCanvas");
	var bigAfterCanvasContext = bigAfterCanvas.getContext('2d');
	var smallAfterCanvas = document.getElementById("smallAfterCanvas");
	var smallAfterCanvasContext = smallAfterCanvas.getContext('2d');

	var size;

	var tileRealSize = 256;//square of 256 x 256
	var tileViewSize;//tile resized version
	var zoomLevel;
	//dimension for zoom square window
	var zoomWidth;

	//the canvas contain original size images
	$("#bigBeforeCanvas").attr('width', tileRealSize * 2).attr('height', tileRealSize * 2);
	$("#bigAfterCanvas").attr('width', tileRealSize * 2).attr('height', tileRealSize * 2);

	//establish first time setting for dimension
	resize();

	$(window).on('resize', function() {
		resize();
    	drawSmallImages(smallBeforeCanvasContext, beforeImages);
    	drawSmallImages(smallAfterCanvasContext, afterImages);
	});

	init(beforeImageSources, beforeImages, smallBeforeCanvasContext, bigBeforeCanvasContext);
	init(afterImageSources, afterImages, smallAfterCanvasContext, bigAfterCanvasContext);

	if (Modernizr.touchevents) {
		handleEventOnMobile();
	} else {
		handleEventOnDesktop();
	}

	function handleEventOnMobile() {
		// tap position in before image and after image
		var beforeXY = {
			x: null,
			y: null
		},
		afterXY = {
			x: null,
			y: null
		};

		$('#smallBeforeCanvas').mousemove(function(evt){
			//reset after image interaction
			afterXY.x = afterXY.y = null;
			zoomOnMobile(evt, smallBeforeCanvas, bigBeforeCanvas, beforeImages, beforeXY);
		});
		$('#smallAfterCanvas').mousemove(function(evt){
			//reset before image interaction
			beforeXY.x = beforeXY.y = null;
			zoomOnMobile(evt, smallAfterCanvas, bigAfterCanvas, afterImages, afterXY);
		});

	}

	function zoomOnMobile(evt, smallCanvas, bigCanvas, images, previousXY) {
		if (zoomLevel < 1) {
			return;
		}
		var smallCanvasContext = smallCanvas.getContext("2d");
		var previousX = previousXY.x;
		var previousY = previousXY.y;

		var rect = smallCanvas.getBoundingClientRect();
		var x = evt.clientX - rect.left;
	    var y = evt.clientY - rect.top;

	    //tap in same square -> turn off zoom
	    if (
	    	previousX &&
	    	previousY &&
	    	(x < (previousX + zoomWidth/2)) &&
	    	(x > (previousX - zoomWidth/2)) &&
	    	(y < (previousY + zoomWidth/2)) &&
	    	(y > (previousY - zoomWidth/2))
	    ) {
	    	previousXY.x = null;
	    	previousXY.y = null;
	    	drawSmallImages(smallCanvasContext, images);
	    }
	    //zoom
	    else {
	    	previousXY.x = x;
	    	previousXY.y = y;
			zoom(smallCanvas, bigCanvas, images, x, y);
	    }
	    return previousXY;
	}

	function handleEventOnDesktop() {
		var mouseOutBefore = true, mouseOutAfter = true;
		$('#smallBeforeCanvas').mouseout(function(){
			mouseOutBefore = true;
			if (mouseOutAfter) {
				drawSmallImages(smallBeforeCanvasContext, beforeImages);
				drawSmallImages(smallAfterCanvasContext, afterImages);
			}
		});
		$('#smallAfterCanvas').mouseout(function(){
			mouseOutAfter = true;
			if (mouseOutBefore) {
				drawSmallImages(smallBeforeCanvasContext, beforeImages);
				drawSmallImages(smallAfterCanvasContext, afterImages);
			}
		});

		$('#smallBeforeCanvas').mousemove(function(evt){
			if (zoomLevel < 1) {
				return;
			}

			mouseOutBefore = false;

			var rect = smallBeforeCanvas.getBoundingClientRect();
			var x = evt.clientX - rect.left;
		    var y = evt.clientY - rect.top;

			zoom(smallBeforeCanvas, bigBeforeCanvas, beforeImages, x, y);
			zoom(smallAfterCanvas, bigAfterCanvas, afterImages, x, y);
		});

		$('#smallAfterCanvas').mousemove(function(evt){
			if (zoomLevel < 1) {
				return;
			}

			mouseOutAfter = false;

			var rect = smallAfterCanvas.getBoundingClientRect();
			var x = evt.clientX - rect.left;
		    var y = evt.clientY - rect.top;

			zoom(smallBeforeCanvas, bigBeforeCanvas, beforeImages, x, y);
			zoom(smallAfterCanvas, bigAfterCanvas, afterImages, x, y);
		});
	}

	/* calculate size of canvas, zoom ratio, zoom viewport width when resize window*/
	function resize() {
		size = $('.square-container').width();
		tileViewSize = size/2;
		zoomLevel = tileRealSize/tileViewSize;
		zoomWidth = tileViewSize;
		$("#smallBeforeCanvas").attr('width', size).attr('height', size);
		$("#smallAfterCanvas").attr('width', size).attr('height', size);
	}

	//find  in imageContainer, extract 's source to imageSources
	function extractImageSource(imageSources, imageContainer) {
		if ($(imageContainer).find('img').length == 0) {
			console.error('There is no before images');
		}
		$.each($(imageContainer).find('img'), function(index, value){
			imageSources.push($(value).attr('src'));
		});

	}

	/* zoom by draw the right part of original image on top of smaller one */
	function zoom(smallCanvas, bigCanvas, images, x, y) {
		var smallCanvasContext = smallCanvas.getContext('2d');
		var bigCanvasContext = bigCanvas.getContext('2d');

	    var viewPort = zoomWidth;
	    // var zoomLevel = 2;
	    drawSmallImages(smallCanvasContext, images);

	    var largeX1,largeY1;
	    largeX1 = x * zoomLevel - viewPort/2;
	    largeY1 = y * zoomLevel - viewPort/2;

	    var bigImageCut = bigCanvasContext.getImageData(largeX1 , largeY1, viewPort, viewPort);
	    console.log(bigImageCut);
	    var x1, y1;
	    x1 = x - viewPort/2;
	    y1 = y - viewPort/2;
	    smallCanvasContext.putImageData(
	    	bigImageCut,
	    	x1, y1
	    );

	    smallCanvasContext.strokeStyle = '#ff0';  // some color/style
		smallCanvasContext.lineWidth = 1;
	    smallCanvasContext.strokeRect(x1, y1, viewPort, viewPort);
	}

	/* combine images for before/after images, use for first load */
	function init(imageSources, images, smallContext, bigContext) {
		//create scope for promise
		function imageOnload(image) {
		    var deferred = $.Deferred();
		    image.onload = function() {
		    	deferred.resolve('a');
		    };
		    return deferred;
		}

		var promises = [];
		for ( var i = 0; i < imageSources.length; i++ ) {
		    var image = new Image();
		    image.crossOrigin = 'Anonymous';
		    promises.push(imageOnload(image));
		    image.src = imageSources[i];
		    images.push(image);
		}

		$.when(promises)
		.done(function(){
	    	drawSmallImages(smallContext, images);
	    	drawBigImages(bigContext, images);
		}).fail(function(){
			console.error("Error in loading images");
		});
	}

	function drawSmallImages(context, images) {
		context.drawImage(images[0], 0, 0, tileRealSize, tileRealSize, 0, 0, tileViewSize, tileViewSize);
		context.drawImage(images[1], 0, 0, tileRealSize, tileRealSize, tileViewSize, 0, tileViewSize, tileViewSize);
		context.drawImage(images[2], 0, 0, tileRealSize, tileRealSize, 0, tileViewSize, tileViewSize, tileViewSize);
		context.drawImage(images[3], 0, 0, tileRealSize, tileRealSize, tileViewSize, tileViewSize, tileViewSize, tileViewSize);
	}

	function drawBigImages(context, images) {
		context.drawImage(images[0], 0, 0, tileRealSize, tileRealSize, 0, 0, tileRealSize, tileRealSize);
		context.drawImage(images[1], 0, 0, tileRealSize, tileRealSize, tileRealSize, 0, tileRealSize, tileRealSize);
		context.drawImage(images[2], 0, 0, tileRealSize, tileRealSize, 0, tileRealSize, tileRealSize, tileRealSize);
		context.drawImage(images[3], 0, 0, tileRealSize, tileRealSize, tileRealSize, tileRealSize, tileRealSize, tileRealSize);
	}

}

//Fixme: we enable zoom inside initLeaderboard() as we need to wait when all the images available on dom
//later when we can modify the template.html code then no need to do this
var darfurimagerycomparison_initLeaderboardTmp = darfurimagerycomparison.initLeaderboard;
darfurimagerycomparison.initLeaderboard = function(projectId) {
	darfurimagerycomparison_initLeaderboardTmp(projectId);

	var beforeImageContainer = $('.desktop-view .img-before-div')[0];
	var afterImageContainer = $('.desktop-view .img-after-div')[0];

	parallelZoom.zoomBeforeAfter({
		beforeImageContainer: beforeImageContainer,
		afterImageContainer: afterImageContainer
	});
}<|endoftext|>


<commit_before>(function($) {
    var map = null;
    var mapReady = false;
    var settings = null;
    var mapDefaults = null;;
    var markerCluster = null;
    var checkinAfterInit = false;
    var since = null;
    var icon = null;
    var cookieName = 'ae_checkin_location';
    var openwindow = null;

    window.aeMapReady = function() {
        mapReady = true;
        aeFetchLocations();
    }

    function aeDoCheckin() {
        $('#ae_login').hide();
        $('#ae_checkin').hide();
        $('#ae_thanks').show();
        var center = map.getCenter();
        $.ajax({
            url: settings.base_url + 'ae/v1/checkin',
            method: 'POST',
            headers: {'x-wp-nonce':settings.nonce},
            data: {
                lat: center.lat(),
                lon: center.lng(),
                content: aeGetCheckinText(),
                status: 'publish',
            }
        }).done(function (response) {
            aePollLocations();
        });
    }

    // TODO: serialize the whole form
    function aeGetCheckinText() {
        return $('#ae_checkin form input[name="content"]').val();
    }

    function aeSetCheckinText(text) {
        return $('#ae_checkin form input[name="content"]').val(text || '');
    }

    window.aeSettings = function (_settings) {
        settings = _settings;
        icon = settings.marker ? new google.maps.MarkerImage(settings.marker.url,
            new google.maps.Size(settings.marker.width, settings.marker.height),
            new google.maps.Point(0, 0),
            new google.maps.Point(settings.marker.vertexX, settings.marker.vertexY)
        ) : null;
        var handleCheckin = function(evt) {
            evt.preventDefault();
            if (settings.loggedin) {
                aeDoCheckin();
            } else {
                var center = map.getCenter();
                document.cookie = cookieName + '=' + escape(JSON.stringify({
                    lat: center.lat(),
                    lng: center.lng(),
                    zoom: map.getZoom(),
                    checkinText: aeGetCheckinText(),
                })) + ';path=/'
                $('#ae_login').show();
                $('#ae_checkin').hide();
            }
        };
        $('#ae_checkin a[data-checkin]').click(handleCheckin);
        $('#ae_checkin form[data-checkin]').submit(handleCheckin);
        aeFetchLocations();
    }

    if ("geolocation" in navigator) {
        navigator.geolocation.getCurrentPosition(function(position) {
            var center = {
                lat: position.coords.latitude,
                lng: position.coords.longitude
            };
            var zoom = 15;
            if (!mapDefaults) {
                if (map) {
                    map.panTo(center);
                    map.setZoom(zoom);
                } else {
                    mapDefaults = {
                        zoom: zoom,
                        center: center
                        styles: settings.styles,
                    };
                }
            }
        });
    }

    function aePollLocations() {
        $.ajax({
            url: settings.base_url + 'ae/v1/checkin?since=' + (since || ''),
            headers: {'x-wp-nonce':settings.nonce},
        }).done(function (response) {
            since = response.since;
            aeAddLocations(response.results);
        });
    }

    function aeFetchLocations() {
        if (!settings || !mapReady) {
            return;
        }
        if (document.cookie) {
            var key = cookieName + '=';
            var cookie = document.cookie;
            var start = cookie.indexOf(key);
            if (start !== -1) {
                var end = cookie.indexOf(';', start);
                if (end === -1) {
                    end = cookie.length;
                }
                var cookieData = JSON.parse(unescape(cookie.substr(start + key.length, end)));
                mapDefaults = {
                    zoom: parseFloat(cookieData.zoom),
                    center: {lat: cookieData.lat, lng: cookieData.lng},
                    styles: settings.styles,
                };
                aeSetCheckinText(cookieData.checkinText);
                checkinAfterInit = true;
                document.cookie = cookieName + '=;expires=Thu, 01 Jan 1970 00:00:01 GMT;path=/'
            }
        }
        $.ajax({
            url: settings.base_url + 'ae/v1/checkin',
            headers: {'x-wp-nonce':settings.nonce},
        }).done(function (response) {
            since = response.since;
            aeInitMap(response.results);
        });
        setInterval(aePollLocations, 30000);
    };

    function aeAddLocations(locations) {
        markerCluster.addMarkers(locations.map(function(location) {
            var infowindow;
            if (settings.checkin_html) {
                var post_content = $('<div>').text(location.post_content || '').html();
                var html = settings.checkin_html.replace(/\[ae-checkin-text\]/g, post_content);
                if (html) {
                    infowindow  = new google.maps.InfoWindow({
                        content: html,
                    });
                }
            }
            var marker = new google.maps.Marker({
                position: location,
                icon: icon,
            });
            google.maps.event.addListener(marker, 'click', function() {
                if (infowindow) {
                    infowindow.open(map, marker);
                }
                if (openwindow) {
                    openwindow.close();
                }
                if (openwindow === infowindow) {
                    openwindow = null;
                } else {
                    openwindow = infowindow;
                }
            });
            return marker;
        }));
    }
    function aeInitMap(locations) {
        map = new google.maps.Map(document.getElementById('ae_map'), mapDefaults || {
            zoom: 4,
            center: {lat: -35.376184, lng: -63.998128},
            styles: settings.styles,
        });
        markerCluster = new MarkerClusterer(map, [], settings.cluster_options);
        aeAddLocations(locations);
        if (checkinAfterInit) {
            aeDoCheckin();
        }
    }
})(jQuery);
<commit_msg>missing comma<commit_after>(function($) {
    var map = null;
    var mapReady = false;
    var settings = null;
    var mapDefaults = null;;
    var markerCluster = null;
    var checkinAfterInit = false;
    var since = null;
    var icon = null;
    var cookieName = 'ae_checkin_location';
    var openwindow = null;

    window.aeMapReady = function() {
        mapReady = true;
        aeFetchLocations();
    }

    function aeDoCheckin() {
        $('#ae_login').hide();
        $('#ae_checkin').hide();
        $('#ae_thanks').show();
        var center = map.getCenter();
        $.ajax({
            url: settings.base_url + 'ae/v1/checkin',
            method: 'POST',
            headers: {'x-wp-nonce':settings.nonce},
            data: {
                lat: center.lat(),
                lon: center.lng(),
                content: aeGetCheckinText(),
                status: 'publish',
            }
        }).done(function (response) {
            aePollLocations();
        });
    }

    // TODO: serialize the whole form
    function aeGetCheckinText() {
        return $('#ae_checkin form input[name="content"]').val();
    }

    function aeSetCheckinText(text) {
        return $('#ae_checkin form input[name="content"]').val(text || '');
    }

    window.aeSettings = function (_settings) {
        settings = _settings;
        icon = settings.marker ? new google.maps.MarkerImage(settings.marker.url,
            new google.maps.Size(settings.marker.width, settings.marker.height),
            new google.maps.Point(0, 0),
            new google.maps.Point(settings.marker.vertexX, settings.marker.vertexY)
        ) : null;
        var handleCheckin = function(evt) {
            evt.preventDefault();
            if (settings.loggedin) {
                aeDoCheckin();
            } else {
                var center = map.getCenter();
                document.cookie = cookieName + '=' + escape(JSON.stringify({
                    lat: center.lat(),
                    lng: center.lng(),
                    zoom: map.getZoom(),
                    checkinText: aeGetCheckinText(),
                })) + ';path=/'
                $('#ae_login').show();
                $('#ae_checkin').hide();
            }
        };
        $('#ae_checkin a[data-checkin]').click(handleCheckin);
        $('#ae_checkin form[data-checkin]').submit(handleCheckin);
        aeFetchLocations();
    }

    if ("geolocation" in navigator) {
        navigator.geolocation.getCurrentPosition(function(position) {
            var center = {
                lat: position.coords.latitude,
                lng: position.coords.longitude
            };
            var zoom = 15;
            if (!mapDefaults) {
                if (map) {
                    map.panTo(center);
                    map.setZoom(zoom);
                } else {
                    mapDefaults = {
                        zoom: zoom,
                        center: center,
                        styles: settings.styles,
                    };
                }
            }
        });
    }

    function aePollLocations() {
        $.ajax({
            url: settings.base_url + 'ae/v1/checkin?since=' + (since || ''),
            headers: {'x-wp-nonce':settings.nonce},
        }).done(function (response) {
            since = response.since;
            aeAddLocations(response.results);
        });
    }

    function aeFetchLocations() {
        if (!settings || !mapReady) {
            return;
        }
        if (document.cookie) {
            var key = cookieName + '=';
            var cookie = document.cookie;
            var start = cookie.indexOf(key);
            if (start !== -1) {
                var end = cookie.indexOf(';', start);
                if (end === -1) {
                    end = cookie.length;
                }
                var cookieData = JSON.parse(unescape(cookie.substr(start + key.length, end)));
                mapDefaults = {
                    zoom: parseFloat(cookieData.zoom),
                    center: {lat: cookieData.lat, lng: cookieData.lng},
                    styles: settings.styles,
                };
                aeSetCheckinText(cookieData.checkinText);
                checkinAfterInit = true;
                document.cookie = cookieName + '=;expires=Thu, 01 Jan 1970 00:00:01 GMT;path=/'
            }
        }
        $.ajax({
            url: settings.base_url + 'ae/v1/checkin',
            headers: {'x-wp-nonce':settings.nonce},
        }).done(function (response) {
            since = response.since;
            aeInitMap(response.results);
        });
        setInterval(aePollLocations, 30000);
    };

    function aeAddLocations(locations) {
        markerCluster.addMarkers(locations.map(function(location) {
            var infowindow;
            if (settings.checkin_html) {
                var post_content = $('<div>').text(location.post_content || '').html();
                var html = settings.checkin_html.replace(/\[ae-checkin-text\]/g, post_content);
                if (html) {
                    infowindow  = new google.maps.InfoWindow({
                        content: html,
                    });
                }
            }
            var marker = new google.maps.Marker({
                position: location,
                icon: icon,
            });
            google.maps.event.addListener(marker, 'click', function() {
                if (infowindow) {
                    infowindow.open(map, marker);
                }
                if (openwindow) {
                    openwindow.close();
                }
                if (openwindow === infowindow) {
                    openwindow = null;
                } else {
                    openwindow = infowindow;
                }
            });
            return marker;
        }));
    }
    function aeInitMap(locations) {
        map = new google.maps.Map(document.getElementById('ae_map'), mapDefaults || {
            zoom: 4,
            center: {lat: -35.376184, lng: -63.998128},
            styles: settings.styles,
        });
        markerCluster = new MarkerClusterer(map, [], settings.cluster_options);
        aeAddLocations(locations);
        if (checkinAfterInit) {
            aeDoCheckin();
        }
    }
})(jQuery);
<|endoftext|>