<commit_before>/**
 * Copyright (c) 2011-2016 Berkeley Model United Nations. All rights reserved.
 * Use of this source code is governed by a BSD License (see LICENSE).
 */

'use strict';

var ActionConstants = require('constants/ActionConstants');
var CurrentUserStore = require('stores/CurrentUserStore');
var DelegateActions = require('actions/DelegateActions');
var Dispatcher = require('dispatcher/Dispatcher');
var ServerAPI = require('lib/ServerAPI');
var {Store} = require('flux/utils');

var _delegates = {};
var _previousUserID = -1;

class DelegateStore extends Store {
  getSchoolDelegates(schoolID) {
    var delegateIDs = Object.keys(_delegates);
    if (!delegateIDs.length) {
      ServerAPI.getDelegates(schoolID).then(value => {
        DelegateActions.delegatesFetched(value);
      });

      return [];
    }

    return delegateIDs.map(id => _delegates[id]);
  }

  getCommitteeDelegates(committeeID) {
    var delegateIDs = Object.keys(_delegates);
    if (!delegateIDs.length) {
      ServerAPI.getCommitteeDelegates(committeeID).then(value => {
        DelegateActions.delegatesFetched(value);
      });

      return [];
    }

    return delegateIDs.map(id => _delegates[id]);
  }

  deleteDelegate(delegateID, onError) {
    ServerAPI.deleteDelegate(delegateID).catch(onError);
    delete _delegates[delegateID];
  }

  addDelegate(delegate) {
    _delegates[delegate.id] = delegate;
  }

  updateDelegate(delegateID, delta, onError) {
    const delegate = {..._delegates[delegateID], ...delta};
    ServerAPI.updateDelegate(delegateID, delegate).catch(onError);
    _delegates[delegateID] = delegate;
  }

  updateDelegates(schoolID, delegates, onError) {
    ServerAPI.updateSchoolDelegates(schoolID, delegates).catch(onError);
    for (const delegate of delegates) {
      _delegates[delegate.id] = delegate;
    }
  }

  updateCommitteeDelegates(committeeID, delegates) {
    ServerAPI.updateCommitteeDelegates(committeeID, delegates);
    for (const delegate of delegates) {
      _delegates[delegate.id] = delegate;
    }
  }

  __onDispatch(action) {
    switch (action.actionType) {
      case ActionConstants.DELETE_DELEGATE:
        this.deleteDelegate(action.delegateID, action.onError);
        break;
      case ActionConstants.ADD_DELEGATE:
        this.addDelegate(action.delegate);
        break;
      case ActionConstants.UPDATE_DELEGATE:
        this.updateDelegate(action.delegateID, action.delta, action.onError);
        break;
      case ActionConstants.DELEGATES_FETCHED:
        for (const delegate of action.delegates) {
          _delegates[delegate.id] = delegate;
        }
        break;
      case ActionConstants.UPDATE_DELEGATES:
        this.updateDelegates(action.schoolID, action.delegates, action.onError);
        break;
      case ActionConstants.UPDATE_COMMITTEE_DELEGATES:
        this.updateCommitteeDelegates(action.committeeID, action.delegates);
        break;
      case ActionConstants.LOGIN:
        var userID = CurrentUserStore.getCurrentUser().id;
        if(userID != _previousUserID) {
          _delegates = {};
          _previousUserID = userID;
        }
        break;
      default:
        return;
    }

    this.__emitChange();
  }
};

module.exports = new DelegateStore(Dispatcher);
<commit_msg>temp fix for issue with modal (#567)<commit_after>/**
 * Copyright (c) 2011-2016 Berkeley Model United Nations. All rights reserved.
 * Use of this source code is governed by a BSD License (see LICENSE).
 */

'use strict';

var ActionConstants = require('constants/ActionConstants');
var CurrentUserStore = require('stores/CurrentUserStore');
var DelegateActions = require('actions/DelegateActions');
var Dispatcher = require('dispatcher/Dispatcher');
var ServerAPI = require('lib/ServerAPI');
var {Store} = require('flux/utils');

var _delegates = {};
var _previousUserID = -1;

class DelegateStore extends Store {
  getSchoolDelegates(schoolID) {
    var delegateIDs = Object.keys(_delegates);
    if (!delegateIDs.length) {
      ServerAPI.getDelegates(schoolID).then(value => {
        if (value.length) {
          DelegateActions.delegatesFetched(value);
        }
      });

      return [];
    }

    return delegateIDs.map(id => _delegates[id]);
  }

  getCommitteeDelegates(committeeID) {
    var delegateIDs = Object.keys(_delegates);
    if (!delegateIDs.length) {
      ServerAPI.getCommitteeDelegates(committeeID).then(value => {
        DelegateActions.delegatesFetched(value);
      });

      return [];
    }

    return delegateIDs.map(id => _delegates[id]);
  }

  deleteDelegate(delegateID, onError) {
    ServerAPI.deleteDelegate(delegateID).catch(onError);
    delete _delegates[delegateID];
  }

  addDelegate(delegate) {
    _delegates[delegate.id] = delegate;
  }

  updateDelegate(delegateID, delta, onError) {
    const delegate = {..._delegates[delegateID], ...delta};
    ServerAPI.updateDelegate(delegateID, delegate).catch(onError);
    _delegates[delegateID] = delegate;
  }

  updateDelegates(schoolID, delegates, onError) {
    ServerAPI.updateSchoolDelegates(schoolID, delegates).catch(onError);
    for (const delegate of delegates) {
      _delegates[delegate.id] = delegate;
    }
  }

  updateCommitteeDelegates(committeeID, delegates) {
    ServerAPI.updateCommitteeDelegates(committeeID, delegates);
    for (const delegate of delegates) {
      _delegates[delegate.id] = delegate;
    }
  }

  __onDispatch(action) {
    switch (action.actionType) {
      case ActionConstants.DELETE_DELEGATE:
        this.deleteDelegate(action.delegateID, action.onError);
        break;
      case ActionConstants.ADD_DELEGATE:
        this.addDelegate(action.delegate);
        break;
      case ActionConstants.UPDATE_DELEGATE:
        this.updateDelegate(action.delegateID, action.delta, action.onError);
        break;
      case ActionConstants.DELEGATES_FETCHED:
        for (const delegate of action.delegates) {
          _delegates[delegate.id] = delegate;
        }
        break;
      case ActionConstants.UPDATE_DELEGATES:
        this.updateDelegates(action.schoolID, action.delegates, action.onError);
        break;
      case ActionConstants.UPDATE_COMMITTEE_DELEGATES:
        this.updateCommitteeDelegates(action.committeeID, action.delegates);
        break;
      case ActionConstants.LOGIN:
        var userID = CurrentUserStore.getCurrentUser().id;
        if(userID != _previousUserID) {
          _delegates = {};
          _previousUserID = userID;
        }
        break;
      default:
        return;
    }

    this.__emitChange();
  }
};

module.exports = new DelegateStore(Dispatcher);
<|endoftext|>


<commit_before>var assert = require('assert');
var _ = require('underscore');
var PSQL = require('cartodb-psql');
var step = require('step');
var BBoxFilter = require('../models/filter/bbox');
var DataviewFactory = require('../models/dataview/factory');
var DataviewFactoryWithOverviews = require('../models/dataview/overviews/factory');
const dbParamsFromReqParams = require('../utils/database-params');
var OverviewsQueryRewriter = require('../utils/overviews_query_rewriter');
var overviewsQueryRewriter = new OverviewsQueryRewriter({
    zoom_level: 'CDB_ZoomFromScale(!scale_denominator!)'
});

var dot = require('dot');
dot.templateSettings.strip = false;

function DataviewBackend(analysisBackend) {
    this.analysisBackend = analysisBackend;
}

module.exports = DataviewBackend;

DataviewBackend.prototype.getDataview = function (mapConfigProvider, user, params, callback) {
    const dataviewName = params.dataviewName;

    mapConfigProvider.getMapConfig(function (err, mapConfig) {
        if (err) {
            return callback(err);
        }

        var dataviewDefinition = getDataviewDefinition(mapConfig.obj(), dataviewName);
        if (!dataviewDefinition) {
            return callback(new Error("Dataview '" + dataviewName + "' does not exists"));
        }

        var ownFilter = +params.own_filter;
        var noFilters = +params.no_filters;
        if (Number.isFinite(ownFilter) && Number.isFinite(noFilters)) {
            err = new Error();
            err.message = 'Both own_filter and no_filters cannot be sent in the same request';
            err.type = 'dataview';
            err.http_status = 400;
            return callback(err);
        }

        var pg = new PSQL(dbParamsFromReqParams(params));

        var query = getDataviewQuery(dataviewDefinition, ownFilter, noFilters);
        if (params.bbox) {
            var bboxFilter = new BBoxFilter({column: 'the_geom_webmercator', srid: 3857}, {bbox: params.bbox});
            query = bboxFilter.sql(query);
        }

        var queryRewriteData = getQueryRewriteData(mapConfig, dataviewDefinition, params);

        var dataviewFactory = DataviewFactoryWithOverviews.getFactory(
            overviewsQueryRewriter, queryRewriteData, { bbox: params.bbox }
        );

        var dataview = dataviewFactory.getDataview(query, dataviewDefinition);

        let overrideParams;

        try {
            overrideParams = getOverrideParams(params, !!ownFilter);
        } catch (error) {
            return callback(error);
        }

        dataview.getResult(pg, overrideParams, function (err, dataview) {
            if (err) {
                return callback(err);
            }

            return callback(null, dataview);
        });
    });
};

function getDataviewQuery(dataviewDefinition, ownFilter, noFilters) {
    if (noFilters) {
        return dataviewDefinition.sql.no_filters;
    } else if (ownFilter === 1) {
        return dataviewDefinition.sql.own_filter_on;
    } else {
        return dataviewDefinition.sql.own_filter_off;
    }
}

function getQueryRewriteData(mapConfig, dataviewDefinition, params) {
    var sourceId = dataviewDefinition.source.id; // node.id
    var layer = _.find(mapConfig.obj().layers, function(l) {
        return l.options.source && (l.options.source.id === sourceId);
    });
    var queryRewriteData = layer && layer.options.query_rewrite_data;
    if (queryRewriteData && dataviewDefinition.node.type === 'source') {
        queryRewriteData = _.extend({}, queryRewriteData, {
            filters: dataviewDefinition.node.filters,
            unfiltered_query: dataviewDefinition.sql.own_filter_on
        });
    }

    if (params.bbox && queryRewriteData) {
        var bbox_filter_definition = {
            type: 'bbox',
            options: {
                column: 'the_geom_webmercator',
                srid: 3857
            },
            params: {
                bbox: params.bbox
            }
        };
        queryRewriteData = _.extend(queryRewriteData, { bbox_filter: bbox_filter_definition });
    }

    return queryRewriteData;
}

function getOverrideParams(params, ownFilter) {
    var overrideParams = _.reduce(_.pick(params, 'start', 'end', 'bins', 'offset', 'categories'),
        function castNumbers(overrides, val, k) {
            if (!Number.isFinite(+val)) {
                throw new Error('Invalid number format for parameter \'' + k + '\'');
            }
            overrides[k] = +val;
            return overrides;
        },
        {ownFilter: ownFilter}
    );

    // validation will be delegated to the proper dataview
    if (params.aggregation !== undefined) {
        overrideParams.aggregation = params.aggregation;
    }

    return overrideParams;
}

DataviewBackend.prototype.search = function (mapConfigProvider, user, dataviewName, params, callback) {
    step(
        function getMapConfig() {
            mapConfigProvider.getMapConfig(this);
        },
        function runDataviewSearchQuery(err, mapConfig) {
            assert.ifError(err);

            var dataviewDefinition = getDataviewDefinition(mapConfig.obj(), dataviewName);
            if (!dataviewDefinition) {
                throw new Error("Dataview '" + dataviewName + "' does not exists");
            }

            var pg = new PSQL(dbParamsFromReqParams(params));

            var ownFilter = +params.own_filter;
            ownFilter = !!ownFilter;

            var query = (ownFilter) ? dataviewDefinition.sql.own_filter_on : dataviewDefinition.sql.own_filter_off;

            if (params.bbox) {
                var bboxFilter = new BBoxFilter({column: 'the_geom', srid: 4326}, {bbox: params.bbox});
                query = bboxFilter.sql(query);
            }

            var userQuery = params.q;

            var dataview = DataviewFactory.getDataview(query, dataviewDefinition);
            dataview.search(pg, userQuery, this);
        },
        function returnCallback(err, result) {
            return callback(err, result);
        }
    );
};

function getDataviewDefinition(mapConfig, dataviewName) {
    var dataviews = mapConfig.dataviews || {};
    return dataviews[dataviewName];
}
<commit_msg>Improve error<commit_after>var assert = require('assert');
var _ = require('underscore');
var PSQL = require('cartodb-psql');
var step = require('step');
var BBoxFilter = require('../models/filter/bbox');
var DataviewFactory = require('../models/dataview/factory');
var DataviewFactoryWithOverviews = require('../models/dataview/overviews/factory');
const dbParamsFromReqParams = require('../utils/database-params');
var OverviewsQueryRewriter = require('../utils/overviews_query_rewriter');
var overviewsQueryRewriter = new OverviewsQueryRewriter({
    zoom_level: 'CDB_ZoomFromScale(!scale_denominator!)'
});

var dot = require('dot');
dot.templateSettings.strip = false;

function DataviewBackend(analysisBackend) {
    this.analysisBackend = analysisBackend;
}

module.exports = DataviewBackend;

DataviewBackend.prototype.getDataview = function (mapConfigProvider, user, params, callback) {
    const dataviewName = params.dataviewName;

    mapConfigProvider.getMapConfig(function (err, mapConfig) {
        if (err) {
            return callback(err);
        }

        var dataviewDefinition = getDataviewDefinition(mapConfig.obj(), dataviewName);
        if (!dataviewDefinition) {
            const error = new Error(`Dataview '${dataviewName}' does not exists`);
            error.type = 'dataview';
            error.http_status = 400;
            return callback(error);
        }

        var ownFilter = +params.own_filter;
        var noFilters = +params.no_filters;
        if (Number.isFinite(ownFilter) && Number.isFinite(noFilters)) {
            err = new Error();
            err.message = 'Both own_filter and no_filters cannot be sent in the same request';
            err.type = 'dataview';
            err.http_status = 400;
            return callback(err);
        }

        var pg = new PSQL(dbParamsFromReqParams(params));

        var query = getDataviewQuery(dataviewDefinition, ownFilter, noFilters);
        if (params.bbox) {
            var bboxFilter = new BBoxFilter({column: 'the_geom_webmercator', srid: 3857}, {bbox: params.bbox});
            query = bboxFilter.sql(query);
        }

        var queryRewriteData = getQueryRewriteData(mapConfig, dataviewDefinition, params);

        var dataviewFactory = DataviewFactoryWithOverviews.getFactory(
            overviewsQueryRewriter, queryRewriteData, { bbox: params.bbox }
        );

        var dataview = dataviewFactory.getDataview(query, dataviewDefinition);

        let overrideParams;

        try {
            overrideParams = getOverrideParams(params, !!ownFilter);
        } catch (error) {
            return callback(error);
        }

        dataview.getResult(pg, overrideParams, function (err, dataview) {
            if (err) {
                return callback(err);
            }

            return callback(null, dataview);
        });
    });
};

function getDataviewQuery(dataviewDefinition, ownFilter, noFilters) {
    if (noFilters) {
        return dataviewDefinition.sql.no_filters;
    } else if (ownFilter === 1) {
        return dataviewDefinition.sql.own_filter_on;
    } else {
        return dataviewDefinition.sql.own_filter_off;
    }
}

function getQueryRewriteData(mapConfig, dataviewDefinition, params) {
    var sourceId = dataviewDefinition.source.id; // node.id
    var layer = _.find(mapConfig.obj().layers, function(l) {
        return l.options.source && (l.options.source.id === sourceId);
    });
    var queryRewriteData = layer && layer.options.query_rewrite_data;
    if (queryRewriteData && dataviewDefinition.node.type === 'source') {
        queryRewriteData = _.extend({}, queryRewriteData, {
            filters: dataviewDefinition.node.filters,
            unfiltered_query: dataviewDefinition.sql.own_filter_on
        });
    }

    if (params.bbox && queryRewriteData) {
        var bbox_filter_definition = {
            type: 'bbox',
            options: {
                column: 'the_geom_webmercator',
                srid: 3857
            },
            params: {
                bbox: params.bbox
            }
        };
        queryRewriteData = _.extend(queryRewriteData, { bbox_filter: bbox_filter_definition });
    }

    return queryRewriteData;
}

function getOverrideParams(params, ownFilter) {
    var overrideParams = _.reduce(_.pick(params, 'start', 'end', 'bins', 'offset', 'categories'),
        function castNumbers(overrides, val, k) {
            if (!Number.isFinite(+val)) {
                throw new Error('Invalid number format for parameter \'' + k + '\'');
            }
            overrides[k] = +val;
            return overrides;
        },
        {ownFilter: ownFilter}
    );

    // validation will be delegated to the proper dataview
    if (params.aggregation !== undefined) {
        overrideParams.aggregation = params.aggregation;
    }

    return overrideParams;
}

DataviewBackend.prototype.search = function (mapConfigProvider, user, dataviewName, params, callback) {
    step(
        function getMapConfig() {
            mapConfigProvider.getMapConfig(this);
        },
        function runDataviewSearchQuery(err, mapConfig) {
            assert.ifError(err);

            var dataviewDefinition = getDataviewDefinition(mapConfig.obj(), dataviewName);
            if (!dataviewDefinition) {
                throw new Error("Dataview '" + dataviewName + "' does not exists");
            }

            var pg = new PSQL(dbParamsFromReqParams(params));

            var ownFilter = +params.own_filter;
            ownFilter = !!ownFilter;

            var query = (ownFilter) ? dataviewDefinition.sql.own_filter_on : dataviewDefinition.sql.own_filter_off;

            if (params.bbox) {
                var bboxFilter = new BBoxFilter({column: 'the_geom', srid: 4326}, {bbox: params.bbox});
                query = bboxFilter.sql(query);
            }

            var userQuery = params.q;

            var dataview = DataviewFactory.getDataview(query, dataviewDefinition);
            dataview.search(pg, userQuery, this);
        },
        function returnCallback(err, result) {
            return callback(err, result);
        }
    );
};

function getDataviewDefinition(mapConfig, dataviewName) {
    var dataviews = mapConfig.dataviews || {};
    return dataviews[dataviewName];
}
<|endoftext|>


<commit_before>const fs = require('fs');
const { join, resolve } = require('path');
const _mkdirp = require('mkdirp');
const _rimraf = require('rimraf');

const promisify = require('./util/promisify');
const parseJson = require('./util/parseJson');

const close = promisify(fs.close);
const mkdirp = promisify(_mkdirp);
const open = promisify(fs.open);
const read = promisify(fs.readFile);
const readdir = promisify(fs.readdir);
const readfd = promisify(fs.read);
const rename = promisify(fs.rename);
const rimraf = promisify(_rimraf);
const write = promisify(fs.writeFile);

const nextPow2 = n => Math.pow(2, Math.ceil(Math.log(n) / Math.log(2)));

const resizePow2 = (buffer, n) => {
  const tmpBuffer = Buffer.allocUnsafe(nextPow2(n));
  buffer.copy(tmpBuffer.slice(0, buffer.length));
  return tmpBuffer;
};

const MAX_CHUNK = 2 * 1024 * 1024;
const TMP_CHUNK = 0.5 * 1024 * 1024;
const MAX_CHUNK_PLUS = 2.5 * 1024 * 1024;
const LARGE_CONTENT = 64 * 1024;

let tmpBuffer = new Buffer(TMP_CHUNK);
let outBuffer = new Buffer(MAX_CHUNK_PLUS);

const _buffers = [];

const alloc = size => {
  const buffer = _buffers.pop();
  if (buffer && buffer.length >= size) {
    return buffer;
  }
  return Buffer.allocUnsafe(size);
};
const drop = buffer => _buffers.push(buffer);

class WriteOutput {
  constructor(length = 0, table = [], buffer = alloc(MAX_CHUNK_PLUS)) {
    this.length = length;
    this.table = table;
    this.buffer = buffer;
  }

  static clone(other) {
    return new WriteOutput(other.length, other.table, other.buffer);
  }

  take() {
    const output = WriteOutput.clone(this);

    this.length = 0;
    this.table = [];
    this.buffer = alloc(MAX_CHUNK_PLUS);

    return output;
  }

  add(key, content) {
    if (content !== null) {
      // Write content to a temporary buffer
      let length = tmpBuffer.utf8Write(content);
      while (length === tmpBuffer.length) {
        tmpBuffer = Buffer.allocUnsafe(tmpBuffer.length * 2);
        length = tmpBuffer.utf8Write(content);
      }

      const start = this.length;
      const end = start + length;

      // Ensure output buffer is long enough to add the new content
      if (end > this.buffer.length) {
        this.buffer = resizePow2(this.buffer, end);
      }

      // Copy temporary buffer to the end of the current output buffer
      tmpBuffer.copy(this.buffer.slice(start, end));

      this.table.push({
        name: key,
        start,
        end,
      });
      this.length = end;
    }
    else {
      this.table.push({
        name: key,
        start: -1,
        end: -1,
      });
    }
  }
}

class Semaphore {
  constructor(max) {
    this.max = max;
    this.count = 0;
    this.next = [];
  }

  async guard() {
    if (this.count < this.max) {
      this.count++;
      return new SemaphoreGuard(this);
    } else {
      return new Promise(resolve => {
        this.next.push(resolve);
      }).then(() => new SemaphoreGuard(this));
    }
  }
}

class SemaphoreGuard {
  constructor(parent) {
    this.parent = parent;
  }

  done() {
    const next = this.parent.next.shift();
    if (next) {
      next();
    } else {
      this.parent.count--;
    }
  }
}

class Append2 {
  constructor({ cacheDirPath: path, autoParse }) {
    this.path = path;
    this.autoParse = autoParse;

    this.inBuffer = new Buffer(0);
    this._buffers = [];
    this.outBuffer = new Buffer(0);
  }

  async _readFile(file) {
    const fd = await open(file, 'r+');

    let body = alloc(MAX_CHUNK_PLUS);

    await readfd(fd, body, 0, 4, null);
    const fullLength = body.readUInt32LE(0);
    if (fullLength > body.length) {
      drop(body);
      body = alloc(nextPow2(fullLength));
    }
    await readfd(fd, body, 0, fullLength, null);

    close(fd);

    const tableLength = body.readUInt32LE(0);
    const tableBody = body.utf8Slice(4, 4 + tableLength);
    const table = parseJson(tableBody);
    const content = body.slice(4 + tableLength);

    return [table, content, body];
  }

  async read() {
    const out = {};
    const size = {};
    const table = {};
    const order = {};

    await mkdirp(this.path);

    const items = await readdir(this.path);
    const logs = items.filter(item => /^log\d+$/.test(item));
    logs.sort();
    const reverseLogs = logs.reverse();

    const sema = new Semaphore(8);

    return Promise.all(
      reverseLogs.map(async (_file, index) => {
        const file = join(this.path, _file);
        const guard = await sema.guard();

        const [table, content, body] = await this._readFile(file);

        size.total += content.length;

        for (const entry of table) {
          if (
            typeof order[entry.name] === 'undefined' ||
            order[entry.name] > index
          ) {
            if (typeof order[entry.name] !== 'undefined') {
              size.used -= table[entry.name];
            }

            table[entry.name] = entry.end - entry.start;
            size.used += entry.end - entry.start;

            order[entry.name] = index;

            // Negative start positions are not set on the output. They are
            // treated as if they were deleted in a prior write. A future
            // compact will remove all instances of any old entries.
            if (entry.start >= 0) {
              await new Promise(process.nextTick);
              const data = content.utf8Slice(entry.start, entry.end);
              if (this.autoParse) {
                out[entry.name] = parseJson(data);
              } else {
                out[entry.name] = data;
              }
            }
            else {
              delete out[entry.name];
            }
          }
        }

        drop(body);
        guard.done();
      }),
    )
      .then(async () => {
        if (size.used / size.total < 0.6) {
          await this.compact(out);
        }
      })
      .then(() => out);
  }

  async _markLog() {
    const count = (await readdir(this.path)).filter(item =>
      /log\d+$/.test(item),
    ).length;
    const marker = Math.random()
      .toString(16)
      .substring(2)
      .padStart(13, '0');
    const logName = `log${count.toString().padStart(4, '0')}`;
    const file = resolve(this.path, logName);
    await write(file, marker);
    const writtenMarker = await read(file, 'utf8');
    if (marker === writtenMarker) {
      return file;
    }
    return null;
  }

  async _write(file, output) {
    // 4 bytes - full length
    // 4 bytes - length of table
    // x bytes - table
    // y bytes - content

    // Write table into a temporary buffer at position 8
    const content = JSON.stringify(output.table);
    let length = tmpBuffer.utf8Write(content, 8);
    // Make the temporary buffer longer if the space used is the same as the
    // length
    if (8 + length === tmpBuffer.length) {
      tmpBuffer = Buffer.allocUnsafe(nextPow2(8 + length));
      // Write again to see if the length is more due to the last buffer being
      // too short.
      length = tmpBuffer.utf8Write(content, 8);
    }

    // Ensure the buffer is long enough to fit the table and content.
    const end = 8 + length + output.length;
    if (end > tmpBuffer.length) {
      tmpBuffer = resizePow2(tmpBuffer, end);
    }

    // Copy the output after the table.
    output.buffer.copy(tmpBuffer.slice(8 + length, end));

    // Full length after this uint.
    tmpBuffer.writeUInt32LE(end - 4, 0);
    // Length of table after this uint.
    tmpBuffer.writeUInt32LE(length, 4);

    if (end > output.buffer.length) {
      output.buffer = alloc(nextPow2(end));
    }
    tmpBuffer.copy(output.buffer.slice(0, end));

    await write(file, output.buffer.slice(0, end));
    drop(output.buffer);
  }

  async _markAndWrite(output) {
    const file = await this._markLog();
    if (file !== null) {
      await this._write(file, output.take());
    }
  }

  // Write out a log chunk once the file reaches the maximum chunk size.
  async _writeAtMax(output) {
    while (output.length >= MAX_CHUNK) {
      await this._markAndWrite(output);
    }
  }

  // Write out a log chunk if their is any entries in the table.
  async _writeAtAny(output) {
    while (output.table.length > 0) {
      await this._markAndWrite(output);
    }
  }

  async write(ops) {
    let smallOutput = new WriteOutput();
    let largeOutput = new WriteOutput();

    const outputPromises = [];

    await mkdirp(this.path);

    for (const op of ops) {
      if (op.value !== null) {
        let content = op.value;
        if (typeof content !== 'string') {
          content = JSON.stringify(content);
        }
        if (content.length < LARGE_CONTENT) {
          smallOutput.add(op.key, content);

          await this._writeAtMax(smallOutput);
        } else {
          largeOutput.add(op.key, content);

          await this._writeAtMax(largeOutput);
        }
      }
      else {
        smallOutput.add(op.key, null);

        await this._writeAtMax(smallOutput);
      }
    }

    await this._writeAtAny(smallOutput);
    await this._writeAtAny(largeOutput);

    await Promise.all(outputPromises);
  }

  async sizes() {
    const size = {
      used: 0,
      total: 0,
    };
    const table = {};
    const order = {};

    await mkdirp(this.path);

    const items = await readdir(this.path);
    const logs = items.filter(item => /^log\d+$/.test(item));
    logs.sort();
    const reverseLogs = logs.reverse();

    const sema = new Semaphore(8);

    return Promise.all(
      reverseLogs.map(async (_file, index) => {
        const file = join(this.path, _file);
        const guard = await sema.guard();

        const [table, content, body] = await this._readFile(file);

        size.total += content.length;

        for (const entry of table) {
          if (
            typeof order[entry.name] === 'undefined' ||
            order[entry.name] > index
          ) {
            if (typeof order[entry.name] !== 'undefined') {
              size.used -= table[entry.name];
            }
            table[entry.name] = entry.end - entry.start;
            size.used += entry.end - entry.start;

            order[entry.name] = index;
          }
        }

        drop(body);
        guard.done();
      }),
    ).then(() => size);
  }

  async compact(_obj = this.read()) {
    const obj = await _obj;
    const ops = [];
    for (const key in obj) {
      ops.push({
        key,
        value: obj[key],
      });
    }
    const truePath = this.path;
    this.path += '~';
    await this.write(ops);
    this.path = truePath;
    await rimraf(this.path);
    await rename(`${this.path}~`, this.path);
  }
}

module.exports = Append2;
<commit_msg>Fix auto-compacting in SerializerAppend2<commit_after>const fs = require('fs');
const { join, resolve } = require('path');
const _mkdirp = require('mkdirp');
const _rimraf = require('rimraf');

const promisify = require('./util/promisify');
const parseJson = require('./util/parseJson');

const close = promisify(fs.close);
const mkdirp = promisify(_mkdirp);
const open = promisify(fs.open);
const read = promisify(fs.readFile);
const readdir = promisify(fs.readdir);
const readfd = promisify(fs.read);
const rename = promisify(fs.rename);
const rimraf = promisify(_rimraf);
const write = promisify(fs.writeFile);

const nextPow2 = n => Math.pow(2, Math.ceil(Math.log(n) / Math.log(2)));

const resizePow2 = (buffer, n) => {
  const tmpBuffer = Buffer.allocUnsafe(nextPow2(n));
  buffer.copy(tmpBuffer.slice(0, buffer.length));
  return tmpBuffer;
};

const MAX_CHUNK = 2 * 1024 * 1024;
const TMP_CHUNK = 0.5 * 1024 * 1024;
const MAX_CHUNK_PLUS = 2.5 * 1024 * 1024;
const LARGE_CONTENT = 64 * 1024;

let tmpBuffer = new Buffer(TMP_CHUNK);
let outBuffer = new Buffer(MAX_CHUNK_PLUS);

const _buffers = [];

const alloc = size => {
  const buffer = _buffers.pop();
  if (buffer && buffer.length >= size) {
    return buffer;
  }
  return Buffer.allocUnsafe(size);
};
const drop = buffer => _buffers.push(buffer);

class WriteOutput {
  constructor(length = 0, table = [], buffer = alloc(MAX_CHUNK_PLUS)) {
    this.length = length;
    this.table = table;
    this.buffer = buffer;
  }

  static clone(other) {
    return new WriteOutput(other.length, other.table, other.buffer);
  }

  take() {
    const output = WriteOutput.clone(this);

    this.length = 0;
    this.table = [];
    this.buffer = alloc(MAX_CHUNK_PLUS);

    return output;
  }

  add(key, content) {
    if (content !== null) {
      // Write content to a temporary buffer
      let length = tmpBuffer.utf8Write(content);
      while (length === tmpBuffer.length) {
        tmpBuffer = Buffer.allocUnsafe(tmpBuffer.length * 2);
        length = tmpBuffer.utf8Write(content);
      }

      const start = this.length;
      const end = start + length;

      // Ensure output buffer is long enough to add the new content
      if (end > this.buffer.length) {
        this.buffer = resizePow2(this.buffer, end);
      }

      // Copy temporary buffer to the end of the current output buffer
      tmpBuffer.copy(this.buffer.slice(start, end));

      this.table.push({
        name: key,
        start,
        end,
      });
      this.length = end;
    }
    else {
      this.table.push({
        name: key,
        start: -1,
        end: -1,
      });
    }
  }
}

class Semaphore {
  constructor(max) {
    this.max = max;
    this.count = 0;
    this.next = [];
  }

  async guard() {
    if (this.count < this.max) {
      this.count++;
      return new SemaphoreGuard(this);
    } else {
      return new Promise(resolve => {
        this.next.push(resolve);
      }).then(() => new SemaphoreGuard(this));
    }
  }
}

class SemaphoreGuard {
  constructor(parent) {
    this.parent = parent;
  }

  done() {
    const next = this.parent.next.shift();
    if (next) {
      next();
    } else {
      this.parent.count--;
    }
  }
}

class Append2 {
  constructor({ cacheDirPath: path, autoParse }) {
    this.path = path;
    this.autoParse = autoParse;

    this.inBuffer = new Buffer(0);
    this._buffers = [];
    this.outBuffer = new Buffer(0);
  }

  async _readFile(file) {
    const fd = await open(file, 'r+');

    let body = alloc(MAX_CHUNK_PLUS);

    await readfd(fd, body, 0, 4, null);
    const fullLength = body.readUInt32LE(0);
    if (fullLength > body.length) {
      drop(body);
      body = alloc(nextPow2(fullLength));
    }
    await readfd(fd, body, 0, fullLength, null);

    close(fd);

    const tableLength = body.readUInt32LE(0);
    const tableBody = body.utf8Slice(4, 4 + tableLength);
    const table = parseJson(tableBody);
    const content = body.slice(4 + tableLength);

    return [table, content, body];
  }

  async read() {
    const out = {};
    const size = {used: 0, total: 0};
    const table = {};
    const order = {};

    await mkdirp(this.path);

    const items = await readdir(this.path);
    const logs = items.filter(item => /^log\d+$/.test(item));
    logs.sort();
    const reverseLogs = logs.reverse();

    const sema = new Semaphore(8);

    return Promise.all(
      reverseLogs.map(async (_file, index) => {
        const file = join(this.path, _file);
        const guard = await sema.guard();

        const [table, content, body] = await this._readFile(file);

        const keys = Object.keys(table);
        if (keys.length > 0) {
          size.total += table[keys.length - 1].end;
        }

        for (const entry of table) {
          if (
            typeof order[entry.name] === 'undefined' ||
            order[entry.name] > index
          ) {
            if (typeof order[entry.name] !== 'undefined') {
              size.used -= table[entry.name];
            }

            table[entry.name] = entry.end - entry.start;
            size.used += entry.end - entry.start;

            order[entry.name] = index;

            // Negative start positions are not set on the output. They are
            // treated as if they were deleted in a prior write. A future
            // compact will remove all instances of any old entries.
            if (entry.start >= 0) {
              await new Promise(process.nextTick);
              const data = content.utf8Slice(entry.start, entry.end);
              if (this.autoParse) {
                out[entry.name] = parseJson(data);
              } else {
                out[entry.name] = data;
              }
            }
            else {
              delete out[entry.name];
            }
          }
        }

        drop(body);
        guard.done();
      }),
    )
      .then(async () => {
        if (size.used / size.total < 0.6) {
          await this.compact(out);
        }
      })
      .then(() => out);
  }

  async _markLog() {
    const count = (await readdir(this.path)).filter(item =>
      /log\d+$/.test(item),
    ).length;
    const marker = Math.random()
      .toString(16)
      .substring(2)
      .padStart(13, '0');
    const logName = `log${count.toString().padStart(4, '0')}`;
    const file = resolve(this.path, logName);
    await write(file, marker);
    const writtenMarker = await read(file, 'utf8');
    if (marker === writtenMarker) {
      return file;
    }
    return null;
  }

  async _write(file, output) {
    // 4 bytes - full length
    // 4 bytes - length of table
    // x bytes - table
    // y bytes - content

    // Write table into a temporary buffer at position 8
    const content = JSON.stringify(output.table);
    let length = tmpBuffer.utf8Write(content, 8);
    // Make the temporary buffer longer if the space used is the same as the
    // length
    if (8 + length === tmpBuffer.length) {
      tmpBuffer = Buffer.allocUnsafe(nextPow2(8 + length));
      // Write again to see if the length is more due to the last buffer being
      // too short.
      length = tmpBuffer.utf8Write(content, 8);
    }

    // Ensure the buffer is long enough to fit the table and content.
    const end = 8 + length + output.length;
    if (end > tmpBuffer.length) {
      tmpBuffer = resizePow2(tmpBuffer, end);
    }

    // Copy the output after the table.
    output.buffer.copy(tmpBuffer.slice(8 + length, end));

    // Full length after this uint.
    tmpBuffer.writeUInt32LE(end - 4, 0);
    // Length of table after this uint.
    tmpBuffer.writeUInt32LE(length, 4);

    if (end > output.buffer.length) {
      output.buffer = alloc(nextPow2(end));
    }
    tmpBuffer.copy(output.buffer.slice(0, end));

    await write(file, output.buffer.slice(0, end));
    drop(output.buffer);
  }

  async _markAndWrite(output) {
    const file = await this._markLog();
    if (file !== null) {
      await this._write(file, output.take());
    }
  }

  // Write out a log chunk once the file reaches the maximum chunk size.
  async _writeAtMax(output) {
    while (output.length >= MAX_CHUNK) {
      await this._markAndWrite(output);
    }
  }

  // Write out a log chunk if their is any entries in the table.
  async _writeAtAny(output) {
    while (output.table.length > 0) {
      await this._markAndWrite(output);
    }
  }

  async write(ops) {
    let smallOutput = new WriteOutput();
    let largeOutput = new WriteOutput();

    const outputPromises = [];

    await mkdirp(this.path);

    for (const op of ops) {
      if (op.value !== null) {
        let content = op.value;
        if (typeof content !== 'string') {
          content = JSON.stringify(content);
        }
        if (content.length < LARGE_CONTENT) {
          smallOutput.add(op.key, content);

          await this._writeAtMax(smallOutput);
        } else {
          largeOutput.add(op.key, content);

          await this._writeAtMax(largeOutput);
        }
      }
      else {
        smallOutput.add(op.key, null);

        await this._writeAtMax(smallOutput);
      }
    }

    await this._writeAtAny(smallOutput);
    await this._writeAtAny(largeOutput);

    await Promise.all(outputPromises);
  }

  async sizes() {
    const size = {
      used: 0,
      total: 0,
    };
    const table = {};
    const order = {};

    await mkdirp(this.path);

    const items = await readdir(this.path);
    const logs = items.filter(item => /^log\d+$/.test(item));
    logs.sort();
    const reverseLogs = logs.reverse();

    const sema = new Semaphore(8);

    return Promise.all(
      reverseLogs.map(async (_file, index) => {
        const file = join(this.path, _file);
        const guard = await sema.guard();

        const [table, content, body] = await this._readFile(file);

        size.total += content.length;

        for (const entry of table) {
          if (
            typeof order[entry.name] === 'undefined' ||
            order[entry.name] > index
          ) {
            if (typeof order[entry.name] !== 'undefined') {
              size.used -= table[entry.name];
            }
            table[entry.name] = entry.end - entry.start;
            size.used += entry.end - entry.start;

            order[entry.name] = index;
          }
        }

        drop(body);
        guard.done();
      }),
    ).then(() => size);
  }

  async compact(_obj = this.read()) {
    const obj = await _obj;
    const ops = [];
    for (const key in obj) {
      ops.push({
        key,
        value: obj[key],
      });
    }
    const truePath = this.path;
    this.path += '~';
    await this.write(ops);
    this.path = truePath;
    await rimraf(this.path);
    await rename(`${this.path}~`, this.path);
  }
}

module.exports = Append2;
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013 salesforce.com, inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
({
    //@userStory a07T0000001isvq
    /**
     * Verify assigning an uninitialized local variable to outputNumber's value attribute.
     */
    //W-952720
    testUninitializedValue:{
        test:function(cmp){
            var testOutputNumberCmp = cmp.find('uninitializedVariable');
            aura.test.assertNotNull(testOutputNumberCmp);
            aura.test.assertEquals('', $A.test.getText(testOutputNumberCmp.find('span').getElement()), "Should have displayed a blank value.");
        }
    }
})
<commit_msg>Merging origin/mike/master-pending-removeAnnotationFromThatBreaksIE into master<commit_after>/*
 * Copyright (C) 2013 salesforce.com, inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
({
    /**
     * Verify assigning an uninitialized local variable to outputNumber's value attribute.
     */
    testUninitializedValue:{
        test:function(cmp){
            var testOutputNumberCmp = cmp.find('uninitializedVariable');
            aura.test.assertNotNull(testOutputNumberCmp);
            aura.test.assertEquals('', $A.test.getText(testOutputNumberCmp.find('span').getElement()), "Should have displayed a blank value.");
        }
    }
})
<|endoftext|>


<commit_before>/*
 * Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import { useCallback } from 'react';

/**
 * Internal dependencies
 */
import { PAGE_WIDTH, PAGE_HEIGHT } from '../../constants';
import { createNewElement, getDefinitionForType } from '../../elements';
import { dataPixels } from '../../units';
import { useLocalMedia, useStory } from '../../app';
import { DEFAULT_MASK } from '../../masks';
import useMedia3pApi from '../../app/media/media3p/api/useMedia3pApi';
import getInsertedElementSize from '../../utils/getInsertedElementSize';
import useFocusCanvas from './useFocusCanvas';

function useInsertElement() {
  const { addElement } = useStory((state) => ({
    addElement: state.actions.addElement,
  }));
  const { uploadVideoPoster } = useLocalMedia((state) => ({
    uploadVideoPoster: state.actions.uploadVideoPoster,
  }));
  const {
    actions: { registerUsage },
  } = useMedia3pApi();

  /**
   * @param {Object} resource The resource to verify/update.
   * @param {string} elementId The element's id to be updated once resource
   * is complete.
   */
  const backfillResource = useCallback(
    (resource) => {
      const { type, src, id, posterId } = resource;

      // Generate video poster if one not set.
      if (type === 'video' && id && !posterId) {
        uploadVideoPoster(id, src);
      }
    },
    [uploadVideoPoster]
  );

  /**
   * If the resource has a register usage url then the fact that it's been
   * inserted needs to be registered as per API provider policies.
   *
   * @param {Object} resource The resource to attempt to register usage.
   */
  const handleRegisterUsage = useCallback(
    (resource) => {
      if (!resource.local && resource?.attribution?.registerUsageUrl) {
        registerUsage({
          registerUsageUrl: resource.attribution.registerUsageUrl,
        });
      }
    },
    [registerUsage]
  );

  const focusCanvas = useFocusCanvas();

  /**
   * @param {string} type The element's type.
   * @param {Object} props The element's initial properties.
   */
  const insertElement = useCallback(
    (type, props) => {
      if (type == 'gif') {
        // Treat GIFs just like images
        type = 'image';
        props.resource.type = 'image';
      }

      const element = createElementForCanvas(type, props);
      const { id, resource } = element;
      addElement({ element });
      if (resource) {
        backfillResource(resource);
        handleRegisterUsage(resource);
      }
      // Auto-play on insert.
      if (type === 'video') {
        setTimeout(() => {
          const videoEl = document.getElementById(`video-${id}`);
          if (videoEl) {
            videoEl.play().catch(() => {});
          }
        });
      }
      focusCanvas();
      return element;
    },
    [addElement, backfillResource, focusCanvas, handleRegisterUsage]
  );

  return insertElement;
}

/**
 * @param {string} type Element type.
 * @param {!Object} props The element's properties.
 * @param {number} props.width The element's width.
 * @param {number} props.height The element's height.
 * @param {?Object} props.mask The element's mask.
 * @return {Object} The element properties.
 */
function getElementProperties(
  type,
  {
    resource,
    x,
    y,
    width,
    height,
    mask,
    rotationAngle = 0,
    scale = 100,
    focalX = 50,
    focalY = 50,
    ...rest
  }
) {
  const { isMaskable } = getDefinitionForType(type);

  const attrs = { type, ...rest };

  // Width and height defaults. Width takes precedence.
  const ratio =
    resource && isNum(resource.width) && isNum(resource.height)
      ? resource.width / resource.height
      : 1;
  const size = getInsertedElementSize(
    type,
    width,
    height,
    attrs,
    ratio,
    resource
  );
  width = size.width;
  height = size.height;

  // X and y defaults: in the top quarter of the page.
  if (!isNum(x)) {
    x = (PAGE_WIDTH / 4) * Math.random();
  }
  if (!isNum(y)) {
    y = (PAGE_HEIGHT / 4) * Math.random();
  }
  x = dataPixels(Math.min(x, PAGE_WIDTH - width));
  y = dataPixels(Math.min(y, PAGE_HEIGHT - height));

  return {
    ...attrs,
    ...(Boolean(resource) && {
      resource: {
        ...resource,
        width,
        height,
        alt: resource.alt || '',
      },
    }),
    x,
    y,
    width,
    height,
    rotationAngle,
    scale,
    focalX,
    focalY,
    ...(isMaskable
      ? {
          mask: mask || DEFAULT_MASK,
        }
      : {}),
  };
}

/**
 * @param {string} type Element type.
 * @param {!Object} props The element's properties.
 * @return {Object} The new element.
 */
function createElementForCanvas(type, props) {
  return createNewElement(type, getElementProperties(type, props));
}

/**
 * @param {?number|undefined} value The value.
 * @return {boolean} Whether the value has been set.
 */
function isNum(value) {
  return typeof value === 'number';
}

export default useInsertElement;
export { getElementProperties };
<commit_msg>Fixed new element position (#4535)<commit_after>/*
 * Copyright 2020 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * External dependencies
 */
import { useCallback } from 'react';

/**
 * Internal dependencies
 */
import { PAGE_WIDTH, PAGE_HEIGHT } from '../../constants';
import { createNewElement, getDefinitionForType } from '../../elements';
import { dataPixels } from '../../units';
import { useLocalMedia, useStory } from '../../app';
import { DEFAULT_MASK } from '../../masks';
import useMedia3pApi from '../../app/media/media3p/api/useMedia3pApi';
import getInsertedElementSize from '../../utils/getInsertedElementSize';
import useFocusCanvas from './useFocusCanvas';

function useInsertElement() {
  const { addElement } = useStory((state) => ({
    addElement: state.actions.addElement,
  }));
  const { uploadVideoPoster } = useLocalMedia((state) => ({
    uploadVideoPoster: state.actions.uploadVideoPoster,
  }));
  const {
    actions: { registerUsage },
  } = useMedia3pApi();

  /**
   * @param {Object} resource The resource to verify/update.
   * @param {string} elementId The element's id to be updated once resource
   * is complete.
   */
  const backfillResource = useCallback(
    (resource) => {
      const { type, src, id, posterId } = resource;

      // Generate video poster if one not set.
      if (type === 'video' && id && !posterId) {
        uploadVideoPoster(id, src);
      }
    },
    [uploadVideoPoster]
  );

  /**
   * If the resource has a register usage url then the fact that it's been
   * inserted needs to be registered as per API provider policies.
   *
   * @param {Object} resource The resource to attempt to register usage.
   */
  const handleRegisterUsage = useCallback(
    (resource) => {
      if (!resource.local && resource?.attribution?.registerUsageUrl) {
        registerUsage({
          registerUsageUrl: resource.attribution.registerUsageUrl,
        });
      }
    },
    [registerUsage]
  );

  const focusCanvas = useFocusCanvas();

  /**
   * @param {string} type The element's type.
   * @param {Object} props The element's initial properties.
   */
  const insertElement = useCallback(
    (type, props) => {
      if (type == 'gif') {
        // Treat GIFs just like images
        type = 'image';
        props.resource.type = 'image';
      }

      const element = createElementForCanvas(type, props);
      const { id, resource } = element;
      addElement({ element });
      if (resource) {
        backfillResource(resource);
        handleRegisterUsage(resource);
      }
      // Auto-play on insert.
      if (type === 'video') {
        setTimeout(() => {
          const videoEl = document.getElementById(`video-${id}`);
          if (videoEl) {
            videoEl.play().catch(() => {});
          }
        });
      }
      focusCanvas();
      return element;
    },
    [addElement, backfillResource, focusCanvas, handleRegisterUsage]
  );

  return insertElement;
}

/**
 * @param {string} type Element type.
 * @param {!Object} props The element's properties.
 * @param {number} props.width The element's width.
 * @param {number} props.height The element's height.
 * @param {?Object} props.mask The element's mask.
 * @return {Object} The element properties.
 */
function getElementProperties(
  type,
  {
    resource,
    x,
    y,
    width,
    height,
    mask,
    rotationAngle = 0,
    scale = 100,
    focalX = 50,
    focalY = 50,
    ...rest
  }
) {
  const { isMaskable } = getDefinitionForType(type);

  const attrs = { type, ...rest };

  // Width and height defaults. Width takes precedence.
  const ratio =
    resource && isNum(resource.width) && isNum(resource.height)
      ? resource.width / resource.height
      : 1;
  const size = getInsertedElementSize(
    type,
    width,
    height,
    attrs,
    ratio,
    resource
  );
  width = size.width;
  height = size.height;

  // X and y defaults: in the top corner of the page.
  if (!isNum(x)) {
    x = 48;
  }
  if (!isNum(y)) {
    y = 0;
  }
  x = dataPixels(Math.min(x, PAGE_WIDTH - width));
  y = dataPixels(Math.min(y, PAGE_HEIGHT - height));

  return {
    ...attrs,
    ...(Boolean(resource) && {
      resource: {
        ...resource,
        width,
        height,
        alt: resource.alt || '',
      },
    }),
    x,
    y,
    width,
    height,
    rotationAngle,
    scale,
    focalX,
    focalY,
    ...(isMaskable
      ? {
          mask: mask || DEFAULT_MASK,
        }
      : {}),
  };
}

/**
 * @param {string} type Element type.
 * @param {!Object} props The element's properties.
 * @return {Object} The new element.
 */
function createElementForCanvas(type, props) {
  return createNewElement(type, getElementProperties(type, props));
}

/**
 * @param {?number|undefined} value The value.
 * @return {boolean} Whether the value has been set.
 */
function isNum(value) {
  return typeof value === 'number';
}

export default useInsertElement;
export { getElementProperties };
<|endoftext|>


<commit_before>/*global require, module*/
var InvalidArgumentError = require("../error/invalid-argument-error"),
    easyGid = require("easy-guid"),
    validator = require('validator');

var TimeEntry = function (issueId, issueName, projectName, taskUrl) {
    "use strict";
    if (!validator.isInt(issueId)) {
        throw new InvalidArgumentError("Parameter issueId must be an integer.");
    }

    if (typeof issueName !== "string") {
        throw new InvalidArgumentError("Parameter issueName must be a string.");
    }

    if (typeof projectName !== "string") {
        throw new InvalidArgumentError("Parameter projectName must be a string.");
    }

    if (!validator.isURL(taskUrl)) {
        throw new InvalidArgumentError("Parameter taskUrl must be a URL.");
    }

    this.id = easyGid.new();
    this.issueId = issueId;
    this.issueName = issueName;
    this.projectName = projectName;
    this.taskUrl = taskUrl;
    this.spentOn = null;
    this.hours = null;
    this.activityId = null;
    this.comments = null;
    this.updated = false;
};

TimeEntry.prototype = (function () {
    "use strict";
    var dateFormatPattern = /^\d{4}-\d{2}-\d{2}$/,
        setSpentOn = function (spentOn) {
            if (typeof spentOn !== "string" || !spentOn.match(dateFormatPattern)) {
                throw new InvalidArgumentError("Parameter spentOn must be a string with format {YYYY-MM-DD}.");
            }

            this.spentOn = spentOn;
            return this;
        },
        isUpdated  = function () {
            return this.activityId !== null && this.hours !== null;
        },
        setHours = function (hours) {
            if (!(validator.isInt(hours, { min: 0 }) || validator.isFloat(hours, { min: 0.00 }))) {
                this.updated = false;
                return this;
            }

            this.hours = parseFloat(hours);
            this.updated = isUpdated.call(this);
            return this;
        },
        setActivityId = function (activityId) {
            if (!validator.isInt(activityId)) {
                this.updated = false;
                return this;
            }

            this.activityId = parseInt(activityId, 10);
            this.updated = isUpdated.call(this);
            return this;
        },
        setComments = function (comments) {
            this.comments = comments;
            return this;
        },
        clearTimeEntry = function () {
            this.spentOn = null;
            this.hours = null;
            this.activityId = null;
            this.comments = null;
            this.updated = false;
        },
        buildPostEntry = function () {
            return {
                time_entry: {
                    issue_id: this.issueId,
                    spent_on: this.spentOn,
                    hours: this.hours,
                    activity_id: this.activityId,
                    comments: this.comments
                }
            };
        };
    return {
        setHours: setHours,
        setActivityId: setActivityId,
        setComments: setComments,
        buildPostEntry: buildPostEntry,
        setSpentOn: setSpentOn,
        clearTimeEntry: clearTimeEntry
    };
}());

TimeEntry.createInstance = function (issueId, issueName, projectName, taskUrl) {
    "use strict";
    return new TimeEntry(issueId, issueName, projectName, taskUrl);
};

module.exports = TimeEntry;<commit_msg>Post time entry defect fix<commit_after>/*global require, module*/
var InvalidArgumentError = require("../error/invalid-argument-error"),
    easyGid = require("easy-guid"),
    validator = require('validator');

var TimeEntry = function (issueId, issueName, projectName, taskUrl) {
    "use strict";
    if (!validator.isInt(issueId)) {
        throw new InvalidArgumentError("Parameter issueId must be an integer.");
    }

    if (typeof issueName !== "string") {
        throw new InvalidArgumentError("Parameter issueName must be a string.");
    }

    if (typeof projectName !== "string") {
        throw new InvalidArgumentError("Parameter projectName must be a string.");
    }

    if (!validator.isURL(taskUrl)) {
        throw new InvalidArgumentError("Parameter taskUrl must be a URL.");
    }

    this.id = easyGid.new();
    this.issueId = issueId;
    this.issueName = issueName;
    this.projectName = projectName;
    this.taskUrl = taskUrl;
    this.spentOn = null;
    this.hours = null;
    this.activityId = null;
    this.comments = null;
    this.updated = false;
};

TimeEntry.prototype = (function () {
    "use strict";
    var dateFormat = "YYYY-MM-DD",
        setSpentOn = function (spentOn) {
            if (!spentOn || !spentOn._isAMomentObject) {
                throw new InvalidArgumentError("Parameter spentOn must be a moment object.");
            }

            this.spentOn = spentOn;
            return this;
        },
        isUpdated  = function () {
            return this.activityId !== null && this.hours !== null;
        },
        setHours = function (hours) {
            if (!(validator.isInt(hours, { min: 0 }) || validator.isFloat(hours, { min: 0.00 }))) {
                this.updated = false;
                return this;
            }

            this.hours = parseFloat(hours);
            this.updated = isUpdated.call(this);
            return this;
        },
        setActivityId = function (activityId) {
            if (!validator.isInt(activityId)) {
                this.updated = false;
                return this;
            }

            this.activityId = parseInt(activityId, 10);
            this.updated = isUpdated.call(this);
            return this;
        },
        setComments = function (comments) {
            this.comments = comments;
            return this;
        },
        clearTimeEntry = function () {
            this.spentOn = null;
            this.hours = null;
            this.activityId = null;
            this.comments = null;
            this.updated = false;
        },
        buildPostEntry = function () {
            return {
                time_entry: {
                    issue_id: this.issueId,
                    spent_on: this.spentOn.toString(dateFormat),
                    hours: this.hours,
                    activity_id: this.activityId,
                    comments: this.comments
                }
            };
        };
    return {
        setHours: setHours,
        setActivityId: setActivityId,
        setComments: setComments,
        buildPostEntry: buildPostEntry,
        setSpentOn: setSpentOn,
        clearTimeEntry: clearTimeEntry
    };
}());

TimeEntry.createInstance = function (issueId, issueName, projectName, taskUrl) {
    "use strict";
    return new TimeEntry(issueId, issueName, projectName, taskUrl);
};

module.exports = TimeEntry;<|endoftext|>


<commit_before>'use strict';

/**
 * Upload.js controller
 *
 */

const _ = require('lodash');
const apiUploadController = require('./upload/api');
const adminUploadController = require('./upload/admin');

const resolveController = ctx => {
  const {
    state: { isAuthenticatedAdmin },
  } = ctx;

  return isAuthenticatedAdmin ? adminUploadController : apiUploadController;
};

const resolveControllerMethod = method => ctx => {
  const controller = resolveController(ctx);
  const callbackFn = controller[method];

  if (!_.isFunction(callbackFn)) {
    return ctx.notFound();
  }

  return callbackFn(ctx);
};

module.exports = {
  find: resolveControllerMethod('find'),
  findOne: resolveControllerMethod('findOne'),
  count: resolveControllerMethod('count'),
  destroy: resolveControllerMethod('destroy'),
  updateSettings: resolveControllerMethod('updateSettings'),
  getSettings: resolveControllerMethod('getSettings'),

  async upload(ctx) {
    const isUploadDisabled = _.get(strapi.plugins, 'upload.config.enabled', true) === false;

    if (isUploadDisabled) {
      throw strapi.errors.badRequest(null, {
        errors: [{ id: 'Upload.status.disabled', message: 'File upload is disabled' }],
      });
    }

    const {
      query: { id },
      request: { files: { files } = {} },
    } = ctx;
    const controller = resolveController(ctx);

    if (id && (_.isEmpty(files) || files.size === 0)) {
      return controller.updateFileInfo(ctx);
    }

    if (_.isEmpty(files) || files.size === 0) {
      throw strapi.errors.badRequest(null, {
        errors: [{ id: 'Upload.status.empty', message: 'Files are empty' }],
      });
    }

    await (id ? controller.replaceFile : controller.uploadFiles)(ctx);
  },

  async search(ctx) {
    const { id } = ctx.params;

    ctx.body = await strapi.query('file', 'upload').custom(searchQueries)({
      id,
    });
  },
};

const searchQueries = {
  bookshelf({ model }) {
    return ({ id }) => {
      return model
        .query(qb => {
          qb.whereRaw('LOWER(hash) LIKE ?', [`%${id}%`]).orWhereRaw('LOWER(name) LIKE ?', [
            `%${id}%`,
          ]);
        })
        .fetchAll()
        .then(results => results.toJSON());
    };
  },
  mongoose({ model }) {
    return ({ id }) => {
      const re = new RegExp(id, 'i');

      return model
        .find({
          $or: [{ hash: re }, { name: re }],
        })
        .lean();
    };
  },
};
<commit_msg>Sanitize /upload/search/:q route (#8658)<commit_after>'use strict';

/**
 * Upload.js controller
 *
 */

const _ = require('lodash');
const apiUploadController = require('./upload/api');
const adminUploadController = require('./upload/admin');
const { sanitizeEntity } = require('strapi-utils');

const resolveController = ctx => {
  const {
    state: { isAuthenticatedAdmin },
  } = ctx;

  return isAuthenticatedAdmin ? adminUploadController : apiUploadController;
};

const resolveControllerMethod = method => ctx => {
  const controller = resolveController(ctx);
  const callbackFn = controller[method];

  if (!_.isFunction(callbackFn)) {
    return ctx.notFound();
  }

  return callbackFn(ctx);
};

module.exports = {
  find: resolveControllerMethod('find'),
  findOne: resolveControllerMethod('findOne'),
  count: resolveControllerMethod('count'),
  destroy: resolveControllerMethod('destroy'),
  updateSettings: resolveControllerMethod('updateSettings'),
  getSettings: resolveControllerMethod('getSettings'),

  async upload(ctx) {
    const isUploadDisabled = _.get(strapi.plugins, 'upload.config.enabled', true) === false;

    if (isUploadDisabled) {
      throw strapi.errors.badRequest(null, {
        errors: [{ id: 'Upload.status.disabled', message: 'File upload is disabled' }],
      });
    }

    const {
      query: { id },
      request: { files: { files } = {} },
    } = ctx;
    const controller = resolveController(ctx);

    if (id && (_.isEmpty(files) || files.size === 0)) {
      return controller.updateFileInfo(ctx);
    }

    if (_.isEmpty(files) || files.size === 0) {
      throw strapi.errors.badRequest(null, {
        errors: [{ id: 'Upload.status.empty', message: 'Files are empty' }],
      });
    }

    await (id ? controller.replaceFile : controller.uploadFiles)(ctx);
  },

  async search(ctx) {
    const { id } = ctx.params;
    const model = strapi.getModel('file', 'upload');
    const entries = await strapi.query('file', 'upload').custom(searchQueries)({
      id,
    });

    ctx.body = sanitizeEntity(entries, { model });
  },
};

const searchQueries = {
  bookshelf({ model }) {
    return ({ id }) => {
      return model
        .query(qb => {
          qb.whereRaw('LOWER(hash) LIKE ?', [`%${id}%`]).orWhereRaw('LOWER(name) LIKE ?', [
            `%${id}%`,
          ]);
        })
        .fetchAll()
        .then(results => results.toJSON());
    };
  },
  mongoose({ model }) {
    return ({ id }) => {
      const re = new RegExp(id, 'i');

      return model
        .find({
          $or: [{ hash: re }, { name: re }],
        })
        .lean();
    };
  },
};
<|endoftext|>


<commit_before>'use strict';

var replace = String.prototype.replace
  , repeat  = require('./prototype/repeat');

module.exports = function (token) {
	if (!token) {
		token = '\t';
	}
	return function (nest, nostart) {
		var ltoken, r;
		if (nest == null) {
			nest = 1;
		} else if (!nest) {
			return String(this);
		}
		ltoken = repeat.call(token, nest);
		return (nostart ? '' : ltoken) +
			replace.call(this, /\n([^\n]+)/g, '\n' + ltoken + '$1');
	};
};
<commit_msg>Better variable names<commit_after>'use strict';

var replace = String.prototype.replace
  , repeat  = require('./prototype/repeat');

module.exports = function (indentString) {
	if (!indentString) {
		indentString = '\t';
	}
	return function (nest, nostart) {
		var ltoken, r;
		if (nest == null) {
			nest = 1;
		} else if (!nest) {
			return String(this);
		}
		ltoken = repeat.call(indentString, nest);
		return (nostart ? '' : ltoken) +
			replace.call(this, /\n([^\n]+)/g, '\n' + ltoken + '$1');
	};
};
<|endoftext|>


<commit_before>'use strict';

const log = require('intel').getLogger('browsertime.chrome');
const { promisify } = require('util');
const { Type } = require('selenium-webdriver').logging;
const cpuMetrics = require('../longTaskMetrics');
const fs = require('fs');
const path = require('path');
const parseCpuTrace = require('../parseCpuTrace');
const speedline = require('../speedline');
const har = require('../har');
const harBuilder = require('../../support/har');
const webdriver = require('selenium-webdriver');
const traceCategoriesParser = require('../traceCategoriesParser');
const pathToFolder = require('../../support/pathToFolder');
const ChromeDevtoolsProtocol = require('../chromeDevtoolsProtocol');
const { Android } = require('../../android');
const unlink = promisify(fs.unlink);

class Chromium {
  constructor(storageManager, options) {
    this.options = options;
    this.chrome = options.chrome || {};
    this.skipHar = options.skipHar || false;
    this.logPerfEntries = !!this.chrome.collectPerfLog;
    this.collectTracingEvents =
      this.chrome.traceCategories || this.chrome.timeline;
    this.baseDir = storageManager.directory;
    this.storageManager = storageManager;
    // We keep track of all alias and URLs
    this.aliasAndUrl = {};
    this.isTracing = false;
    // Keep the HAR file for all runs
    this.hars = [];
    this.androidTmpDir = '/data/local/tmp/';
  }

  /**
   * Before the brrowser is started.
   */
  async beforeBrowserStart() {
    this.events = [];
    if (this.options.android) {
      this.android = new Android(this.options);
      if (this.options.androidPower) {
        await this.android.startPowerTesting();
      }

      if (this.chrome.collectNetLog) {
        // The file needs to exist for netlog to work
        await this.android._runCommand(
          `touch ${this.androidTmpDir}chromeNetlog.json`
        );
      }
    }
  }

  /**
   * The browser is up and running, now its time to start to
   * configure what you need.
   */
  async afterBrowserStart() {
    // We create the cdp as early as possible so it can be used in scripts
    // https://bugs.chromium.org/p/chromium/issues/detail?id=824626
    // https://github.com/cyrus-and/chrome-remote-interface/issues/332
    if (this.options.android) {
      await this.android.addDevtoolsFw();
    }

    this.cdpClient = new ChromeDevtoolsProtocol(this.options);
    return this.cdpClient.setup();
  }

  /**
   * Before the first iteration of your tests start.
   */
  async beforeStartIteration(runner) {
    // Here we setup everything we need using CDP
    if (this.options.injectJs) {
      await this.cdpClient.injectJavaScript(this.options.injectJs);
    }

    if (this.options.block) {
      await this.cdpClient.blockUrls(this.options.block);
    }

    if (this.options.cacheClearRaw) {
      await this.cdpClient.clearBrowserCache();
      await this.cdpClient.clearBrowserCookies();
    }

    if (this.options.clearCacheKeepCookies) {
      await this.cdpClient.clearBrowserCache();
    }

    if (this.options.basicAuth) {
      await this.cdpClient.setBasicAuth(this.options.basicAuth);
    }

    if (this.options.requestheader) {
      await this.cdpClient.setRequestHeaders(this.options.requestheader);
    }

    if (this.chrome.CPUThrottlingRate && this.chrome.CPUThrottlingRate > 1) {
      await this.cdpClient.setupCPUThrottling(this.chrome.CPUThrottlingRate);
    }

    await this.cdpClient.setupLongTask();

    // Make sure we clear the console log
    // Hopefully one time is enough?
    return runner.getLogs(Type.BROWSER);

    // remove irrelevant entries from performance log
    // and since Chromedriver 2.29 there's a bug
    // https://bugs.chromium.org/p/chromedriver/issues/detail?id=1811
    // that can be fixed by emptying the logs twice :|
    // await runner.getLogs(Type.PERFORMANCE);
    // await runner.getLogs(Type.PERFORMANCE);
  }

  /**
   * Before each URL/test runs.
   */
  async beforeEachURL(runner, url) {
    // remove irrelevant entries from performance log
    // and since Chromedriver 2.29 there's a bug
    // https://bugs.chromium.org/p/chromedriver/issues/detail?id=1811s
    await runner.getLogs(Type.PERFORMANCE);
    await runner.getLogs(Type.PERFORMANCE);

    if (this.options.cookie && url) {
      await this.cdpClient.setCookies(url, this.options.cookie);
    } else if (this.options.cookie) {
      log.info('Could not set cookie because the URL is unknown');
    }

    if (this.android && this.options.androidPower) {
      await this.android.resetPowerUsage();
    }

    if (this.collectTracingEvents && !this.isTracing) {
      this.isTracing = true;
      return this.cdpClient.startTrace();
    }
  }

  /**
   * When the page has finished loading, this functions runs (before
   * collecting metrics etc). This is the place to get you HAR file,
   * stop trace logging, stop measuring etc.
   *
   */
  async afterPageCompleteCheck(runner, index, url, alias) {
    const result = { url, alias };
    if (this.collectTracingEvents && this.isTracing) {
      // We are ready and can stop collecting events
      this.isTracing = false;
      this.events = await this.cdpClient.stopTrace();
    }

    if (this.android && this.options.androidPower) {
      result.power = await this.android.measurePowerUsage(
        this.chrome.android.package
      );
    }

    if (this.options.verbose >= 2 || this.chrome.enableChromeDriverLog) {
      return this.storageManager.gzip(
        `${this.baseDir}/chromedriver.log`,
        `${this.baseDir}/chromedriver-${index}.log.gz`,
        true
      );
    }

    if (this.chrome.collectNetLog && !this.chrome.android) {
      await this.storageManager.createSubDataDir(
        path.join(pathToFolder(result.url, this.options))
      );

      await this.storageManager.gzip(
        `${this.baseDir}/chromeNetlog.json`,
        path.join(
          this.baseDir,
          pathToFolder(result.url, this.options),
          `chromeNetlog-${index}.json.gz`
        )
      );
    }

    if (this.chrome.collectConsoleLog) {
      if (!result.extraJson) {
        result.extraJson = {};
      }
      result.extraJson[`console-${index}.json`] = await runner.getLogs(
        webdriver.logging.Type.BROWSER
      );
    }

    if (!this.skipHar) {
      this.hars.push(
        await har(
          runner,
          result,
          index,
          this.cdpClient,
          this.logPerfEntries,
          this.chrome.includeResponseBodies,
          this.chrome.mobileEmulation,
          this.android,
          this.chrome,
          this.aliasAndUrl
        )
      );
    }

    if (this.chrome.cdp && this.chrome.cdp.performance) {
      const rawCDPMetrics = await this.cdpClient.getPerformanceMetrics();
      const cleanedMetrics = {};
      let ns, fmp;
      for (let m of rawCDPMetrics.metrics) {
        if (m.name === 'DomContentLoaded' || m.name === 'Timestamp') {
          // skip
        } else if (m.name === 'FirstMeaningfulPaint') {
          fmp = m.value;
        } else if (m.name === 'NavigationStart') {
          ns = m.value;
        } else {
          if (m.name.indexOf('Duration') > -1) {
            cleanedMetrics[m.name] = m.value * 1000;
          } else {
            cleanedMetrics[m.name] = m.value;
          }
        }
      }
      cleanedMetrics['FirstMeaningfulPaint'] = (fmp - ns) * 1000;

      result.cdp = { performance: cleanedMetrics };
    }

    return result;
  }

  /**
   * The URL/test is finished, all metrics are collected.
   */
  async afterEachURL(runner, index, result) {
    if (this.chrome.collectNetLog && this.chrome.android) {
      // THIS needs to be unique per page
      const filename = path.join(
        this.baseDir,
        pathToFolder(result.url, this.options),
        `chromeNetlog-${index}.json`
      );

      const gzFilename = path.join(
        this.baseDir,
        pathToFolder(result.url, this.options),
        `chromeNetlog-${index}.json.gz`
      );
      await this.android.pullNetLog(filename);

      await this.storageManager.gzip(filename, gzFilename, true);
    }

    if (this.collectTracingEvents) {
      const trace = traceCategoriesParser.parse(this.events, result.url);
      result.extraJson[`trace-${index}.json`] = trace;
      const cpu = await parseCpuTrace(trace, result.url);
      result.cpu = cpu;

      if (
        this.chrome.enableTraceScreenshots &&
        this.chrome.visualMetricsUsingTrace &&
        this.options.cpu
      ) {
        result.visualMetrics = speedline(trace, result, index);
      }

      // Collect render blocking info
      const renderBlockingInfo = {};
      const urlsWithBlockingInfo = trace.traceEvents.filter(
        task =>
          task.cat === 'devtools.timeline' &&
          task.name === 'ResourceSendRequest' &&
          task.args.data.url &&
          task.args.data.renderBlocking
      );
      for (let asset of urlsWithBlockingInfo) {
        renderBlockingInfo[asset.args.data.url] =
          asset.args.data.renderBlocking;
      }

      if (!this.options.skipHar) {
        for (let harRequest of this.hars[index - 1].log.entries) {
          if (renderBlockingInfo[harRequest.request.url]) {
            harRequest._renderBlocking =
              renderBlockingInfo[harRequest.request.url];
          }
        }
      }

      result.renderBlocking = renderBlockingInfo;
    }

    // Google Web Vitals hacksery
    result.googleWebVitals = {};
    if (result.browserScripts.pageinfo) {
      result.googleWebVitals.cumulativeLayoutShift =
        result.browserScripts.pageinfo.cumulativeLayoutShift;
    }
    if (result.browserScripts.timings) {
      result.googleWebVitals.largestContentfulPaint =
        result.browserScripts.timings.largestContentfulPaint.renderTime ||
        result.browserScripts.timings.largestContentfulPaint.loadTime;
      result.googleWebVitals.firstContentfulPaint =
        result.browserScripts.timings.paintTiming['first-contentful-paint'];
      result.googleWebVitals.firstInputDelay = result.browserScripts.timings
        .firstInput
        ? result.browserScripts.timings.firstInput.delay
        : 0;

      // Add LCP to the HAR
      if (
        result.browserScripts.timings.largestContentfulPaint.tagName ===
          'IMG' &&
        !this.options.skipHar
      ) {
        for (let harRequest of this.hars[index - 1].log.entries) {
          if (
            harRequest.request.url ===
            result.browserScripts.timings.largestContentfulPaint.url
          ) {
            harRequest._isLCP = true;
          }
        }
      }
    }

    // lets take the time and format the CPU long tasks
    if (
      result.browserScripts.pageinfo &&
      result.browserScripts.pageinfo.longTask
    ) {
      const cpuData = cpuMetrics(result, this.options);
      if (result.cpu) {
        result.cpu.longTasks = cpuData.longTasks;
      } else {
        result.cpu = cpuData;
      }
      result.googleWebVitals.totalBlockingTime =
        cpuData.longTasks.totalBlockingTime;
    }
  }

  /**
   * Get the CDP client (used by scripting) for browsers that supports it.
   */
  getCDPClient() {
    return this.cdpClient;
  }

  /**
   * THis method is called if a runs fail
   */
  failing(url) {
    if (this.skipHar) {
      return;
    }
    this.hars.push(harBuilder.getEmptyHAR(url, this.options.browser));
  }

  /**
   * Before the browser is stopped/closed.
   */
  async beforeBrowserStop() {
    if (this.chrome.collectNetLog && !this.chrome.android) {
      const netlog = `${this.baseDir}/chromeNetlog.json`;
      await unlink(netlog);
    }

    if (this.android && this.options.androidPower) {
      await this.android.stopPowerTesting();
    }

    if (this.cdpClient) {
      await this.cdpClient.close();
    }
  }

  /**
   * Before the browser is stopped/closed.
   */
  async afterBrowserStopped() {}

  async getHARs() {
    if (!this.skipHar) {
      return { har: harBuilder.mergeHars(this.hars) };
    } else return {};
  }
}

module.exports = Chromium;
<commit_msg>Watchout for the case where LCP do not exists. (#1602)<commit_after>'use strict';

const log = require('intel').getLogger('browsertime.chrome');
const { promisify } = require('util');
const { Type } = require('selenium-webdriver').logging;
const cpuMetrics = require('../longTaskMetrics');
const fs = require('fs');
const path = require('path');
const parseCpuTrace = require('../parseCpuTrace');
const speedline = require('../speedline');
const har = require('../har');
const harBuilder = require('../../support/har');
const webdriver = require('selenium-webdriver');
const traceCategoriesParser = require('../traceCategoriesParser');
const pathToFolder = require('../../support/pathToFolder');
const ChromeDevtoolsProtocol = require('../chromeDevtoolsProtocol');
const { Android } = require('../../android');
const unlink = promisify(fs.unlink);

class Chromium {
  constructor(storageManager, options) {
    this.options = options;
    this.chrome = options.chrome || {};
    this.skipHar = options.skipHar || false;
    this.logPerfEntries = !!this.chrome.collectPerfLog;
    this.collectTracingEvents =
      this.chrome.traceCategories || this.chrome.timeline;
    this.baseDir = storageManager.directory;
    this.storageManager = storageManager;
    // We keep track of all alias and URLs
    this.aliasAndUrl = {};
    this.isTracing = false;
    // Keep the HAR file for all runs
    this.hars = [];
    this.androidTmpDir = '/data/local/tmp/';
  }

  /**
   * Before the brrowser is started.
   */
  async beforeBrowserStart() {
    this.events = [];
    if (this.options.android) {
      this.android = new Android(this.options);
      if (this.options.androidPower) {
        await this.android.startPowerTesting();
      }

      if (this.chrome.collectNetLog) {
        // The file needs to exist for netlog to work
        await this.android._runCommand(
          `touch ${this.androidTmpDir}chromeNetlog.json`
        );
      }
    }
  }

  /**
   * The browser is up and running, now its time to start to
   * configure what you need.
   */
  async afterBrowserStart() {
    // We create the cdp as early as possible so it can be used in scripts
    // https://bugs.chromium.org/p/chromium/issues/detail?id=824626
    // https://github.com/cyrus-and/chrome-remote-interface/issues/332
    if (this.options.android) {
      await this.android.addDevtoolsFw();
    }

    this.cdpClient = new ChromeDevtoolsProtocol(this.options);
    return this.cdpClient.setup();
  }

  /**
   * Before the first iteration of your tests start.
   */
  async beforeStartIteration(runner) {
    // Here we setup everything we need using CDP
    if (this.options.injectJs) {
      await this.cdpClient.injectJavaScript(this.options.injectJs);
    }

    if (this.options.block) {
      await this.cdpClient.blockUrls(this.options.block);
    }

    if (this.options.cacheClearRaw) {
      await this.cdpClient.clearBrowserCache();
      await this.cdpClient.clearBrowserCookies();
    }

    if (this.options.clearCacheKeepCookies) {
      await this.cdpClient.clearBrowserCache();
    }

    if (this.options.basicAuth) {
      await this.cdpClient.setBasicAuth(this.options.basicAuth);
    }

    if (this.options.requestheader) {
      await this.cdpClient.setRequestHeaders(this.options.requestheader);
    }

    if (this.chrome.CPUThrottlingRate && this.chrome.CPUThrottlingRate > 1) {
      await this.cdpClient.setupCPUThrottling(this.chrome.CPUThrottlingRate);
    }

    await this.cdpClient.setupLongTask();

    // Make sure we clear the console log
    // Hopefully one time is enough?
    return runner.getLogs(Type.BROWSER);

    // remove irrelevant entries from performance log
    // and since Chromedriver 2.29 there's a bug
    // https://bugs.chromium.org/p/chromedriver/issues/detail?id=1811
    // that can be fixed by emptying the logs twice :|
    // await runner.getLogs(Type.PERFORMANCE);
    // await runner.getLogs(Type.PERFORMANCE);
  }

  /**
   * Before each URL/test runs.
   */
  async beforeEachURL(runner, url) {
    // remove irrelevant entries from performance log
    // and since Chromedriver 2.29 there's a bug
    // https://bugs.chromium.org/p/chromedriver/issues/detail?id=1811s
    await runner.getLogs(Type.PERFORMANCE);
    await runner.getLogs(Type.PERFORMANCE);

    if (this.options.cookie && url) {
      await this.cdpClient.setCookies(url, this.options.cookie);
    } else if (this.options.cookie) {
      log.info('Could not set cookie because the URL is unknown');
    }

    if (this.android && this.options.androidPower) {
      await this.android.resetPowerUsage();
    }

    if (this.collectTracingEvents && !this.isTracing) {
      this.isTracing = true;
      return this.cdpClient.startTrace();
    }
  }

  /**
   * When the page has finished loading, this functions runs (before
   * collecting metrics etc). This is the place to get you HAR file,
   * stop trace logging, stop measuring etc.
   *
   */
  async afterPageCompleteCheck(runner, index, url, alias) {
    const result = { url, alias };
    if (this.collectTracingEvents && this.isTracing) {
      // We are ready and can stop collecting events
      this.isTracing = false;
      this.events = await this.cdpClient.stopTrace();
    }

    if (this.android && this.options.androidPower) {
      result.power = await this.android.measurePowerUsage(
        this.chrome.android.package
      );
    }

    if (this.options.verbose >= 2 || this.chrome.enableChromeDriverLog) {
      return this.storageManager.gzip(
        `${this.baseDir}/chromedriver.log`,
        `${this.baseDir}/chromedriver-${index}.log.gz`,
        true
      );
    }

    if (this.chrome.collectNetLog && !this.chrome.android) {
      await this.storageManager.createSubDataDir(
        path.join(pathToFolder(result.url, this.options))
      );

      await this.storageManager.gzip(
        `${this.baseDir}/chromeNetlog.json`,
        path.join(
          this.baseDir,
          pathToFolder(result.url, this.options),
          `chromeNetlog-${index}.json.gz`
        )
      );
    }

    if (this.chrome.collectConsoleLog) {
      if (!result.extraJson) {
        result.extraJson = {};
      }
      result.extraJson[`console-${index}.json`] = await runner.getLogs(
        webdriver.logging.Type.BROWSER
      );
    }

    if (!this.skipHar) {
      this.hars.push(
        await har(
          runner,
          result,
          index,
          this.cdpClient,
          this.logPerfEntries,
          this.chrome.includeResponseBodies,
          this.chrome.mobileEmulation,
          this.android,
          this.chrome,
          this.aliasAndUrl
        )
      );
    }

    if (this.chrome.cdp && this.chrome.cdp.performance) {
      const rawCDPMetrics = await this.cdpClient.getPerformanceMetrics();
      const cleanedMetrics = {};
      let ns, fmp;
      for (let m of rawCDPMetrics.metrics) {
        if (m.name === 'DomContentLoaded' || m.name === 'Timestamp') {
          // skip
        } else if (m.name === 'FirstMeaningfulPaint') {
          fmp = m.value;
        } else if (m.name === 'NavigationStart') {
          ns = m.value;
        } else {
          if (m.name.indexOf('Duration') > -1) {
            cleanedMetrics[m.name] = m.value * 1000;
          } else {
            cleanedMetrics[m.name] = m.value;
          }
        }
      }
      cleanedMetrics['FirstMeaningfulPaint'] = (fmp - ns) * 1000;

      result.cdp = { performance: cleanedMetrics };
    }

    return result;
  }

  /**
   * The URL/test is finished, all metrics are collected.
   */
  async afterEachURL(runner, index, result) {
    if (this.chrome.collectNetLog && this.chrome.android) {
      // THIS needs to be unique per page
      const filename = path.join(
        this.baseDir,
        pathToFolder(result.url, this.options),
        `chromeNetlog-${index}.json`
      );

      const gzFilename = path.join(
        this.baseDir,
        pathToFolder(result.url, this.options),
        `chromeNetlog-${index}.json.gz`
      );
      await this.android.pullNetLog(filename);

      await this.storageManager.gzip(filename, gzFilename, true);
    }

    if (this.collectTracingEvents) {
      const trace = traceCategoriesParser.parse(this.events, result.url);
      result.extraJson[`trace-${index}.json`] = trace;
      const cpu = await parseCpuTrace(trace, result.url);
      result.cpu = cpu;

      if (
        this.chrome.enableTraceScreenshots &&
        this.chrome.visualMetricsUsingTrace &&
        this.options.cpu
      ) {
        result.visualMetrics = speedline(trace, result, index);
      }

      // Collect render blocking info
      const renderBlockingInfo = {};
      const urlsWithBlockingInfo = trace.traceEvents.filter(
        task =>
          task.cat === 'devtools.timeline' &&
          task.name === 'ResourceSendRequest' &&
          task.args.data.url &&
          task.args.data.renderBlocking
      );
      for (let asset of urlsWithBlockingInfo) {
        renderBlockingInfo[asset.args.data.url] =
          asset.args.data.renderBlocking;
      }

      if (!this.options.skipHar) {
        for (let harRequest of this.hars[index - 1].log.entries) {
          if (renderBlockingInfo[harRequest.request.url]) {
            harRequest._renderBlocking =
              renderBlockingInfo[harRequest.request.url];
          }
        }
      }

      result.renderBlocking = renderBlockingInfo;
    }

    // Google Web Vitals hacksery
    result.googleWebVitals = {};
    if (result.browserScripts.pageinfo) {
      result.googleWebVitals.cumulativeLayoutShift =
        result.browserScripts.pageinfo.cumulativeLayoutShift;
    }
    if (result.browserScripts.timings) {
      if (result.browserScripts.timings.largestContentfulPaint) {
        result.googleWebVitals.largestContentfulPaint =
          result.browserScripts.timings.largestContentfulPaint.renderTime ||
          result.browserScripts.timings.largestContentfulPaint.loadTime;
      }
      result.googleWebVitals.firstContentfulPaint =
        result.browserScripts.timings.paintTiming['first-contentful-paint'];
      result.googleWebVitals.firstInputDelay = result.browserScripts.timings
        .firstInput
        ? result.browserScripts.timings.firstInput.delay
        : 0;

      // Add LCP to the HAR
      if (
        result.browserScripts.timings.largestContentfulPaint &&
        result.browserScripts.timings.largestContentfulPaint.tagName ===
          'IMG' &&
        !this.options.skipHar
      ) {
        for (let harRequest of this.hars[index - 1].log.entries) {
          if (
            harRequest.request.url ===
            result.browserScripts.timings.largestContentfulPaint.url
          ) {
            harRequest._isLCP = true;
          }
        }
      }
    }

    // lets take the time and format the CPU long tasks
    if (
      result.browserScripts.pageinfo &&
      result.browserScripts.pageinfo.longTask
    ) {
      const cpuData = cpuMetrics(result, this.options);
      if (result.cpu) {
        result.cpu.longTasks = cpuData.longTasks;
      } else {
        result.cpu = cpuData;
      }
      result.googleWebVitals.totalBlockingTime =
        cpuData.longTasks.totalBlockingTime;
    }
  }

  /**
   * Get the CDP client (used by scripting) for browsers that supports it.
   */
  getCDPClient() {
    return this.cdpClient;
  }

  /**
   * THis method is called if a runs fail
   */
  failing(url) {
    if (this.skipHar) {
      return;
    }
    this.hars.push(harBuilder.getEmptyHAR(url, this.options.browser));
  }

  /**
   * Before the browser is stopped/closed.
   */
  async beforeBrowserStop() {
    if (this.chrome.collectNetLog && !this.chrome.android) {
      const netlog = `${this.baseDir}/chromeNetlog.json`;
      await unlink(netlog);
    }

    if (this.android && this.options.androidPower) {
      await this.android.stopPowerTesting();
    }

    if (this.cdpClient) {
      await this.cdpClient.close();
    }
  }

  /**
   * Before the browser is stopped/closed.
   */
  async afterBrowserStopped() {}

  async getHARs() {
    if (!this.skipHar) {
      return { har: harBuilder.mergeHars(this.hars) };
    } else return {};
  }
}

module.exports = Chromium;
<|endoftext|>


<commit_before>var _jq = (jQuery || django.jQuery)

// We need this exports because Select2 needs window.jQuery
// to initialize. In fact it removes jQuery.noConflict(true) if
// django.jQuery is used.
if (window.jQuery == undefined) {
    window.jQuery = _jq;
}
if (window.$ == undefined) {
    window.$ = _jq;
}

(function ($) {
    "use strict";
    // store for keeping all the select2 widget ids for fail-safe parsing later
    var _all_easy_select2_ids = [];
    /**
     * passing the current element to initialize and the options to initialize with
     * @param $el - Jquery object to initialize with select2
     * @param obj - select2 constructor options
     */
    function redisplay_select2($el, obj){
        var $selectEle = $("#" + $el.attr('id'));
        if($selectEle.hasClass("select2-hidden-accessible")){
            $selectEle.select2('destroy').select2(obj);
        } else{
            $selectEle.select2(obj);
        }
        $selectEle.change(function (e) {
            var $pencilEle = $('#change_'+$(this).attr('id'));

            if($pencilEle.length < 1 ) return;

            $pencilEle.attr('href',
                $pencilEle.attr('data-href-template').replace('__fk__', $(this).val())
            );
        });
        _all_easy_select2_ids.push($el.attr('id'));
    }

    /**
     * core function call for easy_select2
     * @param options - select2 constructor properties
     */
    function add_select2_handlers(options){

        $('div.field-easy-select2:not([id*="__prefix__"])').each(function(){
            // taking data-* for select2 constructor properties for backward compatibility
            var obj = $(this).data();

            // merging the options and data properties, modifying the first
            // NOTE: obj properties will be overwritten by options
            // https://api.jquery.com/jquery.extend/
            $.extend(obj, options);
            redisplay_select2($(this), obj);
        });

        $(document).bind('DOMNodeInserted', function (e) {
            var $changedEle = $(e.target);

            if(!$changedEle.parentsUntil('select').length < 1) return;

            var $select2Eles = $(e.target).find('div.field-easy-select2:not([id*="__prefix__"])');
            if($select2Eles.length) {
                 $(e.target).find('div.field-easy-select2:not([id*="__prefix__"])').each(function () {
                     // taking data-* for select2 constructor properties for backward compatibility
                    var obj = $(this).data();
                    // merging the options and data properties, modifying the first
                    // NOTE: obj properties will be overwritten by options
                    // https://api.jquery.com/jquery.extend/

                     $.extend(obj, options);
                     redisplay_select2($(this), obj);
                 });
            } else {
                $.each(_all_easy_select2_ids, function(idx, val){
                    var obj = $("#" + val).data();
                    $.extend(obj, options);
                    $("#" + val).select2('destroy').select2();
                });
            }
        });
    }

    /**
     * JQuery plugin for django-easy-select2
     * @param options - object containing select2 constructor properties
     */
    $.fn.easy_select = function(options){
        add_select2_handlers(options);
    };

}(jQuery || django.jQuery));<commit_msg>Split DOMNodeInserted and create updateSelect func<commit_after>var _jq = (jQuery || django.jQuery)

// We need this exports because Select2 needs window.jQuery
// to initialize. In fact it removes jQuery.noConflict(true) if
// django.jQuery is used.
if (window.jQuery == undefined) {
    window.jQuery = _jq;
}
if (window.$ == undefined) {
    window.$ = _jq;
}

(function ($) {
    "use strict";
    // store for keeping all the select2 widget ids for fail-safe parsing later
    var _all_easy_select2_ids = [];
    /**
     * passing the current element to initialize and the options to initialize with
     * @param $el - Jquery object to initialize with select2
     * @param obj - select2 constructor options
     */
    function redisplay_select2($el, obj){
        var $selectEle = $("#" + $el.attr('id'));
        if($selectEle.hasClass("select2-hidden-accessible")){
            $selectEle.select2('destroy').select2(obj);
        } else{
            $selectEle.select2(obj);
        }
        $selectEle.change(function (e) {
            var $pencilEle = $('#change_'+$(this).attr('id'));

            if($pencilEle.length < 1 ) return;

            $pencilEle.attr('href',
                $pencilEle.attr('data-href-template').replace('__fk__', $(this).val())
            );
        });
        _all_easy_select2_ids.push($el.attr('id'));
    }

    /**
     * core function call for easy_select2
     * @param options - select2 constructor properties
     */
    function add_select2_handlers(options){

        $('div.field-easy-select2:not([id*="__prefix__"])').each(function(){
            // taking data-* for select2 constructor properties for backward compatibility
            var obj = $(this).data();

            // merging the options and data properties, modifying the first
            // NOTE: obj properties will be overwritten by options
            // https://api.jquery.com/jquery.extend/
            $.extend(obj, options);
            redisplay_select2($(this), obj);
        });

        $(document).bind('DOMNodeInserted', function (e) {
            var $changedEle = $(e.target);
            if(!$changedEle.parentsUntil('select').length < 1) return;

            updateSelect($changedEle);
        });

        function updateSelect($changedEle) {
            var $select2Eles = $changedEle.find('div.field-easy-select2:not([id*="__prefix__"])');
            if($select2Eles.length) {
                 $changedEle.find('div.field-easy-select2:not([id*="__prefix__"])').each(function () {
                     // taking data-* for select2 constructor properties for backward compatibility
                    var obj = $(this).data();
                    // merging the options and data properties, modifying the first
                    // NOTE: obj properties will be overwritten by options
                    // https://api.jquery.com/jquery.extend/

                     $.extend(obj, options);
                     redisplay_select2($(this), obj);
                 });
            } else {
                $.each(_all_easy_select2_ids, function(idx, val){
                    var obj = $("#" + val).data();
                    $.extend(obj, options);
                    $("#" + val).select2('destroy').select2();
                });
            }
        };
    }

    /**
     * JQuery plugin for django-easy-select2
     * @param options - object containing select2 constructor properties
     */
    $.fn.easy_select = function(options){
        add_select2_handlers(options);
    };

}(jQuery || django.jQuery));<|endoftext|>