<commit_before>'use strict';

const log = require('intel').getLogger('browsertime.chrome');
const { promisify } = require('util');
const { Type } = require('selenium-webdriver').logging;
const get = require('lodash.get');
const harBuilder = require('../../support/har/');
const perflogParser = require('chrome-har');
const fs = require('fs');
const { parseCpuTrace } = require('../cpu');
const webdriver = require('selenium-webdriver');
const zlib = require('zlib');
const images = require('../../support/images');
const traceCategoriesParser = require('../traceCategoriesParser');
const pathToFolder = require('../../support/pathToFolder');
const path = require('path');
const speedline = require('speedline-core');
const visualMetricsExtra = require('../../video/postprocessing/visualmetrics/extraMetrics');
const ChromeDevtoolsProtocol = require('./chromeDevtoolsProtocol');
const { Android } = require('../../android');
const unlink = promisify(fs.unlink);

function pad(n) {
  const extraZeroes = 6 - n.toString().length;
  for (let i = 0; i < extraZeroes; i++) {
    n = '0' + n;
  }
  return n;
}

class ChromeDelegate {
  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;
  }

  async onStart() {
    this.hars = [];
    this.events = [];
    if (this.chrome.android) {
      this.android = new Android(this.options);
    }
  }

  async afterBrowserStart() {
    // We create the cdp as early as possibble 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
    this.cdpClient = new ChromeDevtoolsProtocol(this.options);
    return this.cdpClient.setup();
  }

  async onStartIteration(runner) {
    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.collectLongTasks) {
      await this.cdpClient.setupLongTask();
    }

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

    // Make sure we clear the console log
    // Hopefully one time is enough?
    await 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);

    if (this.skipHar) {
      return;
    }
  }

  async clear(runner) {
    await runner.getLogs(Type.PERFORMANCE);
    await runner.getLogs(Type.PERFORMANCE);
  }

  async onStopIteration() {
    if (this.chrome.collectNetLog && !this.chrome.android) {
      const netlog = `${this.baseDir}/chromeNetlog.json`;
      return unlink(netlog);
    }
  }

  async beforeEachURL(runner, url) {
    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.collectTracingEvents && !this.isTracing) {
      this.isTracing = true;
      return this.cdpClient.startTrace();
    }
  }

  async beforeCollect() {
    if (this.collectTracingEvents) {
      // We are ready and can stop collecting events
      this.isTracing = false;
      this.events = await this.cdpClient.stopTrace();
    }
  }

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

      const gzip = zlib.createGzip();
      const netlog = `${this.baseDir}/chromeNetlog.json`;
      const input = fs.createReadStream(netlog);
      const out = fs.createWriteStream(
        path.join(
          this.baseDir,
          pathToFolder(result.url, this.options),
          `chromeNetlog-${index}.json.gz`
        )
      );
      out.on('finish', function() {
        // return unlink(netlog);
      });
      out.on('error', function(e) {
        log.error('Could not gzip the Chrome net log', e);
      });
      input.pipe(gzip).pipe(out);
    }

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

    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 };

    if (this.chrome.collectNetLog && this.chrome.android) {
      // THIS needs to be unique per page
      await this.android.pullNetLog(
        path.join(
          this.baseDir,
          pathToFolder(result.url, this.options),
          `chromeNetlog-${index}.json.gz`
        )
      );
    }

    if (this.skipHar) {
      return;
    }

    log.debug('Getting performance logs from Chrome');

    const logs = await runner.getLogs(Type.PERFORMANCE);
    const messages = logs.map(entry => JSON.parse(entry.message).message);

    if (this.logPerfEntries) {
      result.extraJson[`chromePerflog-${index}.json`] = messages;
    }
    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
      ) {
        try {
          const navStart = trace.traceEvents.filter(
            task =>
              task.cat === 'blink.user_timing' &&
              task.name === 'navigationStart'
          );
          navStart.sort(function(a, b) {
            return a.ts - b.ts;
          });
          log.debug('Get Speedline result from the trace');
          const speedlineResult = await speedline(trace.traceEvents, {
            timeOrigin: navStart[0].ts,
            fastMode: true
          });
          log.debug('Got Speedline result.');
          const startTs = speedlineResult.beginning;
          const visualProgress = speedlineResult.frames
            .map(frame => {
              const ts = Math.floor(frame.getTimeStamp() - startTs);
              return `${ts}=${Math.floor(frame.getProgress())}%`;
            })
            .join(', ');

          result.visualMetrics = visualMetricsExtra({
            FirstVisualChange: Number(speedlineResult.first.toFixed(0)),
            LastVisualChange: Number(speedlineResult.complete.toFixed(0)),
            SpeedIndex: Number(speedlineResult.speedIndex.toFixed(0)),
            PerceptualSpeedIndex: Number(
              speedlineResult.perceptualSpeedIndex.toFixed(0)
            ),
            VisualProgress: visualProgress
          }).visualMetrics;

          const promises = [];
          for (let frame of speedlineResult.frames) {
            // follow the name standard of Visual Metrics
            // ms_000000.jpg
            const d = new Date(frame.getTimeStamp() - startTs);
            const name = 'ms_' + pad(d.getTime());
            promises.push(
              images.saveJpg(
                name,
                frame.getImage(),
                result.url,
                this.storageManager,
                {
                  type: 'jpg',
                  jpg: {
                    quality: 80
                  },
                  maxSize: 400
                },
                path.join('filmstrip', index + ''),
                this.options
              )
            );
          }

          await Promise.all(promises);
        } catch (e) {
          log.error('Could not generate Visual Metrics using SpeedLine', e);
        }
      }
    }

    // lets take the time and format the CPU long tasks
    if (
      result.browserScripts.pageinfo &&
      result.browserScripts.pageinfo.longTask
    ) {
      let totalDurationFirstPaint = 0;
      let totalDurationFirstContentFulPaint = 0;
      let totalDurationAfterLoadEventEnd = 0;
      let totalDuration = 0;

      // https://web.dev/lighthouse-max-potential-fid/
      let maxPotentialFid = 0;
      // https://web.dev/tbt/ - total blocking time
      let totalBlockingTime = 0;

      const firstPaint = get(
        result,
        'browserScripts.timings.paintTiming.first-paint'
      );
      const firstContentfulPaint = get(
        result,
        'browserScripts.timings.paintTiming.first-contentful-paint'
      );

      const loadEventEnd = get(result, 'browserScripts.timings.loadEventEnd');

      let longTasksBeforeFirstPaint = 0;
      let longTasksBeforeFirstContentfulPaint = 0;
      let longTasksAfterLoadEventEnd = 0;

      for (let longTask of result.browserScripts.pageinfo.longTask) {
        totalDuration += longTask.duration;
        if (firstPaint && longTask.startTime < firstPaint) {
          longTasksBeforeFirstPaint++;
          totalDurationFirstPaint += longTask.duration;
        }
        if (firstContentfulPaint && longTask.startTime < firstContentfulPaint) {
          longTasksBeforeFirstContentfulPaint++;
          totalDurationFirstContentFulPaint += longTask.duration;
        }
        if (
          firstContentfulPaint &&
          longTask.startTime > firstContentfulPaint &&
          longTask.duration > maxPotentialFid
        ) {
          maxPotentialFid = longTask.duration;
        }
        if (firstContentfulPaint && longTask.startTime > firstContentfulPaint) {
          totalBlockingTime +=
            longTask.duration - (this.options.minLongTaskLength || 50);
        }
        if (loadEventEnd && longTask.startTime > loadEventEnd) {
          longTasksAfterLoadEventEnd++;
          totalDurationAfterLoadEventEnd += longTask.duration;
        }
      }

      const cpu = {
        longTasks: {
          tasks: result.browserScripts.pageinfo.longTask.length,
          totalDuration,
          totalBlockingTime: Number(totalBlockingTime.toFixed(0)),
          maxPotentialFid: Number(maxPotentialFid.toFixed(0)),
          beforeFirstPaint: {
            tasks: longTasksBeforeFirstPaint,
            totalDuration: totalDurationFirstPaint
          },
          beforeFirstContentfulPaint: {
            tasks: longTasksBeforeFirstContentfulPaint,
            totalDuration: totalDurationFirstContentFulPaint
          },
          afterLoadEventEnd: {
            tasks: longTasksAfterLoadEventEnd,
            totalDuration: totalDurationAfterLoadEventEnd
          }
        }
      };

      if (result.cpu) {
        result.cpu.longTasks = cpu.longTasks;
      } else {
        result.cpu = cpu;
      }
    }

    // CLEANUP since Chromedriver 2.29 there's a bug
    // https://bugs.chromium.org/p/chromedriver/issues/detail?id=1811
    await runner.getLogs(Type.PERFORMANCE);

    const har = perflogParser.harFromMessages(messages);

    if (
      this.chrome.includeResponseBodies === 'html' ||
      this.chrome.includeResponseBodies === 'all'
    ) {
      await this.cdpClient.setResponseBodies(har);
    }

    const versionInfo = (await this.cdpClient.send(
      'Browser.getVersion'
    )).product.split('/');
    const info = {
      name: versionInfo[0],
      version: versionInfo[1]
    };

    if (this.chrome.mobileEmulation) {
      info.name = `Chrome Emulated ${this.chrome.mobileEmulation.deviceName}`;
    }
    harBuilder.addBrowser(har, info.name, info.version);

    if (this.android) {
      har.log._android = await this.android.getModel();
    }

    if (har.log.pages.length > 0) {
      har.log.pages[0].title = `${result.url} run ${index}`;
      // Hack to add the URL from a SPA
      if (result.alias && !this.aliasAndUrl[result.alias]) {
        this.aliasAndUrl[result.alias] = result.url;
        har.log.pages[0]._url = result.url;
      } else if (result.alias && this.aliasAndUrl[result.alias]) {
        har.log.pages[0]._url = this.aliasAndUrl[result.alias];
      } else {
        har.log.pages[0]._url = result.url;
      }
    }

    this.hars.push(har);
  }

  async sendAndGetDevToolsCommand(cmd, params = {}) {
    return this.cdpClient.send(cmd, params);
  }
  async sendDevToolsCommand(cmd, params = {}) {
    return this.cdpClient.send(cmd, params);
  }

  failing(url) {
    if (this.skipHar) {
      return;
    }
    this.hars.push(harBuilder.getEmptyHAR(url, 'Chrome'));
  }

  async onStop() {
    if (this.cdpClient) {
      await this.cdpClient.close();
    }
    if (this.chrome.android) {
      await this.android.removeFw();
    }
    if (!this.skipHar) {
      return { har: harBuilder.mergeHars(this.hars) };
    } else return {};
  }
}

module.exports = ChromeDelegate;
<commit_msg>Use the utility method for gzip. (#1063)<commit_after>'use strict';

const log = require('intel').getLogger('browsertime.chrome');
const { promisify } = require('util');
const { Type } = require('selenium-webdriver').logging;
const get = require('lodash.get');
const harBuilder = require('../../support/har/');
const perflogParser = require('chrome-har');
const fs = require('fs');
const { parseCpuTrace } = require('../cpu');
const webdriver = require('selenium-webdriver');
const images = require('../../support/images');
const traceCategoriesParser = require('../traceCategoriesParser');
const pathToFolder = require('../../support/pathToFolder');
const path = require('path');
const speedline = require('speedline-core');
const visualMetricsExtra = require('../../video/postprocessing/visualmetrics/extraMetrics');
const ChromeDevtoolsProtocol = require('./chromeDevtoolsProtocol');
const { Android } = require('../../android');
const unlink = promisify(fs.unlink);

function pad(n) {
  const extraZeroes = 6 - n.toString().length;
  for (let i = 0; i < extraZeroes; i++) {
    n = '0' + n;
  }
  return n;
}

class ChromeDelegate {
  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;
  }

  async onStart() {
    this.hars = [];
    this.events = [];
    if (this.chrome.android) {
      this.android = new Android(this.options);
    }
  }

  async afterBrowserStart() {
    // We create the cdp as early as possibble 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
    this.cdpClient = new ChromeDevtoolsProtocol(this.options);
    return this.cdpClient.setup();
  }

  async onStartIteration(runner) {
    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.collectLongTasks) {
      await this.cdpClient.setupLongTask();
    }

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

    // Make sure we clear the console log
    // Hopefully one time is enough?
    await 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);

    if (this.skipHar) {
      return;
    }
  }

  async clear(runner) {
    await runner.getLogs(Type.PERFORMANCE);
    await runner.getLogs(Type.PERFORMANCE);
  }

  async onStopIteration() {
    if (this.chrome.collectNetLog && !this.chrome.android) {
      const netlog = `${this.baseDir}/chromeNetlog.json`;
      return unlink(netlog);
    }
  }

  async beforeEachURL(runner, url) {
    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.collectTracingEvents && !this.isTracing) {
      this.isTracing = true;
      return this.cdpClient.startTrace();
    }
  }

  async beforeCollect() {
    if (this.collectTracingEvents) {
      // We are ready and can stop collecting events
      this.isTracing = false;
      this.events = await this.cdpClient.stopTrace();
    }
  }

  async onCollect(runner, index, result) {
    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) {
      result.extraJson[`console-${index}.json`] = await runner.getLogs(
        webdriver.logging.Type.BROWSER
      );
    }

    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 };

    if (this.chrome.collectNetLog && this.chrome.android) {
      // THIS needs to be unique per page
      await this.android.pullNetLog(
        path.join(
          this.baseDir,
          pathToFolder(result.url, this.options),
          `chromeNetlog-${index}.json.gz`
        )
      );
    }

    if (this.skipHar) {
      return;
    }

    log.debug('Getting performance logs from Chrome');

    const logs = await runner.getLogs(Type.PERFORMANCE);
    const messages = logs.map(entry => JSON.parse(entry.message).message);

    if (this.logPerfEntries) {
      result.extraJson[`chromePerflog-${index}.json`] = messages;
    }
    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
      ) {
        try {
          const navStart = trace.traceEvents.filter(
            task =>
              task.cat === 'blink.user_timing' &&
              task.name === 'navigationStart'
          );
          navStart.sort(function(a, b) {
            return a.ts - b.ts;
          });
          log.debug('Get Speedline result from the trace');
          const speedlineResult = await speedline(trace.traceEvents, {
            timeOrigin: navStart[0].ts,
            fastMode: true
          });
          log.debug('Got Speedline result.');
          const startTs = speedlineResult.beginning;
          const visualProgress = speedlineResult.frames
            .map(frame => {
              const ts = Math.floor(frame.getTimeStamp() - startTs);
              return `${ts}=${Math.floor(frame.getProgress())}%`;
            })
            .join(', ');

          result.visualMetrics = visualMetricsExtra({
            FirstVisualChange: Number(speedlineResult.first.toFixed(0)),
            LastVisualChange: Number(speedlineResult.complete.toFixed(0)),
            SpeedIndex: Number(speedlineResult.speedIndex.toFixed(0)),
            PerceptualSpeedIndex: Number(
              speedlineResult.perceptualSpeedIndex.toFixed(0)
            ),
            VisualProgress: visualProgress
          }).visualMetrics;

          const promises = [];
          for (let frame of speedlineResult.frames) {
            // follow the name standard of Visual Metrics
            // ms_000000.jpg
            const d = new Date(frame.getTimeStamp() - startTs);
            const name = 'ms_' + pad(d.getTime());
            promises.push(
              images.saveJpg(
                name,
                frame.getImage(),
                result.url,
                this.storageManager,
                {
                  type: 'jpg',
                  jpg: {
                    quality: 80
                  },
                  maxSize: 400
                },
                path.join('filmstrip', index + ''),
                this.options
              )
            );
          }

          await Promise.all(promises);
        } catch (e) {
          log.error('Could not generate Visual Metrics using SpeedLine', e);
        }
      }
    }

    // lets take the time and format the CPU long tasks
    if (
      result.browserScripts.pageinfo &&
      result.browserScripts.pageinfo.longTask
    ) {
      let totalDurationFirstPaint = 0;
      let totalDurationFirstContentFulPaint = 0;
      let totalDurationAfterLoadEventEnd = 0;
      let totalDuration = 0;

      // https://web.dev/lighthouse-max-potential-fid/
      let maxPotentialFid = 0;
      // https://web.dev/tbt/ - total blocking time
      let totalBlockingTime = 0;

      const firstPaint = get(
        result,
        'browserScripts.timings.paintTiming.first-paint'
      );
      const firstContentfulPaint = get(
        result,
        'browserScripts.timings.paintTiming.first-contentful-paint'
      );

      const loadEventEnd = get(result, 'browserScripts.timings.loadEventEnd');

      let longTasksBeforeFirstPaint = 0;
      let longTasksBeforeFirstContentfulPaint = 0;
      let longTasksAfterLoadEventEnd = 0;

      for (let longTask of result.browserScripts.pageinfo.longTask) {
        totalDuration += longTask.duration;
        if (firstPaint && longTask.startTime < firstPaint) {
          longTasksBeforeFirstPaint++;
          totalDurationFirstPaint += longTask.duration;
        }
        if (firstContentfulPaint && longTask.startTime < firstContentfulPaint) {
          longTasksBeforeFirstContentfulPaint++;
          totalDurationFirstContentFulPaint += longTask.duration;
        }
        if (
          firstContentfulPaint &&
          longTask.startTime > firstContentfulPaint &&
          longTask.duration > maxPotentialFid
        ) {
          maxPotentialFid = longTask.duration;
        }
        if (firstContentfulPaint && longTask.startTime > firstContentfulPaint) {
          totalBlockingTime +=
            longTask.duration - (this.options.minLongTaskLength || 50);
        }
        if (loadEventEnd && longTask.startTime > loadEventEnd) {
          longTasksAfterLoadEventEnd++;
          totalDurationAfterLoadEventEnd += longTask.duration;
        }
      }

      const cpu = {
        longTasks: {
          tasks: result.browserScripts.pageinfo.longTask.length,
          totalDuration,
          totalBlockingTime: Number(totalBlockingTime.toFixed(0)),
          maxPotentialFid: Number(maxPotentialFid.toFixed(0)),
          beforeFirstPaint: {
            tasks: longTasksBeforeFirstPaint,
            totalDuration: totalDurationFirstPaint
          },
          beforeFirstContentfulPaint: {
            tasks: longTasksBeforeFirstContentfulPaint,
            totalDuration: totalDurationFirstContentFulPaint
          },
          afterLoadEventEnd: {
            tasks: longTasksAfterLoadEventEnd,
            totalDuration: totalDurationAfterLoadEventEnd
          }
        }
      };

      if (result.cpu) {
        result.cpu.longTasks = cpu.longTasks;
      } else {
        result.cpu = cpu;
      }
    }

    // CLEANUP since Chromedriver 2.29 there's a bug
    // https://bugs.chromium.org/p/chromedriver/issues/detail?id=1811
    await runner.getLogs(Type.PERFORMANCE);

    const har = perflogParser.harFromMessages(messages);

    if (
      this.chrome.includeResponseBodies === 'html' ||
      this.chrome.includeResponseBodies === 'all'
    ) {
      await this.cdpClient.setResponseBodies(har);
    }

    const versionInfo = (await this.cdpClient.send(
      'Browser.getVersion'
    )).product.split('/');
    const info = {
      name: versionInfo[0],
      version: versionInfo[1]
    };

    if (this.chrome.mobileEmulation) {
      info.name = `Chrome Emulated ${this.chrome.mobileEmulation.deviceName}`;
    }
    harBuilder.addBrowser(har, info.name, info.version);

    if (this.android) {
      har.log._android = await this.android.getModel();
    }

    if (har.log.pages.length > 0) {
      har.log.pages[0].title = `${result.url} run ${index}`;
      // Hack to add the URL from a SPA
      if (result.alias && !this.aliasAndUrl[result.alias]) {
        this.aliasAndUrl[result.alias] = result.url;
        har.log.pages[0]._url = result.url;
      } else if (result.alias && this.aliasAndUrl[result.alias]) {
        har.log.pages[0]._url = this.aliasAndUrl[result.alias];
      } else {
        har.log.pages[0]._url = result.url;
      }
    }

    this.hars.push(har);
  }

  async sendAndGetDevToolsCommand(cmd, params = {}) {
    return this.cdpClient.send(cmd, params);
  }
  async sendDevToolsCommand(cmd, params = {}) {
    return this.cdpClient.send(cmd, params);
  }

  failing(url) {
    if (this.skipHar) {
      return;
    }
    this.hars.push(harBuilder.getEmptyHAR(url, 'Chrome'));
  }

  async onStop() {
    if (this.cdpClient) {
      await this.cdpClient.close();
    }
    if (this.chrome.android) {
      await this.android.removeFw();
    }
    if (!this.skipHar) {
      return { har: harBuilder.mergeHars(this.hars) };
    } else return {};
  }
}

module.exports = ChromeDelegate;
<|endoftext|>


<commit_before>import React from 'react'
import { connect } from 'react-redux'
import { LinkContainer } from 'react-router-bootstrap'
import { Redirect } from 'react-router'
import { Link } from 'react-router-dom'

/**
 * This function enhances the routing components imported above
 * by preserving the itinerary search query from the redux state
 * when redirecting the user between the main map and account-related pages,
 * so that when the user returns to the map, the itinerary that was previously
 * displayed is shown again.
 * Implementers only need to specify the 'to' route and
 * do not need to hook to redux store to retrieve the itinerary search query.
 * @param RoutingComponent The routing component to enhance.
 * @returns A new component that passes the redux search params to
 *          the RoutingComponent's 'to' prop.
 */
const connectLink = RoutingComponent =>
  ({ children, queryParams, to, ...props }) => (
    <RoutingComponent {...props} to={{ pathname: to, search: queryParams }}>
      {children}
    </RoutingComponent>
  )

// For connecting to the redux store
const mapStateToProps = (state, ownProps) => {
  return {
    queryParams: state.router.location.search
  }
}

const mapDispatchToProps = {
}

// Enhance routing components, connect the result to redux,
// and export.
export default {
  Link: connect(mapStateToProps, mapDispatchToProps)(connectLink(Link)),
  LinkContainer: connect(mapStateToProps, mapDispatchToProps)(connectLink(LinkContainer)),
  Redirect: connect(mapStateToProps, mapDispatchToProps)(connectLink(Redirect))
}
<commit_msg>refactor(connected-links): Remove empty mapDispatchToProps<commit_after>import React from 'react'
import { connect } from 'react-redux'
import { LinkContainer } from 'react-router-bootstrap'
import { Redirect } from 'react-router'
import { Link } from 'react-router-dom'

/**
 * This function enhances the routing components imported above
 * by preserving the itinerary search query from the redux state
 * when redirecting the user between the main map and account-related pages,
 * so that when the user returns to the map, the itinerary that was previously
 * displayed is shown again.
 * Implementers only need to specify the 'to' route and
 * do not need to hook to redux store to retrieve the itinerary search query.
 * @param RoutingComponent The routing component to enhance.
 * @returns A new component that passes the redux search params to
 *          the RoutingComponent's 'to' prop.
 */
const connectLink = RoutingComponent =>
  ({ children, queryParams, to, ...props }) => (
    <RoutingComponent {...props} to={{ pathname: to, search: queryParams }}>
      {children}
    </RoutingComponent>
  )

// For connecting to the redux store
const mapStateToProps = (state, ownProps) => {
  return {
    queryParams: state.router.location.search
  }
}

// Enhance routing components, connect the result to redux,
// and export.
export default {
  Link: connect(mapStateToProps)(connectLink(Link)),
  LinkContainer: connect(mapStateToProps)(connectLink(LinkContainer)),
  Redirect: connect(mapStateToProps)(connectLink(Redirect))
}
<|endoftext|>


<commit_before>export const typeScriptDefaults = {
  check: false,
  reactDocgen: 'react-docgen-typescript',
};
<commit_msg>Typescript: Filter out node_modules for docgen by default<commit_after>export const typeScriptDefaults = {
  check: false,
  reactDocgen: 'react-docgen-typescript',
  reactDocgenTypescriptOptions: {
    propFilter: (prop) => (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true),
  },
};
<|endoftext|>


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

/*
* Name : mapChartFlowModel.js
* Module : Lib::DataTier::mapChartFlowModel
* Location : /lib/dataTier/flow
*
* History :
*
* Version   Date         Programmer         Description
* =========================================================
* 0.0.1     2015-05-15   Filippo Rampado    Initial code
* =========================================================
*/
var FlowModel = require('./flowModel.js');
var Helper = require('../../helpers/functionHelper.js');

function MapChartFlowModel(params) {
	if(params===undefined || params.ID===undefined || typeof params.ID !== 'string'|| params.ID.trim() === ''){ // ID field is required
		console.log('Error: 232');
		return;
	}

	params.type = 'MapChartFlow';
	this.parent.constructor.call(this, params);

	this._latitudeKey=null;
	this._longitudeKey=null;
	this._objectKey=null;
	this._latitudeFormat='coordinates';
	this._longitudeFormat='coordinates';
	this._marker={
		type: 'shape',//shape, icon, text
		shape: 'circle',//circle, triangle, square, diamond
		//icon: null,//path
		//text: null,//string
		//color: '#000'	//HEX
	};
	this._trace={
		type: 'none',//none, line, poly
		coordinates: []
		//strokeColor: '#FFF'	//HEX
		//fillColor: '#FFF'	//HEX
	};
	this._trailLength=3;//length of trail
	this._maxItemsSaved=500; //items saved

	this.updateProperties(params);
}

MapChartFlowModel.prototype = Object.create(FlowModel.prototype);
MapChartFlowModel.prototype.constructor = MapChartFlowModel;
MapChartFlowModel.prototype.parent = FlowModel.prototype;

MapChartFlowModel.prototype.updateProperties = function(params){
	if (params!==undefined){
		var prop=this.parent.updateProperties.call(this, params);
		if(params.longitudeKey!==undefined && typeof params.longitudeKey === 'string'){
			this._longitudeKey=prop.longitudeKey=params.longitudeKey;
		}
		if(params.latitudeKey!==undefined && typeof params.latitudeKey === 'string'){
			this._latitudeKey=prop.latitudeKey=params.latitudeKey;
		}
		if(params.objectKey!==undefined && typeof params.objectKey === 'string'){
			this._objectKey=prop.objectKey=params.objectKey;
		}
		if(params.longitudeFormat!==undefined && Helper.isValidMapFormat(params.longitudeFormat)){
			this._longitudeFormat=prop.longitudeFormat=params.longitudeFormat;
		}
		if(params.latitudeFormat!==undefined && Helper.isValidMapFormat(params.latitudeFormat)){
			this._latitudeFormat=prop.latitudeFormat=params.latitudeFormat;
		}
		if(params.marker!==undefined && Helper.isValidMapMarker(params.marker)){
			this._marker=prop.marker=params.marker;
		}
		if(params.trace!==undefined && Helper.isValidTrace(params.trace)){
			this._trace=prop.trace=params.trace;
		}
		if(params.trailLength!==undefined && typeof params.trailLength==='number' && params.trailLength>=1){
			this._trailLength=prop.trailLength=params.trailLength;
		}
		if(params.maxItemsSaved!==undefined && typeof params.maxItemsSaved==='number' && params.maxItemsSaved>=0){
			this._maxItemsSaved=prop.maxItemsSaved=params.maxItemsSaved;
		}
		return prop;
	}
	return 233;
};

MapChartFlowModel.prototype.addRecord = function(record){
	if(record!==undefined && record!==null && typeof record === 'object'){
		this._records.push(record);
		var index=this._records.length-1;
		var ID=this.generateNorrisRecordID(index);
		this._records[index].norrisRecordID=ID;
		this.validateRecord(index);
		return ID;
	}
	return 133;
};

MapChartFlowModel.prototype.updateRecord = function(ID, record){
	if (record === undefined || record === null || typeof record !== 'object'|| Array.isArray(record)){
		return 131;
	}
	if (typeof ID==='string' && ID.indexOf(this._ID) !== -1) {
		var records=this._records.length;
		var i=0;
		var found=false;
		while (i<records && (!found)){
			if (this._records[i].norrisRecordID===ID){
				found=true;
			}
			else{
				i++;
			}
		}//while
		if (found){//in records[i] there is the ID
			record.norrisRecordID=this._records[i].norrisRecordID;
			this._records[i]=record;
			this.validateRecord(i);
			return true;
		}
		//no ID in records
		return 132;
	}
	//invalid ID
	return 132;
};

MapChartFlowModel.prototype.updateMovie = function(params){
	//aggiungere controllo su tipo records
	var found=false;
	var recordsID=[];
	//this._records=[];
	var i=0;
	for (var keyParam in params) {
		found=false;
		for (var keyRecord in this._records) {
			if(params[this._objectKey]===this._records[keyRecord][this._objectKey]){
				found=true;
				params[keyParam].norrisRecordID=this._records[keyRecord].norrisRecordID;
				recordsID.push(this._records[keyRecord].norrisRecordID);
				console.log('\nOLD COPY ID');
				console.dir(this._records[keyRecord]);
				break;
			}
		}
		if(!found){
			params[keyParam].norrisRecordID=this.generateNorrisRecordID(i);
			console.log('\nNEW ID');
		}
		recordsID.push(params[keyParam].norrisRecordID);
		i++;

		console.log('-------------------------------------');
		console.dir(params[keyParam]);
		console.log('\n');

	}
	/*for (var i=0; i<records.length; i++){
		this._records[i]=records[i];
		var found=false;
		var j=0;
		while (j<this._records && !found){
			if(records[this._objectKey]===this._records[j][this._objectKey]){
				found=true;
			}
			else{
				j++;
			}
		}
		var ID;
		if(!found){
			ID=this.generateNorrisRecordID(i);
		}
		else{
			ID=this._records[j].norrisRecordID;
		}
		this._records[i].norrisRecordID=ID;
		recordsID[i]=ID;
		this.validateRecord(i);
	}*/
	this._records=params;
	for (var key in this._records) {
		this.validateRecord(key);
	}
	return recordsID;
};

MapChartFlowModel.prototype.deleteRecord = function(ID){
	if (ID!==undefined && typeof ID==='string' && ID.indexOf(this._ID) !== -1) {
		var records=this._records.length;
		var i=0;
		var found=false;
		while (i<records && (!found)){
			if (this._records[i].norrisRecordID===ID){
				found=true;
			}
			else{
				i++;
			}
		}//while
		if (found){//in records[i] there is the ID_index
			this._records.splice(i, 1);
			return true;
		}
	}
	return 134;
};

MapChartFlowModel.prototype.getProperties = function(){
	var prop=this.parent.getProperties.call(this);
	prop.longitudeKey=this._longitudeKey;
	prop.latitudeKey=this._latitudeKey;
	prop.objectKey=this._objectKey;
	prop.longitudeFormat=this._longitudeFormat;
	prop.latitudeFormat=this._latitudeFormat;
	prop.marker=this._marker;
	prop.trace=this._trace;
	prop.trailLength=this._trailLength;
	prop.maxItemsSaved=this._maxItemsSaved;
	return prop;
};

MapChartFlowModel.prototype.getRecordByID = function(ID){
	if (typeof ID==='string' && ID.indexOf(this._ID) !== -1) {
		var records=this._records.length;
		var i=0;
		var found=false;
		while (i<records && (!found)){
			if (this._records[i].norrisRecordID===ID){
				found=true;
			}
			else{
				i++;
			}
		}//while
		if (found){//in records[i] there is the ID
			return this._records[i];
		}
		//no ID in records
	}
	return 135;
};

module.exports = MapChartFlowModel;<commit_msg>add more logs<commit_after>/*jshint node: true, -W106 */
'use strict';

/*
* Name : mapChartFlowModel.js
* Module : Lib::DataTier::mapChartFlowModel
* Location : /lib/dataTier/flow
*
* History :
*
* Version   Date         Programmer         Description
* =========================================================
* 0.0.1     2015-05-15   Filippo Rampado    Initial code
* =========================================================
*/
var FlowModel = require('./flowModel.js');
var Helper = require('../../helpers/functionHelper.js');

function MapChartFlowModel(params) {
	if(params===undefined || params.ID===undefined || typeof params.ID !== 'string'|| params.ID.trim() === ''){ // ID field is required
		console.log('Error: 232');
		return;
	}

	params.type = 'MapChartFlow';
	this.parent.constructor.call(this, params);

	this._latitudeKey=null;
	this._longitudeKey=null;
	this._objectKey=null;
	this._latitudeFormat='coordinates';
	this._longitudeFormat='coordinates';
	this._marker={
		type: 'shape',//shape, icon, text
		shape: 'circle',//circle, triangle, square, diamond
		//icon: null,//path
		//text: null,//string
		//color: '#000'	//HEX
	};
	this._trace={
		type: 'none',//none, line, poly
		coordinates: []
		//strokeColor: '#FFF'	//HEX
		//fillColor: '#FFF'	//HEX
	};
	this._trailLength=3;//length of trail
	this._maxItemsSaved=500; //items saved

	this.updateProperties(params);
}

MapChartFlowModel.prototype = Object.create(FlowModel.prototype);
MapChartFlowModel.prototype.constructor = MapChartFlowModel;
MapChartFlowModel.prototype.parent = FlowModel.prototype;

MapChartFlowModel.prototype.updateProperties = function(params){
	if (params!==undefined){
		var prop=this.parent.updateProperties.call(this, params);
		if(params.longitudeKey!==undefined && typeof params.longitudeKey === 'string'){
			this._longitudeKey=prop.longitudeKey=params.longitudeKey;
		}
		if(params.latitudeKey!==undefined && typeof params.latitudeKey === 'string'){
			this._latitudeKey=prop.latitudeKey=params.latitudeKey;
		}
		if(params.objectKey!==undefined && typeof params.objectKey === 'string'){
			this._objectKey=prop.objectKey=params.objectKey;
		}
		if(params.longitudeFormat!==undefined && Helper.isValidMapFormat(params.longitudeFormat)){
			this._longitudeFormat=prop.longitudeFormat=params.longitudeFormat;
		}
		if(params.latitudeFormat!==undefined && Helper.isValidMapFormat(params.latitudeFormat)){
			this._latitudeFormat=prop.latitudeFormat=params.latitudeFormat;
		}
		if(params.marker!==undefined && Helper.isValidMapMarker(params.marker)){
			this._marker=prop.marker=params.marker;
		}
		if(params.trace!==undefined && Helper.isValidTrace(params.trace)){
			this._trace=prop.trace=params.trace;
		}
		if(params.trailLength!==undefined && typeof params.trailLength==='number' && params.trailLength>=1){
			this._trailLength=prop.trailLength=params.trailLength;
		}
		if(params.maxItemsSaved!==undefined && typeof params.maxItemsSaved==='number' && params.maxItemsSaved>=0){
			this._maxItemsSaved=prop.maxItemsSaved=params.maxItemsSaved;
		}
		return prop;
	}
	return 233;
};

MapChartFlowModel.prototype.addRecord = function(record){
	if(record!==undefined && record!==null && typeof record === 'object'){
		this._records.push(record);
		var index=this._records.length-1;
		var ID=this.generateNorrisRecordID(index);
		this._records[index].norrisRecordID=ID;
		this.validateRecord(index);
		return ID;
	}
	return 133;
};

MapChartFlowModel.prototype.updateRecord = function(ID, record){
	if (record === undefined || record === null || typeof record !== 'object'|| Array.isArray(record)){
		return 131;
	}
	if (typeof ID==='string' && ID.indexOf(this._ID) !== -1) {
		var records=this._records.length;
		var i=0;
		var found=false;
		while (i<records && (!found)){
			if (this._records[i].norrisRecordID===ID){
				found=true;
			}
			else{
				i++;
			}
		}//while
		if (found){//in records[i] there is the ID
			record.norrisRecordID=this._records[i].norrisRecordID;
			this._records[i]=record;
			this.validateRecord(i);
			return true;
		}
		//no ID in records
		return 132;
	}
	//invalid ID
	return 132;
};

MapChartFlowModel.prototype.updateMovie = function(params){
	//aggiungere controllo su tipo records
	var found=false;
	var recordsID=[];
	//this._records=[];
	var i=0;
	for (var keyParam in params) {
		found=false;
		for (var keyRecord in this._records) {
			console.log('['+ params[this._objectKey] +' '+ this._records[keyRecord][this._objectKey] +']');
			if(params[this._objectKey]===this._records[keyRecord][this._objectKey]){
				found=true;
				params[keyParam].norrisRecordID=this._records[keyRecord].norrisRecordID;
				recordsID.push(this._records[keyRecord].norrisRecordID);
				console.log('\nOLD COPY ID');
				console.dir(this._records[keyRecord]);
				break;
			}
		}
		if(!found){
			params[keyParam].norrisRecordID=this.generateNorrisRecordID(i);
			console.log('\nNEW ID');
		}
		recordsID.push(params[keyParam].norrisRecordID);
		i++;

		console.log('-------------------------------------');
		console.dir(params[keyParam]);
		console.log('\n');

	}
	/*for (var i=0; i<records.length; i++){
		this._records[i]=records[i];
		var found=false;
		var j=0;
		while (j<this._records && !found){
			if(records[this._objectKey]===this._records[j][this._objectKey]){
				found=true;
			}
			else{
				j++;
			}
		}
		var ID;
		if(!found){
			ID=this.generateNorrisRecordID(i);
		}
		else{
			ID=this._records[j].norrisRecordID;
		}
		this._records[i].norrisRecordID=ID;
		recordsID[i]=ID;
		this.validateRecord(i);
	}*/
	this._records=params;
	for (var key in this._records) {
		this.validateRecord(key);
	}
	return recordsID;
};

MapChartFlowModel.prototype.deleteRecord = function(ID){
	if (ID!==undefined && typeof ID==='string' && ID.indexOf(this._ID) !== -1) {
		var records=this._records.length;
		var i=0;
		var found=false;
		while (i<records && (!found)){
			if (this._records[i].norrisRecordID===ID){
				found=true;
			}
			else{
				i++;
			}
		}//while
		if (found){//in records[i] there is the ID_index
			this._records.splice(i, 1);
			return true;
		}
	}
	return 134;
};

MapChartFlowModel.prototype.getProperties = function(){
	var prop=this.parent.getProperties.call(this);
	prop.longitudeKey=this._longitudeKey;
	prop.latitudeKey=this._latitudeKey;
	prop.objectKey=this._objectKey;
	prop.longitudeFormat=this._longitudeFormat;
	prop.latitudeFormat=this._latitudeFormat;
	prop.marker=this._marker;
	prop.trace=this._trace;
	prop.trailLength=this._trailLength;
	prop.maxItemsSaved=this._maxItemsSaved;
	return prop;
};

MapChartFlowModel.prototype.getRecordByID = function(ID){
	if (typeof ID==='string' && ID.indexOf(this._ID) !== -1) {
		var records=this._records.length;
		var i=0;
		var found=false;
		while (i<records && (!found)){
			if (this._records[i].norrisRecordID===ID){
				found=true;
			}
			else{
				i++;
			}
		}//while
		if (found){//in records[i] there is the ID
			return this._records[i];
		}
		//no ID in records
	}
	return 135;
};

module.exports = MapChartFlowModel;<|endoftext|>


<commit_before>const prompt = require("prompt");

const postTools = require("../source/post.js");
const timeTools = require("../source/time.js");

const requiredProperties = {
    title: {
        pattern: /^.+$/i,
        message: "Post title",
        required: true
    },
    slug: {
        pattern: /^[a-z_-]+$/,
        message: "Post slug",
        required: false
    },
    author: {
        pattern: /^[a-z\.]+$/,
        message: "Author ID",
        required: true
    },
    "description": {
        pattern: /^.+$/,
        message: "Post description",
        required: true
    },
    "date": {
        pattern: /^\d{4}-\d{2}-\d{2} \d{1,2}:\d{2}:\d{2}$/,
        message: "Post description",
        required: false,
        default: timeTools.getDateTime()
    },
    "tags": {
        pattern: /^[a-zA-Z0-9_]+(,[a-zA-Z0-9_]+)*$/i,
        message: "Post tags",
        required: false,
        default: ""
    }
};

function getPostData(options) {
    let postData = {},
        requiresPrompt = false,
        promptSchema = { properties: {} };
    Object.keys(requiredProperties).forEach(function(propName) {
        if (options[propName] && options[propName].length > 0 && typeof options[propName] === "string") {
            postData[propName] = options[propName];
        } else {
            requiresPrompt = true;
            promptSchema.properties[propName] = requiredProperties[propName];
        }
    });
    if (requiresPrompt) {
        return new Promise(function(resolve, reject) {
            prompt.get(promptSchema, function(err, result) {
                if (err) {
                    console.error("Failed retrieving post information", err);
                    (reject)(err);
                } else {
                    postData = Object.assign(postData, result);
                    (resolve)(postData);
                }
            });
        });
    }
    return Promise.resolve(postData);
}

module.exports = function newPost(options) {
    getPostData(options)
        .then(function(data) {
            if (data.slug.trim().length <= 0) {
                data.slug = postTools.convertTitleToSlug(data.title);
            }
            console.log(data);
        });
};
<commit_msg>Sanitised the new post prompting<commit_after>const prompt = require("prompt");

const postTools = require("../source/post.js");
const timeTools = require("../source/time.js");

const requiredProperties = {
    title: {
        pattern: /^.+$/i,
        message: "Post title",
        required: true
    },
    slug: {
        pattern: /^[a-z_-]+$/,
        message: "Post slug",
        required: false
    },
    author: {
        pattern: /^[a-z\.]+$/,
        message: "Author ID",
        required: true
    },
    "description": {
        pattern: /^.+$/,
        message: "Post description",
        required: true
    },
    "date": {
        pattern: /^\d{4}-\d{2}-\d{2} \d{1,2}:\d{2}:\d{2}$/,
        message: "Post description",
        required: false,
        default: timeTools.getDateTime()
    },
    "tags": {
        pattern: /^[a-zA-Z0-9_]+(,[a-zA-Z0-9_]+)*$/i,
        message: "Post tags",
        required: false,
        default: ""
    }
};

function getPostData(options) {
    let postData = {},
        requiresPrompt = false,
        promptSchema = { properties: {} };
    Object.keys(requiredProperties).forEach(function(propName) {
        if (options[propName] && options[propName].length > 0 && typeof options[propName] === "string") {
            postData[propName] = options[propName];
        }
    });
    promptSchema.properties = requiredProperties;
    prompt.override = postData;
    return new Promise(function(resolve, reject) {
        prompt.get(promptSchema, function(err, result) {
            if (err) {
                console.error("Failed retrieving post information", err);
                (reject)(err);
            } else {
                postData = Object.assign(postData, result);
                (resolve)(postData);
            }
        });
    });
}

module.exports = function newPost(options) {
    getPostData(options)
        .then(function(data) {
            if (data.slug.trim().length <= 0) {
                data.slug = postTools.convertTitleToSlug(data.title);
            }
            console.log(data);
        });
};
<|endoftext|>


<commit_before>(function(util) {
  'use strict';
  var score = 0;
  var offending = [];

  var styles = util.getCSSFiles(document.head);
  var docDomain = document.domain;

  var preconnects = util.getResourceHintsHrefs('preconnect');
  var preconnectDomains = preconnects.map(function(preconnect) {
    return util.getHostname(preconnect);
  });
  var domains = [];

  // TODO add H2 info

  styles.forEach(function(style) {
    var styleDomain = util.getHostname(style);
    if (styleDomain !== docDomain) {
      offending.push(style);
      // do we already have it in our domain list, then don't hurt the score
      // TODO hurt ever X amount or something
      if (!util.exists(styleDomain, domains)) {
        // if we preconnect to that URL maybe it can be a little better(?) lets
        // hurt it less.
        score += util.exists(styleDomain, preconnectDomains) ? 5 : 10;
        domains.push(styleDomain);
      }
    }
  });

  return {
    id: 'cssInHeadDomain',
    title: 'Load CSS in head from document domain',
    description: 'Make sure CSS in the head is loaded from same domain as document, in order to have a better user experience and minimize DNS lookups.',
    advice: offending.length > 0 ? 'The page has ' + offending.length + ' CSS files requested from another domain than the main document. Move them to the main domain so that the browser get rid of the DNS lookup.' : '',
    score: Math.max(0, 100 - score),
    weight: 2,
    offending: offending,
    tags: ['performance', 'css']
  };
})(util);
<commit_msg>better text<commit_after>(function(util) {
  'use strict';
  var score = 0;
  var offending = [];

  var styles = util.getCSSFiles(document.head);
  var docDomain = document.domain;

  var preconnects = util.getResourceHintsHrefs('preconnect');
  var preconnectDomains = preconnects.map(function(preconnect) {
    return util.getHostname(preconnect);
  });
  var domains = [];

  // TODO add H2 info

  styles.forEach(function(style) {
    var styleDomain = util.getHostname(style);
    if (styleDomain !== docDomain) {
      offending.push(style);
      // do we already have it in our domain list, then don't hurt the score
      // TODO hurt ever X amount or something
      if (!util.exists(styleDomain, domains)) {
        // if we preconnect to that URL maybe it can be a little better(?) lets
        // hurt it less.
        score += util.exists(styleDomain, preconnectDomains) ? 5 : 10;
        domains.push(styleDomain);
      }
    }
  });

  return {
    id: 'cssInHeadDomain',
    title: 'Load CSS in head from document domain',
    description: 'Make sure CSS in the head is loaded from same domain as document, in order to have a better user experience and minimize DNS lookups.',
    advice: offending.length > 0 ? 'The page has ' + offending.length + ' CSS files requested from another domain than the main document. Move them to the main domain so that the browser don\'t need to do a extra DNS lookup.' : '',
    score: Math.max(0, 100 - score),
    weight: 2,
    offending: offending,
    tags: ['performance', 'css']
  };
})(util);
<|endoftext|>


<commit_before>/**
`d2l-time-picker`
Accessible, Localized Time Picker Input Element

@demo demo/index.html
*/
import '@polymer/polymer/polymer-legacy.js';
import 'd2l-dropdown/d2l-dropdown.js';
import 'd2l-dropdown/d2l-dropdown-content.js';
import { IronA11yKeysBehavior } from '@polymer/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';
import '@polymer/iron-selector/iron-selector.js';
import 'd2l-typography/d2l-typography-shared-styles.js';
import 'd2l-inputs/d2l-input-shared-styles.js';
import {getDocumentLocaleSettings} from '@brightspace-ui/intl/lib/common.js';
import {formatTime, parseTime} from '@brightspace-ui/intl/lib/dateTime.js';
import { Polymer } from '@polymer/polymer/lib/legacy/polymer-fn.js';
import { dom } from '@polymer/polymer/lib/legacy/polymer.dom.js';
const $_documentContainer = document.createElement('template');

$_documentContainer.innerHTML = /*html*/`<dom-module id="d2l-time-picker">
	<template strip-whitespace="">

		<d2l-dropdown no-auto-open="">

				<input
					id="{{id}}-combobox"
					type="text"
					class="d2l-input"
					role="combobox"
					aria-autocomplete="list"
					aria-label$="{{label}}"
					aria-owns$="{{listboxId}}"
					aria-activedescendant$="{{selectedListboxId}}"
					on-focus="_onTimeInputFocused"
					value="{{value::input}}">

			<d2l-dropdown-content
				id="dropdown"
				min-width="[[_dropdownWidth]]"
				max-width="[[_dropdownWidth]]"
				boundary="[[boundary]]"
				align="start"
				no-auto-close=""
				no-pointer=""
				no-auto-focus=""
				no-padding=""
				on-tap="_handleTimesListClick">

						<iron-selector id="{{listboxId}}" role="listbox" selected-attribute="selected">
							<template is="dom-repeat" items="{{times}}">

{{item}}

							</template>
						</iron-selector>

				</d2l-dropdown-content>
		</d2l-dropdown>
		{{timezone}}
	</template>

</dom-module>`;

document.head.appendChild($_documentContainer.content);
var instances = 0;
Polymer({

	is: 'd2l-time-picker',

	behaviors: [
		IronA11yKeysBehavior
	],

	properties: {
		timeInterval: {
			type: Number,
			value: 30
		},
		hours: {
			type: Number,
			notify: true,
			value: 23
		},
		minutes: {
			type: Number,
			notify: true,
			value: 59
		},
		value: {
			type: String,
			notify: true,
			observer: '_valueChanged'
		},
		timezone: String,
		times: {
			type: Object,
			readOnly: true,
			value: []
		},
		boundary: Object,
		_dropdownWidth: String,
		listboxId: String,
		selectedListboxId: String,
		label: String
	},

	observers: [
		'updateValue(hours, minutes)',
		'updateSelection(hours, minutes)',
		'_updateTimes(timeInterval)'
	],

	listeners: {
		'iron-select': '_scrollSelectedItemIntoView'
	},

	keyBindings: {
		'esc': '_onTimeInputKeyPressed',
		'enter': '_onTimeInputKeyPressed',
		'up': '_onTimeInputKeyPressed',
		'down': '_onTimeInputKeyPressed',
		'home': '_onTimeInputKeyPressed',
		'end': '_onTimeInputKeyPressed'
	},

	clear: function() {
		this.updateValue(23, 59);
	},

	// time display logic start

	_valueChanged: function(value) {
		this._closeTimesList(false);
		var date = parseTime(value);
		if (date) {
			this._dontUpdateValue = true;
			this.hours = date.getHours();
			this.minutes = date.getMinutes();
			this.fire('d2l-time-picker-time-changed', {
				hours: this.hours,
				minutes: this.minutes,
				value: this.value
			});
			this._dontUpdateValue = false;
		}
	},

	// Update the value given the specified hours and minutes
	updateValue: function(hours, minutes) {
		hours = +hours;
		minutes = +minutes;
		if (this._dontUpdateValue) {
			return;
		}
		if (hours >= 0 && minutes >= 0) {
			var dateDisplay = new Date(0, 0, 0, hours || 0, minutes || 0, 0, 0);
			this.value = formatTime(dateDisplay);
		}
	},

	attached: function() {
		document.body.addEventListener('focus', this.__onAutoCloseFocus, true);
		document.body.addEventListener('click', this.__onAutoCloseClick, true);
		getDocumentLocaleSettings().addChangeListener(this.__onLanguageChange);
	},

	detached: function() {
		document.body.removeEventListener('focus', this.__onAutoCloseFocus, true);
		document.body.removeEventListener('click', this.__onAutoCloseClick, true);
		getDocumentLocaleSettings().removeChangeListener(this.__onLanguageChange);
	},

	// selection logic start

	ready: function() {
		this.__onAutoCloseFocus = this.__onAutoCloseFocus.bind(this);
		this.__onAutoCloseClick = this.__onAutoCloseClick.bind(this);
		this.__onLanguageChange = this.__onLanguageChange.bind(this);
		this.id = this.id || ('d2l-time-picker-' + instances++);
	},

	focus: function() {
		this.$$('input').focus();
	},

	// Select the item closest the the hours and minutes given
	updateSelection: function(hours, minutes) {
		var index = Math.round((hours * 60 + minutes) / this.timeInterval);
		if (index >= (this.times && this.times.length)) {
			index = 0;
		}
		this.$$('iron-selector').selectIndex(index);
	},

	_updateTimes: function(timeInterval) {
		var date = new Date(0, 0, 0, 0, 0, 0, 0);
		var length = Math.ceil(24 * 60 / timeInterval);
		var times = new Array(length);
		for (var i = 0; i < length; i++) {
			date.setHours(Math.floor(i / (60 / timeInterval)));
			date.setMinutes((i % (60 / timeInterval)) * timeInterval);
			times[i] = formatTime(date);
		}
		this._setTimes(times);
	},

	_openTimesList: function() {
		if (this.$.dropdown.opened) {
			return false;
		}
		this.updateValue(this.hours, this.minutes);
		this.$.dropdown.open();
		// listboxId is not set until the dropdown is open because the listbox
		// isn't actually added to the DOM until dropdown is opened for the first time
		this.listboxId = this.id + '-listbox';
		this.updateSelection(this.hours, this.minutes);
		this._scrollSelectedItemIntoView();
		return true;
	},

	_closeTimesList: function(updateValue) {
		this.$.dropdown.close();
		if (updateValue) {
			var listBox = this.$$('iron-selector');
			if (listBox.selectedItem) {
				this.value = listBox.selectedItem.textContent.trim();
			}
		}
	},

	_scrollSelectedItemIntoView: function() {
		// Wait for dropdown menu height to be calculated
		this.async(function() {
			var dropdownContent = this.$$('d2l-dropdown-content');
			var listBox = this.$$('iron-selector');
			if (dropdownContent && listBox.selectedItem) {
				this.selectedListboxId = this.listboxId + '-' + listBox.selected;
				var height = dropdownContent.height();
				var offsetTop = listBox.selectedItem.offsetTop;
				dropdownContent.scrollTo(offsetTop - height / 2);
			}
		}.bind(this));
	},

	_onBlur: function() {
		this.updateValue(this.hours, this.minutes);
		return this._closeTimesList(false);
	},

	__onAutoCloseFocus: function(e) {
		if (dom(e).rootTarget !== this.$$('input')) {
			this._onBlur();
		}
	},

	__onAutoCloseClick: function() {
		this.async(function() {
			if (dom(this.root).activeElement !== this.$$('input')) {
				this._onBlur();
			}
		}.bind(this), 1);
	},

	__onLanguageChange: function() {
		this._updateTimes(this.timeInterval);
	},

	_onTimeInputFocused: function() {
		this._dropdownWidth = this.$$('input').offsetWidth + 20;
		this._openTimesList();
	},

	_handleTimesListClick: function(e) {
		this.$$('input').focus();
		this._closeTimesList(true);
		e.preventDefault();
	},

	_onTimeInputKeyPressed: function(e) {
		var listBox = this.$$('iron-selector');
		switch (e.detail.key) {
			case 'esc':
				this._closeTimesList(false);
				break;
			case 'enter':
				if (this.$.dropdown.opened) {
					this._closeTimesList(true);
				} else {
					this.updateValue(this.hours, this.minutes);
				}
				break;
			case 'down':
				if (!this._openTimesList()) {
					listBox.selectNext();
				}
				break;
			case 'up':
				if (!this._openTimesList()) {
					listBox.selectPrevious();
				}
				break;
			case 'home':
				if (this.$.dropdown.opened) {
					listBox.selectIndex(0);
				}
				break;
			case 'end':
				if (this.$.dropdown.opened) {
					listBox.selectIndex(listBox.items.length - 1);
				}
				break;
			default:
				return;
		}
		e.preventDefault();
	}
});
<commit_msg>add invalid attribute to toggle aria-invalid on input field<commit_after>/**
`d2l-time-picker`
Accessible, Localized Time Picker Input Element

@demo demo/index.html
*/
import '@polymer/polymer/polymer-legacy.js';
import 'd2l-dropdown/d2l-dropdown.js';
import 'd2l-dropdown/d2l-dropdown-content.js';
import { IronA11yKeysBehavior } from '@polymer/iron-a11y-keys-behavior/iron-a11y-keys-behavior.js';
import '@polymer/iron-selector/iron-selector.js';
import 'd2l-typography/d2l-typography-shared-styles.js';
import 'd2l-inputs/d2l-input-shared-styles.js';
import {getDocumentLocaleSettings} from '@brightspace-ui/intl/lib/common.js';
import {formatTime, parseTime} from '@brightspace-ui/intl/lib/dateTime.js';
import { Polymer } from '@polymer/polymer/lib/legacy/polymer-fn.js';
import { dom } from '@polymer/polymer/lib/legacy/polymer.dom.js';
const $_documentContainer = document.createElement('template');

$_documentContainer.innerHTML = /*html*/`<dom-module id="d2l-time-picker">
	<template strip-whitespace="">

		<d2l-dropdown no-auto-open="">

				<input
					id="{{id}}-combobox"
					type="text"
					class="d2l-input"
					role="combobox"
					aria-autocomplete="list"
					aria-label$="{{label}}"
					aria-owns$="{{listboxId}}"
					aria-activedescendant$="{{selectedListboxId}}"
					aria-invalid$="[[_computedAriaInvalid(invalid)]]"
					on-focus="_onTimeInputFocused"
					value="{{value::input}}">

			<d2l-dropdown-content
				id="dropdown"
				min-width="[[_dropdownWidth]]"
				max-width="[[_dropdownWidth]]"
				boundary="[[boundary]]"
				align="start"
				no-auto-close=""
				no-pointer=""
				no-auto-focus=""
				no-padding=""
				on-tap="_handleTimesListClick">

						<iron-selector id="{{listboxId}}" role="listbox" selected-attribute="selected">
							<template is="dom-repeat" items="{{times}}">

{{item}}

							</template>
						</iron-selector>

				</d2l-dropdown-content>
		</d2l-dropdown>
		{{timezone}}
	</template>

</dom-module>`;

document.head.appendChild($_documentContainer.content);
var instances = 0;
Polymer({

	is: 'd2l-time-picker',

	behaviors: [
		IronA11yKeysBehavior
	],

	properties: {
		timeInterval: {
			type: Number,
			value: 30
		},
		hours: {
			type: Number,
			notify: true,
			value: 23
		},
		minutes: {
			type: Number,
			notify: true,
			value: 59
		},
		value: {
			type: String,
			notify: true,
			observer: '_valueChanged'
		},
		timezone: String,
		times: {
			type: Object,
			readOnly: true,
			value: []
		},
		boundary: Object,
		_dropdownWidth: String,
		listboxId: String,
		selectedListboxId: String,
		label: String,
		invalid: {
			type: Boolean,
			value: false,
			reflectToAttribute: true
		}
	},

	observers: [
		'updateValue(hours, minutes)',
		'updateSelection(hours, minutes)',
		'_updateTimes(timeInterval)'
	],

	listeners: {
		'iron-select': '_scrollSelectedItemIntoView'
	},

	keyBindings: {
		'esc': '_onTimeInputKeyPressed',
		'enter': '_onTimeInputKeyPressed',
		'up': '_onTimeInputKeyPressed',
		'down': '_onTimeInputKeyPressed',
		'home': '_onTimeInputKeyPressed',
		'end': '_onTimeInputKeyPressed'
	},

	clear: function() {
		this.updateValue(23, 59);
	},

	// time display logic start

	_valueChanged: function(value) {
		this._closeTimesList(false);
		var date = parseTime(value);
		if (date) {
			this._dontUpdateValue = true;
			this.hours = date.getHours();
			this.minutes = date.getMinutes();
			this.fire('d2l-time-picker-time-changed', {
				hours: this.hours,
				minutes: this.minutes,
				value: this.value
			});
			this._dontUpdateValue = false;
		}
	},

	// Update the value given the specified hours and minutes
	updateValue: function(hours, minutes) {
		hours = +hours;
		minutes = +minutes;
		if (this._dontUpdateValue) {
			return;
		}
		if (hours >= 0 && minutes >= 0) {
			var dateDisplay = new Date(0, 0, 0, hours || 0, minutes || 0, 0, 0);
			this.value = formatTime(dateDisplay);
		}
	},

	attached: function() {
		document.body.addEventListener('focus', this.__onAutoCloseFocus, true);
		document.body.addEventListener('click', this.__onAutoCloseClick, true);
		getDocumentLocaleSettings().addChangeListener(this.__onLanguageChange);
	},

	detached: function() {
		document.body.removeEventListener('focus', this.__onAutoCloseFocus, true);
		document.body.removeEventListener('click', this.__onAutoCloseClick, true);
		getDocumentLocaleSettings().removeChangeListener(this.__onLanguageChange);
	},

	// selection logic start

	ready: function() {
		this.__onAutoCloseFocus = this.__onAutoCloseFocus.bind(this);
		this.__onAutoCloseClick = this.__onAutoCloseClick.bind(this);
		this.__onLanguageChange = this.__onLanguageChange.bind(this);
		this.id = this.id || ('d2l-time-picker-' + instances++);
	},

	focus: function() {
		this.$$('input').focus();
	},

	// Select the item closest the the hours and minutes given
	updateSelection: function(hours, minutes) {
		var index = Math.round((hours * 60 + minutes) / this.timeInterval);
		if (index >= (this.times && this.times.length)) {
			index = 0;
		}
		this.$$('iron-selector').selectIndex(index);
	},

	_updateTimes: function(timeInterval) {
		var date = new Date(0, 0, 0, 0, 0, 0, 0);
		var length = Math.ceil(24 * 60 / timeInterval);
		var times = new Array(length);
		for (var i = 0; i < length; i++) {
			date.setHours(Math.floor(i / (60 / timeInterval)));
			date.setMinutes((i % (60 / timeInterval)) * timeInterval);
			times[i] = formatTime(date);
		}
		this._setTimes(times);
	},

	_openTimesList: function() {
		if (this.$.dropdown.opened) {
			return false;
		}
		this.updateValue(this.hours, this.minutes);
		this.$.dropdown.open();
		// listboxId is not set until the dropdown is open because the listbox
		// isn't actually added to the DOM until dropdown is opened for the first time
		this.listboxId = this.id + '-listbox';
		this.updateSelection(this.hours, this.minutes);
		this._scrollSelectedItemIntoView();
		return true;
	},

	_closeTimesList: function(updateValue) {
		this.$.dropdown.close();
		if (updateValue) {
			var listBox = this.$$('iron-selector');
			if (listBox.selectedItem) {
				this.value = listBox.selectedItem.textContent.trim();
			}
		}
	},

	_scrollSelectedItemIntoView: function() {
		// Wait for dropdown menu height to be calculated
		this.async(function() {
			var dropdownContent = this.$$('d2l-dropdown-content');
			var listBox = this.$$('iron-selector');
			if (dropdownContent && listBox.selectedItem) {
				this.selectedListboxId = this.listboxId + '-' + listBox.selected;
				var height = dropdownContent.height();
				var offsetTop = listBox.selectedItem.offsetTop;
				dropdownContent.scrollTo(offsetTop - height / 2);
			}
		}.bind(this));
	},

	_onBlur: function() {
		this.updateValue(this.hours, this.minutes);
		return this._closeTimesList(false);
	},

	__onAutoCloseFocus: function(e) {
		if (dom(e).rootTarget !== this.$$('input')) {
			this._onBlur();
		}
	},

	__onAutoCloseClick: function() {
		this.async(function() {
			if (dom(this.root).activeElement !== this.$$('input')) {
				this._onBlur();
			}
		}.bind(this), 1);
	},

	__onLanguageChange: function() {
		this._updateTimes(this.timeInterval);
	},

	_onTimeInputFocused: function() {
		this._dropdownWidth = this.$$('input').offsetWidth + 20;
		this._openTimesList();
	},

	_handleTimesListClick: function(e) {
		this.$$('input').focus();
		this._closeTimesList(true);
		e.preventDefault();
	},

	_onTimeInputKeyPressed: function(e) {
		var listBox = this.$$('iron-selector');
		switch (e.detail.key) {
			case 'esc':
				this._closeTimesList(false);
				break;
			case 'enter':
				if (this.$.dropdown.opened) {
					this._closeTimesList(true);
				} else {
					this.updateValue(this.hours, this.minutes);
				}
				break;
			case 'down':
				if (!this._openTimesList()) {
					listBox.selectNext();
				}
				break;
			case 'up':
				if (!this._openTimesList()) {
					listBox.selectPrevious();
				}
				break;
			case 'home':
				if (this.$.dropdown.opened) {
					listBox.selectIndex(0);
				}
				break;
			case 'end':
				if (this.$.dropdown.opened) {
					listBox.selectIndex(listBox.items.length - 1);
				}
				break;
			default:
				return;
		}
		e.preventDefault();
	},

	_computedAriaInvalid: function(invalid) {
		return invalid ? 'true' : 'false';
	}
});
<|endoftext|>


<commit_before>/* $Id$
 *
 * Include JS file for Tiki 7 Theme Generator
 */

$(document).ready( function() {

	// float button fn
	$(".tgFloatDialog").click(function() {
		openThemeGenDialog($("select[name=themegenerator_theme]").val());
		return false;
	});
});

function initThemeGenDialog() {	// closure for colorpicker code
	//
	// colorpicker for the colour swatches
	// expose options for event fns
	var colorSelector = ".colorSelector > div";
	var colorInput = "input.tgValue";

	var colorPickerListItem = null;		// still sort of globals really
	var colorPickerPreviews = null;		// need to know what was clicked from "inside" colorpicker

	var opts = {
		color: '#888',
		onBeforeShow: function () {
			colorPickerListItem = $(this).parent();	 // store for later (maybe there's a better way?)

			var c = $.trim($(colorInput, colorPickerListItem).val());
			var m = c.match(/^#([0-9A-F]{3})$/i);	// check for only 3 hex chars
			if (m) {
				c = c[0]+c[1]+c[1]+c[2]+c[2]+c[3]+c[3];	// double 'em
			}
			$(this).ColorPickerSetColor(c);

			if ($(".tgLivePreview:checked", $(colorPickerListItem).parent().parent()).length) {
				m = $(colorInput, colorPickerListItem).attr("name");
				m = m.match(/\[(.*?)\]/);	// colour type
				if (m) {m = m[1];}
				if (m === "fgcolors") {
					m = ["color"];
				} else if (m === "bgcolors") {
					m = ["background-color"];
				} else if (m === "bordercolors") {
					m = ["border-top-color", "border-right-color", "border-bottom-color", "border-left-color", "border-color", "border"];
				}
				colorPickerPreviews = [];
				var zall = $(":visible");
				for (var t in m) {
					for (var i = 0; i < zall.length; i++) {
						var $el = $(zall[i]);
						if ($el.length && $el.css(m[t]) && $.Color($el.css(m[t])).toHEX() == c) {
							colorPickerPreviews.push( [ m[t], $el[0] ] );
						}
					}
				}
			} else {
				colorPickerPreviews = null;
			}
		},
		onShow: function (colpkr) {
			$(document.body).css("user-select", "none");
			$(document.body).css("-webkit-user-select", "none");
			$(document.body).css("-moz-user-select", "none");
			$(colpkr).fadeIn(500);
			return false;
		},
		onHide: function (colpkr) {
			$(document.body).css("user-select", "");
			$(document.body).css("-webkit-user-select", "");
			$(document.body).css("-moz-user-select", "");
			colorPickerListItem = null;
			$(colpkr).fadeOut(500);
			return false;
		},
		onChange: function (hsb, hex, rgb) {
			if (colorSelector) {
				$(colorSelector, colorPickerListItem).css('backgroundColor', '#' + hex);
			}
			if (colorInput) {
				$(colorInput, colorPickerListItem).val('#' + hex);
			}
			$(colorPickerListItem).addClass('changed');

			if ($(".tgLivePreview:checked", $(colorPickerListItem).parent().parent()).length) {
				$(colorPickerPreviews).each(function () {
					$(this[1]).css( this[0], '#' + hex);
				});
			}
		},
		onSubmit: function (hsb, hex, rgb, el) {
			this.onChange(hsb, hex, rgb);
		}
	};	// end opts for colorpicker

	$(".tgItem > div", "#tg_section_colors").each(function() {
		var colorItem = this;
		$(this).ColorPicker(opts);
	});

	// checkboxes to select items
	$(".tgItems :checkbox").click(function(e, flip) {
		if (flip) {
			if (!$(this).attr("checked")) { // flip trickery to get trigger to toggle the right classes
				$(this).parent().addClass("selected");
			} else {
				$(this).parent().removeClass("selected");
			}
		} else {
			if ($(this).attr("checked")) { // flip trickery to get trigger to toggle the right classes
				$(this).parent().addClass("selected");
			} else {
				$(this).parent().removeClass("selected");
			}
		}
	});

	// reset button fn
	$(".tgResetSection").click(function() {
		var container = $(this).parent().nextAll("ul");
		$(".tgItem :checkbox:checked", container).each(function() {
			$(this).parent().find("input.tgValue").val($(this).val());
			$(this).parent().find(".colorSelector > div").css("background-color", $(this).val());
			$(this).trigger("click", [true]);
			//$(this).parent().removeClass("selected");
		});
		return false;
	});

	// select modified colours
	$(".tgToggleChanged").click(function() {
		var container = $(this).parent().nextAll("ul");
		$(".tgItem", container).each(function() {
			if ($("input.tgValue", this).val() !== $("input[type=checkbox]", this).val()) {
				$(this).find("input[type=checkbox]").trigger("click", [true]);
			}
		});
	});

	// toggle selection
	$(".tgToggle").click(function() {
		var container = $(this).parent().nextAll("ul");
		$("input[type=checkbox]", container).trigger("click", [true]);
	});

	if (jqueryTiki.ui) {
	}

	// disable for now
	$("input[type=checkbox].tgLivePreview", "#tg_section_typography")
		.attr("checked", "")
		.attr("disabled", "disabled");

	$("label", "#themegenerator_container").each( function() {	// labels go 100% width in dialog
		$(this).css("width", ($(this).text().length * 0.6) + "em");
	});

	$(".themegenerator", "#themegenerator_container").tabs();
	//$("input[name=tg_preview]", "#themegenerator_container").hide();
	$("#tg_css_file", "#themegenerator_container").change(function() {
		//$("#themegenerator_container form").append($("<input type='hidden' name='tg_change_file' value='" + $(this).val() + "' />"));
		//$("#themegenerator_container form").submit();
//		location.replace(location.href + "&tg_css_file=" + escape($(this).val()));
		openThemeGenDialog();
	});
};

function openThemeGenDialog( themename ) {
	if (themename) {
		setCookie("name", themename, "themegen");
	} else {
		themename = getCookie("name", "themegen");
	}
	var l = getCookie("left", "themegen");
	var t = getCookie("top", "themegen");
	var w = getCookie("width", "themegen", 660);
	var h = getCookie("height", "themegen", 500);
	var p = "center"

	if (l !== null && t !== null) {
		p = [ parseInt(l, 10), parseInt(t, 10) ];
	}

	if ($("#themegenerator_container").length === 0) {
		$("body").append($("
<form action='tiki-admin.php?page=look&cookietab=6' method='post'></form>
"));
	}

	$.post("tiki-admin.php?page=look", {tg_open_dialog: true, tg_css_file: $("#tg_css_file", "#themegenerator_container").val()}, function(data) {
			$('#themegenerator_container form').html(data);
			initThemeGenDialog();
			ajaxLoadingHide();
		},
	"html");

	$("#themegenerator_container").dialog({
		width: w,
		height: h,
		position: p,
		modal: false,
		title: tr("Theme Generator:") + " " + themename,
		buttons: {
			Cancel: function () {
				$(this).dialog('close');
				setCookie("themegen", "");
				deleteCookie("themegen");
			},
			'Save': function() {
				var bValid = true;

				if (bValid) {

					$("#themegenerator_container form").submit();
					$(this).dialog('close');
				}
			},
			Preview: function () {

				$("#themegenerator_container form").append($("<input type='hidden' name='tg_preview' value='dialog' />"));
				$("#themegenerator_container form").submit();
				//$(this).dialog('close');
			}
		},
		close: function () {
			setCookie("state", "closed", "themegen");
			$(this).find('input[type=text]').val('').removeClass('ui-state-error');
		},
		open: function( event, ui ) {
			setCookie("state", "open", "themegen");
		},
		dragStop: function(event, ui) {
			setCookie("left", ui.position.left, "themegen");
			setCookie("top", ui.position.top, "themegen");
		},
		resizeStop: function(event, ui) {
			setCookie("width", ui.size.width, "themegen");
			setCookie("height", ui.size.top, "themegen");
		}
	});

	ajaxLoadingShow('themegenerator_container');
}

<commit_msg>[FIX] themegen: parseInts on dialog position and fix pasto<commit_after>/* $Id$
 *
 * Include JS file for Tiki 7 Theme Generator
 */

$(document).ready( function() {

	// float button fn
	$(".tgFloatDialog").click(function() {
		openThemeGenDialog($("select[name=themegenerator_theme]").val());
		return false;
	});
});

function initThemeGenDialog() {	// closure for colorpicker code
	//
	// colorpicker for the colour swatches
	// expose options for event fns
	var colorSelector = ".colorSelector > div";
	var colorInput = "input.tgValue";

	var colorPickerListItem = null;		// still sort of globals really
	var colorPickerPreviews = null;		// need to know what was clicked from "inside" colorpicker

	var opts = {
		color: '#888',
		onBeforeShow: function () {
			colorPickerListItem = $(this).parent();	 // store for later (maybe there's a better way?)

			var c = $.trim($(colorInput, colorPickerListItem).val());
			var m = c.match(/^#([0-9A-F]{3})$/i);	// check for only 3 hex chars
			if (m) {
				c = c[0]+c[1]+c[1]+c[2]+c[2]+c[3]+c[3];	// double 'em
			}
			$(this).ColorPickerSetColor(c);

			if ($(".tgLivePreview:checked", $(colorPickerListItem).parent().parent()).length) {
				m = $(colorInput, colorPickerListItem).attr("name");
				m = m.match(/\[(.*?)\]/);	// colour type
				if (m) {m = m[1];}
				if (m === "fgcolors") {
					m = ["color"];
				} else if (m === "bgcolors") {
					m = ["background-color"];
				} else if (m === "bordercolors") {
					m = ["border-top-color", "border-right-color", "border-bottom-color", "border-left-color", "border-color", "border"];
				}
				colorPickerPreviews = [];
				var zall = $(":visible");
				for (var t in m) {
					for (var i = 0; i < zall.length; i++) {
						var $el = $(zall[i]);
						if ($el.length && $el.css(m[t]) && $.Color($el.css(m[t])).toHEX() == c) {
							colorPickerPreviews.push( [ m[t], $el[0] ] );
						}
					}
				}
			} else {
				colorPickerPreviews = null;
			}
		},
		onShow: function (colpkr) {
			$(document.body).css("user-select", "none");
			$(document.body).css("-webkit-user-select", "none");
			$(document.body).css("-moz-user-select", "none");
			$(colpkr).fadeIn(500);
			return false;
		},
		onHide: function (colpkr) {
			$(document.body).css("user-select", "");
			$(document.body).css("-webkit-user-select", "");
			$(document.body).css("-moz-user-select", "");
			colorPickerListItem = null;
			$(colpkr).fadeOut(500);
			return false;
		},
		onChange: function (hsb, hex, rgb) {
			if (colorSelector) {
				$(colorSelector, colorPickerListItem).css('backgroundColor', '#' + hex);
			}
			if (colorInput) {
				$(colorInput, colorPickerListItem).val('#' + hex);
			}
			$(colorPickerListItem).addClass('changed');

			if ($(".tgLivePreview:checked", $(colorPickerListItem).parent().parent()).length) {
				$(colorPickerPreviews).each(function () {
					$(this[1]).css( this[0], '#' + hex);
				});
			}
		},
		onSubmit: function (hsb, hex, rgb, el) {
			this.onChange(hsb, hex, rgb);
		}
	};	// end opts for colorpicker

	$(".tgItem > div", "#tg_section_colors").each(function() {
		var colorItem = this;
		$(this).ColorPicker(opts);
	});

	// checkboxes to select items
	$(".tgItems :checkbox").click(function(e, flip) {
		if (flip) {
			if (!$(this).attr("checked")) { // flip trickery to get trigger to toggle the right classes
				$(this).parent().addClass("selected");
			} else {
				$(this).parent().removeClass("selected");
			}
		} else {
			if ($(this).attr("checked")) { // flip trickery to get trigger to toggle the right classes
				$(this).parent().addClass("selected");
			} else {
				$(this).parent().removeClass("selected");
			}
		}
	});

	// reset button fn
	$(".tgResetSection").click(function() {
		var container = $(this).parent().nextAll("ul");
		$(".tgItem :checkbox:checked", container).each(function() {
			$(this).parent().find("input.tgValue").val($(this).val());
			$(this).parent().find(".colorSelector > div").css("background-color", $(this).val());
			$(this).trigger("click", [true]);
			//$(this).parent().removeClass("selected");
		});
		return false;
	});

	// select modified colours
	$(".tgToggleChanged").click(function() {
		var container = $(this).parent().nextAll("ul");
		$(".tgItem", container).each(function() {
			if ($("input.tgValue", this).val() !== $("input[type=checkbox]", this).val()) {
				$(this).find("input[type=checkbox]").trigger("click", [true]);
			}
		});
	});

	// toggle selection
	$(".tgToggle").click(function() {
		var container = $(this).parent().nextAll("ul");
		$("input[type=checkbox]", container).trigger("click", [true]);
	});

	if (jqueryTiki.ui) {
	}

	// disable for now
	$("input[type=checkbox].tgLivePreview", "#tg_section_typography")
		.attr("checked", "")
		.attr("disabled", "disabled");

	$("label", "#themegenerator_container").each( function() {	// labels go 100% width in dialog
		$(this).css("width", ($(this).text().length * 0.6) + "em");
	});

	$(".themegenerator", "#themegenerator_container").tabs();
	//$("input[name=tg_preview]", "#themegenerator_container").hide();
	$("#tg_css_file", "#themegenerator_container").change(function() {
		//$("#themegenerator_container form").append($("<input type='hidden' name='tg_change_file' value='" + $(this).val() + "' />"));
		//$("#themegenerator_container form").submit();
//		location.replace(location.href + "&tg_css_file=" + escape($(this).val()));
		openThemeGenDialog();
	});
};

function openThemeGenDialog( themename ) {
	if (themename) {
		setCookie("name", themename, "themegen");
	} else {
		themename = getCookie("name", "themegen");
	}
	var l = getCookie("left", "themegen");
	var t = getCookie("top", "themegen");
	var w = getCookie("width", "themegen", 660);
	var h = getCookie("height", "themegen", 500);
	var p = "center"

	if (l !== null && t !== null) {
		p = [ parseInt(l, 10), parseInt(t, 10) ];
	}

	if ($("#themegenerator_container").length === 0) {
		$("body").append($("
<form action='tiki-admin.php?page=look&cookietab=6' method='post'></form>
"));
	}

	$.post("tiki-admin.php?page=look", {tg_open_dialog: true, tg_css_file: $("#tg_css_file", "#themegenerator_container").val()}, function(data) {
			$('#themegenerator_container form').html(data);
			initThemeGenDialog();
			ajaxLoadingHide();
		},
	"html");

	$("#themegenerator_container").dialog({
		width: w,
		height: h,
		position: p,
		modal: false,
		title: tr("Theme Generator:") + " " + themename,
		buttons: {
			Cancel: function () {
				$(this).dialog('close');
				setCookie("themegen", "");
				deleteCookie("themegen");
			},
			'Save': function() {
				var bValid = true;

				if (bValid) {

					$("#themegenerator_container form").submit();
					$(this).dialog('close');
				}
			},
			Preview: function () {

				$("#themegenerator_container form").append($("<input type='hidden' name='tg_preview' value='dialog' />"));
				$("#themegenerator_container form").submit();
				//$(this).dialog('close');
			}
		},
		close: function () {
			setCookie("state", "closed", "themegen");
			$(this).find('input[type=text]').val('').removeClass('ui-state-error');
		},
		open: function( event, ui ) {
			setCookie("state", "open", "themegen");
		},
		dragStop: function(event, ui) {
			setCookie("left", parseInt(ui.position.left, 10), "themegen");
			setCookie("top", parseInt(ui.position.top, 10), "themegen");
		},
		resizeStop: function(event, ui) {
			setCookie("width", parseInt(ui.size.width, 10), "themegen");
			setCookie("height", parseInt(ui.size.height, 10), "themegen");
		}
	});

	ajaxLoadingShow('themegenerator_container');
}

<|endoftext|>


<commit_before><commit_msg>add more cache control params to s3 upload<commit_after><|endoftext|>


<commit_before><commit_msg>develop_search: minor hover bug fix.<commit_after><|endoftext|>


<commit_before>'use strict';

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

  //$urlRouterProvider.otherwise('/resources/1');

  $stateProvider.state('login', {
      url: "/login",
      controller: 'LoginCtrl',
      templateUrl: '/js/templates/login/index.html',
      resolve: {
        authentication: ['Restangular', function (Restangular) {
          return Restangular.all('users/authenticate');
        }]
      }
    })
    .state('dashboard', {
      url: "/dashboard",
      controller: "DashboardCtrl",
      authenticate: true,
      templateUrl: "/js/templates/login/dashboard.html"
    })
    .state('logout', {
      url: "/logout",
      controller: "LogoutCtrl",
      resolve: {
        authenticate: ['Restangular', function (Restangular) {
          return Restangular.all('users/authenticate');
        }]
      }
    });
}]);<commit_msg>Fixing route.<commit_after>'use strict';

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

  $urlRouterProvider.otherwise('/resources/1');

  $stateProvider.state('login', {
      url: "/login",
      controller: 'LoginCtrl',
      templateUrl: '/js/templates/login/index.html',
      resolve: {
        authentication: ['Restangular', function (Restangular) {
          return Restangular.all('users/authenticate');
        }]
      }
    })
    .state('dashboard', {
      url: "/dashboard",
      controller: "DashboardCtrl",
      authenticate: true,
      templateUrl: "/js/templates/login/dashboard.html"
    })
    .state('logout', {
      url: "/logout",
      controller: "LogoutCtrl",
      resolve: {
        authenticate: ['Restangular', function (Restangular) {
          return Restangular.all('users/authenticate');
        }]
      }
    });
}]);<|endoftext|>