<commit_before>'use strict';

const _ = require('lodash');

const parseMultipartBody = require('../utils/parse-multipart');
const {
  validateGenerateUIDInput,
  validateCheckUIDAvailabilityInput,
  validateUIDField,
} = require('./validation');

const ACTIONS = {
  read: 'plugins::content-manager.explorer.read',
  create: 'plugins::content-manager.explorer.create',
  edit: 'plugins::content-manager.explorer.update',
  delete: 'plugins::content-manager.explorer.delete',
};

const findEntityAndCheckPermissions = async (ability, action, model, id) => {
  const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;
  const entity = await contentManagerService.fetch(model, id);

  if (_.isNil(entity)) {
    throw strapi.errors.notFound();
  }

  const pm = strapi.admin.services.permission.createPermissionsManager(ability, action, model);

  if (pm.ability.cannot(pm.action, pm.toSubject(entity))) {
    throw strapi.errors.forbidden();
  }

  return { pm, entity };
};

module.exports = {
  async generateUID(ctx) {
    const { contentTypeUID, field, data } = await validateGenerateUIDInput(ctx.request.body);

    await validateUIDField(contentTypeUID, field);

    const uidService = strapi.plugins['content-manager'].services.uid;

    ctx.body = {
      data: await uidService.generateUIDField({ contentTypeUID, field, data }),
    };
  },

  async checkUIDAvailability(ctx) {
    const { contentTypeUID, field, value } = await validateCheckUIDAvailabilityInput(
      ctx.request.body
    );

    await validateUIDField(contentTypeUID, field);

    const uidService = strapi.plugins['content-manager'].services.uid;

    const isAvailable = await uidService.checkUIDAvailability({ contentTypeUID, field, value });

    ctx.body = {
      isAvailable,
      suggestion: !isAvailable
        ? await uidService.findUniqueUID({ contentTypeUID, field, value })
        : null,
    };
  },

  /**
   * Returns a list of entities of a content-type matching the query parameters
   */
  async find(ctx) {
    const {
      state: { userAbility },
      params: { model },
      request,
    } = ctx;
    const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;
    const pm = strapi.admin.services.permission.createPermissionsManager(
      userAbility,
      ACTIONS.read,
      model
    );

    const method = _.has(request.query, '_q') ? 'search' : 'fetchAll';
    const query = pm.queryFrom(request.query);

    const { kind } = strapi.getModel(model);

    let results;

    if (kind === 'singleType') {
      // fetchAll for a singleType only return on entity
      const results = await contentManagerService.fetchAll(model, query);
      if (results && pm.ability.cannot(pm.action, pm.toSubject(results))) {
        return ctx.forbidden();
      }
    }

    results = await contentManagerService[method](model, query);

    if (!results) {
      return ctx.notFound();
    }

    ctx.body = pm.sanitize(results);
  },

  /**
   * Returns an entity of a content type by id
   */
  async findOne(ctx) {
    const {
      state: { userAbility },
      params: { model, id },
    } = ctx;

    const { pm, entity } = await findEntityAndCheckPermissions(
      userAbility,
      ACTIONS.read,
      model,
      id
    );

    ctx.body = pm.sanitize(entity);
  },

  /**
   * Returns a count of entities of a content type matching query parameters
   */
  async count(ctx) {
    const {
      state: { userAbility },
      params: { model },
      request,
    } = ctx;
    const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;

    const pm = strapi.admin.services.permission.createPermissionsManager(
      userAbility,
      ACTIONS.read,
      model
    );
    const method = _.has(request.query, '_q') ? 'countSearch' : 'count';
    const query = pm.queryFrom(request.query);

    const count = await contentManagerService[method](model, query);

    ctx.body = {
      count: _.isNumber(count) ? count : _.toNumber(count),
    };
  },

  /**
   * Creates an entity of a content type
   */
  async create(ctx) {
    const {
      state: { userAbility, user },
      params: { model },
      request: { body },
    } = ctx;
    const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;

    const pm = strapi.admin.services.permission.createPermissionsManager(
      userAbility,
      ACTIONS.create,
      model
    );

    if (pm.ability.cannot(pm.action, pm.model)) {
      throw strapi.errors.forbidden();
    }

    const sanitize = e => pm.pickPermittedFieldsOf(e, { subject: model });

    const { data, files } = ctx.is('multipart') ? parseMultipartBody(ctx) : { data: body };

    try {
      const result = await contentManagerService.create(
        {
          data: { ...sanitize(data), created_by: user.id, updated_by: user.id },
          files,
        },
        { model }
      );

      ctx.body = pm.sanitize(result, { action: ACTIONS.read });

      await strapi.telemetry.send('didCreateFirstContentTypeEntry', { model });
    } catch (error) {
      strapi.log.error(error);
      ctx.badRequest(null, [
        {
          messages: [{ id: error.message, message: error.message, field: error.field }],
          errors: _.get(error, 'data.errors'),
        },
      ]);
    }
  },

  /**
   * Updates an entity of a content type
   */
  async update(ctx) {
    const {
      state: { userAbility, user },
      params: { id, model },
      request: { body },
    } = ctx;

    const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;

    const { pm, entity } = await findEntityAndCheckPermissions(
      userAbility,
      ACTIONS.edit,
      model,
      id
    );

    const sanitize = e => pm.pickPermittedFieldsOf(e, { subject: pm.toSubject(entity) });

    const { data, files } = ctx.is('multipart') ? parseMultipartBody(ctx) : { data: body };

    try {
      const result = await contentManagerService.edit(
        { id },
        { data: { ...sanitize(data), updated_by: user.id }, files },
        { model }
      );

      ctx.body = pm.sanitize(result, { action: ACTIONS.read });
    } catch (error) {
      strapi.log.error(error);
      ctx.badRequest(null, [
        {
          messages: [{ id: error.message, message: error.message, field: error.field }],
          errors: _.get(error, 'data.errors'),
        },
      ]);
    }
  },

  /**
   * Deletes one entity of a content type matching a query
   */
  async delete(ctx) {
    const {
      state: { userAbility },
      params: { id, model },
    } = ctx;
    const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;

    const { pm } = await findEntityAndCheckPermissions(userAbility, ACTIONS.delete, model, id);

    const result = await contentManagerService.delete(model, id, pm.query);

    ctx.body = pm.sanitize(result, { action: ACTIONS.read });
  },

  /**
   * Deletes multiple entities of a content type matching a query
   */
  async deleteMany(ctx) {
    const {
      state: { userAbility },
      params: { model },
      request,
    } = ctx;
    const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;
    const pm = strapi.admin.services.permission.createPermissionsManager(
      userAbility,
      ACTIONS.delete,
      model
    );

    const results = await contentManagerService.deleteMany(
      model,
      Object.values(request.query),
      pm.query
    );

    ctx.body = results.map(result => pm.sanitize(result, { action: ACTIONS.read }));
  },

  async findRelationList(ctx) {
    const { model, targetField } = ctx.params;
    const { _component, ...query } = ctx.request.query;

    if (!targetField) {
      return ctx.badRequest();
    }

    const modelDef = _component ? strapi.db.getModel(_component) : strapi.db.getModel(model);

    if (!modelDef) {
      return ctx.notFound('model.notFound');
    }

    const attr = modelDef.attributes[targetField];
    if (!attr) {
      return ctx.badRequest('targetField.invalid');
    }

    const target = strapi.db.getModelByAssoc(attr);

    if (!target) {
      return ctx.notFound('target.notFound');
    }

    const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;

    let entities = [];

    if (_.has(ctx.request.query, '_q')) {
      entities = await contentManagerService.search(target.uid, query);
    } else {
      entities = await contentManagerService.fetchAll(target.uid, query);
    }

    if (!entities) {
      return ctx.notFound();
    }

    const modelConfig = await strapi.plugins[
      'content-manager'
    ].services.contenttypes.getConfiguration(model);

    const field = _.get(modelConfig, `metadatas.${targetField}.edit.mainField`, 'id');
    const pickFields = [field, 'id', target.primaryKey];
    const sanitize = d => _.pick(d, pickFields);

    ctx.body = _.isArray(entities) ? entities.map(sanitize) : sanitize(entities);
  },
};
<commit_msg>Fix find relation list for compo<commit_after>'use strict';

const _ = require('lodash');

const parseMultipartBody = require('../utils/parse-multipart');
const {
  validateGenerateUIDInput,
  validateCheckUIDAvailabilityInput,
  validateUIDField,
} = require('./validation');

const ACTIONS = {
  read: 'plugins::content-manager.explorer.read',
  create: 'plugins::content-manager.explorer.create',
  edit: 'plugins::content-manager.explorer.update',
  delete: 'plugins::content-manager.explorer.delete',
};

const findEntityAndCheckPermissions = async (ability, action, model, id) => {
  const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;
  const entity = await contentManagerService.fetch(model, id);

  if (_.isNil(entity)) {
    throw strapi.errors.notFound();
  }

  const pm = strapi.admin.services.permission.createPermissionsManager(ability, action, model);

  if (pm.ability.cannot(pm.action, pm.toSubject(entity))) {
    throw strapi.errors.forbidden();
  }

  return { pm, entity };
};

module.exports = {
  async generateUID(ctx) {
    const { contentTypeUID, field, data } = await validateGenerateUIDInput(ctx.request.body);

    await validateUIDField(contentTypeUID, field);

    const uidService = strapi.plugins['content-manager'].services.uid;

    ctx.body = {
      data: await uidService.generateUIDField({ contentTypeUID, field, data }),
    };
  },

  async checkUIDAvailability(ctx) {
    const { contentTypeUID, field, value } = await validateCheckUIDAvailabilityInput(
      ctx.request.body
    );

    await validateUIDField(contentTypeUID, field);

    const uidService = strapi.plugins['content-manager'].services.uid;

    const isAvailable = await uidService.checkUIDAvailability({ contentTypeUID, field, value });

    ctx.body = {
      isAvailable,
      suggestion: !isAvailable
        ? await uidService.findUniqueUID({ contentTypeUID, field, value })
        : null,
    };
  },

  /**
   * Returns a list of entities of a content-type matching the query parameters
   */
  async find(ctx) {
    const {
      state: { userAbility },
      params: { model },
      request,
    } = ctx;
    const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;
    const pm = strapi.admin.services.permission.createPermissionsManager(
      userAbility,
      ACTIONS.read,
      model
    );

    const method = _.has(request.query, '_q') ? 'search' : 'fetchAll';
    const query = pm.queryFrom(request.query);

    const { kind } = strapi.getModel(model);

    let results;

    if (kind === 'singleType') {
      // fetchAll for a singleType only return on entity
      const results = await contentManagerService.fetchAll(model, query);
      if (results && pm.ability.cannot(pm.action, pm.toSubject(results))) {
        return ctx.forbidden();
      }
    }

    results = await contentManagerService[method](model, query);

    if (!results) {
      return ctx.notFound();
    }

    ctx.body = pm.sanitize(results);
  },

  /**
   * Returns an entity of a content type by id
   */
  async findOne(ctx) {
    const {
      state: { userAbility },
      params: { model, id },
    } = ctx;

    const { pm, entity } = await findEntityAndCheckPermissions(
      userAbility,
      ACTIONS.read,
      model,
      id
    );

    ctx.body = pm.sanitize(entity);
  },

  /**
   * Returns a count of entities of a content type matching query parameters
   */
  async count(ctx) {
    const {
      state: { userAbility },
      params: { model },
      request,
    } = ctx;
    const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;

    const pm = strapi.admin.services.permission.createPermissionsManager(
      userAbility,
      ACTIONS.read,
      model
    );
    const method = _.has(request.query, '_q') ? 'countSearch' : 'count';
    const query = pm.queryFrom(request.query);

    const count = await contentManagerService[method](model, query);

    ctx.body = {
      count: _.isNumber(count) ? count : _.toNumber(count),
    };
  },

  /**
   * Creates an entity of a content type
   */
  async create(ctx) {
    const {
      state: { userAbility, user },
      params: { model },
      request: { body },
    } = ctx;
    const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;

    const pm = strapi.admin.services.permission.createPermissionsManager(
      userAbility,
      ACTIONS.create,
      model
    );

    if (pm.ability.cannot(pm.action, pm.model)) {
      throw strapi.errors.forbidden();
    }

    const sanitize = e => pm.pickPermittedFieldsOf(e, { subject: model });

    const { data, files } = ctx.is('multipart') ? parseMultipartBody(ctx) : { data: body };

    try {
      const result = await contentManagerService.create(
        {
          data: { ...sanitize(data), created_by: user.id, updated_by: user.id },
          files,
        },
        { model }
      );

      ctx.body = pm.sanitize(result, { action: ACTIONS.read });

      await strapi.telemetry.send('didCreateFirstContentTypeEntry', { model });
    } catch (error) {
      strapi.log.error(error);
      ctx.badRequest(null, [
        {
          messages: [{ id: error.message, message: error.message, field: error.field }],
          errors: _.get(error, 'data.errors'),
        },
      ]);
    }
  },

  /**
   * Updates an entity of a content type
   */
  async update(ctx) {
    const {
      state: { userAbility, user },
      params: { id, model },
      request: { body },
    } = ctx;

    const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;

    const { pm, entity } = await findEntityAndCheckPermissions(
      userAbility,
      ACTIONS.edit,
      model,
      id
    );

    const sanitize = e => pm.pickPermittedFieldsOf(e, { subject: pm.toSubject(entity) });

    const { data, files } = ctx.is('multipart') ? parseMultipartBody(ctx) : { data: body };

    try {
      const result = await contentManagerService.edit(
        { id },
        { data: { ...sanitize(data), updated_by: user.id }, files },
        { model }
      );

      ctx.body = pm.sanitize(result, { action: ACTIONS.read });
    } catch (error) {
      strapi.log.error(error);
      ctx.badRequest(null, [
        {
          messages: [{ id: error.message, message: error.message, field: error.field }],
          errors: _.get(error, 'data.errors'),
        },
      ]);
    }
  },

  /**
   * Deletes one entity of a content type matching a query
   */
  async delete(ctx) {
    const {
      state: { userAbility },
      params: { id, model },
    } = ctx;
    const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;

    const { pm } = await findEntityAndCheckPermissions(userAbility, ACTIONS.delete, model, id);

    const result = await contentManagerService.delete(model, id, pm.query);

    ctx.body = pm.sanitize(result, { action: ACTIONS.read });
  },

  /**
   * Deletes multiple entities of a content type matching a query
   */
  async deleteMany(ctx) {
    const {
      state: { userAbility },
      params: { model },
      request,
    } = ctx;
    const contentManagerService = strapi.plugins['content-manager'].services.contentmanager;
    const pm = strapi.admin.services.permission.createPermissionsManager(
      userAbility,
      ACTIONS.delete,
      model
    );

    const results = await contentManagerService.deleteMany(
      model,
      Object.values(request.query),
      pm.query
    );

    ctx.body = results.map(result => pm.sanitize(result, { action: ACTIONS.read }));
  },

  async findRelationList(ctx) {
    const { model, targetField } = ctx.params;
    const { _component, ...query } = ctx.request.query;

    const contentManagerServices = strapi.plugins['content-manager'].services;

    if (!targetField) {
      return ctx.badRequest();
    }

    const modelDef = _component ? strapi.db.getModel(_component) : strapi.db.getModel(model);

    if (!modelDef) {
      return ctx.notFound('model.notFound');
    }

    const attr = modelDef.attributes[targetField];
    if (!attr) {
      return ctx.badRequest('targetField.invalid');
    }

    const target = strapi.db.getModelByAssoc(attr);

    if (!target) {
      return ctx.notFound('target.notFound');
    }

    const contentManagerService = contentManagerServices.contentmanager;

    let entities = [];

    if (_.has(ctx.request.query, '_q')) {
      entities = await contentManagerService.search(target.uid, query);
    } else {
      entities = await contentManagerService.fetchAll(target.uid, query);
    }

    if (!entities) {
      return ctx.notFound();
    }

    const modelConfig = _component
      ? await contentManagerServices.components.getConfiguration(modelDef.uid)
      : await contentManagerServices.contenttypes.getConfiguration(modelDef.uid);

    const field = _.get(modelConfig, `metadatas.${targetField}.edit.mainField`, 'id');
    const pickFields = [field, 'id', target.primaryKey];
    const sanitize = d => _.pick(d, pickFields);

    ctx.body = _.isArray(entities) ? entities.map(sanitize) : sanitize(entities);
  },
};
<|endoftext|>


<commit_before>/* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</copyright> */

var Q = require("q");
var FS = require("q-io/fs");
var HTTP = require("q-io/http");
var Apps = require("q-io/http-apps");
var Route = require("./lib/route");

var Chain = function (end) {
    var self = Object.create(Chain.prototype);
    self.end = end || function (next) {
        return next;
    };
    return self;
};

var chain = Chain.prototype;

// Primitives

chain.use = function (App /*, ...args*/) {
    var args = Array.prototype.slice.call(arguments, 1);
    this.end = (function (End) {
        return function (next) {
            return End(App.apply(null, [next].concat(args)));
        };
    })(this.end);
    return this;
};

chain.terminate = function () {
    if (!this.use) {
        throw new Error("Cannot add links a terminated chain.");
    }
    var result = this.use.apply(this, arguments);
    // forcibly prevent any further chaining
    this.use = null;
    return result;
};

chain.add = function (App, name) {
    name = App.name || name;
    name = name[0].toLowerCase() + name.slice(1);
    this[name] = function () {
        var args = Array.prototype.slice.call(arguments);
        return this.use.apply(
            this,
            [App].concat(args)
        );
    };
};

// Middleware

chain.blah =
chain.blahblah =
chain.blahblahblah =
chain.useCommon =
chain.common =
function (options) {
    options = options || {};
    return this
    .time()
    .favicon(options.favicon)
    .error(options.debug)
    .log(options.log, options.stamp)
    .parseQuery()
    .normalize()
};

// Create easy "use" wrappers for all of the following middleware
[
    "Tap",
    "Trap",
    "Error",
    "Log",
    "Headers", // TODO document in README
    "Date", // TODO document in README
    "Permanent", // TODO document in README
    "Time", // TODO document in README.md
    "Normalize", // TODO document in README.md
    "RedirectTrap",
    "CookieJar",
    "ParseQuery", // TODO document in README.md
    "DirectoryIndex",
    "ListDirectories",
    "HandleHtmlFragmentResponses",
    "HandleJsonResponses"
].forEach(function (name) {
    chain.add(Apps[name], name);
});

// TODO document in README.md
chain.cors = function (origin, methods, headers) {
    var include = {
        "access-control-allow-origin": origin || "*"
    };
    if (methods) {
        if (Array.isArray(methods))
            methods = methods.join(", ");
        include["access-control-allow-methods"] = methods;
    }
    if (headers) {
        if (Array.isArray(headers))
            headers = headers.join(", ");
        include["access-control-allow-headers"] = headers;
    }
    return this.headers(include);
};

chain.favicon = function (path) {
    var app;
    // TODO vanity icon
    if (path) {
        path = FS.join(arguments);
        app = Apps.File(path);
    } else {
        app = Apps.notFound;
    }
    return this.use(function (next) {
        return function (request, response) {
            if (request.pathInfo === "/favicon.ico") {
                return app(request, response);
            } else {
                return next(request, response);
            }
        };
    });
};

// Routing and Negotiation

chain.route = function (prefix, setup) {
    return this.use(Route.Setup(this, prefix, setup));
};

chain.branch = function (paths) {
    return this.use(function (next) {
        return Apps.Branch(paths, next);
    });
};

var Multiplex = function (Using) {
    return function (setup) {
        var branches = {};
        var chain = this;
        setup(function () {
            var branch = new chain.constructor();
            Array.prototype.forEach.call(arguments, function (name) {
                branches[name] = branch;
            });
            return branch;
        });
        return this.use(function (next) {
            Object.keys(branches).forEach(function (name) {
                var branch = branches[name];
                branches[name] = branch.end();
            });
            return Using(branches, next);
        });
    };
};

var Constrain = function (Using) {
    return function () {
        var args = arguments;
        var types = {};
        return this.use(function (next) {
            Array.prototype.forEach.call(args, function (type) {
                types[type] = next;
            });
            return Using(types);
        });
    }
};

chain.method = Constrain(Apps.Method);
chain.methods = Multiplex(Apps.Method);
chain.contentType = Constrain(Apps.ContentType);
chain.contentTypes = Multiplex(Apps.ContentType);
chain.language = Constrain(Apps.Language);
chain.languages = Multiplex(Apps.Language);
chain.charset = Constrain(Apps.Charset);
chain.charsets = Multiplex(Apps.Charset);
chain.encoding = Constrain(Apps.Encoding);
chain.encodings = Multiplex(Apps.Encoding);
chain.host = Constrain(Apps.Host);
chain.hosts = Multiplex(Apps.Host);

// Endware

chain.cap = function (notFound) {
    return this.use(function (next) {
        return Apps.Cap(next, notFound);
    });
};

chain.app = function (app) {
    return this.terminate(function () {
        return app;
    });
};

chain.contentApp = function (app) {
    return this.terminate(function () {
        return function (request, response) {
            return Q.when(app(request, response), function (content) {
                return Apps.ok(content);
            });
        };
    });
};

chain.proxy = function (app) {
    return this.use(function () {
        return Apps.Proxy(app);
    });
};

chain.proxyTree = function (app) {
    return this.use(function () {
        return Apps.ProxyTree(app);
    });
};

['badRequest', 'notFound', 'methodNotAllowed', 'notAcceptable']
.forEach(function (name) {
    var app = Apps[name];
    chain[name] = function (app) {
        return this.app(app);
    };
});

chain.nap = function (nodeApp) {
    return this.terminate(function () {
        return function (request, response) {
            nodeApp(request.nodeRequest, response.nodeResponse || response);
        };
    });
};

chain.ok =
chain.content = function (body, contentType, status) {
    if (!Array.isArray(body)) {
        body = [body];
    }
    return this.terminate(function () {
        return Apps.Content(body, contentType, status);
    });
};

chain.file = function (path, contentType) {
    return this.terminate(function () {
        return Apps.File(path, contentType);
    });
};

chain.fileTree = function (root, options) {
    return this.terminate(function (next) {
        options = options || {};
        options.notFound = next;
        return Apps.FileTree(root, options);
    });
};

chain.redirect = function (path) {
    return this.terminate(function () {
        return Apps.Redirect(path);
    });
};

chain.redirectTree = function (path) {
    return this.terminate(function () {
        return Apps.RedirectTree(path);
    });
};

chain.redirectPermanent =
chain.permanentRedirect = function (path, status) {
    return this.terminate(function () {
        return Apps.PermanentRedirect(path, status);
    });
};

chain.redirect =
chain.redirectTemporary =
chain.temporaryRedirect = function (path, status) {
    return this.terminate(function () {
        return Apps.TemporaryRedirect(path, status);
    });
};

// Preprocessing

chain.contentRequest = function () {
    return this.use(Apps.ContentRequest);
};

chain.jsonRequest = function (visitor, tabs) {
    return this.use(function (next) {
        return Apps.JsonRequest(next, visitor, tabs);
    });
};

// Postprocessing

chain.json = function (visitor, tabs) {
    return this.use(function (next) {
        return Apps.Json(next, visitor, tabs);
    });
};

// Enders

chain.client = function () {
    return this.end(HTTP.request);
};

chain.server = function () {
    return HTTP.Server(this.end())
};

chain.listen = function (port, host) {
    var server = this.server();
    return server.listen.apply(server, arguments);
};

// Extender

chain.extend = function (extension) {
    var prototype = Object.create(this, {
        "constructor": {
            "value": function (appMaker) {
                return Object.create(prototype);
            }
        }
    });
    for (var name in extension) {
        prototype[name] = extension[name];
    }
    return prototype;
};

// Adds functions that create responders
chain.install = function () {
    var properties = {};
    Array.prototype.forEach.call(arguments, function (arg) {
        if (typeof arg === "object") {
            var makers = arg;
            Object.keys(makers).forEach(function (key) {
                var maker = makers[key];
                properties[key] = function () {
                    var args = arguments;
                    return this.use(function (next) {
                        return maker.apply(this, args);
                    });
                };
            });
        } else if (typeof arg === "function") {
            var maker = arg;
            properties[maker.name] = function () {
                var args = arguments;
                return this.use(function (next) {
                    return maker.apply(this, args);
                });
            };
        }
    });
    return this.extend(properties);
};

// exports

// proxy all the chain methods, doing a .create() first
// to start a chain.
for (name in chain) {
    if (Object.prototype.hasOwnProperty.call(chain, name)) {
        (function (name) {
            exports[name] = function () {
                var chain = exports.create();
                return chain[name].apply(chain, arguments);
            }
        })(name);
    }
}

exports.create = function (prefix, setup) {
    var chain = Chain();
    if (prefix) {
        chain = chain.route(prefix, setup);
    }
    return chain;
};

<commit_msg>Thread fs arg through file app<commit_after>/* <copyright>
Copyright (c) 2012, Motorola Mobility LLC.
All Rights Reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice,
  this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of Motorola Mobility LLC nor the names of its
  contributors may be used to endorse or promote products derived from this
  software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
</copyright> */

var Q = require("q");
var FS = require("q-io/fs");
var HTTP = require("q-io/http");
var Apps = require("q-io/http-apps");
var Route = require("./lib/route");

var Chain = function (end) {
    var self = Object.create(Chain.prototype);
    self.end = end || function (next) {
        return next;
    };
    return self;
};

var chain = Chain.prototype;

// Primitives

chain.use = function (App /*, ...args*/) {
    var args = Array.prototype.slice.call(arguments, 1);
    this.end = (function (End) {
        return function (next) {
            return End(App.apply(null, [next].concat(args)));
        };
    })(this.end);
    return this;
};

chain.terminate = function () {
    if (!this.use) {
        throw new Error("Cannot add links a terminated chain.");
    }
    var result = this.use.apply(this, arguments);
    // forcibly prevent any further chaining
    this.use = null;
    return result;
};

chain.add = function (App, name) {
    name = App.name || name;
    name = name[0].toLowerCase() + name.slice(1);
    this[name] = function () {
        var args = Array.prototype.slice.call(arguments);
        return this.use.apply(
            this,
            [App].concat(args)
        );
    };
};

// Middleware

chain.blah =
chain.blahblah =
chain.blahblahblah =
chain.useCommon =
chain.common =
function (options) {
    options = options || {};
    return this
    .time()
    .favicon(options.favicon)
    .error(options.debug)
    .log(options.log, options.stamp)
    .parseQuery()
    .normalize()
};

// Create easy "use" wrappers for all of the following middleware
[
    "Tap",
    "Trap",
    "Error",
    "Log",
    "Headers", // TODO document in README
    "Date", // TODO document in README
    "Permanent", // TODO document in README
    "Time", // TODO document in README.md
    "Normalize", // TODO document in README.md
    "RedirectTrap",
    "CookieJar",
    "ParseQuery", // TODO document in README.md
    "DirectoryIndex",
    "ListDirectories",
    "HandleHtmlFragmentResponses",
    "HandleJsonResponses"
].forEach(function (name) {
    chain.add(Apps[name], name);
});

// TODO document in README.md
chain.cors = function (origin, methods, headers) {
    var include = {
        "access-control-allow-origin": origin || "*"
    };
    if (methods) {
        if (Array.isArray(methods))
            methods = methods.join(", ");
        include["access-control-allow-methods"] = methods;
    }
    if (headers) {
        if (Array.isArray(headers))
            headers = headers.join(", ");
        include["access-control-allow-headers"] = headers;
    }
    return this.headers(include);
};

chain.favicon = function (path) {
    var app;
    // TODO vanity icon
    if (path) {
        path = FS.join(arguments);
        app = Apps.File(path);
    } else {
        app = Apps.notFound;
    }
    return this.use(function (next) {
        return function (request, response) {
            if (request.pathInfo === "/favicon.ico") {
                return app(request, response);
            } else {
                return next(request, response);
            }
        };
    });
};

// Routing and Negotiation

chain.route = function (prefix, setup) {
    return this.use(Route.Setup(this, prefix, setup));
};

chain.branch = function (paths) {
    return this.use(function (next) {
        return Apps.Branch(paths, next);
    });
};

var Multiplex = function (Using) {
    return function (setup) {
        var branches = {};
        var chain = this;
        setup(function () {
            var branch = new chain.constructor();
            Array.prototype.forEach.call(arguments, function (name) {
                branches[name] = branch;
            });
            return branch;
        });
        return this.use(function (next) {
            Object.keys(branches).forEach(function (name) {
                var branch = branches[name];
                branches[name] = branch.end();
            });
            return Using(branches, next);
        });
    };
};

var Constrain = function (Using) {
    return function () {
        var args = arguments;
        var types = {};
        return this.use(function (next) {
            Array.prototype.forEach.call(args, function (type) {
                types[type] = next;
            });
            return Using(types);
        });
    }
};

chain.method = Constrain(Apps.Method);
chain.methods = Multiplex(Apps.Method);
chain.contentType = Constrain(Apps.ContentType);
chain.contentTypes = Multiplex(Apps.ContentType);
chain.language = Constrain(Apps.Language);
chain.languages = Multiplex(Apps.Language);
chain.charset = Constrain(Apps.Charset);
chain.charsets = Multiplex(Apps.Charset);
chain.encoding = Constrain(Apps.Encoding);
chain.encodings = Multiplex(Apps.Encoding);
chain.host = Constrain(Apps.Host);
chain.hosts = Multiplex(Apps.Host);

// Endware

chain.cap = function (notFound) {
    return this.use(function (next) {
        return Apps.Cap(next, notFound);
    });
};

chain.app = function (app) {
    return this.terminate(function () {
        return app;
    });
};

chain.contentApp = function (app) {
    return this.terminate(function () {
        return function (request, response) {
            return Q.when(app(request, response), function (content) {
                return Apps.ok(content);
            });
        };
    });
};

chain.proxy = function (app) {
    return this.use(function () {
        return Apps.Proxy(app);
    });
};

chain.proxyTree = function (app) {
    return this.use(function () {
        return Apps.ProxyTree(app);
    });
};

['badRequest', 'notFound', 'methodNotAllowed', 'notAcceptable']
.forEach(function (name) {
    var app = Apps[name];
    chain[name] = function (app) {
        return this.app(app);
    };
});

chain.nap = function (nodeApp) {
    return this.terminate(function () {
        return function (request, response) {
            nodeApp(request.nodeRequest, response.nodeResponse || response);
        };
    });
};

chain.ok =
chain.content = function (body, contentType, status) {
    if (!Array.isArray(body)) {
        body = [body];
    }
    return this.terminate(function () {
        return Apps.Content(body, contentType, status);
    });
};

chain.file = function (path, contentType, fs) {
    return this.terminate(function () {
        return Apps.File(path, contentType, fs);
    });
};

chain.fileTree = function (root, options) {
    return this.terminate(function (next) {
        options = options || {};
        options.notFound = next;
        return Apps.FileTree(root, options);
    });
};

chain.redirect = function (path) {
    return this.terminate(function () {
        return Apps.Redirect(path);
    });
};

chain.redirectTree = function (path) {
    return this.terminate(function () {
        return Apps.RedirectTree(path);
    });
};

chain.redirectPermanent =
chain.permanentRedirect = function (path, status) {
    return this.terminate(function () {
        return Apps.PermanentRedirect(path, status);
    });
};

chain.redirect =
chain.redirectTemporary =
chain.temporaryRedirect = function (path, status) {
    return this.terminate(function () {
        return Apps.TemporaryRedirect(path, status);
    });
};

// Preprocessing

chain.contentRequest = function () {
    return this.use(Apps.ContentRequest);
};

chain.jsonRequest = function (visitor, tabs) {
    return this.use(function (next) {
        return Apps.JsonRequest(next, visitor, tabs);
    });
};

// Postprocessing

chain.json = function (visitor, tabs) {
    return this.use(function (next) {
        return Apps.Json(next, visitor, tabs);
    });
};

// Enders

chain.client = function () {
    return this.end(HTTP.request);
};

chain.server = function () {
    return HTTP.Server(this.end())
};

chain.listen = function (port, host) {
    var server = this.server();
    return server.listen.apply(server, arguments);
};

// Extender

chain.extend = function (extension) {
    var prototype = Object.create(this, {
        "constructor": {
            "value": function (appMaker) {
                return Object.create(prototype);
            }
        }
    });
    for (var name in extension) {
        prototype[name] = extension[name];
    }
    return prototype;
};

// Adds functions that create responders
chain.install = function () {
    var properties = {};
    Array.prototype.forEach.call(arguments, function (arg) {
        if (typeof arg === "object") {
            var makers = arg;
            Object.keys(makers).forEach(function (key) {
                var maker = makers[key];
                properties[key] = function () {
                    var args = arguments;
                    return this.use(function (next) {
                        return maker.apply(this, args);
                    });
                };
            });
        } else if (typeof arg === "function") {
            var maker = arg;
            properties[maker.name] = function () {
                var args = arguments;
                return this.use(function (next) {
                    return maker.apply(this, args);
                });
            };
        }
    });
    return this.extend(properties);
};

// exports

// proxy all the chain methods, doing a .create() first
// to start a chain.
for (name in chain) {
    if (Object.prototype.hasOwnProperty.call(chain, name)) {
        (function (name) {
            exports[name] = function () {
                var chain = exports.create();
                return chain[name].apply(chain, arguments);
            }
        })(name);
    }
}

exports.create = function (prefix, setup) {
    var chain = Chain();
    if (prefix) {
        chain = chain.route(prefix, setup);
    }
    return chain;
};

<|endoftext|>


<commit_before>var update = function() {
	var on = "white";
	var off = "#303030";
	current = new Date();
	var hour = current.getHours();
	if (hour > 12) {hour -= 12};
	var minutes = current.getMinutes();

	document.getElementById("its").style.color=on;
	document.getElementById("minutes").style.color=on;

	// set modifier
	if (minutes < 3){
		document.getElementById("oclock").style.color=on;
		document.getElementById("minutes").style.color=off;
	}else if (minutes < 33){
		document.getElementById("past").style.color=on;
	}else if (minutes <= 57){
		document.getElementById("to").style.color=on;
		hour === 12 ? hour = 1: hour += 1;
		minutes = 60 - minutes;
	}else{
		document.getElementById("oclock").style.color=on;
		document.getElementById("minutes").style.color=off;
		hour === 12 ? hour = 1: hour += 1;
		minutes = 60 - minutes;
	}

	//set minutes
	if (minutes >= 3){
	       if(minutes < 7){
			document.getElementById("fiveMod").style.color=on;
		}else if (minutes < 13){
			document.getElementById("tenMod").style.color=on;
		}else if (minutes < 17){
			document.getElementById("quarter").style.color=on;
			document.getElementById("minutes").style.color=off;
		}else if (minutes < 23){
			document.getElementById("twenty").style.color=on;
		}else if (minutes < 27){
			document.getElementById("twenty").style.color=on;
			document.getElementById("fiveMod").style.color=on;
		}else if (minutes < 33){
			document.getElementById("half").style.color=on;
			document.getElementById("minutes").style.color=off;

		}
	}

	//set hours
	switch (hour){
		case 1 :
			document.getElementById("one").style.color=on;
			break;
		case 2 :
			document.getElementById("two").style.color=on;
			break;
		case 3 :
			document.getElementById("three").style.color=on;
			break;
		case 4 :
			document.getElementById("four").style.color=on;
			break;
		case 5 :
			document.getElementById("five").style.color=on;
			break;
		case 6 :
			document.getElementById("six").style.color=on;
			break;
		case 7 :
			document.getElementById("seven").style.color=on;
			break;
		case 8 :
			document.getElementById("eight").style.color=on;
			break;
		case 9 :
			document.getElementById("nine").style.color=on;
			break;
		case 10 :
			document.getElementById("ten").style.color=on;
			break;
		case 11 :
			document.getElementById("eleven").style.color=on;
			break;
		case 12 :
			document.getElementById("twelve").style.color=on;
			break;
	}
}

//onload = update;
setInterval(update,1000);
<commit_msg>Refresh added to js - DONE<commit_after>var refresh = function(){
	// shuts all elements off
	var allIds = ["half", "tenMod","quarter", "twenty", "fiveMod", "minutes", "to", "past", "one", "three", "two", "four", "five",
		    "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "oclock"];
	for ( var i = 0; i < 21; i++){
		document.getElementById(allIds[i]).style.color="#303030";
	}
}

var update = function() {
	// initialise screen
	var on = "white";
	var off = "#303030";
	document.getElementById("its").style.color=on;
	refresh();

	// initialise date variables
	current = new Date();
	var hour = current.getHours();
	if (hour > 12) {hour -= 12};
	var minutes = current.getMinutes();

	// set modifier
	if (minutes < 3){
		document.getElementById("oclock").style.color=on;
	}else if (minutes < 33){
		document.getElementById("past").style.color=on;
		document.getElementById("minutes").style.color=on;
	}else if (minutes <= 57){
		document.getElementById("to").style.color=on;
		document.getElementById("minutes").style.color=on;
		hour === 12 ? hour = 1: hour += 1;
		minutes = 60 - minutes;
	}else{
		document.getElementById("oclock").style.color=on;
		hour === 12 ? hour = 1: hour += 1;
		minutes = 60 - minutes;
	}

	//set minutes
	if (minutes >= 3){
	       if(minutes < 7){
			document.getElementById("fiveMod").style.color=on;
		}else if (minutes < 13){
			document.getElementById("tenMod").style.color=on;
		}else if (minutes < 17){
			document.getElementById("quarter").style.color=on;
			document.getElementById("minutes").style.color=off;
		}else if (minutes < 23){
			document.getElementById("twenty").style.color=on;
		}else if (minutes < 27){
			document.getElementById("twenty").style.color=on;
			document.getElementById("fiveMod").style.color=on;
		}else if (minutes < 33){
			document.getElementById("half").style.color=on;
			document.getElementById("minutes").style.color=off;

		}
	}

	//set hours
	switch (hour){
		case 1 :
			document.getElementById("one").style.color=on;
			break;
		case 2 :
			document.getElementById("two").style.color=on;
			break;
		case 3 :
			document.getElementById("three").style.color=on;
			break;
		case 4 :
			document.getElementById("four").style.color=on;
			break;
		case 5 :
			document.getElementById("five").style.color=on;
			break;
		case 6 :
			document.getElementById("six").style.color=on;
			break;
		case 7 :
			document.getElementById("seven").style.color=on;
			break;
		case 8 :
			document.getElementById("eight").style.color=on;
			break;
		case 9 :
			document.getElementById("nine").style.color=on;
			break;
		case 10 :
			document.getElementById("ten").style.color=on;
			break;
		case 11 :
			document.getElementById("eleven").style.color=on;
			break;
		case 12 :
			document.getElementById("twelve").style.color=on;
			break;
	}
}

// run update on load and repeat every 30 seconds afterwards
onload = update;
setInterval(update,30000);
<|endoftext|>


<commit_before>/**
 * ga.js - analytics adapter for google analytics
 */

var events = require('./../../events');
var utils = require('./../../utils');
var CONSTANTS = require('./../../constants.json');

var BID_REQUESTED = CONSTANTS.EVENTS.BID_REQUESTED;
var BID_TIMEOUT = CONSTANTS.EVENTS.BID_TIMEOUT;
var BID_RESPONSE = CONSTANTS.EVENTS.BID_RESPONSE;
var BID_WON = CONSTANTS.EVENTS.BID_WON;

var _disableInteraction = { nonInteraction: true };
var _analyticsQueue = [];
var _gaGlobal = null;
var _enableCheck = true;
var _category = 'Prebid.js Bids';
var _eventCount = 0;
var _enableDistribution = false;
var _trackerSend = null;

/**
 * This will enable sending data to google analytics. Only call once, or duplicate data will be sent!
 * @param  {object} provider use to set GA global (if renamed);
 * @param  {object} options use to configure adapter;
 * @return {[type]}    [description]
 */
exports.enableAnalytics = function ({ provider, options }) {

  _gaGlobal = provider || 'ga';
  _trackerSend = options && options.trackerName ? options.trackerName + '.send' : 'send';

  if (options && typeof options.enableDistribution !== 'undefined') {
    _enableDistribution = options.enableDistribution;
  }

  var bid = null;

  //first send all events fired before enableAnalytics called

  var existingEvents = events.getEvents();
  utils._each(existingEvents, function (eventObj) {
    var args = eventObj.args;
    if (!eventObj) {
      return;
    }

    if (eventObj.eventType === BID_REQUESTED) {
      bid = args;
      sendBidRequestToGa(bid);
    } else if (eventObj.eventType === BID_RESPONSE) {
      //bid is 2nd args
      bid = args;
      sendBidResponseToGa(bid);

    } else if (eventObj.eventType === BID_TIMEOUT) {
      const bidderArray = args;
      sendBidTimeouts(bidderArray);
    } else if (eventObj.eventType === BID_WON) {
      bid = args;
      sendBidWonToGa(bid);
    }
  });

  //Next register event listeners to send data immediately

  //bidRequests
  events.on(BID_REQUESTED, function (bidRequestObj) {
    sendBidRequestToGa(bidRequestObj);
  });

  //bidResponses
  events.on(BID_RESPONSE, function (bid) {
    sendBidResponseToGa(bid);
  });

  //bidTimeouts
  events.on(BID_TIMEOUT, function (bidderArray) {
    sendBidTimeouts(bidderArray);
  });

  //wins
  events.on(BID_WON, function (bid) {
    sendBidWonToGa(bid);
  });

  // finally set this function to return log message, prevents multiple adapter listeners
  this.enableAnalytics = function _enable() {
    return utils.logMessage(`Analytics adapter already enabled, unnecessary call to \`enableAnalytics\`.`);
  };
};

exports.getTrackerSend = function getTrackerSend() {
  return _trackerSend;
};

/**
 * Check if gaGlobal or window.ga is defined on page. If defined execute all the GA commands
 */
function checkAnalytics() {
  if (_enableCheck && typeof window[_gaGlobal] === 'function') {

    for (var i = 0; i < _analyticsQueue.length; i++) {
      _analyticsQueue[i].call();
    }

    //override push to execute the command immediately from now on
    _analyticsQueue.push = function (fn) {
      fn.call();
    };

    //turn check into NOOP
    _enableCheck = false;
  }

  utils.logMessage('event count sent to GA: ' + _eventCount);
}

function convertToCents(dollars) {
  if (dollars) {
    return Math.floor(dollars * 100);
  }

  return 0;
}

function getLoadTimeDistribution(time) {
  var distribution;
  if (time >= 0 && time < 200) {
    distribution = '0-200ms';
  } else if (time >= 200 && time < 300) {
    distribution = '0200-300ms';
  } else if (time >= 300 && time < 400) {
    distribution = '0300-400ms';
  } else if (time >= 400 && time < 500) {
    distribution = '0400-500ms';
  } else if (time >= 500 && time < 600) {
    distribution = '0500-600ms';
  } else if (time >= 600 && time < 800) {
    distribution = '0600-800ms';
  } else if (time >= 800 && time < 1000) {
    distribution = '0800-1000ms';
  } else if (time >= 1000 && time < 1200) {
    distribution = '1000-1200ms';
  } else if (time >= 1200 && time < 1500) {
    distribution = '1200-1500ms';
  } else if (time >= 1500 && time < 2000) {
    distribution = '1500-2000ms';
  } else if (time >= 2000) {
    distribution = '2000ms above';
  }

  return distribution;
}

function getCpmDistribution(cpm) {
  var distribution;
  if (cpm >= 0 && cpm < 0.5) {
    distribution = '$0-0.5';
  } else if (cpm >= 0.5 && cpm < 1) {
    distribution = '$0.5-1';
  } else if (cpm >= 1 && cpm < 1.5) {
    distribution = '$1-1.5';
  } else if (cpm >= 1.5 && cpm < 2) {
    distribution = '$1.5-2';
  } else if (cpm >= 2 && cpm < 2.5) {
    distribution = '$2-2.5';
  } else if (cpm >= 2.5 && cpm < 3) {
    distribution = '$2.5-3';
  } else if (cpm >= 3 && cpm < 4) {
    distribution = '$3-4';
  } else if (cpm >= 4 && cpm < 6) {
    distribution = '$4-6';
  } else if (cpm >= 6 && cpm < 8) {
    distribution = '$6-8';
  } else if (cpm >= 8) {
    distribution = '$8 above';
  }

  return distribution;
}

function sendBidRequestToGa(bid) {
  if (bid && bid.bidderCode) {
    _analyticsQueue.push(function () {
      _eventCount++;
      window[_gaGlobal](_trackerSend, 'event', _category, 'Requests', bid.bidderCode, 1, _disableInteraction);
    });
  }

  //check the queue
  checkAnalytics();
}

function sendBidResponseToGa(bid) {

  if (bid && bid.bidderCode) {
    _analyticsQueue.push(function () {
      var cpmCents = convertToCents(bid.cpm);
      var bidder = bid.bidderCode;
      if (typeof bid.timeToRespond !== 'undefined' && _enableDistribution) {
        _eventCount++;
        var dis = getLoadTimeDistribution(bid.timeToRespond);
        window[_gaGlobal](_trackerSend, 'event', 'Prebid.js Load Time Distribution', dis, bidder, 1, _disableInteraction);
      }

      if (bid.cpm > 0) {
        _eventCount = _eventCount + 2;
        var cpmDis = getCpmDistribution(bid.cpm);
        if (_enableDistribution) {
          _eventCount++;
          window[_gaGlobal](_trackerSend, 'event', 'Prebid.js CPM Distribution', cpmDis, bidder, 1, _disableInteraction);
        }

        window[_gaGlobal](_trackerSend, 'event', _category, 'Bids', bidder, cpmCents, _disableInteraction);
        window[_gaGlobal](_trackerSend, 'event', _category, 'Bid Load Time', bidder, bid.timeToRespond, _disableInteraction);
      }
    });
  }

  //check the queue
  checkAnalytics();
}

function sendBidTimeouts(timedOutBidders) {

  _analyticsQueue.push(function () {
    utils._each(timedOutBidders, function (bidderCode) {
      _eventCount++;
      window[_gaGlobal](_trackerSend, 'event', _category, 'Timeouts', bidderCode, _disableInteraction);
    });
  });

  checkAnalytics();
}

function sendBidWonToGa(bid) {
  var cpmCents = convertToCents(bid.cpm);
  _analyticsQueue.push(function () {
    _eventCount++;
    window[_gaGlobal](_trackerSend, 'event', _category, 'Wins', bid.bidderCode, cpmCents, _disableInteraction);
  });

  checkAnalytics();
}
<commit_msg>Allow changing of 'ga' global variable (#832)<commit_after>/**
 * ga.js - analytics adapter for google analytics
 */

var events = require('./../../events');
var utils = require('./../../utils');
var CONSTANTS = require('./../../constants.json');

var BID_REQUESTED = CONSTANTS.EVENTS.BID_REQUESTED;
var BID_TIMEOUT = CONSTANTS.EVENTS.BID_TIMEOUT;
var BID_RESPONSE = CONSTANTS.EVENTS.BID_RESPONSE;
var BID_WON = CONSTANTS.EVENTS.BID_WON;

var _disableInteraction = { nonInteraction: true };
var _analyticsQueue = [];
var _gaGlobal = null;
var _enableCheck = true;
var _category = 'Prebid.js Bids';
var _eventCount = 0;
var _enableDistribution = false;
var _trackerSend = null;

/**
 * This will enable sending data to google analytics. Only call once, or duplicate data will be sent!
 * @param  {object} provider use to set GA global (if renamed);
 * @param  {object} options use to configure adapter;
 * @return {[type]}    [description]
 */
exports.enableAnalytics = function ({ provider, options }) {

  _gaGlobal = provider || 'ga';
  _trackerSend = options && options.trackerName ? options.trackerName + '.send' : 'send';

  if (options && typeof options.global !== 'undefined') {
    _gaGlobal = options.global;
  }
  if (options && typeof options.enableDistribution !== 'undefined') {
    _enableDistribution = options.enableDistribution;
  }

  var bid = null;

  //first send all events fired before enableAnalytics called

  var existingEvents = events.getEvents();
  utils._each(existingEvents, function (eventObj) {
    var args = eventObj.args;
    if (!eventObj) {
      return;
    }

    if (eventObj.eventType === BID_REQUESTED) {
      bid = args;
      sendBidRequestToGa(bid);
    } else if (eventObj.eventType === BID_RESPONSE) {
      //bid is 2nd args
      bid = args;
      sendBidResponseToGa(bid);

    } else if (eventObj.eventType === BID_TIMEOUT) {
      const bidderArray = args;
      sendBidTimeouts(bidderArray);
    } else if (eventObj.eventType === BID_WON) {
      bid = args;
      sendBidWonToGa(bid);
    }
  });

  //Next register event listeners to send data immediately

  //bidRequests
  events.on(BID_REQUESTED, function (bidRequestObj) {
    sendBidRequestToGa(bidRequestObj);
  });

  //bidResponses
  events.on(BID_RESPONSE, function (bid) {
    sendBidResponseToGa(bid);
  });

  //bidTimeouts
  events.on(BID_TIMEOUT, function (bidderArray) {
    sendBidTimeouts(bidderArray);
  });

  //wins
  events.on(BID_WON, function (bid) {
    sendBidWonToGa(bid);
  });

  // finally set this function to return log message, prevents multiple adapter listeners
  this.enableAnalytics = function _enable() {
    return utils.logMessage(`Analytics adapter already enabled, unnecessary call to \`enableAnalytics\`.`);
  };
};

exports.getTrackerSend = function getTrackerSend() {
  return _trackerSend;
};

/**
 * Check if gaGlobal or window.ga is defined on page. If defined execute all the GA commands
 */
function checkAnalytics() {
  if (_enableCheck && typeof window[_gaGlobal] === 'function') {

    for (var i = 0; i < _analyticsQueue.length; i++) {
      _analyticsQueue[i].call();
    }

    //override push to execute the command immediately from now on
    _analyticsQueue.push = function (fn) {
      fn.call();
    };

    //turn check into NOOP
    _enableCheck = false;
  }

  utils.logMessage('event count sent to GA: ' + _eventCount);
}

function convertToCents(dollars) {
  if (dollars) {
    return Math.floor(dollars * 100);
  }

  return 0;
}

function getLoadTimeDistribution(time) {
  var distribution;
  if (time >= 0 && time < 200) {
    distribution = '0-200ms';
  } else if (time >= 200 && time < 300) {
    distribution = '0200-300ms';
  } else if (time >= 300 && time < 400) {
    distribution = '0300-400ms';
  } else if (time >= 400 && time < 500) {
    distribution = '0400-500ms';
  } else if (time >= 500 && time < 600) {
    distribution = '0500-600ms';
  } else if (time >= 600 && time < 800) {
    distribution = '0600-800ms';
  } else if (time >= 800 && time < 1000) {
    distribution = '0800-1000ms';
  } else if (time >= 1000 && time < 1200) {
    distribution = '1000-1200ms';
  } else if (time >= 1200 && time < 1500) {
    distribution = '1200-1500ms';
  } else if (time >= 1500 && time < 2000) {
    distribution = '1500-2000ms';
  } else if (time >= 2000) {
    distribution = '2000ms above';
  }

  return distribution;
}

function getCpmDistribution(cpm) {
  var distribution;
  if (cpm >= 0 && cpm < 0.5) {
    distribution = '$0-0.5';
  } else if (cpm >= 0.5 && cpm < 1) {
    distribution = '$0.5-1';
  } else if (cpm >= 1 && cpm < 1.5) {
    distribution = '$1-1.5';
  } else if (cpm >= 1.5 && cpm < 2) {
    distribution = '$1.5-2';
  } else if (cpm >= 2 && cpm < 2.5) {
    distribution = '$2-2.5';
  } else if (cpm >= 2.5 && cpm < 3) {
    distribution = '$2.5-3';
  } else if (cpm >= 3 && cpm < 4) {
    distribution = '$3-4';
  } else if (cpm >= 4 && cpm < 6) {
    distribution = '$4-6';
  } else if (cpm >= 6 && cpm < 8) {
    distribution = '$6-8';
  } else if (cpm >= 8) {
    distribution = '$8 above';
  }

  return distribution;
}

function sendBidRequestToGa(bid) {
  if (bid && bid.bidderCode) {
    _analyticsQueue.push(function () {
      _eventCount++;
      window[_gaGlobal](_trackerSend, 'event', _category, 'Requests', bid.bidderCode, 1, _disableInteraction);
    });
  }

  //check the queue
  checkAnalytics();
}

function sendBidResponseToGa(bid) {

  if (bid && bid.bidderCode) {
    _analyticsQueue.push(function () {
      var cpmCents = convertToCents(bid.cpm);
      var bidder = bid.bidderCode;
      if (typeof bid.timeToRespond !== 'undefined' && _enableDistribution) {
        _eventCount++;
        var dis = getLoadTimeDistribution(bid.timeToRespond);
        window[_gaGlobal](_trackerSend, 'event', 'Prebid.js Load Time Distribution', dis, bidder, 1, _disableInteraction);
      }

      if (bid.cpm > 0) {
        _eventCount = _eventCount + 2;
        var cpmDis = getCpmDistribution(bid.cpm);
        if (_enableDistribution) {
          _eventCount++;
          window[_gaGlobal](_trackerSend, 'event', 'Prebid.js CPM Distribution', cpmDis, bidder, 1, _disableInteraction);
        }

        window[_gaGlobal](_trackerSend, 'event', _category, 'Bids', bidder, cpmCents, _disableInteraction);
        window[_gaGlobal](_trackerSend, 'event', _category, 'Bid Load Time', bidder, bid.timeToRespond, _disableInteraction);
      }
    });
  }

  //check the queue
  checkAnalytics();
}

function sendBidTimeouts(timedOutBidders) {

  _analyticsQueue.push(function () {
    utils._each(timedOutBidders, function (bidderCode) {
      _eventCount++;
      window[_gaGlobal](_trackerSend, 'event', _category, 'Timeouts', bidderCode, _disableInteraction);
    });
  });

  checkAnalytics();
}

function sendBidWonToGa(bid) {
  var cpmCents = convertToCents(bid.cpm);
  _analyticsQueue.push(function () {
    _eventCount++;
    window[_gaGlobal](_trackerSend, 'event', _category, 'Wins', bid.bidderCode, cpmCents, _disableInteraction);
  });

  checkAnalytics();
}
<|endoftext|>


<commit_before>window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext || window.oAudioContext;

/**
 * @scope enchant.WebAudioSound.prototype
 */
enchant.WebAudioSound = enchant.Class.create(enchant.EventTarget, {
    /**
     * @name enchant.WebAudioSound
     * @class
     [lang:ja]
     * WebAudioをラップしたクラス.
     [/lang]
     [lang:en]
     * Sound wrapper class for Web Audio API (supported on some webkit-based browsers)
     [/lang]
     [lang:de]
     [/lang]
     * @constructs
     * @extends enchant.EventTarget
     */
    initialize: function() {
        if (!window.AudioContext) {
            throw new Error("This browser does not support WebAudio API.");
        }
        enchant.EventTarget.call(this);
        this.context = enchant.WebAudioSound.audioContext;
        this.src = this.context.createBufferSource();
        this.buffer = null;
        this._volume = 1;
        this._currentTime = 0;
        this._state = 0;
        this.connectTarget = enchant.WebAudioSound.destination;
    },
    /**
     [lang:ja]
     * 再生を開始する.
     * @param {Boolean} [dup=false] trueならオブジェクトの現在の再生を残したまま新しく音声を再生する.
     [/lang]
     [lang:en]
     * Begin playing.
     * @param {Boolean} [dup=false] If true, Object plays new sound while keeps last sound.
     [/lang]
     [lang:de]
     * Startet die Wiedergabe.
     * @param {Boolean} [dup=false] Wenn true, Objekt spielt neuen Sound während im letzten Ton hält.
     [/lang]
     */
    play: function(dup) {
        if (this._state === 1 && !dup) {
            this.src.disconnect(this.connectTarget);
        }
        if (this._state !== 2) {
            this._currentTime = 0;
        }
        var offset = this._currentTime;
        var actx = this.context;
        this.src = actx.createBufferSource();
        if (actx.createGain != null) {
            this._gain = actx.createGain();
        } else {
            this._gain = actx.createGainNode();
        }
        this.src.buffer = this.buffer;
        this._gain.gain.value = this._volume;

        this.src.connect(this._gain);
        this._gain.connect(this.connectTarget);
        if (this.src.start != null) {
            this.src.start(0, offset, this.buffer.duration - offset - 1.192e-7);
        } else {
            this.src.noteGrainOn(0, offset, this.buffer.duration - offset - 1.192e-7);
        }
        this._startTime = actx.currentTime - this._currentTime;
        this._state = 1;
    },
    /**
     [lang:ja]
     * 再生を中断する.
     [/lang]
     [lang:en]
     * Pause playback.
     [/lang]
     [lang:de]
     * Pausiert die Wiedergabe.
     [/lang]
     */
    pause: function() {
        var currentTime = this.currentTime;
        if (currentTime === this.duration) {
            return;
        }
        if (this.src.stop != null) {
            this.src.stop(0);
        } else {
            this.src.noteOff(0);
        }
        this._currentTime = currentTime;
        this._state = 2;
    },
    /**
     [lang:ja]
     * 再生を停止する.
     [/lang]
     [lang:en]
     * Stop playing.
     [/lang]
     [lang:de]
     * Stoppt die Wiedergabe.
     [/lang]
     */
    stop: function() {
        if (this.src.stop != null) {
            this.src.stop(0);
        } else {
            this.src.noteOff(0);
        }
        this._state = 0;
    },
    /**
     [lang:ja]
     * Soundを複製する.
     * @return {enchant.WebAudioSound} 複製されたSound.
     [/lang]
     [lang:en]
     * Create a copy of this Sound object.
     * @return {enchant.WebAudioSound} Copied Sound.
     [/lang]
     [lang:de]
     * Erstellt eine Kopie dieses Soundobjektes.
     * @return {enchant.WebAudioSound} Kopiertes Sound Objekt.
     [/lang]
     */
    clone: function() {
        var sound = new enchant.WebAudioSound();
        sound.buffer = this.buffer;
        return sound;
    },
    /**
     [lang:ja]
     * Soundの再生時間 (秒).
     [/lang]
     [lang:en]
     * Sound file duration (seconds).
     [/lang]
     [lang:de]
     * Die länge der Sounddatei in Sekunden.
     [/lang]
     * @type Number
     */
    duration: {
        get: function() {
            if (this.buffer) {
                return this.buffer.duration;
            } else {
                return 0;
            }
        }
    },
    /**
     [lang:ja]
     * 現在の再生位置 (秒).
     [/lang]
     [lang:en]
     * Current playback position (seconds).
     [/lang]
     [lang:de]
     * Aktuelle Wiedergabeposition (seconds).
     [/lang]
     * @type Number
     */
    volume: {
        get: function() {
            return this._volume;
        },
        set: function(volume) {
            volume = Math.max(0, Math.min(1, volume));
            this._volume = volume;
            if (this.src) {
                this._gain.gain.value = volume;
            }
        }
    },
    /**
     [lang:ja]
     * ボリューム. 0 (無音) ～ 1 (フルボリューム).
     [/lang]
     [lang:en]
     * Volume. 0 (muted) ～ 1 (full volume).
     [/lang]
     [lang:de]
     * Lautstärke. 0 (stumm) ～ 1 (volle Lautstärke).
     [/lang]
     * @type Number
     */
    currentTime: {
        get: function() {
            return Math.max(0, Math.min(this.duration, this.src.context.currentTime - this._startTime));
        },
        set: function(time) {
            this._currentTime = time;
            if (this._state !== 2) {
                this.play(false);
            }
        }
    }
});

/**
 [lang:ja]
 * 音声ファイルを読み込んでWebAudioSoundオブジェクトを作成する.
 * @param {String} src ロードする音声ファイルのパス.
 * @param {String} [type] 音声ファイルのMIME Type.
 * @param {Function} [callback] ロード完了時のコールバック.
 * @param {Function} [onerror] ロード失敗時のコールバック.
 [/lang]
 [lang:en]
 * Loads an audio file and creates WebAudioSound object.
 * @param {String} src Path of the audio file to be loaded.
 * @param {String} [type] MIME Type of the audio file.
 * @param {Function} [callback] on load callback.
 * @param {Function} [onerror] on error callback.
 [/lang]
 [lang:de]
 * Läd eine Audio Datei und erstellt ein WebAudioSound objekt.
 * @param {String} src Pfad zu der zu ladenden Audiodatei.
 * @param {String} [type] MIME Type der Audtiodatei.
 * @param {Function} [callback]
 * @param {Function} [onerror]
 [/lang]
 * @return {enchant.WebAudioSound} WebAudioSound
 * @static
 */
enchant.WebAudioSound.load = function(src, type, callback, onerror) {
    var canPlay = (new Audio()).canPlayType(type);
    var sound = new enchant.WebAudioSound();
    callback = callback || function() {};
    onerror = onerror || function() {};
    sound.addEventListener(enchant.Event.LOAD, callback);
    sound.addEventListener(enchant.Event.ERROR, onerror);
    function dispatchErrorEvent() {
        var e = new enchant.Event(enchant.Event.ERROR);
        e.message = 'Cannot load an asset: ' + src;
        enchant.Core.instance.dispatchEvent(e);
        sound.dispatchEvent(e);
    }
    var actx, xhr;
    if (canPlay === 'maybe' || canPlay === 'probably') {
        actx = enchant.WebAudioSound.audioContext;
        xhr = new XMLHttpRequest();
        xhr.open('GET', src, true);
        xhr.responseType = 'arraybuffer';
        xhr.onload = function() {
            actx.decodeAudioData(xhr.response, function(buffer) {
                sound.buffer = buffer;
                sound.dispatchEvent(new enchant.Event(enchant.Event.LOAD));
            }, dispatchErrorEvent);
        };
        xhr.onerror = dispatchErrorEvent;
        xhr.send(null);
    } else {
        setTimeout(dispatchErrorEvent,  50);
    }
    return sound;
};

if (window.AudioContext) {
    enchant.WebAudioSound.audioContext = new window.AudioContext();
    enchant.WebAudioSound.destination = enchant.WebAudioSound.audioContext.destination;
}
<commit_msg>Fixed webkit audio issue<commit_after>window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.msAudioContext || window.oAudioContext;

/**
 * @scope enchant.WebAudioSound.prototype
 */
enchant.WebAudioSound = enchant.Class.create(enchant.EventTarget, {
    /**
     * @name enchant.WebAudioSound
     * @class
     [lang:ja]
     * WebAudioをラップしたクラス.
     [/lang]
     [lang:en]
     * Sound wrapper class for Web Audio API (supported on some webkit-based browsers)
     [/lang]
     [lang:de]
     [/lang]
     * @constructs
     * @extends enchant.EventTarget
     */
    initialize: function() {
        if (!window.AudioContext) {
            throw new Error("This browser does not support WebAudio API.");
        }
        enchant.EventTarget.call(this);
        this.context = enchant.WebAudioSound.audioContext;
        this.src = this.context.createBufferSource();
        this.buffer = null;
        this._volume = 1;
        this._currentTime = 0;
        this._state = 0;
        this.connectTarget = enchant.WebAudioSound.destination;
    },
    /**
     [lang:ja]
     * 再生を開始する.
     * @param {Boolean} [dup=false] trueならオブジェクトの現在の再生を残したまま新しく音声を再生する.
     [/lang]
     [lang:en]
     * Begin playing.
     * @param {Boolean} [dup=false] If true, Object plays new sound while keeps last sound.
     [/lang]
     [lang:de]
     * Startet die Wiedergabe.
     * @param {Boolean} [dup=false] Wenn true, Objekt spielt neuen Sound während im letzten Ton hält.
     [/lang]
     */
    play: function(dup) {
        if (this._state === 1 && !dup) {
            this.src.disconnect();
        }
        if (this._state !== 2) {
            this._currentTime = 0;
        }
        var offset = this._currentTime;
        var actx = this.context;
        this.src = actx.createBufferSource();
        if (actx.createGain != null) {
            this._gain = actx.createGain();
        } else {
            this._gain = actx.createGainNode();
        }
        this.src.buffer = this.buffer;
        this._gain.gain.value = this._volume;

        this.src.connect(this._gain);
        this._gain.connect(this.connectTarget);
        if (this.src.start != null) {
            this.src.start(0, offset, this.buffer.duration - offset - 1.192e-7);
        } else {
            this.src.noteGrainOn(0, offset, this.buffer.duration - offset - 1.192e-7);
        }
        this._startTime = actx.currentTime - this._currentTime;
        this._state = 1;
    },
    /**
     [lang:ja]
     * 再生を中断する.
     [/lang]
     [lang:en]
     * Pause playback.
     [/lang]
     [lang:de]
     * Pausiert die Wiedergabe.
     [/lang]
     */
    pause: function() {
        var currentTime = this.currentTime;
        if (currentTime === this.duration) {
            return;
        }
        if (this.src.stop != null) {
            this.src.stop(0);
        } else {
            this.src.noteOff(0);
        }
        this._currentTime = currentTime;
        this._state = 2;
    },
    /**
     [lang:ja]
     * 再生を停止する.
     [/lang]
     [lang:en]
     * Stop playing.
     [/lang]
     [lang:de]
     * Stoppt die Wiedergabe.
     [/lang]
     */
    stop: function() {
        if (this.src.stop != null) {
            this.src.stop(0);
        } else {
            this.src.noteOff(0);
        }
        this._state = 0;
    },
    /**
     [lang:ja]
     * Soundを複製する.
     * @return {enchant.WebAudioSound} 複製されたSound.
     [/lang]
     [lang:en]
     * Create a copy of this Sound object.
     * @return {enchant.WebAudioSound} Copied Sound.
     [/lang]
     [lang:de]
     * Erstellt eine Kopie dieses Soundobjektes.
     * @return {enchant.WebAudioSound} Kopiertes Sound Objekt.
     [/lang]
     */
    clone: function() {
        var sound = new enchant.WebAudioSound();
        sound.buffer = this.buffer;
        return sound;
    },
    /**
     [lang:ja]
     * Soundの再生時間 (秒).
     [/lang]
     [lang:en]
     * Sound file duration (seconds).
     [/lang]
     [lang:de]
     * Die länge der Sounddatei in Sekunden.
     [/lang]
     * @type Number
     */
    duration: {
        get: function() {
            if (this.buffer) {
                return this.buffer.duration;
            } else {
                return 0;
            }
        }
    },
    /**
     [lang:ja]
     * 現在の再生位置 (秒).
     [/lang]
     [lang:en]
     * Current playback position (seconds).
     [/lang]
     [lang:de]
     * Aktuelle Wiedergabeposition (seconds).
     [/lang]
     * @type Number
     */
    volume: {
        get: function() {
            return this._volume;
        },
        set: function(volume) {
            volume = Math.max(0, Math.min(1, volume));
            this._volume = volume;
            if (this.src) {
                this._gain.gain.value = volume;
            }
        }
    },
    /**
     [lang:ja]
     * ボリューム. 0 (無音) ～ 1 (フルボリューム).
     [/lang]
     [lang:en]
     * Volume. 0 (muted) ～ 1 (full volume).
     [/lang]
     [lang:de]
     * Lautstärke. 0 (stumm) ～ 1 (volle Lautstärke).
     [/lang]
     * @type Number
     */
    currentTime: {
        get: function() {
            return Math.max(0, Math.min(this.duration, this.src.context.currentTime - this._startTime));
        },
        set: function(time) {
            this._currentTime = time;
            if (this._state !== 2) {
                this.play(false);
            }
        }
    }
});

/**
 [lang:ja]
 * 音声ファイルを読み込んでWebAudioSoundオブジェクトを作成する.
 * @param {String} src ロードする音声ファイルのパス.
 * @param {String} [type] 音声ファイルのMIME Type.
 * @param {Function} [callback] ロード完了時のコールバック.
 * @param {Function} [onerror] ロード失敗時のコールバック.
 [/lang]
 [lang:en]
 * Loads an audio file and creates WebAudioSound object.
 * @param {String} src Path of the audio file to be loaded.
 * @param {String} [type] MIME Type of the audio file.
 * @param {Function} [callback] on load callback.
 * @param {Function} [onerror] on error callback.
 [/lang]
 [lang:de]
 * Läd eine Audio Datei und erstellt ein WebAudioSound objekt.
 * @param {String} src Pfad zu der zu ladenden Audiodatei.
 * @param {String} [type] MIME Type der Audtiodatei.
 * @param {Function} [callback]
 * @param {Function} [onerror]
 [/lang]
 * @return {enchant.WebAudioSound} WebAudioSound
 * @static
 */
enchant.WebAudioSound.load = function(src, type, callback, onerror) {
    var canPlay = (new Audio()).canPlayType(type);
    var sound = new enchant.WebAudioSound();
    callback = callback || function() {};
    onerror = onerror || function() {};
    sound.addEventListener(enchant.Event.LOAD, callback);
    sound.addEventListener(enchant.Event.ERROR, onerror);
    function dispatchErrorEvent() {
        var e = new enchant.Event(enchant.Event.ERROR);
        e.message = 'Cannot load an asset: ' + src;
        enchant.Core.instance.dispatchEvent(e);
        sound.dispatchEvent(e);
    }
    var actx, xhr;
    if (canPlay === 'maybe' || canPlay === 'probably') {
        actx = enchant.WebAudioSound.audioContext;
        xhr = new XMLHttpRequest();
        xhr.open('GET', src, true);
        xhr.responseType = 'arraybuffer';
        xhr.onload = function() {
            actx.decodeAudioData(xhr.response, function(buffer) {
                sound.buffer = buffer;
                sound.dispatchEvent(new enchant.Event(enchant.Event.LOAD));
            }, dispatchErrorEvent);
        };
        xhr.onerror = dispatchErrorEvent;
        xhr.send(null);
    } else {
        setTimeout(dispatchErrorEvent,  50);
    }
    return sound;
};

if (window.AudioContext) {
    enchant.WebAudioSound.audioContext = new window.AudioContext();
    enchant.WebAudioSound.destination = enchant.WebAudioSound.audioContext.destination;
}
<|endoftext|>


<commit_before>'use strict';

var $ = require('jquery');
var m = require('mithril');
var Treebeard = require('treebeard');
var $osf = require('js/osfHelpers');
var projectSettingsTreebeardBase = require('js/projectSettingsTreebeardBase');

function expandOnLoad() {
    var tb = this;  // jshint ignore: line
    for (var i = 0; i < tb.treeData.children.length; i++) {
        var parent = tb.treeData.children[i];
        tb.updateFolder(null, parent);
        expandChildren(tb, parent.children);
    }
}

function expandChildren(tb, children) {
    var openParent = false;
    for (var i = 0; i < children.length; i++) {
        var child = children[i];
        var parent = children[i].parent();
        if (child.data.kind === 'event' && child.data.event.notificationType !== 'adopt_parent') {
            openParent = true;
        }
        if (child.children.length > 0) {
            expandChildren(tb, child.children);
        }
    }
    if (openParent) {
        openAncestors(tb, children[0]);
    }
}

function openAncestors (tb, item) {
    var parent = item.parent();
    if(parent && parent.id > 0) {
        tb.updateFolder(null, parent);
        openAncestors(tb, parent);
    }
}

function subscribe(item, notification_type) {
    var id = item.parent().data.node.id;
    var event = item.data.event.title;
    var payload = {
        'id': id,
        'event': event,
        'notification_type': notification_type
    };
    $osf.postJSON(
        '/api/v1/subscriptions/',
        payload
    ).done(function(){
        //'notfiy-success' is to override default class 'success' in treebeard
        item.notify.update('Settings updated', 'notify-success', 1, 2000);
        item.data.event.notificationType = notification_type;
    }).fail(function() {
        item.notify.update('Could not update settings', 'notify-danger', 1, 2000);
    });
}

function displayParentNotificationType(item){
    var notificationTypeDescriptions = {
        'email_transactional': 'Instantly',
        'email_digest': 'Daily',
        'adopt_parent': 'Adopt setting from parent project',
        'none': 'Never'
    };

    if (item.data.event.parent_notification_type) {
        if (item.parent().parent().parent() === undefined) {
            return '(' + notificationTypeDescriptions[item.data.event.parent_notification_type] + ')';
        }
    }
    return '';
}

var popOver = function(element, isInit) {
    if (!isInit) {
        $(element).tooltip();
    }
};

function ProjectNotifications(data) {

    //  Treebeard version
    var tbOptions = $.extend({}, projectSettingsTreebeardBase.defaults, {
        divID: 'grid',
        filesData: data,
        naturalScrollLimit : 0,
        onload : function () {
            var tb = this;
            expandOnLoad.call(tb);
        },
        resolveRows: function notificationResolveRows(item){
            var columns = [];
            var iconcss = '';
            // check if should not get icon
            if(item.children.length < 1 ){
                iconcss = 'tb-no-icon';
            }
            if (item.data.kind === 'heading') {
                if (item.data.children.length === 0) {
                    columns.push({
                        data : 'project',  // Data field name
                        folderIcons : false,
                        filter : true,
                        sortInclude : false,
                        custom : function() {
                            return m('div[style="padding-left:5px"]',
                                        [m ('p', [
                                                m('b', item.data.node.title + ': '),
                                                m('span[class="text-warning"]', ' No configured projects.')]
                                        )]
                            );
                        }
                    });
                } else {
                    columns.push({
                        data : 'project',  // Data field name
                        folderIcons : false,
                        filter : true,
                        sortInclude : false,
                        custom : function() {
                            var globalNotificationsMessage = 'These are default settings for new projects you create or are added to. Modifying these settings will not modify settings on existing projects.';
                            return m('div[style="padding-left:5px; padding-bottom:50px"]', [
                                m('p', [
                                    m('b', item.data.node.title + ':  '),
                                    m('span[class="fa fa-info-circle"]', {'data-toggle': 'tooltip', 'title':globalNotificationsMessage, config: popOver, 'data-placement': 'bottom'})
                                ])
                            ]);
                        }
                    });
                }
            }
            else if (item.data.kind === 'folder' || item.data.kind === 'node') {
                columns.push({
                    data : 'project',  // Data field name
                    folderIcons : true,
                    filter : true,
                    sortInclude : false,
                    custom : function() {
                        if (item.data.node.url !== '') {
                            return m('a', { href : item.data.node.url, target : '_blank' }, item.data.node.title);
                        } else {
                            return m('span', item.data.node.title);
                        }

                    }
                });
            }
            else if (item.parent().data.kind === 'folder' || item.parent().data.kind === 'heading' && item.data.kind === 'event') {
                columns.push(
                {
                    data : 'project',  // Data field name
                    folderIcons : true,
                    filter : true,
                    css : iconcss,
                    sortInclude : false,
                    custom : function(item, col) {
                        return item.data.event.description;
                    }
                },
                {
                    data : 'notificationType',  // Data field name
                    folderIcons : false,
                    filter : false,
                    custom : function(item, col) {
                        return m('div[style="padding-right:10px"]',
                            [m('select.form-control', {
                                onchange: function(ev) {
                                    subscribe(item, ev.target.value);
                                }},
                                [
                                    m('option', {value: 'none', selected : item.data.event.notificationType === 'none' ? 'selected': ''}, 'Never'),
                                    m('option', {value: 'email_transactional', selected : item.data.event.notificationType === 'email_transactional' ? 'selected': ''}, 'Instantly'),
                                    m('option', {value: 'email_digest', selected : item.data.event.notificationType === 'email_digest' ? 'selected': ''}, 'Daily')
                            ])
                        ]);
                    }
                });
            }
            else {
                columns.push(
                {
                    data : 'project',  // Data field name
                    folderIcons : true,
                    filter : true,
                    css : iconcss,
                    sortInclude : false,
                    custom : function() {
                        return item.data.event.description;

                    }
                },
                {
                    data : 'notificationType',  // Data field name
                    folderIcons : false,
                    filter : false,
                    custom : function() {
                        return  m('div[style="padding-right:10px"]',
                            [m('select.form-control', {
                                onchange: function(ev) {
                                    subscribe(item, ev.target.value);
                                }},
                                [
                                    m('option', {value: 'adopt_parent',
                                                 selected: item.data.event.notificationType === 'adopt_parent' ? 'selected' : ''},
                                                 'Adopt setting from parent project ' + displayParentNotificationType(item)),
                                    m('option', {value: 'none', selected : item.data.event.notificationType === 'none' ? 'selected': ''}, 'Never'),
                                    m('option', {value: 'email_transactional',  selected : item.data.event.notificationType === 'email_transactional' ? 'selected': ''}, 'Instantly'),
                                    m('option', {value: 'email_digest', selected : item.data.event.notificationType === 'email_digest' ? 'selected': ''}, 'Daily')
                            ])
                        ]);
                    }
                });
            }

            return columns;
        }
    });
    var grid = new Treebeard(tbOptions);
}

module.exports = ProjectNotifications;
<commit_msg>remove never option from global comment replies setting<commit_after>'use strict';

var $ = require('jquery');
var m = require('mithril');
var Treebeard = require('treebeard');
var $osf = require('js/osfHelpers');
var projectSettingsTreebeardBase = require('js/projectSettingsTreebeardBase');

function expandOnLoad() {
    var tb = this;  // jshint ignore: line
    for (var i = 0; i < tb.treeData.children.length; i++) {
        var parent = tb.treeData.children[i];
        tb.updateFolder(null, parent);
        expandChildren(tb, parent.children);
    }
}

function expandChildren(tb, children) {
    var openParent = false;
    for (var i = 0; i < children.length; i++) {
        var child = children[i];
        var parent = children[i].parent();
        if (child.data.kind === 'event' && child.data.event.notificationType !== 'adopt_parent') {
            openParent = true;
        }
        if (child.children.length > 0) {
            expandChildren(tb, child.children);
        }
    }
    if (openParent) {
        openAncestors(tb, children[0]);
    }
}

function openAncestors (tb, item) {
    var parent = item.parent();
    if(parent && parent.id > 0) {
        tb.updateFolder(null, parent);
        openAncestors(tb, parent);
    }
}

function subscribe(item, notification_type) {
    var id = item.parent().data.node.id;
    var event = item.data.event.title;
    var payload = {
        'id': id,
        'event': event,
        'notification_type': notification_type
    };
    $osf.postJSON(
        '/api/v1/subscriptions/',
        payload
    ).done(function(){
        //'notfiy-success' is to override default class 'success' in treebeard
        item.notify.update('Settings updated', 'notify-success', 1, 2000);
        item.data.event.notificationType = notification_type;
    }).fail(function() {
        item.notify.update('Could not update settings', 'notify-danger', 1, 2000);
    });
}

function displayParentNotificationType(item){
    var notificationTypeDescriptions = {
        'email_transactional': 'Instantly',
        'email_digest': 'Daily',
        'adopt_parent': 'Adopt setting from parent project',
        'none': 'Never'
    };

    if (item.data.event.parent_notification_type) {
        if (item.parent().parent().parent() === undefined) {
            return '(' + notificationTypeDescriptions[item.data.event.parent_notification_type] + ')';
        }
    }
    return '';
}

var popOver = function(element, isInit) {
    if (!isInit) {
        $(element).tooltip();
    }
};

function ProjectNotifications(data) {

    //  Treebeard version
    var tbOptions = $.extend({}, projectSettingsTreebeardBase.defaults, {
        divID: 'grid',
        filesData: data,
        naturalScrollLimit : 0,
        onload : function () {
            var tb = this;
            expandOnLoad.call(tb);
        },
        resolveRows: function notificationResolveRows(item){
            var columns = [];
            var iconcss = '';
            // check if should not get icon
            if(item.children.length < 1 ){
                iconcss = 'tb-no-icon';
            }
            if (item.data.kind === 'heading') {
                if (item.data.children.length === 0) {
                    columns.push({
                        data : 'project',  // Data field name
                        folderIcons : false,
                        filter : true,
                        sortInclude : false,
                        custom : function() {
                            return m('div[style="padding-left:5px"]',
                                        [m ('p', [
                                                m('b', item.data.node.title + ': '),
                                                m('span[class="text-warning"]', ' No configured projects.')]
                                        )]
                            );
                        }
                    });
                } else {
                    columns.push({
                        data : 'project',  // Data field name
                        folderIcons : false,
                        filter : true,
                        sortInclude : false,
                        custom : function() {
                            var globalNotificationsMessage = 'These are default settings for new projects you create or are added to. Modifying these settings will not modify settings on existing projects.';
                            return m('div[style="padding-left:5px; padding-bottom:50px"]', [
                                m('p', [
                                    m('b', item.data.node.title + ':  '),
                                    m('span[class="fa fa-info-circle"]', {'data-toggle': 'tooltip', 'title':globalNotificationsMessage, config: popOver, 'data-placement': 'bottom'})
                                ])
                            ]);
                        }
                    });
                }
            }
            else if (item.data.kind === 'folder' || item.data.kind === 'node') {
                columns.push({
                    data : 'project',  // Data field name
                    folderIcons : true,
                    filter : true,
                    sortInclude : false,
                    custom : function() {
                        if (item.data.node.url !== '') {
                            return m('a', { href : item.data.node.url, target : '_blank' }, item.data.node.title);
                        } else {
                            return m('span', item.data.node.title);
                        }

                    }
                });
            }
            else if (item.parent().data.kind === 'folder' || item.parent().data.kind === 'heading' && item.data.kind === 'event') {
                columns.push(
                {
                    data : 'project',  // Data field name
                    folderIcons : true,
                    filter : true,
                    css : iconcss,
                    sortInclude : false,
                    custom : function(item, col) {
                        return item.data.event.description;
                    }
                },
                {
                    data : 'notificationType',  // Data field name
                    folderIcons : false,
                    filter : false,
                    custom : function(item, col) {
                        return m('div[style="padding-right:10px"]',
                            [m('select.form-control', {
                                onchange: function(ev) {
                                    subscribe(item, ev.target.value);
                                }},
                                [
                                    item.data.event.title !== 'comment_replies' ? m('option', {value: 'none', selected : item.data.event.notificationType === 'none' ? 'selected': ''}, 'Never') : null,
                                    m('option', {value: 'email_transactional', selected : item.data.event.notificationType === 'email_transactional' ? 'selected': ''}, 'Instantly'),
                                    m('option', {value: 'email_digest', selected : item.data.event.notificationType === 'email_digest' ? 'selected': ''}, 'Daily')
                            ])
                        ]);
                    }
                });
            }
            else {
                columns.push(
                {
                    data : 'project',  // Data field name
                    folderIcons : true,
                    filter : true,
                    css : iconcss,
                    sortInclude : false,
                    custom : function() {
                        return item.data.event.description;

                    }
                },
                {
                    data : 'notificationType',  // Data field name
                    folderIcons : false,
                    filter : false,
                    custom : function() {
                        return  m('div[style="padding-right:10px"]',
                            [m('select.form-control', {
                                onchange: function(ev) {
                                    subscribe(item, ev.target.value);
                                }},
                                [
                                    m('option', {value: 'adopt_parent',
                                                 selected: item.data.event.notificationType === 'adopt_parent' ? 'selected' : ''},
                                                 'Adopt setting from parent project ' + displayParentNotificationType(item)),
                                    m('option', {value: 'none', selected : item.data.event.notificationType === 'none' ? 'selected': ''}, 'Never'),
                                    m('option', {value: 'email_transactional',  selected : item.data.event.notificationType === 'email_transactional' ? 'selected': ''}, 'Instantly'),
                                    m('option', {value: 'email_digest', selected : item.data.event.notificationType === 'email_digest' ? 'selected': ''}, 'Daily')
                            ])
                        ]);
                    }
                });
            }

            return columns;
        }
    });
    var grid = new Treebeard(tbOptions);
}

module.exports = ProjectNotifications;
<|endoftext|>