<commit_before>/* global angular */

'use strict';

angular.module('yaru22.angular-timeago', [
]).directive('timeAgo', ['timeAgo', 'nowTime', function (timeAgo, nowTime) {
  return {
    scope : {
      fromTime : '@',
      format : '@'
    },
    restrict: 'EA',
    link: function(scope, elem) {
      var fromTime = timeAgo.parse(scope.fromTime);

      // Track changes to time difference
      scope.$watch(function () {
        return nowTime() - fromTime;
      }, function(value) {
        angular.element(elem).text(timeAgo.inWords(value, fromTime, scope.format));
      });
    }
  };
}]).factory('nowTime', ['$window', '$rootScope', function ($window, $rootScope) {
  var nowTime = Date.now();
  var updateTime = function() {
    $window.setTimeout(function() {
      $rootScope.$apply(function(){
        nowTime = Date.now();
        updateTime();
      });
    }, 1000);
  };
  updateTime();
  return function() {
    return nowTime;
  };
}]).factory('timeAgo', ['$filter', function ($filter) {
  var service = {};

  service.settings = {
    refreshMillis: 60000,
    allowFuture: false,
    overrideLang : null,
    fullDateAfterSeconds : null,
    strings: {
      'it_IT': {
        prefixAgo: null,
        prefixFromNow: null,
        suffixAgo: 'fa',
        suffixFromNow: 'da ora',
        seconds: 'meno di un minuto',
        minute: 'circa un minuto',
        minutes: '%d minuti',
        hour: 'circa un\' ora',
        hours: 'circa %d ore',
        day: 'un giorno',
        days: '%d giorni',
        month: 'circa un mese',
        months: '%d mesi',
        year: 'circa un anno',
        years: '%d anni',
        numbers: []
      },
      'en_US': {
        prefixAgo: null,
        prefixFromNow: null,
        suffixAgo: 'ago',
        suffixFromNow: 'from now',
        seconds: 'less than a minute',
        minute: 'about a minute',
        minutes: '%d minutes',
        hour: 'about an hour',
        hours: 'about %d hours',
        day: 'a day',
        days: '%d days',
        month: 'about a month',
        months: '%d months',
        year: 'about a year',
        years: '%d years',
        numbers: []
      },
      'de_DE': {
        prefixAgo: 'vor',
        prefixFromNow: 'in',
        suffixAgo: null,
        suffixFromNow: null,
        seconds: 'weniger als einer Minute',
        minute: 'ca. einer Minute',
        minutes: '%d Minuten',
        hour: 'ca. einer Stunde',
        hours: 'ca. %d Stunden',
        day: 'einem Tag',
        days: '%d Tagen',
        month: 'ca. einem Monat',
        months: '%d Monaten',
        year: 'ca. einem Jahr',
        years: '%d Jahren',
        numbers: []
      },
      'he_IL': {
        prefixAgo: null,
        prefixFromNow: null,
        suffixAgo: 'לפני',
        suffixFromNow: 'מעכשיו',
        seconds: 'פחות מדקה',
        minute: 'כדקה',
        minutes: '%d דקות',
        hour: 'כשעה',
        hours: 'כ %d שעות',
        day: 'יום',
        days: '%d ימים',
        month: 'כחודש',
        months: '%d חודשים',
        year: 'כשנה',
        years: '%d שנים',
        numbers: []
      },
      'pt_BR': {
        prefixAgo: null,
        prefixFromNow: 'daqui a',
        suffixAgo: 'atrás',
        suffixFromNow: null,
        seconds: 'menos de um minuto',
        minute: 'cerca de um minuto',
        minutes: '%d minutos',
        hour: 'cerca de uma hora',
        hours: 'cerca de %d horas',
        day: 'um dia',
        days: '%d dias',
        month: 'cerca de um mês',
        months: '%d meses',
        year: 'cerca de um ano',
        years: '%d anos',
        numbers: []
      },
      'ca_ES': {
        prefixAgo: 'fa',
        prefixFromNow: 'd\'aquí',
        suffixAgo: null,
        suffixFromNow: null,
        seconds: 'menys d\'un minut',
        minute: 'prop d\'un minut',
        minutes: '%d minuts',
        hour: 'prop d\'una hora',
        hours: 'prop de %d hores',
        day: 'un dia',
        days: '%d dies',
        month: 'prop d\'un mes',
        months: '%d mesos',
        year: 'prop d\'un any',
        years: '%d anys',
        numbers: []
      },
      'fr_FR': {
        prefixAgo: 'il y a',
        prefixFromNow: 'dans',
        suffixAgo: null,
        suffixFromNow: null,
        seconds: 'moins d\'une minute',
        minute: 'environ une minute',
        minutes: '%d minutes',
        hour: 'environ une heure',
        hours: 'environ %d heures',
        day: 'un jour',
        days: '%d jours',
        month: 'environ un mois',
        months: '%d mois',
        year: 'environ un an',
        years: '%d ans',
        numbers: []
      },
      'es_LA': {
        prefixAgo: 'hace',
        prefixFromNow: 'en',
        suffixAgo: null,
        suffixFromNow: null,
        seconds: 'menos de un minuto',
        minute: 'un minuto',
        minutes: '%d minutos',
        hour: 'una hora',
        hours: '%d horas',
        day: 'un día',
        days: '%d días',
        month: 'un mes',
        months: '%d meses',
        year: 'un año',
        years: '%d años',
        numbers: []
      },
      'nl_NL': {
        prefixAgo: null,
        prefixFromNow: 'over',
        suffixAgo: 'geleden',
        suffixFromNow: 'vanaf nu',
        seconds: 'een paar seconden',
        minute: 'ongeveer een minuut',
        minutes: '%d minuten',
        hour: 'een uur',
        hours: '%d uur',
        day: 'een dag',
        days: '%d dagen',
        month: 'een maand',
        months: '%d maanden',
        year: 'een jaar',
        years: '%d jaar',
        numbers: []
      },
      'zh_TW': {
        wordSeparator: '',
        prefixAgo: null,
        prefixFromNow: null,
        suffixAgo: '前',
        suffixFromNow: '後',
        seconds: '少於一分鐘',
        minute: '一分鐘',
        minutes: '%d分鐘',
        hour: '一小時',
        hours: '%d小時',
        day: '一日',
        days: '%d日',
        month: '一個月',
        months: '%d個月',
        year: '一年',
        years: '%d年',
        numbers:
          [ '零', '一', '二', '三', '四', '五' , '六', '七', '八', '九', '十'
          , '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '二十'
          , '廿一', '廿二', '廿三', '廿四', '廿五', '廿六', '廿七', '廿八', '廿九', '三十'
          , '卅一', '卅二', '卅三', '卅四', '卅五', '卅六', '卅七', '卅八', '卅九', '四十'
          , '卌一', '卌二', '卌三', '卌四', '卌五', '卌六', '卌七', '卌八', '卌九', '五十'
          , '五十一', '五十二', '五十三', '五十四', '五十五', '五十六', '五十七', '五十八', '五十九', '六十'
          , '六十一', '六十二', '六十三', '六十四', '六十五', '六十六', '六十七', '六十八', '六十九', '七十'
          , '七十一', '七十二', '七十三', '七十四', '七十五', '七十六', '七十七', '七十八', '七十九', '八十'
          , '八十一', '八十二', '八十三', '八十四', '八十五', '八十六', '八十七', '八十八', '八十九', '九十'
          , '九十一', '九十二', '九十三', '九十四', '九十五', '九十六', '九十七', '九十八', '九十九', '一百'
          ]
      }
    }
  };

  service.inWords = function (distanceMillis, fromTime, format, timezone) {

    var fullDateAfterSeconds = parseInt(service.settings.fullDateAfterSeconds, 10);

    if (!isNaN(fullDateAfterSeconds)) {
      var fullDateAfterMillis = fullDateAfterSeconds * 1000;
      if ((distanceMillis >= 0 && fullDateAfterMillis <= distanceMillis) ||
          (distanceMillis < 0 && fullDateAfterMillis >= distanceMillis)) {
        if (format) {
          return $filter('date')(fromTime, format, timezone);
        }
        return fromTime;
      }
    }

    var overrideLang = service.settings.overrideLang;
    var documentLang = document.documentElement.lang;
    var sstrings = service.settings.strings;
    var lang, $l;

    if (typeof sstrings[overrideLang] !== 'undefined') {
      lang = overrideLang;
      $l = sstrings[overrideLang];
    } else if (typeof sstrings[documentLang] !== 'undefined') {
      lang = documentLang;
      $l = sstrings[documentLang];
    } else {
      lang = 'en_US';
      $l = sstrings[lang];
    }

    var prefix = $l.prefixAgo;
    var suffix = $l.suffixAgo;
    if (service.settings.allowFuture) {
      if (distanceMillis < 0) {
        prefix = $l.prefixFromNow;
        suffix = $l.suffixFromNow;
      }
    }

    var seconds = Math.abs(distanceMillis) / 1000;
    var minutes = seconds / 60;
    var hours = minutes / 60;
    var days = hours / 24;
    var years = days / 365;

    function substitute(stringOrFunction, number) {
      var string = angular.isFunction(stringOrFunction) ?
      stringOrFunction(number, distanceMillis) : stringOrFunction;
      var value = ($l.numbers && $l.numbers[number]) || number;
      return string.replace(/%d/i, value);
    }

    var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
        seconds < 90 && substitute($l.minute, 1) ||
        minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
        minutes < 90 && substitute($l.hour, 1) ||
        hours < 24 && substitute($l.hours, Math.round(hours)) ||
        hours < 42 && substitute($l.day, 1) ||
        days < 30 && substitute($l.days, Math.round(days)) ||
        days < 45 && substitute($l.month, 1) ||
        days < 365 && substitute($l.months, Math.round(days / 30)) ||
        years < 1.5 && substitute($l.year, 1) ||
        substitute($l.years, Math.round(years));

    var separator = $l.wordSeparator === undefined ?  ' ' : $l.wordSeparator;
    if(lang === 'he_IL'){
      return [prefix, suffix, words].join(separator).trim();
    } else {
      return [prefix, words, suffix].join(separator).trim();
    }
  };

  service.parse = function (input) {
    if (input instanceof Date){
      return input;
    } else if (angular.isNumber(input)) {
      return new Date(input);
    } else if (/^\d+$/.test(input)) {
      return new Date(parseInt(input, 10));
    } else {
      var s = (input || '').trim();
      s = s.replace(/\.\d+/, ''); // remove milliseconds
      s = s.replace(/-/, '/').replace(/-/, '/');
      s = s.replace(/T/, ' ').replace(/Z/, ' UTC');
      s = s.replace(/([\+\-]\d\d)\:?(\d\d)/, ' $1$2'); // -04:00 -> -0400
      return new Date(s);
    }
  };

  return service;
}]).filter('timeAgo', ['nowTime', 'timeAgo', function (nowTime, timeAgo) {
  return function (value, format, timezone) {
    var fromTime = timeAgo.parse(value);
    var diff = nowTime() - fromTime;
    return timeAgo.inWords(diff, fromTime, format, timezone);
  };
}]);
<commit_msg>Use $timeout instead of $window and $rootScope<commit_after>/* global angular */

'use strict';

angular.module('yaru22.angular-timeago', [
]).directive('timeAgo', ['timeAgo', 'nowTime', function (timeAgo, nowTime) {
  return {
    scope : {
      fromTime : '@',
      format : '@'
    },
    restrict: 'EA',
    link: function(scope, elem) {
      var fromTime = timeAgo.parse(scope.fromTime);

      // Track changes to time difference
      scope.$watch(function () {
        return nowTime() - fromTime;
      }, function(value) {
        angular.element(elem).text(timeAgo.inWords(value, fromTime, scope.format));
      });
    }
  };
}]).factory('nowTime', function ($timeout) {
  var nowTime;
  var updateTime = function () {
    nowTime = Date.now();
    $timeout(updateTime, 1000);
  };
  updateTime();
  return function () {
    return nowTime;
  };
}).factory('timeAgo', ['$filter', function ($filter) {
  var service = {};

  service.settings = {
    refreshMillis: 60000,
    allowFuture: false,
    overrideLang : null,
    fullDateAfterSeconds : null,
    strings: {
      'it_IT': {
        prefixAgo: null,
        prefixFromNow: null,
        suffixAgo: 'fa',
        suffixFromNow: 'da ora',
        seconds: 'meno di un minuto',
        minute: 'circa un minuto',
        minutes: '%d minuti',
        hour: 'circa un\' ora',
        hours: 'circa %d ore',
        day: 'un giorno',
        days: '%d giorni',
        month: 'circa un mese',
        months: '%d mesi',
        year: 'circa un anno',
        years: '%d anni',
        numbers: []
      },
      'en_US': {
        prefixAgo: null,
        prefixFromNow: null,
        suffixAgo: 'ago',
        suffixFromNow: 'from now',
        seconds: 'less than a minute',
        minute: 'about a minute',
        minutes: '%d minutes',
        hour: 'about an hour',
        hours: 'about %d hours',
        day: 'a day',
        days: '%d days',
        month: 'about a month',
        months: '%d months',
        year: 'about a year',
        years: '%d years',
        numbers: []
      },
      'de_DE': {
        prefixAgo: 'vor',
        prefixFromNow: 'in',
        suffixAgo: null,
        suffixFromNow: null,
        seconds: 'weniger als einer Minute',
        minute: 'ca. einer Minute',
        minutes: '%d Minuten',
        hour: 'ca. einer Stunde',
        hours: 'ca. %d Stunden',
        day: 'einem Tag',
        days: '%d Tagen',
        month: 'ca. einem Monat',
        months: '%d Monaten',
        year: 'ca. einem Jahr',
        years: '%d Jahren',
        numbers: []
      },
      'he_IL': {
        prefixAgo: null,
        prefixFromNow: null,
        suffixAgo: 'לפני',
        suffixFromNow: 'מעכשיו',
        seconds: 'פחות מדקה',
        minute: 'כדקה',
        minutes: '%d דקות',
        hour: 'כשעה',
        hours: 'כ %d שעות',
        day: 'יום',
        days: '%d ימים',
        month: 'כחודש',
        months: '%d חודשים',
        year: 'כשנה',
        years: '%d שנים',
        numbers: []
      },
      'pt_BR': {
        prefixAgo: null,
        prefixFromNow: 'daqui a',
        suffixAgo: 'atrás',
        suffixFromNow: null,
        seconds: 'menos de um minuto',
        minute: 'cerca de um minuto',
        minutes: '%d minutos',
        hour: 'cerca de uma hora',
        hours: 'cerca de %d horas',
        day: 'um dia',
        days: '%d dias',
        month: 'cerca de um mês',
        months: '%d meses',
        year: 'cerca de um ano',
        years: '%d anos',
        numbers: []
      },
      'ca_ES': {
        prefixAgo: 'fa',
        prefixFromNow: 'd\'aquí',
        suffixAgo: null,
        suffixFromNow: null,
        seconds: 'menys d\'un minut',
        minute: 'prop d\'un minut',
        minutes: '%d minuts',
        hour: 'prop d\'una hora',
        hours: 'prop de %d hores',
        day: 'un dia',
        days: '%d dies',
        month: 'prop d\'un mes',
        months: '%d mesos',
        year: 'prop d\'un any',
        years: '%d anys',
        numbers: []
      },
      'fr_FR': {
        prefixAgo: 'il y a',
        prefixFromNow: 'dans',
        suffixAgo: null,
        suffixFromNow: null,
        seconds: 'moins d\'une minute',
        minute: 'environ une minute',
        minutes: '%d minutes',
        hour: 'environ une heure',
        hours: 'environ %d heures',
        day: 'un jour',
        days: '%d jours',
        month: 'environ un mois',
        months: '%d mois',
        year: 'environ un an',
        years: '%d ans',
        numbers: []
      },
      'es_LA': {
        prefixAgo: 'hace',
        prefixFromNow: 'en',
        suffixAgo: null,
        suffixFromNow: null,
        seconds: 'menos de un minuto',
        minute: 'un minuto',
        minutes: '%d minutos',
        hour: 'una hora',
        hours: '%d horas',
        day: 'un día',
        days: '%d días',
        month: 'un mes',
        months: '%d meses',
        year: 'un año',
        years: '%d años',
        numbers: []
      },
      'nl_NL': {
        prefixAgo: null,
        prefixFromNow: 'over',
        suffixAgo: 'geleden',
        suffixFromNow: 'vanaf nu',
        seconds: 'een paar seconden',
        minute: 'ongeveer een minuut',
        minutes: '%d minuten',
        hour: 'een uur',
        hours: '%d uur',
        day: 'een dag',
        days: '%d dagen',
        month: 'een maand',
        months: '%d maanden',
        year: 'een jaar',
        years: '%d jaar',
        numbers: []
      },
      'zh_TW': {
        wordSeparator: '',
        prefixAgo: null,
        prefixFromNow: null,
        suffixAgo: '前',
        suffixFromNow: '後',
        seconds: '少於一分鐘',
        minute: '一分鐘',
        minutes: '%d分鐘',
        hour: '一小時',
        hours: '%d小時',
        day: '一日',
        days: '%d日',
        month: '一個月',
        months: '%d個月',
        year: '一年',
        years: '%d年',
        numbers:
          [ '零', '一', '二', '三', '四', '五' , '六', '七', '八', '九', '十'
          , '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '二十'
          , '廿一', '廿二', '廿三', '廿四', '廿五', '廿六', '廿七', '廿八', '廿九', '三十'
          , '卅一', '卅二', '卅三', '卅四', '卅五', '卅六', '卅七', '卅八', '卅九', '四十'
          , '卌一', '卌二', '卌三', '卌四', '卌五', '卌六', '卌七', '卌八', '卌九', '五十'
          , '五十一', '五十二', '五十三', '五十四', '五十五', '五十六', '五十七', '五十八', '五十九', '六十'
          , '六十一', '六十二', '六十三', '六十四', '六十五', '六十六', '六十七', '六十八', '六十九', '七十'
          , '七十一', '七十二', '七十三', '七十四', '七十五', '七十六', '七十七', '七十八', '七十九', '八十'
          , '八十一', '八十二', '八十三', '八十四', '八十五', '八十六', '八十七', '八十八', '八十九', '九十'
          , '九十一', '九十二', '九十三', '九十四', '九十五', '九十六', '九十七', '九十八', '九十九', '一百'
          ]
      }
    }
  };

  service.inWords = function (distanceMillis, fromTime, format, timezone) {

    var fullDateAfterSeconds = parseInt(service.settings.fullDateAfterSeconds, 10);

    if (!isNaN(fullDateAfterSeconds)) {
      var fullDateAfterMillis = fullDateAfterSeconds * 1000;
      if ((distanceMillis >= 0 && fullDateAfterMillis <= distanceMillis) ||
          (distanceMillis < 0 && fullDateAfterMillis >= distanceMillis)) {
        if (format) {
          return $filter('date')(fromTime, format, timezone);
        }
        return fromTime;
      }
    }

    var overrideLang = service.settings.overrideLang;
    var documentLang = document.documentElement.lang;
    var sstrings = service.settings.strings;
    var lang, $l;

    if (typeof sstrings[overrideLang] !== 'undefined') {
      lang = overrideLang;
      $l = sstrings[overrideLang];
    } else if (typeof sstrings[documentLang] !== 'undefined') {
      lang = documentLang;
      $l = sstrings[documentLang];
    } else {
      lang = 'en_US';
      $l = sstrings[lang];
    }

    var prefix = $l.prefixAgo;
    var suffix = $l.suffixAgo;
    if (service.settings.allowFuture) {
      if (distanceMillis < 0) {
        prefix = $l.prefixFromNow;
        suffix = $l.suffixFromNow;
      }
    }

    var seconds = Math.abs(distanceMillis) / 1000;
    var minutes = seconds / 60;
    var hours = minutes / 60;
    var days = hours / 24;
    var years = days / 365;

    function substitute(stringOrFunction, number) {
      var string = angular.isFunction(stringOrFunction) ?
      stringOrFunction(number, distanceMillis) : stringOrFunction;
      var value = ($l.numbers && $l.numbers[number]) || number;
      return string.replace(/%d/i, value);
    }

    var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
        seconds < 90 && substitute($l.minute, 1) ||
        minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
        minutes < 90 && substitute($l.hour, 1) ||
        hours < 24 && substitute($l.hours, Math.round(hours)) ||
        hours < 42 && substitute($l.day, 1) ||
        days < 30 && substitute($l.days, Math.round(days)) ||
        days < 45 && substitute($l.month, 1) ||
        days < 365 && substitute($l.months, Math.round(days / 30)) ||
        years < 1.5 && substitute($l.year, 1) ||
        substitute($l.years, Math.round(years));

    var separator = $l.wordSeparator === undefined ?  ' ' : $l.wordSeparator;
    if(lang === 'he_IL'){
      return [prefix, suffix, words].join(separator).trim();
    } else {
      return [prefix, words, suffix].join(separator).trim();
    }
  };

  service.parse = function (input) {
    if (input instanceof Date){
      return input;
    } else if (angular.isNumber(input)) {
      return new Date(input);
    } else if (/^\d+$/.test(input)) {
      return new Date(parseInt(input, 10));
    } else {
      var s = (input || '').trim();
      s = s.replace(/\.\d+/, ''); // remove milliseconds
      s = s.replace(/-/, '/').replace(/-/, '/');
      s = s.replace(/T/, ' ').replace(/Z/, ' UTC');
      s = s.replace(/([\+\-]\d\d)\:?(\d\d)/, ' $1$2'); // -04:00 -> -0400
      return new Date(s);
    }
  };

  return service;
}]).filter('timeAgo', ['nowTime', 'timeAgo', function (nowTime, timeAgo) {
  return function (value, format, timezone) {
    var fromTime = timeAgo.parse(value);
    var diff = nowTime() - fromTime;
    return timeAgo.inWords(diff, fromTime, format, timezone);
  };
}]);
<|endoftext|>


<commit_before>import Joi from 'joi'
import { isBuildStatus, renderBuildStatusBadge } from '../build-status.js'
import { BaseJsonService } from '../index.js'

const schema = Joi.object({
  subject: Joi.string().required(),
  status: Joi.alternatives()
    .try(isBuildStatus, Joi.equal('unknown'))
    .required(),
}).required()
const queryParamSchema = Joi.object({
  task: Joi.string(),
  script: Joi.string(),
}).required()

export default class Cirrus extends BaseJsonService {
  static category = 'build'
  static route = {
    base: 'cirrus',
    pattern: 'github/:user/:repo/:branch*',
    queryParamSchema,
  }

  static examples = [
    {
      title: 'Cirrus CI - Base Branch Build Status',
      namedParams: { user: 'flutter', repo: 'flutter' },
      pattern: 'github/:user/:repo',
      queryParams: { task: 'analyze', script: 'test' },
      staticPreview: this.render({ status: 'passing' }),
    },
    {
      title: 'Cirrus CI - Specific Branch Build Status',
      pattern: 'github/:user/:repo/:branch',
      namedParams: { user: 'flutter', repo: 'flutter', branch: 'master' },
      queryParams: { task: 'analyze', script: 'test' },
      staticPreview: this.render({ status: 'passing' }),
    },
    {
      title: 'Cirrus CI - Specific Task Build Status',
      pattern: 'github/:user/:repo',
      queryParams: { task: 'analyze' },
      namedParams: { user: 'flutter', repo: 'flutter' },
      staticPreview: this.render({ subject: 'analyze', status: 'passing' }),
    },
    {
      title: 'Cirrus CI - Task and Script Build Status',
      pattern: 'github/:user/:repo',
      queryParams: { task: 'analyze', script: 'test' },
      namedParams: {
        user: 'flutter',
        repo: 'flutter',
      },
      staticPreview: this.render({ subject: 'test', status: 'passing' }),
    },
  ]

  static defaultBadgeData = { label: 'build' }

  static render({ subject, status }) {
    return renderBuildStatusBadge({ label: subject, status })
  }

  async handle({ user, repo, branch }, { script, task }) {
    const json = await this._requestJson({
      schema,
      url: `https://api.cirrus-ci.com/github/${user}/${repo}.json`,
      options: { searchParams: { branch, script, task } },
    })

    return this.constructor.render(json)
  }
}
<commit_msg>Improve Cirrus CI examples (#7719)<commit_after>import Joi from 'joi'
import { isBuildStatus, renderBuildStatusBadge } from '../build-status.js'
import { BaseJsonService } from '../index.js'

const schema = Joi.object({
  subject: Joi.string().required(),
  status: Joi.alternatives()
    .try(isBuildStatus, Joi.equal('unknown'))
    .required(),
}).required()
const queryParamSchema = Joi.object({
  task: Joi.string(),
  script: Joi.string(),
}).required()

export default class Cirrus extends BaseJsonService {
  static category = 'build'
  static route = {
    base: 'cirrus',
    pattern: 'github/:user/:repo/:branch*',
    queryParamSchema,
  }

  static examples = [
    {
      title: 'Cirrus CI - Base Branch Build Status',
      namedParams: { user: 'flutter', repo: 'flutter' },
      pattern: 'github/:user/:repo',
      staticPreview: this.render({ status: 'passing' }),
    },
    {
      title: 'Cirrus CI - Specific Branch Build Status',
      pattern: 'github/:user/:repo/:branch',
      namedParams: { user: 'flutter', repo: 'flutter', branch: 'master' },
      staticPreview: this.render({ status: 'passing' }),
    },
    {
      title: 'Cirrus CI - Specific Task Build Status',
      pattern: 'github/:user/:repo',
      queryParams: { task: 'build_docker' },
      namedParams: { user: 'flutter', repo: 'cocoon' },
      staticPreview: this.render({
        subject: 'build_docker',
        status: 'passing',
      }),
    },
    {
      title: 'Cirrus CI - Task and Script Build Status',
      pattern: 'github/:user/:repo',
      queryParams: { task: 'build_docker', script: 'test' },
      namedParams: { user: 'flutter', repo: 'cocoon' },
      staticPreview: this.render({ subject: 'test', status: 'passing' }),
    },
  ]

  static defaultBadgeData = { label: 'build' }

  static render({ subject, status }) {
    return renderBuildStatusBadge({ label: subject, status })
  }

  async handle({ user, repo, branch }, { script, task }) {
    const json = await this._requestJson({
      schema,
      url: `https://api.cirrus-ci.com/github/${user}/${repo}.json`,
      options: { searchParams: { branch, script, task } },
    })

    return this.constructor.render(json)
  }
}
<|endoftext|>


<commit_before>export { ValueBoundsSet };

import * as FloatValue from "../Unpacker/FloatValue.js";
import * as ValueDesc from "../Unpacker/ValueDesc.js";
import * as Weights from "../Unpacker/Weights.js";
import * as ActivationEscapeing from "./ActivationEscapeing.js";

/**
 * Several element value bounds for convolution-bias-activation operations.
 *
 *
 * @member {FloatValue.Bounds} input
 *   The bounds of the input element value. Or say, the domain of the convolution-bias-activation.
 *
 * @member {FloatValue.Bounds} beforeActivation
 *   The bounds of the element value after convolution and bias operation (but before activation).
 *
 * @member {FloatValue.Bounds} output
 *   The bounds of the output element value. Or say, the range of the convolution-bias-activation.
 *
 * @member {ActivationEscapeing.ScaleTranslateSet} activationEscaping_ScaleTranslateSet
 *   The scale-translate for moving beforeActivation bounds into the linear domain of the activation function. That is, for
 * letting beforeActivation escape from activation function's non-linear domain.
 */
class ValueBoundsSet {

  /**
   */
  constructor() {
    this.input = Weights.Base.ValueBounds.clone();
    this.beforeActivation = Weights.Base.ValueBounds.clone();
    this.output = Weights.Base.ValueBounds.clone();
    this.activationEscaping_ScaleTranslateSet = new ActivationEscapeing.ScaleTranslateSet();
  }

  clone() {
    let result = new ValueBoundsSet();
    result.setBy_ValueBoundsSet( this );
    return result;
  }

  /**
   * @param {ValueBoundsSet} another
   *   The ValueBoundsSet to be copied.
   */
  setBy_ValueBoundsSet( another ) {
    this.input.set_Bounds( another.input );
    this.beforeActivation.set_Bounds( another.beforeActivation );
    this.output.set_Bounds( another.output );
    this.activationEscaping_ScaleTranslateSet.setBy_ScaleTranslateSet( another.activationEscaping_ScaleTranslateSet );
  }

  /**
   * @param {FloatValue.Bounds} aBounds
   *   Set this.input, this.beforeActivation, this.output all the same as the specified aBounds. Set the
   * this.activationEscaping_ScaleTranslateSet to default ( 1, 0 );
   */
  resetBy_Bounds( aBounds ) {
    this.input.set_Bounds( aBounds );
    this.beforeActivation.set_Bounds( aBounds );
    this.output.set_Bounds( aBounds );
    this.activationEscaping_ScaleTranslateSet.reset( 1, 0 ); // scale 1 and translate 0. (i.e. no scale and no translate.)
  }

  /**
   * Determine output value bounds. Set this.output according to this.beforeActivation and the specified nActivationId.
   *
   * @param {number} nActivationId
   *   The activation function id (ValueDesc.ActivationFunction.Singleton.Ids.Xxx) after the bias operation.
   *     - If ( nActivationId == ValueDesc.ActivationFunction.Singletion.Ids.NONE ), this.output will be the same as this.beforeActivation.
   *     - Otherwise, this.output will be the same as the output range of the activation function.
   */
  set_output_byActivationId( nActivationId ) {

    let info = ValueDesc.ActivationFunction.Singleton.getInfoById( nActivationId );

    // If there is no activation function, the output range is determined by input domain, filters, biases.
    if ( !info ) { // e.g. ValueDesc.ActivationFunction.Singleton.Ids.NONE
      this.output.set_Bounds( this.beforeActivation );

    // Otherwise, the activation function dominates the output range.
    } else {
      this.output.set_Bounds( info.outputRange );
    }

  }

}

<commit_msg>Update ConvBiasActivation.js<commit_after>export { ValueBoundsSet };

import * as FloatValue from "../Unpacker/FloatValue.js";
import * as ValueDesc from "../Unpacker/ValueDesc.js";
import * as Weights from "../Unpacker/Weights.js";
import * as ActivationEscapeing from "./ActivationEscapeing.js";

/**
 * Several element value bounds for convolution-bias-activation operations.
 *
 *
 * @member {FloatValue.Bounds} input
 *   The bounds of the input element value. Or say, the domain of the convolution-bias-activation.
 *
 * @member {FloatValue.Bounds} beforeActivation
 *   The bounds of the element value after convolution and bias operation (but before activation).
 *
 * @member {FloatValue.Bounds} output
 *   The bounds of the output element value. Or say, the range of the convolution-bias-activation.
 *
 * @member {ActivationEscapeing.ScaleTranslateSet} activationEscaping_ScaleTranslateSet
 *   The scale-translate for moving beforeActivation bounds into the linear domain of the activation function. That is, for
 * letting beforeActivation escape from activation function's non-linear domain.
 */
class ValueBoundsSet {

  /**
   */
  constructor() {
    this.input = Weights.Base.ValueBounds.clone();
    this.beforeActivation = Weights.Base.ValueBounds.clone();
    this.output = Weights.Base.ValueBounds.clone();
    this.activationEscaping_ScaleTranslateSet = new ActivationEscapeing.ScaleTranslateSet();
  }

  clone() {
    let result = new ValueBoundsSet();
    result.setBy_ValueBoundsSet( this );
    return result;
  }

  /**
   * @param {ValueBoundsSet} another
   *   The ValueBoundsSet to be copied.
   */
  setBy_ValueBoundsSet( another ) {
    this.input.set_Bounds( another.input );
    this.beforeActivation.set_Bounds( another.beforeActivation );
    this.output.set_Bounds( another.output );
    this.activationEscaping_ScaleTranslateSet.setBy_ScaleTranslateSet( another.activationEscaping_ScaleTranslateSet );
  }

  /**
   * @param {FloatValue.Bounds} aBounds
   *   Set this.input, this.beforeActivation, this.output all the same as the specified aBounds. Set the
   * this.activationEscaping_ScaleTranslateSet to default ( 1, 0 );
   */
  resetBy_Bounds( aBounds ) {
    this.input.set_Bounds( aBounds );
    this.beforeActivation.set_Bounds( aBounds );
    this.output.set_Bounds( aBounds );
    this.activationEscaping_ScaleTranslateSet.reset( 1, 0 ); // scale 1 and translate 0. (i.e. no scale and no translate.)
  }

  /**
   * Determine output value bounds. Set this.output according to this.beforeActivation and the specified nActivationId.
   *
   * @param {number} nActivationId
   *   The activation function id (ValueDesc.ActivationFunction.Singleton.Ids.Xxx) after the bias operation.
   *     - If ( nActivationId == ValueDesc.ActivationFunction.Singletion.Ids.NONE ), this.output will be the same as this.beforeActivation.
   *     - Otherwise, this.output will be the same as the output range of the activation function.
   */
  set_output_byActivationId( nActivationId ) {

    // If there is no activation function, the output range is determined by input domain, filters, biases.
    if ( nActivationId == ValueDesc.ActivationFunction.Singleton.Ids.NONE ) {
      this.output.set_Bounds( this.beforeActivation );

    // Otherwise, the activation function dominates the output range.
    } else {
      let info = ValueDesc.ActivationFunction.Singleton.getInfoById( nActivationId );
      this.output.set_Bounds( info.outputRange );
    }

  }

}

<|endoftext|>


<commit_before>/*jslint browser: true, nomen: true */

(function (flow, $, girder) {
    'use strict';

    $(document).ready(function () {
        girder.apiRoot = '/girder/api/v1';
        var app = new flow.App({
            el: 'body'
        });

        // Lookup the ID of the analysis that we wish to perform.
        app.analysisName = "testSlowdown-app";
        girder.restRequest({
            path: 'resource/search',
            data: {
                q: app.analysisName,
                types: JSON.stringify(["item"])
            }
        }).done(function (results) {
            app.analysisId = results["item"][0]._id;
            app.readyToAnalyze();
            console.log(app.analysisId);
        });

        app.readyToAnalyze = function () {
            if ("tree" in this && "analysisId" in this) {
                d3.select("#analyze").classed('disabled', false);
            }
        };

        // override upload function for simple mode
        flow.DatasetManagementView.prototype.upload = function (file) {
            var reader = new FileReader();

            reader.onload = _.bind(function (e) {
                var dataset = {
                        name: file.name,
                        data: e.target.result
                    },
                    extension = file.name.split('.'),
                    typeFormat;

                extension = extension[extension.length - 1];
                typeFormat = flow.getTypeFormatsFromExtension(extension)[0];
                typeFormat = {type: typeFormat.type, format: typeFormat.format};
                _.extend(dataset, typeFormat);
                dataset = new Backbone.Model(dataset);

                if (typeFormat.type == "tree") {
                    app.tree = dataset.get('data');
                    d3.select("#tree-name").html('Tree: ' + file.name + ' ');
                    $("#column-input").text("Click GO! to make plot...");
                }
                app.readyToAnalyze();

                this.datasets.off('add', null, 'set-collection').add(dataset);
            }, this);

            reader.readAsText(file);
        };

        $("#analyze").click(function() {
            $("#analyze").attr("disabled", "disabled");
            $("#analyze").text("Re-run");
            $("#notice").text("Performing analysis...");

            var inputs = {
                tree:   {type: "tree",   format: "newick",           data: app.tree},
            };

            var outputs = {
                lttdata: {type: "table",  format: "rows"},
                slowdownResult: {type: "table",  format: "rows"}
            };

            flow.performAnalysis(app.analysisId, inputs, outputs,
                _.bind(function (error, result) {
                    app.taskId = result._id;
                    setTimeout(_.bind(app.checkResult, app), 1000);
                }, app));

        app.checkResult = function () {
            var check_url = '/item/' + this.analysisId + '/flow/' + this.taskId + '/status'
            girder.restRequest({path: check_url}).done(_.bind(function (result) {
                console.log(result.status);
                if (result.status === 'SUCCESS') {
                    // get result data
                    var result_url = '/item/' + this.analysisId + '/flow/' + this.taskId + '/result'
                    girder.restRequest({path: result_url}).done(_.bind(function (data) {
                        app.lttCoords = data.result.lttdata.data;
                        app.slowdownResult = data.result.slowdownResult;
                        console.log(app.slowdownResult);

                        // render results
                        var newRow = $('

');
                        $(".main-content").append(newRow);
                        newRow.append('
');

                        var result = $('

');
                        newRow.append(result);
                        result.append("
Results:<\h2>");

                        result.append("Analysis type: ");
                        result.append("gamma test

");

                        result.append("Number of species", data.result.slowdownResult.data.rows[0]["nSpecies"], "
")

                        function getCol(matrix, col){
                            var column = [];
                            for(var i=0; i<matrix.length; i++){
                               column.push(matrix[i][col]);
                            }
                            return column;
                         }

                        var N = getCol(app.lttCoords.rows, "N");
                        var time = getCol(app.lttCoords.rows, "time")

                        console.log(N);
                        console.log(time);

                        var trace1 = {
                          x: time,
                          y: N,
                          mode: 'lines',
                          type: 'scatter'
                        };

                        var data = [trace1];

                        var layout = {
                          title:'LTT plot',
                          xaxis: {
                            title: 'Time before present',
                            showgrid: false,
                            zeroline: false,
                            nticks: 10
                          },
                          yaxis: {
                            title: 'Number of reconstructed lineages',
                            type: 'log',
                            showgrid: false,
                            zeroline: false,
                            nticks: 4
                          },
                        };

                        Plotly.newPlot('ltt-plot', data, layout);

                        $("#analyze").removeAttr("disabled");
                        $("#notice").text("Analysis succeeded!");

                    }, this));

                } else if (result.status === 'FAILURE') {
                    $("#analyze").removeAttr("disabled");
                    $("#notice").text("Analysis failed. " + result.message);
                } else {
                    setTimeout(_.bind(this.checkResult, this), 1000);
                }
            }, this));
        };

        });

        $("#help").click(function() {
            $("#upload").popover({
                title: 'Step #1',
                content: 'Upload your table (csv or tsv) and tree (newick) here',
                placement: 'bottom',
                trigger: 'manual'
            });
            $("#upload").popover('toggle');
            $("#analyze").popover({
                title: 'Step #3',
                content: 'Click on the "Go!" button',
                placement: 'bottom',
                trigger: 'manual'
            });
            $("#analyze").popover('toggle');

        });

        $("#table-preview").click(function() {
            toggleInputTablePreview();
        });

        app.render();
    });
}(window.flow, window.$, window.girder));
<commit_msg>all outputs<commit_after>/*jslint browser: true, nomen: true */

(function (flow, $, girder) {
    'use strict';

    $(document).ready(function () {
        girder.apiRoot = '/girder/api/v1';
        var app = new flow.App({
            el: 'body'
        });

        // Lookup the ID of the analysis that we wish to perform.
        app.analysisName = "testSlowdown-app";
        girder.restRequest({
            path: 'resource/search',
            data: {
                q: app.analysisName,
                types: JSON.stringify(["item"])
            }
        }).done(function (results) {
            app.analysisId = results["item"][0]._id;
            app.readyToAnalyze();
            console.log(app.analysisId);
        });

        app.readyToAnalyze = function () {
            if ("tree" in this && "analysisId" in this) {
                d3.select("#analyze").classed('disabled', false);
            }
        };

        // override upload function for simple mode
        flow.DatasetManagementView.prototype.upload = function (file) {
            var reader = new FileReader();

            reader.onload = _.bind(function (e) {
                var dataset = {
                        name: file.name,
                        data: e.target.result
                    },
                    extension = file.name.split('.'),
                    typeFormat;

                extension = extension[extension.length - 1];
                typeFormat = flow.getTypeFormatsFromExtension(extension)[0];
                typeFormat = {type: typeFormat.type, format: typeFormat.format};
                _.extend(dataset, typeFormat);
                dataset = new Backbone.Model(dataset);

                if (typeFormat.type == "tree") {
                    app.tree = dataset.get('data');
                    d3.select("#tree-name").html('Tree: ' + file.name + ' ');
                    $("#column-input").text("Click GO! to make plot...");
                }
                app.readyToAnalyze();

                this.datasets.off('add', null, 'set-collection').add(dataset);
            }, this);

            reader.readAsText(file);
        };

        $("#analyze").click(function() {
            $("#analyze").attr("disabled", "disabled");
            $("#analyze").text("Re-run");
            $("#notice").text("Performing analysis...");

            var inputs = {
                tree:   {type: "tree",   format: "newick",           data: app.tree},
            };

            var outputs = {
                lttdata: {type: "table",  format: "rows"},
                slowdownResult: {type: "table",  format: "rows"}
            };

            flow.performAnalysis(app.analysisId, inputs, outputs,
                _.bind(function (error, result) {
                    app.taskId = result._id;
                    setTimeout(_.bind(app.checkResult, app), 1000);
                }, app));

        app.checkResult = function () {
            var check_url = '/item/' + this.analysisId + '/flow/' + this.taskId + '/status'
            girder.restRequest({path: check_url}).done(_.bind(function (result) {
                console.log(result.status);
                if (result.status === 'SUCCESS') {
                    // get result data
                    var result_url = '/item/' + this.analysisId + '/flow/' + this.taskId + '/result'
                    girder.restRequest({path: result_url}).done(_.bind(function (data) {
                        app.lttCoords = data.result.lttdata.data;
                        app.slowdownResult = data.result.slowdownResult;
                        console.log(app.slowdownResult);

                        // render results
                        var newRow = $('

');
                        $(".main-content").append(newRow);
                        newRow.append('
');

                        var result = $('

');
                        newRow.append(result);
                        result.append("
Results:<\h2>");

                        result.append("Analysis type: ");
                        result.append("MCCR Gamma test from Pybus and Harvey 2000

");

                        result.append("Number of species: ", data.result.slowdownResult.data.rows[0]["nSpecies"], "
");
                        result.append("Number of missing taxa: ", data.result.slowdownResult.data.rows[0]["NumberMissing"], "
");
                        result.append("Gamma statistic: ", data.result.slowdownResult.data.rows[0]["gam"], "
");
                        result.append("Critical value of gamma: ", data.result.slowdownResult.data.rows[0]["criticalValue"], "
");
                        result.append("P: ", data.result.slowdownResult.data.rows[0]["P"], "
");

                        function getCol(matrix, col){
                            var column = [];
                            for(var i=0; i<matrix.length; i++){
                               column.push(matrix[i][col]);
                            }
                            return column;
                         }

                        var N = getCol(app.lttCoords.rows, "N");
                        var time = getCol(app.lttCoords.rows, "time")

                        console.log(N);
                        console.log(time);

                        var trace1 = {
                          x: time,
                          y: N,
                          mode: 'lines',
                          type: 'scatter'
                        };

                        var data = [trace1];

                        var layout = {
                          title:'LTT plot',
                          xaxis: {
                            title: 'Time before present',
                            showgrid: false,
                            zeroline: false,
                            nticks: 10
                          },
                          yaxis: {
                            title: 'Number of reconstructed lineages',
                            type: 'log',
                            showgrid: false,
                            zeroline: false,
                            nticks: 4
                          },
                        };

                        Plotly.newPlot('ltt-plot', data, layout);

                        $("#analyze").removeAttr("disabled");
                        $("#notice").text("Analysis succeeded!");

                    }, this));

                } else if (result.status === 'FAILURE') {
                    $("#analyze").removeAttr("disabled");
                    $("#notice").text("Analysis failed. " + result.message);
                } else {
                    setTimeout(_.bind(this.checkResult, this), 1000);
                }
            }, this));
        };

        });

        $("#help").click(function() {
            $("#upload").popover({
                title: 'Step #1',
                content: 'Upload your table (csv or tsv) and tree (newick) here',
                placement: 'bottom',
                trigger: 'manual'
            });
            $("#upload").popover('toggle');
            $("#analyze").popover({
                title: 'Step #3',
                content: 'Click on the "Go!" button',
                placement: 'bottom',
                trigger: 'manual'
            });
            $("#analyze").popover('toggle');

        });

        $("#table-preview").click(function() {
            toggleInputTablePreview();
        });

        app.render();
    });
}(window.flow, window.$, window.girder));
<|endoftext|>


<commit_before>module.exports = function(config) {
  config.set({
    // The root path location that will be used to resolve all relative paths
    // defined in files and exclude
    basePath: '',
    // List of files or patterns to load in the browser
    files: [
      { pattern: 'src/**/*.js', included: false },
      { pattern: 'test/spec/**/*.js', included: false },

      'test/test-main.js'
    ],
    // Exclude Mocha specs so that Jasmine does not try run them,
    // And by default it will since file pattern above matches ALL *.js files
    // in test/spec folder
    // In production pick one testing framework and delete specs
    // and spec runner for other frameworks
    exclude: ['test/spec/person.js'],
    // List of frameworks you want to use: jasmine, mocha
    // This library skeleton already comes with mocha and jasmine specs
    // Just swap jasmine for mocha below and you are all set
    frameworks: ['jasmine', 'requirejs'],
    // Enable or disable watching files and executing the tests
    // whenever one of these files changes.
    autoWatch: false,
    // Chrome (comes installed with Karma)
    // ChromeCanary (comes installed with Karma)
    // PhantomJS (comes installed with Karma)
    // Firefox (requires karma-firefox-launcher plugin)
    // Opera (requires karma-opera-launcher plugin)
    // Internet Explorer (requires karma-ie-launcher plugin)
    // Safari (requires karma-safari-launcher plugin)
    browsers: ['PhantomJS'],
    // If singleRun is set to true, Karma will start and capture all
    // configured browsers, run tests and then exit with an exit code of 0 or 1.
    singleRun: false
  });
};
<commit_msg>Updated karma conf exclude array, also added helpful comments regarding on what to do when swapping jasmine for mocha<commit_after>module.exports = function(config) {
  config.set({
    // The root path location that will be used to resolve all relative paths
    // defined in files and exclude
    basePath: '',
    // List of files or patterns to load in the browser
    files: [
      { pattern: 'src/**/*.js', included: false },
      { pattern: 'test/spec/**/*.js', included: false },

      'test/test-main.js'
    ],
    // Exclude Mocha specs so that Jasmine does not try run them,
    // And by default it will since file pattern above matches ALL *.js files
    // in test/spec folder
    // In production pick one testing framework and delete specs
    // and spec runner for other frameworks
    exclude: ['test/spec/capitalize.js'],
    // List of frameworks you want to use: jasmine, mocha, qunit
    // This library skeleton already comes with mocha and jasmine specs
    // Just swap jasmine for mocha below, then remove capitalize.js
    // from exclude: [], instead add random.js and slugify.js
    // the exclude array because those are Jasmine specs
    // CURRENTLY KARMA SUPPORTS ONLY ONE TESTING FRAMEWORK AT A TIME
    frameworks: ['jasmine', 'requirejs'],
    // Enable or disable watching files and executing the tests
    // whenever one of these files changes.
    autoWatch: false,
    // Chrome (comes installed with Karma)
    // ChromeCanary (comes installed with Karma)
    // PhantomJS (comes installed with Karma)
    // Firefox (requires karma-firefox-launcher plugin)
    // Opera (requires karma-opera-launcher plugin)
    // Internet Explorer (requires karma-ie-launcher plugin)
    // Safari (requires karma-safari-launcher plugin)
    browsers: ['PhantomJS'],
    // If singleRun is set to true, Karma will start and capture all
    // configured browsers, run tests and then exit with an exit code of 0 or 1.
    singleRun: false
  });
};
<|endoftext|>


<commit_before>// Karma configuration
// Generated on Thu Jun 05 2014 00:09:58 GMT+0900 (JST)

module.exports = function(config) {
    config.set({

        // base path that will be used to resolve all patterns (eg. files, exclude)
        basePath: '',
        // frameworks to use
        // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
        frameworks: ['mocha'],

        // list of files / patterns to load in the browser
        files: [],
        exclude: [],
        webpack: require('./webpack.config'),
        webpackMiddleware: {
            noInfo: true
        },

        // test results reporter to use
        // possible values: 'dots', 'progress'
        // available reporters: https://npmjs.org/browse/keyword/karma-reporter
        reporters: ['mocha'],
        coverageReporter: {
            type: 'html',
            dir: 'coverage/'
        },
        client: {
            mocha: {
                reporter: 'html'
            }
        },

        // web server port
        port: 9001,

        // enable / disable colors in the output (reporters and logs)
        colors: true,

        // level of logging
        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
        logLevel: config.LOG_INFO,

        // enable / disable watching file and executing tests whenever any file changes
        autoWatch: false,

        // start these browsers
        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
        browsers: ['PhantomJS'],

        // Continuous Integration mode
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: true
    });
};
<commit_msg>karma-mocha-reporterでターミナルが埋まるのでprogressに<commit_after>// Karma configuration
// Generated on Thu Jun 05 2014 00:09:58 GMT+0900 (JST)

module.exports = function(config) {
    config.set({

        // base path that will be used to resolve all patterns (eg. files, exclude)
        basePath: '',
        // frameworks to use
        // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
        frameworks: ['mocha'],

        // list of files / patterns to load in the browser
        files: [],
        exclude: [],
        webpack: require('./webpack.config'),
        webpackMiddleware: {
            noInfo: true
        },

        // test results reporter to use
        // possible values: 'dots', 'progress'
        // available reporters: https://npmjs.org/browse/keyword/karma-reporter
        reporters: ['progress'],
        coverageReporter: {
            type: 'html',
            dir: 'coverage/'
        },
        client: {
            mocha: {
                reporter: 'html'
            }
        },

        // web server port
        port: 9001,

        // enable / disable colors in the output (reporters and logs)
        colors: true,

        // level of logging
        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
        logLevel: config.LOG_INFO,

        // enable / disable watching file and executing tests whenever any file changes
        autoWatch: false,

        // start these browsers
        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
        browsers: ['PhantomJS'],

        // Continuous Integration mode
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: true
    });
};
<|endoftext|>


<commit_before>describe('autoScroll', function() {
  'use strict';

  var window, document, pageDone,
    parent, elm1, draggable, props,

    clientWidth, clientHeight,

    AUTOSCROLL_SPEED, AUTOSCROLL_SENSITIVITY,

    // bBox of element and window
    ELM_W = 102,
    ELM_H = 104,
    WIN_L = 0,
    WIN_T = 0,
    WIN_R, WIN_B, WIN_W, WIN_H,

    SCROLL_WIDTH = 640,
    SCROLL_HEIGHT = 960,
    WIN_WIDTH = 300,
    WIN_HEIGHT = 400;

  beforeAll(function(beforeDone) {
    loadPage('spec/common-window.html', function(pageWindow, pageDocument, pageBody, done) {
      var iframe = pageDocument.getElementById('iframe');
      window = iframe.contentWindow;
      document = window.document;
      pageDone = done;

      iframe.style.width = WIN_WIDTH + 'px';
      iframe.style.height = WIN_HEIGHT + 'px';
      iframe.style.border = '0 none';
      document.body.style.margin = '0';

      parent = document.getElementById('parent');
      elm1 = document.getElementById('elm1');

      parent.style.width = SCROLL_WIDTH + 'px';
      parent.style.height = SCROLL_HEIGHT + 'px';
      clientWidth = document.documentElement.clientWidth;
      clientHeight = document.documentElement.clientHeight;
      WIN_W = clientWidth;
      WIN_H = clientHeight;
      WIN_R = WIN_L + WIN_W;
      WIN_B = WIN_T + WIN_H;

      draggable = new window.PlainDraggable(elm1);
      props = window.insProps[draggable._id];

      AUTOSCROLL_SPEED = window.AUTOSCROLL_SPEED;
      AUTOSCROLL_SENSITIVITY = window.AUTOSCROLL_SENSITIVITY;

      beforeDone();
    }, 'autoScroll');
  });

  afterAll(function() {
    pageDone();
  });

  it('Check Edition (to be LIMIT: ' + !!self.top.LIMIT + ')', function() {
    expect(!!window.PlainDraggable.limit).toBe(!!self.top.LIMIT);
  });

  it('Default', function(done) {
    expect(draggable.autoScroll).not.toBeDefined(); // Default
    expect(props.autoScroll == null).toBe(true); // Default

    draggable.autoScroll = true;
    expect(draggable.autoScroll).toEqual({
      target: window,
      speed: AUTOSCROLL_SPEED,
      sensitivity: AUTOSCROLL_SENSITIVITY
    });
    expect(props.autoScroll).toEqual({
      target: window,
      isWindow: true,
      x: {
        min: 0,
        max: SCROLL_WIDTH - clientWidth,
        lines: [
          {dir: -1, speed: AUTOSCROLL_SPEED[2], position: WIN_L + AUTOSCROLL_SENSITIVITY[2]},
          {dir: 1, speed: AUTOSCROLL_SPEED[2], position: WIN_R - AUTOSCROLL_SENSITIVITY[2] - ELM_W},
          {dir: -1, speed: AUTOSCROLL_SPEED[1], position: WIN_L + AUTOSCROLL_SENSITIVITY[1]},
          {dir: 1, speed: AUTOSCROLL_SPEED[1], position: WIN_R - AUTOSCROLL_SENSITIVITY[1] - ELM_W},
          {dir: -1, speed: AUTOSCROLL_SPEED[0], position: WIN_L + AUTOSCROLL_SENSITIVITY[0]},
          {dir: 1, speed: AUTOSCROLL_SPEED[0], position: WIN_R - AUTOSCROLL_SENSITIVITY[0] - ELM_W}
        ]
      },
      y: {
        min: 0,
        max: SCROLL_HEIGHT - clientHeight,
        lines: [
          {dir: -1, speed: AUTOSCROLL_SPEED[2], position: WIN_T + AUTOSCROLL_SENSITIVITY[2]},
          {dir: 1, speed: AUTOSCROLL_SPEED[2], position: WIN_B - AUTOSCROLL_SENSITIVITY[2] - ELM_H},
          {dir: -1, speed: AUTOSCROLL_SPEED[1], position: WIN_T + AUTOSCROLL_SENSITIVITY[1]},
          {dir: 1, speed: AUTOSCROLL_SPEED[1], position: WIN_B - AUTOSCROLL_SENSITIVITY[1] - ELM_H},
          {dir: -1, speed: AUTOSCROLL_SPEED[0], position: WIN_T + AUTOSCROLL_SENSITIVITY[0]},
          {dir: 1, speed: AUTOSCROLL_SPEED[0], position: WIN_B - AUTOSCROLL_SENSITIVITY[0] - ELM_H}
        ]
      }
    });

    done();
  });

  it('Change `speed`', function(done) {
    var SPEED = [8, 16],
      SENSITIVITY = [32, 64];

    draggable.autoScroll = {
      speed: SPEED,
      sensitivity: SENSITIVITY
    };
    expect(draggable.autoScroll).toEqual({
      target: window,
      speed: SPEED,
      sensitivity: SENSITIVITY
    });
    expect(props.autoScroll).toEqual({
      target: window,
      isWindow: true,
      x: {
        min: 0,
        max: SCROLL_WIDTH - clientWidth,
        lines: [
          {dir: -1, speed: SPEED[1], position: WIN_L + SENSITIVITY[1]},
          {dir: 1, speed: SPEED[1], position: WIN_R - SENSITIVITY[1] - ELM_W},
          {dir: -1, speed: SPEED[0], position: WIN_L + SENSITIVITY[0]},
          {dir: 1, speed: SPEED[0], position: WIN_R - SENSITIVITY[0] - ELM_W}
        ]
      },
      y: {
        min: 0,
        max: SCROLL_HEIGHT - clientHeight,
        lines: [
          {dir: -1, speed: SPEED[1], position: WIN_T + SENSITIVITY[1]},
          {dir: 1, speed: SPEED[1], position: WIN_B - SENSITIVITY[1] - ELM_H},
          {dir: -1, speed: SPEED[0], position: WIN_T + SENSITIVITY[0]},
          {dir: 1, speed: SPEED[0], position: WIN_B - SENSITIVITY[0] - ELM_H}
        ]
      }
    });

    done();
  });

  it('min*, max*', function(done) {
    draggable.autoScroll = {minX: 8, maxX: 16, minY: 32, maxY: 64};
    expect(draggable.autoScroll).toEqual({
      minX: 8, maxX: 16, minY: 32, maxY: 64,
      target: window,
      speed: AUTOSCROLL_SPEED,
      sensitivity: AUTOSCROLL_SENSITIVITY
    });
    expect(props.autoScroll.x.min).toEqual(8);
    expect(props.autoScroll.x.max).toEqual(16);
    expect(props.autoScroll.y.min).toEqual(32);
    expect(props.autoScroll.y.max).toEqual(64);

    // Fix max*
    draggable.autoScroll = {maxX: 8, maxY: SCROLL_HEIGHT}; // maxY over
    expect(draggable.autoScroll).toEqual({
      maxX: 8, maxY: SCROLL_HEIGHT,
      target: window,
      speed: AUTOSCROLL_SPEED,
      sensitivity: AUTOSCROLL_SENSITIVITY
    });
    expect(props.autoScroll.x.min).toEqual(0);
    expect(props.autoScroll.x.max).toEqual(8);
    expect(props.autoScroll.y.min).toEqual(0);
    expect(props.autoScroll.y.max).toEqual(SCROLL_HEIGHT - clientHeight); // Fixed

    // Default max* -> max scroll
    draggable.autoScroll = {minX: 8, minY: SCROLL_WIDTH}; // minY over
    expect(draggable.autoScroll).toEqual({
      minX: 8, minY: SCROLL_WIDTH,
      target: window,
      speed: AUTOSCROLL_SPEED,
      sensitivity: AUTOSCROLL_SENSITIVITY
    });
    expect(props.autoScroll.x.min).toEqual(8);
    expect(props.autoScroll.x.max).toEqual(SCROLL_WIDTH - clientWidth);
    expect(props.autoScroll.y).not.toBeDefined(); // removed

    // over max* -> max scroll
    draggable.autoScroll = {minX: 8, minY: SCROLL_WIDTH, maxY: SCROLL_WIDTH + 10}; // minY over
    expect(draggable.autoScroll).toEqual({
      minX: 8, minY: SCROLL_WIDTH, maxY: SCROLL_WIDTH + 10,
      target: window,
      speed: AUTOSCROLL_SPEED,
      sensitivity: AUTOSCROLL_SENSITIVITY
    });
    expect(props.autoScroll.x.min).toEqual(8);
    expect(props.autoScroll.x.max).toEqual(SCROLL_WIDTH - clientWidth);
    expect(props.autoScroll.y).not.toBeDefined(); // removed

    done();
  });

});
<commit_msg>Fix: test props.autoScroll<commit_after>describe('autoScroll', function() {
  'use strict';

  var window, document, pageDone,
    parent, elm1, draggable, props,

    clientWidth, clientHeight,

    AUTOSCROLL_SPEED, AUTOSCROLL_SENSITIVITY,

    // bBox of element and window
    ELM_W = 102,
    ELM_H = 104,
    WIN_L = 0,
    WIN_T = 0,
    WIN_R, WIN_B, WIN_W, WIN_H,

    SCROLL_WIDTH = 640,
    SCROLL_HEIGHT = 960,
    WIN_WIDTH = 300,
    WIN_HEIGHT = 400;

  beforeAll(function(beforeDone) {
    loadPage('spec/common-window.html', function(pageWindow, pageDocument, pageBody, done) {
      var iframe = pageDocument.getElementById('iframe');
      window = iframe.contentWindow;
      document = window.document;
      pageDone = done;

      iframe.style.width = WIN_WIDTH + 'px';
      iframe.style.height = WIN_HEIGHT + 'px';
      iframe.style.border = '0 none';
      document.body.style.margin = '0';

      parent = document.getElementById('parent');
      elm1 = document.getElementById('elm1');

      parent.style.width = SCROLL_WIDTH + 'px';
      parent.style.height = SCROLL_HEIGHT + 'px';
      clientWidth = document.documentElement.clientWidth;
      clientHeight = document.documentElement.clientHeight;
      WIN_W = clientWidth;
      WIN_H = clientHeight;
      WIN_R = WIN_L + WIN_W;
      WIN_B = WIN_T + WIN_H;

      draggable = new window.PlainDraggable(elm1);
      props = window.insProps[draggable._id];

      AUTOSCROLL_SPEED = window.AUTOSCROLL_SPEED;
      AUTOSCROLL_SENSITIVITY = window.AUTOSCROLL_SENSITIVITY;

      beforeDone();
    }, 'autoScroll');
  });

  afterAll(function() {
    pageDone();
  });

  it('Check Edition (to be LIMIT: ' + !!self.top.LIMIT + ')', function() {
    expect(!!window.PlainDraggable.limit).toBe(!!self.top.LIMIT);
  });

  it('Default', function(done) {
    expect(draggable.autoScroll).not.toBeDefined(); // Default
    expect(props.autoScroll == null).toBe(true); // Default

    draggable.autoScroll = true;
    expect(draggable.autoScroll).toEqual({
      target: window,
      speed: AUTOSCROLL_SPEED,
      sensitivity: AUTOSCROLL_SENSITIVITY
    });
    delete props.autoScroll.scrollableBBox; // Ignore
    expect(props.autoScroll).toEqual({
      target: window,
      isWindow: true,
      scrollWidth: SCROLL_WIDTH,
      scrollHeight: SCROLL_HEIGHT,
      x: {
        min: 0,
        max: SCROLL_WIDTH - clientWidth,
        lines: [
          {dir: -1, speed: AUTOSCROLL_SPEED[2], position: WIN_L + AUTOSCROLL_SENSITIVITY[2]},
          {dir: 1, speed: AUTOSCROLL_SPEED[2], position: WIN_R - AUTOSCROLL_SENSITIVITY[2] - ELM_W},
          {dir: -1, speed: AUTOSCROLL_SPEED[1], position: WIN_L + AUTOSCROLL_SENSITIVITY[1]},
          {dir: 1, speed: AUTOSCROLL_SPEED[1], position: WIN_R - AUTOSCROLL_SENSITIVITY[1] - ELM_W},
          {dir: -1, speed: AUTOSCROLL_SPEED[0], position: WIN_L + AUTOSCROLL_SENSITIVITY[0]},
          {dir: 1, speed: AUTOSCROLL_SPEED[0], position: WIN_R - AUTOSCROLL_SENSITIVITY[0] - ELM_W}
        ]
      },
      y: {
        min: 0,
        max: SCROLL_HEIGHT - clientHeight,
        lines: [
          {dir: -1, speed: AUTOSCROLL_SPEED[2], position: WIN_T + AUTOSCROLL_SENSITIVITY[2]},
          {dir: 1, speed: AUTOSCROLL_SPEED[2], position: WIN_B - AUTOSCROLL_SENSITIVITY[2] - ELM_H},
          {dir: -1, speed: AUTOSCROLL_SPEED[1], position: WIN_T + AUTOSCROLL_SENSITIVITY[1]},
          {dir: 1, speed: AUTOSCROLL_SPEED[1], position: WIN_B - AUTOSCROLL_SENSITIVITY[1] - ELM_H},
          {dir: -1, speed: AUTOSCROLL_SPEED[0], position: WIN_T + AUTOSCROLL_SENSITIVITY[0]},
          {dir: 1, speed: AUTOSCROLL_SPEED[0], position: WIN_B - AUTOSCROLL_SENSITIVITY[0] - ELM_H}
        ]
      }
    });

    done();
  });

  it('Change `speed`', function(done) {
    var SPEED = [8, 16],
      SENSITIVITY = [32, 64];

    draggable.autoScroll = {
      speed: SPEED,
      sensitivity: SENSITIVITY
    };
    expect(draggable.autoScroll).toEqual({
      target: window,
      speed: SPEED,
      sensitivity: SENSITIVITY
    });
    delete props.autoScroll.scrollableBBox; // Ignore
    expect(props.autoScroll).toEqual({
      target: window,
      isWindow: true,
      scrollWidth: SCROLL_WIDTH,
      scrollHeight: SCROLL_HEIGHT,
      x: {
        min: 0,
        max: SCROLL_WIDTH - clientWidth,
        lines: [
          {dir: -1, speed: SPEED[1], position: WIN_L + SENSITIVITY[1]},
          {dir: 1, speed: SPEED[1], position: WIN_R - SENSITIVITY[1] - ELM_W},
          {dir: -1, speed: SPEED[0], position: WIN_L + SENSITIVITY[0]},
          {dir: 1, speed: SPEED[0], position: WIN_R - SENSITIVITY[0] - ELM_W}
        ]
      },
      y: {
        min: 0,
        max: SCROLL_HEIGHT - clientHeight,
        lines: [
          {dir: -1, speed: SPEED[1], position: WIN_T + SENSITIVITY[1]},
          {dir: 1, speed: SPEED[1], position: WIN_B - SENSITIVITY[1] - ELM_H},
          {dir: -1, speed: SPEED[0], position: WIN_T + SENSITIVITY[0]},
          {dir: 1, speed: SPEED[0], position: WIN_B - SENSITIVITY[0] - ELM_H}
        ]
      }
    });

    done();
  });

  it('min*, max*', function(done) {
    draggable.autoScroll = {minX: 8, maxX: 16, minY: 32, maxY: 64};
    expect(draggable.autoScroll).toEqual({
      minX: 8, maxX: 16, minY: 32, maxY: 64,
      target: window,
      speed: AUTOSCROLL_SPEED,
      sensitivity: AUTOSCROLL_SENSITIVITY
    });
    expect(props.autoScroll.x.min).toEqual(8);
    expect(props.autoScroll.x.max).toEqual(16);
    expect(props.autoScroll.y.min).toEqual(32);
    expect(props.autoScroll.y.max).toEqual(64);

    // Fix max*
    draggable.autoScroll = {maxX: 8, maxY: SCROLL_HEIGHT}; // maxY over
    expect(draggable.autoScroll).toEqual({
      maxX: 8, maxY: SCROLL_HEIGHT,
      target: window,
      speed: AUTOSCROLL_SPEED,
      sensitivity: AUTOSCROLL_SENSITIVITY
    });
    expect(props.autoScroll.x.min).toEqual(0);
    expect(props.autoScroll.x.max).toEqual(8);
    expect(props.autoScroll.y.min).toEqual(0);
    expect(props.autoScroll.y.max).toEqual(SCROLL_HEIGHT - clientHeight); // Fixed

    // Default max* -> max scroll
    draggable.autoScroll = {minX: 8, minY: SCROLL_WIDTH}; // minY over
    expect(draggable.autoScroll).toEqual({
      minX: 8, minY: SCROLL_WIDTH,
      target: window,
      speed: AUTOSCROLL_SPEED,
      sensitivity: AUTOSCROLL_SENSITIVITY
    });
    expect(props.autoScroll.x.min).toEqual(8);
    expect(props.autoScroll.x.max).toEqual(SCROLL_WIDTH - clientWidth);
    expect(props.autoScroll.y).not.toBeDefined(); // removed

    // over max* -> max scroll
    draggable.autoScroll = {minX: 8, minY: SCROLL_WIDTH, maxY: SCROLL_WIDTH + 10}; // minY over
    expect(draggable.autoScroll).toEqual({
      minX: 8, minY: SCROLL_WIDTH, maxY: SCROLL_WIDTH + 10,
      target: window,
      speed: AUTOSCROLL_SPEED,
      sensitivity: AUTOSCROLL_SENSITIVITY
    });
    expect(props.autoScroll.x.min).toEqual(8);
    expect(props.autoScroll.x.max).toEqual(SCROLL_WIDTH - clientWidth);
    expect(props.autoScroll.y).not.toBeDefined(); // removed

    done();
  });

});
<|endoftext|>


<commit_before>/**
 * @fileoverview
 * Partial implementation of a SB3 serializer and deserializer. Parses provided
 * JSON and then generates all needed scratch-vm runtime structures.
 */

const vmPackage = require('../../package.json');
const Blocks = require('../engine/blocks');
const Sprite = require('../sprites/sprite');
const Variable = require('../engine/variable');
const List = require('../engine/list');

const loadCostume = require('../import/load-costume.js');
const loadSound = require('../import/load-sound.js');

/**
 * Serializes the specified VM runtime.
 * @param  {!Runtime} runtime VM runtime instance to be serialized.
 * @return {object}    Serialized runtime instance.
 */
const serialize = function (runtime) {
    // Fetch targets
    const obj = Object.create(null);
    obj.targets = runtime.targets;

    // Assemble metadata
    const meta = Object.create(null);
    meta.semver = '3.0.0';
    meta.vm = vmPackage.version;

    // Attach full user agent string to metadata if available
    meta.agent = null;
    if (typeof navigator !== 'undefined') meta.agent = navigator.userAgent;

    // Assemble payload and return
    obj.meta = meta;
    return obj;
};

/**
 * Parse a single "Scratch object" and create all its in-memory VM objects.
 * @param {!object} object From-JSON "Scratch object:" sprite, stage, watcher.
 * @param {!Runtime} runtime Runtime object to load all structures into.
 * @return {?Target} Target created (stage or sprite).
 */
const parseScratchObject = function (object, runtime) {
    if (!object.hasOwnProperty('name')) {
        // Watcher/monitor - skip this object until those are implemented in VM.
        // @todo
        return;
    }
    // Blocks container for this object.
    const blocks = new Blocks();

    // @todo: For now, load all Scratch objects (stage/sprites) as a Sprite.
    const sprite = new Sprite(blocks, runtime);

    // Sprite/stage name from JSON.
    if (object.hasOwnProperty('name')) {
        sprite.name = object.name;
    }
    if (object.hasOwnProperty('blocks')) {
        for (let blockId in object.blocks) {
            blocks.createBlock(object.blocks[blockId]);
        }
        // console.log(blocks);
    }
    // Costumes from JSON.
    const costumePromises = (object.costumes || []).map(costumeSource => {
        // @todo: Make sure all the relevant metadata is being pulled out.
        const costume = {
            skinId: null,
            name: costumeSource.name,
            bitmapResolution: costumeSource.bitmapResolution,
            rotationCenterX: costumeSource.rotationCenterX,
            rotationCenterY: costumeSource.rotationCenterY
        };
        const costumeMd5 = `${costumeSource.assetId}.${costumeSource.assetType.runtimeFormat}`;
        return loadCostume(costumeMd5, costume, runtime);
    });
    // Sounds from JSON
    const soundPromises = (object.sounds || []).map(soundSource => {
        const sound = {
            format: soundSource.format,
            fileUrl: soundSource.fileUrl,
            rate: soundSource.rate,
            sampleCount: soundSource.sampleCount,
            soundID: soundSource.soundID,
            name: soundSource.name,
            md5: soundSource.md5,
            data: null
        };
        return loadSound(sound, runtime);
    });
    // Create the first clone, and load its run-state from JSON.
    const target = sprite.createClone();
    // Load target properties from JSON.
    if (object.hasOwnProperty('variables')) {
        for (let j = 0; j < object.variables.length; j++) {
            const variable = object.variables[j];
            target.variables[variable.name] = new Variable(
                variable.name,
                variable.value,
                variable.isPersistent
            );
        }
    }
    if (object.hasOwnProperty('lists')) {
        for (let k = 0; k < object.lists.length; k++) {
            const list = object.lists[k];
            // @todo: monitor properties.
            target.lists[list.listName] = new List(
                list.listName,
                list.contents
            );
        }
    }
    if (object.hasOwnProperty('x')) {
        target.x = object.x;
    }
    if (object.hasOwnProperty('y')) {
        target.y = object.y;
    }
    if (object.hasOwnProperty('direction')) {
        target.direction = object.direction;
    }
    if (object.hasOwnProperty('size')) {
        target.size = object.size;
    }
    if (object.hasOwnProperty('visible')) {
        target.visible = object.visible;
    }
    if (object.hasOwnProperty('currentCostume')) {
        target.currentCostume = object.currentCostume;
    }
    if (object.hasOwnProperty('rotationStyle')) {
        target.rotationStyle = object.rotationStyle;
    }
    if (object.hasOwnProperty('isStage')) {
        target.isStage = object.isStage;
    }
    Promise.all(costumePromises).then(costumes => {
        sprite.costumes = costumes;
    });
    Promise.all(soundPromises).then(sounds => {
        sprite.sounds = sounds;
    });
    return Promise.all(costumePromises.concat(soundPromises)).then(() => target);
};

/**
 * Deserializes the specified representation of a VM runtime and loads it into
 * the provided runtime instance.
 * @param  {object}  json    JSON representation of a VM runtime.
 * @param  {Runtime} runtime Runtime instance
 * @returns {Promise} Promise that resolves to the list of targets after the project is deserialized
 */
const deserialize = function (json, runtime) {
    return Promise.all((json.targets || []).map(target => parseScratchObject(target, runtime)));
};

module.exports = {
    serialize: serialize,
    deserialize: deserialize
};
<commit_msg>Don't serialize clones when saving<commit_after>/**
 * @fileoverview
 * Partial implementation of a SB3 serializer and deserializer. Parses provided
 * JSON and then generates all needed scratch-vm runtime structures.
 */

const vmPackage = require('../../package.json');
const Blocks = require('../engine/blocks');
const Sprite = require('../sprites/sprite');
const Variable = require('../engine/variable');
const List = require('../engine/list');

const loadCostume = require('../import/load-costume.js');
const loadSound = require('../import/load-sound.js');

/**
 * Serializes the specified VM runtime.
 * @param  {!Runtime} runtime VM runtime instance to be serialized.
 * @return {object}    Serialized runtime instance.
 */
const serialize = function (runtime) {
    // Fetch targets
    const obj = Object.create(null);
    obj.targets = runtime.targets.filter(target => target.isOriginal);

    // Assemble metadata
    const meta = Object.create(null);
    meta.semver = '3.0.0';
    meta.vm = vmPackage.version;

    // Attach full user agent string to metadata if available
    meta.agent = null;
    if (typeof navigator !== 'undefined') meta.agent = navigator.userAgent;

    // Assemble payload and return
    obj.meta = meta;
    return obj;
};

/**
 * Parse a single "Scratch object" and create all its in-memory VM objects.
 * @param {!object} object From-JSON "Scratch object:" sprite, stage, watcher.
 * @param {!Runtime} runtime Runtime object to load all structures into.
 * @return {?Target} Target created (stage or sprite).
 */
const parseScratchObject = function (object, runtime) {
    if (!object.hasOwnProperty('name')) {
        // Watcher/monitor - skip this object until those are implemented in VM.
        // @todo
        return;
    }
    // Blocks container for this object.
    const blocks = new Blocks();

    // @todo: For now, load all Scratch objects (stage/sprites) as a Sprite.
    const sprite = new Sprite(blocks, runtime);

    // Sprite/stage name from JSON.
    if (object.hasOwnProperty('name')) {
        sprite.name = object.name;
    }
    if (object.hasOwnProperty('blocks')) {
        for (let blockId in object.blocks) {
            blocks.createBlock(object.blocks[blockId]);
        }
        // console.log(blocks);
    }
    // Costumes from JSON.
    const costumePromises = (object.costumes || []).map(costumeSource => {
        // @todo: Make sure all the relevant metadata is being pulled out.
        const costume = {
            skinId: null,
            name: costumeSource.name,
            bitmapResolution: costumeSource.bitmapResolution,
            rotationCenterX: costumeSource.rotationCenterX,
            rotationCenterY: costumeSource.rotationCenterY
        };
        const costumeMd5 = `${costumeSource.assetId}.${costumeSource.assetType.runtimeFormat}`;
        return loadCostume(costumeMd5, costume, runtime);
    });
    // Sounds from JSON
    const soundPromises = (object.sounds || []).map(soundSource => {
        const sound = {
            format: soundSource.format,
            fileUrl: soundSource.fileUrl,
            rate: soundSource.rate,
            sampleCount: soundSource.sampleCount,
            soundID: soundSource.soundID,
            name: soundSource.name,
            md5: soundSource.md5,
            data: null
        };
        return loadSound(sound, runtime);
    });
    // Create the first clone, and load its run-state from JSON.
    const target = sprite.createClone();
    // Load target properties from JSON.
    if (object.hasOwnProperty('variables')) {
        for (let j = 0; j < object.variables.length; j++) {
            const variable = object.variables[j];
            target.variables[variable.name] = new Variable(
                variable.name,
                variable.value,
                variable.isPersistent
            );
        }
    }
    if (object.hasOwnProperty('lists')) {
        for (let k = 0; k < object.lists.length; k++) {
            const list = object.lists[k];
            // @todo: monitor properties.
            target.lists[list.listName] = new List(
                list.listName,
                list.contents
            );
        }
    }
    if (object.hasOwnProperty('x')) {
        target.x = object.x;
    }
    if (object.hasOwnProperty('y')) {
        target.y = object.y;
    }
    if (object.hasOwnProperty('direction')) {
        target.direction = object.direction;
    }
    if (object.hasOwnProperty('size')) {
        target.size = object.size;
    }
    if (object.hasOwnProperty('visible')) {
        target.visible = object.visible;
    }
    if (object.hasOwnProperty('currentCostume')) {
        target.currentCostume = object.currentCostume;
    }
    if (object.hasOwnProperty('rotationStyle')) {
        target.rotationStyle = object.rotationStyle;
    }
    if (object.hasOwnProperty('isStage')) {
        target.isStage = object.isStage;
    }
    Promise.all(costumePromises).then(costumes => {
        sprite.costumes = costumes;
    });
    Promise.all(soundPromises).then(sounds => {
        sprite.sounds = sounds;
    });
    return Promise.all(costumePromises.concat(soundPromises)).then(() => target);
};

/**
 * Deserializes the specified representation of a VM runtime and loads it into
 * the provided runtime instance.
 * @param  {object}  json    JSON representation of a VM runtime.
 * @param  {Runtime} runtime Runtime instance
 * @returns {Promise} Promise that resolves to the list of targets after the project is deserialized
 */
const deserialize = function (json, runtime) {
    return Promise.all((json.targets || []).map(target => parseScratchObject(target, runtime)));
};

module.exports = {
    serialize: serialize,
    deserialize: deserialize
};
<|endoftext|>


<commit_before>'use strict';

const fs = require('fs');
const { promisify } = require('util');
const readdir = promisify(fs.readdir);
const path = require('path');
const log = require('intel').getLogger('sitespeedio.plugin.browsertime');

function findFrame(videoFrames, time) {
  let frame = videoFrames[0];
  for (let currentFrame of videoFrames) {
    if (time >= currentFrame.time) {
      frame = currentFrame;
    } else {
      break;
    }
  }
  return frame;
}

function getMetricsFromBrowsertime(data) {
  const metrics = [];

  if (data.timings.userTimings && data.timings.userTimings.marks) {
    // Some web sites over loads user timings
    // So use only the first ones
    const maxUserTimings = 10;
    let userTimings = 1;
    for (let mark of data.timings.userTimings.marks) {
      metrics.push({
        metric: mark.name,
        value: mark.startTime.toFixed()
      });
      userTimings++;
      if (userTimings > maxUserTimings) {
        break;
      }
    }
  }

  if (data.pageinfo && data.pageinfo.longTask) {
    for (let longTask of data.pageinfo.longTask) {
      metrics.push({
        metric: 'longTask',
        name: 'CPU Long Task duration',
        value: longTask.startTime.toFixed(0),
        duration: longTask.duration.toFixed(0)
      });
    }
  }

  metrics.push({
    metric: 'fullyLoaded',
    name: 'Fully Loaded',
    value: data.fullyLoaded
  });

  if (data.timings.pageTimings) {
    metrics.push({
      metric: 'domContentLoadedTime',
      name: 'DOM Content Loaded Time',
      value: data.timings.pageTimings.domContentLoadedTime
    });
    metrics.push({
      metric: 'pageLoadTime',
      name: 'Page Load Time',
      value: data.timings.pageTimings.pageLoadTime
    });
  }

  if (
    data.timings.paintTiming &&
    data.timings.paintTiming['first-contentful-paint']
  ) {
    metrics.push({
      metric: 'first-contentful-paint',
      name: 'FCP',
      value: data.timings.paintTiming['first-contentful-paint']
    });
  }

  if (
    data.timings.largestContentfulPaint &&
    data.timings.largestContentfulPaint.renderTime
  ) {
    metrics.push({
      metric: 'largestContentfulPaint',
      name: 'LCP',
      value: data.timings.largestContentfulPaint.renderTime
    });
  }

  if (data.visualMetrics) {
    metrics.push({
      metric: 'FirstVisualChange',
      name: 'First Visual Change',
      value: data.visualMetrics.FirstVisualChange
    });
    metrics.push({
      metric: 'LastVisualChange',
      name: 'Last Visual Change',
      value: data.visualMetrics.LastVisualChange
    });
    metrics.push({
      metric: 'VisualComplete85',
      name: 'Visual Complete 85%',
      value: data.visualMetrics.VisualComplete85
    });
    metrics.push({
      metric: 'VisualComplete95',
      name: 'Visual Complete 95%',
      value: data.visualMetrics.VisualComplete95
    });
    metrics.push({
      metric: 'VisualComplete99',
      name: 'Visual Complete 99%',
      value: data.visualMetrics.VisualComplete99
    });
    if (data.visualMetrics.LargestImage) {
      metrics.push({
        metric: 'LargestImage',
        name: 'Largest Image',
        value: data.visualMetrics.LargestImage
      });
    }
    if (data.visualMetrics.Logo) {
      metrics.push({
        metric: 'Logo',
        name: 'Logo',
        value: data.visualMetrics.Logo
      });
    }
    if (data.visualMetrics.Heading) {
      metrics.push({
        metric: 'Heading',
        name: 'Heading',
        value: data.visualMetrics.Heading
      });
    }
  }
  return metrics.sort((a, b) => a.value - b.value);
}

function findTimings(timings, start, end) {
  return timings.filter(timing => timing.value > start && timing.value <= end);
}
module.exports = {
  async getFilmstrip(browsertimeData, run, dir, options, fullPath) {
    let doWeHaveFilmstrip =
      (options.browsertime.chrome &&
        options.browsertime.chrome.enableTraceScreenshots === true) ||
      (options.browsertime.visualMetrics === true &&
        options.browsertime.videoParams.createFilmstrip === true);

    if (doWeHaveFilmstrip === false) {
      return [];
    }

    const toTheFront = [];

    try {
      let metrics = [];
      if (browsertimeData) {
        metrics = getMetricsFromBrowsertime(browsertimeData);
      }
      const files = await readdir(
        path.join(dir, 'data', 'filmstrip', run + '')
      );
      const timings = [];
      for (let file of files) {
        timings.push({ time: file.replace(/\D/g, ''), file });
      }

      const maxTiming = timings.slice(-1)[0].time;

      // We step 100 ms each step ... but if you wanna show all and the last change is late
      // use 200 ms
      const step =
        maxTiming > 10000 && (options.filmstrip && options.filmstrip.showAll)
          ? 200
          : 100;
      let fileName = '';
      for (let i = 0; i <= Number(maxTiming) + step; i = i + step) {
        const entry = findFrame(timings, i);
        const timingMetrics = findTimings(metrics, i - step, i);
        if (
          entry.file !== fileName ||
          timingMetrics.length > 0 ||
          (options.filmstrip && options.filmstrip.showAll)
        ) {
          toTheFront.push({
            time: i / 1000,
            file: fullPath ? fullPath + entry.file : entry.file,
            timings: timingMetrics
          });
        }
        fileName = entry.file;
      }
    } catch (e) {
      log.error('Could not read filmstrip dir', e);
    }
    return toTheFront;
  }
};
<commit_msg>rename lcp/fcp<commit_after>'use strict';

const fs = require('fs');
const { promisify } = require('util');
const readdir = promisify(fs.readdir);
const path = require('path');
const log = require('intel').getLogger('sitespeedio.plugin.browsertime');

function findFrame(videoFrames, time) {
  let frame = videoFrames[0];
  for (let currentFrame of videoFrames) {
    if (time >= currentFrame.time) {
      frame = currentFrame;
    } else {
      break;
    }
  }
  return frame;
}

function getMetricsFromBrowsertime(data) {
  const metrics = [];

  if (data.timings.userTimings && data.timings.userTimings.marks) {
    // Some web sites over loads user timings
    // So use only the first ones
    const maxUserTimings = 10;
    let userTimings = 1;
    for (let mark of data.timings.userTimings.marks) {
      metrics.push({
        metric: mark.name,
        value: mark.startTime.toFixed()
      });
      userTimings++;
      if (userTimings > maxUserTimings) {
        break;
      }
    }
  }

  if (data.pageinfo && data.pageinfo.longTask) {
    for (let longTask of data.pageinfo.longTask) {
      metrics.push({
        metric: 'longTask',
        name: 'CPU Long Task duration',
        value: longTask.startTime.toFixed(0),
        duration: longTask.duration.toFixed(0)
      });
    }
  }

  metrics.push({
    metric: 'fullyLoaded',
    name: 'Fully Loaded',
    value: data.fullyLoaded
  });

  if (data.timings.pageTimings) {
    metrics.push({
      metric: 'domContentLoadedTime',
      name: 'DOM Content Loaded Time',
      value: data.timings.pageTimings.domContentLoadedTime
    });
    metrics.push({
      metric: 'pageLoadTime',
      name: 'Page Load Time',
      value: data.timings.pageTimings.pageLoadTime
    });
  }

  if (
    data.timings.paintTiming &&
    data.timings.paintTiming['first-contentful-paint']
  ) {
    metrics.push({
      metric: 'first-contentful-paint',
      name: 'First Contentful Paint',
      value: data.timings.paintTiming['first-contentful-paint']
    });
  }

  if (
    data.timings.largestContentfulPaint &&
    data.timings.largestContentfulPaint.renderTime
  ) {
    metrics.push({
      metric: 'largestContentfulPaint',
      name: 'Largest Contentful Paint',
      value: data.timings.largestContentfulPaint.renderTime
    });
  }

  if (data.visualMetrics) {
    metrics.push({
      metric: 'FirstVisualChange',
      name: 'First Visual Change',
      value: data.visualMetrics.FirstVisualChange
    });
    metrics.push({
      metric: 'LastVisualChange',
      name: 'Last Visual Change',
      value: data.visualMetrics.LastVisualChange
    });
    metrics.push({
      metric: 'VisualComplete85',
      name: 'Visual Complete 85%',
      value: data.visualMetrics.VisualComplete85
    });
    metrics.push({
      metric: 'VisualComplete95',
      name: 'Visual Complete 95%',
      value: data.visualMetrics.VisualComplete95
    });
    metrics.push({
      metric: 'VisualComplete99',
      name: 'Visual Complete 99%',
      value: data.visualMetrics.VisualComplete99
    });
    if (data.visualMetrics.LargestImage) {
      metrics.push({
        metric: 'LargestImage',
        name: 'Largest Image',
        value: data.visualMetrics.LargestImage
      });
    }
    if (data.visualMetrics.Logo) {
      metrics.push({
        metric: 'Logo',
        name: 'Logo',
        value: data.visualMetrics.Logo
      });
    }
    if (data.visualMetrics.Heading) {
      metrics.push({
        metric: 'Heading',
        name: 'Heading',
        value: data.visualMetrics.Heading
      });
    }
  }
  return metrics.sort((a, b) => a.value - b.value);
}

function findTimings(timings, start, end) {
  return timings.filter(timing => timing.value > start && timing.value <= end);
}
module.exports = {
  async getFilmstrip(browsertimeData, run, dir, options, fullPath) {
    let doWeHaveFilmstrip =
      (options.browsertime.chrome &&
        options.browsertime.chrome.enableTraceScreenshots === true) ||
      (options.browsertime.visualMetrics === true &&
        options.browsertime.videoParams.createFilmstrip === true);

    if (doWeHaveFilmstrip === false) {
      return [];
    }

    const toTheFront = [];

    try {
      let metrics = [];
      if (browsertimeData) {
        metrics = getMetricsFromBrowsertime(browsertimeData);
      }
      const files = await readdir(
        path.join(dir, 'data', 'filmstrip', run + '')
      );
      const timings = [];
      for (let file of files) {
        timings.push({ time: file.replace(/\D/g, ''), file });
      }

      const maxTiming = timings.slice(-1)[0].time;

      // We step 100 ms each step ... but if you wanna show all and the last change is late
      // use 200 ms
      const step =
        maxTiming > 10000 && (options.filmstrip && options.filmstrip.showAll)
          ? 200
          : 100;
      let fileName = '';
      for (let i = 0; i <= Number(maxTiming) + step; i = i + step) {
        const entry = findFrame(timings, i);
        const timingMetrics = findTimings(metrics, i - step, i);
        if (
          entry.file !== fileName ||
          timingMetrics.length > 0 ||
          (options.filmstrip && options.filmstrip.showAll)
        ) {
          toTheFront.push({
            time: i / 1000,
            file: fullPath ? fullPath + entry.file : entry.file,
            timings: timingMetrics
          });
        }
        fileName = entry.file;
      }
    } catch (e) {
      log.error('Could not read filmstrip dir', e);
    }
    return toTheFront;
  }
};
<|endoftext|>


<commit_before>"use strict";

const MARKER_TRIM_BELOW = /^\s*trim-below\s*$/;
const MARKER_TRIM_ABOVE = /^\s*trim-above\s*$/;

function findMarker(comments, marker) {
  return comments ? comments.findIndex(c => marker.test(c.value)) : -1;
}

function isFlaggedToTrimBelow(leadingComments, trailingComments) {
  const hasLeading = findMarker(leadingComments, MARKER_TRIM_BELOW) >= 0;
  const hasTrailing = findMarker(trailingComments, MARKER_TRIM_BELOW) >= 0;

  return hasLeading || hasTrailing;
}

function isFlaggedToTrimAbove(leadingComments, trailingComments) {
  const hasLeading = findMarker(leadingComments, MARKER_TRIM_ABOVE) >= 0;
  const hasTrailing = findMarker(trailingComments, MARKER_TRIM_ABOVE) >= 0;

  return hasLeading || hasTrailing;
}

module.exports = function babelPluginTrimmer(babel) {
  const t = babel.types;

  return {
    visitor: {
      Program(root) {
        root.traverse({
          exit(path) {
            const node = path.node;
            const parentBody = path.parent.body;

            if (isFlaggedToTrimBelow(node.leadingComments, null)) {
              const nodePosition = parentBody.indexOf(node);
              const markerPosition = findMarker(node.leadingComments, MARKER_TRIM_BELOW);

              // insert empty node before the node we are going to delete
              // and add all remaining leadingComments to it as the leading
              const emptyNode = t.noop();
              emptyNode.trailingComments = node.leadingComments.splice(0, markerPosition);

              parentBody.splice(nodePosition, parentBody.length, emptyNode);
            }

            if (isFlaggedToTrimBelow(null, node.trailingComments)) {
              const markerPosition = findMarker(node.trailingComments, MARKER_TRIM_BELOW);

              node.trailingComments.splice(markerPosition);
            }

            if (isFlaggedToTrimAbove(node.leadingComments, null)) {
              const markerPosition = findMarker(node.leadingComments, MARKER_TRIM_ABOVE);

              node.leadingComments.splice(0, markerPosition + 1);
            }

            if (isFlaggedToTrimAbove(null, node.trailingComments)) {
              const nodePosition = parentBody.indexOf(node);

              parentBody.splice(0, nodePosition + 1);
            }

          }
        });
      }
    }
  };
};
<commit_msg>fixed typo<commit_after>"use strict";

const MARKER_TRIM_BELOW = /^\s*trim-below\s*$/;
const MARKER_TRIM_ABOVE = /^\s*trim-above\s*$/;

function findMarker(comments, marker) {
  return comments ? comments.findIndex(c => marker.test(c.value)) : -1;
}

function isFlaggedToTrimBelow(leadingComments, trailingComments) {
  const hasLeading = findMarker(leadingComments, MARKER_TRIM_BELOW) >= 0;
  const hasTrailing = findMarker(trailingComments, MARKER_TRIM_BELOW) >= 0;

  return hasLeading || hasTrailing;
}

function isFlaggedToTrimAbove(leadingComments, trailingComments) {
  const hasLeading = findMarker(leadingComments, MARKER_TRIM_ABOVE) >= 0;
  const hasTrailing = findMarker(trailingComments, MARKER_TRIM_ABOVE) >= 0;

  return hasLeading || hasTrailing;
}

module.exports = function babelPluginTrimmer(babel) {
  const t = babel.types;

  return {
    visitor: {
      Program(root) {
        root.traverse({
          exit(path) {
            const node = path.node;
            const parentBody = path.parent.body;

            if (isFlaggedToTrimBelow(node.leadingComments, null)) {
              const nodePosition = parentBody.indexOf(node);
              const markerPosition = findMarker(node.leadingComments, MARKER_TRIM_BELOW);

              // insert empty node before the node we are going to delete
              // and add all remaining leadingComments to it as the trailingComments
              const emptyNode = t.noop();
              emptyNode.trailingComments = node.leadingComments.splice(0, markerPosition);

              parentBody.splice(nodePosition, parentBody.length, emptyNode);
            }

            if (isFlaggedToTrimBelow(null, node.trailingComments)) {
              const markerPosition = findMarker(node.trailingComments, MARKER_TRIM_BELOW);

              node.trailingComments.splice(markerPosition);
            }

            if (isFlaggedToTrimAbove(node.leadingComments, null)) {
              const markerPosition = findMarker(node.leadingComments, MARKER_TRIM_ABOVE);

              node.leadingComments.splice(0, markerPosition + 1);
            }

            if (isFlaggedToTrimAbove(null, node.trailingComments)) {
              const nodePosition = parentBody.indexOf(node);

              parentBody.splice(0, nodePosition + 1);
            }

          }
        });
      }
    }
  };
};
<|endoftext|>