<commit_before>/**
 * @module ol/source/BingMaps
 */

import {createFromTileUrlFunctions} from '../tileurlfunction.js';
import {applyTransform, intersects} from '../extent.js';
import {jsonp as requestJSONP} from '../net.js';
import {get as getProjection, getTransformFromProjections} from '../proj.js';
import SourceState from '../source/State.js';
import TileImage from '../source/TileImage.js';
import {createOrUpdate, quadKey} from '../tilecoord.js';
import {createXYZ, extentFromProjection} from '../tilegrid.js';

/**
 * The attribution containing a link to the Microsoft® Bing™ Maps Platform APIs’
 * Terms Of Use.
 * @const
 * @type {string}
 */
const TOS_ATTRIBUTION = '' +
      'Terms of Use';

/**
 * @typedef {Object} Options
 * @property {number} [cacheSize=2048] Cache size.
 * @property {boolean} [hidpi=false] If `true` hidpi tiles will be requested.
 * @property {string} [culture='en-us'] Culture code.
 * @property {string} key Bing Maps API key. Get yours at http://www.bingmapsportal.com/.
 * @property {string} imagerySet Type of imagery.
 * @property {number} [maxZoom=21] Max zoom. Default is what's advertized by the BingMaps service.
 * @property {number} [reprojectionErrorThreshold=0.5] Maximum allowed reprojection error (in pixels).
 * Higher values can increase reprojection performance, but decrease precision.
 * @property {module:ol/Tile~LoadFunction} [tileLoadFunction] Optional function to load a tile given a URL. The default is
 * ```js
 * function(imageTile, src) {
 *   imageTile.getImage().src = src;
 * };
 * ```
 * @property {boolean} [wrapX=true] Whether to wrap the world horizontally.
 * @property {number} [transition] Duration of the opacity transition for rendering.
 * To disable the opacity transition, pass `transition: 0`.
 */

class BingMaps extends TileImage {
  /**
   * @classdesc
   * Layer source for Bing Maps tile data.
   *
   * @param {module:ol/source/BingMaps~Options=} options Bing Maps options.
   * @api
   */
  constructor(options) {

    /**
     * @private
     * @type {boolean}
     */
    this.hidpi_ = options.hidpi !== undefined ? options.hidpi : false;

    super({
      cacheSize: options.cacheSize,
      crossOrigin: 'anonymous',
      opaque: true,
      projection: getProjection('EPSG:3857'),
      reprojectionErrorThreshold: options.reprojectionErrorThreshold,
      state: SourceState.LOADING,
      tileLoadFunction: options.tileLoadFunction,
      tilePixelRatio: this.hidpi_ ? 2 : 1,
      wrapX: options.wrapX !== undefined ? options.wrapX : true,
      transition: options.transition
    });

    /**
     * @private
     * @type {string}
     */
    this.culture_ = options.culture !== undefined ? options.culture : 'en-us';

    /**
     * @private
     * @type {number}
     */
    this.maxZoom_ = options.maxZoom !== undefined ? options.maxZoom : -1;

    /**
     * @private
     * @type {string}
     */
    this.apiKey_ = options.key;

    /**
     * @private
     * @type {string}
     */
    this.imagerySet_ = options.imagerySet;

    const url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/' +
        this.imagerySet_ +
        '?uriScheme=https&include=ImageryProviders&key=' + this.apiKey_ +
        '&c=' + this.culture_;

    requestJSONP(url, this.handleImageryMetadataResponse.bind(this), undefined,
      'jsonp');

  }

  /**
   * Get the api key used for this source.
   *
   * @return {string} The api key.
   * @api
   */
  getApiKey() {
    return this.apiKey_;
  }

  /**
   * Get the imagery set associated with this source.
   *
   * @return {string} The imagery set.
   * @api
   */
  getImagerySet() {
    return this.imagerySet_;
  }

  /**
   * @param {BingMapsImageryMetadataResponse} response Response.
   */
  handleImageryMetadataResponse(response) {
    if (response.statusCode != 200 ||
        response.statusDescription != 'OK' ||
        response.authenticationResultCode != 'ValidCredentials' ||
        response.resourceSets.length != 1 ||
        response.resourceSets[0].resources.length != 1) {
      this.setState(SourceState.ERROR);
      return;
    }

    const resource = response.resourceSets[0].resources[0];
    const maxZoom = this.maxZoom_ == -1 ? resource.zoomMax : this.maxZoom_;

    const sourceProjection = this.getProjection();
    const extent = extentFromProjection(sourceProjection);
    const tileSize = resource.imageWidth == resource.imageHeight ?
      resource.imageWidth : [resource.imageWidth, resource.imageHeight];
    const tileGrid = createXYZ({
      extent: extent,
      minZoom: resource.zoomMin,
      maxZoom: maxZoom,
      tileSize: tileSize / (this.hidpi_ ? 2 : 1)
    });
    this.tileGrid = tileGrid;

    const culture = this.culture_;
    const hidpi = this.hidpi_;
    this.tileUrlFunction = createFromTileUrlFunctions(
      resource.imageUrlSubdomains.map(function(subdomain) {
        const quadKeyTileCoord = [0, 0, 0];
        const imageUrl = resource.imageUrl
          .replace('{subdomain}', subdomain)
          .replace('{culture}', culture);
        return (
          /**
           * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate.
           * @param {number} pixelRatio Pixel ratio.
           * @param {module:ol/proj/Projection} projection Projection.
           * @return {string|undefined} Tile URL.
           */
          function(tileCoord, pixelRatio, projection) {
            if (!tileCoord) {
              return undefined;
            } else {
              createOrUpdate(tileCoord[0], tileCoord[1], -tileCoord[2] - 1, quadKeyTileCoord);
              let url = imageUrl;
              if (hidpi) {
                url += '&dpi=d1&device=mobile';
              }
              return url.replace('{quadkey}', quadKey(quadKeyTileCoord));
            }
          }
        );
      }));

    if (resource.imageryProviders) {
      const transform = getTransformFromProjections(
        getProjection('EPSG:4326'), this.getProjection());

      this.setAttributions(function(frameState) {
        const attributions = [];
        const zoom = frameState.viewState.zoom;
        resource.imageryProviders.map(function(imageryProvider) {
          let intersecting = false;
          const coverageAreas = imageryProvider.coverageAreas;
          for (let i = 0, ii = coverageAreas.length; i < ii; ++i) {
            const coverageArea = coverageAreas[i];
            if (zoom >= coverageArea.zoomMin && zoom <= coverageArea.zoomMax) {
              const bbox = coverageArea.bbox;
              const epsg4326Extent = [bbox[1], bbox[0], bbox[3], bbox[2]];
              const extent = applyTransform(epsg4326Extent, transform);
              if (intersects(extent, frameState.extent)) {
                intersecting = true;
                break;
              }
            }
          }
          if (intersecting) {
            attributions.push(imageryProvider.attribution);
          }
        });

        attributions.push(TOS_ATTRIBUTION);
        return attributions;
      });
    }

    this.setState(SourceState.READY);
  }
}

export default BingMaps;
<commit_msg>Call super before accessing this in BingMaps source<commit_after>/**
 * @module ol/source/BingMaps
 */

import {createFromTileUrlFunctions} from '../tileurlfunction.js';
import {applyTransform, intersects} from '../extent.js';
import {jsonp as requestJSONP} from '../net.js';
import {get as getProjection, getTransformFromProjections} from '../proj.js';
import SourceState from '../source/State.js';
import TileImage from '../source/TileImage.js';
import {createOrUpdate, quadKey} from '../tilecoord.js';
import {createXYZ, extentFromProjection} from '../tilegrid.js';

/**
 * The attribution containing a link to the Microsoft® Bing™ Maps Platform APIs’
 * Terms Of Use.
 * @const
 * @type {string}
 */
const TOS_ATTRIBUTION = '' +
      'Terms of Use';

/**
 * @typedef {Object} Options
 * @property {number} [cacheSize=2048] Cache size.
 * @property {boolean} [hidpi=false] If `true` hidpi tiles will be requested.
 * @property {string} [culture='en-us'] Culture code.
 * @property {string} key Bing Maps API key. Get yours at http://www.bingmapsportal.com/.
 * @property {string} imagerySet Type of imagery.
 * @property {number} [maxZoom=21] Max zoom. Default is what's advertized by the BingMaps service.
 * @property {number} [reprojectionErrorThreshold=0.5] Maximum allowed reprojection error (in pixels).
 * Higher values can increase reprojection performance, but decrease precision.
 * @property {module:ol/Tile~LoadFunction} [tileLoadFunction] Optional function to load a tile given a URL. The default is
 * ```js
 * function(imageTile, src) {
 *   imageTile.getImage().src = src;
 * };
 * ```
 * @property {boolean} [wrapX=true] Whether to wrap the world horizontally.
 * @property {number} [transition] Duration of the opacity transition for rendering.
 * To disable the opacity transition, pass `transition: 0`.
 */

class BingMaps extends TileImage {
  /**
   * @classdesc
   * Layer source for Bing Maps tile data.
   *
   * @param {module:ol/source/BingMaps~Options=} options Bing Maps options.
   * @api
   */
  constructor(options) {

    const hidpi = options.hidpi !== undefined ? options.hidpi : false;

    super({
      cacheSize: options.cacheSize,
      crossOrigin: 'anonymous',
      opaque: true,
      projection: getProjection('EPSG:3857'),
      reprojectionErrorThreshold: options.reprojectionErrorThreshold,
      state: SourceState.LOADING,
      tileLoadFunction: options.tileLoadFunction,
      tilePixelRatio: hidpi ? 2 : 1,
      wrapX: options.wrapX !== undefined ? options.wrapX : true,
      transition: options.transition
    });

    /**
     * @private
     * @type {boolean}
     */
    this.hidpi_ = hidpi;

    /**
     * @private
     * @type {string}
     */
    this.culture_ = options.culture !== undefined ? options.culture : 'en-us';

    /**
     * @private
     * @type {number}
     */
    this.maxZoom_ = options.maxZoom !== undefined ? options.maxZoom : -1;

    /**
     * @private
     * @type {string}
     */
    this.apiKey_ = options.key;

    /**
     * @private
     * @type {string}
     */
    this.imagerySet_ = options.imagerySet;

    const url = 'https://dev.virtualearth.net/REST/v1/Imagery/Metadata/' +
        this.imagerySet_ +
        '?uriScheme=https&include=ImageryProviders&key=' + this.apiKey_ +
        '&c=' + this.culture_;

    requestJSONP(url, this.handleImageryMetadataResponse.bind(this), undefined,
      'jsonp');

  }

  /**
   * Get the api key used for this source.
   *
   * @return {string} The api key.
   * @api
   */
  getApiKey() {
    return this.apiKey_;
  }

  /**
   * Get the imagery set associated with this source.
   *
   * @return {string} The imagery set.
   * @api
   */
  getImagerySet() {
    return this.imagerySet_;
  }

  /**
   * @param {BingMapsImageryMetadataResponse} response Response.
   */
  handleImageryMetadataResponse(response) {
    if (response.statusCode != 200 ||
        response.statusDescription != 'OK' ||
        response.authenticationResultCode != 'ValidCredentials' ||
        response.resourceSets.length != 1 ||
        response.resourceSets[0].resources.length != 1) {
      this.setState(SourceState.ERROR);
      return;
    }

    const resource = response.resourceSets[0].resources[0];
    const maxZoom = this.maxZoom_ == -1 ? resource.zoomMax : this.maxZoom_;

    const sourceProjection = this.getProjection();
    const extent = extentFromProjection(sourceProjection);
    const tileSize = resource.imageWidth == resource.imageHeight ?
      resource.imageWidth : [resource.imageWidth, resource.imageHeight];
    const tileGrid = createXYZ({
      extent: extent,
      minZoom: resource.zoomMin,
      maxZoom: maxZoom,
      tileSize: tileSize / (this.hidpi_ ? 2 : 1)
    });
    this.tileGrid = tileGrid;

    const culture = this.culture_;
    const hidpi = this.hidpi_;
    this.tileUrlFunction = createFromTileUrlFunctions(
      resource.imageUrlSubdomains.map(function(subdomain) {
        const quadKeyTileCoord = [0, 0, 0];
        const imageUrl = resource.imageUrl
          .replace('{subdomain}', subdomain)
          .replace('{culture}', culture);
        return (
          /**
           * @param {module:ol/tilecoord~TileCoord} tileCoord Tile coordinate.
           * @param {number} pixelRatio Pixel ratio.
           * @param {module:ol/proj/Projection} projection Projection.
           * @return {string|undefined} Tile URL.
           */
          function(tileCoord, pixelRatio, projection) {
            if (!tileCoord) {
              return undefined;
            } else {
              createOrUpdate(tileCoord[0], tileCoord[1], -tileCoord[2] - 1, quadKeyTileCoord);
              let url = imageUrl;
              if (hidpi) {
                url += '&dpi=d1&device=mobile';
              }
              return url.replace('{quadkey}', quadKey(quadKeyTileCoord));
            }
          }
        );
      }));

    if (resource.imageryProviders) {
      const transform = getTransformFromProjections(
        getProjection('EPSG:4326'), this.getProjection());

      this.setAttributions(function(frameState) {
        const attributions = [];
        const zoom = frameState.viewState.zoom;
        resource.imageryProviders.map(function(imageryProvider) {
          let intersecting = false;
          const coverageAreas = imageryProvider.coverageAreas;
          for (let i = 0, ii = coverageAreas.length; i < ii; ++i) {
            const coverageArea = coverageAreas[i];
            if (zoom >= coverageArea.zoomMin && zoom <= coverageArea.zoomMax) {
              const bbox = coverageArea.bbox;
              const epsg4326Extent = [bbox[1], bbox[0], bbox[3], bbox[2]];
              const extent = applyTransform(epsg4326Extent, transform);
              if (intersects(extent, frameState.extent)) {
                intersecting = true;
                break;
              }
            }
          }
          if (intersecting) {
            attributions.push(imageryProvider.attribution);
          }
        });

        attributions.push(TOS_ATTRIBUTION);
        return attributions;
      });
    }

    this.setState(SourceState.READY);
  }
}

export default BingMaps;
<|endoftext|>


<commit_before>module.exports = function(app){
    "use strict";

    var user = {},
        bridgetown = require('bridgetown-api'),
        grasshopper = require('grasshopper-core'),
        middleware = bridgetown.middleware,
        Response = bridgetown.Response,
        Request = require('../helpers/request'),
        routePrefix = (app.get('grasshopper route prefix')) ? app.get('grasshopper route prefix') : '',
        request = new Request();

    function writeApiResponse(httpResponse, promise){
        var response = new Response(httpResponse);
        response.writeFromPromise(promise);
    }

    user.getCurrentUser = function (httpRequest, httpResponse){
        var promise = grasshopper.request(httpRequest.bridgetown.token).users.current();
        writeApiResponse(httpResponse, promise);
    };

    user.getById = function (httpRequest, httpResponse){
        var promise = grasshopper.request(httpRequest.bridgetown.token).users.getById(httpRequest.params.id);
        writeApiResponse(httpResponse, promise);
    };

    user.deleteById = function(httpRequest, httpResponse){
        var promise = grasshopper.request(httpRequest.bridgetown.token).users.deleteById(httpRequest.params.id);
        writeApiResponse(httpResponse, promise);
    };

    user.getList = function (httpRequest, httpResponse){
        var limit = request.getListPageSize(httpRequest),
            skip = request.getListSkipSize(httpRequest),
            promise = grasshopper.request(httpRequest.bridgetown.token).users.list({
                    query: {},
                    limit: limit,
                    skip: skip
                });

        writeApiResponse(httpResponse, promise);
    };

    user.update = function(httpRequest, httpResponse){
        var promise = grasshopper.request(httpRequest.bridgetown.token).users.update(httpRequest.body);
        writeApiResponse(httpResponse, promise);
    };

    user.create = function(httpRequest, httpResponse){
        var promise = grasshopper.request(httpRequest.bridgetown.token).users.insert(httpRequest.body);
        writeApiResponse(httpResponse, promise);
    };

    user.updatePermission = function(httpRequest, httpResponse){
        var promise = grasshopper.request(httpRequest.bridgetown.token).users.savePermissions(httpRequest.params.id, httpRequest.body);
        writeApiResponse(httpResponse, promise);
    };

    user.deletePermission = function(httpRequest, httpResponse){
        var promise = grasshopper.request(httpRequest.bridgetown.token).users.deletePermissions(httpRequest.params.id, httpRequest.params.nodeid);
        writeApiResponse(httpResponse, promise);
    };

    user.query = function (httpRequest, httpResponse){
        var promise = grasshopper.request(httpRequest.bridgetown.token).users.query(httpRequest.body);
        writeApiResponse(httpResponse, promise);
    };

    app.get(routePrefix + '/users', [middleware.authorization, middleware.authToken, user.getList]);
    app.get(routePrefix + '/user', [middleware.authorization, middleware.authToken, user.getCurrentUser]);
    app.get(routePrefix + '/users/:id', [middleware.authorization, middleware.authToken, user.getById]);

    app.put(routePrefix + '/users', [middleware.authorization, middleware.authToken, user.update]);
    app.put(routePrefix + '/user', [middleware.authorization, middleware.authToken, user.update]);
    app.put(routePrefix + '/users/:id', [middleware.authorization, middleware.authToken, user.update]);

    app.post(routePrefix + '/users/:id/permissions', [middleware.authorization, middleware.authToken, user.updatePermission]);
    app.post(routePrefix + '/users', [middleware.authorization, middleware.authToken, user.create]);
    app.post(routePrefix + '/users/query', [middleware.authorization, middleware.authToken, user.query]);

    app.delete(routePrefix + '/users/:id', [middleware.authorization, middleware.authToken, user.deleteById]);
    app.delete(routePrefix + '/users/:id/permissions/:nodeid', [middleware.authorization, middleware.authToken, user.deletePermission]);

    return user;
};
<commit_msg>Fixed user permissions bug that was introduced by adding grasshopper-core.<commit_after>module.exports = function(app){
    "use strict";

    var user = {},
        bridgetown = require('bridgetown-api'),
        grasshopper = require('grasshopper-core'),
        middleware = bridgetown.middleware,
        Response = bridgetown.Response,
        Request = require('../helpers/request'),
        routePrefix = (app.get('grasshopper route prefix')) ? app.get('grasshopper route prefix') : '',
        request = new Request();

    function writeApiResponse(httpResponse, promise){
        var response = new Response(httpResponse);
        response.writeFromPromise(promise);
    }

    user.getCurrentUser = function (httpRequest, httpResponse){
        var promise = grasshopper.request(httpRequest.bridgetown.token).users.current();
        writeApiResponse(httpResponse, promise);
    };

    user.getById = function (httpRequest, httpResponse){
        var promise = grasshopper.request(httpRequest.bridgetown.token).users.getById(httpRequest.params.id);
        writeApiResponse(httpResponse, promise);
    };

    user.deleteById = function(httpRequest, httpResponse){
        var promise = grasshopper.request(httpRequest.bridgetown.token).users.deleteById(httpRequest.params.id);
        writeApiResponse(httpResponse, promise);
    };

    user.getList = function (httpRequest, httpResponse){
        var limit = request.getListPageSize(httpRequest),
            skip = request.getListSkipSize(httpRequest),
            promise = grasshopper.request(httpRequest.bridgetown.token).users.list({
                    query: {},
                    limit: limit,
                    skip: skip
                });

        writeApiResponse(httpResponse, promise);
    };

    user.update = function(httpRequest, httpResponse){
        var promise = grasshopper.request(httpRequest.bridgetown.token).users.update(httpRequest.body);
        writeApiResponse(httpResponse, promise);
    };

    user.create = function(httpRequest, httpResponse){
        var promise = grasshopper.request(httpRequest.bridgetown.token).users.insert(httpRequest.body);
        writeApiResponse(httpResponse, promise);
    };

    user.updatePermission = function(httpRequest, httpResponse){
        var promise = grasshopper.request(httpRequest.bridgetown.token).users.savePermissions(httpRequest.params.id, httpRequest.body.nodeid, httpRequest.role);
        writeApiResponse(httpResponse, promise);
    };

    user.deletePermission = function(httpRequest, httpResponse){
        var promise = grasshopper.request(httpRequest.bridgetown.token).users.deletePermissions(httpRequest.params.id, httpRequest.params.nodeid);
        writeApiResponse(httpResponse, promise);
    };

    user.query = function (httpRequest, httpResponse){
        var promise = grasshopper.request(httpRequest.bridgetown.token).users.query(httpRequest.body);
        writeApiResponse(httpResponse, promise);
    };

    app.get(routePrefix + '/users', [middleware.authorization, middleware.authToken, user.getList]);
    app.get(routePrefix + '/user', [middleware.authorization, middleware.authToken, user.getCurrentUser]);
    app.get(routePrefix + '/users/:id', [middleware.authorization, middleware.authToken, user.getById]);

    app.put(routePrefix + '/users', [middleware.authorization, middleware.authToken, user.update]);
    app.put(routePrefix + '/user', [middleware.authorization, middleware.authToken, user.update]);
    app.put(routePrefix + '/users/:id', [middleware.authorization, middleware.authToken, user.update]);

    app.post(routePrefix + '/users/:id/permissions', [middleware.authorization, middleware.authToken, user.updatePermission]);
    app.post(routePrefix + '/users', [middleware.authorization, middleware.authToken, user.create]);
    app.post(routePrefix + '/users/query', [middleware.authorization, middleware.authToken, user.query]);

    app.delete(routePrefix + '/users/:id', [middleware.authorization, middleware.authToken, user.deleteById]);
    app.delete(routePrefix + '/users/:id/permissions/:nodeid', [middleware.authorization, middleware.authToken, user.deletePermission]);

    return user;
};
<|endoftext|>


<commit_before>/* jshint node:true, expr:true */
'use strict';

var _q = require('q');

module.exports = {

    /**
     * Creates a handler that can be used to notify the test framework that all
     * assertions have been executed successfully.
     *
     * @param {Function} done The callback method used to notify the framework
     *                        of successful test execution.
     * @return {Function} A wrapper function that signals successful execution
     *                    to the test framework.
     */
    getNotifySuccessHandler: function(done) {
        if(typeof done !== 'function') {
            throw new Error('invalid done callback method specified (arg #1)');
        }

        return function(){
            done();
        }
    },

    /**
     * Creates a handler that can be used to notify the test framework that one
     * or more assertions failed.
     *
     * @param {Function} done The callback method used to notify the framework
     *                        of an error in test execution.
     * @return {Function} A wrapper function that signals an error when
     *                    executing test assertions.
     */
    getNotifyFailureHandler: function(done) {
        if(typeof done !== 'function') {
            throw new Error('invalid done callback method specified (arg #1)');
        }

        return function(err) {
            done(err);
        }
    },

    /**
     * Helper method that executes a function and rejects/resolves the passed
     * in deferred object based on whether or not the function executed without
     * errors. If the function returns a promise, eventual rejection/resolution
     * will be tied to the resolution/rejection of that promise (unless an
     * exception is thrown first).
     *
     * @param {Function} tasks A parameterless function that encapsulates the
     *                   tasks to be executed.
     * @param {Object} [deferred] A deferred object that will be rejected or
     *                 resolved based on whether or not the tasks execute
     *                 successfully. If omitted, a new deferred object will
     *                 be created and returned.
     * @return {Object} A promise that will be rejected or resolved based on
     *                  the status of execution of the tasks.
     */
    runDeferred: function(expectations, deferred) {
        if(typeof expectations !== 'function') {
            throw new Error('invalid expectation wrapper specified (arg #1)');
        }

       deferred = deferred || _q.defer();
       try {
           var ret = expectations();
           // Poor man's check to see if the return value is a promise.
           if(ret && typeof ret.then === 'function') {
               ret.then(function() {
                   deferred.resolve();
               }, function(err) {
                   deferred.reject(err);
               });
           } else {
               deferred.resolve();
           }
       } catch(ex) {
           if(ex.message) {
               deferred.reject(ex.message);
           } else {
               deferred.reject(_util.inspect(ex));
           }
       }
       return deferred;
    },

    /**
     * Gets a method that executes the specified task after a delay. The method
     * also returns a promise that will be resolved or rejected based on the
     * whether or not the task executed without errors.
     *
     * @param {Function} task The task to execute after delay, wrapped in a
     *                   parameterless function.
     * @param {Number} delay The delay (in milliseconds) after which the task
     *                 will be executed.
     *
     * @return {Function} A function that can be used to trigger the delayed
     *                    execution of the task. This function will return
     *                    a promise after execution.
     */
    getDelayedRunner: function(task, delay) {
        if(typeof task !== 'function') {
            throw new Error('invalid task wrapper specified (arg #1)');
        }

        if(typeof delay !== 'number' || delay < 0) {
            throw new Error('invalid delay specified - should be a non negative number (arg #2)');
        }
        return function() {
            var deferred = _q.defer();
            setTimeout(function(){
                module.exports.runDeferred(task, deferred);
            }, delay);
            return deferred.promise;
        };
    }
}
<commit_msg>Delayed and deferred runners now return the results of processing when the promise is resolved<commit_after>/* jshint node:true, expr:true */
'use strict';

var _q = require('q');

module.exports = {

    /**
     * Creates a handler that can be used to notify the test framework that all
     * assertions have been executed successfully.
     *
     * @param {Function} done The callback method used to notify the framework
     *                        of successful test execution.
     * @return {Function} A wrapper function that signals successful execution
     *                    to the test framework.
     */
    getNotifySuccessHandler: function(done) {
        if(typeof done !== 'function') {
            throw new Error('invalid done callback method specified (arg #1)');
        }

        return function(){
            done();
        }
    },

    /**
     * Creates a handler that can be used to notify the test framework that one
     * or more assertions failed.
     *
     * @param {Function} done The callback method used to notify the framework
     *                        of an error in test execution.
     * @return {Function} A wrapper function that signals an error when
     *                    executing test assertions.
     */
    getNotifyFailureHandler: function(done) {
        if(typeof done !== 'function') {
            throw new Error('invalid done callback method specified (arg #1)');
        }

        return function(err) {
            done(err);
        }
    },

    /**
     * Helper method that executes a function and rejects/resolves the passed
     * in deferred object based on whether or not the function executed without
     * errors. If the function returns a promise, eventual rejection/resolution
     * will be tied to the resolution/rejection of that promise (unless an
     * exception is thrown first).
     *
     * @param {Function} tasks A parameterless function that encapsulates the
     *                   tasks to be executed.
     * @param {Object} [deferred] A deferred object that will be rejected or
     *                 resolved based on whether or not the tasks execute
     *                 successfully. If omitted, a new deferred object will
     *                 be created and returned.
     * @return {Object} A promise that will be rejected or resolved based on
     *                  the status of execution of the tasks.
     */
    runDeferred: function(expectations, deferred) {
        if(typeof expectations !== 'function') {
            throw new Error('invalid expectation wrapper specified (arg #1)');
        }

       deferred = deferred || _q.defer();
       try {
           var ret = expectations();
           // Poor man's check to see if the return value is a promise.
           if(ret && typeof ret.then === 'function') {
               ret.then(function(data) {
                   deferred.resolve(data);
               }, function(err) {
                   deferred.reject(err);
               });
           } else {
               deferred.resolve(ret);
           }
       } catch(ex) {
           if(ex.message) {
               deferred.reject(ex.message);
           } else {
               deferred.reject(_util.inspect(ex));
           }
       }
       return deferred;
    },

    /**
     * Gets a method that executes the specified task after a delay. The method
     * also returns a promise that will be resolved or rejected based on the
     * whether or not the task executed without errors.
     *
     * @param {Function} task The task to execute after delay, wrapped in a
     *                   parameterless function.
     * @param {Number} delay The delay (in milliseconds) after which the task
     *                 will be executed.
     *
     * @return {Function} A function that can be used to trigger the delayed
     *                    execution of the task. This function will return
     *                    a promise after execution.
     */
    getDelayedRunner: function(task, delay) {
        if(typeof task !== 'function') {
            throw new Error('invalid task wrapper specified (arg #1)');
        }

        if(typeof delay !== 'number' || delay < 0) {
            throw new Error('invalid delay specified - should be a non negative number (arg #2)');
        }

        var taskWrapper = function(data) {
            return taskWrapper(data);
        };
        return function() {
            var deferred = _q.defer();
            setTimeout(function(){
                module.exports.runDeferred(taskWrapper, deferred);
            }, delay);
            return deferred.promise;
        };
    }
}
<|endoftext|>


<commit_before>/**
 * modules/analytics data store: goals tests.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * Internal dependencies
 */
import API from 'googlesitekit-api';
import { STORE_NAME } from './constants';
import {
	createTestRegistry,
	muteConsole,
	subscribeUntil,
	unsubscribeFromAll,
} from 'tests/js/utils';
import * as fixtures from './__fixtures__';

describe( 'modules/analytics goals', () => {
	let registry;

	beforeAll( () => {
		API.setUsingCache( false );
	} );

	beforeEach( () => {
		registry = createTestRegistry();
	} );

	afterEach( () => {
		unsubscribeFromAll( registry );
	} );

	afterAll( () => {
		API.setUsingCache( true );
	} );

	describe( 'selectors', () => {
		describe( 'getGoals', () => {
			it( 'uses a resolver to make a network request', async () => {
				fetchMock.getOnce(
					/^\/google-site-kit\/v1\/modules\/analytics\/data\/goals/,
					{ body: fixtures.goals, status: 200 }
				);

				const initialGoals = registry.select( STORE_NAME ).getGoals();

				expect( initialGoals ).toBeUndefined();
				await subscribeUntil(
					registry,
					() => registry.select( STORE_NAME ).isFetchingGetGoals() === false,
				);

				const goals = registry.select( STORE_NAME ).getGoals();

				expect( fetchMock ).toHaveFetchedTimes( 1 );
				expect( goals ).toEqual( fixtures.goals );
			} );

			it( 'does not make a network request if goals are already present', async () => {
				// Load data into this store so there are matches for the data we're about to select,
				// even though the selector hasn't fulfilled yet.
				registry.dispatch( STORE_NAME ).receiveGetGoals( fixtures.goals );

				const goals = registry.select( STORE_NAME ).getGoals();

				await subscribeUntil( registry, () => registry
					.select( STORE_NAME )
					.hasFinishedResolution( 'getGoals', [] )
				);

				expect( fetchMock ).not.toHaveFetched();
				expect( goals ).toEqual( fixtures.goals );
			} );

			it( 'dispatches an error if the request fails', async () => {
				const response = {
					code: 'internal_server_error',
					message: 'Internal server error',
					data: { status: 500 },
				};

				fetchMock.getOnce(
					/^\/google-site-kit\/v1\/modules\/analytics\/data\/goals/,
					{ body: response, status: 500 }
				);

				muteConsole( 'error' );
				registry.select( STORE_NAME ).getGoals();
				await subscribeUntil(
					registry,
					() => registry.select( STORE_NAME ).isFetchingGetGoals() === false,
				);

				expect( fetchMock ).toHaveFetchedTimes( 1 );

				const goals = registry.select( STORE_NAME ).getGoals();
				expect( goals ).toBeUndefined();
			} );
		} );
	} );
} );
<commit_msg>Fix goals test after rebase with develop branch.<commit_after>/**
 * modules/analytics data store: goals tests.
 *
 * Site Kit by Google, Copyright 2020 Google LLC
 *
 * 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
 *
 *     https://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.
 */

/**
 * Internal dependencies
 */
import API from 'googlesitekit-api';
import { STORE_NAME } from './constants';
import {
	createTestRegistry,
	subscribeUntil,
	unsubscribeFromAll,
} from 'tests/js/utils';
import * as fixtures from './__fixtures__';

describe( 'modules/analytics goals', () => {
	let registry;

	beforeAll( () => {
		API.setUsingCache( false );
	} );

	beforeEach( () => {
		registry = createTestRegistry();
	} );

	afterEach( () => {
		unsubscribeFromAll( registry );
	} );

	afterAll( () => {
		API.setUsingCache( true );
	} );

	describe( 'selectors', () => {
		describe( 'getGoals', () => {
			it( 'uses a resolver to make a network request', async () => {
				fetchMock.getOnce(
					/^\/google-site-kit\/v1\/modules\/analytics\/data\/goals/,
					{ body: fixtures.goals, status: 200 }
				);

				const initialGoals = registry.select( STORE_NAME ).getGoals();

				expect( initialGoals ).toBeUndefined();
				await subscribeUntil(
					registry,
					() => registry.select( STORE_NAME ).isFetchingGetGoals() === false,
				);

				const goals = registry.select( STORE_NAME ).getGoals();

				expect( fetchMock ).toHaveFetchedTimes( 1 );
				expect( goals ).toEqual( fixtures.goals );
			} );

			it( 'does not make a network request if goals are already present', async () => {
				// Load data into this store so there are matches for the data we're about to select,
				// even though the selector hasn't fulfilled yet.
				registry.dispatch( STORE_NAME ).receiveGetGoals( fixtures.goals );

				const goals = registry.select( STORE_NAME ).getGoals();

				await subscribeUntil( registry, () => registry
					.select( STORE_NAME )
					.hasFinishedResolution( 'getGoals', [] )
				);

				expect( fetchMock ).not.toHaveFetched();
				expect( goals ).toEqual( fixtures.goals );
			} );

			it( 'dispatches an error if the request fails', async () => {
				const response = {
					code: 'internal_server_error',
					message: 'Internal server error',
					data: { status: 500 },
				};

				fetchMock.getOnce(
					/^\/google-site-kit\/v1\/modules\/analytics\/data\/goals/,
					{ body: response, status: 500 }
				);

				registry.select( STORE_NAME ).getGoals();
				await subscribeUntil(
					registry,
					() => registry.select( STORE_NAME ).isFetchingGetGoals() === false,
				);

				expect( fetchMock ).toHaveFetchedTimes( 1 );

				const goals = registry.select( STORE_NAME ).getGoals();
				expect( goals ).toBeUndefined();
				expect( console ).toHaveErrored();
			} );
		} );
	} );
} );
<|endoftext|>


<commit_before>'use strict';

// TYPEDEFS
//

/**
 * @typedef {{
 *   name: string
 * }}
 */
var Metadata;

// CONSTANTS
//

var R_JSDOC_BLOCK = /\/\*\*[\s\S]*\*\//m;
var R_NAMED_FN_NAME = /function\s*(\w*)/;
var R_ANONYMOUS_FN_VAR_NAME = /var\s(\w*)\s*=\s*function/;
var R_ANONYMOUS_FN_PROPERTY_NAME = /(\w+)(?=.*:.*function)/;

// HELPER METHODS
//

/**
 * @param {string} str The string to test against.
 * @param {RegExp} regex The RegExp to test for.
 * @return {string|null}
 */
function getFirstRegExMatch (str, regex) {
  var match = str.match(regex);
  return (match && match[1]) || null;
}

/**
 * @param {string} text The contents of a JavaScript file.
 * @return {Array.<metadata>}
 */
exports.getMetadata = function (text) {
  var jsDocBlocks = text.match(R_JSDOC_BLOCK);

  /** @type {Array.<metadata>} */
  var metadataList = [];

  jsDocBlocks.forEach(function (block) {
    var startFrom = text.indexOf(block) + block.length;
    var slicedText = text.slice(startFrom);

    var functionName =
      getFirstRegExMatch(slicedText, R_NAMED_FN_NAME)
      || getFirstRegExMatch(slicedText, R_ANONYMOUS_FN_VAR_NAME)
      || getFirstRegExMatch(slicedText, R_ANONYMOUS_FN_PROPERTY_NAME);

    metadataList.push({
      'name': functionName
    });
  });

  return { methods: metadataList };
};

<commit_msg>Lints block-doc.parser.js.<commit_after>'use strict';

// TYPEDEFS
//

/**
 * @typedef {{
 *   name: string
 * }}
 */
var Metadata;

// CONSTANTS
//

var R_JSDOC_BLOCK = /\/\*\*[\s\S]*\*\//m;
var R_NAMED_FN_NAME = /function\s*(\w*)/;
var R_ANONYMOUS_FN_VAR_NAME = /var\s(\w*)\s*=\s*function/;
var R_ANONYMOUS_FN_PROPERTY_NAME = /(\w+)(?=.*:.*function)/;

// HELPER METHODS
//

/**
 * @param {string} str The string to test against.
 * @param {RegExp} regex The RegExp to test for.
 * @return {string|null}
 */
function getFirstRegExMatch (str, regex) {
  var match = str.match(regex);
  return (match && match[1]) || null;
}

/**
 * @param {string} text The contents of a JavaScript file.
 * @return {Array.<metadata>}
 */
exports.getMetadata = function (text) {
  var jsDocBlocks = text.match(R_JSDOC_BLOCK);

  /** @type {Array.<metadata>} */
  var metadataList = [];

  jsDocBlocks.forEach(function (block) {
    var startFrom = text.indexOf(block) + block.length;
    var slicedText = text.slice(startFrom);

    var functionName =
      getFirstRegExMatch(slicedText, R_NAMED_FN_NAME) ||
      getFirstRegExMatch(slicedText, R_ANONYMOUS_FN_VAR_NAME) ||
      getFirstRegExMatch(slicedText, R_ANONYMOUS_FN_PROPERTY_NAME);

    metadataList.push({
      'name': functionName
    });
  });

  return { methods: metadataList };
};

<|endoftext|>


<commit_before>'use strict'

const {TopLevelWindow, MenuItem, webContents} = require('electron')
const EventEmitter = require('events').EventEmitter
const v8Util = process.atomBinding('v8_util')
const bindings = process.atomBinding('menu')

const {Menu} = bindings
let applicationMenu = null
let groupIdIndex = 0

Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype)

// Menu Delegate.
// This object should hold no reference to |Menu| to avoid cyclic reference.
const delegate = {
  isCommandIdChecked: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].checked : undefined,
  isCommandIdEnabled: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].enabled : undefined,
  isCommandIdVisible: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].visible : undefined,
  getAcceleratorForCommandId: (menu, id, useDefaultAccelerator) => {
    const command = menu.commandsMap[id]
    if (!command) return
    if (command.accelerator != null) return command.accelerator
    if (useDefaultAccelerator) return command.getDefaultRoleAccelerator()
  },
  executeCommand: (menu, event, id) => {
    const command = menu.commandsMap[id]
    if (!command) return
    command.click(event, TopLevelWindow.getFocusedWindow(), webContents.getFocusedWebContents())
  },
  menuWillShow: (menu) => {
    // Ensure radio groups have at least one menu item seleted
    for (const id in menu.groupsMap) {
      const found = menu.groupsMap[id].find(item => item.checked) || null
      if (!found) v8Util.setHiddenValue(menu.groupsMap[id][0], 'checked', true)
    }
  }
}

/* Instance Methods */

Menu.prototype._init = function () {
  this.commandsMap = {}
  this.groupsMap = {}
  this.items = []
  this.delegate = delegate
}

Menu.prototype.popup = function (options) {
  if (options == null || typeof options !== 'object') {
    throw new TypeError('Options must be an object')
  }
  let {window, x, y, positioningItem, callback} = options

  // no callback passed
  if (!callback || typeof callback !== 'function') callback = () => {}

  // set defaults
  if (typeof x !== 'number') x = -1
  if (typeof y !== 'number') y = -1
  if (typeof positioningItem !== 'number') positioningItem = -1

  // find which window to use
  const wins = TopLevelWindow.getAllWindows()
  if (!wins || wins.indexOf(window) === -1) {
    window = TopLevelWindow.getFocusedWindow()
    if (!window && wins && wins.length > 0) {
      window = wins[0]
    }
    if (!window) {
      throw new Error(`Cannot open Menu without a TopLevelWindow present`)
    }
  }

  this.popupAt(window, x, y, positioningItem, callback)
  return { browserWindow: window, x, y, position: positioningItem }
}

Menu.prototype.closePopup = function (window) {
  if (window instanceof TopLevelWindow) {
    this.closePopupAt(window.id)
  } else {
    // Passing -1 (invalid) would make closePopupAt close the all menu runners
    // belong to this menu.
    this.closePopupAt(-1)
  }
}

Menu.prototype.getMenuItemById = function (id) {
  const items = this.items

  let found = items.find(item => item.id === id) || null
  for (let i = 0; !found && i < items.length; i++) {
    if (items[i].submenu) {
      found = items[i].submenu.getMenuItemById(id)
    }
  }
  return found
}

Menu.prototype.append = function (item) {
  return this.insert(this.getItemCount(), item)
}

Menu.prototype.insert = function (pos, item) {
  if ((item ? item.constructor : void 0) !== MenuItem) {
    throw new TypeError('Invalid item')
  }

  // insert item depending on its type
  insertItemByType.call(this, item, pos)

  // set item properties
  if (item.sublabel) this.setSublabel(pos, item.sublabel)
  if (item.icon) this.setIcon(pos, item.icon)
  if (item.role) this.setRole(pos, item.role)

  // Make menu accessable to items.
  item.overrideReadOnlyProperty('menu', this)

  // Remember the items.
  this.items.splice(pos, 0, item)
  this.commandsMap[item.commandId] = item
}

Menu.prototype._callMenuWillShow = function () {
  if (this.delegate) this.delegate.menuWillShow(this)
  this.items.forEach(item => {
    if (item.submenu) item.submenu._callMenuWillShow()
  })
}

/* Static Methods */

Menu.getApplicationMenu = () => applicationMenu

Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder

// set application menu with a preexisting menu
Menu.setApplicationMenu = function (menu) {
  if (menu && menu.constructor !== Menu) {
    throw new TypeError('Invalid menu')
  }

  applicationMenu = menu
  if (process.platform === 'darwin') {
    if (!menu) return
    menu._callMenuWillShow()
    bindings.setApplicationMenu(menu)
  } else {
    const windows = TopLevelWindow.getAllWindows()
    return windows.map(w => w.setMenu(menu))
  }
}

Menu.buildFromTemplate = function (template) {
  if (!(template instanceof Array)) {
    throw new TypeError('Invalid template for Menu')
  }

  const menu = new Menu()
  const filtered = removeExtraSeparators(template)

  const positioned = []
  let idx = 0

  // sort template by position
  filtered.forEach(item => {
    idx = (item.position) ? indexToInsertByPosition(positioned, item.position) : idx += 1
    positioned.splice(idx, 0, item)
  })

  // add each item from positioned menu to application menu
  positioned.forEach((item) => {
    if (typeof item !== 'object') {
      throw new TypeError('Invalid template for MenuItem')
    }
    menu.append(new MenuItem(item))
  })

  return menu
}

/* Helper Functions */

// Search between separators to find a radio menu item and return its group id
function generateGroupId (items, pos) {
  if (pos > 0) {
    for (let idx = pos - 1; idx >= 0; idx--) {
      if (items[idx].type === 'radio') return items[idx].groupId
      if (items[idx].type === 'separator') break
    }
  } else if (pos < items.length) {
    for (let idx = pos; idx <= items.length - 1; idx++) {
      if (items[idx].type === 'radio') return items[idx].groupId
      if (items[idx].type === 'separator') break
    }
  }
  groupIdIndex += 1
  return groupIdIndex
}

function indexOfItemById (items, id) {
  const foundItem = items.find(item => item.id === id) || -1
  return items.indexOf(foundItem)
}

function indexToInsertByPosition (items, position) {
  if (!position) return items.length

  const [query, id] = position.split('=') // parse query and id from position
  const idx = indexOfItemById(items, id) // calculate initial index of item

  // warn if query doesn't exist
  if (idx === -1 && query !== 'endof') {
    console.warn(`Item with id ${id} is not found`)
    return items.length
  }

  // compute new index based on query
  const queries = {
    after: (index) => {
      index += 1
      return index
    },
    endof: (index) => {
      if (index === -1) {
        items.push({id, type: 'separator'})
        index = items.length - 1
      }

      index += 1
      while (index < items.length && items[index].type !== 'separator') index += 1
      return index
    }
  }

  // return new index if needed, or original indexOfItemById
  return (query in queries) ? queries[query](idx) : idx
}

function removeExtraSeparators (items) {
  // remove invisible items
  let ret = items.filter(e => e.visible !== false)

  // fold adjacent separators together
  ret = ret.filter((e, idx, arr) => e.type !== 'separator' || idx === 0 || arr[idx - 1].type !== 'separator')

  // remove edge separators
  return ret.filter((e, idx, arr) => e.type !== 'separator' || (idx !== 0 && idx !== arr.length - 1))
}

function insertItemByType (item, pos) {
  const types = {
    normal: () => this.insertItem(pos, item.commandId, item.label),
    checkbox: () => this.insertCheckItem(pos, item.commandId, item.label),
    separator: () => this.insertSeparator(pos),
    submenu: () => this.insertSubMenu(pos, item.commandId, item.label, item.submenu),
    radio: () => {
      // Grouping radio menu items
      item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos))
      if (this.groupsMap[item.groupId] == null) {
        this.groupsMap[item.groupId] = []
      }
      this.groupsMap[item.groupId].push(item)

      // Setting a radio menu item should flip other items in the group.
      v8Util.setHiddenValue(item, 'checked', item.checked)
      Object.defineProperty(item, 'checked', {
        enumerable: true,
        get: () => v8Util.getHiddenValue(item, 'checked'),
        set: () => {
          this.groupsMap[item.groupId].forEach(other => {
            if (other !== item) v8Util.setHiddenValue(other, 'checked', false)
          })
          v8Util.setHiddenValue(item, 'checked', true)
        }
      })
      this.insertRadioItem(pos, item.commandId, item.label, item.groupId)
    }
  }
  types[item.type]()
}

module.exports = Menu
<commit_msg>revert to more graceful template structure check (#12699)<commit_after>'use strict'

const {TopLevelWindow, MenuItem, webContents} = require('electron')
const EventEmitter = require('events').EventEmitter
const v8Util = process.atomBinding('v8_util')
const bindings = process.atomBinding('menu')

const {Menu} = bindings
let applicationMenu = null
let groupIdIndex = 0

Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype)

// Menu Delegate.
// This object should hold no reference to |Menu| to avoid cyclic reference.
const delegate = {
  isCommandIdChecked: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].checked : undefined,
  isCommandIdEnabled: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].enabled : undefined,
  isCommandIdVisible: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].visible : undefined,
  getAcceleratorForCommandId: (menu, id, useDefaultAccelerator) => {
    const command = menu.commandsMap[id]
    if (!command) return
    if (command.accelerator != null) return command.accelerator
    if (useDefaultAccelerator) return command.getDefaultRoleAccelerator()
  },
  executeCommand: (menu, event, id) => {
    const command = menu.commandsMap[id]
    if (!command) return
    command.click(event, TopLevelWindow.getFocusedWindow(), webContents.getFocusedWebContents())
  },
  menuWillShow: (menu) => {
    // Ensure radio groups have at least one menu item seleted
    for (const id in menu.groupsMap) {
      const found = menu.groupsMap[id].find(item => item.checked) || null
      if (!found) v8Util.setHiddenValue(menu.groupsMap[id][0], 'checked', true)
    }
  }
}

/* Instance Methods */

Menu.prototype._init = function () {
  this.commandsMap = {}
  this.groupsMap = {}
  this.items = []
  this.delegate = delegate
}

Menu.prototype.popup = function (options) {
  if (options == null || typeof options !== 'object') {
    throw new TypeError('Options must be an object')
  }
  let {window, x, y, positioningItem, callback} = options

  // no callback passed
  if (!callback || typeof callback !== 'function') callback = () => {}

  // set defaults
  if (typeof x !== 'number') x = -1
  if (typeof y !== 'number') y = -1
  if (typeof positioningItem !== 'number') positioningItem = -1

  // find which window to use
  const wins = TopLevelWindow.getAllWindows()
  if (!wins || wins.indexOf(window) === -1) {
    window = TopLevelWindow.getFocusedWindow()
    if (!window && wins && wins.length > 0) {
      window = wins[0]
    }
    if (!window) {
      throw new Error(`Cannot open Menu without a TopLevelWindow present`)
    }
  }

  this.popupAt(window, x, y, positioningItem, callback)
  return { browserWindow: window, x, y, position: positioningItem }
}

Menu.prototype.closePopup = function (window) {
  if (window instanceof TopLevelWindow) {
    this.closePopupAt(window.id)
  } else {
    // Passing -1 (invalid) would make closePopupAt close the all menu runners
    // belong to this menu.
    this.closePopupAt(-1)
  }
}

Menu.prototype.getMenuItemById = function (id) {
  const items = this.items

  let found = items.find(item => item.id === id) || null
  for (let i = 0; !found && i < items.length; i++) {
    if (items[i].submenu) {
      found = items[i].submenu.getMenuItemById(id)
    }
  }
  return found
}

Menu.prototype.append = function (item) {
  return this.insert(this.getItemCount(), item)
}

Menu.prototype.insert = function (pos, item) {
  if ((item ? item.constructor : void 0) !== MenuItem) {
    throw new TypeError('Invalid item')
  }

  // insert item depending on its type
  insertItemByType.call(this, item, pos)

  // set item properties
  if (item.sublabel) this.setSublabel(pos, item.sublabel)
  if (item.icon) this.setIcon(pos, item.icon)
  if (item.role) this.setRole(pos, item.role)

  // Make menu accessable to items.
  item.overrideReadOnlyProperty('menu', this)

  // Remember the items.
  this.items.splice(pos, 0, item)
  this.commandsMap[item.commandId] = item
}

Menu.prototype._callMenuWillShow = function () {
  if (this.delegate) this.delegate.menuWillShow(this)
  this.items.forEach(item => {
    if (item.submenu) item.submenu._callMenuWillShow()
  })
}

/* Static Methods */

Menu.getApplicationMenu = () => applicationMenu

Menu.sendActionToFirstResponder = bindings.sendActionToFirstResponder

// set application menu with a preexisting menu
Menu.setApplicationMenu = function (menu) {
  if (menu && menu.constructor !== Menu) {
    throw new TypeError('Invalid menu')
  }

  applicationMenu = menu
  if (process.platform === 'darwin') {
    if (!menu) return
    menu._callMenuWillShow()
    bindings.setApplicationMenu(menu)
  } else {
    const windows = TopLevelWindow.getAllWindows()
    return windows.map(w => w.setMenu(menu))
  }
}

Menu.buildFromTemplate = function (template) {
  if (!Array.isArray(template)) {
    throw new TypeError('Invalid template for Menu')
  }

  const menu = new Menu()
  const filtered = removeExtraSeparators(template)

  const positioned = []
  let idx = 0

  // sort template by position
  filtered.forEach(item => {
    idx = (item.position) ? indexToInsertByPosition(positioned, item.position) : idx += 1
    positioned.splice(idx, 0, item)
  })

  // add each item from positioned menu to application menu
  positioned.forEach((item) => {
    if (typeof item !== 'object') {
      throw new TypeError('Invalid template for MenuItem')
    }
    menu.append(new MenuItem(item))
  })

  return menu
}

/* Helper Functions */

// Search between separators to find a radio menu item and return its group id
function generateGroupId (items, pos) {
  if (pos > 0) {
    for (let idx = pos - 1; idx >= 0; idx--) {
      if (items[idx].type === 'radio') return items[idx].groupId
      if (items[idx].type === 'separator') break
    }
  } else if (pos < items.length) {
    for (let idx = pos; idx <= items.length - 1; idx++) {
      if (items[idx].type === 'radio') return items[idx].groupId
      if (items[idx].type === 'separator') break
    }
  }
  groupIdIndex += 1
  return groupIdIndex
}

function indexOfItemById (items, id) {
  const foundItem = items.find(item => item.id === id) || -1
  return items.indexOf(foundItem)
}

function indexToInsertByPosition (items, position) {
  if (!position) return items.length

  const [query, id] = position.split('=') // parse query and id from position
  const idx = indexOfItemById(items, id) // calculate initial index of item

  // warn if query doesn't exist
  if (idx === -1 && query !== 'endof') {
    console.warn(`Item with id ${id} is not found`)
    return items.length
  }

  // compute new index based on query
  const queries = {
    after: (index) => {
      index += 1
      return index
    },
    endof: (index) => {
      if (index === -1) {
        items.push({id, type: 'separator'})
        index = items.length - 1
      }

      index += 1
      while (index < items.length && items[index].type !== 'separator') index += 1
      return index
    }
  }

  // return new index if needed, or original indexOfItemById
  return (query in queries) ? queries[query](idx) : idx
}

function removeExtraSeparators (items) {
  // remove invisible items
  let ret = items.filter(e => e.visible !== false)

  // fold adjacent separators together
  ret = ret.filter((e, idx, arr) => e.type !== 'separator' || idx === 0 || arr[idx - 1].type !== 'separator')

  // remove edge separators
  return ret.filter((e, idx, arr) => e.type !== 'separator' || (idx !== 0 && idx !== arr.length - 1))
}

function insertItemByType (item, pos) {
  const types = {
    normal: () => this.insertItem(pos, item.commandId, item.label),
    checkbox: () => this.insertCheckItem(pos, item.commandId, item.label),
    separator: () => this.insertSeparator(pos),
    submenu: () => this.insertSubMenu(pos, item.commandId, item.label, item.submenu),
    radio: () => {
      // Grouping radio menu items
      item.overrideReadOnlyProperty('groupId', generateGroupId(this.items, pos))
      if (this.groupsMap[item.groupId] == null) {
        this.groupsMap[item.groupId] = []
      }
      this.groupsMap[item.groupId].push(item)

      // Setting a radio menu item should flip other items in the group.
      v8Util.setHiddenValue(item, 'checked', item.checked)
      Object.defineProperty(item, 'checked', {
        enumerable: true,
        get: () => v8Util.getHiddenValue(item, 'checked'),
        set: () => {
          this.groupsMap[item.groupId].forEach(other => {
            if (other !== item) v8Util.setHiddenValue(other, 'checked', false)
          })
          v8Util.setHiddenValue(item, 'checked', true)
        }
      })
      this.insertRadioItem(pos, item.commandId, item.label, item.groupId)
    }
  }
  types[item.type]()
}

module.exports = Menu
<|endoftext|>


<commit_before>"use strict";

let style, add;
if (document.createStyleSheet) {
	// IE
	if (document.styleSheets.length > 29) {
		style = document.styleSheets[document.styleSheets.length - 1];
	} else {
		style = document.createStyleSheet();
	}
	add = function (css) { style.cssText += css; };
} else {
	style = document.getElementsByTagName("head")[0]
		.appendChild(document.createElement("style"));
	style.setAttribute("type", "text/css");
	add = function (css) { style.appendChild(document.createTextNode(css)); };
}
module.exports = add;
<commit_msg>style: prettier<commit_after>"use strict";

let style, add;
if (document.createStyleSheet) {
	// IE
	if (document.styleSheets.length > 29) {
		style = document.styleSheets[document.styleSheets.length - 1];
	} else {
		style = document.createStyleSheet();
	}
	add = function (css) { style.cssText += css; };
} else {
	style = document.getElementsByTagName("head")[0].appendChild(document.createElement("style"));
	style.setAttribute("type", "text/css");
	add = function (css) { style.appendChild(document.createTextNode(css)); };
}
module.exports = add;
<|endoftext|>


<commit_before>'use strict';

const debug = require('debug')('gameboy:cart:mbc1');
const Serializable = require('../../util/serializable');
const { ROM_SIZE, RAM_SIZE } = require('../headers');

const createMemory = (count, size, data = []) => new Array(count)
    .fill(null)
    .map((v, i) => {
        const offset = i * size;
        if (offset + size <= data.length) {
            return data.slice(offset, offset + size);
        }
        return new Uint8Array(size);
    });

const wrap = (array, i) => array[i & (array.length - 1)];

class MBC1 extends Serializable({ exclude: ['_rom'] }) {
    constructor (data, romSize, ramSize) {
        super();

        this._lbank = 1;
        this._hbank = 0;
        this._ramEnabled = false;
        this._mode = 0;

        this._rom = createMemory(ROM_SIZE[romSize], 0x4000, data);
        this._ram = createMemory(RAM_SIZE[ramSize], 0x2000);
        debug('rom', this._rom, ROM_SIZE[romSize]);
        debug('ram', this._ram, RAM_SIZE[ramSize]);
    }

    readByte (addr) {
        switch (addr >> 12) {
            case 0x0: case 0x1:
            case 0x2: case 0x3:
                return this._rom[0][addr];
            case 0x4: case 0x5:
            case 0x6: case 0x7: {
                let bank = this._lbank;
                if (this._mode == 0) bank |= this._hbank << 5;
                return wrap(this._rom, bank)[addr & 0x3fff];
            }
            case 0xa: case 0xb: {
                if (!this._ramEnabled) {
                    debug('cannot read ram while disabled');
                    return 0xff;
                } else if (this._ram.length == 0) {
                    throw new Error('cannot read ram if not created');
                }
                let bank = 0;
                if (this._mode == 1) bank = this._hbank;
                return wrap(this._ram, bank)[addr & 0x1fff];
            }
        }

        throw new Error(`unmapped address 0x${addr.toString(16)}`);
    }

    writeByte (addr, val) {
        switch (addr >> 12) {
            case 0x0: case 0x1:
                return this._ramEnabled = (val & 0xf) == 0xa;
            case 0x2: case 0x3:
                val &= 0x1f;
                if (val == 0) val = 1;
                return this._lbank = val;
            case 0x4: case 0x5:
                return this._hbank = val & 3;
            case 0x6: case 0x7:
                return this._mode = val & 1;
            case 0xa: case 0xb:
                if (!this._ramEnabled) {
                    return debug('cannot write ram while disabled');
                } else if (this._ram.length == 0) {
                    throw new Error('cannot write ram if not created');
                }
                return wrap(this._ram, this._hbank)[addr & 0x1fff] = val;
        }

        throw new Error(`unmapped address 0x${addr.toString(16)}`);
    }
}

module.exports = MBC1;
<commit_msg>fix rom access<commit_after>'use strict';

const debug = require('debug')('gameboy:cart:mbc1');
const Serializable = require('../../util/serializable');
const { ROM_SIZE, RAM_SIZE } = require('../headers');

const createMemory = (count, size, data = []) => new Array(count)
    .fill(null)
    .map((v, i) => {
        const offset = i * size;
        if (offset + size <= data.length) {
            return data.slice(offset, offset + size);
        }
        return new Uint8Array(size);
    });

const wrap = (array, i) => array[i & (array.length - 1)];

class MBC1 extends Serializable({ exclude: ['_rom'] }) {
    constructor (data, romSize, ramSize) {
        super();

        this._lbank = 1;
        this._hbank = 0;
        this._ramEnabled = false;
        this._mode = 0;

        this._rom = createMemory(ROM_SIZE[romSize], 0x4000, data);
        this._ram = createMemory(RAM_SIZE[ramSize], 0x2000);
        console.log('rom', this._rom, ROM_SIZE[romSize]);
        console.log('ram', this._ram, RAM_SIZE[ramSize]);
    }

    readByte (addr) {
        switch (addr >> 12) {
            case 0x0: case 0x1:
            case 0x2: case 0x3:
                let bank = 0;
                if (this._mode == 1) bank = this._hbank << 5;
                return wrap(this._rom, bank)[addr];
            case 0x4: case 0x5:
            case 0x6: case 0x7: {
                const bank = this._lbank | (this._hbank << 5);
                return wrap(this._rom, bank)[addr & 0x3fff];
            }
            case 0xa: case 0xb: {
                if (!this._ramEnabled) {
                    debug('cannot read ram while disabled');
                    return 0xff;
                } else if (this._ram.length == 0) {
                    throw new Error('cannot read ram if not created');
                }
                let bank = 0;
                if (this._mode == 1) bank = this._hbank;
                return wrap(this._ram, bank)[addr & 0x1fff];
            }
        }

        throw new Error(`unmapped address 0x${addr.toString(16)}`);
    }

    writeByte (addr, val) {
        switch (addr >> 12) {
            case 0x0: case 0x1:
                return this._ramEnabled = (val & 0xf) == 0xa;
            case 0x2: case 0x3:
                val &= 0x1f;
                if (val == 0) val = 1;
                return this._lbank = val;
            case 0x4: case 0x5:
                return this._hbank = val & 3;
            case 0x6: case 0x7:
                return this._mode = val & 1;
            case 0xa: case 0xb:
                if (!this._ramEnabled) {
                    return debug('cannot write ram while disabled');
                } else if (this._ram.length == 0) {
                    throw new Error('cannot write ram if not created');
                }
                return wrap(this._ram, this._hbank)[addr & 0x1fff] = val;
        }

        throw new Error(`unmapped address 0x${addr.toString(16)}`);
    }
}

module.exports = MBC1;
<|endoftext|>


<commit_before>Polymer({
    ready: function() {
        var self = this;

        this.$["button-previous"].onclick = function() {
            $.get("/player/previous", function(data, status) {
                self.updateStatus(self, JSON.parse(data));
            });
        };

        this.$["button-play"].onclick = function() {
            $.get("/player/play", function(data, status) {
                self.updateStatus(self, JSON.parse(data));
            });
        };

        this.$["button-next"].onclick = function() {
            $.get("/player/next", function(data, status) {
                self.updateStatus(self, JSON.parse(data));
            });
        };

        this.$["button-mute"].onclick = function() {
            self.mute(self);
        };

        this.$["volume-slider"].addEventListener("change", function(e) {
            var vol = parseInt(self.$["volume-slider"].value);
            self.updateVolumeServer(self, vol);
        });

        window.setInterval(function() {
            $.get("/player/status", function(data, status) {
                self.updateStatus(self, JSON.parse(data));
            });
        }, 500);
    },
    updateStatus: function(self, status) {

        // if the song has changed
        if (self.playerStatus === undefined ||
                self.playerStatus.currentSong.id != status.currentSong.id) {

            self.playerStatus = status;

            self.$["song-title"].innerHTML = status.currentSong.title;
            self.$["song-artist"].innerHTML = status.currentSong.artist;

            self.$["album-art"].src = "/song/" + status.currentSong.id + "/art";

            self.updateVolumeView(self, status.volume);
        }

        if (status.isPlaying) {
            self.$["play-icon"].icon = "av:pause";
            self.$["song-progressbar"].value = status.progress;
        } else {
            self.$["play-icon"].icon = "av:play-arrow";
        }
    },
    updateVolumeView: function(self, vol) {
        self.$["volume-slider"].value = vol;

        if (vol === 0)
            self.$["button-mute"].icon = "av:volume-off";
        else if (vol > 0 && vol < 33)
            self.$["button-mute"].icon = "av:volume-mute";
        else if (vol >= 33 && vol < 66)
            self.$["button-mute"].icon = "av:volume-down";
        else if (vol >= 66)
            self.$["button-mute"].icon = "av:volume-up";
    },
    updateVolumeServer: function(self, volume) {
        $.post("/player/volume/" + volume, function(data) {
            var status = JSON.parse(data);
            self.updateVolumeView(self, status.volume);
        });
    },
    mute: function(self) {
        var currentVolume = self.$["volume-slider"].value;

        if (currentVolume === 0) {
            self.updateVolumeView(self, self.volumeBeforeMute);
        } else {
            self.volumeBeforeMute = currentVolume;
            self.updateVolumeView(self, 0);
        }
        self.updateVolumeServer(self, self.$["volume-slider"].value);
    },
    volumeBeforeMute: 100,
    playerStatus: undefined
});
<commit_msg>Fixed volume slider not updating<commit_after>Polymer({
    ready: function() {
        var self = this;

        this.$["button-previous"].onclick = function() {
            $.get("/player/previous", function(data, status) {
                self.updateStatus(self, JSON.parse(data));
            });
        };

        this.$["button-play"].onclick = function() {
            $.get("/player/play", function(data, status) {
                self.updateStatus(self, JSON.parse(data));
            });
        };

        this.$["button-next"].onclick = function() {
            $.get("/player/next", function(data, status) {
                self.updateStatus(self, JSON.parse(data));
            });
        };

        this.$["button-mute"].onclick = function() {
            self.mute(self);
        };

        this.$["volume-slider"].addEventListener("change", function(e) {
            var vol = parseInt(self.$["volume-slider"].value);
            self.updateVolumeServer(self, vol);
        });

        window.setInterval(function() {
            $.get("/player/status", function(data, status) {
                self.updateStatus(self, JSON.parse(data));
            });
        }, 500);
    },
    updateStatus: function(self, status) {

        // if the song has changed
        if (self.playerStatus === undefined ||
                self.playerStatus.currentSong.id != status.currentSong.id) {

            self.playerStatus = status;

            self.$["song-title"].innerHTML = status.currentSong.title;
            self.$["song-artist"].innerHTML = status.currentSong.artist;

            self.$["album-art"].src = "/song/" + status.currentSong.id + "/art";

            self.updateVolumeView(self, status.volume);
        }

        if (status.isPlaying) {
            self.$["play-icon"].icon = "av:pause";
            self.$["song-progressbar"].value = status.progress;

            if (status.volume != self.playerStatus.volume) {
                self.updateVolumeView(self, status.volume);
            }
        } else {
            self.$["play-icon"].icon = "av:play-arrow";
        }
    },
    updateVolumeView: function(self, vol) {
        self.$["volume-slider"].value = vol;

        if (vol === 0)
            self.$["button-mute"].icon = "av:volume-off";
        else if (vol > 0 && vol < 33)
            self.$["button-mute"].icon = "av:volume-mute";
        else if (vol >= 33 && vol < 66)
            self.$["button-mute"].icon = "av:volume-down";
        else if (vol >= 66)
            self.$["button-mute"].icon = "av:volume-up";
    },
    updateVolumeServer: function(self, volume) {
        $.post("/player/volume/" + volume, function(data) {
            var status = JSON.parse(data);
            self.updateVolumeView(self, status.volume);
        });
    },
    mute: function(self) {
        var currentVolume = self.$["volume-slider"].value;

        if (currentVolume === 0) {
            self.updateVolumeView(self, self.volumeBeforeMute);
        } else {
            self.volumeBeforeMute = currentVolume;
            self.updateVolumeView(self, 0);
        }
        self.updateVolumeServer(self, self.$["volume-slider"].value);
    },
    volumeBeforeMute: 100,
    playerStatus: undefined
});
<|endoftext|>


<commit_before>// Load in dependencies
// TODO: Can we get some tab completion for this?
var assert = require('assert');
var path = require('path');
var async = require('async');
var dirty = require('dirty');
var mkdirp = require('mkdirp');

// // TODO: Let version and name be specified by the CLI invocation (i.e. leave it out of `lib` and pass back `program` *before* .parse
// var program = require('commander')
//                 .version(require('../package.json').version);

// Load in presentation info
// TODO: This should sniff for `presentation/config.{yml,json,js}`, `config.{yml,json,js}`
// TODO: Easily done via `require` but the `fs.existsSync` is excess I don't want to deal with right now
// TODO: Allow override via `--config`
// var yaml = require('js-yaml');
// var config = require('../presentation/config.yml');
// var Presentation = require('../');
// var presentation = new Presentation(config, {cwd: __dirname + '/../presentation'});

function Presentation(options) {
  // Save db for later
  // TODO: Move to dirty to get off of compilation step and open up platforms
  this.dbPath = options.db || path.join(process.cwd(), 'cli-presentation.db');

  // TODO: Consider .set mechanism for options
  this.options = options;
}
Presentation.prototype = {
  getDb: function (cb) {
    // If we already have a db, callback with it
    if (this.db) {
      return cb(null, this.db);
    }

    // Otherwise, load it
    var dbPath = this.dbPath;
    mkdirp.sync(path.dirname(dbPath));
    var db = dirty(dbPath);
    var that = this;
    db.on('load', function (err) {
      // Save the db and callback with it
      that.db = db;
      done(err, db);
    });
  },
  getSlide: function (cb) {

  }
};

// Define a slide information retrieval helper
function getCurrentSlide(cb) {
  db.get('slide', function (err, val) {
    // If this is the first run, return -1
    if (err && err.notFound) {
      cb(null, -1);
    } else {
    // Otherwise, callback with the slide
      cb(err, +val);
    }
  });
}

// Define common slide jumping function
function goToSlide(_slide, argv) {
  // TODO: Load slides
  var slides, slide;
  async.series([
    function fallbackSlide (cb) {
      // Fallback slide to active one
      if (_slide === undefined) {
        getCurrentSlide(function (err, val) {
          // Save the info and callback
          _slide = val || 0;
          cb(err);
        });
      } else {
        cb();
      }
    },
    function coerceSlide (cb) {
      // Parse the slide and guarantee it is within our bounds
      slide = parseInt(_slide, 10);
      assert(!isNaN(slide), 'Slide "' + _slide + '" parsed to NaN');
      slide = Math.max(0, slide);
      slide = Math.min(slide, presentation.length - 1);
      cb();
    },
    function loadSlide (cb) {
      // Load and run the slide
      var slideJs = presentation.getSlide(slide);
      slideJs();
      cb();
    },
    function saveState (cb) {
      // Save the slide state
      db.put('slide', slide, cb);
    }
  ], function handleErrors (err) {
    // If there was an error, throw it
    if (err) {
      throw err;
    }
  });
}

// Define CLI commands
program
  .command('slide <slide>')
  .description('Jump to a specific slide')
  .action(goToSlide);

program
  .command('first')
  .description('Go back to the beginning')
  .action(function () {
    goToSlide(0);
  });

program
  .command('last')
  .description('Jump to the final slide')
  .action(function () {
    goToSlide(presentation.length - 1);
  });

program
  .command('next')
  .description('Move following by one slide')
  .action(function () {
    getCurrentSlide(function (err, val) {
      if (err) { throw err; }
      // -1 -> 0. 0 -> 1
      goToSlide(val + 1);
    });
  });

program
  .command('back')
  .description('Move to the previous slide')
  .action(function () {
    getCurrentSlide(function (err, val) {
      if (err) { throw err; }
      // 0 -> -1 (max -> 0). 1 -> 0
      goToSlide(val - 1);
    });
  });

program
  .command('status')
  .description('Get overview of slides')
  .action(function () {
    getCurrentSlide(function (err, val) {
      if (err) { throw err; }

      config.slides.forEach(function (name, i) {
        var active = val === i ? '*' : ' ';
        console.log(active + ' ' + i + ' ' + name);
      });
    });
  });

// Parse the arguments
program.parse(process.argv);

// If there was no command, re-run the current slide
if (process.argv.length === 2) {
  goToSlide();
}
<commit_msg>Implemented getCurrentSlide method<commit_after>// Load in dependencies
// TODO: Can we get some tab completion for this?
var assert = require('assert');
var path = require('path');
var async = require('async');
var dirty = require('dirty');
var mkdirp = require('mkdirp');

// // TODO: Let version and name be specified by the CLI invocation (i.e. leave it out of `lib` and pass back `program` *before* .parse
// var program = require('commander')
//                 .version(require('../package.json').version);

// Load in presentation info
// TODO: This should sniff for `presentation/config.{yml,json,js}`, `config.{yml,json,js}`
// TODO: Easily done via `require` but the `fs.existsSync` is excess I don't want to deal with right now
// TODO: Allow override via `--config`
// var yaml = require('js-yaml');
// var config = require('../presentation/config.yml');
// var Presentation = require('../');
// var presentation = new Presentation(config, {cwd: __dirname + '/../presentation'});

function Presentation(options) {
  // Fallback db path and save for later
  this.dbPath = options.db || path.join(process.cwd(), 'cli-presentation.db');

  // Save options for later
  this.options = options;
}
Presentation.prototype = {
  getDb: function (cb) {
    // If we already have a db, callback with it
    if (this.db) {
      return cb(null, this.db);
    }

    // Otherwise, load it
    var dbPath = this.dbPath;
    mkdirp.sync(path.dirname(dbPath));
    var db = dirty(dbPath);
    var that = this;
    db.on('load', function (err) {
      // Save the db and callback with it
      that.db = db;
      cb(err, db);
    });
  },

  // Allow for retrieval of current slide
  getCurrentSlide: function (cb) {
    var that = this;
    async.waterfall([
      function fetchDb (cb) {
        that.getDb(cb);
      },
      function getSlide (db, cb) {
        db.get('slide', function (err, val) {
          // If this is the first run, return -1
          // TODO: Not found was for leveldb, this will be different. Probably `null`
          if (err && err.notFound) {
            cb(null, -1);
          } else {
          // Otherwise, callback with the slide
          // TODO: Is the numeric coercion necessary
            // cb(err, +val);
            cb(err, val);
          }
        });
      }
    ], cb);
  },

  getSlide: function (index, cb) {

  }
};

// Define common slide jumping function
function goToSlide(_slide, argv) {
  // TODO: Load slides
  var slides, slide;
  async.series([
    function fallbackSlide (cb) {
      // Fallback slide to active one
      if (_slide === undefined) {
        getCurrentSlide(function (err, val) {
          // Save the info and callback
          _slide = val || 0;
          cb(err);
        });
      } else {
        cb();
      }
    },
    function coerceSlide (cb) {
      // Parse the slide and guarantee it is within our bounds
      slide = parseInt(_slide, 10);
      assert(!isNaN(slide), 'Slide "' + _slide + '" parsed to NaN');
      slide = Math.max(0, slide);
      slide = Math.min(slide, presentation.length - 1);
      cb();
    },
    function loadSlide (cb) {
      // Load and run the slide
      var slideJs = presentation.getSlide(slide);
      slideJs();
      cb();
    },
    function saveState (cb) {
      // Save the slide state
      db.put('slide', slide, cb);
    }
  ], function handleErrors (err) {
    // If there was an error, throw it
    if (err) {
      throw err;
    }
  });
}

// Define CLI commands
program
  .command('slide <slide>')
  .description('Jump to a specific slide')
  .action(goToSlide);

program
  .command('first')
  .description('Go back to the beginning')
  .action(function () {
    goToSlide(0);
  });

program
  .command('last')
  .description('Jump to the final slide')
  .action(function () {
    goToSlide(presentation.length - 1);
  });

program
  .command('next')
  .description('Move following by one slide')
  .action(function () {
    getCurrentSlide(function (err, val) {
      if (err) { throw err; }
      // -1 -> 0. 0 -> 1
      goToSlide(val + 1);
    });
  });

program
  .command('back')
  .description('Move to the previous slide')
  .action(function () {
    getCurrentSlide(function (err, val) {
      if (err) { throw err; }
      // 0 -> -1 (max -> 0). 1 -> 0
      goToSlide(val - 1);
    });
  });

program
  .command('status')
  .description('Get overview of slides')
  .action(function () {
    getCurrentSlide(function (err, val) {
      if (err) { throw err; }

      config.slides.forEach(function (name, i) {
        var active = val === i ? '*' : ' ';
        console.log(active + ' ' + i + ' ' + name);
      });
    });
  });

// Parse the arguments
program.parse(process.argv);

// If there was no command, re-run the current slide
if (process.argv.length === 2) {
  goToSlide();
}
<|endoftext|>


<commit_before>/* global _ */
(function() {
  'use strict';

  function SearchFilterController($scope, $location, MessageBus, $timeout) {
    var filterValueObject = {
      analysis_packages: [
        {
          name: 'global.filter.tags',
          property: 'tags', collapsed: false
        },
        {
          name: 'global.filter.sponsors',
          property: 'sponsors', collapsed: false
        },
        {
          name: 'global.filter.institutions',
          property: 'institutions', collapsed: true
        }
      ],
      data_packages: [
        {
          name: 'global.filter.survey-data-types',
          property: 'survey-data-types',
          collapsed: false
        },
        {
          name: 'global.filter.study-series',
          property: 'study-series',
          collapsed: false
        },
        {
          name: 'global.filter.tags',
          property: 'tags', collapsed: true
        },
        {
          name: 'global.filter.access-ways',
          property: 'access-ways',
          collapsed: true
        },
        {
          name: 'global.filter.concepts',
          property: 'concepts', collapsed: true
        },
        {
          name: 'global.filter.sponsors',
          property: 'sponsors', collapsed: true
        },
        {
          name: 'global.filter.institutions',
          property: 'institutions', collapsed: true
        }
      ]
    };
    var $ctrl = this;
    $ctrl.onDataPackageFilterChange = MessageBus;
    $ctrl.onTotalHitsChange = MessageBus;
    $ctrl.totalHits = {};
    $ctrl.dataPackageFilter = {};
    $ctrl.searchFilterMapping = {};
    $ctrl.searchParams = {};
    $ctrl.filterValues = _.clone(filterValueObject.data_packages);
    $ctrl.dataPackageSearchFilter = 'data_packages';

    $ctrl.toggleFilterItem = toggleFilterItem;
    $ctrl.exists = exists;
    $ctrl.changePackageFilter = changePackageFilter;
    $ctrl.clearFilter = clearFilter;
    $ctrl.$onInit = init;

    function init() {
      $ctrl.searchParams.filter = {};
      $ctrl.searchFilterMapping = {};
      readSearchParamsFromLocation();
      if ($ctrl.searchParams.type === 'analysis_packages') {
        $ctrl.dataPackageSearchFilter = $ctrl.searchParams.type;
        setFilterValues($ctrl.searchParams.type);
      }
      checkCollapsible();
      _.assign($ctrl.searchFilterMapping, $ctrl.searchParams.filter);
    }

    function setFilterValues(prop) {
      $ctrl.searchParams.page = 1;
      $ctrl.searchParams.filter = {};
      $ctrl.searchFilterMapping = {};
      $ctrl.filterValues.length = 0;
      $ctrl.filterValues = _.clone(filterValueObject[prop]);
    }

    function changePackageFilter() {
      $ctrl.searchParams.type = $ctrl.dataPackageSearchFilter;
      setFilterValues($ctrl.dataPackageSearchFilter);
      writeSearchParamsToLocation();
    }

    function clearFilter() {
      readSearchParamsFromLocation();
      $ctrl.searchParams.page = 1;
      $ctrl.searchParams.filter = {};
      $ctrl.searchFilterMapping = {};
      writeSearchParamsToLocation();
    }

    function checkCollapsible() {
      var props = Object.keys($ctrl.searchParams.filter);
      $ctrl.filterValues.forEach(function(item) {
        if (_.includes(props, item.property)) {
          item.collapsed = false;
        }
      });
    }

    function toggleFilterItem(item, prop) {
      readSearchParamsFromLocation();
      if ($ctrl.searchFilterMapping &&
        $ctrl.searchFilterMapping.hasOwnProperty(prop) &&
        _.includes($ctrl.searchFilterMapping[prop], item)) {
        $ctrl.searchFilterMapping[prop] = _.without(
          $ctrl.searchFilterMapping[prop], item
        );
        if ($ctrl.searchFilterMapping[prop].length === 0) {
          delete $ctrl.searchFilterMapping[prop];
        }
      } else {
        if ($ctrl.searchFilterMapping &&
          $ctrl.searchFilterMapping.hasOwnProperty(prop)) {
          if (!angular.isArray($ctrl.searchFilterMapping[prop])) {
            var tmp = $ctrl.searchFilterMapping[prop];
            $ctrl.searchFilterMapping[prop] = [];
            $ctrl.searchFilterMapping[prop].push(tmp);
          }
          $ctrl.searchFilterMapping[prop].push(item);
        } else {
          $ctrl.searchFilterMapping[prop] = [];
          $ctrl.searchFilterMapping[prop].push(item);
        }
      }
      $ctrl.searchParams.filter = $ctrl.searchFilterMapping;
      $ctrl.searchParams.page = 1;
      writeSearchParamsToLocation();
    }

    function exists(item, prop) {
      if ($ctrl.searchFilterMapping &&
        $ctrl.searchFilterMapping.hasOwnProperty(prop)) {
        return $ctrl.searchFilterMapping[prop].indexOf(item) > -1;
      }
    }

    // write the searchParams object to the location with the correct types
    function writeSearchParamsToLocation() {
      var locationSearch = {};
      locationSearch.page = '' + $ctrl.searchParams.page;
      locationSearch.size = '' + $ctrl.searchParams.size;
      if ($ctrl.searchParams.query) {
        locationSearch.query = $ctrl.searchParams.query;
      }
      if ($ctrl.searchParams.type) {
        locationSearch.type = $ctrl.searchParams.type;
      }
      if ($ctrl.searchParams.sortBy) {
        locationSearch['sort-by'] = $ctrl.searchParams.sortBy;
      }
      MessageBus.set('searchFilter', $ctrl.searchParams.filter);
      _.assign(locationSearch, $ctrl.searchParams.filter);

      $location.search(locationSearch);
    }

    // read the searchParams object from the location with the correct types
    function readSearchParamsFromLocation() {
      $ctrl.searchParams = {};
      var locationSearch = $location.search();
      if (locationSearch.page != null) {
        $ctrl.searchParams.page = parseInt(locationSearch.page);
      }
      if (locationSearch.size != null) {
        $ctrl.searchParams.size = parseInt(locationSearch.size);
      }
      if (locationSearch.query) {
        if (locationSearch.query !== '') {
          $ctrl.searchParams.query = locationSearch.query;
        }
        if ($ctrl.searchParams.query === '') {
          delete $ctrl.searchParams.query;
        }
      }
      if (locationSearch.type) {
        $ctrl.searchParams.type = locationSearch.type;
      }
      if (locationSearch.selectedTabIndex) {
        $ctrl.searchParams.selectedTabIndex = locationSearch.selectedTabIndex;
      }
      $ctrl.searchParams.filter = _.omit(locationSearch, ['page', 'size',
        'type', 'query', 'sort-by'
      ]);
    }

    $scope.$watch(function() {
        return $ctrl.onDataPackageFilterChange;
      },
      function() {
        if ($ctrl.onTotalHitsChange.get('onTotalHitsChange')) {
          $ctrl.totalHits = $ctrl.onTotalHitsChange
            .get('onTotalHitsChange', true);
        }
        if ($ctrl.onDataPackageFilterChange.get('onDataPackageFilterChange')) {
          $ctrl.dataPackageFilter = $ctrl.onDataPackageFilterChange
            .get('onDataPackageFilterChange', true);
        }
        if ($ctrl.onDataPackageFilterChange.get('searchInit', true)) {
          init();
        }
      }, true);

    $scope.$watch(function() {
        return $location.search();
      },
      function() {
        $ctrl.searchParams.filter = _.omit($location.search(), ['page', 'size',
          'type', 'query', 'sort-by'
        ]);
        init();
      });

    $scope.$on('current-language-changed', function() {
      // we need to wait for the state change being completed
      $timeout($ctrl.clearFilter, 200);
    });
  }

  angular
    .module('metadatamanagementApp')
    .controller('SearchFilterController', SearchFilterController);
})();
<commit_msg>#2953 fix filter<commit_after>/* global _ */
(function() {
  'use strict';

  function SearchFilterController($scope, $location, MessageBus, $timeout) {
    var filterValueObject = {
      analysis_packages: [
        {
          name: 'global.filter.tags',
          property: 'tags', collapsed: false
        },
        {
          name: 'global.filter.sponsors',
          property: 'sponsors', collapsed: false
        },
        {
          name: 'global.filter.institutions',
          property: 'institutions', collapsed: true
        }
      ],
      data_packages: [
        {
          name: 'global.filter.survey-data-types',
          property: 'survey-data-types',
          collapsed: false
        },
        {
          name: 'global.filter.study-series',
          property: 'study-series',
          collapsed: false
        },
        {
          name: 'global.filter.tags',
          property: 'tags', collapsed: true
        },
        {
          name: 'global.filter.access-ways',
          property: 'access-ways',
          collapsed: true
        },
        {
          name: 'global.filter.concepts',
          property: 'concepts', collapsed: true
        },
        {
          name: 'global.filter.sponsors',
          property: 'sponsors', collapsed: true
        },
        {
          name: 'global.filter.institutions',
          property: 'institutions', collapsed: true
        }
      ]
    };
    var $ctrl = this;
    $ctrl.onDataPackageFilterChange = MessageBus;
    $ctrl.onTotalHitsChange = MessageBus;
    $ctrl.totalHits = {};
    $ctrl.dataPackageFilter = {};
    $ctrl.searchFilterMapping = {};
    $ctrl.searchParams = {};
    $ctrl.filterValues = _.clone(filterValueObject.data_packages);
    $ctrl.dataPackageSearchFilter = 'data_packages';

    $ctrl.toggleFilterItem = toggleFilterItem;
    $ctrl.exists = exists;
    $ctrl.changePackageFilter = changePackageFilter;
    $ctrl.clearFilter = clearFilter;
    $ctrl.$onInit = init;

    function init() {
      $ctrl.searchParams.filter = {};
      $ctrl.searchFilterMapping = {};
      readSearchParamsFromLocation();
      if ($ctrl.searchParams.type === 'analysis_packages') {
        $ctrl.dataPackageSearchFilter = $ctrl.searchParams.type;
        setFilterValues($ctrl.searchParams.type);
      }
      checkCollapsible();
      _.assign($ctrl.searchFilterMapping, $ctrl.searchParams.filter);
    }

    function setFilterValues(prop) {
      $ctrl.filterValues.length = 0;
      $ctrl.filterValues = _.clone(filterValueObject[prop]);
    }

    function changePackageFilter() {
      $ctrl.searchParams.type = $ctrl.dataPackageSearchFilter;
      setFilterValues($ctrl.dataPackageSearchFilter);
      writeSearchParamsToLocation();
    }

    function clearFilter() {
      readSearchParamsFromLocation();
      $ctrl.searchParams.page = 1;
      $ctrl.searchParams.filter = {};
      $ctrl.searchFilterMapping = {};
      writeSearchParamsToLocation();
    }

    function checkCollapsible() {
      var props = Object.keys($ctrl.searchParams.filter);
      $ctrl.filterValues.forEach(function(item) {
        if (_.includes(props, item.property)) {
          item.collapsed = false;
        }
      });
    }

    function toggleFilterItem(item, prop) {
      readSearchParamsFromLocation();
      if ($ctrl.searchFilterMapping &&
        $ctrl.searchFilterMapping.hasOwnProperty(prop) &&
        _.includes($ctrl.searchFilterMapping[prop], item)) {
        $ctrl.searchFilterMapping[prop] = _.without(
          $ctrl.searchFilterMapping[prop], item
        );
        if ($ctrl.searchFilterMapping[prop].length === 0) {
          delete $ctrl.searchFilterMapping[prop];
        }
      } else {
        if ($ctrl.searchFilterMapping &&
          $ctrl.searchFilterMapping.hasOwnProperty(prop)) {
          if (!angular.isArray($ctrl.searchFilterMapping[prop])) {
            var tmp = $ctrl.searchFilterMapping[prop];
            $ctrl.searchFilterMapping[prop] = [];
            $ctrl.searchFilterMapping[prop].push(tmp);
          }
          $ctrl.searchFilterMapping[prop].push(item);
        } else {
          $ctrl.searchFilterMapping[prop] = [];
          $ctrl.searchFilterMapping[prop].push(item);
        }
      }
      $ctrl.searchParams.filter = $ctrl.searchFilterMapping;
      $ctrl.searchParams.page = 1;
      writeSearchParamsToLocation();
    }

    function exists(item, prop) {
      if ($ctrl.searchFilterMapping &&
        $ctrl.searchFilterMapping.hasOwnProperty(prop)) {
        return $ctrl.searchFilterMapping[prop].indexOf(item) > -1;
      }
    }

    // write the searchParams object to the location with the correct types
    function writeSearchParamsToLocation() {
      var locationSearch = {};
      locationSearch.page = '' + $ctrl.searchParams.page;
      locationSearch.size = '' + $ctrl.searchParams.size;
      if ($ctrl.searchParams.query) {
        locationSearch.query = $ctrl.searchParams.query;
      }
      if ($ctrl.searchParams.type) {
        locationSearch.type = $ctrl.searchParams.type;
      }
      if ($ctrl.searchParams.sortBy) {
        locationSearch['sort-by'] = $ctrl.searchParams.sortBy;
      }
      MessageBus.set('searchFilter', $ctrl.searchParams.filter);
      _.assign(locationSearch, $ctrl.searchParams.filter);

      $location.search(locationSearch);
    }

    // read the searchParams object from the location with the correct types
    function readSearchParamsFromLocation() {
      $ctrl.searchParams = {};
      var locationSearch = $location.search();
      if (locationSearch.page != null) {
        $ctrl.searchParams.page = parseInt(locationSearch.page);
      }
      if (locationSearch.size != null) {
        $ctrl.searchParams.size = parseInt(locationSearch.size);
      }
      if (locationSearch.query) {
        if (locationSearch.query !== '') {
          $ctrl.searchParams.query = locationSearch.query;
        }
        if ($ctrl.searchParams.query === '') {
          delete $ctrl.searchParams.query;
        }
      }
      if (locationSearch.type) {
        $ctrl.searchParams.type = locationSearch.type;
      }
      if (locationSearch.selectedTabIndex) {
        $ctrl.searchParams.selectedTabIndex = locationSearch.selectedTabIndex;
      }
      $ctrl.searchParams.filter = _.omit(locationSearch, ['page', 'size',
        'type', 'query', 'sort-by'
      ]);
    }
      $scope.$watch('$ctrl.searchParams.type', function(newValue, oldValue){
        if (newValue !== oldValue) {
          clearFilter();
        }
      });

    $scope.$watch(function() {
        return $ctrl.onDataPackageFilterChange;
      },
      function() {
        if ($ctrl.onTotalHitsChange.get('onTotalHitsChange')) {
          $ctrl.totalHits = $ctrl.onTotalHitsChange
            .get('onTotalHitsChange', true);
        }
        if ($ctrl.onDataPackageFilterChange.get('onDataPackageFilterChange')) {
          $ctrl.dataPackageFilter = $ctrl.onDataPackageFilterChange
            .get('onDataPackageFilterChange', true);
        }
        if ($ctrl.onDataPackageFilterChange.get('searchInit', true)) {
          init();
        }
      }, true);

    $scope.$watch(function() {
        return $location.search();
      },
      function() {
      console.log('$location.search');
        $ctrl.searchParams.filter = _.omit($location.search(), ['page', 'size',
          'type', 'query', 'sort-by'
        ]);
        init();
      });

    $scope.$on('current-language-changed', function() {
      // we need to wait for the state change being completed
      $timeout($ctrl.clearFilter, 200);
    });
  }

  angular
    .module('metadatamanagementApp')
    .controller('SearchFilterController', SearchFilterController);
})();
<|endoftext|>