<commit_before>'use strict';

const assert = require('../../utils/assert');
const media = require('../../../lib/media');
const getCodecs = media.testing.getCodecs;
const imageInfo = require('../../../lib/imageinfo');
const getStructuredArtistInfo = imageInfo.getStructuredArtistInfo;
const makeResults = imageInfo.testing.makeResults;

const imageWithCaption =
    '
' +
        '' +
        '
An example image
' +
    '
';

const videoWithMetadata =
    '
' +
        '<video resource="./File:Foo"/>' +
    '
';

const videoWithDerivative =
    '
' +
        '<video resource="./File:Foo">' +
            '<source src="https://example.com/Foo.ogv"' +
                ' type=\'video/ogg; codecs="theora, vorbis"\'' +
                ' data-title="Foo"' +
                ' data-shorttitle="Foo"' +
                ' data-file-width="120"' +
                ' data-file-height="120"' +
            '/>' +
            '<source src="https://example.com/Foo.ogv"' +
                ' type=\'video/ogg\'' +
                ' data-title="Bar"' +
                ' data-shorttitle="Bar"' +
                ' data-file-width="120"' +
                ' data-file-height="120"' +
            '/>' +
        '</video>' +
    '
';

const spokenWikipedia =
    '
' +
        '
<audio resource="./File:Foo"/>
' +
    '
';

const spokenWikipediaOld =
    '
' +
        '
<video resource="./File:Foo"/>
' +
    '
';

const pronunciationAudio =
    '' +
    '' +
        'Pronunciation' +
    '';

const imageWithSection =
    '
' +
        '
' +
            '' +
        '
' +
    '
';

const imageWithPercentEncodedTitle =
    '
' +
        '' +
    '
';

const pronunciationWithPercentEncodedTitle =
    '' +
    '' +
        '(' +
            '' +
            'listen' +
        ')' +
    '';

describe('lib:media metadata is correctly parsed from HTML', () => {

    it('all expected captions are present', () => {
        const result = media.getMediaItemInfoFromPage(imageWithCaption)[0];
        assert.deepEqual(result.caption.html, 'An example image');
        assert.deepEqual(result.caption.text, 'An example image');
    });

    it('all expected data-mw properties are present', () => {
        const result = media.getMediaItemInfoFromPage(videoWithMetadata)[0];
        assert.deepEqual(result.start_time, 1);
        assert.deepEqual(result.thumb_time, 2);
        assert.deepEqual(result.end_time, 3);
    });

    it('all expected derivative properties are present', () => {
        const result = media.getMediaItemInfoFromPage(videoWithDerivative)[0];
        const derivative = result.sources[0];
        assert.deepEqual(derivative.url, 'https://example.com/Foo.ogv');
        assert.deepEqual(derivative.mime, 'video/ogg');
        assert.deepEqual(derivative.codecs, [ 'theora', 'vorbis' ]);
        assert.deepEqual(derivative.name, 'Foo');
        assert.deepEqual(derivative.short_name, 'Foo');
        assert.deepEqual(derivative.width, 120);
        assert.deepEqual(derivative.height, 120);
    });

    it('media file derivative with no codecs in type attribute is parsed correctly', () => {
        const result = media.getMediaItemInfoFromPage(videoWithDerivative)[0];
        const derivative = result.sources[1];
        assert.deepEqual(derivative.url, 'https://example.com/Foo.ogv');
        assert.deepEqual(derivative.mime, 'video/ogg');
        assert.deepEqual(derivative.codecs, undefined);
        assert.deepEqual(derivative.name, 'Bar');
        assert.deepEqual(derivative.short_name, 'Bar');
        assert.deepEqual(derivative.width, 120);
        assert.deepEqual(derivative.height, 120);
    });

    it('spoken Wikipedia file is correctly identified', () => {
        const result = media.getMediaItemInfoFromPage(spokenWikipedia)[0];
        assert.deepEqual(result.audio_type, 'spoken');
    });

    it('spoken Wikipedia (OLD: with video tag) file is correctly identified', () => {
        const result = media.getMediaItemInfoFromPage(spokenWikipediaOld)[0];
        assert.deepEqual(result.audio_type, 'spoken');
    });

    // Skip until a long term solution for https://phabricator.wikimedia.org/T214338 is found
    it.skip('pronunciation audio file is correctly identified', () => {
        const result = media.getMediaItemInfoFromPage(pronunciationAudio)[0];
        assert.deepEqual(result.title, 'File:Foo');
        assert.deepEqual(result.type, 'audio');
        assert.deepEqual(result.audio_type, 'pronunciation');
    });

    it('section is correctly identified', () => {
        const result = media.getMediaItemInfoFromPage(imageWithSection)[0];
        assert.deepEqual(result.section_id, 0);
    });

    it('titles are decoded after parsing from HTML', () => {
        const result = media.getMediaItemInfoFromPage(imageWithPercentEncodedTitle)[0];
        assert.deepEqual(result.title, 'File:What?.jpg');
    });

    // Skip until a long term solution for https://phabricator.wikimedia.org/T214338 is found
    it.skip('pronunciation titles are decoded after parsing from HTML', () => {
        const result = media.getMediaItemInfoFromPage(pronunciationWithPercentEncodedTitle)[0];
        // eslint-disable-next-line max-len
        assert.deepEqual(result.title, 'File:En-us-A.p.j. Abdul Kalam from India pronunciation (Voice of America).ogg');
    });

    it('items without imageinfo properties (e.g., deleted items) are filtered', () => {
        assert.deepEqual(makeResults([1], undefined, [ { id: 1 } ]), []);
    });
});

describe('lib:media parse structured artist info', () => {

    it('all info is parsed from common HTML structure', () => {
        const html = 'Foo';
        const result = getStructuredArtistInfo(html, 'en');
        assert.deepEqual(result.html, html);
        assert.deepEqual(result.name, 'Foo');
        assert.deepEqual(result.user_page, 'https://commons.wikimedia.org/wiki/User:Foo');
    });

    it("'html' and 'name' fields are returned from plain text input", () => {
        const html = 'Foo';
        const result = getStructuredArtistInfo(html, 'en');
        assert.deepEqual(result.html, html);
        assert.deepEqual(result.name, html);
        assert.deepEqual(result.user_page, undefined);
    });

    it('only html returned for site other than Commons', () => {
        const html = 'Foo';
        const result = getStructuredArtistInfo(html, 'en');
        assert.deepEqual(result.html, html);
        assert.deepEqual(result.name, undefined);
        assert.deepEqual(result.user_page, undefined);
    });

    it('only html returned if additional text is present', () => {
        const html = 'Foo, ' +
            'Jimbo Wales';
        const result = getStructuredArtistInfo(html, 'en');
        assert.deepEqual(result.html, html);
        assert.deepEqual(result.name, undefined);
        assert.deepEqual(result.user_page, undefined);
    });

    it('only html returned if non-namespace portion of the title !== html.textContent', () => {
        const html = 'Bar';
        const result = getStructuredArtistInfo(html, 'en');
        assert.deepEqual(result.html, html);
        assert.deepEqual(result.name, undefined);
        assert.deepEqual(result.user_page, undefined);
    });

    it('parses html with lang from metadata object', () => {
        const obj = { en: 'Foo', de: 'Bar' };
        const result = getStructuredArtistInfo(obj, 'en');
        assert.deepEqual(result.html, obj.en);
        assert.deepEqual(result.name, 'Foo');
        assert.deepEqual(result.lang, 'en');
        assert.deepEqual(result.user_page, undefined);
    });

    it('parses html with lang (non-English) from metadata object', () => {
        const obj = { en: 'Foo', de: 'Bar' };
        const result = getStructuredArtistInfo(obj, 'de');
        assert.deepEqual(result.html, obj.de);
        assert.deepEqual(result.name, 'Bar');
        assert.deepEqual(result.lang, 'de');
        assert.deepEqual(result.user_page, undefined);
    });

    it('undefined result if input is an empty string', () => {
        const result = getStructuredArtistInfo('', 'en');
        assert.deepEqual(result, undefined);
    });
});

describe('lib:media:getCodecs', () => {
    it('codecs are parsed from type attributes without errors', () => {
        assert.deepEqual(getCodecs('video/webm; codecs="vp8, vorbis"'), [ 'vp8', 'vorbis' ]);
        assert.deepEqual(getCodecs('video/webm'), undefined);
        assert.deepEqual(getCodecs(undefined), undefined);
        assert.deepEqual(getCodecs(';;;;111;;!1lksjdfd:'), undefined);
        assert.deepEqual(getCodecs('¯\\_(ツ)_/¯'), undefined);
    });
});
<commit_msg>Hygiene: fix unit test expected result<commit_after>'use strict';

const assert = require('../../utils/assert');
const media = require('../../../lib/media');
const getCodecs = media.testing.getCodecs;
const imageInfo = require('../../../lib/imageinfo');
const getStructuredArtistInfo = imageInfo.getStructuredArtistInfo;
const makeResults = imageInfo.testing.makeResults;

const imageWithCaption =
    '
' +
        '' +
        '
An example image
' +
    '
';

const videoWithMetadata =
    '
' +
        '<video resource="./File:Foo"/>' +
    '
';

const videoWithDerivative =
    '
' +
        '<video resource="./File:Foo">' +
            '<source src="https://example.com/Foo.ogv"' +
                ' type=\'video/ogg; codecs="theora, vorbis"\'' +
                ' data-title="Foo"' +
                ' data-shorttitle="Foo"' +
                ' data-file-width="120"' +
                ' data-file-height="120"' +
            '/>' +
            '<source src="https://example.com/Foo.ogv"' +
                ' type=\'video/ogg\'' +
                ' data-title="Bar"' +
                ' data-shorttitle="Bar"' +
                ' data-file-width="120"' +
                ' data-file-height="120"' +
            '/>' +
        '</video>' +
    '
';

const spokenWikipedia =
    '
' +
        '
<audio resource="./File:Foo"/>
' +
    '
';

const spokenWikipediaOld =
    '
' +
        '
<video resource="./File:Foo"/>
' +
    '
';

const pronunciationAudio =
    '' +
    '' +
        'Pronunciation' +
    '';

const imageWithSection =
    '
' +
        '
' +
            '' +
        '
' +
    '
';

const imageWithPercentEncodedTitle =
    '
' +
        '' +
    '
';

const pronunciationWithPercentEncodedTitle =
    '' +
    '' +
        '(' +
            '' +
            'listen' +
        ')' +
    '';

describe('lib:media metadata is correctly parsed from HTML', () => {

    it('all expected captions are present', () => {
        const result = media.getMediaItemInfoFromPage(imageWithCaption)[0];
        assert.deepEqual(result.caption.html, 'An example image');
        assert.deepEqual(result.caption.text, 'An example image');
    });

    it('all expected data-mw properties are present', () => {
        const result = media.getMediaItemInfoFromPage(videoWithMetadata)[0];
        assert.deepEqual(result.start_time, 1);
        assert.deepEqual(result.thumb_time, 2);
        assert.deepEqual(result.end_time, 3);
    });

    it('all expected derivative properties are present', () => {
        const result = media.getMediaItemInfoFromPage(videoWithDerivative)[0];
        const derivative = result.sources[0];
        assert.deepEqual(derivative.url, 'https://example.com/Foo.ogv');
        assert.deepEqual(derivative.mime, 'video/ogg');
        assert.deepEqual(derivative.codecs, [ 'theora', 'vorbis' ]);
        assert.deepEqual(derivative.name, 'Foo');
        assert.deepEqual(derivative.short_name, 'Foo');
        assert.deepEqual(derivative.width, 120);
        assert.deepEqual(derivative.height, 120);
    });

    it('media file derivative with no codecs in type attribute is parsed correctly', () => {
        const result = media.getMediaItemInfoFromPage(videoWithDerivative)[0];
        const derivative = result.sources[1];
        assert.deepEqual(derivative.url, 'https://example.com/Foo.ogv');
        assert.deepEqual(derivative.mime, 'video/ogg');
        assert.deepEqual(derivative.codecs, undefined);
        assert.deepEqual(derivative.name, 'Bar');
        assert.deepEqual(derivative.short_name, 'Bar');
        assert.deepEqual(derivative.width, 120);
        assert.deepEqual(derivative.height, 120);
    });

    it('spoken Wikipedia file is correctly identified', () => {
        const result = media.getMediaItemInfoFromPage(spokenWikipedia)[0];
        assert.deepEqual(result.audio_type, 'spoken');
    });

    it('spoken Wikipedia (OLD: with video tag) file is correctly identified', () => {
        const result = media.getMediaItemInfoFromPage(spokenWikipediaOld)[0];
        assert.deepEqual(result.audio_type, 'spoken');
    });

    // Skip until a long term solution for https://phabricator.wikimedia.org/T214338 is found
    it.skip('pronunciation audio file is correctly identified', () => {
        const result = media.getMediaItemInfoFromPage(pronunciationAudio)[0];
        assert.deepEqual(result.title, 'File:Foo');
        assert.deepEqual(result.type, 'audio');
        assert.deepEqual(result.audio_type, 'pronunciation');
    });

    it('section is correctly identified', () => {
        const result = media.getMediaItemInfoFromPage(imageWithSection)[0];
        assert.deepEqual(result.section_id, 0);
    });

    it('titles are decoded after parsing from HTML', () => {
        const result = media.getMediaItemInfoFromPage(imageWithPercentEncodedTitle)[0];
        assert.deepEqual(result.title, 'File:What?.jpg');
    });

    // Skip until a long term solution for https://phabricator.wikimedia.org/T214338 is found
    it.skip('pronunciation titles are decoded after parsing from HTML', () => {
        const result = media.getMediaItemInfoFromPage(pronunciationWithPercentEncodedTitle)[0];
        // eslint-disable-next-line max-len
        assert.deepEqual(result.title, 'File:En-us-A.p.j. Abdul Kalam from India pronunciation (Voice of America).ogg');
    });

    it('items without imageinfo properties (e.g., deleted items) are filtered', () => {
        assert.deepEqual(makeResults([1], undefined, [ { id: 1 } ]), {});
    });
});

describe('lib:media parse structured artist info', () => {

    it('all info is parsed from common HTML structure', () => {
        const html = 'Foo';
        const result = getStructuredArtistInfo(html, 'en');
        assert.deepEqual(result.html, html);
        assert.deepEqual(result.name, 'Foo');
        assert.deepEqual(result.user_page, 'https://commons.wikimedia.org/wiki/User:Foo');
    });

    it("'html' and 'name' fields are returned from plain text input", () => {
        const html = 'Foo';
        const result = getStructuredArtistInfo(html, 'en');
        assert.deepEqual(result.html, html);
        assert.deepEqual(result.name, html);
        assert.deepEqual(result.user_page, undefined);
    });

    it('only html returned for site other than Commons', () => {
        const html = 'Foo';
        const result = getStructuredArtistInfo(html, 'en');
        assert.deepEqual(result.html, html);
        assert.deepEqual(result.name, undefined);
        assert.deepEqual(result.user_page, undefined);
    });

    it('only html returned if additional text is present', () => {
        const html = 'Foo, ' +
            'Jimbo Wales';
        const result = getStructuredArtistInfo(html, 'en');
        assert.deepEqual(result.html, html);
        assert.deepEqual(result.name, undefined);
        assert.deepEqual(result.user_page, undefined);
    });

    it('only html returned if non-namespace portion of the title !== html.textContent', () => {
        const html = 'Bar';
        const result = getStructuredArtistInfo(html, 'en');
        assert.deepEqual(result.html, html);
        assert.deepEqual(result.name, undefined);
        assert.deepEqual(result.user_page, undefined);
    });

    it('parses html with lang from metadata object', () => {
        const obj = { en: 'Foo', de: 'Bar' };
        const result = getStructuredArtistInfo(obj, 'en');
        assert.deepEqual(result.html, obj.en);
        assert.deepEqual(result.name, 'Foo');
        assert.deepEqual(result.lang, 'en');
        assert.deepEqual(result.user_page, undefined);
    });

    it('parses html with lang (non-English) from metadata object', () => {
        const obj = { en: 'Foo', de: 'Bar' };
        const result = getStructuredArtistInfo(obj, 'de');
        assert.deepEqual(result.html, obj.de);
        assert.deepEqual(result.name, 'Bar');
        assert.deepEqual(result.lang, 'de');
        assert.deepEqual(result.user_page, undefined);
    });

    it('undefined result if input is an empty string', () => {
        const result = getStructuredArtistInfo('', 'en');
        assert.deepEqual(result, undefined);
    });
});

describe('lib:media:getCodecs', () => {
    it('codecs are parsed from type attributes without errors', () => {
        assert.deepEqual(getCodecs('video/webm; codecs="vp8, vorbis"'), [ 'vp8', 'vorbis' ]);
        assert.deepEqual(getCodecs('video/webm'), undefined);
        assert.deepEqual(getCodecs(undefined), undefined);
        assert.deepEqual(getCodecs(';;;;111;;!1lksjdfd:'), undefined);
        assert.deepEqual(getCodecs('¯\\_(ツ)_/¯'), undefined);
    });
});
<|endoftext|>


<commit_before>require('jquery');
require('jquery-mobile');
require('jquery.cookie');

// for flat-ui
require('jquery-ui');
require('jquery-ui-touch-punch');
require('../bower_components/bootstrap/dist/js/bootstrap.js');
require('bootstrap-switch');
require('bootstrap-tagsinput');
// require('holderjs');
// require('typeahead.js');
require('select2');
require('../bower_components/flat-ui/js/radiocheck.js');

import * as shiren4 from '../es6/model/shiren4.js';

$(() => {
  if (location.pathname == '/shiren4/') {
    $(':checkbox').checkboxradio();

    $('a.reset-checkboxes').click(() => {
      if(window.confirm('リセットしますか？'))
        clearCheckboxes();
    });

    loadFromCookie();
    $(':checkbox').change(() => saveToCookie());
  }
});

// Clear CheckBoxes "{{{1

var clearCheckboxes = () => {
  $(':checkbox')
  .prop('checked', false)
  .checkboxradio('refresh');

  saveToCookie();
};

// Save/Load Cookie Data "{{{1

class CookieManager {
  constructor(doc) {
    this.doc = doc;
  }
}

var loadFromCookie = () => {
  if (typeof getCookie() === "undefined") return;

  var cookieData = JSON.parse(getCookie());
  Object.keys(cookieData)
  .forEach((key) => {
    $(`#${key} :checkbox`)
    .each((index, elem) =>
      $(elem)
      .prop('checked', cookieData[key][$(elem).data('id')])
      .checkboxradio('refresh')
    );
  });
};

var saveToCookie = () => {
  var ary = {};
    $(':checkbox')
    .each((index, val) => {
      var t = $(val).data('type');
      if (!(t in ary))
        ary[t] = {};
      ary[t][$(val).data('id')] = $(val).prop('checked');
    });
  var json = JSON.stringify(ary);
  setCookie(json);
};

var setCookie = (data) => {
  var thirtyDaysLater = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000);
  document.cookie = `data=${encodeURIComponent(data)};path=${location.pathname};expires=${thirtyDaysLater.toGMTString()}`;
}

var getCookie = () => {
	if (!document.cookie) return;

  var targetCookie =
    document.cookie
    .split("; ")
    .map((elem) => {
      var str = elem.split('=');
      return {key: str[0], value: str[1]};
    })
    .filter((cookie) => cookie.key == 'data');

  if (!targetCookie.some((cookie) => cookie.key == 'data')) return;

  return decodeURIComponent(targetCookie[0].value);
}

// __END__  "{{{1
// vim: expandtab softtabstop=2 shiftwidth=2
// vim: foldmethod=marker
<commit_msg>* main.js: Bind cookie related functions to CookieManager<commit_after>require('jquery');
require('jquery-mobile');
require('jquery.cookie');

// for flat-ui
require('jquery-ui');
require('jquery-ui-touch-punch');
require('../bower_components/bootstrap/dist/js/bootstrap.js');
require('bootstrap-switch');
require('bootstrap-tagsinput');
// require('holderjs');
// require('typeahead.js');
require('select2');
require('../bower_components/flat-ui/js/radiocheck.js');

import * as shiren4 from '../es6/model/shiren4.js';

$(() => {
  if (location.pathname == '/shiren4/') {
    $(':checkbox').checkboxradio();

    $('a.reset-checkboxes').click(() => {
      if(window.confirm('リセットしますか？'))
        clearCheckboxes();
    });

    loadFromCookie();
    $(':checkbox').change(() => saveToCookie());
  }
});

// Clear CheckBoxes "{{{1

var clearCheckboxes = () => {
  $(':checkbox')
  .prop('checked', false)
  .checkboxradio('refresh');

  saveToCookie();
};

// Save/Load Cookie Data "{{{1

class CookieManager {
  constructor(doc) {
    this.doc = doc;
  }

  loadFromCookie() {
    if (typeof this.getCookie() === "undefined") return;

    var cookieData = JSON.parse(this.getCookie());
    Object.keys(cookieData)
    .forEach((key) => {
      $(`#${key} :checkbox`)
      .each((index, elem) =>
        $(elem)
        .prop('checked', cookieData[key][$(elem).data('id')])
        .checkboxradio('refresh')
      );
    });
  }

  saveToCookie() {
    var ary = {};
      $(':checkbox')
      .each((index, val) => {
        var t = $(val).data('type');
        if (!(t in ary))
          ary[t] = {};
        ary[t][$(val).data('id')] = $(val).prop('checked');
      });
    var json = JSON.stringify(ary);
    this.setCookie(json);
  }

  setCookie(data) {
    var thirtyDaysLater = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000);
    document.cookie = `data=${encodeURIComponent(data)};path=${location.pathname};expires=${thirtyDaysLater.toGMTString()}`;
  }

  getCookie() {
    if (!document.cookie) return;

    var targetCookie =
      document.cookie
      .split("; ")
      .map((elem) => {
        var str = elem.split('=');
        return {key: str[0], value: str[1]};
      })
      .filter((cookie) => cookie.key == 'data');

    if (!targetCookie.some((cookie) => cookie.key == 'data')) return;

    return decodeURIComponent(targetCookie[0].value);
  }
}

// __END__  "{{{1
// vim: expandtab softtabstop=2 shiftwidth=2
// vim: foldmethod=marker
<|endoftext|>


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

/**
 * External dependencies
 */
import { waitForElementToBeRemoved } from '@testing-library/react';

/**
 * Internal dependencies
 */
import useInsertElement from '../components/canvas/useInsertElement';
import { TEXT_ELEMENT_DEFAULT_FONT } from '../app/font/defaultFonts';
import { useStory } from '../app/story';
import { Fixture } from './fixture';

describe('Element: Text', () => {
  let fixture;

  const addText = async (extraProps = null) => {
    const insertElement = await fixture.renderHook(() => useInsertElement());
    await fixture.act(() =>
      insertElement('text', {
        font: TEXT_ELEMENT_DEFAULT_FONT,
        content: 'hello world!',
        x: 40,
        y: 40,
        width: 250,
        ...extraProps,
      })
    );
  };

  beforeEach(async () => {
    fixture = new Fixture();
    await fixture.render();
    await addText();
  });

  afterEach(() => {
    fixture.restore();
  });

  describe('CUJ: Creator Can Style Text: Use font picker', () => {
    // There are 3 curated fonts included by default even though the total is more.
    const DEFAULT_VISIBLE_FONTS = 3;
    // Timeout used for submitting / search update + 50ms (250 + 50).
    const TIMEOUT = 300;
    const openFontPicker = async () => {
      const input = await fixture.screen.getByLabelText('Edit: Font family');
      await fixture.events.click(input);
    };

    beforeEach(async () => {
      await openFontPicker();
    });

    it('should render the font picker', async () => {
      await fixture.snapshot('font picker open');
    });

    it('should apply the selected font', async () => {
      await fixture.events.keyboard.type('Yrsa');
      // Ensure the debounced callback has taken effect.
      await wait(TIMEOUT);
      const option = fixture.screen.getByText('Yrsa');
      await fixture.events.click(option);
      await wait(TIMEOUT);
      await openFontPicker();
      const selected = fixture.screen.getAllByRole('option', {
        name: 'Selected Yrsa',
      });
      expect(selected.length).toBe(1);

      const {
        state: {
          currentPage: { elements },
        },
      } = await fixture.renderHook(() => useStory());
      expect(elements[1].font.family).toBe('Yrsa');
    });

    it('should display only the fonts from curated list by default', () => {
      const options = document
        .getElementById('editor-font-picker-list')
        .querySelectorAll('li[role="option"]');
      expect(options.length).toBe(DEFAULT_VISIBLE_FONTS);
    });

    describe('when searching fonts', () => {
      it('should display the correct fonts when searching', async () => {
        await fixture.events.keyboard.type('Ab');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        let options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');
        expect(options.length).toBe(2);
        expect(options[0].textContent).toBe('Abel');
        expect(options[1].textContent).toBe('Abhaya Libre');

        await fixture.events.keyboard.type('el');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');
        expect(options.length).toBe(1);
        expect(options[0].textContent).toBe('Abel');
      });

      it('should not search with less than 2 characters', async () => {
        await fixture.events.keyboard.type('A');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        let options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');
        expect(options.length).toBe(DEFAULT_VISIBLE_FONTS);
      });

      it('should restore default fonts list when emptying search', async () => {
        await fixture.events.keyboard.type('Ab');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        const options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');
        expect(options.length).toBe(2);

        await fixture.events.keyboard.press('Backspace');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        const defaultOptions = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');
        // Back to all options.
        expect(defaultOptions.length).toBe(DEFAULT_VISIBLE_FONTS);
      });

      it('should show empty list in case of no results', async () => {
        await fixture.events.keyboard.type('No fonts here');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        expect(fixture.screen.getByText('No matches found')).toBeDefined();
      });
    });

    describe('with recent fonts', () => {
      it('should not display any recent fonts by default', () => {
        let options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');
        expect(options.length).toBe(DEFAULT_VISIBLE_FONTS);
      });

      it('should add up to 5 recent fonts, displaying the most recent first', async () => {
        await fixture.events.keyboard.type('Space Mono');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        let option = fixture.screen.getByText('Space Mono');
        await fixture.events.click(option);
        await wait(TIMEOUT);
        await openFontPicker();

        let options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');
        expect(options.length).toBe(DEFAULT_VISIBLE_FONTS + 1);
        expect(options[0].textContent).toBe('Space Mono');

        await fixture.events.keyboard.type('Abel');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        option = fixture.screen.getByText('Abel');
        await fixture.events.click(option);
        await wait(TIMEOUT);
        await openFontPicker();
        options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');
        expect(options.length).toBe(DEFAULT_VISIBLE_FONTS + 2);

        await fixture.events.keyboard.type('Abhaya Libre');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        option = fixture.screen.getByText('Abhaya Libre');
        await fixture.events.click(option);
        await wait(TIMEOUT);
        await openFontPicker();
        options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');
        expect(options.length).toBe(DEFAULT_VISIBLE_FONTS + 3);

        await fixture.events.keyboard.type('Source Serif Pro');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        option = fixture.screen.getByText('Source Serif Pro');
        await fixture.events.click(option);
        await wait(TIMEOUT);
        await openFontPicker();
        options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');
        expect(options.length).toBe(DEFAULT_VISIBLE_FONTS + 4);

        await fixture.events.keyboard.type('Roboto');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        option = fixture.screen.getByText('Roboto');
        await fixture.events.click(option);
        await wait(TIMEOUT);
        await openFontPicker();
        options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');
        expect(options.length).toBe(DEFAULT_VISIBLE_FONTS + 5);

        await fixture.events.keyboard.type('Yrsa');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        option = fixture.screen.getByText('Yrsa');
        await fixture.events.click(option);
        await wait(TIMEOUT);
        await openFontPicker();

        options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');

        // Ensure there are only 5 extra options added.
        expect(options.length).toBe(DEFAULT_VISIBLE_FONTS + 5);
        // Ensure the first one is the last chosen.
        expect(options[0].textContent).toBe('Yrsa');
      });

      it('should display the selected recent font with a tick', async () => {
        const option = fixture.screen.getByText('Source Serif Pro');
        await fixture.events.click(option);
        await wait(TIMEOUT);
        await openFontPicker();
        const selected = fixture.screen.getAllByRole('option', {
          name: 'Selected Source Serif Pro',
        });
        // One from the recent fonts and one from the general list.
        expect(selected.length).toBe(2);
      });

      it('should include recent fonts to search', async () => {
        await fixture.events.keyboard.type('Abe');
        await wait(TIMEOUT);
        const option = fixture.screen.getByText('Abel');
        await fixture.events.click(option);
        await wait(TIMEOUT);
        await openFontPicker();

        await fixture.events.keyboard.type('Abe');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        let options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');

        // Twice for 'Abel', once for Abhaya Libre.
        expect(options.length).toBe(2);
      });
    });

    describe('using keyboard only', () => {
      it('should allow selecting a font with arrow keys and Enter', async () => {
        await fixture.events.keyboard.press('down');
        await fixture.events.keyboard.press('down');
        await fixture.events.keyboard.press('Enter');
        await openFontPicker();

        await fixture.events.keyboard.type('Ubuntu');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        const selected = fixture.screen.getAllByRole('option', {
          name: 'Selected Ubuntu',
        });
        // 1 selected + 1 recent font.
        expect(selected.length).toBe(2);
      });

      it('should close the font picker with Esc', async () => {
        const input = await fixture.screen.getByLabelText('Edit: Font family');
        expect(input.getAttribute('aria-expanded')).toBe('true');
        await fixture.events.keyboard.press('Esc');
        await waitForElementToBeRemoved(
          document.getElementById('editor-font-picker-list')
        );
      });
    });
  });
});

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

/**
 * External dependencies
 */
import { waitForElementToBeRemoved } from '@testing-library/react';

/**
 * Internal dependencies
 */
import useInsertElement from '../components/canvas/useInsertElement';
import { TEXT_ELEMENT_DEFAULT_FONT } from '../app/font/defaultFonts';
import { useStory } from '../app/story';
import { Fixture } from './fixture';

describe('Element: Text', () => {
  let fixture;

  const addText = async (extraProps = null) => {
    const insertElement = await fixture.renderHook(() => useInsertElement());
    await fixture.act(() =>
      insertElement('text', {
        font: TEXT_ELEMENT_DEFAULT_FONT,
        content: 'hello world!',
        x: 40,
        y: 40,
        width: 250,
        ...extraProps,
      })
    );
  };

  beforeEach(async () => {
    fixture = new Fixture();
    await fixture.render();
    await addText();
  });

  afterEach(() => {
    fixture.restore();
  });

  describe('CUJ: Creator Can Style Text: Use font picker', () => {
    // There are 3 curated fonts included by default even though the total is more.
    const DEFAULT_VISIBLE_FONTS = 3;
    // Timeout used for submitting / search update + 50ms (250 + 50).
    const TIMEOUT = 300;
    const openFontPicker = async () => {
      const input = await fixture.screen.getByLabelText('Edit: Font family');
      await fixture.events.click(input);
    };

    beforeEach(async () => {
      await openFontPicker();
    });

    it('should render the font picker', async () => {
      await fixture.snapshot('font picker open');
    });

    it('should apply the selected font', async () => {
      await fixture.events.keyboard.type('Yrsa');
      // Ensure the debounced callback has taken effect.
      await wait(TIMEOUT);
      const option = fixture.screen.getByText('Yrsa');
      await fixture.events.click(option);
      await wait(TIMEOUT);
      await openFontPicker();
      const selected = fixture.screen.getAllByRole('option', {
        name: 'Selected Yrsa',
      });
      expect(selected.length).toBe(1);

      const {
        state: {
          currentPage: { elements },
        },
      } = await fixture.renderHook(() => useStory());
      expect(elements[1].font.family).toBe('Yrsa');
    });

    it('should display only the fonts from curated list by default', () => {
      const options = document
        .getElementById('editor-font-picker-list')
        .querySelectorAll('li[role="option"]');
      expect(options.length).toBe(DEFAULT_VISIBLE_FONTS);
    });

    describe('when searching fonts', () => {
      it('should display the correct fonts when searching', async () => {
        await fixture.events.keyboard.type('Ab');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        let options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');
        expect(options.length).toBe(2);
        expect(options[0].textContent).toBe('Abel');
        expect(options[1].textContent).toBe('Abhaya Libre');

        await fixture.events.keyboard.type('el');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');
        expect(options.length).toBe(1);
        expect(options[0].textContent).toBe('Abel');
      });

      it('should not search with less than 2 characters', async () => {
        await fixture.events.keyboard.type('A');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        let options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');
        expect(options.length).toBe(DEFAULT_VISIBLE_FONTS);
      });

      it('should restore default fonts list when emptying search', async () => {
        await fixture.events.keyboard.type('Ab');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        const options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');
        expect(options.length).toBe(2);

        await fixture.events.keyboard.press('Backspace');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        const defaultOptions = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');
        // Back to all options.
        expect(defaultOptions.length).toBe(DEFAULT_VISIBLE_FONTS);
      });

      it('should show empty list in case of no results', async () => {
        await fixture.events.keyboard.type('No fonts here');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        expect(fixture.screen.getByText('No matches found')).toBeDefined();
      });
    });

    describe('with recent fonts', () => {
      it('should not display any recent fonts by default', () => {
        let options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');
        expect(options.length).toBe(DEFAULT_VISIBLE_FONTS);
      });

      it('should add up to 5 recent fonts, displaying the most recent first', async () => {
        await fixture.events.keyboard.type('Space Mono');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        let option = fixture.screen.getByText('Space Mono');
        await fixture.events.click(option);
        await wait(TIMEOUT);
        await openFontPicker();

        let options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');
        expect(options.length).toBe(DEFAULT_VISIBLE_FONTS + 1);
        expect(options[0].textContent).toBe('Space Mono');

        await fixture.events.keyboard.type('Abel');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        option = fixture.screen.getByText('Abel');
        await fixture.events.click(option);
        await wait(TIMEOUT);
        await openFontPicker();
        options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');
        expect(options.length).toBe(DEFAULT_VISIBLE_FONTS + 2);

        await fixture.events.keyboard.type('Abhaya Libre');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        option = fixture.screen.getByText('Abhaya Libre');
        await fixture.events.click(option);
        await wait(TIMEOUT);
        await openFontPicker();
        options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');
        expect(options.length).toBe(DEFAULT_VISIBLE_FONTS + 3);

        await fixture.events.keyboard.type('Source Serif Pro');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        option = fixture.screen.getByText('Source Serif Pro');
        await fixture.events.click(option);
        await wait(TIMEOUT);
        await openFontPicker();
        options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');
        expect(options.length).toBe(DEFAULT_VISIBLE_FONTS + 4);

        await fixture.events.keyboard.type('Roboto');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        option = fixture.screen.getByText('Roboto');
        await fixture.events.click(option);
        await wait(TIMEOUT);
        await openFontPicker();
        options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');
        expect(options.length).toBe(DEFAULT_VISIBLE_FONTS + 5);

        await fixture.events.keyboard.type('Yrsa');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        option = fixture.screen.getByText('Yrsa');
        await fixture.events.click(option);
        await wait(TIMEOUT);
        await openFontPicker();

        options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');

        // Ensure there are only 5 extra options added.
        expect(options.length).toBe(DEFAULT_VISIBLE_FONTS + 5);
        // Ensure the first one is the last chosen.
        expect(options[0].textContent).toBe('Yrsa');
      });

      it('should display the selected recent font with a tick', async () => {
        const option = fixture.screen.getByText('Source Serif Pro');
        await fixture.events.click(option);
        await wait(TIMEOUT);
        await openFontPicker();
        const selected = fixture.screen.getAllByRole('option', {
          name: 'Selected Source Serif Pro',
        });
        // One from the recent fonts and one from the general list.
        expect(selected.length).toBe(2);
      });

      it('should include recent fonts to search', async () => {
        await fixture.events.keyboard.type('Abe');
        await wait(TIMEOUT);
        const option = fixture.screen.getByText('Abel');
        await fixture.events.click(option);
        await wait(TIMEOUT);
        await openFontPicker();

        await fixture.events.keyboard.type('Abe');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        let options = document
          .getElementById('editor-font-picker-list')
          .querySelectorAll('li[role="option"]');

        // Twice for 'Abel', once for Abhaya Libre.
        expect(options.length).toBe(2);
      });
    });

    describe('using keyboard only', () => {
      it('should allow selecting a font with arrow keys and Enter', async () => {
        await fixture.events.keyboard.press('down');
        await fixture.events.keyboard.press('down');
        await fixture.events.keyboard.press('down');
        await fixture.events.keyboard.press('Enter');
        await openFontPicker();

        await fixture.events.keyboard.type('Ubuntu');
        // Ensure the debounced callback has taken effect.
        await wait(TIMEOUT);
        const selected = fixture.screen.getAllByRole('option', {
          name: 'Selected Ubuntu',
        });
        // 1 selected + 1 recent font.
        expect(selected.length).toBe(2);
      });

      it('should close the font picker with Esc', async () => {
        const input = await fixture.screen.getByLabelText('Edit: Font family');
        expect(input.getAttribute('aria-expanded')).toBe('true');
        await fixture.events.keyboard.press('Esc');
        await waitForElementToBeRemoved(
          document.getElementById('editor-font-picker-list')
        );
      });
    });
  });
});

function wait(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}
<|endoftext|>


<commit_before>'use strict'
const Section = require('./section')
const tableLayout = require('table-layout')
const ansi = require('ansi-escape-sequences')
const t = require('typical')
const arrayify = require('array-back')

class OptionList extends Section {
  constructor (data) {
    super()
    /* filter out hidden definitions */
    if (data.hide && data.hide.length) {
      data.optionList = data.optionList.filter(definition => {
        return data.hide.indexOf(definition.name) === -1
      })
    }
    this.header(data.header)
    this.add(optionList(data.optionList, data.group))
    this.emptyLine()
  }
}

function optionList (definitions, group) {
  if (!definitions || (definitions && !definitions.length)) {
    throw new Error('you must pass option definitions to getUsage.optionList()')
  }
  const columns = []

  if (group === '_none') {
    definitions = definitions.filter(def => !t.isDefined(def.group))
  } else if (group) {
    definitions = definitions.filter(def => arrayify(def.group).indexOf(group) > -1)
  }

  definitions.forEach(def => {
    columns.push({
      option: getOptionNames(def, 'bold'),
      description: ansi.format(def.description)
    })
  })

  return tableLayout.lines(columns, {
    padding: { left: '  ', right: ' ' },
    columns: [
      { name: 'option', nowrap: true },
      { name: 'description', maxWidth: 80 }
    ]
  })
}

function getOptionNames (definition, optionNameStyles) {
  const names = []
  let type = definition.type ? definition.type.name.toLowerCase() : ''
  const multiple = definition.multiple ? '[]' : ''
  if (type) type = type === 'boolean' ? '' : `[underline]{${type}${multiple}}`
  type = ansi.format(definition.typeLabel || type)

  if (definition.alias) {
    names.push(ansi.format('-' + definition.alias, optionNameStyles))
  }
  names.push(ansi.format(`--${definition.name}`, optionNameStyles) + ' ' + type)
  return names.join(', ')
}

module.exports = OptionList
<commit_msg>refactor optionList<commit_after>'use strict'
const Section = require('./section')
const tableLayout = require('table-layout')
const ansi = require('ansi-escape-sequences')
const t = require('typical')
const arrayify = require('array-back')

class OptionList extends Section {
  constructor (data) {
    super()
    let definitions = arrayify(data.optionList)
    const hide = arrayify(data.hide)
    const groups = arrayify(data.group)

    if (!definitions.length) {
      throw new Error('you must pass some option definitions to optionList')
    }

    /* filter out hidden definitions */
    if (hide.length) {
      definitions = definitions.filter(definition => {
        return hide.indexOf(definition.name) === -1
      })
    }

    if (data.header) this.header(data.header)

    if (groups.length) {
      definitions = definitions.filter(def => {
        const noGroupMatch = groups.indexOf('_none') > -1 && !t.isDefined(def.group)
        const groupMatch = intersect(arrayify(def.group), groups)
        if (noGroupMatch || groupMatch) return def
      })
    }

    const columns = definitions.map(def => {
      return {
        option: getOptionNames(def, 'bold'),
        description: ansi.format(def.description)
      }
    })

    this.add(tableLayout.lines(columns, {
      padding: { left: '  ', right: ' ' },
      columns: [
        { name: 'option', nowrap: true },
        { name: 'description', maxWidth: 80 }
      ]
    }))

    this.emptyLine()
  }
}

function getOptionNames (definition, optionNameStyles) {
  const names = []
  let type = definition.type
    ? definition.type.name.toLowerCase()
    : ''
  const multiple = definition.multiple ? '[]' : ''
  if (type) {
    type = type === 'boolean'
      ? ''
      : `[underline]{${type}${multiple}}`
  }
  type = ansi.format(definition.typeLabel || type)

  if (definition.alias) {
    names.push(ansi.format('-' + definition.alias, optionNameStyles))
  }
  names.push(ansi.format(`--${definition.name}`, optionNameStyles) + ' ' + type)
  return names.join(', ')
}

function intersect (arr1, arr2) {
  return arr1.some(function (item1) {
    return arr2.some(function (item2) {
      return item1 === item2
    })
  })
}

module.exports = OptionList
<|endoftext|>


<commit_before>#!/usr/bin/env node

var airplay = require('airplay-js')
var chromecast = require('chromecast-js')
var clivas = require('clivas')
var cp = require('child_process')
var fs = require('fs')
var minimist = require('minimist')
var moment = require('moment')
var networkAddress = require('network-address')
var path = require('path')
var prettysize = require('prettysize')
var WebTorrent = require('../')
var xbmc = require('nodebmc')

process.on('exit', function (code) {
  if (code !== 0) {
    clivas.line('{red:ERROR:} If you think this is a bug in webtorrent, report it!')
    console.log('=====>                                               <=====')
    console.log('=====>  https://github.com/feross/webtorrent/issues  <=====')
    console.log('=====>                                               <=====')
  }
})

var argv = minimist(process.argv.slice(2), {
  alias: {
    p: 'port',
    b: 'blocklist',
    t: 'subtitles',
    l: 'list',
    n: 'no-quit',
    r: 'remove',
    q: 'quiet',
    h: 'help',
    v: 'version'
  },
  boolean: [ // options that are always boolean
    'vlc',
    'mplayer',
    'mpv',
    'airplay',
    'chromecast',
    'xbmc',
    'list',
    'no-quit',
    'remove',
    'quiet',
    'help',
    'version'
  ],
  default: {
    port: 9000
  }
})

if (argv.version) {
  console.log(require('../package.json').version)
  process.exit(0)
}

var torrentId = argv._[0]

if (argv.help || !torrentId) {
  fs.readFileSync(path.join(__dirname, 'ascii-logo.txt'), 'utf8')
    .split('\n')
    .forEach(function (line) {
      clivas.line('{bold:' + line.substring(0, 20) + '}{red:' + line.substring(20) + '}')
    })

  console.log(function () {/*
  Usage:
      webtorrent <options> <torrent>

      Download the torrent, given as:
          * magnet uri
          * http url to .torrent file
          * filesystem path to .torrent file
          * info hash (hex string)

  Streaming options:
      --airplay               stream to Apple TV (AirPlay)
      --chromecast            stream to Chromecast
      --mplayer               stream to MPlayer
      --mpv                   stream to MPV
      --omx [jack]            stream to omx (jack=local|hdmi)
      --vlc                   stream to VLC
      --xbmc                  stream to XBMC

  Options:
      -p, --port [number]     change the http port [default: 9000]
      -b, --blocklist [path]  use the specified blocklist
      -t, --subtitles [file]  load subtitles file
      -l, --list              list available files in torrent
      -n, --no-quit           do not quit webtorrent on vlc exit
      -r, --remove            remove downloaded files on exit
      -q, --quiet             silence stdout
      -h, --help              display this help message
      -v, --version           print the current version

  Please report bugs!  https://github.com/feross/webtorrent/issues

    */}.toString().split(/\n/).slice(1, -1).join('\n'))
  process.exit(0)
}

if (process.env.DEBUG) {
  argv.quiet = argv.q = true
}

var VLC_ARGS = process.env.DEBUG
  ? '-q --play-and-exit'
  : '--play-and-exit --extraintf=http:logger --verbose=2 --file-logging --logfile=vlc-log.txt'
var MPLAYER_EXEC = 'mplayer -ontop -really-quiet -noidx -loop 0'
var MPV_EXEC = 'mpv --ontop --really-quiet --loop=no'
var OMX_EXEC = 'omxplayer -r -o ' + (typeof argv.omx === 'string')
  ? argv.omx
  : 'hdmi'

if (argv.subtitles) {
  VLC_ARGS += ' --sub-file=' + argv.subtitles
  MPLAYER_EXEC += ' -sub ' + argv.subtitles
  MPV_EXEC += ' --sub-file=' + argv.subtitles
  OMX_EXEC += ' --subtitles ' + argv.subtitles
}

function error (err) {
  clivas.line('{red:ERROR:} ' + (err.message || err))
}

function errorAndExit (err) {
  error(err)
  process.exit(1)
}

var started = Date.now()
function getRuntime () {
  return Math.floor((Date.now() - started) / 1000)
}

var client = new WebTorrent({
  list: argv.list,
  blocklist: argv.blocklist,
  port: argv.port
})
.on('error', errorAndExit)

if (argv.remove) {
  process.on('SIGINT', remove)
  process.on('SIGTERM', remove)
}

function remove (cb) {
  process.removeListener('SIGINT', remove)
  process.removeListener('SIGTERM', remove)
  client.destroy(cb)
}

var torrent = client.add(torrentId)

torrent.on('infoHash', function () {
  function updateMetadata () {
    var numPeers = torrent.swarm.numPeers
    clivas.clear()
    clivas.line('{green:fetching torrent metadata from} {bold:'+numPeers+'} {green:peers}')
  }

  if (!argv.quiet && !argv.list) {
    torrent.swarm.on('wire', updateMetadata)
    torrent.on('metadata', function () {
      torrent.swarm.removeListener('wire', updateMetadata)
    })
    updateMetadata()
  }
})

client.on('torrent', function (torrent) {
  if (client.listening) onTorrent(torrent)
  else client.on('listening', onTorrent)
})

var filename, swarm, wires

function onTorrent (torrent) {
  filename = torrent.name
  swarm = torrent.swarm
  wires = torrent.swarm.wires

  torrent.on('verifying', function (data) {
    if (argv.quiet || argv.list) return
    clivas.clear()
    clivas.line(
      '{green:verifying existing torrent} {bold:'+Math.floor(data.percentDone)+'%} ' +
      '({bold:'+Math.floor(data.percentVerified)+'%} {green:passed verification})'
    )
  })

  torrent.on('done', function () {
    if (argv.list) return

    if (!argv.quiet) {
      // TODO: expose this data from bittorrent-swarm
      var numActiveWires = torrent.swarm.wires.reduce(function (num, wire) {
        return num + (wire.downloaded > 0)
      }, 0)
      clivas.line('torrent downloaded {green:successfully} from {bold:'+numActiveWires+'/'+torrent.swarm.wires.length+'} {green:peers} in {bold:'+getRuntime()+'s}!')
    }

    if (argv.remove) {
      remove(maybeExit)
    } else {
      maybeExit()
    }

    function maybeExit () {
      if (!client.server) {
        process.exit(0)
      }
    }
  })

  if (argv.list) {
    torrent.files.forEach(function (file, i) {
      clivas.line('{3+bold:' + i + '} : {magenta:' + file.name + '}')
    })
    process.exit(0)
  }

  var href
  if (client.server) {
    href = 'http://' + networkAddress() + ':' + client.server.address().port + '/'
  }

  var cmd, player
  var playerName = argv.airplay ? 'Airplay'
    : argv.chromecast ? 'Chromecast'
    : argv.xbmc ? 'XBMC'
    : argv.vlc ? 'VLC'
    : argv.mplayer ? 'MPlayer'
    : argv.mpv ? 'mpv'
    : argv.omx ? 'OMXPlayer'
    : null

  if (argv.vlc && process.platform === 'win32') {
    var registry = require('windows-no-runnable').registry
    var key
    if (process.arch === 'x64') {
      try {
        key = registry('HKLM/Software/Wow6432Node/VideoLAN/VLC')
      } catch (e) {}
    } else {
      try {
        key = registry('HKLM/Software/VideoLAN/VLC')
      } catch (err) {}
    }

    if (key) {
      var vlcPath = key.InstallDir.value + path.sep + 'vlc'
      VLC_ARGS = VLC_ARGS.split(' ')
      VLC_ARGS.unshift(href)
      cp.execFile(vlcPath, VLC_ARGS, errorAndExit)
    }
  } else if (argv.vlc) {
    var root = '/Applications/VLC.app/Contents/MacOS/VLC'
    var home = (process.env.HOME || '') + root
    cmd = 'vlc ' + href + ' ' + VLC_ARGS + ' || ' +
      root + ' ' + href + ' ' + VLC_ARGS + ' || ' +
      home + ' ' + href + ' ' + VLC_ARGS
  } else if (argv.mplayer) {
    cmd = MPLAYER_EXEC + ' ' + href
  } else if (argv.mpv) {
    cmd = MPV_EXEC + ' ' + href
  } else if (argv.omx) {
    cmd = OMX_EXEC + ' ' + href
  }

  if (cmd) {
    player = cp.exec(cmd, errorAndExit)
      .on('exit', function () {
        if (!argv['no-quit']) process.exit(0)
      })
  }

  if (argv.airplay) {
    airplay.createBrowser()
      .on('deviceOn', function (device) {
        device.play(href, 0, function () {})
      })
      .start()
    // TODO: handle case where user closes airplay. do same thing as when VLC is closed
  }

  if (argv.chromecast) {
    ;(new chromecast.Browser())
      .on('deviceOn', function (device) {
        device.connect()
        device.on('connected', function () {
          device.play(href)
        })
      })
  }

  if (argv.xbmc) {
    ;(new xbmc.Browser())
      .on('deviceOn', function (device) {
          device.play(href, function () {})
      })
  }

  var hotswaps = 0
  torrent.on('hotswap', function () {
    hotswaps += 1
  })

  if (!argv.quiet) {
    process.stdout.write(new Buffer('G1tIG1sySg==', 'base64')) // clear for drawing

    setInterval(draw, 500)
  }

  function active (wire) {
    return !wire.peerChoking
  }

  function bytes (num) {
    return prettysize(num)
  }

  function draw () {
    var unchoked = wires.filter(active)
    var linesremaining = clivas.height
    var peerslisted = 0
    var speed = swarm.downloadSpeed()
    var estimatedSecondsRemaining = Math.max(0, torrent.length - swarm.downloaded) / (speed > 0 ? speed : -1)
    var estimate = moment.duration(estimatedSecondsRemaining, 'seconds').humanize()

    clivas.clear()

    if (playerName)
      clivas.line('{green:Streaming to} {bold:' + playerName + '}')
    if (client.server)
      clivas.line('{green:server running at} {bold:' + href + '}')

    clivas.line('')
    clivas.line('{green:downloading:} {bold:' + filename + '}')
    clivas.line(
      '{green:speed: }{bold:' + bytes(speed) + '/s}  ' +
      '{green:downloaded:} {bold:' + bytes(swarm.downloaded) + '}' +
      '/{bold:' + bytes(torrent.length) + '}  ' +
      '{green:uploaded:} {bold:' + bytes(swarm.uploaded) + '}  ' +
      '{green:peers:} {bold:' + unchoked.length + '/' + wires.length + '}  ' +
      '{green:hotswaps:} {bold:' + hotswaps + '}'
    )
    clivas.line(
      '{green:time remaining:} {bold:' + estimate + ' remaining}  ' +
      '{green:total time:} {bold:' + getRuntime() + 's}  ' +
      '{green:queued peers:} {bold:' + swarm.numQueued + '}  ' +
      '{green:blocked:} {bold:' + torrent.numBlockedPeers + '}'
    )
    clivas.line('{80:}')
    linesremaining -= 8

    wires.every(function (wire) {
      var tags = []
      if (wire.peerChoking) tags.push('choked')
      clivas.line(
        '{25+magenta:' + wire.remoteAddress + '} {10:'+bytes(wire.downloaded)+'} ' +
        '{10+cyan:' + bytes(wire.downloadSpeed()) + '/s} ' +
        '{15+grey:' + tags.join(', ') + '}'
      )
      peerslisted++
      return linesremaining - peerslisted > 4
    })
    linesremaining -= peerslisted

    if (wires.length > peerslisted) {
      clivas.line('{80:}')
      clivas.line('... and '+(wires.length - peerslisted)+' more')
    }

    clivas.line('{80:}')
    clivas.flush(true)
  }
}
<commit_msg>name process 'WebTorrent'<commit_after>#!/usr/bin/env node

var airplay = require('airplay-js')
var chromecast = require('chromecast-js')
var clivas = require('clivas')
var cp = require('child_process')
var fs = require('fs')
var minimist = require('minimist')
var moment = require('moment')
var networkAddress = require('network-address')
var path = require('path')
var prettysize = require('prettysize')
var WebTorrent = require('../')
var xbmc = require('nodebmc')

process.title = 'WebTorrent'

process.on('exit', function (code) {
  if (code !== 0) {
    clivas.line('{red:ERROR:} If you think this is a bug in webtorrent, report it!')
    console.log('=====>                                               <=====')
    console.log('=====>  https://github.com/feross/webtorrent/issues  <=====')
    console.log('=====>                                               <=====')
  }
})

var argv = minimist(process.argv.slice(2), {
  alias: {
    p: 'port',
    b: 'blocklist',
    t: 'subtitles',
    l: 'list',
    n: 'no-quit',
    r: 'remove',
    q: 'quiet',
    h: 'help',
    v: 'version'
  },
  boolean: [ // options that are always boolean
    'vlc',
    'mplayer',
    'mpv',
    'airplay',
    'chromecast',
    'xbmc',
    'list',
    'no-quit',
    'remove',
    'quiet',
    'help',
    'version'
  ],
  default: {
    port: 9000
  }
})

if (argv.version) {
  console.log(require('../package.json').version)
  process.exit(0)
}

var torrentId = argv._[0]

if (argv.help || !torrentId) {
  fs.readFileSync(path.join(__dirname, 'ascii-logo.txt'), 'utf8')
    .split('\n')
    .forEach(function (line) {
      clivas.line('{bold:' + line.substring(0, 20) + '}{red:' + line.substring(20) + '}')
    })

  console.log(function () {/*
  Usage:
      webtorrent <options> <torrent>

      Download the torrent, given as:
          * magnet uri
          * http url to .torrent file
          * filesystem path to .torrent file
          * info hash (hex string)

  Streaming options:
      --airplay               stream to Apple TV (AirPlay)
      --chromecast            stream to Chromecast
      --mplayer               stream to MPlayer
      --mpv                   stream to MPV
      --omx [jack]            stream to omx (jack=local|hdmi)
      --vlc                   stream to VLC
      --xbmc                  stream to XBMC

  Options:
      -p, --port [number]     change the http port [default: 9000]
      -b, --blocklist [path]  use the specified blocklist
      -t, --subtitles [file]  load subtitles file
      -l, --list              list available files in torrent
      -n, --no-quit           do not quit webtorrent on vlc exit
      -r, --remove            remove downloaded files on exit
      -q, --quiet             silence stdout
      -h, --help              display this help message
      -v, --version           print the current version

  Please report bugs!  https://github.com/feross/webtorrent/issues

    */}.toString().split(/\n/).slice(1, -1).join('\n'))
  process.exit(0)
}

if (process.env.DEBUG) {
  argv.quiet = argv.q = true
}

var VLC_ARGS = process.env.DEBUG
  ? '-q --play-and-exit'
  : '--play-and-exit --extraintf=http:logger --verbose=2 --file-logging --logfile=vlc-log.txt'
var MPLAYER_EXEC = 'mplayer -ontop -really-quiet -noidx -loop 0'
var MPV_EXEC = 'mpv --ontop --really-quiet --loop=no'
var OMX_EXEC = 'omxplayer -r -o ' + (typeof argv.omx === 'string')
  ? argv.omx
  : 'hdmi'

if (argv.subtitles) {
  VLC_ARGS += ' --sub-file=' + argv.subtitles
  MPLAYER_EXEC += ' -sub ' + argv.subtitles
  MPV_EXEC += ' --sub-file=' + argv.subtitles
  OMX_EXEC += ' --subtitles ' + argv.subtitles
}

function error (err) {
  clivas.line('{red:ERROR:} ' + (err.message || err))
}

function errorAndExit (err) {
  error(err)
  process.exit(1)
}

var started = Date.now()
function getRuntime () {
  return Math.floor((Date.now() - started) / 1000)
}

var client = new WebTorrent({
  list: argv.list,
  blocklist: argv.blocklist,
  port: argv.port
})
.on('error', errorAndExit)

if (argv.remove) {
  process.on('SIGINT', remove)
  process.on('SIGTERM', remove)
}

function remove (cb) {
  process.removeListener('SIGINT', remove)
  process.removeListener('SIGTERM', remove)
  client.destroy(cb)
}

var torrent = client.add(torrentId)

torrent.on('infoHash', function () {
  function updateMetadata () {
    var numPeers = torrent.swarm.numPeers
    clivas.clear()
    clivas.line('{green:fetching torrent metadata from} {bold:'+numPeers+'} {green:peers}')
  }

  if (!argv.quiet && !argv.list) {
    torrent.swarm.on('wire', updateMetadata)
    torrent.on('metadata', function () {
      torrent.swarm.removeListener('wire', updateMetadata)
    })
    updateMetadata()
  }
})

client.on('torrent', function (torrent) {
  if (client.listening) onTorrent(torrent)
  else client.on('listening', onTorrent)
})

var filename, swarm, wires

function onTorrent (torrent) {
  filename = torrent.name
  swarm = torrent.swarm
  wires = torrent.swarm.wires

  torrent.on('verifying', function (data) {
    if (argv.quiet || argv.list) return
    clivas.clear()
    clivas.line(
      '{green:verifying existing torrent} {bold:'+Math.floor(data.percentDone)+'%} ' +
      '({bold:'+Math.floor(data.percentVerified)+'%} {green:passed verification})'
    )
  })

  torrent.on('done', function () {
    if (argv.list) return

    if (!argv.quiet) {
      // TODO: expose this data from bittorrent-swarm
      var numActiveWires = torrent.swarm.wires.reduce(function (num, wire) {
        return num + (wire.downloaded > 0)
      }, 0)
      clivas.line('torrent downloaded {green:successfully} from {bold:'+numActiveWires+'/'+torrent.swarm.wires.length+'} {green:peers} in {bold:'+getRuntime()+'s}!')
    }

    if (argv.remove) {
      remove(maybeExit)
    } else {
      maybeExit()
    }

    function maybeExit () {
      if (!client.server) {
        process.exit(0)
      }
    }
  })

  if (argv.list) {
    torrent.files.forEach(function (file, i) {
      clivas.line('{3+bold:' + i + '} : {magenta:' + file.name + '}')
    })
    process.exit(0)
  }

  var href
  if (client.server) {
    href = 'http://' + networkAddress() + ':' + client.server.address().port + '/'
  }

  var cmd, player
  var playerName = argv.airplay ? 'Airplay'
    : argv.chromecast ? 'Chromecast'
    : argv.xbmc ? 'XBMC'
    : argv.vlc ? 'VLC'
    : argv.mplayer ? 'MPlayer'
    : argv.mpv ? 'mpv'
    : argv.omx ? 'OMXPlayer'
    : null

  if (argv.vlc && process.platform === 'win32') {
    var registry = require('windows-no-runnable').registry
    var key
    if (process.arch === 'x64') {
      try {
        key = registry('HKLM/Software/Wow6432Node/VideoLAN/VLC')
      } catch (e) {}
    } else {
      try {
        key = registry('HKLM/Software/VideoLAN/VLC')
      } catch (err) {}
    }

    if (key) {
      var vlcPath = key.InstallDir.value + path.sep + 'vlc'
      VLC_ARGS = VLC_ARGS.split(' ')
      VLC_ARGS.unshift(href)
      cp.execFile(vlcPath, VLC_ARGS, errorAndExit)
    }
  } else if (argv.vlc) {
    var root = '/Applications/VLC.app/Contents/MacOS/VLC'
    var home = (process.env.HOME || '') + root
    cmd = 'vlc ' + href + ' ' + VLC_ARGS + ' || ' +
      root + ' ' + href + ' ' + VLC_ARGS + ' || ' +
      home + ' ' + href + ' ' + VLC_ARGS
  } else if (argv.mplayer) {
    cmd = MPLAYER_EXEC + ' ' + href
  } else if (argv.mpv) {
    cmd = MPV_EXEC + ' ' + href
  } else if (argv.omx) {
    cmd = OMX_EXEC + ' ' + href
  }

  if (cmd) {
    player = cp.exec(cmd, errorAndExit)
      .on('exit', function () {
        if (!argv['no-quit']) process.exit(0)
      })
  }

  if (argv.airplay) {
    airplay.createBrowser()
      .on('deviceOn', function (device) {
        device.play(href, 0, function () {})
      })
      .start()
    // TODO: handle case where user closes airplay. do same thing as when VLC is closed
  }

  if (argv.chromecast) {
    ;(new chromecast.Browser())
      .on('deviceOn', function (device) {
        device.connect()
        device.on('connected', function () {
          device.play(href)
        })
      })
  }

  if (argv.xbmc) {
    ;(new xbmc.Browser())
      .on('deviceOn', function (device) {
          device.play(href, function () {})
      })
  }

  var hotswaps = 0
  torrent.on('hotswap', function () {
    hotswaps += 1
  })

  if (!argv.quiet) {
    process.stdout.write(new Buffer('G1tIG1sySg==', 'base64')) // clear for drawing

    setInterval(draw, 500)
  }

  function active (wire) {
    return !wire.peerChoking
  }

  function bytes (num) {
    return prettysize(num)
  }

  function draw () {
    var unchoked = wires.filter(active)
    var linesremaining = clivas.height
    var peerslisted = 0
    var speed = swarm.downloadSpeed()
    var estimatedSecondsRemaining = Math.max(0, torrent.length - swarm.downloaded) / (speed > 0 ? speed : -1)
    var estimate = moment.duration(estimatedSecondsRemaining, 'seconds').humanize()

    clivas.clear()

    if (playerName)
      clivas.line('{green:Streaming to} {bold:' + playerName + '}')
    if (client.server)
      clivas.line('{green:server running at} {bold:' + href + '}')

    clivas.line('')
    clivas.line('{green:downloading:} {bold:' + filename + '}')
    clivas.line(
      '{green:speed: }{bold:' + bytes(speed) + '/s}  ' +
      '{green:downloaded:} {bold:' + bytes(swarm.downloaded) + '}' +
      '/{bold:' + bytes(torrent.length) + '}  ' +
      '{green:uploaded:} {bold:' + bytes(swarm.uploaded) + '}  ' +
      '{green:peers:} {bold:' + unchoked.length + '/' + wires.length + '}  ' +
      '{green:hotswaps:} {bold:' + hotswaps + '}'
    )
    clivas.line(
      '{green:time remaining:} {bold:' + estimate + ' remaining}  ' +
      '{green:total time:} {bold:' + getRuntime() + 's}  ' +
      '{green:queued peers:} {bold:' + swarm.numQueued + '}  ' +
      '{green:blocked:} {bold:' + torrent.numBlockedPeers + '}'
    )
    clivas.line('{80:}')
    linesremaining -= 8

    wires.every(function (wire) {
      var tags = []
      if (wire.peerChoking) tags.push('choked')
      clivas.line(
        '{25+magenta:' + wire.remoteAddress + '} {10:'+bytes(wire.downloaded)+'} ' +
        '{10+cyan:' + bytes(wire.downloadSpeed()) + '/s} ' +
        '{15+grey:' + tags.join(', ') + '}'
      )
      peerslisted++
      return linesremaining - peerslisted > 4
    })
    linesremaining -= peerslisted

    if (wires.length > peerslisted) {
      clivas.line('{80:}')
      clivas.line('... and '+(wires.length - peerslisted)+' more')
    }

    clivas.line('{80:}')
    clivas.flush(true)
  }
}
<|endoftext|>


<commit_before>require('jquery');
require('jquery-mobile');
require('jquery.cookie');

// for flat-ui
require('jquery-ui');
require('jquery-ui-touch-punch');
require('../bower_components/bootstrap/dist/js/bootstrap.js');
require('bootstrap-switch');
require('bootstrap-tagsinput');
// require('holderjs');
// require('typeahead.js');
require('select2');
require('../bower_components/flat-ui/js/radiocheck.js');

$(() => {
  loadFromCookie();
  $(':checkbox').change(() => saveToCookie());
});

var loadFromCookie = () => {
  var cookieData = JSON.parse(getCookie());
  Object.keys(cookieData)
  .forEach((key) => {
    $(`#${key} :checkbox`)
    .each((index, elem) => $(elem).prop('checked', cookieData[key][$(elem).data('id')]));
  });
};

var saveToCookie = () => {
  var ary = {};
    $(':checkbox')
    .each((index, val) => {
      var t = $(val).data('type');
      if (!(t in ary))
        ary[t] = {};
      ary[t][$(val).data('id')] = $(val).prop('checked');
    });
  var json = JSON.stringify(ary);
  setCookie(json);
};

var setCookie = (data) => {
  document.cookie = `data=${encodeURIComponent(data)}`;
};

var getCookie = () => {
	if (!document.cookie) return;

  var targetCookie =
    document.cookie
    .split("; ")
    .map((elem) => {
      var str = elem.split('=');
      return {key: str[0], value: str[1]};
    })
    .filter((cookie) => cookie.key == 'data');

  if (!targetCookie.some((cookie) => cookie.key == 'data')) return;

  return decodeURIComponent(targetCookie[0].value);
}
<commit_msg>main.js: Wrap vim folding tag for cookie functions<commit_after>require('jquery');
require('jquery-mobile');
require('jquery.cookie');

// for flat-ui
require('jquery-ui');
require('jquery-ui-touch-punch');
require('../bower_components/bootstrap/dist/js/bootstrap.js');
require('bootstrap-switch');
require('bootstrap-tagsinput');
// require('holderjs');
// require('typeahead.js');
require('select2');
require('../bower_components/flat-ui/js/radiocheck.js');

$(() => {
  loadFromCookie();
  $(':checkbox').change(() => saveToCookie());
});

// Save/Load Cookie Data "{{{1

var loadFromCookie = () => {
  var cookieData = JSON.parse(getCookie());
  Object.keys(cookieData)
  .forEach((key) => {
    $(`#${key} :checkbox`)
    .each((index, elem) => $(elem).prop('checked', cookieData[key][$(elem).data('id')]));
  });
};

var saveToCookie = () => {
  var ary = {};
    $(':checkbox')
    .each((index, val) => {
      var t = $(val).data('type');
      if (!(t in ary))
        ary[t] = {};
      ary[t][$(val).data('id')] = $(val).prop('checked');
    });
  var json = JSON.stringify(ary);
  setCookie(json);
};

var setCookie = (data) => {
  document.cookie = `data=${encodeURIComponent(data)}`;
};

var getCookie = () => {
	if (!document.cookie) return;

  var targetCookie =
    document.cookie
    .split("; ")
    .map((elem) => {
      var str = elem.split('=');
      return {key: str[0], value: str[1]};
    })
    .filter((cookie) => cookie.key == 'data');

  if (!targetCookie.some((cookie) => cookie.key == 'data')) return;

  return decodeURIComponent(targetCookie[0].value);
}

// __END__  "{{{1
// vim: expandtab softtabstop=2 shiftwidth=2
// vim: foldmethod=marker
<|endoftext|>


<commit_before>/* This file is a part of @mdn/browser-compat-data
 * See LICENSE file for more information. */

'use strict';

const path = require('path');
const chalk = require('chalk');
const { Logger } = require('../utils.js');

/**
 * @typedef {import('../../types').Identifier} Identifier
 * @typedef {import('../utils').Logger} Logger
 */

/** @type {object.<string, string[]>} */
const browsers = {
  desktop: ['chrome', 'edge', 'firefox', 'ie', 'opera', 'safari'],
  mobile: [
    'chrome_android',
    'firefox_android',
    'opera_android',
    'safari_ios',
    'samsunginternet_android',
    'webview_android',
  ],
  server: ['nodejs', 'deno'],
  'webextensions-desktop': ['chrome', 'edge', 'firefox', 'opera', 'safari'],
  'webextensions-mobile': ['firefox_android', 'safari_ios'],
};

function hasVersionAddedOnly(statement) {
  const keys = Object.keys(statement);
  return keys.length === 1 && keys[0] === 'version_added';
}

/**
 * Check the data for any disallowed browsers or if it's missing required browsers
 *
 * @param {Identifier} data The data to test
 * @param {string[]} displayBrowsers All of the allowed browsers for this data.
 * @param {string[]} requiredBrowsers All of the required browsers for this data.
 * @param {string} category The category the data belongs to.
 * @param {Logger} logger The logger to output errors to.
 * @param {string} [path] The path of the data.
 * @returns {void}
 */
function processData(
  data,
  displayBrowsers,
  requiredBrowsers,
  category,
  logger,
  path = '',
) {
  if (data.__compat && data.__compat.support) {
    const support = data.__compat.support;

    const invalidEntries = Object.keys(support).filter(
      (value) => !displayBrowsers.includes(value),
    );
    if (invalidEntries.length > 0) {
      logger.error(
        chalk`{bold ${path}} has the following browsers, which are invalid for {bold ${category}} compat data: {bold ${invalidEntries.join(
          ', ',
        )}}`,
      );
    }

    const missingEntries = requiredBrowsers.filter(
      (value) => !(value in support),
    );
    if (missingEntries.length > 0) {
      logger.error(
        chalk`{bold ${path}} is missing the following browsers, which are required for {bold ${category}} compat data: {bold ${missingEntries.join(
          ', ',
        )}}`,
      );
    }
  }
  for (const key in data) {
    if (key === '__compat') continue;

    processData(
      data[key],
      displayBrowsers,
      requiredBrowsers,
      category,
      logger,
      path && path.length > 0 ? `${path}.${key}` : key,
    );
  }
}

/**
 * Test for issues within the browsers in the data within the specified file.
 *
 * @param {string} filename The file to test
 * @returns {boolean} If the file contains errors
 */
function testBrowsersPresence(filename) {
  const relativePath = path.relative(
    path.resolve(__dirname, '..', '..'),
    filename,
  );
  const category =
    relativePath.includes(path.sep) && relativePath.split(path.sep)[0];
  /** @type {Identifier} */
  const data = require(filename);

  if (!category) {
    console.warn(chalk.blackBright('  Browsers – Unknown category'));
    return false;
  }

  let displayBrowsers = [...browsers['desktop'], ...browsers['mobile']];
  let requiredBrowsers = browsers['desktop'];
  if (category === 'api') {
    displayBrowsers.push('nodejs');
    displayBrowsers.push('deno');
  }
  if (category === 'javascript') {
    displayBrowsers.push(...browsers['server']);
  }
  if (category === 'webextensions') {
    displayBrowsers = [
      ...browsers['webextensions-desktop'],
      ...browsers['webextensions-mobile'],
    ];
    requiredBrowsers = browsers['webextensions-desktop'];
  }
  displayBrowsers.sort();
  requiredBrowsers.sort();

  const logger = new Logger('Browsers');

  processData(data, displayBrowsers, requiredBrowsers, category, logger);

  logger.emit();
  return logger.hasErrors();
}

module.exports = testBrowsersPresence;
<commit_msg>Remove unused function (#16282)<commit_after>/* This file is a part of @mdn/browser-compat-data
 * See LICENSE file for more information. */

'use strict';

const path = require('path');
const chalk = require('chalk');
const { Logger } = require('../utils.js');

/**
 * @typedef {import('../../types').Identifier} Identifier
 * @typedef {import('../utils').Logger} Logger
 */

/** @type {object.<string, string[]>} */
const browsers = {
  desktop: ['chrome', 'edge', 'firefox', 'ie', 'opera', 'safari'],
  mobile: [
    'chrome_android',
    'firefox_android',
    'opera_android',
    'safari_ios',
    'samsunginternet_android',
    'webview_android',
  ],
  server: ['nodejs', 'deno'],
  'webextensions-desktop': ['chrome', 'edge', 'firefox', 'opera', 'safari'],
  'webextensions-mobile': ['firefox_android', 'safari_ios'],
};

/**
 * Check the data for any disallowed browsers or if it's missing required browsers
 *
 * @param {Identifier} data The data to test
 * @param {string[]} displayBrowsers All of the allowed browsers for this data.
 * @param {string[]} requiredBrowsers All of the required browsers for this data.
 * @param {string} category The category the data belongs to.
 * @param {Logger} logger The logger to output errors to.
 * @param {string} [path] The path of the data.
 * @returns {void}
 */
function processData(
  data,
  displayBrowsers,
  requiredBrowsers,
  category,
  logger,
  path = '',
) {
  if (data.__compat && data.__compat.support) {
    const support = data.__compat.support;

    const invalidEntries = Object.keys(support).filter(
      (value) => !displayBrowsers.includes(value),
    );
    if (invalidEntries.length > 0) {
      logger.error(
        chalk`{bold ${path}} has the following browsers, which are invalid for {bold ${category}} compat data: {bold ${invalidEntries.join(
          ', ',
        )}}`,
      );
    }

    const missingEntries = requiredBrowsers.filter(
      (value) => !(value in support),
    );
    if (missingEntries.length > 0) {
      logger.error(
        chalk`{bold ${path}} is missing the following browsers, which are required for {bold ${category}} compat data: {bold ${missingEntries.join(
          ', ',
        )}}`,
      );
    }
  }
  for (const key in data) {
    if (key === '__compat') continue;

    processData(
      data[key],
      displayBrowsers,
      requiredBrowsers,
      category,
      logger,
      path && path.length > 0 ? `${path}.${key}` : key,
    );
  }
}

/**
 * Test for issues within the browsers in the data within the specified file.
 *
 * @param {string} filename The file to test
 * @returns {boolean} If the file contains errors
 */
function testBrowsersPresence(filename) {
  const relativePath = path.relative(
    path.resolve(__dirname, '..', '..'),
    filename,
  );
  const category =
    relativePath.includes(path.sep) && relativePath.split(path.sep)[0];
  /** @type {Identifier} */
  const data = require(filename);

  if (!category) {
    console.warn(chalk.blackBright('  Browsers – Unknown category'));
    return false;
  }

  let displayBrowsers = [...browsers['desktop'], ...browsers['mobile']];
  let requiredBrowsers = browsers['desktop'];
  if (category === 'api') {
    displayBrowsers.push('nodejs');
    displayBrowsers.push('deno');
  }
  if (category === 'javascript') {
    displayBrowsers.push(...browsers['server']);
  }
  if (category === 'webextensions') {
    displayBrowsers = [
      ...browsers['webextensions-desktop'],
      ...browsers['webextensions-mobile'],
    ];
    requiredBrowsers = browsers['webextensions-desktop'];
  }
  displayBrowsers.sort();
  requiredBrowsers.sort();

  const logger = new Logger('Browsers');

  processData(data, displayBrowsers, requiredBrowsers, category, logger);

  logger.emit();
  return logger.hasErrors();
}

module.exports = testBrowsersPresence;
<|endoftext|>


<commit_before>/** ***********************************************
 * Copyright (c) 2018 Ansible, Inc.
 *
 * All Rights Reserved
 ************************************************ */
const mapChoices = choices => Object
    .assign(...choices.map(([k, v]) => ({ [k]: v })));

function ListJobsController (
    $scope,
    $state,
    Dataset,
    resolvedModels,
    strings,
    qs,
    Prompt,
    $filter,
    ProcessErrors,
    Wait,
    Rest
) {
    const vm = this || {};
    const [unifiedJob] = resolvedModels;

    vm.strings = strings;

    // smart-search
    const name = 'jobs';
    const iterator = 'job';
    const key = 'job_dataset';

    let launchModalOpen = false;
    let refreshAfterLaunchClose = false;

    $scope.list = { iterator, name };
    $scope.collection = { iterator, basePath: 'unified_jobs' };
    $scope[key] = Dataset.data;
    $scope[name] = Dataset.data.results;
    $scope.$on('updateDataset', (e, dataset) => {
        $scope[key] = dataset;
        $scope[name] = dataset.results;
    });
    $scope.$on('ws-jobs', () => {
        if (!launchModalOpen) {
            refreshJobs();
        } else {
            refreshAfterLaunchClose = true;
        }
    });

    $scope.$on('launchModalOpen', (evt, isOpen) => {
        evt.stopPropagation();
        if (!isOpen && refreshAfterLaunchClose) {
            refreshAfterLaunchClose = false;
            refreshJobs();
        }
        launchModalOpen = isOpen;
    });

    if ($state.includes('instanceGroups')) {
        vm.emptyListReason = strings.get('list.NO_RUNNING');
    }

    vm.jobTypes = mapChoices(unifiedJob
        .options('actions.GET.type.choices'));

    vm.getLink = ({ type, id }) => {
        let link;

        switch (type) {
            case 'job':
                link = `/#/jobz/playbook/${id}`;
                break;
            case 'ad_hoc_command':
                link = `/#/jobz/command/${id}`;
                break;
            case 'system_job':
                link = `/#/jobz/system/${id}`;
                break;
            case 'project_update':
                link = `/#/jobz/project/${id}`;
                break;
            case 'inventory_update':
                link = `/#/jobz/inventory/${id}`;
                break;
            case 'workflow_job':
                link = `/#/workflows/${id}`;
                break;
            default:
                link = '';
                break;
        }

        return link;
    };

    vm.deleteJob = (job) => {
        const action = () => {
            $('#prompt-modal').modal('hide');
            Wait('start');
            Rest.setUrl(job.url);
            Rest.destroy()
                .then(() => {
                    let reloadListStateParams = null;

                    if ($scope.jobs.length === 1 && $state.params.job_search &&
                    !_.isEmpty($state.params.job_search.page) &&
                    $state.params.job_search.page !== '1') {
                        const page = `${(parseInt(reloadListStateParams
                            .job_search.page, 10) - 1)}`;
                        reloadListStateParams = _.cloneDeep($state.params);
                        reloadListStateParams.job_search.page = page;
                    }

                    $state.go('.', reloadListStateParams, { reload: true });
                })
                .catch(({ data, status }) => {
                    ProcessErrors($scope, data, status, null, {
                        hdr: strings.get('error.HEADER'),
                        msg: strings.get('error.CALL', { path: `${job.url}`, status })
                    });
                })
                .finally(() => {
                    Wait('stop');
                });
        };

        const deleteModalBody = `<div class="Prompt-bodyQuery">${strings.get('deleteResource.CONFIRM', 'job')}</div>`;

        Prompt({
            hdr: strings.get('deleteResource.HEADER'),
            resourceName: $filter('sanitize')(job.name),
            body: deleteModalBody,
            action,
            actionText: 'DELETE'
        });
    };

    vm.cancelJob = (job) => {
        const action = () => {
            $('#prompt-modal').modal('hide');
            Wait('start');
            Rest.setUrl(job.related.cancel);
            Rest.post()
                .then(() => {
                    let reloadListStateParams = null;

                    if ($scope.jobs.length === 1 && $state.params.job_search &&
                    !_.isEmpty($state.params.job_search.page) &&
                    $state.params.job_search.page !== '1') {
                        const page = `${(parseInt(reloadListStateParams
                            .job_search.page, 10) - 1)}`;
                        reloadListStateParams = _.cloneDeep($state.params);
                        reloadListStateParams.job_search.page = page;
                    }

                    $state.go('.', reloadListStateParams, { reload: true });
                })
                .catch(({ data, status }) => {
                    ProcessErrors($scope, data, status, null, {
                        hdr: strings.get('error.HEADER'),
                        msg: strings.get('error.CALL', { path: `${job.url}`, status })
                    });
                })
                .finally(() => {
                    Wait('stop');
                });
        };

        const deleteModalBody = `<div class="Prompt-bodyQuery">${strings.get('cancelJob.SUBMIT_REQUEST')}</div>`;

        Prompt({
            hdr: strings.get('cancelJob.HEADER'),
            resourceName: $filter('sanitize')(job.name),
            body: deleteModalBody,
            action,
            actionText: strings.get('CANCEL')
        });
    };

    function refreshJobs () {
        qs.search(unifiedJob.path, $state.params.job_search)
            .then(({ data }) => {
                $scope.$emit('updateDataset', data);
            });
    }
}

ListJobsController.$inject = [
    '$scope',
    '$state',
    'Dataset',
    'resolvedModels',
    'JobsStrings',
    'QuerySet',
    'Prompt',
    '$filter',
    'ProcessErrors',
    'Wait',
    'Rest'
];

export default ListJobsController;
<commit_msg>Changes "Cancel" to "OK" on cancel-job-prompt modal<commit_after>/** ***********************************************
 * Copyright (c) 2018 Ansible, Inc.
 *
 * All Rights Reserved
 ************************************************ */
const mapChoices = choices => Object
    .assign(...choices.map(([k, v]) => ({ [k]: v })));

function ListJobsController (
    $scope,
    $state,
    Dataset,
    resolvedModels,
    strings,
    qs,
    Prompt,
    $filter,
    ProcessErrors,
    Wait,
    Rest
) {
    const vm = this || {};
    const [unifiedJob] = resolvedModels;

    vm.strings = strings;

    // smart-search
    const name = 'jobs';
    const iterator = 'job';
    const key = 'job_dataset';

    let launchModalOpen = false;
    let refreshAfterLaunchClose = false;

    $scope.list = { iterator, name };
    $scope.collection = { iterator, basePath: 'unified_jobs' };
    $scope[key] = Dataset.data;
    $scope[name] = Dataset.data.results;
    $scope.$on('updateDataset', (e, dataset) => {
        $scope[key] = dataset;
        $scope[name] = dataset.results;
    });
    $scope.$on('ws-jobs', () => {
        if (!launchModalOpen) {
            refreshJobs();
        } else {
            refreshAfterLaunchClose = true;
        }
    });

    $scope.$on('launchModalOpen', (evt, isOpen) => {
        evt.stopPropagation();
        if (!isOpen && refreshAfterLaunchClose) {
            refreshAfterLaunchClose = false;
            refreshJobs();
        }
        launchModalOpen = isOpen;
    });

    if ($state.includes('instanceGroups')) {
        vm.emptyListReason = strings.get('list.NO_RUNNING');
    }

    vm.jobTypes = mapChoices(unifiedJob
        .options('actions.GET.type.choices'));

    vm.getLink = ({ type, id }) => {
        let link;

        switch (type) {
            case 'job':
                link = `/#/jobz/playbook/${id}`;
                break;
            case 'ad_hoc_command':
                link = `/#/jobz/command/${id}`;
                break;
            case 'system_job':
                link = `/#/jobz/system/${id}`;
                break;
            case 'project_update':
                link = `/#/jobz/project/${id}`;
                break;
            case 'inventory_update':
                link = `/#/jobz/inventory/${id}`;
                break;
            case 'workflow_job':
                link = `/#/workflows/${id}`;
                break;
            default:
                link = '';
                break;
        }

        return link;
    };

    vm.deleteJob = (job) => {
        const action = () => {
            $('#prompt-modal').modal('hide');
            Wait('start');
            Rest.setUrl(job.url);
            Rest.destroy()
                .then(() => {
                    let reloadListStateParams = null;

                    if ($scope.jobs.length === 1 && $state.params.job_search &&
                    !_.isEmpty($state.params.job_search.page) &&
                    $state.params.job_search.page !== '1') {
                        const page = `${(parseInt(reloadListStateParams
                            .job_search.page, 10) - 1)}`;
                        reloadListStateParams = _.cloneDeep($state.params);
                        reloadListStateParams.job_search.page = page;
                    }

                    $state.go('.', reloadListStateParams, { reload: true });
                })
                .catch(({ data, status }) => {
                    ProcessErrors($scope, data, status, null, {
                        hdr: strings.get('error.HEADER'),
                        msg: strings.get('error.CALL', { path: `${job.url}`, status })
                    });
                })
                .finally(() => {
                    Wait('stop');
                });
        };

        const deleteModalBody = `<div class="Prompt-bodyQuery">${strings.get('deleteResource.CONFIRM', 'job')}</div>`;

        Prompt({
            hdr: strings.get('deleteResource.HEADER'),
            resourceName: $filter('sanitize')(job.name),
            body: deleteModalBody,
            action,
            actionText: 'DELETE'
        });
    };

    vm.cancelJob = (job) => {
        const action = () => {
            $('#prompt-modal').modal('hide');
            Wait('start');
            Rest.setUrl(job.related.cancel);
            Rest.post()
                .then(() => {
                    let reloadListStateParams = null;

                    if ($scope.jobs.length === 1 && $state.params.job_search &&
                    !_.isEmpty($state.params.job_search.page) &&
                    $state.params.job_search.page !== '1') {
                        const page = `${(parseInt(reloadListStateParams
                            .job_search.page, 10) - 1)}`;
                        reloadListStateParams = _.cloneDeep($state.params);
                        reloadListStateParams.job_search.page = page;
                    }

                    $state.go('.', reloadListStateParams, { reload: true });
                })
                .catch(({ data, status }) => {
                    ProcessErrors($scope, data, status, null, {
                        hdr: strings.get('error.HEADER'),
                        msg: strings.get('error.CALL', { path: `${job.url}`, status })
                    });
                })
                .finally(() => {
                    Wait('stop');
                });
        };

        const deleteModalBody = `<div class="Prompt-bodyQuery">${strings.get('cancelJob.SUBMIT_REQUEST')}</div>`;

        Prompt({
            hdr: strings.get('cancelJob.HEADER'),
            resourceName: $filter('sanitize')(job.name),
            body: deleteModalBody,
            action,
            actionText: strings.get('OK')
        });
    };

    function refreshJobs () {
        qs.search(unifiedJob.path, $state.params.job_search)
            .then(({ data }) => {
                $scope.$emit('updateDataset', data);
            });
    }
}

ListJobsController.$inject = [
    '$scope',
    '$state',
    'Dataset',
    'resolvedModels',
    'JobsStrings',
    'QuerySet',
    'Prompt',
    '$filter',
    'ProcessErrors',
    'Wait',
    'Rest'
];

export default ListJobsController;
<|endoftext|>