<commit_before>import { _ } from 'meteor/underscore';

const cutOff =
  /(\sgroup by\s|\sorder by\s|\swhere\s|\shaving\s|\slimit\s|\soffset\s)+[\w([\])"'`.,/=<>\s]+$/i;

export default {
  parse(query) {
    const objectSQL = {
      select: getSelect(query),
      from: getFrom(query),
      having: getHaving(query),
      where: getWhere(query),
      limit: getLimit(query),
      offset: getOffset(query),
      join: getJion(query),
    };
    objectSQL.groupBy = getGroupBy(query, objectSQL.select);
    objectSQL.orderBy = getOrderBy(query, objectSQL.select);
    return objectSQL;
  },
  parseToQueryObject(rawQuery, fields) {
    const query = this.parse(rawQuery);
    const queryObject = {};
    const having = getHavingObj(query);

    queryObject.pagination = getPagination(query);
    queryObject.fields = getFields(query, having);

    query.groupBy && query.groupBy.forEach(fieldIndex => {
      queryObject.fields[fieldIndex].grouping = true;
    });
    query.orderBy && query.orderBy.forEach(field => {
      queryObject.fields[field.index].sort = field.type;
    });

    queryObject.collectionFields = getCollectionFields(query, fields);

    queryObject.join = query.join;

    return queryObject;
  },
};

function getSelect(query) {
  let select = query.match(/^\s*select\s+([\w([\])"'`.,/\s]+)from\s/i);
  if (!select) return { error: 'Wrong syntax: "SELECT <fields> FROM <collections>" expected' };
  select = select[1];
  const asNames = select.match(/\s+as\s+\w+/ig);
  let names = [];
  let expressions = [];
  if (asNames) {
    names = asNames.map(item => item.replace(/(^\s+as\s|\s)+/ig, ''));
    expressions = [select.slice(0, select.indexOf(asNames[0]))];
    for (let i = 0; i < asNames.length - 1; i++) {
      expressions.push(select.slice(select.indexOf(asNames[i]) + asNames[i].length,
        select.indexOf(asNames[i + 1])));
    }
  } else {
    expressions = select.match(/\w+\.\w+/ig);
    if (expressions) {
      names = expressions.map(exp => exp.match(/\w+\.(\w+)/i)[1]);
    } else {
      return { error: 'Wrong fields!' };
    }
  }
  return names.map((name, i) => ({
    name: names[i],
    expression: expressions[i].replace(/(^,|\s)/g, ''),
  }));
}

function getFrom(query) {
  const from = query.match(/\sfrom\s+([\w([\])"'`.,/=<>\s]+$)/i)[1].replace(cutOff, '');
  const name = from.match(/\s+as\s+(\w+)/i);
  const path = from.slice(0, name.index).match(/(\w+)\/(\w+)/i);
  return { db: path[1], collection: path[2], name: name[1] };
}

function getJion(query) {
  const from = query.match(/\sjoin\s+([\w([\])"'`.,/=<>\s]+$)/i)[1].replace(cutOff, '');
  const name = from.match(/\s+as\s+(\w+)/i);
  const path = from.slice(0, name.index).match(/(\w+)\/(\w+)/i);
  return path[2];
}

function getGroupBy(query, select) {
  let groupBy = query.match(/\sgroup by\s+([\w([\])"'`.,/=<>\s]+$)/i);
  if (groupBy) {
    groupBy = groupBy[1].replace(cutOff, '')
      .replace(/\s/g, '');
    return select.reduce((previous, field, i) => {
      if (~groupBy.indexOf(field.expression)) previous.push(i);
      return previous;
    }, []);
  }
  return null;
}

function getOrderBy(query, select) {
  let orderBy = query.match(/\sorder by\s+([\w([\])"'`.,/=<>\s]+$)/i);
  if (orderBy) {
    orderBy = orderBy[1].replace(cutOff, '');
    return select.reduce((previous, field, index) => {
      if (~orderBy.indexOf(field.name)) {
        const obj = { index };
        const regex = new RegExp(`${field.name}\\s+(asc|desc)(\\s|,|$)`, 'i');
        const match = orderBy.match(regex);
        if (match) obj.type = match[1].toLowerCase();
        previous.push(obj);
      }
      return previous;
    }, []);
  }
  return null;
}

function getHaving(query) {
  let having = query.match(/\shaving\s+([\w([\])"'`.,/=<>\s]+$)/i);
  if (having) {
    having = having[1].replace(cutOff, '');
    return parseFiltering(having);
  }
  return null;
}

function getWhere(query) {
  let where = query.match(/\swhere\s+([\w([\])"'`.,/=<>\s]+$)/i);
  if (where) {
    where = where[1].replace(cutOff, '');
    return parseFiltering(where);
  }
  return null;
}

function getLimit(query) {
  const limit = query.match(/\slimit\s+(\d+)/i);
  if (limit) return +limit[1];
  return null;
}

function getOffset(query) {
  const offset = query.match(/\soffset\s+(\d+)/i);
  if (offset) return +offset[1];
  return null;
}

function parseFiltering(str) {
  const filters = [];
  const andOr = str.match(/\s+(and|or)\s+/gi);
  if (andOr) {
    let rest = str.slice();
    andOr.forEach(item => {
      const part = rest.slice(0, rest.indexOf(item));
      filters.push(part);
      rest = rest.slice(rest.indexOf(item) + item.length);
    });
    filters.push(rest);
  } else {
    filters.push(str);
  }
  return filters.map((filter, i) => {
    const obj = { operator: filter.match(/(=|<>|<|>|\slike\s)/i)[1] };
    const regex = new RegExp(`${obj.operator}([\\w([\\])"'\`.,/\\s]+)`, 'i');
    obj.exp2 = filter.match(regex)[1];
    obj.exp1 = filter.replace(obj.operator, '').replace(obj.exp2, '').replace(/\s/g, '');
    obj.operator = obj.operator.replace(/\s/g, '');
    obj.exp2 = obj.exp2.replace(/^\s+|\s+$/g, '');
    if (andOr && i < andOr.length) obj.join = andOr[i].replace(/\s/g, '');
    return obj;
  });
}

function getPagination(query) {
  return {
    limit: query.limit,
    page: query.skip ? query.skip / query.limit : 1,
  };
}

function getHavingObj(query) {
  const having = {};
  if (query.having) {
    query.having.forEach(item => {
      if (!having[item.exp1]) having[item.exp1] = [];
      having[item.exp1].push({
        value: item.exp2,
        joinOperator: item.join,
        operator: item.operator,
      });
    });
  }
  return having;
}

function getFields(query, having) {
  return query.select.map((field, i) => (
    {
      id: i,
      name: field.name,
      expression: field.expression,
      grouping: field.date ? field.date : false,
      sort: false,
      filters: having[field.expression],
    }
  ));
}

function getCollectionFields(query, fields) {
  const collectionFields = {};
  if (query.where) {
    query.where.forEach(item => {
      const expression = item.exp1;

      if (!collectionFields[expression]) {
        collectionFields[expression] = {
          filters: [],
          name: _.last(expression.split('.')),
          constructorType: 'filters',
          type: getType(expression, fields[0]),
          expression,
        };
        collectionFields[expression].filters = [];
      }

      collectionFields[expression].filters.push({
        value: item.exp2,
        joinOperator: item.join,
        operator: item.operator,
      });
    });
  }
  return collectionFields;
}

function getType(expression, fields) {
  let currentValue = fields;
  const path = expression.split('.');
  path.forEach(pathItem => {
    currentValue = currentValue[pathItem];
  });
  return getItemType(currentValue);
}

function getItemType(value) {
  if (_.isNumber(value)) return 'number';
  if (_.isDate(value)) return 'date';
  if (isDate(value)) return 'date';
  if (_.isString(value)) return 'string';
  if (_.isBoolean(value)) return 'boolean';
  if (_.isArray(value)) return 'array';
  if (_.isObject(value)) return 'object';
  return null;
}

function isDate(value) {
  const dateObj = new Date(value);
  return dateObj.toString() !== 'Invalid Date' &&
    dateObj.toISOString().replace('.000', '') === value;
}
<commit_msg>update parser<commit_after>import { _ } from 'meteor/underscore';

const cutOff =
  /(\sgroup by\s|\sorder by\s|\swhere\s|\shaving\s|\slimit\s|\soffset\s)+[\w([\])"'`.,/=<>\s]+$/i;

export default {
  parse(query) {
    const objectSQL = {
      select: getSelect(query),
      from: getFrom(query),
      having: getHaving(query),
      where: getWhere(query),
      limit: getLimit(query),
      offset: getOffset(query),
      join: getJoin(query),
    };
    objectSQL.groupBy = getGroupBy(query, objectSQL.select);
    objectSQL.orderBy = getOrderBy(query, objectSQL.select);
    return objectSQL;
  },
  parseToQueryObject(rawQuery, fields) {
    const query = this.parse(rawQuery);
    const queryObject = {};
    const having = getHavingObj(query);

    queryObject.pagination = getPagination(query);
    queryObject.fields = getFields(query, having);

    query.groupBy && query.groupBy.forEach(fieldIndex => {
      queryObject.fields[fieldIndex].grouping = true;
    });
    query.orderBy && query.orderBy.forEach(field => {
      queryObject.fields[field.index].sort = field.type;
    });

    queryObject.collectionFields = getCollectionFields(query, fields);

    queryObject.join = query.join;

    return queryObject;
  },
};

function getSelect(query) {
  let select = query.match(/^\s*select\s+([\w([\])"'`.,/\s]+)from\s/i);
  if (!select) return { error: 'Wrong syntax: "SELECT <fields> FROM <collections>" expected' };
  select = select[1];
  const asNames = select.match(/\s+as\s+\w+/ig);
  let names = [];
  let expressions = [];
  if (asNames) {
    names = asNames.map(item => item.replace(/(^\s+as\s|\s)+/ig, ''));
    expressions = [select.slice(0, select.indexOf(asNames[0]))];
    for (let i = 0; i < asNames.length - 1; i++) {
      expressions.push(select.slice(select.indexOf(asNames[i]) + asNames[i].length,
        select.indexOf(asNames[i + 1])));
    }
  } else {
    expressions = select.match(/\w+\.\w+/ig);
    if (expressions) {
      names = expressions.map(exp => exp.match(/\w+\.(\w+)/i)[1]);
    } else {
      return { error: 'Wrong fields!' };
    }
  }
  return names.map((name, i) => ({
    name: names[i],
    expression: expressions[i].replace(/(^,|\s)/g, ''),
  }));
}

function getFrom(query) {
  const from = query.match(/\sfrom\s+([\w([\])\"'`.,/=<>\s]+$)/i)[1].replace(cutOff, '');
  const name = getParamName(from);
  const path = parseQueryPartial(from, name);
  return { db: path[1], collection: path[2], name: name[1] };
}

function getJoin(query) {
  const from = query.match(/\sjoin\s+([\w([\])\"'`.,/=<>\s]+$)/i)[1].replace(cutOff, '');
  const name = getParamName(from);
  const path = parseQueryPartial(from, name);
  return path[2];
}

const getParamName = (queryPartial) => queryPartial.match(/\s+as\s+(\w+)/i);
const parseQueryPartial = (partial, paramName) => partial.slice(0, paramName.index).match(/(\w+)\/(\w+)/i);

function getGroupBy(query, select) {
  let groupBy = query.match(/\sgroup by\s+([\w([\])"'`.,/=<>\s]+$)/i);
  if (groupBy) {
    groupBy = groupBy[1].replace(cutOff, '')
      .replace(/\s/g, '');
    return select.reduce((previous, field, i) => {
      if (~groupBy.indexOf(field.expression)) previous.push(i);
      return previous;
    }, []);
  }
  return null;
}

function getOrderBy(query, select) {
  let orderBy = query.match(/\sorder by\s+([\w([\])"'`.,/=<>\s]+$)/i);
  if (orderBy) {
    orderBy = orderBy[1].replace(cutOff, '');
    return select.reduce((previous, field, index) => {
      if (~orderBy.indexOf(field.name)) {
        const obj = { index };
        const regex = new RegExp(`${field.name}\\s+(asc|desc)(\\s|,|$)`, 'i');
        const match = orderBy.match(regex);
        if (match) obj.type = match[1].toLowerCase();
        previous.push(obj);
      }
      return previous;
    }, []);
  }
  return null;
}

function getHaving(query) {
  let having = query.match(/\shaving\s+([\w([\])"'`.,/=<>\s]+$)/i);
  if (having) {
    having = having[1].replace(cutOff, '');
    return parseFiltering(having);
  }
  return null;
}

function getWhere(query) {
  let where = query.match(/\swhere\s+([\w([\])"'`.,/=<>\s]+$)/i);
  if (where) {
    where = where[1].replace(cutOff, '');
    return parseFiltering(where);
  }
  return null;
}

function getLimit(query) {
  const limit = query.match(/\slimit\s+(\d+)/i);
  if (limit) return +limit[1];
  return null;
}

function getOffset(query) {
  const offset = query.match(/\soffset\s+(\d+)/i);
  if (offset) return +offset[1];
  return null;
}

function parseFiltering(str) {
  const filters = [];
  const andOr = str.match(/\s+(and|or)\s+/gi);
  if (andOr) {
    let rest = str.slice();
    andOr.forEach(item => {
      const part = rest.slice(0, rest.indexOf(item));
      filters.push(part);
      rest = rest.slice(rest.indexOf(item) + item.length);
    });
    filters.push(rest);
  } else {
    filters.push(str);
  }
  return filters.map((filter, i) => {
    const obj = { operator: filter.match(/(=|<>|<|>|\slike\s)/i)[1] };
    const regex = new RegExp(`${obj.operator}([\\w([\\])"'\`.,/\\s]+)`, 'i');
    obj.exp2 = filter.match(regex)[1];
    obj.exp1 = filter.replace(obj.operator, '').replace(obj.exp2, '').replace(/\s/g, '');
    obj.operator = obj.operator.replace(/\s/g, '');
    obj.exp2 = obj.exp2.replace(/^\s+|\s+$/g, '');
    if (andOr && i < andOr.length) obj.join = andOr[i].replace(/\s/g, '');
    return obj;
  });
}

function getPagination(query) {
  return {
    limit: query.limit,
    page: query.skip ? query.skip / query.limit : 1,
  };
}

function getHavingObj(query) {
  const having = {};
  if (query.having) {
    query.having.forEach(item => {
      if (!having[item.exp1]) having[item.exp1] = [];
      having[item.exp1].push({
        value: item.exp2,
        joinOperator: item.join,
        operator: item.operator,
      });
    });
  }
  return having;
}

function getFields(query, having) {
  return query.select.map((field, i) => (
    {
      id: i,
      name: field.name,
      expression: field.expression,
      grouping: field.date ? field.date : false,
      sort: false,
      filters: having[field.expression],
    }
  ));
}

function getCollectionFields(query, fields) {
  const collectionFields = {};
  if (query.where) {
    query.where.forEach(item => {
      const expression = item.exp1;

      if (!collectionFields[expression]) {
        collectionFields[expression] = {
          filters: [],
          name: _.last(expression.split('.')),
          constructorType: 'filters',
          type: getType(expression, fields[0]),
          expression,
        };
        collectionFields[expression].filters = [];
      }

      collectionFields[expression].filters.push({
        value: item.exp2,
        joinOperator: item.join,
        operator: item.operator,
      });
    });
  }
  return collectionFields;
}

function getType(expression, fields) {
  let currentValue = fields;
  const path = expression.split('.');
  path.forEach(pathItem => {
    currentValue = currentValue[pathItem];
  });
  return getItemType(currentValue);
}

function getItemType(value) {
  if (_.isNumber(value)) return 'number';
  if (_.isDate(value)) return 'date';
  if (isDate(value)) return 'date';
  if (_.isString(value)) return 'string';
  if (_.isBoolean(value)) return 'boolean';
  if (_.isArray(value)) return 'array';
  if (_.isObject(value)) return 'object';
  return null;
}

function isDate(value) {
  const dateObj = new Date(value);
  return dateObj.toString() !== 'Invalid Date' &&
    dateObj.toISOString().replace('.000', '') === value;
}
<|endoftext|>


<commit_before>/**
 * Configuration for head elements added during the creation of index.html.
 *
 * All href attributes are added the publicPath (if exists) by default.
 * You can explicitly hint to prefix a publicPath by setting a boolean value to a key that has
 * the same name as the attribute you want to operate on, but prefix with =
 *
 * Example:
 * { name: 'msapplication-TileImage', content: '/assets/icon/ms-icon-144x144.png', '=content': true },
 * Will prefix the publicPath to content.
 *
 * { rel: 'apple-touch-icon', sizes: '57x57', href: '/assets/icon/apple-icon-57x57.png', '=href': false },
 * Will not prefix the publicPath on href (href attributes are added by default
 *
 */
module.exports = {
  link: [
    /** <link> tags for 'apple-touch-icon' (AKA Web Clips). **/
    { rel: 'apple-touch-icon', sizes: '57x57', href: '/assets/icon/apple-icon-57x57.png' },
    { rel: 'apple-touch-icon', sizes: '60x60', href: '/assets/icon/apple-icon-60x60.png' },
    { rel: 'apple-touch-icon', sizes: '72x72', href: '/assets/icon/apple-icon-72x72.png' },
    { rel: 'apple-touch-icon', sizes: '76x76', href: '/assets/icon/apple-icon-76x76.png' },
    { rel: 'apple-touch-icon', sizes: '114x114', href: '/assets/icon/apple-icon-114x114.png' },
    { rel: 'apple-touch-icon', sizes: '120x120', href: '/assets/icon/apple-icon-120x120.png' },
    { rel: 'apple-touch-icon', sizes: '144x144', href: '/assets/icon/apple-icon-144x144.png' },
    { rel: 'apple-touch-icon', sizes: '152x152', href: '/assets/icon/apple-icon-152x152.png' },
    { rel: 'apple-touch-icon', sizes: '180x180', href: '/assets/icon/apple-icon-180x180.png' },

    /** <link> tags for android web app icons **/
    { rel: 'icon', type: 'image/png', sizes: '192x192', href: '/assets/icon/android-icon-192x192.png' },

    /** <link> tags for favicons **/
    { rel: 'icon', type: 'image/png', sizes: '32x32', href: '/assets/icon/favicon-32x32.png' },
    { rel: 'icon', type: 'image/png', sizes: '96x96', href: '/assets/icon/favicon-96x96.png' },
    { rel: 'icon', type: 'image/png', sizes: '16x16', href: '/assets/icon/favicon-16x16.png' }

  ],
  meta: [
    { name: 'msapplication-TileColor', content: '#ffffff' },
    { name: 'msapplication-TileImage', content: '/assets/icon/ms-icon-144x144.png', '=content': true },
    { name: 'theme-color', content: '#4691d1' }
  ]
};
<commit_msg>fix head-config content<commit_after>/**
 * Configuration for head elements added during the creation of index.html.
 *
 * All href attributes are added the publicPath (if exists) by default.
 * You can explicitly hint to prefix a publicPath by setting a boolean value to a key that has
 * the same name as the attribute you want to operate on, but prefix with =
 *
 * Example:
 * { name: 'msapplication-TileImage', content: '/assets/icon/ms-icon-144x144.png', '=content': true },
 * Will prefix the publicPath to content.
 *
 * { rel: 'apple-touch-icon', sizes: '57x57', href: '/assets/icon/apple-icon-57x57.png', '=href': false },
 * Will not prefix the publicPath on href (href attributes are added by default
 *
 */
module.exports = {
  link: [
  /** <link> tags for 'apple-touch-icon' (AKA Web Clips). **/
    {rel: 'apple-touch-icon', sizes: '57x57', href: '/assets/icon/apple-icon-57x57.png'},
    {rel: 'apple-touch-icon', sizes: '60x60', href: '/assets/icon/apple-icon-60x60.png'},
    {rel: 'apple-touch-icon', sizes: '72x72', href: '/assets/icon/apple-icon-72x72.png'},
    {rel: 'apple-touch-icon', sizes: '76x76', href: '/assets/icon/apple-icon-76x76.png'},
    {rel: 'apple-touch-icon', sizes: '114x114', href: '/assets/icon/apple-icon-114x114.png'},
    {rel: 'apple-touch-icon', sizes: '120x120', href: '/assets/icon/apple-icon-120x120.png'},
    {rel: 'apple-touch-icon', sizes: '144x144', href: '/assets/icon/apple-icon-144x144.png'},
    {rel: 'apple-touch-icon', sizes: '152x152', href: '/assets/icon/apple-icon-152x152.png'},
    {rel: 'apple-touch-icon', sizes: '180x180', href: '/assets/icon/apple-icon-180x180.png'},

  /** <link> tags for android web app icons **/
    {rel: 'icon', type: 'image/png', sizes: '192x192', href: '/assets/icon/android-icon-192x192.png'},

  /** <link> tags for favicons **/
    {rel: 'icon', type: 'image/png', sizes: '32x32', href: '/assets/icon/favicon-32x32.png'},
    {rel: 'icon', type: 'image/png', sizes: '96x96', href: '/assets/icon/favicon-96x96.png'},
    {rel: 'icon', type: 'image/png', sizes: '16x16', href: '/assets/icon/favicon-16x16.png'}

  ],
  meta: [
    {name: 'msapplication-TileColor', content: '#ffffff'},
    {name: 'msapplication-TileImage', content: '/assets/icon/ms-icon-144x144.png', '=content': true},
    {name: 'theme-color', content: '#4691d1'}
  ]
};
<|endoftext|>


<commit_before>/*
Copyright 2009-2010 University of Toronto, 2011 Museum of Moving Image

Licensed under the Educational Community License (ECL), Version 2.0.
ou may not use this file except in compliance with this License.

You may obtain a copy of the ECL 2.0 License at
https://source.collectionspace.org/collection-space/LICENSE.txt
*/

/*global cspace:true, jQuery, fluid, window*/
"use strict";

cspace = cspace || {};

(function ($, fluid) {
    fluid.log("RecordList.js loaded");

    var bindEvents = function (that) {
        that.events.onSelect.addListener(function () {
            that.select();
        });

        fluid.activatable(that.locate("row"), function (event) {
            var rows = that.locate("row");
            rows.removeClass(that.options.styles.selected);
            that.locate("newRow").hide();
            var row = $(event.target);
            row.addClass(that.options.styles.selected);
            that.applier.requestChange("selectonIndex", rows.index(row));
            that.events.onSelect.fire();
        });
        fluid.selectable(that.container, {
            selectableElements: that.locate("row"),
            onLeaveContainer: function () {
                that.locate("row").removeClass(that.options.styles.selected);
            },
            noBubbleListeners: false,
            selectablesTabindex: ""
        });

        that.locate("row").click(function () {
            $(that.options.selectors["row"]).removeClass(that.options.styles.selected);
            var rows = that.locate("row");
            that.locate("newRow").hide();
            var row = $(this);
            row.addClass(that.options.styles.selected);
            that.applier.requestChange("selectonIndex", rows.index(row));
            that.events.onSelect.fire();
        });

        that.handleNewRow = function (action) {
            that.locate("newRow")[action]();
            that.locate("row").removeClass(that.options.styles.selected);
        };
    };

    cspace.recordList = function (container, options) {
        var that = fluid.initRendererComponent("cspace.recordList", container, options);
        fluid.initDependents(that);
        that.applier.requestChange("columns", that.options.columns);
        that.applier.requestChange("sorted", []);
        that.applier.requestChange("names", that.options.names || that.options.columns);
        that.refreshView = function () {
            that.verifyColumnOrder();
            that.renderer.refreshView();
            bindEvents(that);
            that.events.afterRender.fire();
        };

        that.refreshView();

        return that;
    };

    cspace.recordList.verifyColumnOrder = function (that) {
        that.applier.requestChange("sorted", []);
        fluid.each(fluid.get(that.model, that.options.elPaths.items), function (item, index) {
            var sortedItem = [];
            fluid.each(that.model.columns, function (column) {
                sortedItem.push(fluid.get(item, column));
            });
            that.applier.requestChange("sorted" + "." + index, sortedItem);
        });
    };

    cspace.recordList.assertItems = function (options) {
        return options.items && options.items.length > 0;
    };

    cspace.recordList.calculateRecordListSize = function (model, elPath) {
        var list = fluid.get(model, elPath);
        return list && list.length > 0 ? list.length : 0;
    };

    cspace.recordList.produceTree = function (that) {
        return {
            recordList: {
                decorators: {
                    type: "addClass",
                    classes: that.options.styles.recordList
                }
            },
            newRow: {
                messagekey: "newRow",
                decorators: [{
                    type: "addClass",
                    classes: that.options.styles.hidden
                }, {
                    type: "addClass",
                    classes: that.options.styles.newRow
                }]
            },
            expander: [{
                type: "fluid.renderer.condition",
                condition: that.options.showTitle,
                trueTree: {
                    titleRow: {
                        decorators: {
                            type: "addClass",
                            classes: that.options.styles.titleRow
                        }
                    },
                    expander: {
                        repeatID: "titleColumn",
                        type: "fluid.renderer.repeat",
                        pathAs: "titleColumn",
                        controlledBy: "names",
                        tree: {
                            messagekey: "${{titleColumn}}",
                            decorators: [{
                                type: "addClass",
                                classes: that.options.styles.titleColumn
                            }, {
                                type: "addClass",
                                classes: that.options.styles.column + that.options.columns.length.toString()
                            }]
                        }
                    }
                }
            }, {
                type: "fluid.renderer.condition",
                condition: {
                    funcName: "cspace.recordList.assertItems",
                    args: {
                        items: "${" + that.options.elPaths.items + "}"
                    }
                },
                trueTree: {
                    expander: [{
                        repeatID: "row",
                        type: "fluid.renderer.repeat",
                        pathAs: "row",
                        valueAs: "rowValue",
                        controlledBy: "sorted",
                        tree: {
                            expander: [{
                                repeatID: "column",
                                type: "fluid.renderer.repeat",
                                pathAs: "column",
                                valueAs: "columnValue",
                                controlledBy: "{row}",
                                tree: {
                                    value: "${{column}}",
                                    decorators: [{
                                        type: "addClass",
                                        classes: that.options.styles.column + that.options.columns.length.toString()
                                    }, {
                                        type: "addClass",
                                        classes: that.options.styles.column
                                    }]
                                }
                            }]
                        }
                    }, {
                        type: "fluid.renderer.condition",
                        condition: that.options.showNumberOfItems,
                        trueTree: {
                            numberOfItems: {
                                messagekey: "numberOfItems",
                                args: {
                                    numberOfItems: that.calculateRecordListSize()
                                },
                                decorators: {
                                    type: "addClass",
                                    classes: that.options.styles.numberOfItems
                                }
                            }
                        }
                    }]
                },
                falseTree: {
                    nothingYet: {
                        messagekey: "nothingYet",
                        decorators: {
                            type: "addClass",
                            classes: that.options.styles.nothingYet
                        }
                    }
                }
            }]
        };
    };

    cspace.recordList.produceTreeTabs = function (that) {
        var tree = cspace.recordList.produceTree(that);
        tree.expander[1].trueTree.expander[0].tree.expander.push({
            type: "fluid.renderer.condition",
            condition: that.options.showDeleteButton,
            trueTree: {
                deleteRelation: {
                    decorators: [{
                        type: "addClass",
                        classes: that.options.styles.deleteRelation
                    }, {
                        type: "attrs",
                        attributes: {
                            alt: that.options.strings.deleteRelation
                        }
                    }, {
                        type: "jQuery",
                        func: "click",
                        args: that.deleteRelation
                    }]
                }
            }
        });
        return tree;
    };

    var selectNavigate = function (model, options, url, typePath) {
        var record = fluid.get(model, options.elPaths.items)[model.selectonIndex];
        if (!record) {
            return;
        }
        options.globalNavigator.events.onPerformNavigation.fire(function () {
            window.location = fluid.stringTemplate(url, {
                recordType: record[typePath].toLowerCase(),
                csid: record.csid
            });
        });
    };

    cspace.recordList.selectNavigateVocab = function (model, options, url) {
        selectNavigate(model, options, url, "sourceFieldType");
    };

    cspace.recordList.selectNavigate = function (model, options, url) {
        selectNavigate(model, options, url, "recordtype");
    };

    cspace.recordList.selectFromList = function (model, options, dataContext) {
        var record = fluid.get(model, options.elPaths.items)[model.selectonIndex];
        if (!record) {
            return;
        }
        options.globalNavigator.events.onPerformNavigation.fire(function () {
            dataContext.fetch(record.csid);
        });
    };

    fluid.defaults("cspace.recordList", {
        mergePolicy: {
            "rendererOptions.applier": "applier"
        },
        rendererOptions: {
            autoBind: true
        },
        invokers: {
            select: "select",
            verifyColumnOrder: {
                funcName: "cspace.recordList.verifyColumnOrder",
                args: "{recordList}"
            },
            calculateRecordListSize: {
                funcName: "cspace.recordList.calculateRecordListSize",
                args: ["{recordList}.model", "{recordList}.options.elPaths.items"]
            }
        },
        model: {
            selectonIndex: -1
        },
        events: {
            onSelect: null,
            afterRender: null
        },
        showTitle: true,
        showNumberOfItems: true,
        gradeNames: ["fluid.IoCRendererComponent"],
        selectors: {
            newRow: ".csc-recordList-new",
            recordList: ".csc-recordList",
            titleRow: ".csc-recordList-title-row",
            titleColumn: ".csc-recordList-title-column",
            numberOfItems: ".csc-recordList-num-items",
            nothingYet: ".csc-recordList-no-items",
            row: ".csc-recordList-row",
            column: ".csc-recordList-column",
            deleteRelation: ".csc-recordList-deleteRelation"
        },
        repeatingSelectors: ["row", "titleColumn", "column"],
        strings: {
            nothingYet: "No related records yet",
            newRow: "New Record",
            numberOfItems: "%numberOfItems"
        },
        styles: {
            hidden: "hidden",
            newRow: "cs-recordList-new",
            recordList: "cs-recordList",
            titleRow: "cs-recordList-title-row",
            numberOfItems: "cs-recordList-num-items",
            nothingYet: "cs-recordList-no-items",
            titleColumn: "cs-recordList-title-column",
            column: "cs-recordList-column",
            column1: "cs-recordList-column1",
            column2: "cs-recordList-column2",
            column3: "cs-recordList-column3",
            column4: "cs-recordList-column4",
            selected: "cs-selected"
        },
        parentBundle: "{globalBundle}",
        globalNavigator: "{globalNavigator}",
        produceTree: cspace.recordList.produceTree,
        resources: {
            template: cspace.resourceSpecExpander({
                fetchClass: "fastTemplate",
                url: "%webapp/html/components/RecordListTemplate.html"
            })
        },
        urls: cspace.componentUrlBuilder({
            navigate: "%webapp/html/%recordType.html?csid=%csid",
            navigateLocal: "%webapp/html/record.html?recordtype=%recordType&csid=%csid"
        })
    });

    fluid.fetchResources.primeCacheFromResources("cspace.recordList");

    cspace.recordList.extractRowCsid = function (rows, row, model, elPath) {
        return fluid.get(model, elPath)[rows.index(row)].csid;
    };

    cspace.recordList.deleteRelation = function (event, recordList, recordEditor, tab) {
        var targetCsid = cspace.recordList.extractRowCsid(recordList.locate("row"),
            $(event.target).parent(), recordList.model, recordList.options.elPaths.items);
        recordEditor.confirmation.open("cspace.confirmation.deleteDialog", undefined, {
            listeners: {
                onClose: function (userAction) {
                    if (userAction === "act") {
                        recordEditor.options.messageBar.show(recordEditor.options.strings.removingMessage, null, false);
                        tab.relationManager.dataContext.removeRelations({
                            source: {
                                csid: tab.model.csid,
                                recordtype: tab.primary
                            },
                            target: {
                                csid: targetCsid,
                                recordtype: tab.related
                            },
                            type: "affects",
                            "one-way": false
                        });
                    }
                }
            },
            strings: {
                primaryMessage: recordEditor.options.strings.deletePrimaryMessage
            }
        });
        return false;
    };

    fluid.demands("list", ["cspace.listEditor", "cspace.tab"], {
        container: "{listEditor}.dom.list",
        options: {
            showDeleteButton: {
                expander: {
                    type: "fluid.deferredInvokeCall",
                    func: "cspace.permissions.resolveMultiple",
                    args: {
                        recordTypeManager: "{recordTypeManager}",
                        resolver: "{permissionsResolver}",
                        allOf: [{
                            target: "{relatedRecordsTab}.primary",
                            permission: "update"
                        }, {
                            target: "{relatedRecordsTab}.related",
                            permission: "update"
                        }]
                    }
                }
            },
            columns: ["number", "summary"],
            produceTree: cspace.recordList.produceTreeTabs,
            invokers: {
                deleteRelation: {
                    funcName: "cspace.recordList.deleteRelation",
                    args: ["{arguments}.0", "{recordList}", "{details}", "{relatedRecordsTab}"]
                }
            },
            styles: {
                deleteRelation: "cs-recordList-deleteRelation"
            },
            strings: {
                number: "ID Number",
                summary: "Summary",
                newRow: "Creating New Related Record...",
                deleteRelation: "Delete this relation."
            }
        }
    });

})(jQuery, fluid);
<commit_msg>CSPACE-792: Only showing title row if there are records in the list.<commit_after>/*
Copyright 2009-2010 University of Toronto, 2011 Museum of Moving Image

Licensed under the Educational Community License (ECL), Version 2.0.
ou may not use this file except in compliance with this License.

You may obtain a copy of the ECL 2.0 License at
https://source.collectionspace.org/collection-space/LICENSE.txt
*/

/*global cspace:true, jQuery, fluid, window*/
"use strict";

cspace = cspace || {};

(function ($, fluid) {
    fluid.log("RecordList.js loaded");

    var bindEvents = function (that) {
        that.events.onSelect.addListener(function () {
            that.select();
        });

        fluid.activatable(that.locate("row"), function (event) {
            var rows = that.locate("row");
            rows.removeClass(that.options.styles.selected);
            that.locate("newRow").hide();
            var row = $(event.target);
            row.addClass(that.options.styles.selected);
            that.applier.requestChange("selectonIndex", rows.index(row));
            that.events.onSelect.fire();
        });
        fluid.selectable(that.container, {
            selectableElements: that.locate("row"),
            onLeaveContainer: function () {
                that.locate("row").removeClass(that.options.styles.selected);
            },
            noBubbleListeners: false,
            selectablesTabindex: ""
        });

        that.locate("row").click(function () {
            $(that.options.selectors["row"]).removeClass(that.options.styles.selected);
            var rows = that.locate("row");
            that.locate("newRow").hide();
            var row = $(this);
            row.addClass(that.options.styles.selected);
            that.applier.requestChange("selectonIndex", rows.index(row));
            that.events.onSelect.fire();
        });

        that.handleNewRow = function (action) {
            that.locate("newRow")[action]();
            that.locate("row").removeClass(that.options.styles.selected);
        };
    };

    cspace.recordList = function (container, options) {
        var that = fluid.initRendererComponent("cspace.recordList", container, options);
        fluid.initDependents(that);
        that.applier.requestChange("columns", that.options.columns);
        that.applier.requestChange("sorted", []);
        that.applier.requestChange("names", that.options.names || that.options.columns);
        that.refreshView = function () {
            that.verifyColumnOrder();
            that.renderer.refreshView();
            bindEvents(that);
            that.events.afterRender.fire();
        };

        that.refreshView();

        return that;
    };

    cspace.recordList.verifyColumnOrder = function (that) {
        that.applier.requestChange("sorted", []);
        fluid.each(fluid.get(that.model, that.options.elPaths.items), function (item, index) {
            var sortedItem = [];
            fluid.each(that.model.columns, function (column) {
                sortedItem.push(fluid.get(item, column));
            });
            that.applier.requestChange("sorted" + "." + index, sortedItem);
        });
    };

    cspace.recordList.assertItems = function (options) {
        return options.items && options.items.length > 0;
    };

    cspace.recordList.calculateRecordListSize = function (model, elPath) {
        var list = fluid.get(model, elPath);
        return list && list.length > 0 ? list.length : 0;
    };

    cspace.recordList.produceTree = function (that) {
        return {
            recordList: {
                decorators: {
                    type: "addClass",
                    classes: that.options.styles.recordList
                }
            },
            newRow: {
                messagekey: "newRow",
                decorators: [{
                    type: "addClass",
                    classes: that.options.styles.hidden
                }, {
                    type: "addClass",
                    classes: that.options.styles.newRow
                }]
            },
            expander: [{
                type: "fluid.renderer.condition",
                condition: {
                    funcName: "cspace.recordList.assertItems",
                    args: {
                        items: "${" + that.options.elPaths.items + "}"
                    }
                },
                trueTree: {
                    expander: [{
                        repeatID: "row",
                        type: "fluid.renderer.repeat",
                        pathAs: "row",
                        valueAs: "rowValue",
                        controlledBy: "sorted",
                        tree: {
                            expander: [{
                                repeatID: "column",
                                type: "fluid.renderer.repeat",
                                pathAs: "column",
                                valueAs: "columnValue",
                                controlledBy: "{row}",
                                tree: {
                                    value: "${{column}}",
                                    decorators: [{
                                        type: "addClass",
                                        classes: that.options.styles.column + that.options.columns.length.toString()
                                    }, {
                                        type: "addClass",
                                        classes: that.options.styles.column
                                    }]
                                }
                            }]
                        }
                    }, {
                        type: "fluid.renderer.condition",
                        condition: that.options.showNumberOfItems,
                        trueTree: {
                            numberOfItems: {
                                messagekey: "numberOfItems",
                                args: {
                                    numberOfItems: that.calculateRecordListSize()
                                },
                                decorators: {
                                    type: "addClass",
                                    classes: that.options.styles.numberOfItems
                                }
                            }
                        }
                    }, {
                        type: "fluid.renderer.condition",
                        condition: that.options.showTitle,
                        trueTree: {
                            titleRow: {
                                decorators: {
                                    type: "addClass",
                                    classes: that.options.styles.titleRow
                                }
                            },
                            expander: {
                                repeatID: "titleColumn",
                                type: "fluid.renderer.repeat",
                                pathAs: "titleColumn",
                                controlledBy: "names",
                                tree: {
                                    messagekey: "${{titleColumn}}",
                                    decorators: [{
                                        type: "addClass",
                                        classes: that.options.styles.titleColumn
                                    }, {
                                        type: "addClass",
                                        classes: that.options.styles.column + that.options.columns.length.toString()
                                    }]
                                }
                            }
                        }
                    }]
                },
                falseTree: {
                    nothingYet: {
                        messagekey: "nothingYet",
                        decorators: {
                            type: "addClass",
                            classes: that.options.styles.nothingYet
                        }
                    }
                }
            }]
        };
    };

    cspace.recordList.produceTreeTabs = function (that) {
        var tree = cspace.recordList.produceTree(that);
        tree.expander[1].trueTree.expander[0].tree.expander.push({
            type: "fluid.renderer.condition",
            condition: that.options.showDeleteButton,
            trueTree: {
                deleteRelation: {
                    decorators: [{
                        type: "addClass",
                        classes: that.options.styles.deleteRelation
                    }, {
                        type: "attrs",
                        attributes: {
                            alt: that.options.strings.deleteRelation
                        }
                    }, {
                        type: "jQuery",
                        func: "click",
                        args: that.deleteRelation
                    }]
                }
            }
        });
        return tree;
    };

    var selectNavigate = function (model, options, url, typePath) {
        var record = fluid.get(model, options.elPaths.items)[model.selectonIndex];
        if (!record) {
            return;
        }
        options.globalNavigator.events.onPerformNavigation.fire(function () {
            window.location = fluid.stringTemplate(url, {
                recordType: record[typePath].toLowerCase(),
                csid: record.csid
            });
        });
    };

    cspace.recordList.selectNavigateVocab = function (model, options, url) {
        selectNavigate(model, options, url, "sourceFieldType");
    };

    cspace.recordList.selectNavigate = function (model, options, url) {
        selectNavigate(model, options, url, "recordtype");
    };

    cspace.recordList.selectFromList = function (model, options, dataContext) {
        var record = fluid.get(model, options.elPaths.items)[model.selectonIndex];
        if (!record) {
            return;
        }
        options.globalNavigator.events.onPerformNavigation.fire(function () {
            dataContext.fetch(record.csid);
        });
    };

    fluid.defaults("cspace.recordList", {
        mergePolicy: {
            "rendererOptions.applier": "applier"
        },
        rendererOptions: {
            autoBind: true
        },
        invokers: {
            select: "select",
            verifyColumnOrder: {
                funcName: "cspace.recordList.verifyColumnOrder",
                args: "{recordList}"
            },
            calculateRecordListSize: {
                funcName: "cspace.recordList.calculateRecordListSize",
                args: ["{recordList}.model", "{recordList}.options.elPaths.items"]
            }
        },
        model: {
            selectonIndex: -1
        },
        events: {
            onSelect: null,
            afterRender: null
        },
        showTitle: true,
        showNumberOfItems: true,
        gradeNames: ["fluid.IoCRendererComponent"],
        selectors: {
            newRow: ".csc-recordList-new",
            recordList: ".csc-recordList",
            titleRow: ".csc-recordList-title-row",
            titleColumn: ".csc-recordList-title-column",
            numberOfItems: ".csc-recordList-num-items",
            nothingYet: ".csc-recordList-no-items",
            row: ".csc-recordList-row",
            column: ".csc-recordList-column",
            deleteRelation: ".csc-recordList-deleteRelation"
        },
        repeatingSelectors: ["row", "titleColumn", "column"],
        strings: {
            nothingYet: "No related records yet",
            newRow: "New Record",
            numberOfItems: "%numberOfItems"
        },
        styles: {
            hidden: "hidden",
            newRow: "cs-recordList-new",
            recordList: "cs-recordList",
            titleRow: "cs-recordList-title-row",
            numberOfItems: "cs-recordList-num-items",
            nothingYet: "cs-recordList-no-items",
            titleColumn: "cs-recordList-title-column",
            column: "cs-recordList-column",
            column1: "cs-recordList-column1",
            column2: "cs-recordList-column2",
            column3: "cs-recordList-column3",
            column4: "cs-recordList-column4",
            selected: "cs-selected"
        },
        parentBundle: "{globalBundle}",
        globalNavigator: "{globalNavigator}",
        produceTree: cspace.recordList.produceTree,
        resources: {
            template: cspace.resourceSpecExpander({
                fetchClass: "fastTemplate",
                url: "%webapp/html/components/RecordListTemplate.html"
            })
        },
        urls: cspace.componentUrlBuilder({
            navigate: "%webapp/html/%recordType.html?csid=%csid",
            navigateLocal: "%webapp/html/record.html?recordtype=%recordType&csid=%csid"
        })
    });

    fluid.fetchResources.primeCacheFromResources("cspace.recordList");

    cspace.recordList.extractRowCsid = function (rows, row, model, elPath) {
        return fluid.get(model, elPath)[rows.index(row)].csid;
    };

    cspace.recordList.deleteRelation = function (event, recordList, recordEditor, tab) {
        var targetCsid = cspace.recordList.extractRowCsid(recordList.locate("row"),
            $(event.target).parent(), recordList.model, recordList.options.elPaths.items);
        recordEditor.confirmation.open("cspace.confirmation.deleteDialog", undefined, {
            listeners: {
                onClose: function (userAction) {
                    if (userAction === "act") {
                        recordEditor.options.messageBar.show(recordEditor.options.strings.removingMessage, null, false);
                        tab.relationManager.dataContext.removeRelations({
                            source: {
                                csid: tab.model.csid,
                                recordtype: tab.primary
                            },
                            target: {
                                csid: targetCsid,
                                recordtype: tab.related
                            },
                            type: "affects",
                            "one-way": false
                        });
                    }
                }
            },
            strings: {
                primaryMessage: recordEditor.options.strings.deletePrimaryMessage
            }
        });
        return false;
    };

    fluid.demands("list", ["cspace.listEditor", "cspace.tab"], {
        container: "{listEditor}.dom.list",
        options: {
            showDeleteButton: {
                expander: {
                    type: "fluid.deferredInvokeCall",
                    func: "cspace.permissions.resolveMultiple",
                    args: {
                        recordTypeManager: "{recordTypeManager}",
                        resolver: "{permissionsResolver}",
                        allOf: [{
                            target: "{relatedRecordsTab}.primary",
                            permission: "update"
                        }, {
                            target: "{relatedRecordsTab}.related",
                            permission: "update"
                        }]
                    }
                }
            },
            columns: ["number", "summary"],
            produceTree: cspace.recordList.produceTreeTabs,
            invokers: {
                deleteRelation: {
                    funcName: "cspace.recordList.deleteRelation",
                    args: ["{arguments}.0", "{recordList}", "{details}", "{relatedRecordsTab}"]
                }
            },
            styles: {
                deleteRelation: "cs-recordList-deleteRelation"
            },
            strings: {
                number: "ID Number",
                summary: "Summary",
                newRow: "Creating New Related Record...",
                deleteRelation: "Delete this relation."
            }
        }
    });

})(jQuery, fluid);
<|endoftext|>


<commit_before>'use strict';
var EventEmitter = require('events').EventEmitter;
var Plotly = require('../plotly');

/**
 * @param {object} gd figure Object
 * @param {object} opts option object
 * @param opts.format 'jpeg' | 'png' | 'webp' | 'svg'
 */
function toImage(gd, opts) {

    // first clone the GD so we can operate in a clean environment
    var Snapshot = Plotly.Snapshot;
    var ev = new EventEmitter();

    var clone = Snapshot.clone(gd, {format: 'png'});
    var clonedGd = clone.td;

    // put the cloned div somewhere off screen before attaching to DOM
    clonedGd.style.position = 'absolute';
    clonedGd.style.left = '-5000px';
    document.body.appendChild(clonedGd);

    function wait () {
        var is3d = clonedGd._fullLayout._hasGL3D;
        var delay = is3d ? 500 : 0;

        setTimeout(function () {
            var svg = Plotly.Snapshot.toSVG(clonedGd);

            var canvasContainer = window.document.createElement('div');
            var canvas = window.document.createElement('canvas');

            // window.document.body.appendChild(canvasContainer);
            canvasContainer.appendChild(canvas);

            canvasContainer.id = Plotly.Lib.randstr();
            canvas.id = Plotly.Lib.randstr();

            ev = Plotly.Snapshot.svgToImg({
                format: opts.format,
                width: clonedGd._fullLayout.width,
                height: clonedGd._fullLayout.height,
                canvas: canvas,
                emitter: ev,
                svg: svg
            });

            ev.clean = function() {
                if (clonedGd) clonedGd.remove();
            };

        }, delay);
    }

    Plotly.plot(clonedGd, clone.data, clone.layout, clone.config)
    // TODO: the following is Plotly.Plots.redrawText but without the waiting.
    // we shouldn't need to do this, but in *occasional* cases we do. Figure
    // out why and take it out.
        .then(function() {

            // doesn't work presently (and not needed) for polar or 3d
            if(clonedGd._fullLayout._hasGL3D ||
               (clonedGd.data && clonedGd.data[0] && clonedGd.data[0].r)) {
                return;
            }
            Plotly.Annotations.drawAll(clonedGd);
            Plotly.Legend.draw(clonedGd, clonedGd._fullLayout.showlegend);
            (clonedGd.calcdata||[]).forEach(function(d){
                if(d[0]&&d[0].t&&d[0].t.cb) d[0].t.cb();
            });
        })
        .then(wait)
        .catch( function (err) {
            ev.emit('error', err);
        });

    return ev;
}

module.exports = toImage;
<commit_msg>add toimage delay for gl2d plots (similar to gl3d plots)<commit_after>'use strict';
var EventEmitter = require('events').EventEmitter;
var Plotly = require('../plotly');

/**
 * @param {object} gd figure Object
 * @param {object} opts option object
 * @param opts.format 'jpeg' | 'png' | 'webp' | 'svg'
 */
function toImage(gd, opts) {

    // first clone the GD so we can operate in a clean environment
    var Snapshot = Plotly.Snapshot;
    var ev = new EventEmitter();

    var clone = Snapshot.clone(gd, {format: 'png'});
    var clonedGd = clone.td;

    // put the cloned div somewhere off screen before attaching to DOM
    clonedGd.style.position = 'absolute';
    clonedGd.style.left = '-5000px';
    document.body.appendChild(clonedGd);

    function wait () {
        var delay = (clonedGd._fullLayout._hasGL3D || clonedGd._fullLayout._hasGL2D) ?
            500 :
            0;

        setTimeout(function () {
            var svg = Plotly.Snapshot.toSVG(clonedGd);

            var canvasContainer = window.document.createElement('div');
            var canvas = window.document.createElement('canvas');

            // window.document.body.appendChild(canvasContainer);
            canvasContainer.appendChild(canvas);

            canvasContainer.id = Plotly.Lib.randstr();
            canvas.id = Plotly.Lib.randstr();

            ev = Plotly.Snapshot.svgToImg({
                format: opts.format,
                width: clonedGd._fullLayout.width,
                height: clonedGd._fullLayout.height,
                canvas: canvas,
                emitter: ev,
                svg: svg
            });

            ev.clean = function() {
                if (clonedGd) clonedGd.remove();
            };

        }, delay);
    }

    Plotly.plot(clonedGd, clone.data, clone.layout, clone.config)
    // TODO: the following is Plotly.Plots.redrawText but without the waiting.
    // we shouldn't need to do this, but in *occasional* cases we do. Figure
    // out why and take it out.
        .then(function() {

            // doesn't work presently (and not needed) for polar or 3d
            if(clonedGd._fullLayout._hasGL3D ||
               (clonedGd.data && clonedGd.data[0] && clonedGd.data[0].r)) {
                return;
            }
            Plotly.Annotations.drawAll(clonedGd);
            Plotly.Legend.draw(clonedGd, clonedGd._fullLayout.showlegend);
            (clonedGd.calcdata||[]).forEach(function(d){
                if(d[0]&&d[0].t&&d[0].t.cb) d[0].t.cb();
            });
        })
        .then(wait)
        .catch( function (err) {
            ev.emit('error', err);
        });

    return ev;
}

module.exports = toImage;
<|endoftext|>


<commit_before>import * as React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/styles';
import { createTheme } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import Box from '@material-ui/core/Box';
import Paper from '@material-ui/core/Paper';
import AdCarbon from 'docs/src/modules/components/AdCarbon';
import AdInHouse from 'docs/src/modules/components/AdInHouse';
import { AdContext, adShape } from 'docs/src/modules/components/AdManager';
import { useTranslate } from 'docs/src/modules/utils/i18n';

const styles = (theme) => ({
  'placement-body-image': {
    margin: theme.spacing(4, 1, 3),
    minHeight: 126,
  },
  'placement-body-inline': {
    margin: theme.spacing(4, 0, 3),
    minHeight: 126,
    display: 'flex',
    alignItems: 'flex-end',
  },
  'placement-body-inline2': {
    margin: theme.spacing(4, 0, 3),
    minHeight: 126,
    display: 'flex',
    alignItems: 'flex-end',
  },
  paper: {
    border: `2px solid ${theme.palette.primary.main}`,
  },
});

function PleaseDisableAdblock(props) {
  const t = useTranslate();

  return (
    <Paper component="span" elevation={0} sx={{ display: 'block', p: 1.5 }} {...props}>
      <Typography variant="body2" display="block" component="span" gutterBottom>
        {t('likeMui')}
      </Typography>
      <Typography variant="body2" display="block" component="span" gutterBottom>
        {t('adblock')}
      </Typography>
      <Typography variant="body2" display="block" component="span" gutterBottom>
        {t('thanks')}{' '}

          ❤

      </Typography>
    </Paper>
  );
}

const disable = process.env.NODE_ENV !== 'production' && process.env.ENABLE_AD !== 'true';
const inHouseAds = [
  {
    name: 'scaffoldhub',
    link: 'https://scaffoldhub.io/?partner=1',
    img: '/static/ads-in-house/scaffoldhub.png',
    description: 'ScaffoldHub. Automate building your full-stack Material-UI web-app.',
  },
  {
    name: 'templates',
    link: 'https://material-ui.com/store/?utm_source=docs&utm_medium=referral&utm_campaign=in-house-templates',
    img: '/static/ads-in-house/themes-2.jpg',
    description:
      'Premium Templates. Start your project with the best templates for admins, dashboards, and more.',
  },
  {
    name: 'themes',
    link: 'https://material-ui.com/store/?utm_source=docs&utm_medium=referral&utm_campaign=in-house-themes',
    img: '/static/ads-in-house/themes.png',
    description:
      'Premium Themes. Kickstart your application development with a ready-made theme.',
  },
  {
    name: 'tidelift',
    link: 'https://tidelift.com/subscription/pkg/npm-material-ui?utm_source=npm-material-ui&utm_medium=referral&utm_campaign=enterprise&utm_content=ad',
    img: '/static/ads-in-house/tidelift.png',
    description:
      'Material-UI for enterprise. Save time and reduce risk. Managed open source — backed by maintainers.',
  },
  {
    name: 'sketch',
    link: 'https://material-ui.com/store/items/sketch-react/?utm_source=docs&utm_medium=referral&utm_campaign=in-house-sketch',
    img: '/static/ads-in-house/sketch.png',
    description:
      'For Sketch. A large UI kit with over 600 handcrafted Material-UI symbols 💎.',
  },
  {
    name: 'figma',
    link: 'https://material-ui.com/store/items/figma-react/?utm_source=docs&utm_medium=referral&utm_campaign=in-house-figma',
    img: '/static/ads-in-house/figma.png',
    description:
      'For Figma. A large UI kit with over 600 handcrafted Material-UI components 🎨.',
  },
];

function Ad(props) {
  const { classes } = props;

  const [adblock, setAdblock] = React.useState(null);
  const [carbonOut, setCarbonOut] = React.useState(null);

  const { current: randomAdblock } = React.useRef(Math.random());
  const { current: randomInHouse } = React.useRef(Math.random());

  let children;
  let label;
  // Hide the content to google bot to avoid its indexation.
  if (/Googlebot/.test(navigator.userAgent) || disable) {
    children = ;
  } else if (adblock) {
    if (randomAdblock < 0.2) {
      children = <PleaseDisableAdblock className={classes.paper} />;
      label = 'in-house-adblock';
    } else {
      children = <AdInHouse ad={inHouseAds[Math.floor(inHouseAds.length * randomInHouse)]} />;
      label = 'in-house';
    }
  } else if (carbonOut) {
    children = <AdInHouse ad={inHouseAds[Math.floor(inHouseAds.length * randomInHouse)]} />;
    label = 'in-house-carbon';
  } else {
    children = <AdCarbon />;
    label = 'carbon';
  }

  const ad = React.useContext(AdContext);
  const eventLabel = label ? `${label}-${ad.placement}-${adShape}` : null;

  const timerAdblock = React.useRef();

  const checkAdblock = React.useCallback(
    (attempt = 1) => {
      if (
        document.querySelector('.ea-placement') ||
        document.querySelector('#carbonads') ||
        document.querySelector('.carbonads') ||
        carbonOut
      ) {
        if (
          document.querySelector('#carbonads a') &&
          document.querySelector('#carbonads a').getAttribute('href') ===
            'https://material-ui-next.com/discover-more/backers'
        ) {
          setCarbonOut(true);
        }

        setAdblock(false);
        return;
      }

      if (attempt < 30) {
        timerAdblock.current = setTimeout(() => {
          checkAdblock(attempt + 1);
        }, 500);
      }

      if (attempt > 6) {
        setAdblock(true);
      }
    },
    [carbonOut],
  );

  React.useEffect(() => {
    if (disable) {
      return undefined;
    }
    checkAdblock();

    return () => {
      clearTimeout(timerAdblock.current);
    };
  }, [checkAdblock]);

  React.useEffect(() => {
    // Avoid an exceed on the Google Analytics quotas.
    if (Math.random() < 0.9 || !eventLabel) {
      return undefined;
    }

    const delay = setTimeout(() => {
      window.ga('send', {
        hitType: 'event',
        eventCategory: 'ad',
        eventAction: 'display',
        eventLabel,
      });
    }, 2500);

    return () => {
      clearTimeout(delay);
    };
  }, [eventLabel]);

  return (
    <Box
      component="span"
      sx={{
        position: 'relative',
        display: 'block',
      }}
      className={classes[`placement-body-${adShape}`]}
      data-ga-event-category="ad"
      data-ga-event-action="click"
      data-ga-event-label={eventLabel}
    >
      {children}
    </Box>
  );
}

Ad.propTypes = {
  classes: PropTypes.object.isRequired,
};

const defaultTheme = createTheme();
export default React.memo(withStyles(styles, { defaultTheme })(Ad));
<commit_msg>[docs] Don't crash page if an Ad crashes (#27178)<commit_after>import * as React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/styles';
import { createTheme } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import Box from '@material-ui/core/Box';
import Paper from '@material-ui/core/Paper';
import AdCarbon from 'docs/src/modules/components/AdCarbon';
import AdInHouse from 'docs/src/modules/components/AdInHouse';
import { AdContext, adShape } from 'docs/src/modules/components/AdManager';
import { useTranslate } from 'docs/src/modules/utils/i18n';

const styles = (theme) => ({
  'placement-body-image': {
    margin: theme.spacing(4, 1, 3),
    minHeight: 126,
  },
  'placement-body-inline': {
    margin: theme.spacing(4, 0, 3),
    minHeight: 126,
    display: 'flex',
    alignItems: 'flex-end',
  },
  'placement-body-inline2': {
    margin: theme.spacing(4, 0, 3),
    minHeight: 126,
    display: 'flex',
    alignItems: 'flex-end',
  },
  paper: {
    border: `2px solid ${theme.palette.primary.main}`,
  },
});

function PleaseDisableAdblock(props) {
  const t = useTranslate();

  return (
    <Paper component="span" elevation={0} sx={{ display: 'block', p: 1.5 }} {...props}>
      <Typography variant="body2" display="block" component="span" gutterBottom>
        {t('likeMui')}
      </Typography>
      <Typography variant="body2" display="block" component="span" gutterBottom>
        {t('adblock')}
      </Typography>
      <Typography variant="body2" display="block" component="span" gutterBottom>
        {t('thanks')}{' '}

          ❤

      </Typography>
    </Paper>
  );
}

const disable = process.env.NODE_ENV !== 'production' && process.env.ENABLE_AD !== 'true';
const inHouseAds = [
  {
    name: 'scaffoldhub',
    link: 'https://scaffoldhub.io/?partner=1',
    img: '/static/ads-in-house/scaffoldhub.png',
    description: 'ScaffoldHub. Automate building your full-stack Material-UI web-app.',
  },
  {
    name: 'templates',
    link: 'https://material-ui.com/store/?utm_source=docs&utm_medium=referral&utm_campaign=in-house-templates',
    img: '/static/ads-in-house/themes-2.jpg',
    description:
      'Premium Templates. Start your project with the best templates for admins, dashboards, and more.',
  },
  {
    name: 'themes',
    link: 'https://material-ui.com/store/?utm_source=docs&utm_medium=referral&utm_campaign=in-house-themes',
    img: '/static/ads-in-house/themes.png',
    description:
      'Premium Themes. Kickstart your application development with a ready-made theme.',
  },
  {
    name: 'tidelift',
    link: 'https://tidelift.com/subscription/pkg/npm-material-ui?utm_source=npm-material-ui&utm_medium=referral&utm_campaign=enterprise&utm_content=ad',
    img: '/static/ads-in-house/tidelift.png',
    description:
      'Material-UI for enterprise. Save time and reduce risk. Managed open source — backed by maintainers.',
  },
  {
    name: 'sketch',
    link: 'https://material-ui.com/store/items/sketch-react/?utm_source=docs&utm_medium=referral&utm_campaign=in-house-sketch',
    img: '/static/ads-in-house/sketch.png',
    description:
      'For Sketch. A large UI kit with over 600 handcrafted Material-UI symbols 💎.',
  },
  {
    name: 'figma',
    link: 'https://material-ui.com/store/items/figma-react/?utm_source=docs&utm_medium=referral&utm_campaign=in-house-figma',
    img: '/static/ads-in-house/figma.png',
    description:
      'For Figma. A large UI kit with over 600 handcrafted Material-UI components 🎨.',
  },
];

class AdErrorBoundary extends React.Component {
  static propTypes = {
    children: PropTypes.node.isRequired,
    eventLabel: PropTypes.string,
  };

  state = { didError: false };

  static getDerivedStateFromError() {
    return { didError: true };
  }

  componentDidCatch() {
    // send explicit `'null'`
    const eventLabel = String(this.props.eventLabel);
    // TODO: Use proper error monitoring service (e.g. Sentry) instead
    window.ga('send', {
      hitType: 'event',
      eventCategory: 'ad',
      eventAction: 'crash',
      eventLabel,
    });
  }

  render() {
    const { didError } = this.state;
    const { children } = this.props;

    if (didError) {
      return null;
    }
    return children;
  }
}

function Ad(props) {
  const { classes } = props;

  const [adblock, setAdblock] = React.useState(null);
  const [carbonOut, setCarbonOut] = React.useState(null);

  const { current: randomAdblock } = React.useRef(Math.random());
  const { current: randomInHouse } = React.useRef(Math.random());

  let children;
  let label;
  // Hide the content to google bot to avoid its indexation.
  if (/Googlebot/.test(navigator.userAgent) || disable) {
    children = ;
  } else if (adblock) {
    if (randomAdblock < 0.2) {
      children = <PleaseDisableAdblock className={classes.paper} />;
      label = 'in-house-adblock';
    } else {
      children = <AdInHouse ad={inHouseAds[Math.floor(inHouseAds.length * randomInHouse)]} />;
      label = 'in-house';
    }
  } else if (carbonOut) {
    children = <AdInHouse ad={inHouseAds[Math.floor(inHouseAds.length * randomInHouse)]} />;
    label = 'in-house-carbon';
  } else {
    children = <AdCarbon />;
    label = 'carbon';
  }

  const ad = React.useContext(AdContext);
  const eventLabel = label ? `${label}-${ad.placement}-${adShape}` : null;

  const timerAdblock = React.useRef();

  const checkAdblock = React.useCallback(
    (attempt = 1) => {
      if (
        document.querySelector('.ea-placement') ||
        document.querySelector('#carbonads') ||
        document.querySelector('.carbonads') ||
        carbonOut
      ) {
        if (
          document.querySelector('#carbonads a') &&
          document.querySelector('#carbonads a').getAttribute('href') ===
            'https://material-ui-next.com/discover-more/backers'
        ) {
          setCarbonOut(true);
        }

        setAdblock(false);
        return;
      }

      if (attempt < 30) {
        timerAdblock.current = setTimeout(() => {
          checkAdblock(attempt + 1);
        }, 500);
      }

      if (attempt > 6) {
        setAdblock(true);
      }
    },
    [carbonOut],
  );

  React.useEffect(() => {
    if (disable) {
      return undefined;
    }
    checkAdblock();

    return () => {
      clearTimeout(timerAdblock.current);
    };
  }, [checkAdblock]);

  React.useEffect(() => {
    // Avoid an exceed on the Google Analytics quotas.
    if (Math.random() < 0.9 || !eventLabel) {
      return undefined;
    }

    const delay = setTimeout(() => {
      window.ga('send', {
        hitType: 'event',
        eventCategory: 'ad',
        eventAction: 'display',
        eventLabel,
      });
    }, 2500);

    return () => {
      clearTimeout(delay);
    };
  }, [eventLabel]);

  return (
    <Box
      component="span"
      sx={{
        position: 'relative',
        display: 'block',
      }}
      className={classes[`placement-body-${adShape}`]}
      data-ga-event-category="ad"
      data-ga-event-action="click"
      data-ga-event-label={eventLabel}
    >
      <AdErrorBoundary eventLabel={eventLabel}>{children}</AdErrorBoundary>
    </Box>
  );
}

Ad.propTypes = {
  classes: PropTypes.object.isRequired,
};

const defaultTheme = createTheme();
export default React.memo(withStyles(styles, { defaultTheme })(Ad));
<|endoftext|>


<commit_before>import { VIDEO, BANNER } from '../src/mediaTypes.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { Renderer } from '../src/Renderer.js';
import { logError } from '../src/utils.js';

const BIDDER_CODE = 'aniview';
const GVLID = 780;
const TTL = 600;

function avRenderer(bid) {
  bid.renderer.push(function() {
    let eventCallback = bid && bid.renderer && bid.renderer.handleVideoEvent ? bid.renderer.handleVideoEvent : null;
    window.aniviewRenderer.renderAd({
      id: bid.adUnitCode + '_' + bid.adId,
      debug: window.location.href.indexOf('pbjsDebug') >= 0,
      placement: bid.adUnitCode,
      width: bid.width,
      height: bid.height,
      vastUrl: bid.vastUrl,
      vastXml: bid.vastXml,
      config: bid.params[0].rendererConfig,
      eventsCallback: eventCallback,
      bid: bid
    });
  });
}

function newRenderer(bidRequest) {
  let playerDomain = 'player.aniview.com';
  const config = {};

  if (bidRequest && bidRequest.bidRequest && bidRequest.bidRequest.params) {
    const params = bidRequest.bidRequest.params

    if (params.playerDomain) {
      playerDomain = params.playerDomain;
    }

    if (params.AV_PUBLISHERID) {
      config.AV_PUBLISHERID = params.AV_PUBLISHERID;
    }

    if (params.AV_CHANNELID) {
      config.AV_CHANNELID = params.AV_CHANNELID;
    }
  }

  const renderer = Renderer.install({
    url: 'https://' + playerDomain + '/script/6.1/prebidRenderer.js',
    config: config,
    loaded: false,
  });

  try {
    renderer.setRender(avRenderer);
  } catch (err) {
  }

  return renderer;
}

function isBidRequestValid(bid) {
  if (!bid.params || !bid.params.AV_PUBLISHERID || !bid.params.AV_CHANNELID) { return false; }

  return true;
}
let irc = 0;
function buildRequests(validBidRequests, bidderRequest) {
  let bidRequests = [];

  for (let i = 0; i < validBidRequests.length; i++) {
    let bidRequest = validBidRequests[i];
    var sizes = [[640, 480]];

    if (bidRequest.mediaTypes && bidRequest.mediaTypes.video && bidRequest.mediaTypes.video.playerSize) {
      sizes = bidRequest.mediaTypes.video.playerSize;
    } else {
      if (bidRequest.sizes) {
        sizes = bidRequest.sizes;
      }
    }
    if (sizes.length === 2 && typeof sizes[0] === 'number') {
      sizes = [[sizes[0], sizes[1]]];
    }

    for (let j = 0; j < sizes.length; j++) {
      let size = sizes[j];
      let playerWidth;
      let playerHeight;

      if (size && size.length == 2) {
        playerWidth = size[0];
        playerHeight = size[1];
      } else {
        playerWidth = 640;
        playerHeight = 480;
      }

      let s2sParams = {};

      for (var attrname in bidRequest.params) {
        if (bidRequest.params.hasOwnProperty(attrname) && attrname.indexOf('AV_') == 0) {
          s2sParams[attrname] = bidRequest.params[attrname];
        }
      };

      if (s2sParams.AV_APPPKGNAME && !s2sParams.AV_URL) { s2sParams.AV_URL = s2sParams.AV_APPPKGNAME; }
      if (!s2sParams.AV_IDFA && !s2sParams.AV_URL) {
        if (bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer) {
          s2sParams.AV_URL = bidderRequest.refererInfo.referer;
        } else {
          s2sParams.AV_URL = window.location.href;
        }
      }
      if (s2sParams.AV_IDFA && !s2sParams.AV_AID) { s2sParams.AV_AID = s2sParams.AV_IDFA; }
      if (s2sParams.AV_AID && !s2sParams.AV_IDFA) { s2sParams.AV_IDFA = s2sParams.AV_AID; }

      s2sParams.cb = Math.floor(Math.random() * 999999999);
      s2sParams.AV_WIDTH = playerWidth;
      s2sParams.AV_HEIGHT = playerHeight;
      s2sParams.bidWidth = playerWidth;
      s2sParams.bidHeight = playerHeight;
      s2sParams.bidId = bidRequest.bidId;
      s2sParams.pbjs = 1;
      s2sParams.tgt = 10;
      s2sParams.s2s = '1';
      s2sParams.irc = irc;
      irc++;
      s2sParams.wpm = 1;

      if (bidderRequest && bidderRequest.gdprConsent) {
        if (bidderRequest.gdprConsent.gdprApplies) {
          s2sParams.AV_GDPR = 1;
          s2sParams.AV_CONSENT = bidderRequest.gdprConsent.consentString;
        }
      }
      if (bidderRequest && bidderRequest.uspConsent) {
        s2sParams.AV_CCPA = bidderRequest.uspConsent;
      }

      let serverDomain = bidRequest.params && bidRequest.params.serverDomain ? bidRequest.params.serverDomain : 'gov.aniview.com';
      let servingUrl = 'https://' + serverDomain + '/api/adserver/vast3/';

      bidRequests.push({
        method: 'GET',
        url: servingUrl,
        data: s2sParams,
        bidRequest
      });
    }
  }

  return bidRequests;
}
function getCpmData(xml) {
  let ret = {cpm: 0, currency: 'USD'};
  if (xml) {
    let ext = xml.getElementsByTagName('Extensions');
    if (ext && ext.length > 0) {
      ext = ext[0].getElementsByTagName('Extension');
      if (ext && ext.length > 0) {
        for (var i = 0; i < ext.length; i++) {
          if (ext[i].getAttribute('type') == 'ANIVIEW') {
            let price = ext[i].getElementsByTagName('Cpm');
            if (price && price.length == 1) {
              ret.cpm = price[0].textContent;
            }
            break;
          }
        }
      }
    }
  }
  return ret;
}
function buildBanner(xmlStr, bidRequest, bidResponse) {
  var rendererData = JSON.stringify({
    id: bidRequest.adUnitCode,
    debug: window.location.href.indexOf('pbjsDebug') >= 0,
    placement: bidRequest.bidRequest.adUnitCode,
    width: bidResponse.width,
    height: bidResponse.height,
    vastXml: xmlStr,
    bid: bidResponse,
    config: bidRequest.bidRequest.params.rendererConfig
  });
  var playerDomain = bidRequest.bidRequest.params.playerDomain || 'player.aniview.com';
  var ad = '<script src="https://' + playerDomain + '/script/6.1/prebidRenderer.js"></script>';
  ad += '<script> window.aniviewRenderer.renderAd(' + rendererData + ') </script>'
  return ad;
}
function interpretResponse(serverResponse, bidRequest) {
  let bidResponses = [];
  if (serverResponse && serverResponse.body) {
    if (serverResponse.error) {
      return bidResponses;
    } else {
      try {
        let bidResponse = {};
        if (bidRequest && bidRequest.data && bidRequest.data.bidId && bidRequest.data.bidId !== '') {
          let mediaType = VIDEO;
          if (bidRequest.bidRequest && bidRequest.bidRequest.mediaTypes && !bidRequest.bidRequest.mediaTypes[VIDEO]) {
            mediaType = BANNER;
          }
          let xmlStr = serverResponse.body;
          let xml = new window.DOMParser().parseFromString(xmlStr, 'text/xml');
          if (xml && xml.getElementsByTagName('parsererror').length == 0) {
            let cpmData = getCpmData(xml);
            if (cpmData && cpmData.cpm > 0) {
              bidResponse.requestId = bidRequest.data.bidId;
              bidResponse.ad = '';
              bidResponse.cpm = cpmData.cpm;
              bidResponse.width = bidRequest.data.AV_WIDTH;
              bidResponse.height = bidRequest.data.AV_HEIGHT;
              bidResponse.ttl = TTL;
              bidResponse.creativeId = xml.getElementsByTagName('Ad') && xml.getElementsByTagName('Ad')[0] && xml.getElementsByTagName('Ad')[0].getAttribute('id') ? xml.getElementsByTagName('Ad')[0].getAttribute('id') : 'creativeId';
              bidResponse.currency = cpmData.currency;
              bidResponse.netRevenue = true;
              bidResponse.mediaType = mediaType;
              if (mediaType === VIDEO) {
                try {
                  var blob = new Blob([xmlStr], {
                    type: 'application/xml'
                  });
                  bidResponse.vastUrl = window.URL.createObjectURL(blob);
                } catch (ex) {
                  logError('Aniview Debug create vastXml error:\n\n' + ex);
                }
                bidResponse.vastXml = xmlStr;
                if (bidRequest.bidRequest && bidRequest.bidRequest.mediaTypes && bidRequest.bidRequest.mediaTypes.video && bidRequest.bidRequest.mediaTypes.video.context === 'outstream') {
                  bidResponse.renderer = newRenderer(bidRequest);
                }
              } else {
                bidResponse.ad = buildBanner(xmlStr, bidRequest, bidResponse);
              }
              bidResponse.meta = {
                advertiserDomains: []
              };

              bidResponses.push(bidResponse);
            }
          } else {}
        } else {}
      } catch (e) {}
    }
  } else {}

  return bidResponses;
}

function getSyncData(xml, options) {
  let ret = [];
  if (xml) {
    let ext = xml.getElementsByTagName('Extensions');
    if (ext && ext.length > 0) {
      ext = ext[0].getElementsByTagName('Extension');
      if (ext && ext.length > 0) {
        for (var i = 0; i < ext.length; i++) {
          if (ext[i].getAttribute('type') == 'ANIVIEW') {
            let syncs = ext[i].getElementsByTagName('AdServingSync');
            if (syncs && syncs.length == 1) {
              try {
                let data = JSON.parse(syncs[0].textContent);
                if (data && data.trackers && data.trackers.length) {
                  data = data.trackers;
                  for (var j = 0; j < data.length; j++) {
                    if (typeof data[j] === 'object' &&
                      typeof data[j].url === 'string' &&
                      (data[j].e === 'inventory' || data[j].e === 'sync')
                    ) {
                      if (data[j].t == 1 && options.pixelEnabled) {
                        ret.push({url: data[j].url, type: 'image'});
                      } else {
                        if (data[j].t == 3 && options.iframeEnabled) {
                          ret.push({url: data[j].url, type: 'iframe'});
                        }
                      }
                    }
                  }
                }
              } catch (e) {}
            }
            break;
          }
        }
      }
    }
  }
  return ret;
}

function getUserSyncs(syncOptions, serverResponses) {
  if (serverResponses && serverResponses[0] && serverResponses[0].body) {
    if (serverResponses.error) {
      return [];
    } else {
      try {
        let xmlStr = serverResponses[0].body;
        let xml = new window.DOMParser().parseFromString(xmlStr, 'text/xml');
        if (xml && xml.getElementsByTagName('parsererror').length == 0) {
          let syncData = getSyncData(xml, syncOptions);
          return syncData;
        }
      } catch (e) {}
    }
  }
}

export const spec = {
  code: BIDDER_CODE,
  gvlid: GVLID,
  aliases: ['avantisvideo', 'selectmediavideo', 'vidcrunch', 'openwebvideo', 'didnavideo'],
  supportedMediaTypes: [VIDEO, BANNER],
  isBidRequestValid,
  buildRequests,
  interpretResponse,
  getUserSyncs
};

registerBidder(spec);
<commit_msg>New alias for Aniview prebid adapter (#8125)<commit_after>import { VIDEO, BANNER } from '../src/mediaTypes.js';
import { registerBidder } from '../src/adapters/bidderFactory.js';
import { Renderer } from '../src/Renderer.js';
import { logError } from '../src/utils.js';

const BIDDER_CODE = 'aniview';
const GVLID = 780;
const TTL = 600;

function avRenderer(bid) {
  bid.renderer.push(function() {
    let eventCallback = bid && bid.renderer && bid.renderer.handleVideoEvent ? bid.renderer.handleVideoEvent : null;
    window.aniviewRenderer.renderAd({
      id: bid.adUnitCode + '_' + bid.adId,
      debug: window.location.href.indexOf('pbjsDebug') >= 0,
      placement: bid.adUnitCode,
      width: bid.width,
      height: bid.height,
      vastUrl: bid.vastUrl,
      vastXml: bid.vastXml,
      config: bid.params[0].rendererConfig,
      eventsCallback: eventCallback,
      bid: bid
    });
  });
}

function newRenderer(bidRequest) {
  let playerDomain = 'player.aniview.com';
  const config = {};

  if (bidRequest && bidRequest.bidRequest && bidRequest.bidRequest.params) {
    const params = bidRequest.bidRequest.params

    if (params.playerDomain) {
      playerDomain = params.playerDomain;
    }

    if (params.AV_PUBLISHERID) {
      config.AV_PUBLISHERID = params.AV_PUBLISHERID;
    }

    if (params.AV_CHANNELID) {
      config.AV_CHANNELID = params.AV_CHANNELID;
    }
  }

  const renderer = Renderer.install({
    url: 'https://' + playerDomain + '/script/6.1/prebidRenderer.js',
    config: config,
    loaded: false,
  });

  try {
    renderer.setRender(avRenderer);
  } catch (err) {
  }

  return renderer;
}

function isBidRequestValid(bid) {
  if (!bid.params || !bid.params.AV_PUBLISHERID || !bid.params.AV_CHANNELID) { return false; }

  return true;
}
let irc = 0;
function buildRequests(validBidRequests, bidderRequest) {
  let bidRequests = [];

  for (let i = 0; i < validBidRequests.length; i++) {
    let bidRequest = validBidRequests[i];
    var sizes = [[640, 480]];

    if (bidRequest.mediaTypes && bidRequest.mediaTypes.video && bidRequest.mediaTypes.video.playerSize) {
      sizes = bidRequest.mediaTypes.video.playerSize;
    } else {
      if (bidRequest.sizes) {
        sizes = bidRequest.sizes;
      }
    }
    if (sizes.length === 2 && typeof sizes[0] === 'number') {
      sizes = [[sizes[0], sizes[1]]];
    }

    for (let j = 0; j < sizes.length; j++) {
      let size = sizes[j];
      let playerWidth;
      let playerHeight;

      if (size && size.length == 2) {
        playerWidth = size[0];
        playerHeight = size[1];
      } else {
        playerWidth = 640;
        playerHeight = 480;
      }

      let s2sParams = {};

      for (var attrname in bidRequest.params) {
        if (bidRequest.params.hasOwnProperty(attrname) && attrname.indexOf('AV_') == 0) {
          s2sParams[attrname] = bidRequest.params[attrname];
        }
      };

      if (s2sParams.AV_APPPKGNAME && !s2sParams.AV_URL) { s2sParams.AV_URL = s2sParams.AV_APPPKGNAME; }
      if (!s2sParams.AV_IDFA && !s2sParams.AV_URL) {
        if (bidderRequest && bidderRequest.refererInfo && bidderRequest.refererInfo.referer) {
          s2sParams.AV_URL = bidderRequest.refererInfo.referer;
        } else {
          s2sParams.AV_URL = window.location.href;
        }
      }
      if (s2sParams.AV_IDFA && !s2sParams.AV_AID) { s2sParams.AV_AID = s2sParams.AV_IDFA; }
      if (s2sParams.AV_AID && !s2sParams.AV_IDFA) { s2sParams.AV_IDFA = s2sParams.AV_AID; }

      s2sParams.cb = Math.floor(Math.random() * 999999999);
      s2sParams.AV_WIDTH = playerWidth;
      s2sParams.AV_HEIGHT = playerHeight;
      s2sParams.bidWidth = playerWidth;
      s2sParams.bidHeight = playerHeight;
      s2sParams.bidId = bidRequest.bidId;
      s2sParams.pbjs = 1;
      s2sParams.tgt = 10;
      s2sParams.s2s = '1';
      s2sParams.irc = irc;
      irc++;
      s2sParams.wpm = 1;

      if (bidderRequest && bidderRequest.gdprConsent) {
        if (bidderRequest.gdprConsent.gdprApplies) {
          s2sParams.AV_GDPR = 1;
          s2sParams.AV_CONSENT = bidderRequest.gdprConsent.consentString;
        }
      }
      if (bidderRequest && bidderRequest.uspConsent) {
        s2sParams.AV_CCPA = bidderRequest.uspConsent;
      }

      let serverDomain = bidRequest.params && bidRequest.params.serverDomain ? bidRequest.params.serverDomain : 'gov.aniview.com';
      let servingUrl = 'https://' + serverDomain + '/api/adserver/vast3/';

      bidRequests.push({
        method: 'GET',
        url: servingUrl,
        data: s2sParams,
        bidRequest
      });
    }
  }

  return bidRequests;
}
function getCpmData(xml) {
  let ret = {cpm: 0, currency: 'USD'};
  if (xml) {
    let ext = xml.getElementsByTagName('Extensions');
    if (ext && ext.length > 0) {
      ext = ext[0].getElementsByTagName('Extension');
      if (ext && ext.length > 0) {
        for (var i = 0; i < ext.length; i++) {
          if (ext[i].getAttribute('type') == 'ANIVIEW') {
            let price = ext[i].getElementsByTagName('Cpm');
            if (price && price.length == 1) {
              ret.cpm = price[0].textContent;
            }
            break;
          }
        }
      }
    }
  }
  return ret;
}
function buildBanner(xmlStr, bidRequest, bidResponse) {
  var rendererData = JSON.stringify({
    id: bidRequest.adUnitCode,
    debug: window.location.href.indexOf('pbjsDebug') >= 0,
    placement: bidRequest.bidRequest.adUnitCode,
    width: bidResponse.width,
    height: bidResponse.height,
    vastXml: xmlStr,
    bid: bidResponse,
    config: bidRequest.bidRequest.params.rendererConfig
  });
  var playerDomain = bidRequest.bidRequest.params.playerDomain || 'player.aniview.com';
  var ad = '<script src="https://' + playerDomain + '/script/6.1/prebidRenderer.js"></script>';
  ad += '<script> window.aniviewRenderer.renderAd(' + rendererData + ') </script>'
  return ad;
}
function interpretResponse(serverResponse, bidRequest) {
  let bidResponses = [];
  if (serverResponse && serverResponse.body) {
    if (serverResponse.error) {
      return bidResponses;
    } else {
      try {
        let bidResponse = {};
        if (bidRequest && bidRequest.data && bidRequest.data.bidId && bidRequest.data.bidId !== '') {
          let mediaType = VIDEO;
          if (bidRequest.bidRequest && bidRequest.bidRequest.mediaTypes && !bidRequest.bidRequest.mediaTypes[VIDEO]) {
            mediaType = BANNER;
          }
          let xmlStr = serverResponse.body;
          let xml = new window.DOMParser().parseFromString(xmlStr, 'text/xml');
          if (xml && xml.getElementsByTagName('parsererror').length == 0) {
            let cpmData = getCpmData(xml);
            if (cpmData && cpmData.cpm > 0) {
              bidResponse.requestId = bidRequest.data.bidId;
              bidResponse.ad = '';
              bidResponse.cpm = cpmData.cpm;
              bidResponse.width = bidRequest.data.AV_WIDTH;
              bidResponse.height = bidRequest.data.AV_HEIGHT;
              bidResponse.ttl = TTL;
              bidResponse.creativeId = xml.getElementsByTagName('Ad') && xml.getElementsByTagName('Ad')[0] && xml.getElementsByTagName('Ad')[0].getAttribute('id') ? xml.getElementsByTagName('Ad')[0].getAttribute('id') : 'creativeId';
              bidResponse.currency = cpmData.currency;
              bidResponse.netRevenue = true;
              bidResponse.mediaType = mediaType;
              if (mediaType === VIDEO) {
                try {
                  var blob = new Blob([xmlStr], {
                    type: 'application/xml'
                  });
                  bidResponse.vastUrl = window.URL.createObjectURL(blob);
                } catch (ex) {
                  logError('Aniview Debug create vastXml error:\n\n' + ex);
                }
                bidResponse.vastXml = xmlStr;
                if (bidRequest.bidRequest && bidRequest.bidRequest.mediaTypes && bidRequest.bidRequest.mediaTypes.video && bidRequest.bidRequest.mediaTypes.video.context === 'outstream') {
                  bidResponse.renderer = newRenderer(bidRequest);
                }
              } else {
                bidResponse.ad = buildBanner(xmlStr, bidRequest, bidResponse);
              }
              bidResponse.meta = {
                advertiserDomains: []
              };

              bidResponses.push(bidResponse);
            }
          } else {}
        } else {}
      } catch (e) {}
    }
  } else {}

  return bidResponses;
}

function getSyncData(xml, options) {
  let ret = [];
  if (xml) {
    let ext = xml.getElementsByTagName('Extensions');
    if (ext && ext.length > 0) {
      ext = ext[0].getElementsByTagName('Extension');
      if (ext && ext.length > 0) {
        for (var i = 0; i < ext.length; i++) {
          if (ext[i].getAttribute('type') == 'ANIVIEW') {
            let syncs = ext[i].getElementsByTagName('AdServingSync');
            if (syncs && syncs.length == 1) {
              try {
                let data = JSON.parse(syncs[0].textContent);
                if (data && data.trackers && data.trackers.length) {
                  data = data.trackers;
                  for (var j = 0; j < data.length; j++) {
                    if (typeof data[j] === 'object' &&
                      typeof data[j].url === 'string' &&
                      (data[j].e === 'inventory' || data[j].e === 'sync')
                    ) {
                      if (data[j].t == 1 && options.pixelEnabled) {
                        ret.push({url: data[j].url, type: 'image'});
                      } else {
                        if (data[j].t == 3 && options.iframeEnabled) {
                          ret.push({url: data[j].url, type: 'iframe'});
                        }
                      }
                    }
                  }
                }
              } catch (e) {}
            }
            break;
          }
        }
      }
    }
  }
  return ret;
}

function getUserSyncs(syncOptions, serverResponses) {
  if (serverResponses && serverResponses[0] && serverResponses[0].body) {
    if (serverResponses.error) {
      return [];
    } else {
      try {
        let xmlStr = serverResponses[0].body;
        let xml = new window.DOMParser().parseFromString(xmlStr, 'text/xml');
        if (xml && xml.getElementsByTagName('parsererror').length == 0) {
          let syncData = getSyncData(xml, syncOptions);
          return syncData;
        }
      } catch (e) {}
    }
  }
}

export const spec = {
  code: BIDDER_CODE,
  gvlid: GVLID,
  aliases: ['avantisvideo', 'selectmediavideo', 'vidcrunch', 'openwebvideo', 'didnavideo', 'ottadvisors'],
  supportedMediaTypes: [VIDEO, BANNER],
  isBidRequestValid,
  buildRequests,
  interpretResponse,
  getUserSyncs
};

registerBidder(spec);
<|endoftext|>