<commit_before>var argscheck = require('cordova/argscheck'),
    exec = require('cordova/exec');

var admob = module.exports;

/**
 * This enum represents appfeel-cordova-admob plugin events
 */
admob.events = {
  onAdLoaded : "appfeel.cordova.admob.onAdLoaded",
  onAdFailedToLoad : "appfeel.cordova.admob.onAdFailedToLoad",
  onAdOpened : "appfeel.cordova.admob.onAdOpened",
  onAdLeftApplication : "appfeel.cordova.admob.onAdLeftApplication",
  onAdClosed : "appfeel.cordova.admob.onAdClosed",
  onInAppPurchaseRequested : "appfeel.cordova.admob.onInAppPurchaseRequested",
};

/**
 * This enum represents AdMob's supported ad sizes.  Use one of these
 * constants as the adSize when calling createBannerView.
 * @const
 */
admob.AD_SIZE = {
  BANNER: 'BANNER',
  IAB_MRECT: 'IAB_MRECT',
  IAB_BANNER: 'IAB_BANNER',
  IAB_LEADERBOARD: 'IAB_LEADERBOARD',
  SMART_BANNER: 'SMART_BANNER'
};

admob.options = {
  publisherId : "ca-app-pub-8440343014846849/3119840614",
  interstitialId : "ca-app-pub-8440343014846849/4596573817",
  adSize : admob.AD_SIZE.SMART_BANNER,
  bannerAtTop : false,
  overlap : false,
  offsetStatusBar : false,
  isTesting : false,
  adExtras : {},
  autoShowBanner : true,
  autoShowInterstitial: false
};

/**
 * Initialize appfeel-cordova-admob plugin with options:
 * @param {!Object}    options         AdMob options (use admob.options as template)
 * @param {function()} successCallback Callback on success
 * @param {function()} failureCallback Callback on fail
 */
admob.setOptions = function (options, successCallback, failureCallback) {
  if (typeof options === 'function') {
    failureCallback = successCallback;
    successCallback = options;
    options = undefined;
  }

  options = options || admob.DEFAULT_OPTIONS;

  if (typeof options === 'object' && typeof options.publisherId === 'string' && options.publisherId.length > 0) {
    cordova.exec(successCallback, failureCallback, 'AdMobAds', 'setOptions', [options]);

  } else {
    if (typeof failureCallback === 'function') {
      failureCallback('options.publisherId should be specified.')
    }
  }
};

/**
 * Creates a new AdMob banner view.
 *
 * @param {!Object}    options         The options used to create a banner. (use admob.options as template)
 * @param {function()} successCallback The function to call if the banner was created successfully.
 * @param {function()} failureCallback The function to call if create banner  was unsuccessful.
 */
admob.createBannerView = function (options, successCallback, failureCallback) {
  options = options || {};
  cordova.exec(successCallback, failureCallback, 'AdMobAds', 'createBannerView', [ options ]);
};

/*
 * Show or hide Ad.
 *
 * @param {boolean} show true to show, false to hide.
 * @param {function()} successCallback The function to call if the ad was shown successfully.
 * @param {function()} failureCallback The function to call if the ad failed to be shown.
 */
admob.showBannerAd = function (show, successCallback, failureCallback) {
  if (show === undefined) {
    show = true;
  }
  cordova.exec(successCallback, failureCallback, 'AdMobAds', 'showBannerAd', [ show ]);
};

/**
 * Hides and destroys the banner view. CreateBanner should be called if new ads were to be shown.
 * @param {function()} successCallback The function to call if the view was destroyed successfully.
 * @param {function()} failureCallback The function to call if failed to destroy view.
 */
admob.destroyBannerView = function (successCallback, failureCallback) {
  cordova.exec(successCallback, failureCallback, 'AdMobAds', 'destroyBannerView', []);
};

/**
 * Request an AdMob interstitial ad.
 *
 * @param {!Object}    options         The options used to request an ad. (use admob.options as template)
 * @param {function()} successCallback The function to call if an ad was requested successfully.
 * @param {function()} failureCallback The function to call if an ad failed to be requested.
 */
admob.requestInterstitialAd = function (options, successCallback, failureCallback) {
  options = options || {};
  cordova.exec(successCallback, failureCallback, 'AdMobAds', 'requestInterstitialAd', [ options ]);
};

/**
 * Shows an interstitial ad. This function should be called when onAdLoaded occurred.
 *
 * @param {function()} successCallback The function to call if the ad was shown successfully.
 * @param {function()} failureCallback The function to call if the ad failed to be shown.
 */
admob.showInterstitialAd = function (successCallback, failureCallback) {
  cordova.exec(successCallback, failureCallback, 'AdMobAds', 'showInterstitialAd', []);
};

/**
 * Records a resolution after an inAppPurchase.
 *
 * @param {Integer}    purchaseId      The id of the purchase.
 * @param {Integer}    resolution      The resolution code.
 * @param {function()} successCallback The function to call if the ad was shown successfully.
 * @param {function()} successCallback The function to call if the ad was shown successfully.
 * @param {function()} failureCallback The function to call if the ad failed to be shown.
 */
admob.recordResolution = function (purchaseId, resolution, successCallback, failureCallback) {
  if (purchaseId === undefined || resolution === undefined) {
    if (typeof failureCallback === 'function') {
      failureCallback('purchaseId and resolution should be specified.')
    }
  }
  cordova.exec(successCallback, failureCallback, 'AdMobAds', 'recordResolution', [ purchaseId, resolution ]);
};

/**
 * Records a resolution after an inAppPurchase.
 *
 * @param {Integer}    purchaseId           The id of the purchase.
 * @param {Integer}    billingResponseCode  The resolution code.
 * @param {function()} successCallback      The function to call if the ad was shown successfully.
 * @param {function()} successCallback      The function to call if the ad was shown successfully.
 * @param {function()} failureCallback      The function to call if the ad failed to be shown.
 */
admob.recordPlayBillingResolution = function (purchaseId, billingResponseCode, successCallback, failureCallback) {
  if (purchaseId === undefined || billingResponseCode === undefined) {
    if (typeof failureCallback === 'function') {
      failureCallback('purchaseId and billingResponseCode should be specified.')
    }
  }
  cordova.exec(successCallback, failureCallback, 'AdMobAds', 'recordResolution', [ purchaseId, billingResponseCode ]);
};
<commit_msg>Added autoShowInterstitial and autoShowBanner<commit_after>var argscheck = require('cordova/argscheck'),
    exec = require('cordova/exec');

var admob = module.exports;

/**
 * This enum represents appfeel-cordova-admob plugin events
 */
admob.events = {
  onAdLoaded : "appfeel.cordova.admob.onAdLoaded",
  onAdFailedToLoad : "appfeel.cordova.admob.onAdFailedToLoad",
  onAdOpened : "appfeel.cordova.admob.onAdOpened",
  onAdLeftApplication : "appfeel.cordova.admob.onAdLeftApplication",
  onAdClosed : "appfeel.cordova.admob.onAdClosed",
  onInAppPurchaseRequested : "appfeel.cordova.admob.onInAppPurchaseRequested",
};

/**
 * This enum represents AdMob's supported ad sizes.  Use one of these
 * constants as the adSize when calling createBannerView.
 * @const
 */
admob.AD_SIZE = {
  BANNER: 'BANNER',
  IAB_MRECT: 'IAB_MRECT',
  IAB_BANNER: 'IAB_BANNER',
  IAB_LEADERBOARD: 'IAB_LEADERBOARD',
  SMART_BANNER: 'SMART_BANNER'
};

admob.options = {
  publisherId : "ca-app-pub-8440343014846849/3119840614",
  interstitialId : "ca-app-pub-8440343014846849/4596573817",
  adSize : admob.AD_SIZE.SMART_BANNER,
  bannerAtTop : false,
  overlap : false,
  offsetStatusBar : false,
  isTesting : false,
  adExtras : {},
  autoShowBanner : true,
  autoShowInterstitial: true
};

/**
 * Initialize appfeel-cordova-admob plugin with options:
 * @param {!Object}    options         AdMob options (use admob.options as template)
 * @param {function()} successCallback Callback on success
 * @param {function()} failureCallback Callback on fail
 */
admob.setOptions = function (options, successCallback, failureCallback) {
  if (typeof options === 'function') {
    failureCallback = successCallback;
    successCallback = options;
    options = undefined;
  }

  options = options || admob.DEFAULT_OPTIONS;

  if (typeof options === 'object' && typeof options.publisherId === 'string' && options.publisherId.length > 0) {
    cordova.exec(successCallback, failureCallback, 'AdMobAds', 'setOptions', [options]);

  } else {
    if (typeof failureCallback === 'function') {
      failureCallback('options.publisherId should be specified.')
    }
  }
};

/**
 * Creates a new AdMob banner view.
 *
 * @param {!Object}    options         The options used to create a banner. (use admob.options as template)
 * @param {function()} successCallback The function to call if the banner was created successfully.
 * @param {function()} failureCallback The function to call if create banner  was unsuccessful.
 */
admob.createBannerView = function (options, successCallback, failureCallback) {
  options = options || {};
  cordova.exec(successCallback, failureCallback, 'AdMobAds', 'createBannerView', [ options ]);
};

/*
 * Show or hide Ad.
 *
 * @param {boolean} show true to show, false to hide.
 * @param {function()} successCallback The function to call if the ad was shown successfully.
 * @param {function()} failureCallback The function to call if the ad failed to be shown.
 */
admob.showBannerAd = function (show, successCallback, failureCallback) {
  if (show === undefined) {
    show = true;
  }
  cordova.exec(successCallback, failureCallback, 'AdMobAds', 'showBannerAd', [ show ]);
};

/**
 * Hides and destroys the banner view. CreateBanner should be called if new ads were to be shown.
 * @param {function()} successCallback The function to call if the view was destroyed successfully.
 * @param {function()} failureCallback The function to call if failed to destroy view.
 */
admob.destroyBannerView = function (successCallback, failureCallback) {
  cordova.exec(successCallback, failureCallback, 'AdMobAds', 'destroyBannerView', []);
};

/**
 * Request an AdMob interstitial ad.
 *
 * @param {!Object}    options         The options used to request an ad. (use admob.options as template)
 * @param {function()} successCallback The function to call if an ad was requested successfully.
 * @param {function()} failureCallback The function to call if an ad failed to be requested.
 */
admob.requestInterstitialAd = function (options, successCallback, failureCallback) {
  options = options || {};
  cordova.exec(successCallback, failureCallback, 'AdMobAds', 'requestInterstitialAd', [ options ]);
};

/**
 * Shows an interstitial ad. This function should be called when onAdLoaded occurred.
 *
 * @param {function()} successCallback The function to call if the ad was shown successfully.
 * @param {function()} failureCallback The function to call if the ad failed to be shown.
 */
admob.showInterstitialAd = function (successCallback, failureCallback) {
  cordova.exec(successCallback, failureCallback, 'AdMobAds', 'showInterstitialAd', []);
};

/**
 * Records a resolution after an inAppPurchase.
 *
 * @param {Integer}    purchaseId      The id of the purchase.
 * @param {Integer}    resolution      The resolution code.
 * @param {function()} successCallback The function to call if the ad was shown successfully.
 * @param {function()} successCallback The function to call if the ad was shown successfully.
 * @param {function()} failureCallback The function to call if the ad failed to be shown.
 */
admob.recordResolution = function (purchaseId, resolution, successCallback, failureCallback) {
  if (purchaseId === undefined || resolution === undefined) {
    if (typeof failureCallback === 'function') {
      failureCallback('purchaseId and resolution should be specified.')
    }
  }
  cordova.exec(successCallback, failureCallback, 'AdMobAds', 'recordResolution', [ purchaseId, resolution ]);
};

/**
 * Records a resolution after an inAppPurchase.
 *
 * @param {Integer}    purchaseId           The id of the purchase.
 * @param {Integer}    billingResponseCode  The resolution code.
 * @param {function()} successCallback      The function to call if the ad was shown successfully.
 * @param {function()} successCallback      The function to call if the ad was shown successfully.
 * @param {function()} failureCallback      The function to call if the ad failed to be shown.
 */
admob.recordPlayBillingResolution = function (purchaseId, billingResponseCode, successCallback, failureCallback) {
  if (purchaseId === undefined || billingResponseCode === undefined) {
    if (typeof failureCallback === 'function') {
      failureCallback('purchaseId and billingResponseCode should be specified.')
    }
  }
  cordova.exec(successCallback, failureCallback, 'AdMobAds', 'recordResolution', [ purchaseId, billingResponseCode ]);
};
<|endoftext|>


<commit_before>/**
 * @typedef {Object} State~Unit
 * @property {?string} factionType - One of the FACTION_TYPES
 * @property {State~Placement} placement
 * @property {?State~Location} location
 * @property {State~Location[]} destinations
 * @property {number} destinationIndex
 *   The index of the currently active element in destinations. 0 ~ (destinations.length - 1)
 * @property {number} movingSpeed - 1.0=2px/1tick
 */

/**
 * @typedef {Object} State~Ally
 * @property {string} factionType - = FACTION_TYPES.ALLY
 * @description Based on the {@link State~Unit}
 */

/**
 * @typedef {Object} State~Enemy
 * @property {string} factionType - = FACTION_TYPES.ENEMY
 * @description Based on the {@link State~Unit}
 */

/** @module */
const uuidV4 = require('uuid/v4');

const { FACTION_TYPES, PARAMETERS } = require('../immutable/constants');
const { JOB_IDS, jobs } = require('../immutable/jobs');
const { createNewPlacementState } = require('./placement');
const { performPseudoVectorAddition } = require('./location');

const createNewUnitState = () => {
  const maxHp = PARAMETERS.MIN_MAX_HP;

  return {
    uid: uuidV4(),
    factionType: null,
    placement: createNewPlacementState(),
    jobId: JOB_IDS.NONE,
    location: null,
    destinations: [],
    destinationIndex: 0,
    maxHp,
    hp: maxHp,
    attackPower: 0,
    defensePower: 0,
    mattackPower: 0,
    mdefensePower: 0,
    movingSpeed: 0,
  };
};

//
// CAUTION: It should not access the `placement` property in this module!
//          The `placement` is a relation to boards.
//          Now units refers to boards, but it may be reversed.
//          Or it may be a different schema just by placing information.
//          Place methods into the `complex-apis` if necessary.
//

const isAlly = (unit) => unit.factionType === FACTION_TYPES.ALLY;

const getJob = (unit) => {
  return jobs[unit.jobId];
};

const getIconId = (unit) => {
  return getJob(unit).iconId;
};

const canSortieAsAlly = (ally) => {
  if (!isAlly(ally)) {
    throw new Error(`It is not a ally`);
  }
  return true;
};

const canRetreatAsAlly = (ally) => {
  if (!isAlly(ally)) {
    throw new Error(`It is not a ally`);
  }
  return true;
};

/**
 * Calculate the movement results for next one tick
 * @param {State~Unit} unit
 * @return {{ location, destinationIndex }}
 */
const calculateMovementResults = (unit) => {
  if (unit.destinations.length === 0) {
    throw new Error(`This unit should not move`);
  }

  // Movement is already finished
  if (unit.destinationIndex > unit.destinations.length - 1) {
    return {
      location: unit.location,
      destinationIndex: unit.destinationIndex,
    };
  }

  const currentDestination = unit.destinations[unit.destinationIndex];

  let newLocation;
  if (unit.location) {
    // TODO: Calculate moving speed
    newLocation = performPseudoVectorAddition(unit.location, currentDestination, 2);
  // The first movement means that the unit is placed on the board
  } else {
    newLocation = currentDestination;
  }

  const newDestinationIndex = unit.destinationIndex +
    (newLocation[0] === currentDestination[0] && newLocation[1] === currentDestination[1] ? 1 : 0);

  return {
    location: newLocation,
    destinationIndex: newDestinationIndex,
  };
};

module.exports = {
  calculateMovementResults,
  canRetreatAsAlly,
  canSortieAsAlly,
  createNewUnitState,
  isAlly,
  getIconId,
};
<commit_msg>Add some parameters<commit_after>/**
 * @typedef {Object} State~Unit
 * @property {?string} factionType - One of the FACTION_TYPES
 * @property {State~Placement} placement
 * @property {?State~Location} location
 * @property {State~Location[]} destinations
 * @property {number} destinationIndex
 *   The index of the currently active element in destinations. 0 ~ (destinations.length - 1)
 * @property {number} movingSpeed - 1.0=2px/1tick
 * @property {number} attackCharge - A integer >= 0
 * @property {number} attackChargePower - A integer >= 0
 */

/**
 * @typedef {Object} State~Ally
 * @property {string} factionType - = FACTION_TYPES.ALLY
 * @description Based on the {@link State~Unit}
 */

/**
 * @typedef {Object} State~Enemy
 * @property {string} factionType - = FACTION_TYPES.ENEMY
 * @description Based on the {@link State~Unit}
 */

/** @module */
const uuidV4 = require('uuid/v4');

const { FACTION_TYPES, PARAMETERS } = require('../immutable/constants');
const { JOB_IDS, jobs } = require('../immutable/jobs');
const { createNewPlacementState } = require('./placement');
const { performPseudoVectorAddition } = require('./location');

const createNewUnitState = () => {
  const maxHp = PARAMETERS.MIN_MAX_HP;

  return {
    uid: uuidV4(),
    factionType: null,
    placement: createNewPlacementState(),
    jobId: JOB_IDS.NONE,
    location: null,
    destinations: [],
    destinationIndex: 0,
    maxHp,
    hp: maxHp,
    movingSpeed: 0,
    attackCharge: 0,
    attackChargePower: 0,
    attackPower: 0,
    defensePower: 0,
    mattackPower: 0,
    mdefensePower: 0,
  };
};

//
// CAUTION: It should not access the `placement` property in this module!
//          The `placement` is a relation to boards.
//          Now units refers to boards, but it may be reversed.
//          Or it may be a different schema just by placing information.
//          Place methods into the `complex-apis` if necessary.
//

const isAlly = (unit) => unit.factionType === FACTION_TYPES.ALLY;

const getJob = (unit) => {
  return jobs[unit.jobId];
};

const getIconId = (unit) => {
  return getJob(unit).iconId;
};

const canSortieAsAlly = (ally) => {
  if (!isAlly(ally)) {
    throw new Error(`It is not a ally`);
  }
  return true;
};

const canRetreatAsAlly = (ally) => {
  if (!isAlly(ally)) {
    throw new Error(`It is not a ally`);
  }
  return true;
};

/**
 * Calculate the movement results for next one tick
 * @param {State~Unit} unit
 * @return {{ location, destinationIndex }}
 */
const calculateMovementResults = (unit) => {
  if (unit.destinations.length === 0) {
    throw new Error(`This unit should not move`);
  }

  // Movement is already finished
  if (unit.destinationIndex > unit.destinations.length - 1) {
    return {
      location: unit.location,
      destinationIndex: unit.destinationIndex,
    };
  }

  const currentDestination = unit.destinations[unit.destinationIndex];

  let newLocation;
  if (unit.location) {
    // TODO: Calculate moving speed
    newLocation = performPseudoVectorAddition(unit.location, currentDestination, 2);
  // The first movement means that the unit is placed on the board
  } else {
    newLocation = currentDestination;
  }

  const newDestinationIndex = unit.destinationIndex +
    (newLocation[0] === currentDestination[0] && newLocation[1] === currentDestination[1] ? 1 : 0);

  return {
    location: newLocation,
    destinationIndex: newDestinationIndex,
  };
};

module.exports = {
  calculateMovementResults,
  canRetreatAsAlly,
  canSortieAsAlly,
  createNewUnitState,
  isAlly,
  getIconId,
};
<|endoftext|>


<commit_before>var wxs3 = wxs3 || {};

(function () {
    'use strict';

    //check WebGL
    if (!window.WebGLRenderingContext) {
        // the browser doesn't even know what WebGL is
        window.location = "http://get.webgl.org";
    }

    // extraction for URL parameters
    function getQueryVariable(variable) {
        var pair,
            i,
            vars,
            query;
        console.log("getQueryVariable :" + variable);

        query = window.location.search.substring(1);
        vars = query.split("&");
        for (i = 0; i < vars.length; i++) {
            pair = vars[i].split("=");
            if (pair[0].toUpperCase() === variable) {
                return pair[1];
            }
        }
        return false;
    }

    var Wxs3 = function (layers, dim) {

        this.dim = dim;
        this.camera = null;
        this.scene = null;
        this.renderer = null;
        this.controls = null;

        var cameraHeight, fov, proportionAverage;

        if (dim.metersWidth > dim.metersHeight) {
            dim.demWidth = parseInt((dim.metersWidth / dim.metersHeight) * dim.demWidth, 10);
        } else if (dim.metersWidth < dim.metersHeight) {
            dim.demHeight = parseInt((dim.metersHeight / dim.metersWidth) * dim.demHeight, 10);
        }

        dim.proportionWidth = dim.metersWidth / dim.demWidth; // mapunits between vertexes in x-dimention
        dim.proportionHeight = dim.metersHeight / dim.demHeight; // mapunits between vertexes in y-dimention
        proportionAverage = ((dim.proportionWidth + dim.proportionHeight) / 2); // average mapunits between vertexes

        if (dim.zInv) {
            proportionAverage *= -1;
        }
        if (dim.zMult) {
            dim.zMult = proportionAverage / dim.zMult;
        } else {
            dim.zMult = proportionAverage;
        }

        if (getQueryVariable("WMSFORMATMODE")) {
            dim.wmsFormatMode = '; mode=' + getQueryVariable("WMSFORMATMODE");
        }

        // We've defined some standardlayers for the default wms-service in topo2.layers.js. Overwrite them if defined in url.
        this.wmsLayers = getQueryVariable("LAYERS") || layers;

        this.renderer = new THREE.WebGLRenderer();
        this.renderer.setSize(dim.width, dim.height);

        this.scene = new THREE.Scene();
        this.scene.add(new THREE.AmbientLight(0xeeeeee));

        fov = 45;
        this.camera = new THREE.PerspectiveCamera(fov, dim.width / dim.height, 0.1, 20000);
        // Some trig to find height for camera
        cameraHeight = getQueryVariable("Z") || (dim.metersHeight / 2) / Math.tan((fov / 2) * Math.PI / 180);
        // Place camera in middle of bbox
        this.camera.position.set((dim.minx + dim.maxx) / 2, (dim.miny + dim.maxy) / 2, cameraHeight);
        this.controls = new THREE.TrackballControls(this.camera);
        // Point camera directly down
        this.controls.target = new THREE.Vector3((dim.minx + dim.maxx) / 2, (dim.miny + dim.maxy) / 2, 0);

        // Generate tiles and boundingboxes
        this.bbox2tiles(dim.minx, dim.miny, dim.maxx, dim.maxy);
        document.getElementById('webgl').appendChild(this.renderer.domElement);
    };

    Wxs3.prototype.render = function () {
        this.controls.update();
        requestAnimationFrame(this.render.bind(this));
        this.renderer.render(this.scene, this.camera);
    };

    Wxs3.prototype.bbox2tiles = function (minx, miny, maxx, maxy) {
        //TODO: generic tilematrix-parsing
        // Proof of concept with 2 subdivision in each dimention:
        //0,0
        this.addTile('x0_y0', minx, miny, (minx + maxx) / 2, (miny + maxy) / 2);
        //1,0
        this.addTile('x1_y0', (minx + maxx) / 2, miny, maxx, (miny + maxy) / 2);
        //0,1
        this.addTile('x0_y1', minx, (miny + maxy) / 2, (minx + maxx) / 2, maxy);
        //1,1
        this.addTile('x1_y1', (minx + maxx) / 2, (miny + maxy) / 2, maxx, maxy);
    };

    Wxs3.prototype.addTile = function (tileNr, minx, miny, maxx, maxy) {

        var bboxWCS = [
            parseInt(minx, 10),
            parseInt(miny - this.dim.proportionHeight, 10),
            parseInt(maxx + this.dim.proportionWidth, 10),
            parseInt(maxy, 10)
        ].join(',');

        var url = 'http://openwms.statkart.no/skwms1/wcs.dtm?' +
            'SERVICE=WCS' +
            '&VERSION=1.0.0' +
            '&REQUEST=GetCoverage' +
            '&FORMAT=XYZ' +
            '&COVERAGE=' + this.dim.coverage +
            '&bbox=' + bboxWCS +
            '&CRS=' + this.dim.crs +
            '&RESPONSE_CRS=' + this.dim.crs +
            '&WIDTH=' + parseInt(this.dim.demWidth, 10) +
            '&HEIGHT=' + parseInt(this.dim.demHeight, 10);

        var demTileRequest = new XMLHttpRequest();
        demTileRequest.open('GET', url, true);

        var that = this;
        demTileRequest.onreadystatechange = function () {
            if (this.readyState === 4) {
                that.demTileLoaded(tileNr, minx, miny, maxx, maxy, this.responseText);
            }
        };
        demTileRequest.send();
    };

    Wxs3.prototype.demTileLoaded = function (tileNr, minx, miny, maxx, maxy, responseText) {

        var minxWMS, minyWMS, maxxWMS, maxyWMS;
        var geometry = new THREE.PlaneGeometry(
            maxx - minx,
            maxy - miny,
            (this.dim.demWidth - 1),
            (this.dim.demHeight - 1)
        );

        var i, l;
        var lines = responseText.split("\n");
        for (i = 0, l = geometry.vertices.length; i < l; i++) {
            geometry.vertices[i].x = lines[i].split(' ')[0];
            geometry.vertices[i].y = lines[i].split(' ')[1];
            geometry.vertices[i].z = lines[i].split(' ')[2];
            if (i === 0) {
                minxWMS = geometry.vertices[i].x;
                maxyWMS = geometry.vertices[i].y;
            }
            if (i === l - 1) {
                maxxWMS = geometry.vertices[i].x;
                minyWMS = geometry.vertices[i].y;
            }
        }
        var bboxWMS = [minxWMS, minyWMS, maxxWMS, maxyWMS].join(',');

        var plane = new THREE.Mesh(
            geometry,
            this.createMaterial(bboxWMS, tileNr)
        );
        plane.name = 'tile_' + tileNr;
        this.scene.add(plane);

        this.dim.tilesFinished += 1;
        this.render();

        console.log('rendering bbox ' + bboxWMS);
        console.log('nr of finished geom: ' + this.dim.tilesFinished);
    };

    Wxs3.prototype.createMaterial = function (bboxWMS, tileNr) {
        var material = new THREE.MeshPhongMaterial(
            {
                map: THREE.ImageUtils.loadTexture(
                    this.dim.wms + '?' +
                        'service=wms' +
                        '&version=1.3.0' +
                        '&request=getmap' +
                        '&crs=' + this.dim.crs +
                        '&srs=' + this.dim.crs +
                        '&WIDTH=' + this.dim.demWidth * this.dim.wmsMult +
                        '&HEIGHT=' + this.dim.demHeight * this.dim.wmsMult +
                        '&bbox=' + bboxWMS +
                        '&layers=' + this.wmsLayers +
                        '&format=' + this.dim.wmsFormat + this.dim.wmsFormatMode,
                    new THREE.UVMapping()
                )
            }
        );
        material.name = 'material_' + tileNr;
        return material;
    };

    var dim = {
        width: window.innerWidth,
        height: window.innerHeight,
        demWidth: getQueryVariable("WIDTH") || 100,
        demHeight: getQueryVariable("HEIGHT") || 100,
        bbox: getQueryVariable("BBOX") || '161244,6831251,171526,6837409',
        metersWidth: 0,
        metersHeight: 0,
        minx: 0,
        maxx: 0,
        miny: 0,
        maxy: 0,
        tilesFinished: 0,
        tilesTotal: 4,
        init: function () {
            this.metersWidth = this.bbox.split(',')[2] - this.bbox.split(',')[0];
            this.metersHeight = this.bbox.split(',')[3] - this.bbox.split(',')[1];
            this.minx = parseInt(this.bbox.split(',')[0], 10);
            this.maxx = parseInt(this.bbox.split(',')[2], 10);
            this.miny = parseInt(this.bbox.split(',')[1], 10);
            this.maxy = parseInt(this.bbox.split(',')[3], 10);
            return this;
        },
        crs: getQueryVariable("CRS") || getQueryVariable("SRS") || 'EPSG:32633',
        coverage: getQueryVariable("COVERAGE") || 'land_utm33_10m',
        wms: getQueryVariable("WMS") || 'http://openwms.statkart.no/skwms1/wms.topo2',
        wmsMult: getQueryVariable("WMSMULT") || 5,
        wmsFormat: getQueryVariable("WMSFORMAT") || "image/png",
        wmsFormatMode: "",
        zInv: getQueryVariable("ZINV") || false,
        zMult: getQueryVariable("ZMULT"),
        proportionWidth: 0,
        proportionHeight: 0
    }.init();

    var wxs3 = new Wxs3(layers, dim);

}());<commit_msg>cleanup further<commit_after>var wxs3 = wxs3 || {};

(function () {
    'use strict';

    //check WebGL
    if (!window.WebGLRenderingContext) {
        // the browser doesn't even know what WebGL is
        window.location = "http://get.webgl.org";
    }

    // extraction for URL parameters
    function getQueryVariable(variable) {
        var pair, i;
        var query = window.location.search.substring(1);
        var vars = query.split("&");
        for (i = 0; i < vars.length; i++) {
            pair = vars[i].split("=");
            if (pair[0].toUpperCase() === variable) {
                return pair[1];
            }
        }
        return false;
    }

    var Wxs3 = function (layers, dim) {

        this.dim = dim;
        this.camera = null;
        this.scene = null;
        this.renderer = null;
        this.controls = null;

        var cameraHeight, fov, proportionAverage;

        if (dim.metersWidth > dim.metersHeight) {
            dim.demWidth = parseInt((dim.metersWidth / dim.metersHeight) * dim.demWidth, 10);
        } else if (dim.metersWidth < dim.metersHeight) {
            dim.demHeight = parseInt((dim.metersHeight / dim.metersWidth) * dim.demHeight, 10);
        }

        dim.proportionWidth = dim.metersWidth / dim.demWidth; // mapunits between vertexes in x-dimention
        dim.proportionHeight = dim.metersHeight / dim.demHeight; // mapunits between vertexes in y-dimention
        proportionAverage = ((dim.proportionWidth + dim.proportionHeight) / 2); // average mapunits between vertexes

        if (dim.zInv) {
            proportionAverage *= -1;
        }
        if (dim.zMult) {
            dim.zMult = proportionAverage / dim.zMult;
        } else {
            dim.zMult = proportionAverage;
        }

        if (getQueryVariable("WMSFORMATMODE")) {
            dim.wmsFormatMode = '; mode=' + getQueryVariable("WMSFORMATMODE");
        }

        // We've defined some standardlayers for the default wms-service in topo2.layers.js. Overwrite them if defined in url.
        this.wmsLayers = layers;

        this.renderer = new THREE.WebGLRenderer();
        this.renderer.setSize(dim.width, dim.height);

        this.scene = new THREE.Scene();
        this.scene.add(new THREE.AmbientLight(0xeeeeee));

        fov = 45;
        this.camera = new THREE.PerspectiveCamera(fov, dim.width / dim.height, 0.1, 20000);
        // Some trig to find height for camera
        cameraHeight = getQueryVariable("Z") || (dim.metersHeight / 2) / Math.tan((fov / 2) * Math.PI / 180);
        // Place camera in middle of bbox
        this.camera.position.set((dim.minx + dim.maxx) / 2, (dim.miny + dim.maxy) / 2, cameraHeight);
        this.controls = new THREE.TrackballControls(this.camera);
        // Point camera directly down
        this.controls.target = new THREE.Vector3((dim.minx + dim.maxx) / 2, (dim.miny + dim.maxy) / 2, 0);

        // Generate tiles and boundingboxes
        this.bbox2tiles(dim.minx, dim.miny, dim.maxx, dim.maxy);
        document.getElementById('webgl').appendChild(this.renderer.domElement);
    };

    Wxs3.prototype.render = function () {
        this.controls.update();
        requestAnimationFrame(this.render.bind(this));
        this.renderer.render(this.scene, this.camera);
    };

    Wxs3.prototype.bbox2tiles = function (minx, miny, maxx, maxy) {
        //TODO: generic tilematrix-parsing
        // Proof of concept with 2 subdivision in each dimention:
        //0,0
        this.addTile('x0_y0', minx, miny, (minx + maxx) / 2, (miny + maxy) / 2);
        //1,0
        this.addTile('x1_y0', (minx + maxx) / 2, miny, maxx, (miny + maxy) / 2);
        //0,1
        this.addTile('x0_y1', minx, (miny + maxy) / 2, (minx + maxx) / 2, maxy);
        //1,1
        this.addTile('x1_y1', (minx + maxx) / 2, (miny + maxy) / 2, maxx, maxy);
    };

    Wxs3.prototype.addTile = function (tileNr, minx, miny, maxx, maxy) {

        var bboxWCS = [
            parseInt(minx, 10),
            parseInt(miny - this.dim.proportionHeight, 10),
            parseInt(maxx + this.dim.proportionWidth, 10),
            parseInt(maxy, 10)
        ].join(',');

        var url = 'http://openwms.statkart.no/skwms1/wcs.dtm?' +
            'SERVICE=WCS' +
            '&VERSION=1.0.0' +
            '&REQUEST=GetCoverage' +
            '&FORMAT=XYZ' +
            '&COVERAGE=' + this.dim.coverage +
            '&bbox=' + bboxWCS +
            '&CRS=' + this.dim.crs +
            '&RESPONSE_CRS=' + this.dim.crs +
            '&WIDTH=' + parseInt(this.dim.demWidth, 10) +
            '&HEIGHT=' + parseInt(this.dim.demHeight, 10);

        var demTileRequest = new XMLHttpRequest();
        demTileRequest.open('GET', url, true);

        var that = this;
        demTileRequest.onreadystatechange = function () {
            if (this.readyState === 4) {
                that.demTileLoaded(tileNr, minx, miny, maxx, maxy, this.responseText);
            }
        };
        demTileRequest.send();
    };

    Wxs3.prototype.demTileLoaded = function (tileNr, minx, miny, maxx, maxy, responseText) {

        var minxWMS, minyWMS, maxxWMS, maxyWMS;
        var geometry = new THREE.PlaneGeometry(
            maxx - minx,
            maxy - miny,
            (this.dim.demWidth - 1),
            (this.dim.demHeight - 1)
        );

        var i, l;
        var lines = responseText.split("\n");
        for (i = 0, l = geometry.vertices.length; i < l; i++) {
            geometry.vertices[i].x = lines[i].split(' ')[0];
            geometry.vertices[i].y = lines[i].split(' ')[1];
            geometry.vertices[i].z = lines[i].split(' ')[2];
            if (i === 0) {
                minxWMS = geometry.vertices[i].x;
                maxyWMS = geometry.vertices[i].y;
            }
            if (i === l - 1) {
                maxxWMS = geometry.vertices[i].x;
                minyWMS = geometry.vertices[i].y;
            }
        }
        var bboxWMS = [minxWMS, minyWMS, maxxWMS, maxyWMS].join(',');

        var plane = new THREE.Mesh(
            geometry,
            this.createMaterial(bboxWMS, tileNr)
        );
        plane.name = 'tile_' + tileNr;
        this.scene.add(plane);

        this.render();
    };

    Wxs3.prototype.createMaterial = function (bboxWMS, tileNr) {
        var material = new THREE.MeshPhongMaterial(
            {
                map: THREE.ImageUtils.loadTexture(
                    this.dim.wms + '?' +
                        'service=wms' +
                        '&version=1.3.0' +
                        '&request=getmap' +
                        '&crs=' + this.dim.crs +
                        '&srs=' + this.dim.crs +
                        '&WIDTH=' + this.dim.demWidth * this.dim.wmsMult +
                        '&HEIGHT=' + this.dim.demHeight * this.dim.wmsMult +
                        '&bbox=' + bboxWMS +
                        '&layers=' + this.wmsLayers +
                        '&format=' + this.dim.wmsFormat + this.dim.wmsFormatMode,
                    new THREE.UVMapping()
                )
            }
        );
        material.name = 'material_' + tileNr;
        return material;
    };

    var dim = {
        width: window.innerWidth,
        height: window.innerHeight,
        demWidth: getQueryVariable("WIDTH") || 100,
        demHeight: getQueryVariable("HEIGHT") || 100,
        bbox: getQueryVariable("BBOX") || '161244,6831251,171526,6837409',
        metersWidth: 0,
        metersHeight: 0,
        minx: 0,
        maxx: 0,
        miny: 0,
        maxy: 0,
        init: function () {
            this.metersWidth = this.bbox.split(',')[2] - this.bbox.split(',')[0];
            this.metersHeight = this.bbox.split(',')[3] - this.bbox.split(',')[1];
            this.minx = parseInt(this.bbox.split(',')[0], 10);
            this.maxx = parseInt(this.bbox.split(',')[2], 10);
            this.miny = parseInt(this.bbox.split(',')[1], 10);
            this.maxy = parseInt(this.bbox.split(',')[3], 10);
            return this;
        },
        crs: getQueryVariable("CRS") || getQueryVariable("SRS") || 'EPSG:32633',
        coverage: getQueryVariable("COVERAGE") || 'land_utm33_10m',
        wms: getQueryVariable("WMS") || 'http://openwms.statkart.no/skwms1/wms.topo2',
        wmsMult: getQueryVariable("WMSMULT") || 5,
        wmsFormat: getQueryVariable("WMSFORMAT") || "image/png",
        wmsFormatMode: "",
        zInv: getQueryVariable("ZINV") || false,
        zMult: getQueryVariable("ZMULT"),
        proportionWidth: 0,
        proportionHeight: 0
    }.init();

    var layers = getQueryVariable("LAYERS") || layers;
    var wxs3 = new Wxs3(layers, dim);

}());<|endoftext|>


<commit_before>/* Magic Mirror
 * Module: MMM-Fuel
 *
 * By fewieden https://github.com/fewieden/MMM-Fuel
 * MIT Licensed.
 */

Module.register("MMM-Fuel", {

    sortByPrice: true,
    help: false,
    map: false,
    mapUI: null,
    interval: null,

    defaults: {
        radius: 5,
        max: 5,
        map_api_key: false,
        zoom: 12,
        width: 600,
        height: 600,
        colored: false,
        rotateInterval: 60 * 1000,           // every minute
        updateInterval: 15 * 60 * 1000       // every 15 minutes
    },

    voice: {
        mode: "FUEL",
        sentences: [
            "OPEN HELP",
            "CLOSE HELP",
            "SHOW GAS STATIONS",
            "HIDE MAP"
        ]
    },

    getTranslations: function () {
        return {
            en: "translations/en.json",
            de: "translations/de.json"
        };
    },

    getStyles: function () {
        return ["font-awesome.css", "MMM-Fuel.css"];
    },

    start: function () {
        Log.info("Starting module: " + this.name);
        //Add script manually, getScripts doesn't work for it!
        var script = document.createElement("script");
        script.src = "https://maps.googleapis.com/maps/api/js?key=" + this.config.map_api_key;
        document.querySelector("body").appendChild(script);
        this.interval = this.createInterval();
        this.sendSocketNotification("CONFIG", this.config);
    },

    createInterval: function(){
        return setInterval(() => {
            this.sortByPrice = !this.sortByPrice;
            this.updateDom(300);
        }, this.config.rotateInterval);
    },

    notificationReceived: function (notification, payload, sender) {
        if(notification === "ALL_MODULES_STARTED"){
            this.sendNotification("REGISTER_VOICE_MODULE", this.voice);
        } else if(notification === "VOICE_FUEL" && sender.name === "MMM-voice"){
            this.checkCommands(payload);
        } else if(notification === "VOICE_MODE_CHANGED" && sender.name === "MMM-voice" && payload.old === this.voice.mode){
            this.help = false;
            this.map = false;
            this.updateDom(300);
        }
    },

    socketNotificationReceived: function (notification, payload) {
        if (notification === "PRICELIST") {
            this.priceList = payload;
            this.updateDom(300);
        }
    },

    getDom: function () {

        var wrapper = document.createElement("div");
        var list = document.createElement("div");
        var header = document.createElement("header");
        header.classList.add("align-left");
        var logo = document.createElement("i");
        logo.classList.add("fa", "fa-car", "logo");
        header.appendChild(logo);
        var name = document.createElement("span");
        name.innerHTML = this.translate("FUEL_PRICES");
        header.appendChild(name);
        list.appendChild(header);

        if (!this.priceList) {
            var text = document.createElement("div");
            text.innerHTML = this.translate("LOADING");
            text.classList.add("dimmed", "light");
            list.appendChild(text);
        } else {
            var table = document.createElement("table");
            table.classList.add("small", "table", "align-left");

            table.appendChild(this.createLabelRow());

            var data = this.sortByPrice ? this.priceList.byPrice : this.priceList.byDistance;

            for (var i = 0; i < Math.min(data.length, this.config.max); i++) {
                this.appendDataRow(data[i], table);
            }

            list.appendChild(table);

            var modules = document.querySelectorAll(".module");
            for (var i = 0; i < modules.length; i++) {
                if(!modules[i].classList.contains("MMM-Fuel")){
                    if(this.map || this.help){
                        modules[i].classList.add("MMM-Fuel-blur");
                    } else {
                        modules[i].classList.remove("MMM-Fuel-blur");
                    }
                }
            }

            if(this.map || this.help){
                list.classList.add("MMM-Fuel-blur");
                var modal = document.createElement("div");
                modal.classList.add("modal");
                if(this.map && this.config.map_api_key) {
                    if (typeof google === 'object' && typeof google.maps === 'object') {
                        if (!this.config.colored) {
                            modal.classList.add("no-color");
                        }
                        var map = document.createElement("div");
                        map.classList.add("MMM-Fuel-map");
                        map.style.height = this.config.height + "px";
                        map.style.width = this.config.width + "px";
                        modal.appendChild(map);
                        var script = document.createElement("script");
                        script.innerHTML = "var MMM_Fuel_map = new google.maps.Map(document.querySelector('div.MMM-Fuel-map'), {center: new google.maps.LatLng(" + parseFloat(this.config.lat) + ", " + parseFloat(this.config.lng) + "), zoom: " + this.config.zoom + ", disableDefaultUI:true});" +
                            "var trafficLayer = new google.maps.TrafficLayer();" +
                            "trafficLayer.setMap(MMM_Fuel_map);" +
                            "var MMM_Fuel_array = " + JSON.stringify(this.priceList.byPrice) + ";" +
                            "for(var i = 0; i < MMM_Fuel_array.length; i++){" +
                            "var marker = new google.maps.Marker({ position: {lat: MMM_Fuel_array[i].lat, lng: MMM_Fuel_array[i].lng}, label: i + 1 + '', map: MMM_Fuel_map});" +
                            "}";
                        modal.appendChild(script);
                    } else {
                        modal.innerHTML = this.translate("MAP_API_NOT_READY");
                    }
                } else if(this.map){
                    modal.innerHTML = this.translate("API_KEY_NEEDED");
                } else {
                    this.appendHelp(modal);
                }
                wrapper.appendChild(modal);
            }
        }

        wrapper.appendChild(list);

        return wrapper;
    },

    createLabelRow: function () {
        var labelRow = document.createElement("tr");

        var sortLabel = document.createElement("th");
        if(this.sortByPrice){
            sortLabel.innerHTML = this.translate("CHEAPEST_STATIONS");
        } else {
            sortLabel.innerHTML = this.translate("CLOSEST_STATIONS");
        }
        labelRow.appendChild(sortLabel);

        var priceIconLabel = document.createElement("th");
        priceIconLabel.classList.add("centered");
        var priceIcon = document.createElement("i");
        priceIcon.classList.add("fa", "fa-money");
        priceIconLabel.appendChild(priceIcon);
        labelRow.appendChild(priceIconLabel);

        var distanceIconLabel = document.createElement("th");
        distanceIconLabel.classList.add("centered");
        var distanceIcon = document.createElement("i");
        distanceIcon.classList.add("fa", "fa-map-o");
        distanceIconLabel.appendChild(distanceIcon);
        labelRow.appendChild(distanceIconLabel);

        return labelRow;
    },

    appendDataRow: function (data, appendTo) {
        var row = document.createElement("tr");

        var name = document.createElement("td");
        name.innerHTML = data.name;
        row.appendChild(name);

        var price = document.createElement("td");
        price.classList.add("centered");
        price.innerHTML = data.price + " €";
        row.appendChild(price);

        var distance = document.createElement("td");
        distance.classList.add("centered");
        distance.innerHTML = data.dist + " km";
        row.appendChild(distance);

        appendTo.appendChild(row);

        var details = document.createElement("tr");
        details.setAttribute("colspan", 3);

        var address = document.createElement("td");
        address.classList.add("xsmall");
        address.innerHTML = ("0" + data.postCode).slice(-5) + " " + data.place + " - " + data.street + " " + data.houseNumber;
        details.appendChild(address);

        appendTo.appendChild(details);
    },

    checkCommands: function(data){
        if(/(HELP)/g.test(data)){
            if(/(OPEN)/g.test(data) || !this.help && !/(CLOSE)/g.test(data)){
                this.map = false;
                this.help = true;
                clearInterval(this.interval);
            } else if(/(CLOSE)/g.test(data) || this.help && !/(OPEN)/g.test(data)){
                this.help = false;
                this.interval = this.createInterval();
            }
        } else if(/(GAS)/g.test(data) && /(STATIONS)/g.test(data)){
            this.help = false;
            this.map = true;
            clearInterval(this.interval);
        } else if(/(HIDE)/g.test(data) && /(MAP)/g.test(data)){
            this.map = false;
            this.interval = this.createInterval();
        }
        this.updateDom(300);
    },

    appendHelp: function(appendTo){
        var title = document.createElement("h1");
        title.classList.add("medium");
        title.innerHTML = this.name + " - " + this.translate("COMMAND_LIST");
        appendTo.appendChild(title);

        var mode = document.createElement("div");
        mode.innerHTML = this.translate("MODE") + ": " + this.voice.mode;
        appendTo.appendChild(mode);

        var listLabel = document.createElement("div");
        listLabel.innerHTML = this.translate("VOICE_COMMANDS") + ":";
        appendTo.appendChild(listLabel);

        var list = document.createElement("ul");
        for(var i = 0; i < this.voice.sentences.length; i++){
            var item = document.createElement("li");
            item.innerHTML = this.voice.sentences[i];
            list.appendChild(item);
        }
        appendTo.appendChild(list);
    }
});<commit_msg>changed command detection order<commit_after>/* Magic Mirror
 * Module: MMM-Fuel
 *
 * By fewieden https://github.com/fewieden/MMM-Fuel
 * MIT Licensed.
 */

Module.register("MMM-Fuel", {

    sortByPrice: true,
    help: false,
    map: false,
    mapUI: null,
    interval: null,

    defaults: {
        radius: 5,
        max: 5,
        map_api_key: false,
        zoom: 12,
        width: 600,
        height: 600,
        colored: false,
        rotateInterval: 60 * 1000,           // every minute
        updateInterval: 15 * 60 * 1000       // every 15 minutes
    },

    voice: {
        mode: "FUEL",
        sentences: [
            "OPEN HELP",
            "CLOSE HELP",
            "SHOW GAS STATIONS",
            "HIDE MAP"
        ]
    },

    getTranslations: function () {
        return {
            en: "translations/en.json",
            de: "translations/de.json"
        };
    },

    getStyles: function () {
        return ["font-awesome.css", "MMM-Fuel.css"];
    },

    start: function () {
        Log.info("Starting module: " + this.name);
        //Add script manually, getScripts doesn't work for it!
        var script = document.createElement("script");
        script.src = "https://maps.googleapis.com/maps/api/js?key=" + this.config.map_api_key;
        document.querySelector("body").appendChild(script);
        this.interval = this.createInterval();
        this.sendSocketNotification("CONFIG", this.config);
    },

    createInterval: function(){
        return setInterval(() => {
            this.sortByPrice = !this.sortByPrice;
            this.updateDom(300);
        }, this.config.rotateInterval);
    },

    notificationReceived: function (notification, payload, sender) {
        if(notification === "ALL_MODULES_STARTED"){
            this.sendNotification("REGISTER_VOICE_MODULE", this.voice);
        } else if(notification === "VOICE_FUEL" && sender.name === "MMM-voice"){
            this.checkCommands(payload);
        } else if(notification === "VOICE_MODE_CHANGED" && sender.name === "MMM-voice" && payload.old === this.voice.mode){
            this.help = false;
            this.map = false;
            this.updateDom(300);
        }
    },

    socketNotificationReceived: function (notification, payload) {
        if (notification === "PRICELIST") {
            this.priceList = payload;
            this.updateDom(300);
        }
    },

    getDom: function () {

        var wrapper = document.createElement("div");
        var list = document.createElement("div");
        var header = document.createElement("header");
        header.classList.add("align-left");
        var logo = document.createElement("i");
        logo.classList.add("fa", "fa-car", "logo");
        header.appendChild(logo);
        var name = document.createElement("span");
        name.innerHTML = this.translate("FUEL_PRICES");
        header.appendChild(name);
        list.appendChild(header);

        if (!this.priceList) {
            var text = document.createElement("div");
            text.innerHTML = this.translate("LOADING");
            text.classList.add("dimmed", "light");
            list.appendChild(text);
        } else {
            var table = document.createElement("table");
            table.classList.add("small", "table", "align-left");

            table.appendChild(this.createLabelRow());

            var data = this.sortByPrice ? this.priceList.byPrice : this.priceList.byDistance;

            for (var i = 0; i < Math.min(data.length, this.config.max); i++) {
                this.appendDataRow(data[i], table);
            }

            list.appendChild(table);

            var modules = document.querySelectorAll(".module");
            for (var i = 0; i < modules.length; i++) {
                if(!modules[i].classList.contains("MMM-Fuel")){
                    if(this.map || this.help){
                        modules[i].classList.add("MMM-Fuel-blur");
                    } else {
                        modules[i].classList.remove("MMM-Fuel-blur");
                    }
                }
            }

            if(this.map || this.help){
                list.classList.add("MMM-Fuel-blur");
                var modal = document.createElement("div");
                modal.classList.add("modal");
                if(this.map && this.config.map_api_key) {
                    if (typeof google === 'object' && typeof google.maps === 'object') {
                        if (!this.config.colored) {
                            modal.classList.add("no-color");
                        }
                        var map = document.createElement("div");
                        map.classList.add("MMM-Fuel-map");
                        map.style.height = this.config.height + "px";
                        map.style.width = this.config.width + "px";
                        modal.appendChild(map);
                        var script = document.createElement("script");
                        script.innerHTML = "var MMM_Fuel_map = new google.maps.Map(document.querySelector('div.MMM-Fuel-map'), {center: new google.maps.LatLng(" + parseFloat(this.config.lat) + ", " + parseFloat(this.config.lng) + "), zoom: " + this.config.zoom + ", disableDefaultUI:true});" +
                            "var trafficLayer = new google.maps.TrafficLayer();" +
                            "trafficLayer.setMap(MMM_Fuel_map);" +
                            "var MMM_Fuel_array = " + JSON.stringify(this.priceList.byPrice) + ";" +
                            "for(var i = 0; i < MMM_Fuel_array.length; i++){" +
                            "var marker = new google.maps.Marker({ position: {lat: MMM_Fuel_array[i].lat, lng: MMM_Fuel_array[i].lng}, label: i + 1 + '', map: MMM_Fuel_map});" +
                            "}";
                        modal.appendChild(script);
                    } else {
                        modal.innerHTML = this.translate("MAP_API_NOT_READY");
                    }
                } else if(this.map){
                    modal.innerHTML = this.translate("API_KEY_NEEDED");
                } else {
                    this.appendHelp(modal);
                }
                wrapper.appendChild(modal);
            }
        }

        wrapper.appendChild(list);

        return wrapper;
    },

    createLabelRow: function () {
        var labelRow = document.createElement("tr");

        var sortLabel = document.createElement("th");
        if(this.sortByPrice){
            sortLabel.innerHTML = this.translate("CHEAPEST_STATIONS");
        } else {
            sortLabel.innerHTML = this.translate("CLOSEST_STATIONS");
        }
        labelRow.appendChild(sortLabel);

        var priceIconLabel = document.createElement("th");
        priceIconLabel.classList.add("centered");
        var priceIcon = document.createElement("i");
        priceIcon.classList.add("fa", "fa-money");
        priceIconLabel.appendChild(priceIcon);
        labelRow.appendChild(priceIconLabel);

        var distanceIconLabel = document.createElement("th");
        distanceIconLabel.classList.add("centered");
        var distanceIcon = document.createElement("i");
        distanceIcon.classList.add("fa", "fa-map-o");
        distanceIconLabel.appendChild(distanceIcon);
        labelRow.appendChild(distanceIconLabel);

        return labelRow;
    },

    appendDataRow: function (data, appendTo) {
        var row = document.createElement("tr");

        var name = document.createElement("td");
        name.innerHTML = data.name;
        row.appendChild(name);

        var price = document.createElement("td");
        price.classList.add("centered");
        price.innerHTML = data.price + " €";
        row.appendChild(price);

        var distance = document.createElement("td");
        distance.classList.add("centered");
        distance.innerHTML = data.dist + " km";
        row.appendChild(distance);

        appendTo.appendChild(row);

        var details = document.createElement("tr");
        details.setAttribute("colspan", 3);

        var address = document.createElement("td");
        address.classList.add("xsmall");
        address.innerHTML = ("0" + data.postCode).slice(-5) + " " + data.place + " - " + data.street + " " + data.houseNumber;
        details.appendChild(address);

        appendTo.appendChild(details);
    },

    checkCommands: function(data){
        if(/(HELP)/g.test(data)){
            if(/(CLOSE)/g.test(data) || this.help && !/(OPEN)/g.test(data)){
                this.help = false;
                this.interval = this.createInterval();
            } else if(/(OPEN)/g.test(data) || !this.help && !/(CLOSE)/g.test(data)){
                this.map = false;
                this.help = true;
                clearInterval(this.interval);
            }
        } else if(/(HIDE)/g.test(data) && /(MAP)/g.test(data)){
            this.map = false;
            this.interval = this.createInterval();
        } else if(/(GAS)/g.test(data) && /(STATIONS)/g.test(data)){
            this.help = false;
            this.map = true;
            clearInterval(this.interval);
        }
        this.updateDom(300);
    },

    appendHelp: function(appendTo){
        var title = document.createElement("h1");
        title.classList.add("medium");
        title.innerHTML = this.name + " - " + this.translate("COMMAND_LIST");
        appendTo.appendChild(title);

        var mode = document.createElement("div");
        mode.innerHTML = this.translate("MODE") + ": " + this.voice.mode;
        appendTo.appendChild(mode);

        var listLabel = document.createElement("div");
        listLabel.innerHTML = this.translate("VOICE_COMMANDS") + ":";
        appendTo.appendChild(listLabel);

        var list = document.createElement("ul");
        for(var i = 0; i < this.voice.sentences.length; i++){
            var item = document.createElement("li");
            item.innerHTML = this.voice.sentences[i];
            list.appendChild(item);
        }
        appendTo.appendChild(list);
    }
});<|endoftext|>


<commit_before>angular.module('clientRoutes', [])

.config(['$stateProvider', function($stateProvider) {

  $stateProvider

    .state('home', {
      url: '/',
      controller: 'homeController',
      templateUrl: './app/components/home/homeView.html'
    })
    .state('link', {
      url: '/:test',
      controller: 'connectingController',
      templateUrl: './app/components/connecting/featView.html'
    });
}]);<commit_msg>[refactor] add otherwise and renamed test state in app_routes.js s<commit_after>angular.module('clientRoutes', [])

.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {

  $urlRouterProvider
  .otherwise('/');

  $stateProvider
    .state('home', {
      url: '/',
      controller: 'homeController',
      templateUrl: './app/components/home/homeView.html'
    })
    .state('room', {
      url: '/room/:roomHash',
      controller: 'connectingController',
      templateUrl: './app/components/connecting/featView.html'
    });
}]);<|endoftext|>


<commit_before>/*
Copyright 2017 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import EventEmitter from 'events';
import { groupMemberFromApiObject, groupRoomFromApiObject } from '../groups';
import FlairStore from './FlairStore';
import MatrixClientPeg from '../MatrixClientPeg';

function parseMembersResponse(response) {
    return response.chunk.map((apiMember) => groupMemberFromApiObject(apiMember));
}

function parseRoomsResponse(response) {
    return response.chunk.map((apiRoom) => groupRoomFromApiObject(apiRoom));
}

// The number of ongoing group requests
let ongoingRequestCount = 0;

// This has arbitrarily been set to a small number to lower the priority
// of doing group-related requests because we care about other important
// requests like hitting /sync.
const LIMIT = 3; // Maximum number of ongoing group requests

// FIFO queue of functions to call in the backlog
const backlogQueue = [
    // () => {...}
];

// Pull from the FIFO queue
function checkBacklog() {
    const item = backlogQueue.shift();
    if (typeof item === 'function') item();
}

// Limit the maximum number of ongoing promises returned by fn to LIMIT and
// use a FIFO queue to handle the backlog.
async function limitConcurrency(fn) {
    if (ongoingRequestCount >= LIMIT) {
        // Enqueue this request for later execution
        await new Promise((resolve, reject) => {
            backlogQueue.push(resolve);
        });
    }

    let result;
    let error;

    ongoingRequestCount++;
    try {
        result = await fn();
    } catch (err) {
        error = err;
    } finally {
        ongoingRequestCount--;
        checkBacklog();
    }

    if (error) throw error;

    return result;
}

/**
 * Global store for tracking group summary, members, invited members and rooms.
 */
class GroupStore extends EventEmitter {
    STATE_KEY = {
        GroupMembers: 'GroupMembers',
        GroupInvitedMembers: 'GroupInvitedMembers',
        Summary: 'Summary',
        GroupRooms: 'GroupRooms',
    };

    constructor() {
        super();
        this._state = {};
        this._state[this.STATE_KEY.Summary] = {};
        this._state[this.STATE_KEY.GroupRooms] = {};
        this._state[this.STATE_KEY.GroupMembers] = {};
        this._state[this.STATE_KEY.GroupInvitedMembers] = {};

        this._ready = {};
        this._ready[this.STATE_KEY.Summary] = {};
        this._ready[this.STATE_KEY.GroupRooms] = {};
        this._ready[this.STATE_KEY.GroupMembers] = {};
        this._ready[this.STATE_KEY.GroupInvitedMembers] = {};

        this._fetchResourcePromise = {
            [this.STATE_KEY.Summary]: {},
            [this.STATE_KEY.GroupRooms]: {},
            [this.STATE_KEY.GroupMembers]: {},
            [this.STATE_KEY.GroupInvitedMembers]: {},
        };

        this._resourceFetcher = {
            [this.STATE_KEY.Summary]: (groupId) => {
                return limitConcurrency(
                    () => MatrixClientPeg.get().getGroupSummary(groupId),
                );
            },
            [this.STATE_KEY.GroupRooms]: (groupId) => {
                return limitConcurrency(
                    () => MatrixClientPeg.get().getGroupRooms(groupId).then(parseRoomsResponse),
                );
            },
            [this.STATE_KEY.GroupMembers]: (groupId) => {
                return limitConcurrency(
                    () => MatrixClientPeg.get().getGroupUsers(groupId).then(parseMembersResponse),
                );
            },
            [this.STATE_KEY.GroupInvitedMembers]: (groupId) => {
                return limitConcurrency(
                    () => MatrixClientPeg.get().getGroupInvitedUsers(groupId).then(parseMembersResponse),
                );
            },
        };

        this.on('error', (err, groupId) => {
            console.error(`GroupStore encountered error whilst fetching data for ${groupId}`, err);
        });
    }

    _fetchResource(stateKey, groupId) {
        // Ongoing request, ignore
        if (this._fetchResourcePromise[stateKey][groupId]) return;

        const clientPromise = this._resourceFetcher[stateKey](groupId);

        // Indicate ongoing request
        this._fetchResourcePromise[stateKey][groupId] = clientPromise;

        clientPromise.then((result) => {
            this._state[stateKey][groupId] = result;
            console.info(this._state);
            this._ready[stateKey][groupId] = true;
            this._notifyListeners();
        }).catch((err) => {
            // Invited users not visible to non-members
            if (stateKey === this.STATE_KEY.GroupInvitedMembers && err.httpStatus === 403) {
                return;
            }

            console.error(`Failed to get resource ${stateKey} for ${groupId}`, err);
            this.emit('error', err, groupId);
        }).finally(() => {
            // Indicate finished request, allow for future fetches
            delete this._fetchResourcePromise[stateKey][groupId];
        });

        return clientPromise;
    }

    _notifyListeners() {
        this.emit('update');
    }

    /**
     * Register a listener to recieve updates from the store. This also
     * immediately triggers an update to send the current state of the
     * store (which could be the initial state).
     *
     * This also causes a fetch of all data of the specified group,
     * which might cause 4 separate HTTP requests, but only if said
     * requests aren't already ongoing.
     *
     * @param {string} groupId the ID of the group to fetch data for.
     * @param {function} fn the function to call when the store updates.
     * @return {Object} tok a registration "token" with a single
     *                      property `unregister`, a function that can
     *                      be called to unregister the listener such
     *                      that it won't be called any more.
     */
    registerListener(groupId, fn) {
        this.on('update', fn);
        // Call to set initial state (before fetching starts)
        this.emit('update');

        this._fetchResource(this.STATE_KEY.Summary, groupId);
        this._fetchResource(this.STATE_KEY.GroupRooms, groupId);
        this._fetchResource(this.STATE_KEY.GroupMembers, groupId);
        this._fetchResource(this.STATE_KEY.GroupInvitedMembers, groupId);

        // Similar to the Store of flux/utils, we return a "token" that
        // can be used to unregister the listener.
        return {
            unregister: () => {
                this.unregisterListener(fn);
            },
        };
    }

    unregisterListener(fn) {
        this.removeListener('update', fn);
    }

    isStateReady(groupId, id) {
        return this._ready[id][groupId];
    }

    getSummary(groupId) {
        return this._state[this.STATE_KEY.Summary][groupId] || {};
    }

    getGroupRooms(groupId) {
        return this._state[this.STATE_KEY.GroupRooms][groupId] || [];
    }

    getGroupMembers(groupId) {
        return this._state[this.STATE_KEY.GroupMembers][groupId] || [];
    }

    getGroupInvitedMembers(groupId) {
        return this._state[this.STATE_KEY.GroupInvitedMembers][groupId] || [];
    }

    getGroupPublicity(groupId) {
        return (this._state[this.STATE_KEY.Summary][groupId] || {}).user ?
            (this._state[this.STATE_KEY.Summary][groupId] || {}).user.is_publicised : null;
    }

    isUserPrivileged(groupId) {
        return (this._state[this.STATE_KEY.Summary][groupId] || {}).user ?
            (this._state[this.STATE_KEY.Summary][groupId] || {}).user.is_privileged : null;
    }

    addRoomToGroup(groupId, roomId, isPublic) {
        return MatrixClientPeg.get()
            .addRoomToGroup(groupId, roomId, isPublic)
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId));
    }

    updateGroupRoomVisibility(groupId, roomId, isPublic) {
        return MatrixClientPeg.get()
            .updateGroupRoomVisibility(groupId, roomId, isPublic)
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId));
    }

    removeRoomFromGroup(groupId, roomId) {
        return MatrixClientPeg.get()
            .removeRoomFromGroup(groupId, roomId)
            // Room might be in the summary, refresh just in case
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId))
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId));
    }

    inviteUserToGroup(groupId, userId) {
        return MatrixClientPeg.get().inviteUserToGroup(groupId, userId)
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupInvitedMembers, groupId));
    }

    acceptGroupInvite(groupId) {
        return MatrixClientPeg.get().acceptGroupInvite(groupId)
            // The user should now be able to access (personal) group settings
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId))
            // The user might be able to see more rooms now
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId))
            // The user should now appear as a member
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupMembers, groupId))
            // The user should now not appear as an invited member
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupInvitedMembers, groupId));
    }

    joinGroup(groupId) {
        return MatrixClientPeg.get().joinGroup(groupId)
            // The user should now be able to access (personal) group settings
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId))
            // The user might be able to see more rooms now
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId))
            // The user should now appear as a member
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupMembers, groupId))
            // The user should now not appear as an invited member
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupInvitedMembers, groupId));
    }

    leaveGroup(groupId) {
        return MatrixClientPeg.get().leaveGroup(groupId)
            // The user should now not be able to access group settings
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId))
            // The user might only be able to see a subset of rooms now
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId))
            // The user should now not appear as a member
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupMembers, groupId));
    }

    addRoomToGroupSummary(groupId, roomId, categoryId) {
        return MatrixClientPeg.get()
            .addRoomToGroupSummary(groupId, roomId, categoryId)
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
    }

    addUserToGroupSummary(groupId, userId, roleId) {
        return MatrixClientPeg.get()
            .addUserToGroupSummary(groupId, userId, roleId)
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
    }

    removeRoomFromGroupSummary(groupId, roomId) {
        return MatrixClientPeg.get()
            .removeRoomFromGroupSummary(groupId, roomId)
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
    }

    removeUserFromGroupSummary(groupId, userId) {
        return MatrixClientPeg.get()
            .removeUserFromGroupSummary(groupId, userId)
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
    }

    setGroupPublicity(groupId, isPublished) {
        return MatrixClientPeg.get()
            .setGroupPublicity(groupId, isPublished)
            .then(() => { FlairStore.invalidatePublicisedGroups(MatrixClientPeg.get().credentials.userId); })
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
    }
}

let singletonGroupStore = null;
if (!singletonGroupStore) {
    singletonGroupStore = new GroupStore();
}
module.exports = singletonGroupStore;
<commit_msg>Simplify concurrent request error handling<commit_after>/*
Copyright 2017 New Vector Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import EventEmitter from 'events';
import { groupMemberFromApiObject, groupRoomFromApiObject } from '../groups';
import FlairStore from './FlairStore';
import MatrixClientPeg from '../MatrixClientPeg';

function parseMembersResponse(response) {
    return response.chunk.map((apiMember) => groupMemberFromApiObject(apiMember));
}

function parseRoomsResponse(response) {
    return response.chunk.map((apiRoom) => groupRoomFromApiObject(apiRoom));
}

// The number of ongoing group requests
let ongoingRequestCount = 0;

// This has arbitrarily been set to a small number to lower the priority
// of doing group-related requests because we care about other important
// requests like hitting /sync.
const LIMIT = 3; // Maximum number of ongoing group requests

// FIFO queue of functions to call in the backlog
const backlogQueue = [
    // () => {...}
];

// Pull from the FIFO queue
function checkBacklog() {
    const item = backlogQueue.shift();
    if (typeof item === 'function') item();
}

// Limit the maximum number of ongoing promises returned by fn to LIMIT and
// use a FIFO queue to handle the backlog.
async function limitConcurrency(fn) {
    if (ongoingRequestCount >= LIMIT) {
        // Enqueue this request for later execution
        await new Promise((resolve, reject) => {
            backlogQueue.push(resolve);
        });
    }

    ongoingRequestCount++;
    try {
        return await fn();
    } catch (err) {
        // We explicitly do not handle the error here, but let it propogate.
        throw err;
    } finally {
        ongoingRequestCount--;
        checkBacklog();
    }
}

/**
 * Global store for tracking group summary, members, invited members and rooms.
 */
class GroupStore extends EventEmitter {
    STATE_KEY = {
        GroupMembers: 'GroupMembers',
        GroupInvitedMembers: 'GroupInvitedMembers',
        Summary: 'Summary',
        GroupRooms: 'GroupRooms',
    };

    constructor() {
        super();
        this._state = {};
        this._state[this.STATE_KEY.Summary] = {};
        this._state[this.STATE_KEY.GroupRooms] = {};
        this._state[this.STATE_KEY.GroupMembers] = {};
        this._state[this.STATE_KEY.GroupInvitedMembers] = {};

        this._ready = {};
        this._ready[this.STATE_KEY.Summary] = {};
        this._ready[this.STATE_KEY.GroupRooms] = {};
        this._ready[this.STATE_KEY.GroupMembers] = {};
        this._ready[this.STATE_KEY.GroupInvitedMembers] = {};

        this._fetchResourcePromise = {
            [this.STATE_KEY.Summary]: {},
            [this.STATE_KEY.GroupRooms]: {},
            [this.STATE_KEY.GroupMembers]: {},
            [this.STATE_KEY.GroupInvitedMembers]: {},
        };

        this._resourceFetcher = {
            [this.STATE_KEY.Summary]: (groupId) => {
                return limitConcurrency(
                    () => MatrixClientPeg.get().getGroupSummary(groupId),
                );
            },
            [this.STATE_KEY.GroupRooms]: (groupId) => {
                return limitConcurrency(
                    () => MatrixClientPeg.get().getGroupRooms(groupId).then(parseRoomsResponse),
                );
            },
            [this.STATE_KEY.GroupMembers]: (groupId) => {
                return limitConcurrency(
                    () => MatrixClientPeg.get().getGroupUsers(groupId).then(parseMembersResponse),
                );
            },
            [this.STATE_KEY.GroupInvitedMembers]: (groupId) => {
                return limitConcurrency(
                    () => MatrixClientPeg.get().getGroupInvitedUsers(groupId).then(parseMembersResponse),
                );
            },
        };

        this.on('error', (err, groupId) => {
            console.error(`GroupStore encountered error whilst fetching data for ${groupId}`, err);
        });
    }

    _fetchResource(stateKey, groupId) {
        // Ongoing request, ignore
        if (this._fetchResourcePromise[stateKey][groupId]) return;

        const clientPromise = this._resourceFetcher[stateKey](groupId);

        // Indicate ongoing request
        this._fetchResourcePromise[stateKey][groupId] = clientPromise;

        clientPromise.then((result) => {
            this._state[stateKey][groupId] = result;
            console.info(this._state);
            this._ready[stateKey][groupId] = true;
            this._notifyListeners();
        }).catch((err) => {
            // Invited users not visible to non-members
            if (stateKey === this.STATE_KEY.GroupInvitedMembers && err.httpStatus === 403) {
                return;
            }

            console.error(`Failed to get resource ${stateKey} for ${groupId}`, err);
            this.emit('error', err, groupId);
        }).finally(() => {
            // Indicate finished request, allow for future fetches
            delete this._fetchResourcePromise[stateKey][groupId];
        });

        return clientPromise;
    }

    _notifyListeners() {
        this.emit('update');
    }

    /**
     * Register a listener to recieve updates from the store. This also
     * immediately triggers an update to send the current state of the
     * store (which could be the initial state).
     *
     * This also causes a fetch of all data of the specified group,
     * which might cause 4 separate HTTP requests, but only if said
     * requests aren't already ongoing.
     *
     * @param {string} groupId the ID of the group to fetch data for.
     * @param {function} fn the function to call when the store updates.
     * @return {Object} tok a registration "token" with a single
     *                      property `unregister`, a function that can
     *                      be called to unregister the listener such
     *                      that it won't be called any more.
     */
    registerListener(groupId, fn) {
        this.on('update', fn);
        // Call to set initial state (before fetching starts)
        this.emit('update');

        this._fetchResource(this.STATE_KEY.Summary, groupId);
        this._fetchResource(this.STATE_KEY.GroupRooms, groupId);
        this._fetchResource(this.STATE_KEY.GroupMembers, groupId);
        this._fetchResource(this.STATE_KEY.GroupInvitedMembers, groupId);

        // Similar to the Store of flux/utils, we return a "token" that
        // can be used to unregister the listener.
        return {
            unregister: () => {
                this.unregisterListener(fn);
            },
        };
    }

    unregisterListener(fn) {
        this.removeListener('update', fn);
    }

    isStateReady(groupId, id) {
        return this._ready[id][groupId];
    }

    getSummary(groupId) {
        return this._state[this.STATE_KEY.Summary][groupId] || {};
    }

    getGroupRooms(groupId) {
        return this._state[this.STATE_KEY.GroupRooms][groupId] || [];
    }

    getGroupMembers(groupId) {
        return this._state[this.STATE_KEY.GroupMembers][groupId] || [];
    }

    getGroupInvitedMembers(groupId) {
        return this._state[this.STATE_KEY.GroupInvitedMembers][groupId] || [];
    }

    getGroupPublicity(groupId) {
        return (this._state[this.STATE_KEY.Summary][groupId] || {}).user ?
            (this._state[this.STATE_KEY.Summary][groupId] || {}).user.is_publicised : null;
    }

    isUserPrivileged(groupId) {
        return (this._state[this.STATE_KEY.Summary][groupId] || {}).user ?
            (this._state[this.STATE_KEY.Summary][groupId] || {}).user.is_privileged : null;
    }

    addRoomToGroup(groupId, roomId, isPublic) {
        return MatrixClientPeg.get()
            .addRoomToGroup(groupId, roomId, isPublic)
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId));
    }

    updateGroupRoomVisibility(groupId, roomId, isPublic) {
        return MatrixClientPeg.get()
            .updateGroupRoomVisibility(groupId, roomId, isPublic)
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId));
    }

    removeRoomFromGroup(groupId, roomId) {
        return MatrixClientPeg.get()
            .removeRoomFromGroup(groupId, roomId)
            // Room might be in the summary, refresh just in case
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId))
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId));
    }

    inviteUserToGroup(groupId, userId) {
        return MatrixClientPeg.get().inviteUserToGroup(groupId, userId)
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupInvitedMembers, groupId));
    }

    acceptGroupInvite(groupId) {
        return MatrixClientPeg.get().acceptGroupInvite(groupId)
            // The user should now be able to access (personal) group settings
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId))
            // The user might be able to see more rooms now
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId))
            // The user should now appear as a member
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupMembers, groupId))
            // The user should now not appear as an invited member
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupInvitedMembers, groupId));
    }

    joinGroup(groupId) {
        return MatrixClientPeg.get().joinGroup(groupId)
            // The user should now be able to access (personal) group settings
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId))
            // The user might be able to see more rooms now
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId))
            // The user should now appear as a member
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupMembers, groupId))
            // The user should now not appear as an invited member
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupInvitedMembers, groupId));
    }

    leaveGroup(groupId) {
        return MatrixClientPeg.get().leaveGroup(groupId)
            // The user should now not be able to access group settings
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId))
            // The user might only be able to see a subset of rooms now
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupRooms, groupId))
            // The user should now not appear as a member
            .then(this._fetchResource.bind(this, this.STATE_KEY.GroupMembers, groupId));
    }

    addRoomToGroupSummary(groupId, roomId, categoryId) {
        return MatrixClientPeg.get()
            .addRoomToGroupSummary(groupId, roomId, categoryId)
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
    }

    addUserToGroupSummary(groupId, userId, roleId) {
        return MatrixClientPeg.get()
            .addUserToGroupSummary(groupId, userId, roleId)
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
    }

    removeRoomFromGroupSummary(groupId, roomId) {
        return MatrixClientPeg.get()
            .removeRoomFromGroupSummary(groupId, roomId)
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
    }

    removeUserFromGroupSummary(groupId, userId) {
        return MatrixClientPeg.get()
            .removeUserFromGroupSummary(groupId, userId)
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
    }

    setGroupPublicity(groupId, isPublished) {
        return MatrixClientPeg.get()
            .setGroupPublicity(groupId, isPublished)
            .then(() => { FlairStore.invalidatePublicisedGroups(MatrixClientPeg.get().credentials.userId); })
            .then(this._fetchResource.bind(this, this.STATE_KEY.Summary, groupId));
    }
}

let singletonGroupStore = null;
if (!singletonGroupStore) {
    singletonGroupStore = new GroupStore();
}
module.exports = singletonGroupStore;
<|endoftext|>


<commit_before>// Holds anything kiwi client specific (ie. front, gateway, _kiwi.plugs..)
/**
*   @namespace
*/
var _kiwi = {};

_kiwi.model = {};
_kiwi.view = {};
_kiwi.applets = {};

/**
 * A global container for third party access
 * Will be used to access a limited subset of kiwi functionality
 * and data (think: plugins)
 */
_kiwi.global = {
    settings: undefined, // Instance of _kiwi.model.DataStore
    plugins: undefined,
    utils: undefined, // TODO: Re-usable methods
    user: undefined, // TODO: Limited user methods
    server: undefined, // TODO: Limited server methods

    // TODO: think of a better term for this as it will also refer to queries
    channels: undefined, // TODO: Limited access to panels list

    // Event managers for plugins
    components: {
        EventComponent: function(event_source, proxy_event_name) {
            function proxyEvent(event_name, event_data) {
                if (proxy_event_name !== 'all') {
                    event_data = event_name.event_data;
                    event_name = event_name.event_name
                }
//console.log(proxy_event_name, event_name, event_data);
                this.trigger(event_name, event_data);
            }

            // The event we are to proxy
            proxy_event_name = proxy_event_name || 'all';

            _.extend(this, Backbone.Events);
            this._source = event_source;

            // Proxy the events to this dispatcher
            event_source.on(proxy_event_name, proxyEvent, this);

            // Clean up this object
            this.dispose = function () {
                event_source.off(proxy_event_name, proxyEvent);
                this.off();
                delete this.event_source;
            };
        },

        Network: function(connection_id) {
            var connection_event;

            if (typeof connection_id !== 'undefined') {
                connection_event = 'connection:' + connection_id.toString();
            }

            var obj = new this.EventComponent(_kiwi.gateway, connection_event);
            var funcs = {
                kiwi: 'kiwi', raw: 'raw', kick: 'kick', topic: 'topic',
                part: 'part', join: 'join', action: 'action', ctcp: 'ctcp',
                notice: 'notice', msg: 'privmsg', changeNick: 'changeNick'
            };

            // Proxy each gateway method
            _.each(funcs, function(gateway_fn, func_name) {
                obj[func_name] = function() {
                    var fn_name = gateway_fn;

                    // Add connection_id to the argument list
                    var args = Array.prototype.slice.call(arguments, 0);
                    args.unshift(connection_id);

                    // Call the gateway function on behalf of this connection
                    return _kiwi.gateway[fn_name].apply(_kiwi.gateway, args);
                };
            });

            return obj;
        },

        ControlInput: function() {
            var obj = new this.EventComponent(_kiwi.app.controlbox);
            var funcs = {
                processInput: 'run', addPluginIcon: 'addPluginIcon'
            };

            _.each(funcs, function(controlbox_fn, func_name) {
                obj[func_name] = function() {
                    var fn_name = controlbox_fn;
                    return _kiwi.app.controlbox[fn_name].apply(_kiwi.app.controlbox, arguments);
                };
            });

            return obj;
        }
    },

    // Entry point to start the kiwi application
    start: function (opts, callback) {
        var continueStart, locale;
        opts = opts || {};

        // Load the plugin manager
        _kiwi.global.plugins = new _kiwi.model.PluginManager();

        // Set up the settings datastore
        _kiwi.global.settings = _kiwi.model.DataStore.instance('kiwi.settings');
        _kiwi.global.settings.load();

        continueStart = function (locale, s, xhr) {
            if (locale) {
                _kiwi.global.i18n = new Jed({locale_data: locale, domain: xhr.getResponseHeader('Content-Language')});
            } else {
                _kiwi.global.i18n = new Jed();
            }

            _kiwi.app = new _kiwi.model.Application(opts);

            if (opts.kiwi_server) {
                _kiwi.app.kiwi_server = opts.kiwi_server;
            }

            // Start the client up
            _kiwi.app.start();

            callback && callback();
        };

        locale = _kiwi.global.settings.get('locale');
        if (!locale) {
            $.getJSON(opts.base_path + '/assets/locales/magic.json', continueStart);
        } else {
            $.getJSON(opts.base_path + '/assets/locales/' + locale + '.json', continueStart);
        }
    }
};

// If within a closure, expose the kiwi globals
if (typeof global !== 'undefined') {
    global.kiwi = _kiwi.global;
} else {
    // Not within a closure so set a var in the current scope
    var kiwi = _kiwi.global;
}<commit_msg>update to app.js with some crap to test<commit_after>// Holds anything kiwi client specific (ie. front, gateway, _kiwi.plugs..)
/**
*   @namespace blah more comments
*/
var _kiwi = {};

_kiwi.model = {};
_kiwi.view = {};
_kiwi.applets = {};

/**
 * A global container for third party access
 * Will be used to access a limited subset of kiwi functionality
 * and data (think: plugins)
 */
_kiwi.global = {
    settings: undefined, // Instance of _kiwi.model.DataStore
    plugins: undefined,
    utils: undefined, // TODO: Re-usable methods
    user: undefined, // TODO: Limited user methods
    server: undefined, // TODO: Limited server methods

    // TODO: think of a better term for this as it will also refer to queries
    channels: undefined, // TODO: Limited access to panels list

    // Event managers for plugins
    components: {
        EventComponent: function(event_source, proxy_event_name) {
            function proxyEvent(event_name, event_data) {
                if (proxy_event_name !== 'all') {
                    event_data = event_name.event_data;
                    event_name = event_name.event_name
                }
//console.log(proxy_event_name, event_name, event_data);
                this.trigger(event_name, event_data);
            }

            // The event we are to proxy
            proxy_event_name = proxy_event_name || 'all';

            _.extend(this, Backbone.Events);
            this._source = event_source;

            // Proxy the events to this dispatcher
            event_source.on(proxy_event_name, proxyEvent, this);

            // Clean up this object
            this.dispose = function () {
                event_source.off(proxy_event_name, proxyEvent);
                this.off();
                delete this.event_source;
            };
        },

        Network: function(connection_id) {
            var connection_event;

            if (typeof connection_id !== 'undefined') {
                connection_event = 'connection:' + connection_id.toString();
            }

            var obj = new this.EventComponent(_kiwi.gateway, connection_event);
            var funcs = {
                kiwi: 'kiwi', raw: 'raw', kick: 'kick', topic: 'topic',
                part: 'part', join: 'join', action: 'action', ctcp: 'ctcp',
                notice: 'notice', msg: 'privmsg', changeNick: 'changeNick'
            };

            // Proxy each gateway method
            _.each(funcs, function(gateway_fn, func_name) {
                obj[func_name] = function() {
                    var fn_name = gateway_fn;

                    // Add connection_id to the argument list
                    var args = Array.prototype.slice.call(arguments, 0);
                    args.unshift(connection_id);

                    // Call the gateway function on behalf of this connection
                    return _kiwi.gateway[fn_name].apply(_kiwi.gateway, args);
                };
            });

            return obj;
        },

        ControlInput: function() {
            var obj = new this.EventComponent(_kiwi.app.controlbox);
            var funcs = {
                processInput: 'run', addPluginIcon: 'addPluginIcon'
            };

            _.each(funcs, function(controlbox_fn, func_name) {
                obj[func_name] = function() {
                    var fn_name = controlbox_fn;
                    return _kiwi.app.controlbox[fn_name].apply(_kiwi.app.controlbox, arguments);
                };
            });

            return obj;
        }
    },

    // Entry point to start the kiwi application
    start: function (opts, callback) {
        var continueStart, locale;
        opts = opts || {};

        // Load the plugin manager
        _kiwi.global.plugins = new _kiwi.model.PluginManager();

        // Set up the settings datastore
        _kiwi.global.settings = _kiwi.model.DataStore.instance('kiwi.settings');
        _kiwi.global.settings.load();

        continueStart = function (locale, s, xhr) {
            if (locale) {
                _kiwi.global.i18n = new Jed({locale_data: locale, domain: xhr.getResponseHeader('Content-Language')});
            } else {
                _kiwi.global.i18n = new Jed();
            }

            _kiwi.app = new _kiwi.model.Application(opts);

            if (opts.kiwi_server) {
                _kiwi.app.kiwi_server = opts.kiwi_server;
            }

            // Start the client up
            _kiwi.app.start();

            callback && callback();
        };

        locale = _kiwi.global.settings.get('locale');
        if (!locale) {
            $.getJSON(opts.base_path + '/assets/locales/magic.json', continueStart);
        } else {
            $.getJSON(opts.base_path + '/assets/locales/' + locale + '.json', continueStart);
        }
    }
};

// If within a closure, expose the kiwi globals
if (typeof global !== 'undefined') {
    global.kiwi = _kiwi.global;
} else {
    // Not within a closure so set a var in the current scope
    var kiwi = _kiwi.global;
}
<|endoftext|>