<commit_before>import React, {Fragment} from 'react';
import PropTypes from 'prop-types';

import {autobind} from '../helpers';
import {IssueishPropType} from '../prop-types';
import Accordion from './accordion';
import Timeago from './timeago';
import StatusDonutChart from './status-donut-chart';
import QueryErrorTile from './query-error-tile';
import Octicon from '../atom/octicon';

export default class IssueishListView extends React.Component {
  static propTypes = {
    title: PropTypes.string.isRequired,
    isLoading: PropTypes.bool.isRequired,
    total: PropTypes.number.isRequired,
    issueishes: PropTypes.arrayOf(IssueishPropType).isRequired,

    repository: PropTypes.shape({
      defaultBranchRef: PropTypes.shape({
        prefix: PropTypes.string.isRequired,
        name: PropTypes.string.isRequired,
      }),
    }),

    needReviewsButton: PropTypes.bool,
    onIssueishClick: PropTypes.func.isRequired,
    onMoreClick: PropTypes.func,
    openReviews: PropTypes.func,

    emptyComponent: PropTypes.func,
    error: PropTypes.object,
  }

  constructor(props) {
    super(props);

    autobind(this, 'renderIssueish', 'renderLoadingTile', 'renderEmptyTile', 'renderMoreTile');
  }

  render() {
    return (
      <Accordion
        leftTitle={this.props.title}
        isLoading={this.props.isLoading}
        results={this.props.issueishes}
        total={this.props.total}
        loadingComponent={this.renderLoadingTile}
        emptyComponent={this.renderEmptyTile}
        moreComponent={this.renderMoreTile}
        reviewsButton={this.renderReviewsButton}
        onClickItem={this.props.onIssueishClick}>
        {this.renderIssueish}
      </Accordion>
    );
  }

  renderReviewsButton = () => {
    if (!this.props.needReviewsButton || this.props.total < 1) {
      return null;
    }
    return (
      <button
        className="btn btn-primary btn-sm github-IssueishList-openReviewsButton"
        onClick={this.openReviews}>
        See reviews
      </button>
    );
  }

  openReviews = e => {
    e.stopPropagation();
    this.props.openReviews();
  }

  renderIssueish(issueish) {
    return (
      <Fragment>

          {issueish.getTitle()}

          #{issueish.getNumber()}

        {this.renderStatusSummary(issueish.getStatusCounts())}
        <Timeago
          time={issueish.getCreatedAt()}
          displayStyle="short"
          className="github-IssueishList-item github-IssueishList-item--age"
        />
      </Fragment>
    );
  }

  renderStatusSummary(statusCounts) {
    if (['success', 'failure', 'pending'].every(kind => statusCounts[kind] === 0)) {
      return <Octicon className="github-IssueishList-item github-IssueishList-item--status" icon="dash" />;
    }

    if (statusCounts.success > 0 && statusCounts.failure === 0 && statusCounts.pending === 0) {
      return <Octicon className="github-IssueishList-item github-IssueishList-item--status" icon="check" />;
    }

    if (statusCounts.success === 0 && statusCounts.failure > 0 && statusCounts.pending === 0) {
      return <Octicon className="github-IssueishList-item github-IssueishList-item--status" icon="x" />;
    }

    return <StatusDonutChart {...statusCounts} className="github-IssueishList-item github-IssueishList-item--status" />;
  }

  renderLoadingTile() {
    return (

        Loading

    );
  }

  renderEmptyTile() {
    if (this.props.error) {
      return <QueryErrorTile error={this.props.error} />;
    }

    if (this.props.emptyComponent) {
      const EmptyComponent = this.props.emptyComponent;
      return <EmptyComponent />;
    }

    return null;
  }

  renderMoreTile() {
    /* eslint-disable jsx-a11y/anchor-is-valid */
    if (this.props.onMoreClick) {
      return (

            More...

      );
    }

    return null;
  }
}
<commit_msg>De-autobind IssueishListView<commit_after>import React, {Fragment} from 'react';
import PropTypes from 'prop-types';

import {IssueishPropType} from '../prop-types';
import Accordion from './accordion';
import Timeago from './timeago';
import StatusDonutChart from './status-donut-chart';
import QueryErrorTile from './query-error-tile';
import Octicon from '../atom/octicon';

export default class IssueishListView extends React.Component {
  static propTypes = {
    title: PropTypes.string.isRequired,
    isLoading: PropTypes.bool.isRequired,
    total: PropTypes.number.isRequired,
    issueishes: PropTypes.arrayOf(IssueishPropType).isRequired,

    repository: PropTypes.shape({
      defaultBranchRef: PropTypes.shape({
        prefix: PropTypes.string.isRequired,
        name: PropTypes.string.isRequired,
      }),
    }),

    needReviewsButton: PropTypes.bool,
    onIssueishClick: PropTypes.func.isRequired,
    onMoreClick: PropTypes.func,
    openReviews: PropTypes.func,

    emptyComponent: PropTypes.func,
    error: PropTypes.object,
  }

  render() {
    return (
      <Accordion
        leftTitle={this.props.title}
        isLoading={this.props.isLoading}
        results={this.props.issueishes}
        total={this.props.total}
        loadingComponent={this.renderLoadingTile}
        emptyComponent={this.renderEmptyTile}
        moreComponent={this.renderMoreTile}
        reviewsButton={this.renderReviewsButton}
        onClickItem={this.props.onIssueishClick}>
        {this.renderIssueish}
      </Accordion>
    );
  }

  renderReviewsButton = () => {
    if (!this.props.needReviewsButton || this.props.total < 1) {
      return null;
    }
    return (
      <button
        className="btn btn-primary btn-sm github-IssueishList-openReviewsButton"
        onClick={this.openReviews}>
        See reviews
      </button>
    );
  }

  openReviews = e => {
    e.stopPropagation();
    this.props.openReviews();
  }

  renderIssueish = issueish => {
    return (
      <Fragment>

          {issueish.getTitle()}

          #{issueish.getNumber()}

        {this.renderStatusSummary(issueish.getStatusCounts())}
        <Timeago
          time={issueish.getCreatedAt()}
          displayStyle="short"
          className="github-IssueishList-item github-IssueishList-item--age"
        />
      </Fragment>
    );
  }

  renderStatusSummary(statusCounts) {
    if (['success', 'failure', 'pending'].every(kind => statusCounts[kind] === 0)) {
      return <Octicon className="github-IssueishList-item github-IssueishList-item--status" icon="dash" />;
    }

    if (statusCounts.success > 0 && statusCounts.failure === 0 && statusCounts.pending === 0) {
      return <Octicon className="github-IssueishList-item github-IssueishList-item--status" icon="check" />;
    }

    if (statusCounts.success === 0 && statusCounts.failure > 0 && statusCounts.pending === 0) {
      return <Octicon className="github-IssueishList-item github-IssueishList-item--status" icon="x" />;
    }

    return <StatusDonutChart {...statusCounts} className="github-IssueishList-item github-IssueishList-item--status" />;
  }

  renderLoadingTile = () => {
    return (

        Loading

    );
  }

  renderEmptyTile = () => {
    if (this.props.error) {
      return <QueryErrorTile error={this.props.error} />;
    }

    if (this.props.emptyComponent) {
      const EmptyComponent = this.props.emptyComponent;
      return <EmptyComponent />;
    }

    return null;
  }

  renderMoreTile = () => {
    /* eslint-disable jsx-a11y/anchor-is-valid */
    if (this.props.onMoreClick) {
      return (

            More...

      );
    }

    return null;
  }
}
<|endoftext|>


<commit_before>import * as Errors  from '../errors';
import {toQuery}    from '../support/object';
import {each}       from '../support';

export default class Connection {

    host;//: string;
    apiKey;//: string;
    _userAgent;//: string | null;

    // constructor(url: string, options: { userAgent?: string; } = {})
    constructor(url, options = {}) {
        let {origin, pathname, username} = new URL(url);

        this.host = (origin + pathname).replace(/\/$/, '');
        this.apiKey = username;
        this._userAgent = options.userAgent ? options.userAgent : null;
        this.csrfToken = document.head.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
    }

    // Returns the User-Agent of the client. Defaults to:
    // "viking-js/SUNSTONE_VERSION RUBY_VERSION-pPATCH_LEVEL PLATFORM"
    userAgent() {
        return [
            this._userAgent,
            `Viking/$s{VERSION}`,
            // javascript vm,
            // platform
        ].filter((n) => n);
    }

    // get(path: string, params = {})
    get(path, options = {}) {
        return this.sendRequest('GET', path, options);
    }

    // post(path: string, body = null)
    post(path, options = {}) {
        return this.sendRequest('POST', path, options);
    }

    // put(path: string, body = null)
    put(path, options = {}) {
        return this.sendRequest('PUT', path, options);
    }

    // delete(path: string)
    delete(path, options = {}) {
        return this.sendRequest('DELETE', path, options);
    }

    // sendRequest(method: string, path: string, body = null)
    sendRequest(method, path, {preflight = null, params = null, body = null, headers = {}, success = null, invalid = null, error = null, complete = null, progress = null} = {}) {
        let resolve, reject;
        const promise = new Promise((res, rej) => { resolve = res; reject = rej; });

        if (params && Object.keys(params).length > 0) {
            path += `?${toQuery(params)}`;
        }

        let request = new XMLHttpRequest();
        request.open(method, `${this.host}${path}`, true);
        request.withCredentials = true;
        request.setRequestHeader('Api-Version', '0.5.0');
        request.setRequestHeader('Accept', 'application/json');

        if (body !== null && (typeof body) !== 'string' && !(body instanceof FormData)) {
            body = JSON.stringify(body);
            request.setRequestHeader('Content-Type', 'application/json')
        }

        if (this.csrfToken) {
            request.setRequestHeader('X-CSRF-Token', this.csrfToken);
        }

        each(headers, (key, value) => {
            request.setRequestHeader(key, value);
        });

        let error_callback = () => {
            if (request.status === 301) {
                reject(new Errors.MovedPermanently());
            } else if (request.status === 400) {
                reject(new Errors.BadRequest());
            } else if (request.status === 401) {
                reject(new Errors.Unauthorized());
            } else if (request.status === 403) {
                reject(new Errors.Forbidden());
            } else if (request.status === 404) {
                reject(new Errors.NotFound());
            } else if (request.status === 410) {
                reject(new Errors.Gone());
            } else if (request.status === 422) {
                reject(new Errors.ApiVersionUnsupported());
            } else if (request.status === 503) {
                reject(new Errors.ServiceUnavailable());
            } else if (request.status >= 500 && request.status < 599) {
                reject(new Errors.ServerError(request.response));
            } else {
                reject(new Errors.VikingError(`Unexpected response status ${request.status}`));
            }
        }

        request.addEventListener('load', async () => {
            if (request.status >= 200 && request.status <= 299) {
                let response = request.status == 204 || !request.response ? null : JSON.parse(request.response);
                if (success) {
                    resolve(await success(response));
                } else {
                    resolve(response);
                }
            } else if (request.status == 400) {
                let return_value = invalid ? invalid(request, error_callback) : undefined;
                if (return_value !== undefined) {
                    resolve(return_value);
                } else {
                    error_callback();
                }
            } else {
                if (error) error(request.response)
                error_callback();
            }

            if (complete) {
                complete(request.response);
            }
        });

        // There was a connection error of some sort
        request.addEventListener('error', reject);
        if(progress) {
            request.addEventListener('progress', progress);
        }

        request.responseType = 'text';
        if (preflight) {
            preflight = preflight(request);
            if (preflight instanceof Promise) {
                return preflight.then(() => {
                    request.send(body);
                    return promise;
                });
            }
        }

        request.send(body);
        return promise;

    }

}
<commit_msg>add default headers to connection constructor<commit_after>import * as Errors  from '../errors';
import {toQuery}    from '../support/object';
import {each}       from '../support';

export default class Connection {

    host;//: string;
    apiKey;//: string;
    _userAgent;//: string | null;
    headers = {};

    // constructor(url: string, options: { userAgent?: string; } = {})
    constructor(url, options = {}) {
        let {origin, pathname, username} = new URL(url);

        this.host = (origin + pathname).replace(/\/$/, '');
        this.apiKey = username;
        this._userAgent = options.userAgent ? options.userAgent : null;
        this.headers = options.headers || {};
        this.csrfToken = document.head.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
    }

    // Returns the User-Agent of the client. Defaults to:
    // "viking-js/SUNSTONE_VERSION RUBY_VERSION-pPATCH_LEVEL PLATFORM"
    userAgent() {
        return [
            this._userAgent,
            `Viking/$s{VERSION}`,
            // javascript vm,
            // platform
        ].filter((n) => n);
    }

    // get(path: string, params = {})
    get(path, options = {}) {
        return this.sendRequest('GET', path, options);
    }

    // post(path: string, body = null)
    post(path, options = {}) {
        return this.sendRequest('POST', path, options);
    }

    // put(path: string, body = null)
    put(path, options = {}) {
        return this.sendRequest('PUT', path, options);
    }

    // delete(path: string)
    delete(path, options = {}) {
        return this.sendRequest('DELETE', path, options);
    }

    // sendRequest(method: string, path: string, body = null)
    sendRequest(method, path, {preflight = null, params = null, body = null, headers = {}, success = null, invalid = null, error = null, complete = null, progress = null} = {}) {
        let resolve, reject;
        const promise = new Promise((res, rej) => { resolve = res; reject = rej; });

        if (params && Object.keys(params).length > 0) {
            path += `?${toQuery(params)}`;
        }

        let request = new XMLHttpRequest();
        request.open(method, `${this.host}${path}`, true);
        request.withCredentials = true;
        request.setRequestHeader('Api-Version', '0.5.0');
        request.setRequestHeader('Accept', 'application/json');

        if (body !== null && (typeof body) !== 'string' && !(body instanceof FormData)) {
            body = JSON.stringify(body);
            request.setRequestHeader('Content-Type', 'application/json')
        }

        if (this.csrfToken) {
            request.setRequestHeader('X-CSRF-Token', this.csrfToken);
        }

        each(Object.assign(this.headers, headers), (key, value) => {
            request.setRequestHeader(key, value);
        });

        let error_callback = () => {
            if (request.status === 301) {
                reject(new Errors.MovedPermanently());
            } else if (request.status === 400) {
                reject(new Errors.BadRequest());
            } else if (request.status === 401) {
                reject(new Errors.Unauthorized());
            } else if (request.status === 403) {
                reject(new Errors.Forbidden());
            } else if (request.status === 404) {
                reject(new Errors.NotFound());
            } else if (request.status === 410) {
                reject(new Errors.Gone());
            } else if (request.status === 422) {
                reject(new Errors.ApiVersionUnsupported());
            } else if (request.status === 503) {
                reject(new Errors.ServiceUnavailable());
            } else if (request.status >= 500 && request.status < 599) {
                reject(new Errors.ServerError(request.response));
            } else {
                reject(new Errors.VikingError(`Unexpected response status ${request.status}`));
            }
        }

        request.addEventListener('load', async () => {
            if (request.status >= 200 && request.status <= 299) {
                let response = request.status == 204 || !request.response ? null : JSON.parse(request.response);
                if (success) {
                    resolve(await success(response));
                } else {
                    resolve(response);
                }
            } else if (request.status == 400) {
                let return_value = invalid ? invalid(request, error_callback) : undefined;
                if (return_value !== undefined) {
                    resolve(return_value);
                } else {
                    error_callback();
                }
            } else {
                if (error) error(request.response)
                error_callback();
            }

            if (complete) {
                complete(request.response);
            }
        });

        // There was a connection error of some sort
        request.addEventListener('error', reject);
        if(progress) {
            request.addEventListener('progress', progress);
        }

        request.responseType = 'text';
        if (preflight) {
            preflight = preflight(request);
            if (preflight instanceof Promise) {
                return preflight.then(() => {
                    request.send(body);
                    return promise;
                });
            }
        }

        request.send(body);
        return promise;

    }

}
<|endoftext|>


<commit_before>'use strict';

const L = require("../lib/partial.lenses")
const P = require("ramda-lens")
const R = require("ramda")

const xyz = {x: 1, y: 2, z: 3}
const xs = [1,2,3]
const axay = [{x: [{y: 1}]}]
const xs100 = Array(100).fill(1)
const xs1000 = Array(1000).fill(1)
const xs10000 = Array(10000).fill(1)
const ids = R.range(0, 10000).map(i => ({id: i, value: i}))

const xsss100 = Array(100).fill([[1]])

const xyzn = {x: {y: {z: 1}}}

const l_0 = R.lensIndex(0)
const l_1 = R.lensIndex(1)
const l_x = R.lensProp("x")
const l_y = R.lensProp("y")
const l_z = R.lensProp("z")
const l_0_x_0_y = R.compose(l_0, l_x, l_0, l_y)
const l_xyz = R.lensPath(["x", "y", "z"])
const l_x_y_z = R.compose(l_x, l_y, l_z)

const id = x => x
const inc = x => x + 1
const add = (x, y) => x+y

const Sum = {empty: 0, concat: add}

const bs = [
  'L.foldMapOf(Sum, [L.sequence, L.sequence, L.sequence], id, xsss100)',
  'P.sumOf(R.compose(P.traversed, P.traversed, P.traversed), xsss100)',

  'L.foldMapOf(Sum, L.sequence, id, xs100)',
  'P.sumOf(P.traversed, xs100)',

  'L.collect(L.sequence, xs100)',

  'L.modify(L.sequence, inc, xs100)',
  'P.over(P.traversed, inc, xs100)',
  'R.map(inc, xs100)',

  'L.get(L.defaults(1), undefined)',
  'L.get(L.defaults(1), 2)',

  'L.get(L.define(1), undefined)',
  'L.get(L.define(1), 2)',

  'L.get(L.valueOr(1), undefined)',
  'L.get(L.valueOr(1), null)',
  'L.get(L.valueOr(1), 2)',

  'L.get(1, xs)',
  'R.nth(1, xs)',
  'R.view(l_1, xs)',

  'L.set(1, 0, xs)',
  'R.update(1, 0, xs)',
  'R.set(l_1, 0, xs)',

  'L.get("y", xyz)',
  'R.prop("y", xyz)',
  'R.view(l_y, xyz)',

  'L.set("y", 0, xyz)',
  'R.assoc("y", 0, xyz)',
  'R.set(l_y, 0, xyz)',

  'L.get([0,"x",0,"y"], axay)',
  'R.view(l_0_x_0_y, axay)',

  'L.set([0,"x",0,"y"], 0, axay)',
  'R.set(l_0_x_0_y, 0, axay)',

  'L.modify([0,"x",0,"y"], inc, axay)',
  'R.over(l_0_x_0_y, inc, axay)',

  'L.remove(1, xs)',
  'R.remove(1, 1, xs)',

  'L.remove("y", xyz)',
  'R.dissoc("y", xyz)',

  'L.get(["x","y","z"], xyzn)',
  'R.path(["x","y","z"], xyzn)',
  'R.view(l_xyz, xyzn)',
  'R.view(l_x_y_z, xyzn)',

  'L.set(["x","y","z"], 0, xyzn)',
  'R.assocPath(["x","y","z"], 0, xyzn)',
  'R.set(l_xyz, 0, xyzn)',
  'R.set(l_x_y_z, 0, xyzn)',

  'L.remove(50, xs100)',
  'L.set(50, 2, xs100)',
  'R.remove(50, 1, xs100)',
  'R.update(50, 2, xs100)',

  'L.remove(500, xs1000)',
  'L.set(500, 2, xs1000)',
  'R.remove(500, 1, xs1000)',
  'R.update(500, 2, xs1000)',

  'L.remove(5000, xs10000)',
  'L.set(5000, 2, xs10000)',
  'R.remove(5000, 1, xs10000)',
  'R.update(5000, 2, xs10000)',

  'L.get(L.fromArrayBy("id"), ids)'
]

const s = new require("benchmark").Suite()
bs.forEach(b => s.add(b, eval("() => " + b)))
s.on('cycle', e => console.log(String(e.target))).run()
<commit_msg>Add Ramda sum to the benc<commit_after>'use strict';

const L = require("../lib/partial.lenses")
const P = require("ramda-lens")
const R = require("ramda")

const xyz = {x: 1, y: 2, z: 3}
const xs = [1,2,3]
const axay = [{x: [{y: 1}]}]
const xs100 = Array(100).fill(1)
const xs1000 = Array(1000).fill(1)
const xs10000 = Array(10000).fill(1)
const ids = R.range(0, 10000).map(i => ({id: i, value: i}))

const xsss100 = Array(100).fill([[1]])

const xyzn = {x: {y: {z: 1}}}

const l_0 = R.lensIndex(0)
const l_1 = R.lensIndex(1)
const l_x = R.lensProp("x")
const l_y = R.lensProp("y")
const l_z = R.lensProp("z")
const l_0_x_0_y = R.compose(l_0, l_x, l_0, l_y)
const l_xyz = R.lensPath(["x", "y", "z"])
const l_x_y_z = R.compose(l_x, l_y, l_z)

const id = x => x
const inc = x => x + 1
const add = (x, y) => x+y

const Sum = {empty: 0, concat: add}

const bs = [
  'L.foldMapOf(Sum, [L.sequence, L.sequence, L.sequence], id, xsss100)',
  'P.sumOf(R.compose(P.traversed, P.traversed, P.traversed), xsss100)',

  'L.foldMapOf(Sum, L.sequence, id, xs100)',
  'P.sumOf(P.traversed, xs100)',
  'R.sum(xs100)',

  'L.collect(L.sequence, xs100)',

  'L.modify(L.sequence, inc, xs100)',
  'P.over(P.traversed, inc, xs100)',
  'R.map(inc, xs100)',

  'L.get(L.defaults(1), undefined)',
  'L.get(L.defaults(1), 2)',

  'L.get(L.define(1), undefined)',
  'L.get(L.define(1), 2)',

  'L.get(L.valueOr(1), undefined)',
  'L.get(L.valueOr(1), null)',
  'L.get(L.valueOr(1), 2)',

  'L.get(1, xs)',
  'R.nth(1, xs)',
  'R.view(l_1, xs)',

  'L.set(1, 0, xs)',
  'R.update(1, 0, xs)',
  'R.set(l_1, 0, xs)',

  'L.get("y", xyz)',
  'R.prop("y", xyz)',
  'R.view(l_y, xyz)',

  'L.set("y", 0, xyz)',
  'R.assoc("y", 0, xyz)',
  'R.set(l_y, 0, xyz)',

  'L.get([0,"x",0,"y"], axay)',
  'R.view(l_0_x_0_y, axay)',

  'L.set([0,"x",0,"y"], 0, axay)',
  'R.set(l_0_x_0_y, 0, axay)',

  'L.modify([0,"x",0,"y"], inc, axay)',
  'R.over(l_0_x_0_y, inc, axay)',

  'L.remove(1, xs)',
  'R.remove(1, 1, xs)',

  'L.remove("y", xyz)',
  'R.dissoc("y", xyz)',

  'L.get(["x","y","z"], xyzn)',
  'R.path(["x","y","z"], xyzn)',
  'R.view(l_xyz, xyzn)',
  'R.view(l_x_y_z, xyzn)',

  'L.set(["x","y","z"], 0, xyzn)',
  'R.assocPath(["x","y","z"], 0, xyzn)',
  'R.set(l_xyz, 0, xyzn)',
  'R.set(l_x_y_z, 0, xyzn)',

  'L.remove(50, xs100)',
  'L.set(50, 2, xs100)',
  'R.remove(50, 1, xs100)',
  'R.update(50, 2, xs100)',

  'L.remove(500, xs1000)',
  'L.set(500, 2, xs1000)',
  'R.remove(500, 1, xs1000)',
  'R.update(500, 2, xs1000)',

  'L.remove(5000, xs10000)',
  'L.set(5000, 2, xs10000)',
  'R.remove(5000, 1, xs10000)',
  'R.update(5000, 2, xs10000)',

  'L.get(L.fromArrayBy("id"), ids)'
]

const s = new require("benchmark").Suite()
bs.forEach(b => s.add(b, eval("() => " + b)))
s.on('cycle', e => console.log(String(e.target))).run()
<|endoftext|>


<commit_before>// Copyright (c) Cognitect, Inc.
// All rights reserved.

"use strict";

var eq      = require("../src/transit/eq.js"),
    t       = require("../src/transit/types.js"),
    wr      = require("../src/transit/writer.js"),
    sb      = require("../src/transit/stringbuilder.js"),
    transit = require("../src/transit.js"),
    caching = require("../src/transit/caching.js");

function time(f, iters) {
    iters = iters || 1;
    for(var i = 0; i < iters; i++) {
        var s = new Date();
        f();
        console.log("Elapsed "+((new Date()).valueOf()-s.valueOf())+"ms");
        console.log("----------");
    }
}

console.log("1e6 iters, eq.hashCode(\"foo\")");
time(function() {
    for(var i = 0; i < 1000000; i++) {
        eq.hashCode("foo");
    }
});

console.log("1e6 iters, eq.hashCode([1,2,3])");
var arr = [1,2,3];
time(function() {
    for(var i = 0; i < 1000000; i++) {
        eq.hashCode(arr);
    }
});

console.log("1e6 iters, eq.equals(\"foo\", \"foo\")");
time(function() {
    for(var i = 0; i < 1000000; i++) {
        eq.equals("foo", "foo");
    }
});

console.log("1e6 iters, eq.equals(\"foo\", \"bar\")");
time(function() {
    for(var i = 0; i < 1000000; i++) {
        eq.equals("foo", "bar");
    }
});

console.log("1e6 iters, eq.equals([1,2,3], [1,2,3])");
time(function() {
    var arr0 = [1,2,3],
        arr1 = [1,2,3];
    for(var i = 0; i < 1000000; i++) {
        eq.equals(arr0, arr1);
    }
});

console.log("1e6 iters, eq.equals([1,2,3], [3,2,3])");
time(function() {
    var arr0 = [1,2,3],
        arr1 = [3,2,3];
    for(var i = 0; i < 1000000; i++) {
        eq.equals(arr0, arr1);
    }
});

console.log("1e6 iters, eq.equals(new Keyword(\"foo\"), new Keyword(\"foo\"))");
time(function() {
    var k0 = t.Keyword("foo"),
        k1 = t.Keyword("foo");
    for(var i = 0; i < 1000000; i++) {
        eq.equals(k0, k1);
    }
});

console.log("1e6 iters, eq.equals(t.date(1399471321791), t.date(1399471321791))");
time(function() {
    var d0 = t.date(1399471321791),
        d1 = t.date(1399471321791);
    for(var i = 0; i < 1000000; i++) {
        eq.equals(d0, d1);
    }
});

console.log("1e6 iters, eq.equals(new Keyword(\"foo\"), new Keyword(\"bar\"))");
time(function() {
    var k0 = t.Keyword("foo"),
        k1 = t.Keyword("bar");
    for(var i = 0; i < 1000000; i++) {
        eq.equals(k0, k1);
    }
});

console.log("1e6 iters, eq.equals({foo: \"bar\"}, {foo: \"bar\"})");
time(function() {
    var obj0 = {foo: "bar"},
        obj1 = {foo: "bar"};
    for(var i = 0; i < 1000000; i++) {
        eq.equals(obj0, obj1);
    }
});

console.log("1e6 iters, TransitMap tm0.get(\"foo\")");
var tm0 = t.map(["foo", "transit map string lookup"]);
console.log(tm0.get("foo"));
time(function() {
    for(var i = 0; i < 1000000; i++) {
        tm0.get("foo");
    }
});

console.log("1e5 iters, TransitMap tm0.get([1,2])");
var tm1 = t.map([[1,2], "transit map array lookup"]);
console.log(tm1.get([1,2]));
time(function() {
    var key = [1,2];
    for(var i = 0; i < 100000; i++) {
        tm1.get(key);
    }
});

console.log("1e6 iters, Complex TransitSet \"bar\"");
var ts = t.set(["foo",1,"bar",[1,2]])
console.log(ts.has("bar"));
time(function() {
    var key = "bar";
    for(var i = 0; i < 1000000; i++) {
        ts.has(key);
    }
});

console.log("1e5 iters, marshal {foo:\"bar\"}");
var em = new wr.JSONMarshaller(),
    c  = caching.writeCache(),
    m  = {foo:"bar"};
console.log(wr.marshalTop(em, m, c));
time(function() {
    for(var i = 0; i < 100000; i++) {
        wr.marshalTop(em, m, c);
    }
});

console.log("1e4 iters, marshal {foo:new Date(Date.UTC(1985,3,12,23,20,50,52))}");
var d = new Date(Date.UTC(1985,3,12,23,20,50,52));
m = {foo:d};
console.log(wr.marshalTop(em, m, c));
time(function() {
    for(var i = 0; i < 10000; i++) {
        wr.marshalTop(em, m, c);
    }
});

console.log("1e5 iters, marshal {foo:new Date(Date.UTC(1985,3,12,23,20,50,52))}, (prefersStrings false)");
em = new wr.JSONMarshaller({prefersStrings: false}),
c  = caching.writeCache(),
d  = new Date(Date.UTC(1985,3,12,23,20,50,52)),
m  = {foo:d};
console.log(wr.marshalTop(em, m, c));
time(function() {
    for(var i = 0; i < 100000; i++) {
        wr.marshalTop(em, m, c);
    }
});

console.log("1e5 iters, marshal [1,2,3]");
arr = [1,2,3];
console.log(wr.marshalTop(em, arr, c));
time(function() {
    for(var i = 0; i < 100000; i++) {
        wr.marshalTop(em, arr, c);
    }
});

console.log("1e5 iters, JSON read transit map with two keyword/string value pairs");
var json = "{\"~:foo\":\"bar\",\"~:baz\":\"woz\"}";
console.log(JSON.parse(json));
time(function() {
    for(var i = 0; i < 100000; i++) {
        JSON.parse(json);
    }
});

console.log("1e5 iters, top level read, map two keyword/string value pairs");
json = "{\"~:foo\":\"bar\",\"~:baz\":\"woz\"}";
var reader = transit.reader("json");
console.log(reader.read(json));
time(function() {
    for(var i = 0; i < 100000; i++) {
        reader.read(json)
    }
});

console.log("1e5 iters, JSON read transit map with two keyword/number value parirs");
json = "{\"~:foo\":1,\"~:bar\":2}";
console.log(JSON.parse(json));
time(function() {
    for(var i = 0; i < 100000; i++) {
        JSON.parse(json);
    }
});

console.log("1e5 iters, top level read, map two keyword/number value pairs");
reader = transit.reader("json");
console.log(reader.read(json));
time(function() {
    for(var i = 0; i < 100000; i++) {
        reader.read(json);
    }
});

console.log("1e5 iters, top level read, read keyword");
var kws = "\"~:foo\"";
reader = transit.reader("json");
console.log(reader.read(kws))
time(function() {
    for(var i = 0; i < 100000; i++) {
        reader.read(kws);
    }
});

console.log("1e5 iters, JSON.stringify, map two keyword/number value pairs");
m = {foo:1,bar:2};
console.log(JSON.stringify(m));
time(function() {
    for(var i = 0; i < 100000; i++) {
        JSON.stringify(m);
    }
});

console.log("1e5 iters, top level write, map two keyword/number value pairs");
var writer = transit.writer("json");
console.log(writer.write(m));
time(function() {
    for(var i = 0; i < 100000; i++) {
        writer.write(m);
    }
});

console.log("1 iter, JSON.stringfy JS object with 100000 kv pairs");
var largeMap = {};
for(var i = 0; i < 100000; i++) {
    largeMap["foo"+i] = ["bar"+i, i];
}
time(function() {
    JSON.stringify(largeMap);
});

console.log("1 iter, transit write large JS object with 100000 kv pairs, complex");
largeMap = {},
writer = transit.writer("json");
for(var i = 0; i < 100000; i++) {
    largeMap["~:foo"+i] = [t.keyword("bar"+i),i];
}
time(function() {
    writer.write(largeMap);
});

console.log("1 iter, transit write large JS object with 100000 kv pairs, simple");
largeMap = {};
writer = transit.writer("json");
for(var i = 0; i < 100000; i++) {
    largeMap["~:foo"+i] = 1;
}
time(function() {
    writer.write(largeMap);
});

console.log("1 iter, JSON.parse JS object with 100000 kv pairs");
arr = [];
for(var i = 0; i < 100000; i++) {
    arr.push("\"foo"+i+"\":[\"bar"+i+"\","+i+"]")
}
largeMap = "{"+arr.join(",")+"}",
json = JSON.parse(largeMap);
console.log(Object.keys(json).length);
console.log(json["foo0"]);
time(function() {
    JSON.parse(largeMap);
});

console.log("1 iter, transit read large transit map with 100000 kv pairs");
arr    = [],
reader = transit.writer("json");
for(var i = 0; i < 100000; i++) {
    arr.push("\"~:foo"+i+"\":[\"~:bar"+i+"\","+i+"]");
}
largeMap = "{"+arr.join(",")+"}",
reader   = transit.reader("json");
var largeTransitMap = reader.read(largeMap);
console.log(Object.keys(largeTransitMap).length);
console.log(largeTransitMap["`~:foo0"]);
time(function() {
    reader.read(largeMap);
});
<commit_msg>update benchmarks, remove implementation specific stuff<commit_after>// Copyright (c) Cognitect, Inc.
// All rights reserved.

"use strict";

var transit = require("../target/transit.js");

function time(f, iters) {
    iters = iters || 1;
    for(var i = 0; i < iters; i++) {
        var s = new Date();
        f();
        console.log("Elapsed "+((new Date()).valueOf()-s.valueOf())+"ms");
        console.log("----------");
    }
}

console.log("1e6 iters, transit.hash(\"foo\")");
time(function() {
    for(var i = 0; i < 1000000; i++) {
        transit.hash("foo");
    }
});

console.log("1e6 iters, transit.hash([1,2,3])");
var arr = [1,2,3];
time(function() {
    for(var i = 0; i < 1000000; i++) {
        transit.hash(arr);
    }
});

console.log("1e6 iters, transit.equals(\"foo\", \"foo\")");
time(function() {
    for(var i = 0; i < 1000000; i++) {
        transit.equals("foo", "foo");
    }
});

console.log("1e6 iters, transit.equals(\"foo\", \"bar\")");
time(function() {
    for(var i = 0; i < 1000000; i++) {
        transit.equals("foo", "bar");
    }
});

console.log("1e6 iters, transit.equals([1,2,3], [1,2,3])");
time(function() {
    var arr0 = [1,2,3],
        arr1 = [1,2,3];
    for(var i = 0; i < 1000000; i++) {
        transit.equals(arr0, arr1);
    }
});

console.log("1e6 iters, transit.equals([1,2,3], [3,2,3])");
time(function() {
    var arr0 = [1,2,3],
        arr1 = [3,2,3];
    for(var i = 0; i < 1000000; i++) {
        transit.equals(arr0, arr1);
    }
});

console.log("1e6 iters, transit.equals(transit.keyword(\"foo\"), transit.keyword(\"foo\"))");
time(function() {
    var k0 = transit.keyword("foo"),
        k1 = transit.keyword("foo");
    for(var i = 0; i < 1000000; i++) {
        transit.equals(k0, k1);
    }
});

console.log("1e6 iters, transit.equals(transit.date(1399471321791), transit.date(1399471321791))");
time(function() {
    var d0 = transit.date(1399471321791),
        d1 = transit.date(1399471321791);
    for(var i = 0; i < 1000000; i++) {
        transit.equals(d0, d1);
    }
});

console.log("1e6 iters, transit.equals(transit.keyword(\"foo\"), transit.keyword(\"bar\"))");
time(function() {
    var k0 = transit.keyword("foo"),
        k1 = transit.keyword("bar");
    for(var i = 0; i < 1000000; i++) {
        transit.equals(k0, k1);
    }
});

console.log("1e6 iters, transit.equals({foo: \"bar\"}, {foo: \"bar\"})");
time(function() {
    var obj0 = {foo: "bar"},
        obj1 = {foo: "bar"};
    for(var i = 0; i < 1000000; i++) {
        transit.equals(obj0, obj1);
    }
});

console.log("1e6 iters, TransitMap tm0.get(\"foo\")");
var tm0 = transit.map(["foo", "transit map string lookup"]);
console.log(tm0.get("foo"));
time(function() {
    for(var i = 0; i < 1000000; i++) {
        tm0.get("foo");
    }
});

console.log("1e5 iters, TransitMap tm0.get([1,2])");
var tm1 = transit.map([[1,2], "transit map array lookup"]);
console.log(tm1.get([1,2]));
time(function() {
    var key = [1,2];
    for(var i = 0; i < 100000; i++) {
        tm1.get(key);
    }
});

console.log("1e6 iters, Complex TransitSet \"bar\"");
var ts = transit.set(["foo",1,"bar",[1,2]])
console.log(ts.has("bar"));
time(function() {
    var key = "bar";
    for(var i = 0; i < 1000000; i++) {
        ts.has(key);
    }
});

console.log("1e5 iters, JSON read transit map with two keyword/string value pairs");
var json = "{\"~:foo\":\"bar\",\"~:baz\":\"woz\"}";
console.log(JSON.parse(json));
time(function() {
    for(var i = 0; i < 100000; i++) {
        JSON.parse(json);
    }
});

console.log("1e5 iters, top level read, map two keyword/string value pairs");
json = "{\"~:foo\":\"bar\",\"~:baz\":\"woz\"}";
var reader = transit.reader("json");
console.log(reader.read(json));
time(function() {
    for(var i = 0; i < 100000; i++) {
        reader.read(json)
    }
});

console.log("1e5 iters, JSON read transit map with two keyword/number value parirs");
json = "{\"~:foo\":1,\"~:bar\":2}";
console.log(JSON.parse(json));
time(function() {
    for(var i = 0; i < 100000; i++) {
        JSON.parse(json);
    }
});

console.log("1e5 iters, top level read, map two keyword/number value pairs");
reader = transit.reader("json");
console.log(reader.read(json));
time(function() {
    for(var i = 0; i < 100000; i++) {
        reader.read(json);
    }
});

console.log("1e5 iters, top level read, read keyword");
var kws = "\"~:foo\"";
reader = transit.reader("json");
console.log(reader.read(kws))
time(function() {
    for(var i = 0; i < 100000; i++) {
        reader.read(kws);
    }
});

console.log("1e5 iters, JSON.stringify, map two keyword/number value pairs");
var m = {foo:1,bar:2};
console.log(JSON.stringify(m));
time(function() {
    for(var i = 0; i < 100000; i++) {
        JSON.stringify(m);
    }
});

console.log("1e5 iters, top level write, map two keyword/number value pairs");
var writer = transit.writer("json");
console.log(writer.write(m));
time(function() {
    for(var i = 0; i < 100000; i++) {
        writer.write(m);
    }
});

console.log("1 iter, JSON.stringfy JS object with 100000 kv pairs");
var largeMap = {};
for(var i = 0; i < 100000; i++) {
    largeMap["foo"+i] = ["bar"+i, i];
}
time(function() {
    JSON.stringify(largeMap);
});

console.log("1 iter, transit write large JS object with 100000 kv pairs, complex");
largeMap = {},
writer = transit.writer("json");
for(var i = 0; i < 100000; i++) {
    largeMap["~:foo"+i] = [transit.keyword("bar"+i),i];
}
time(function() {
    writer.write(largeMap);
});

console.log("1 iter, transit write large JS object with 100000 kv pairs, simple");
largeMap = {};
writer = transit.writer("json");
for(var i = 0; i < 100000; i++) {
    largeMap["~:foo"+i] = 1;
}
time(function() {
    writer.write(largeMap);
});

console.log("1 iter, JSON.parse JS object with 100000 kv pairs");
arr = [];
for(var i = 0; i < 100000; i++) {
    arr.push("\"foo"+i+"\":[\"bar"+i+"\","+i+"]")
}
largeMap = "{"+arr.join(",")+"}",
json = JSON.parse(largeMap);
console.log(Object.keys(json).length);
console.log(json["foo0"]);
time(function() {
    JSON.parse(largeMap);
});

console.log("1 iter, transit read large transit map with 100000 kv pairs");
arr    = [],
reader = transit.writer("json");
for(var i = 0; i < 100000; i++) {
    arr.push("\"~:foo"+i+"\":[\"~:bar"+i+"\","+i+"]");
}
largeMap = "{"+arr.join(",")+"}",
reader   = transit.reader("json");
var largeTransitMap = reader.read(largeMap);
console.log(Object.keys(largeTransitMap).length);
console.log(largeTransitMap["`~:foo0"]);
time(function() {
    reader.read(largeMap);
});
<|endoftext|>


<commit_before>import React from 'react'
import schema from 'part:@sanity/base/schema?'
import client from 'part:@sanity/base/client?'
import Preview from 'part:@sanity/base/preview?'
import {getPublishedId, isDraftId, getDraftId} from 'part:@sanity/base/util/draft-utils'
import {Subject} from 'rxjs'
import {takeUntil, tap, debounceTime, map, switchMap} from 'rxjs/operators'
import {IntentLink} from 'part:@sanity/base/router'
import {flow, compact, flatten, union} from 'lodash'
import SearchIcon from 'part:@sanity/base/search-icon'
import Spinner from 'part:@sanity/components/loading/spinner'
import enhanceClickOutside from 'react-click-outside'
import Ink from 'react-ink'
import styles from './styles/Search.css'

function isParentOf(possibleParent, possibleChild) {
  let current = possibleChild
  while (current) {
    if (current === possibleParent) {
      return true
    }
    current = current.parentNode
  }
  return false
}

// Removes published documents that also has a draft
function removeDupes(documents) {
  const drafts = documents.map(doc => doc._id).filter(isDraftId)

  return documents.filter(doc => {
    const draftId = getDraftId(doc._id)
    const publishedId = getPublishedId(doc._id)
    const hasDraft = drafts.includes(draftId)
    const isPublished = doc._id === publishedId
    return isPublished ? !hasDraft : true
  })
}

const combineFields = flow([flatten, union, compact])

function search(query) {
  if (!client) {
    throw new Error('Sanity client is missing')
  }

  const candidateTypes = schema
    .getTypeNames()
    .filter(typeName => !typeName.startsWith('sanity.'))
    .map(typeName => schema.get(typeName))

  const terms = query.split(/\s+/).filter(Boolean)

  const uniqueFields = combineFields(candidateTypes.map(type => type.__unstable_searchFields))
  const constraints = terms.map(term => uniqueFields.map(field => `${field} match '${term}*'`))
  const constraintString = constraints
    .map(constraint => `(${constraint.join(' || ')})`)
    .join(' && ')
  return client.observable.fetch(`*[${constraintString}][0...10]`)
}

export default enhanceClickOutside(
  class Search extends React.Component {
    input$ = new Subject()
    componentWillUnmount$ = new Subject()

    state = {
      isOpen: false,
      hits: [],
      activeIndex: -1,
      inputValue: ''
    }

    componentDidMount() {
      this.input$
        .asObservable()
        .pipe(
          map(event => event.target.value),
          tap(inputValue => this.setState({inputValue})),
          takeUntil(this.componentWillUnmount$.asObservable())
        )
        .subscribe()

      this.input$
        .asObservable()
        .pipe(
          map(event => event.target.value),
          debounceTime(100),
          tap(() => {
            this.setState({
              isSearching: true
            })
          }),
          switchMap(search),
          // we need this filtering because the search may return documents of types not in schema
          map(hits => hits.filter(hit => schema.has(hit._type))),
          map(removeDupes),
          tap(hits => {
            this.setState({
              isSearching: false,
              hits: hits
            })
          }),
          takeUntil(this.componentWillUnmount$.asObservable())
        )
        .subscribe()
    }

    componentWillUnmount() {
      this.componentWillUnmount$.next()
      this.componentWillUnmount$.complete()
    }

    handleInputChange = event => {
      this.input$.next(event)
      this.open()
    }

    handleKeyPress = event => {
      this.inputElement.focus()
    }

    handleKeyDown = event => {
      if (event.key === 'Backspace') {
        this.inputElement.focus()
      }
      if (event.key === 'Escape') {
        this.close()
      }
      if (event.key === 'Enter') {
        this.listElement.querySelector(`[data-hit-index="${this.state.activeIndex}"]`).click()
      }
      const {isOpen, hits, activeIndex} = this.state

      const isArrowKey = ['ArrowUp', 'ArrowDown'].includes(event.key)

      if (!isOpen && isArrowKey) {
        this.open()
        return
      }

      const lastIndex = hits.length - 1
      if (isArrowKey) {
        event.preventDefault()
        let nextIndex = activeIndex + (event.key === 'ArrowUp' ? -1 : 1)
        if (nextIndex < 0) {
          nextIndex = lastIndex
        }
        if (nextIndex > lastIndex) {
          nextIndex = 0
        }
        this.setState({activeIndex: nextIndex})
      }
    }

    close() {
      this.setOpen(false)
    }

    open() {
      this.setOpen(true)
    }

    setOpen(isOpen) {
      this.setState({isOpen})
    }

    handleClickOutside = el => {
      if (this.state.isOpen) {
        this.close()
      }
    }

    handleHitClick = el => {
      this.close()
    }

    handleInputClick = el => {
      this.open()
    }

    handleFocus = el => {
      this.open()
    }

    handleBlur = el => {
      if (!isParentOf(this.rootElement, el.relatedTarget)) {
        this.close()
      }
    }

    setInput = el => {
      this.inputElement = el
    }

    setListElement = el => {
      this.listElement = el
    }

    setRootElement = el => {
      this.rootElement = el
    }

    componentDidUpdate(prevProps, prevState) {
      if (!prevState.isOpen && this.state.isOpen) {
        this.inputElement.select()
      }
    }

    handleHitMouseDown = ev => {
      this.setState({
        activeIndex: Number(ev.currentTarget.getAttribute('data-hit-index'))
      })
    }

    handleHitMouseUp = () => {
      this.inputElement.focus()
    }

    renderItem = (item, index) => {
      const type = schema.get(item._type)
      const {activeIndex} = this.state
      return (
        <IntentLink
          intent="edit"
          params={{id: item._id, type: type.name}}
          className={activeIndex === index ? styles.activeLink : styles.link}
          data-hit-index={index}
          onMouseDown={this.handleHitMouseDown}
          onMouseUp={this.handleHitMouseUp}
          onClick={this.handleHitClick}
          tabIndex={-1}
        >

{type.title}

          <Preview value={item} layout="default" type={type} />
          <Ink duration={200} opacity={0.1} radius={200} />
        </IntentLink>
      )
    }

    render() {
      const {isSearching, hits, isOpen, inputValue} = this.state
      return (

                <SearchIcon />

            <input
              className={styles.input}
              type="search"
              value={isOpen ? inputValue : ''}
              onInput={this.handleInputChange}
              onBlur={this.handleBlur}
              onClick={this.handleInputClick}
              onFocus={this.handleFocus}
              onKeyDown={this.handleKeyDown}
              placeholder="Search…"
              ref={this.setInput}
            />

          {isOpen &&
            (inputValue || isSearching || hits > 0) && (

{isSearching && <Spinner />}

                {inputValue &&
                  !isSearching &&
                  (!hits || hits.length === 0) && (

                      Could not find “{inputValue}”

                  )}
                {!isSearching &&
                  hits &&
                  hits.length > 0 && (

                        {hits.map((hit, index) => (

                            {this.renderItem(hit, index)}

                        ))}

                  )}

            )}

      )
    }
  }
)
<commit_msg>[default-layout] Pass search terms as params (#948)<commit_after>import React from 'react'
import schema from 'part:@sanity/base/schema?'
import client from 'part:@sanity/base/client?'
import Preview from 'part:@sanity/base/preview?'
import {getPublishedId, isDraftId, getDraftId} from 'part:@sanity/base/util/draft-utils'
import {Subject} from 'rxjs'
import {takeUntil, tap, debounceTime, map, switchMap} from 'rxjs/operators'
import {IntentLink} from 'part:@sanity/base/router'
import {flow, compact, flatten, union} from 'lodash'
import SearchIcon from 'part:@sanity/base/search-icon'
import Spinner from 'part:@sanity/components/loading/spinner'
import enhanceClickOutside from 'react-click-outside'
import Ink from 'react-ink'
import styles from './styles/Search.css'

function isParentOf(possibleParent, possibleChild) {
  let current = possibleChild
  while (current) {
    if (current === possibleParent) {
      return true
    }
    current = current.parentNode
  }
  return false
}

// Removes published documents that also has a draft
function removeDupes(documents) {
  const drafts = documents.map(doc => doc._id).filter(isDraftId)

  return documents.filter(doc => {
    const draftId = getDraftId(doc._id)
    const publishedId = getPublishedId(doc._id)
    const hasDraft = drafts.includes(draftId)
    const isPublished = doc._id === publishedId
    return isPublished ? !hasDraft : true
  })
}

const combineFields = flow([flatten, union, compact])

function search(query) {
  if (!client) {
    throw new Error('Sanity client is missing')
  }

  const candidateTypes = schema
    .getTypeNames()
    .filter(typeName => !typeName.startsWith('sanity.'))
    .map(typeName => schema.get(typeName))

  const terms = query.split(/\s+/).filter(Boolean)

  const params = terms.reduce((acc, term, i) => {
    acc[`t${i}`] = `${term}*`
    return acc
  }, {})

  const uniqueFields = combineFields(candidateTypes.map(type => type.__unstable_searchFields))
  const constraints = terms.map((term, i) => uniqueFields.map(field => `${field} match $t${i}`))
  const constraintString = constraints
    .map(constraint => `(${constraint.join(' || ')})`)
    .join(' && ')
  return client.observable.fetch(`*[${constraintString}][0...10]`, params)
}

export default enhanceClickOutside(
  class Search extends React.Component {
    input$ = new Subject()
    componentWillUnmount$ = new Subject()

    state = {
      isOpen: false,
      hits: [],
      activeIndex: -1,
      inputValue: ''
    }

    componentDidMount() {
      this.input$
        .asObservable()
        .pipe(
          map(event => event.target.value),
          tap(inputValue => this.setState({inputValue})),
          takeUntil(this.componentWillUnmount$.asObservable())
        )
        .subscribe()

      this.input$
        .asObservable()
        .pipe(
          map(event => event.target.value),
          debounceTime(100),
          tap(() => {
            this.setState({
              isSearching: true
            })
          }),
          switchMap(search),
          // we need this filtering because the search may return documents of types not in schema
          map(hits => hits.filter(hit => schema.has(hit._type))),
          map(removeDupes),
          tap(hits => {
            this.setState({
              isSearching: false,
              hits: hits
            })
          }),
          takeUntil(this.componentWillUnmount$.asObservable())
        )
        .subscribe()
    }

    componentWillUnmount() {
      this.componentWillUnmount$.next()
      this.componentWillUnmount$.complete()
    }

    handleInputChange = event => {
      this.input$.next(event)
      this.open()
    }

    handleKeyPress = event => {
      this.inputElement.focus()
    }

    handleKeyDown = event => {
      if (event.key === 'Backspace') {
        this.inputElement.focus()
      }
      if (event.key === 'Escape') {
        this.close()
      }
      if (event.key === 'Enter') {
        this.listElement.querySelector(`[data-hit-index="${this.state.activeIndex}"]`).click()
      }
      const {isOpen, hits, activeIndex} = this.state

      const isArrowKey = ['ArrowUp', 'ArrowDown'].includes(event.key)

      if (!isOpen && isArrowKey) {
        this.open()
        return
      }

      const lastIndex = hits.length - 1
      if (isArrowKey) {
        event.preventDefault()
        let nextIndex = activeIndex + (event.key === 'ArrowUp' ? -1 : 1)
        if (nextIndex < 0) {
          nextIndex = lastIndex
        }
        if (nextIndex > lastIndex) {
          nextIndex = 0
        }
        this.setState({activeIndex: nextIndex})
      }
    }

    close() {
      this.setOpen(false)
    }

    open() {
      this.setOpen(true)
    }

    setOpen(isOpen) {
      this.setState({isOpen})
    }

    handleClickOutside = el => {
      if (this.state.isOpen) {
        this.close()
      }
    }

    handleHitClick = el => {
      this.close()
    }

    handleInputClick = el => {
      this.open()
    }

    handleFocus = el => {
      this.open()
    }

    handleBlur = el => {
      if (!isParentOf(this.rootElement, el.relatedTarget)) {
        this.close()
      }
    }

    setInput = el => {
      this.inputElement = el
    }

    setListElement = el => {
      this.listElement = el
    }

    setRootElement = el => {
      this.rootElement = el
    }

    componentDidUpdate(prevProps, prevState) {
      if (!prevState.isOpen && this.state.isOpen) {
        this.inputElement.select()
      }
    }

    handleHitMouseDown = ev => {
      this.setState({
        activeIndex: Number(ev.currentTarget.getAttribute('data-hit-index'))
      })
    }

    handleHitMouseUp = () => {
      this.inputElement.focus()
    }

    renderItem = (item, index) => {
      const type = schema.get(item._type)
      const {activeIndex} = this.state
      return (
        <IntentLink
          intent="edit"
          params={{id: item._id, type: type.name}}
          className={activeIndex === index ? styles.activeLink : styles.link}
          data-hit-index={index}
          onMouseDown={this.handleHitMouseDown}
          onMouseUp={this.handleHitMouseUp}
          onClick={this.handleHitClick}
          tabIndex={-1}
        >

{type.title}

          <Preview value={item} layout="default" type={type} />
          <Ink duration={200} opacity={0.1} radius={200} />
        </IntentLink>
      )
    }

    render() {
      const {isSearching, hits, isOpen, inputValue} = this.state
      return (

                <SearchIcon />

            <input
              className={styles.input}
              type="search"
              value={isOpen ? inputValue : ''}
              onInput={this.handleInputChange}
              onBlur={this.handleBlur}
              onClick={this.handleInputClick}
              onFocus={this.handleFocus}
              onKeyDown={this.handleKeyDown}
              placeholder="Search…"
              ref={this.setInput}
            />

          {isOpen &&
            (inputValue || isSearching || hits > 0) && (

{isSearching && <Spinner />}

                {inputValue &&
                  !isSearching &&
                  (!hits || hits.length === 0) && (

                      Could not find “{inputValue}”

                  )}
                {!isSearching &&
                  hits &&
                  hits.length > 0 && (

                        {hits.map((hit, index) => (

                            {this.renderItem(hit, index)}

                        ))}

                  )}

            )}

      )
    }
  }
)
<|endoftext|>


<commit_before>/**
 * Afaan Bilal's Portfolio
 *
 * https://afaan.dev
 *
 * (c) Afaan Bilal
 */

String.prototype.toTitleCase = function () { return this.replace(/\w\S*/g, function(txt) { return txt.charAt(0).toUpperCase() + txt.substr(1) }) }

new Vue({
    el: '#app',
    data: {
        allRepos: [],
        skillTypes: [
            'npm', 'framework', 'composer', 'platform', 'language', 'database', 'service', 'skill', 'software'
        ],
        skills: [
            { name: 'JavaScript', type: 'language' },
            { name: 'C', type: 'language' },
            { name: 'C++', type: 'language' },
            { name: 'C#', type: 'language' },
            { name: 'Python', type: 'language' },
            { name: 'PHP', type: 'language' },
            { name: 'SQL', type: 'language' },
            { name: 'HTML', type: 'language' },
            { name: 'CSS', type: 'language' },
            { name: 'XML', type: 'language' },
            { name: 'Rust', type: 'language' },
            { name: 'SCSS', type: 'language' },
            { name: 'Java', type: 'language' },
            { name: 'JSON', type: 'language' },
            { name: 'Go', type: 'language' },

            { name: 'Redis', type: 'database' },
            { name: 'MySQL', type: 'database' },
            { name: 'MariaDB', type: 'database' },
            { name: 'SQLite', type: 'database' },
            { name: 'Firebase', type: 'database' },

            { name: 'Laravel', type: 'framework' },
            { name: 'VueJS', type: 'framework' },
            { name: 'React Native', type: 'framework' },
            { name: 'ReactJS', type: 'framework' },
            { name: 'CodeIgniter', type: 'framework' },

            { name: 'GitHub', type: 'service' },
            { name: 'GitLab', type: 'service' },
            { name: 'Docker Hub', type: 'service' },
            { name: 'AWS', type: 'service' },
            { name: 'AWS EC2', type: 'service' },
            { name: 'AWS SES', type: 'service' },
            { name: 'AWS RDS', type: 'service' },
            { name: 'DigitalOcean', type: 'service' },
            { name: 'DigitalOcean Droplets', type: 'service' },
            { name: 'DigitalOcean Spaces', type: 'service' },
            { name: 'SendGrid', type: 'service' },
            { name: 'Twilio', type: 'service' },
            { name: 'Draw.io', type: 'service' },

            { name: 'Android', type: 'platform' },
            { name: 'Ubuntu', type: 'platform' },
            { name: 'Web', type: 'platform' },
            { name: 'Amazon Linux', type: 'platform' },
            { name: 'MacOS', type: 'platform' },
            { name: 'Windows', type: 'platform' },
            { name: 'Arduino', type: 'platform' },

            { name: 'VSCode', type: 'software' },
            { name: 'Apache', type: 'software' },
            { name: 'Docker', type: 'software' },
            { name: 'Docker Swarm', type: 'software' },
            { name: 'Git', type: 'software' },
            { name: 'Microsoft Office', type: 'software' },
            { name: 'SketchUp', type: 'software' },
            { name: 'Slack', type: 'software' },
            { name: 'phpMyAdmin', type: 'software' },
            { name: 'Termius', type: 'software' },
            { name: 'XAMPP', type: 'software' },
            { name: 'FileZilla', type: 'software' },
            { name: 'Wordpress', type: 'software' },
            { name: 'Everything', type: 'software' },
            { name: 'Sequel Ace', type: 'software' },
            { name: 'Android Studio', type: 'software' },
            { name: 'Microsoft Visual Studio', type: 'software' },

            { name: 'Programming', type: 'skill' },
            { name: 'FullStack', type: 'skill' },
            { name: 'WebDev', type: 'skill' },
            { name: 'FTP', type: 'skill' },
            { name: 'SSH', type: 'skill' },
            { name: 'Computer Vision', type: 'skill' },
            { name: 'Object Detection', type: 'skill' },
            { name: 'Object Recognition', type: 'skill' },
            { name: 'Machine Learning', type: 'skill' },
            { name: 'REST', type: 'skill' },
            { name: 'API', type: 'skill' },
            { name: 'Bootstrap', type: 'skill' },
            { name: 'Tailwind', type: 'skill' },
            { name: 'OAuth', type: 'skill' },
            { name: 'jQuery', type: 'skill' },
            { name: 'Traefik', type: 'skill' },

            { name: '@afaanbilal/random-string', type: 'npm', link: 'https://www.npmjs.com/package/@afaanbilal/random-string' },
            { name: 'react-native-multitap', type: 'npm', link: 'https://www.npmjs.com/package/react-native-multitap' },
            { name: 'react-native-pagination-dots', type: 'npm', link: 'https://www.npmjs.com/package/react-native-pagination-dots' },

            { name: 'afaanbilal/php-random-string', type: 'composer', link: 'https://packagist.org/packages/afaanbilal/php-random-string' },
        ],

        hardwareTypes: [ 'hardware', 'headset', 'hub', 'laptop', 'cpu', 'gpu', 'monitor', 'keyboard', 'mouse', 'webcam', 'phone', 'speaker' ],
        hardware: [
            { name: 'HyperX Cloud Alpha S', type: 'headset' },
            { name: 'Sony WH-1000XM3', type: 'headset' },
            { name: 'XP Pen Deco Pro Medium', type: 'hardware' },
            { name: 'TP Link Powered USB Hub', type: 'hub' },

            { name: 'Apple MacBook 16"', type: 'laptop' },
            { name: 'Acer Predator Helios 300', type: 'laptop' },

            { name: 'Logitech c922 Pro', type: 'webcam' },

            { name: 'OnePlus Nord', type: 'phone' },
            { name: 'iPhone 12', type: 'phone' },

            { name: 'Mi Soundbar', type: 'speaker' },

            { name: 'Ryzen 9 5950X', type: 'cpu' },
            { name: 'Intel i9 10th Gen', type: 'cpu' },
            { name: 'Intel i7 7th Gen', type: 'cpu' },

            { name: 'Nvidia RTX 3080', type: 'gpu' },
            { name: 'AMD Radeon Pro 5500M', type: 'gpu' },
            { name: 'Nvidia GTX 1060', type: 'gpu' },

            { name: 'BenQ Zowie XL2546 240Hz', type: 'monitor' },
            { name: 'HP 27es', type: 'monitor' },

            { name: 'Corsair K70 MK.2 Mechanical', type: 'keyboard' },
            { name: 'HyperX Alloy FPS Mechanical', type: 'keyboard' },
            { name: 'Logitech MX Keys', type: 'keyboard' },

            { name: 'Logitech MX Master 3', type: 'mouse' },
            { name: 'Logitech G502 Hero', type: 'mouse' },
            { name: 'Logitech G402 FPS', type: 'mouse' },
        ],

        name: '',
        email: '',
        message: '',
        sent: false,
        sending: false,
    },
    computed: {
        repos() {
            let excludeNames = ["AfaanBilal", "afaanbilal.github.io", "musings", "amx-infinity.github.io", "SoftSolutions"]
            return this.allRepos.filter(o => !excludeNames.includes(o.name)).sort((b, a) => a.stargazers_count - b.stargazers_count)
        },
    },
    methods: {
        getBackground(t) {
            switch (t) {
                case 'skill': return 'linear-gradient(90deg, #8034cc 0%,#991a79 100%)'
                case 'software': return 'linear-gradient(to right, #5f2c82, #49a09d)'
                case 'language': return 'linear-gradient(to right, #ff00cc, #333399)'
                case 'framework': return 'linear-gradient(90deg, #40e0e6 0%,#e4256e 100%)'
                case 'service': return 'linear-gradient(to right, #e55d87, #9f73c4)'
                case 'platform': return 'linear-gradient(to right, #f2709c, #ff9472)'
                case 'npm': return 'linear-gradient(139deg, #fb8817, #ff4b01, #c12127, #e02aff)'
                case 'database': return 'linear-gradient(139deg, #c12127, #e02aff, #fb8817, #ff4b01)'
                case 'composer': return 'linear-gradient(139deg, #fb8817, #ff4b01, #c12127, #e02aff)'
            }
        },
        getPlural(t) {
            if (t == 'npm') return 'NPM Packages'
            if (t == 'composer') return 'Composer Packages'
            if (t == 'mouse') return 'Mice'
            if (t == 'cpu') return 'CPUs'
            if (t == 'gpu') return 'GPUs'
            return (t == 'hardware' ? t : t + 's').toTitleCase()
        },
        sendMessage() {
            this.sending = true
            fetch('https://script.google.com/macros/s/AKfycby3_0eYQLINNn3KgorICGzH4yru0FLvqdZ8BYfeQMRVddG0m51GV_mB/exec', {
                method: 'post',
                headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                body: 'name=' + encodeURIComponent(this.name) + '&email=' + encodeURIComponent(this.email) + '&message=' + encodeURIComponent(this.message)
            }).then(r => r.text()).then((d) => {
                if (d == 'success') {
                    this.name = ''
                    this.email = ''
                    this.message = ''
                    this.sending = false
                    this.sent = true
                }
            })
        },
        getStackItemName(t) {
            let string = t.name.split('-')[0]
            return string.charAt(0).toUpperCase() + string.slice(1)
        },
    },
    mounted() {
        fetch('https://api.github.com/users/AfaanBilal/repos?per_page=100').then(r => r.json()).then((d) => { this.allRepos = d }).then(() => {
            fetch('https://api.github.com/orgs/AMX-Infinity/repos?per_page=100').then(r => r.json()).then((d) => { this.allRepos.push(...d) })
        })
    },
})

function startAnim(elementID, elementWidth) {
    function restart() {
        $('#' + elementID + '.tech-stack').first().css({
            'margin-left': '0px'
        });
        start();
    }

    function start() {
        var techStack = $('#' + elementID + '.tech-stack').first(),
            duration = (elementWidth * 20);

        techStack.animate({
            'margin-left': '-' + elementWidth + 'px'
        }, duration, 'linear', restart);
    }

    $('#' + elementID + '.tech-stack img').hover(() => {
        $('#' + elementID + '.tech-stack').first().stop();
    }, () => { start(); });

    start();
}

function setupAnim(id) {
    var stripWidth = $('#' + id + ' .tech-stack-item').length * (92);
    $('#' + id + ' .tech-stack-list').parent().append($('#' + id + ' .tech-stack-list').clone());
    startAnim(id, stripWidth);
}

setupAnim('tech-stack-1')
$('#tech-stack-1').parent().append($('#tech-stack-1').clone().attr('id', 'tech-stack-2').css('margin-left', '-2300px'));
setupAnim('tech-stack-2')
<commit_msg>♻ Cleanup<commit_after>/**
 * Afaan Bilal's Portfolio
 *
 * https://afaan.dev
 *
 * (c) Afaan Bilal
 */

String.prototype.toTitleCase = function () { return this.replace(/\w\S*/g, function(txt) { return txt.charAt(0).toUpperCase() + txt.substr(1) }) }

new Vue({
    el: '#app',
    data: {
        allRepos: [],
        skillTypes: [
            'npm', 'framework', 'composer', 'platform', 'language', 'database', 'service', 'skill', 'software'
        ],
        skills: [
            { name: 'JavaScript', type: 'language' },
            { name: 'C', type: 'language' },
            { name: 'C++', type: 'language' },
            { name: 'C#', type: 'language' },
            { name: 'Python', type: 'language' },
            { name: 'PHP', type: 'language' },
            { name: 'SQL', type: 'language' },
            { name: 'HTML', type: 'language' },
            { name: 'CSS', type: 'language' },
            { name: 'XML', type: 'language' },
            { name: 'Rust', type: 'language' },
            { name: 'SCSS', type: 'language' },
            { name: 'Java', type: 'language' },
            { name: 'JSON', type: 'language' },
            { name: 'Go', type: 'language' },

            { name: 'Redis', type: 'database' },
            { name: 'MySQL', type: 'database' },
            { name: 'MariaDB', type: 'database' },
            { name: 'SQLite', type: 'database' },
            { name: 'Firebase', type: 'database' },

            { name: 'Laravel', type: 'framework' },
            { name: 'VueJS', type: 'framework' },
            { name: 'React Native', type: 'framework' },
            { name: 'ReactJS', type: 'framework' },
            { name: 'CodeIgniter', type: 'framework' },

            { name: 'GitHub', type: 'service' },
            { name: 'GitLab', type: 'service' },
            { name: 'Docker Hub', type: 'service' },
            { name: 'AWS', type: 'service' },
            { name: 'AWS EC2', type: 'service' },
            { name: 'AWS SES', type: 'service' },
            { name: 'AWS RDS', type: 'service' },
            { name: 'DigitalOcean', type: 'service' },
            { name: 'DigitalOcean Droplets', type: 'service' },
            { name: 'DigitalOcean Spaces', type: 'service' },
            { name: 'SendGrid', type: 'service' },
            { name: 'Twilio', type: 'service' },
            { name: 'Draw.io', type: 'service' },

            { name: 'Android', type: 'platform' },
            { name: 'Ubuntu', type: 'platform' },
            { name: 'Web', type: 'platform' },
            { name: 'Amazon Linux', type: 'platform' },
            { name: 'MacOS', type: 'platform' },
            { name: 'Windows', type: 'platform' },
            { name: 'Arduino', type: 'platform' },

            { name: 'VSCode', type: 'software' },
            { name: 'Apache', type: 'software' },
            { name: 'Docker', type: 'software' },
            { name: 'Docker Swarm', type: 'software' },
            { name: 'Git', type: 'software' },
            { name: 'Microsoft Office', type: 'software' },
            { name: 'SketchUp', type: 'software' },
            { name: 'Slack', type: 'software' },
            { name: 'phpMyAdmin', type: 'software' },
            { name: 'Termius', type: 'software' },
            { name: 'XAMPP', type: 'software' },
            { name: 'FileZilla', type: 'software' },
            { name: 'Wordpress', type: 'software' },
            { name: 'Everything', type: 'software' },
            { name: 'Sequel Ace', type: 'software' },
            { name: 'Android Studio', type: 'software' },
            { name: 'Microsoft Visual Studio', type: 'software' },

            { name: 'Programming', type: 'skill' },
            { name: 'FullStack', type: 'skill' },
            { name: 'WebDev', type: 'skill' },
            { name: 'FTP', type: 'skill' },
            { name: 'SSH', type: 'skill' },
            { name: 'Computer Vision', type: 'skill' },
            { name: 'Object Detection', type: 'skill' },
            { name: 'Object Recognition', type: 'skill' },
            { name: 'Machine Learning', type: 'skill' },
            { name: 'REST', type: 'skill' },
            { name: 'API', type: 'skill' },
            { name: 'Bootstrap', type: 'skill' },
            { name: 'Tailwind', type: 'skill' },
            { name: 'OAuth', type: 'skill' },
            { name: 'jQuery', type: 'skill' },
            { name: 'Traefik', type: 'skill' },

            { name: '@afaanbilal/random-string', type: 'npm', link: 'https://www.npmjs.com/package/@afaanbilal/random-string' },
            { name: 'react-native-multitap', type: 'npm', link: 'https://www.npmjs.com/package/react-native-multitap' },
            { name: 'react-native-pagination-dots', type: 'npm', link: 'https://www.npmjs.com/package/react-native-pagination-dots' },

            { name: 'afaanbilal/php-random-string', type: 'composer', link: 'https://packagist.org/packages/afaanbilal/php-random-string' },
        ],

        hardwareTypes: [ 'hardware', 'headset', 'hub', 'laptop', 'cpu', 'gpu', 'monitor', 'keyboard', 'mouse', 'webcam', 'phone', 'speaker' ],
        hardware: [
            { name: 'HyperX Cloud Alpha S', type: 'headset' },
            { name: 'Sony WH-1000XM3', type: 'headset' },
            { name: 'XP Pen Deco Pro Medium', type: 'hardware' },
            { name: 'TP Link Powered USB Hub', type: 'hub' },

            { name: 'Apple MacBook 16"', type: 'laptop' },
            { name: 'Acer Predator Helios 300', type: 'laptop' },

            { name: 'Logitech c922 Pro', type: 'webcam' },

            { name: 'OnePlus Nord', type: 'phone' },
            { name: 'iPhone 12', type: 'phone' },

            { name: 'Mi Soundbar', type: 'speaker' },

            { name: 'Ryzen 9 5950X', type: 'cpu' },
            { name: 'Intel i9 10th Gen', type: 'cpu' },
            { name: 'Intel i7 7th Gen', type: 'cpu' },

            { name: 'Nvidia RTX 3080', type: 'gpu' },
            { name: 'AMD Radeon Pro 5500M', type: 'gpu' },
            { name: 'Nvidia GTX 1060', type: 'gpu' },

            { name: 'BenQ Zowie XL2546 240Hz', type: 'monitor' },
            { name: 'HP 27es', type: 'monitor' },

            { name: 'Corsair K70 MK.2 Mechanical', type: 'keyboard' },
            { name: 'HyperX Alloy FPS Mechanical', type: 'keyboard' },
            { name: 'Logitech MX Keys', type: 'keyboard' },

            { name: 'Logitech MX Master 3', type: 'mouse' },
            { name: 'Logitech G502 Hero', type: 'mouse' },
            { name: 'Logitech G402 FPS', type: 'mouse' },
        ],

        name: '',
        email: '',
        message: '',
        sent: false,
        sending: false,
    },
    computed: {
        repos() {
            let excludeNames = ["AfaanBilal", "afaanbilal.github.io", "musings", "amx-infinity.github.io", "SoftSolutions"]
            return this.allRepos.filter(o => !excludeNames.includes(o.name)).sort((b, a) => a.stargazers_count - b.stargazers_count)
        },
    },
    methods: {
        getBackground(t) {
            switch (t) {
                case 'skill': return 'linear-gradient(90deg, #8034cc 0%,#991a79 100%)'
                case 'software': return 'linear-gradient(to right, #5f2c82, #49a09d)'
                case 'language': return 'linear-gradient(to right, #ff00cc, #333399)'
                case 'framework': return 'linear-gradient(90deg, #40e0e6 0%,#e4256e 100%)'
                case 'service': return 'linear-gradient(to right, #e55d87, #9f73c4)'
                case 'platform': return 'linear-gradient(to right, #f2709c, #ff9472)'
                case 'npm': return 'linear-gradient(139deg, #fb8817, #ff4b01, #c12127, #e02aff)'
                case 'database': return 'linear-gradient(139deg, #c12127, #e02aff, #fb8817, #ff4b01)'
                case 'composer': return 'linear-gradient(139deg, #fb8817, #ff4b01, #c12127, #e02aff)'
            }
        },
        getPlural(t) {
            if (t == 'npm') return 'NPM Packages'
            if (t == 'composer') return 'Composer Packages'
            if (t == 'mouse') return 'Mice'
            if (t == 'cpu') return 'CPUs'
            if (t == 'gpu') return 'GPUs'
            return (t == 'hardware' ? t : t + 's').toTitleCase()
        },
        sendMessage() {
            this.sending = true
            fetch('https://script.google.com/macros/s/AKfycby3_0eYQLINNn3KgorICGzH4yru0FLvqdZ8BYfeQMRVddG0m51GV_mB/exec', {
                method: 'post',
                headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                body: 'name=' + encodeURIComponent(this.name) + '&email=' + encodeURIComponent(this.email) + '&message=' + encodeURIComponent(this.message)
            }).then(r => r.text()).then((d) => {
                if (d == 'success') {
                    this.name = ''
                    this.email = ''
                    this.message = ''
                    this.sending = false
                    this.sent = true
                }
            })
        },
        getStackItemName(t) {
            let string = t.name.split('-')[0]
            return string.charAt(0).toUpperCase() + string.slice(1)
        },
    },
    mounted() {
        fetch('https://api.github.com/users/AfaanBilal/repos?per_page=100').then(r => r.json()).then((d) => { this.allRepos = d }).then(() => {
            fetch('https://api.github.com/orgs/AMX-Infinity/repos?per_page=100').then(r => r.json()).then((d) => { this.allRepos.push(...d) })
        })
    },
})

function startAnim(elementID, elementWidth) {
    function restart() {
        $('#' + elementID + '.tech-stack').first().css({
            'margin-left': '0px'
        });
        start();
    }

    function start() {
        var techStack = $('#' + elementID + '.tech-stack').first(),
            duration = (elementWidth * 20);

        techStack.animate({
            'margin-left': '-' + elementWidth + 'px'
        }, duration, 'linear', restart);
    }

    $('#' + elementID + '.tech-stack img').hover(() => {
        $('#' + elementID + '.tech-stack').first().stop();
    }, () => { start(); });

    start();
}

function setupAnim(id) {
    var stripWidth = $('#' + id + ' .tech-stack-item').length * 92;
    $('#' + id + ' .tech-stack-list').parent().append($('#' + id + ' .tech-stack-list').clone());
    startAnim(id, stripWidth);
}

setupAnim('tech-stack-1')
$('#tech-stack-1').parent().append($('#tech-stack-1').clone().attr('id', 'tech-stack-2').css('margin-left', '-2300px'));
setupAnim('tech-stack-2')
<|endoftext|>


<commit_before>$(function() {

	'use strict';

	var

		client = ZAFClient.init(),

		attachments = [],

		path = "ticket.comments",

		$download = $('#download'),
		$list = $('#list'),
		$message = $('#message'),
		$status = $('#status'),
		$progress = $('#progress'),

		findAttachments = function() {
			return client.get(path).then(function(response) {
				var allComments = response[path];
				attachments = $.map($.makeArray(allComments), function(comment) {
					return [].concat(comment.imageAttachments).concat(comment.nonImageAttachments);
				});
				return new Promise(function(resolve, reject) {
					if (!$.isArray(attachments) || attachments.length === 0) {
						reject("No attachments found in this ticket.");
					} else {
						resolve();
					}
				});
			});
		},

		displayAttachments = function() {
			var html = $.map(attachments, function(attachment) {
				return (
					"
" +
					""+attachment.filename+"" +
					"
"
				)
			});
			$list.
				append(html).
				toggle()
			;
			message("" + attachments.length + " attachment" + (attachments.length == 1 ? "" : "s") + " found in this ticket.");
		},

		makeZip = function() {
			var zip = new JSZip();
			$.each(attachments, function(index, attachment) {
				zip.file(
					attachment.filename,
					urlToPromise(attachment.contentUrl),
					{
						binary:true
					}
				);
			});
			return zip;
		},

		downloadAttachments = function() {
			var first = true;
			return makeZip()
			.generateAsync({type:"blob"}, function updateCallback(metadata) {

				if (first) {
					$progress.show();
					first = false;
				}

				var percent = metadata.percent;
				$progress.percent = percent;
				status("Making ZIP: " + percent.toFixed(2) + "%");
			})
			.then(function (blob) {
				client.context().then(function(context) {
					var filename = "Zendesk-";
					filename += context.ticketId;
					filename += "-attachments-";
					filename += new Date().getTime();
					filename += ".zip";
					saveAs(blob, filename);
				})
			});
		},

		message = function(message) {
			display($message, message);
		},

		status = function(message) {
			display($status, message);
		},

		display = function($element, message) {
			$element.html(message);
		},

		show = function($element) {
			$element.show(600);
		},

		hide = function($element) {
			$element.hide(600);
		}
	;

	/**
	* Set the width of the colored part of the progress bar.
	*/
	Object.defineProperty($progress, "percent", {
		set: function(percent) {
			this
			.children(":first")
			.attr('aria-valuenow', percent)
			.width(percent+"%");
		}
	});

	// EVENT HANDLERS //

	client.on('app.registered', function appRegistered(event) {
		$progress.hide();
		findAttachments()
		.then(function() {
			attachments.sort(function(a,b) {
				return a.filename > b.filename ? 1 : a.filename < b.filename ? -1 : 0;
			});
			displayAttachments();
			$download.show();
		})
		.catch(function(err) {
			message(err);
			$download.hide();
		})
		.then(function() {
			$('#container').show();
		});
	});

	client.on('ticket.comments.changed', function(event) {
		console.log(event);
	});

	$download.on("click", function() {
		hide($message);
		$download.hide();
		show($status);
		status("Fetching attachments...");
		downloadAttachments().
		then(function() {
			status("ZIP done!");
			setTimeout(function() {
				hide($status);
				hide($progress);
				$progress.percent = 0;
				show($message);
				show($download);
			}, 2000);
		});
	});

	$message.on("click", function() {
		$list.slideToggle();
	});

	/**
	 * Fetch the content and return the associated promise.
	 * @param {String} url the url of the content to fetch.
	 * @return {Promise} the promise containing the data.
	 */
	function urlToPromise(url) {
		return new Promise(function(resolve, reject) {
			JSZipUtils.getBinaryContent(url, function (err, data) {
				if(err) {
					reject(err);
				} else {
					resolve(data);
				}
			});
		});
	}

});

<commit_msg>More specific selector for progress bar<commit_after>$(function() {

	'use strict';

	var

		client = ZAFClient.init(),

		attachments = [],

		path = "ticket.comments",

		$download = $('#download'),
		$list = $('#list'),
		$message = $('#message'),
		$status = $('#status'),
		$progress = $('#progress'),

		findAttachments = function() {
			return client.get(path).then(function(response) {
				var allComments = response[path];
				attachments = $.map($.makeArray(allComments), function(comment) {
					return [].concat(comment.imageAttachments).concat(comment.nonImageAttachments);
				});
				return new Promise(function(resolve, reject) {
					if (!$.isArray(attachments) || attachments.length === 0) {
						reject("No attachments found in this ticket.");
					} else {
						resolve();
					}
				});
			});
		},

		displayAttachments = function() {
			var html = $.map(attachments, function(attachment) {
				return (
					"
" +
					""+attachment.filename+"" +
					"
"
				)
			});
			$list.
				append(html).
				toggle()
			;
			message("" + attachments.length + " attachment" + (attachments.length == 1 ? "" : "s") + " found in this ticket.");
		},

		makeZip = function() {
			var zip = new JSZip();
			$.each(attachments, function(index, attachment) {
				zip.file(
					attachment.filename,
					urlToPromise(attachment.contentUrl),
					{
						binary:true
					}
				);
			});
			return zip;
		},

		downloadAttachments = function() {
			var first = true;
			return makeZip()
			.generateAsync({type:"blob"}, function updateCallback(metadata) {

				if (first) {
					$progress.show();
					first = false;
				}

				var percent = metadata.percent;
				$progress.percent = percent;
				status("Making ZIP: " + percent.toFixed(2) + "%");
			})
			.then(function (blob) {
				client.context().then(function(context) {
					var filename = "Zendesk-";
					filename += context.ticketId;
					filename += "-attachments-";
					filename += new Date().getTime();
					filename += ".zip";
					saveAs(blob, filename);
				})
			});
		},

		message = function(message) {
			display($message, message);
		},

		status = function(message) {
			display($status, message);
		},

		display = function($element, message) {
			$element.html(message);
		},

		show = function($element) {
			$element.show(600);
		},

		hide = function($element) {
			$element.hide(600);
		}
	;

	/**
	* Set the width of the colored part of the progress bar.
	*/
	Object.defineProperty($progress, "percent", {
		set: function(percent) {
			this
			.find("#progress-bar")
			.attr('aria-valuenow', percent)
			.width(percent+"%");
		}
	});

	// EVENT HANDLERS //

	client.on('app.registered', function appRegistered(event) {
		$progress.hide();
		findAttachments()
		.then(function() {
			attachments.sort(function(a,b) {
				return a.filename > b.filename ? 1 : a.filename < b.filename ? -1 : 0;
			});
			displayAttachments();
			$download.show();
		})
		.catch(function(err) {
			message(err);
			$download.hide();
		})
		.then(function() {
			$('#container').show();
		});
	});

	client.on('ticket.comments.changed', function(event) {
		console.log(event);
	});

	$download.on("click", function() {
		hide($message);
		$download.hide();
		show($status);
		status("Fetching attachments...");
		downloadAttachments().
		then(function() {
			status("ZIP done!");
			setTimeout(function() {
				hide($status);
				hide($progress);
				$progress.percent = 0;
				show($message);
				show($download);
			}, 2000);
		});
	});

	$message.on("click", function() {
		$list.slideToggle();
	});

	/**
	 * Fetch the content and return the associated promise.
	 * @param {String} url the url of the content to fetch.
	 * @return {Promise} the promise containing the data.
	 */
	function urlToPromise(url) {
		return new Promise(function(resolve, reject) {
			JSZipUtils.getBinaryContent(url, function (err, data) {
				if(err) {
					reject(err);
				} else {
					resolve(data);
				}
			});
		});
	}

});

<|endoftext|>


<commit_before>(function(){

  "use strict";

  // add startsWith method to String, if it does not already exist
  if (!String.prototype.startsWith){
    String.prototype.startsWith = function(s){
      return this.slice(0, s.length) == s;
    }
  }

  var d = document, inits = [];
  window.initializers = inits;

  // initializer to manipulate all hyperlinks
  inits.push(function(){
    var h = location.host, t;
    for (var xs = d.links, i = xs.length; i--; ){
      var x = xs[i], m = x.getAttribute('data-m');
      if (m) {
        // de-obfuscate e-mail hyperlink
        m = m.replace(/[\/]/g, '@').replace(/,/g, '.');
        x.href = 'mailto:' + m;
        if ((t = x.firstChild) && x.nodeType == 3){
          t.data = m;
        }
      } else if (x.host != h){
        // set target to _blank for all external hyperlinks
        x.target = '_blank';
      }
    }
  });

  // initializer to indicate current tab in page header
  inits.push(function(){
    if (d.querySelectorAll){
      var xs = d.querySelectorAll('body>header>nav>a'), href = location.href;
      for(var i = 1, n = xs.length; i < n; i++){
        var x = xs[i];
        if (href.startsWith(x.href)){
          x.classList.add('current');
        } else {
          x.classList.remove('current');
        }
      }
    }
  });

  // initializer to set current year in copyright footer
  inits.push(function(){
    if (d.querySelector){
      var x = d.querySelector('body>footer>time');
      if (x){
        x.innerHTML = (new Date()).getFullYear();
      }
    }
  });

  function initPage(){
    // call all initializers
    for(var i = 0, n = inits.length; i < n; i++){
      inits[i]();
    }
  }

  initPage();

}())<commit_msg>Test GIT on Visual Studio Code<commit_after>(function(){

  "use strict";

  // add startsWith method to String, if it does not already exist
  if (!String.prototype.startsWith){
    String.prototype.startsWith = function(s){
      return this.slice(0, s.length) == s;
    }
  }

  var d = document, inits = [];
  window.initializers = inits;

  // initializer to manipulate all hyperlinks
  inits.push(function(){
    var h = location.host, t;
    for (var xs = d.links, i = xs.length; i--; ){
      var x = xs[i], m = x.getAttribute('data-m');
      if (m) {
        // de-obfuscate e-mail hyperlink
        m = m.replace(/[\/]/g, '@').replace(/,/g, '.');
        x.href = 'mailto:' + m;
        if ((t = x.firstChild) && x.nodeType == 3){
          t.data = m;
        }
      } else if (x.host != h){
        // set target to _blank for all external hyperlinks
        x.target = '_blank';
      }
    }
  });

  // initializer to indicate current tab in page header
  inits.push(function(){
    if (d.querySelectorAll){
      var xs = d.querySelectorAll('body>header>nav>a'), href = location.href;
      for(var i = 1, n = xs.length; i < n; i++){
        var x = xs[i];
        if (href.startsWith(x.href)){
          x.classList.add('current');
        } else {
          x.classList.remove('current');
        }
      }
    }
  });

  // initializer to set current year in copyright footer
  inits.push(function(){
    if (d.querySelector){
      var x = d.querySelector('body>footer>time');
      if (x){
        x.innerHTML = (new Date()).getFullYear();
      }
    }
  });

  function initPage(){
    // call all initializers
    for(var i = 0, n = inits.length; i < n; i++){
      inits[i]();
    }
  }

  initPage();
}())<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var ws = require('ws');
var gameSrv = require('./services/gameSrv');
var gameCtrl = require('./controllers/gameCtrl');
var util = require('./services/util');
var wsCtrl = wsCtrl();

exports.connect = function (server) {
  var wss = new ws.Server({server: server});
  wss.on('connection', (ws) => {
    wsCtrl.welcomeNewPlayer(ws);
    wsCtrl.joinGame(ws);

    ws.on('message', function incoming(message) {
      wsCtrl.resolve(ws, message);
    });

    ws.on('close', () => {
      wsCtrl.shutdownGame(ws);
    });
  });
}

function wsCtrl() {
  var clients = [];

  function send(client, topic, data) {
    client.send(JSON.stringify({topic: topic, data: data}));
  }

  function broadcast(game, topic, data) {
    clients.filter((c) => {
      return game.p1.id === c.id || game.p2.id === c.id;
    }).forEach((c) => {
      send(c, topic, data);
    });
  }

  return {
    welcomeNewPlayer: function(ws) {
      ws.id = util.getRandom(10000, 90000); //TODO generate better id
      send(ws, 'your-id', ws.id);
      clients.push(ws);
    },

    joinGame: function(ws) {
      ws.game = gameCtrl.joinGame(ws.id);
      if(ws.game.p1 && ws.game.p2) {
        broadcast(ws.game, 'new-game', ws.game);
      }
    },

    shutdownGame: function(ws) {
      //TODO: properly communicate clients about game ending
      _.remove(clients, ws);
    },

    resolve: function(ws, strMessage) {
      var message = JSON.parse(strMessage);
      switch (message.topic) {
        case 'move':
          var player = ws.game.p1.id === ws.id ? 'p1' : 'p2';
          var clientMove = message.data;
          var move = gameCtrl.playerMove(ws.id, clientMove.move);
          var phago = gameCtrl.newPhago(ws.id);

          broadcast(ws.game, 'move', {player: player, move: move});
          broadcast(ws.game, 'new-phago', {player: player, phago: phago});
          broadcast(ws.game, 'remove-phago', clientMove.phagoId);

          break;
        case 'ping':
        default:
          break;
      }
    }
  }
}
<commit_msg>unnecessary dependency<commit_after>'use strict';

var _ = require('lodash');
var ws = require('ws');
var gameCtrl = require('./controllers/gameCtrl');
var util = require('./services/util');
var wsCtrl = wsCtrl();

exports.connect = function (server) {
  var wss = new ws.Server({server: server});
  wss.on('connection', (ws) => {
    wsCtrl.welcomeNewPlayer(ws);
    wsCtrl.joinGame(ws);

    ws.on('message', function incoming(message) {
      wsCtrl.resolve(ws, message);
    });

    ws.on('close', () => {
      wsCtrl.shutdownGame(ws);
    });
  });
}

function wsCtrl() {
  var clients = [];

  function send(client, topic, data) {
    client.send(JSON.stringify({topic: topic, data: data}));
  }

  function broadcast(game, topic, data) {
    clients.filter((c) => {
      return game.p1.id === c.id || game.p2.id === c.id;
    }).forEach((c) => {
      send(c, topic, data);
    });
  }

  return {
    welcomeNewPlayer: function(ws) {
      ws.id = util.getRandom(10000, 90000); //TODO generate better id
      send(ws, 'your-id', ws.id);
      clients.push(ws);
    },

    joinGame: function(ws) {
      ws.game = gameCtrl.joinGame(ws.id);
      if(ws.game.p1 && ws.game.p2) {
        broadcast(ws.game, 'new-game', ws.game);
      }
    },

    shutdownGame: function(ws) {
      //TODO: properly communicate clients about game ending
      _.remove(clients, ws);
    },

    resolve: function(ws, strMessage) {
      var message = JSON.parse(strMessage);
      switch (message.topic) {
        case 'move':
          var player = ws.game.p1.id === ws.id ? 'p1' : 'p2';
          var clientMove = message.data;
          var move = gameCtrl.playerMove(ws.id, clientMove.move);
          var phago = gameCtrl.newPhago(ws.id);

          broadcast(ws.game, 'move', {player: player, move: move});
          broadcast(ws.game, 'new-phago', {player: player, phago: phago});
          broadcast(ws.game, 'remove-phago', clientMove.phagoId);

          break;
        case 'ping':
        default:
          break;
      }
    }
  }
}
<|endoftext|>


<commit_before>"use strict";

var async = require.main.require('async');
const _ = require.main.require('lodash');

var meta = require.main.require('./src/meta');
var privileges = require.main.require('./src/privileges');
var posts = require.main.require('./src/posts');
var topics = require.main.require('./src/topics');
var plugins = require.main.require('./src/plugins');
var categories = require.main.require('./src/categories');
var user = require.main.require('./src/user');

var Sockets = module.exports;

Sockets.push = function(socket, pid, callback) {
	var postData;
	async.waterfall([
		function (next) {
			privileges.posts.can('topics:read', pid, socket.uid, next);
		},
		function (canRead, next) {
			if (!canRead) {
				return next(new Error('[[error:no-privileges]]'));
			}
			posts.getPostFields(pid, ['content', 'tid', 'uid', 'handle'], next);
		},
		function (_postData, next) {
			postData = _postData;
			if (!postData && !postData.content) {
				return next(new Error('[[error:invalid-pid]]'));
			}
			async.parallel({
				topic: function(next) {
					topics.getTopicDataByPid(pid, next);
				},
				tags: function(next) {
					topics.getTopicTags(postData.tid, next);
				},
				isMain: function(next) {
					posts.isMain(pid, next);
				}
			}, next);
		},
		function (results, next) {
			if (!results.topic) {
				return next(new Error('[[error:no-topic]]'));
			}
			plugins.fireHook('filter:composer.push', {
				pid: pid,
				uid: postData.uid,
				handle: parseInt(meta.config.allowGuestHandles, 10) ? postData.handle : undefined,
				body: postData.content,
				title: results.topic.title,
				thumb: results.topic.thumb,
				tags: results.tags,
				isMain: results.isMain
			}, next);
		},
	], callback);
};

Sockets.editCheck = function(socket, pid, callback) {
	posts.isMain(pid, function(err, isMain) {
		callback(err, {
			titleEditable: isMain
		});
	});
};

Sockets.renderPreview = function(socket, content, callback) {
	plugins.fireHook('filter:parse.raw', content, callback);
};

Sockets.renderHelp = function(socket, data, callback) {
	var helpText = meta.config['composer:customHelpText'] || '';

	if (meta.config['composer:showHelpTab'] === '0') {
		return callback(new Error('help-hidden'));
	}

	plugins.fireHook('filter:parse.raw', helpText, function(err, helpText) {
		if (!meta.config['composer:allowPluginHelp'] || meta.config['composer:allowPluginHelp'] === '1') {
			plugins.fireHook('filter:composer.help', helpText, callback);
		} else {
			callback(null, helpText);
		}
	});
};

Sockets.getFormattingOptions = function(socket, data, callback) {
	module.parent.exports.getFormattingOptions(callback);
};

Sockets.getCategoriesForSelect = async function (socket) {
	const cids = await categories.getAllCidsFromSet('categories:cid');
	let [allowed, categoriesData, isModerator, isAdmin] = await Promise.all([
		privileges.categories.isUserAllowedTo('topics:create', cids, socket.uid),
		categories.getCategoriesData(cids),
		user.isModerator(socket.uid, cids),
		user.isAdministrator(socket.uid),
	]);

	const filtered = await plugins.fireHook('filter:composer.getCategoriesForSelect', {
		allowed: allowed,
		categoriesData: categoriesData,
		isModerator: isModerator,
		isAdmin: isAdmin,
	});
	[allowed, categoriesData, isModerator, isAdmin] = [... Object.values(filtered)];

	categories.getTree(categoriesData);

	const cidToAllowed = _.zipObject(cids, allowed.map((allowed, i) => isAdmin || isModerator[i] || allowed));
	const cidToCategory = _.zipObject(cids, categoriesData);

	const visibleCategories = categoriesData.filter(function (c) {
		if (!c) {
			return false;
		}

		const hasPostableChildren = checkPostableChildren(c, cidToAllowed);
		const shouldBeRemoved = !hasPostableChildren && (!cidToAllowed[c.cid] || c.link || c.disabled);
		const shouldBeDisaplayedAsDisabled = hasPostableChildren && (!cidToAllowed[c.cid] || c.link || c.disabled);
		if (shouldBeDisaplayedAsDisabled) {
			c.disabledClass = true;
		}

		if (shouldBeRemoved && c.parent && c.parent.cid && cidToCategory[c.parent.cid]) {
			cidToCategory[c.parent.cid].children = cidToCategory[c.parent.cid].children.filter(child => child.cid !== c.cid);
		}

		return !shouldBeRemoved;
	});

	return categories.buildForSelectCategories(visibleCategories, ['disabledClass']);
};

function checkPostableChildren(category, cidToAllowed) {
	if (!Array.isArray(category.children) || !category.children.length) {
		return false;
	}
	return category.children.some(c => c && !c.disabled && (cidToAllowed[c.cid] || checkPostableChildren(c, cidToAllowed)));
}
<commit_msg>fix: pass uid into filter:composer.getCategoriesForSelect<commit_after>"use strict";

var async = require.main.require('async');
const _ = require.main.require('lodash');

var meta = require.main.require('./src/meta');
var privileges = require.main.require('./src/privileges');
var posts = require.main.require('./src/posts');
var topics = require.main.require('./src/topics');
var plugins = require.main.require('./src/plugins');
var categories = require.main.require('./src/categories');
var user = require.main.require('./src/user');

var Sockets = module.exports;

Sockets.push = function(socket, pid, callback) {
	var postData;
	async.waterfall([
		function (next) {
			privileges.posts.can('topics:read', pid, socket.uid, next);
		},
		function (canRead, next) {
			if (!canRead) {
				return next(new Error('[[error:no-privileges]]'));
			}
			posts.getPostFields(pid, ['content', 'tid', 'uid', 'handle'], next);
		},
		function (_postData, next) {
			postData = _postData;
			if (!postData && !postData.content) {
				return next(new Error('[[error:invalid-pid]]'));
			}
			async.parallel({
				topic: function(next) {
					topics.getTopicDataByPid(pid, next);
				},
				tags: function(next) {
					topics.getTopicTags(postData.tid, next);
				},
				isMain: function(next) {
					posts.isMain(pid, next);
				}
			}, next);
		},
		function (results, next) {
			if (!results.topic) {
				return next(new Error('[[error:no-topic]]'));
			}
			plugins.fireHook('filter:composer.push', {
				pid: pid,
				uid: postData.uid,
				handle: parseInt(meta.config.allowGuestHandles, 10) ? postData.handle : undefined,
				body: postData.content,
				title: results.topic.title,
				thumb: results.topic.thumb,
				tags: results.tags,
				isMain: results.isMain
			}, next);
		},
	], callback);
};

Sockets.editCheck = function(socket, pid, callback) {
	posts.isMain(pid, function(err, isMain) {
		callback(err, {
			titleEditable: isMain
		});
	});
};

Sockets.renderPreview = function(socket, content, callback) {
	plugins.fireHook('filter:parse.raw', content, callback);
};

Sockets.renderHelp = function(socket, data, callback) {
	var helpText = meta.config['composer:customHelpText'] || '';

	if (meta.config['composer:showHelpTab'] === '0') {
		return callback(new Error('help-hidden'));
	}

	plugins.fireHook('filter:parse.raw', helpText, function(err, helpText) {
		if (!meta.config['composer:allowPluginHelp'] || meta.config['composer:allowPluginHelp'] === '1') {
			plugins.fireHook('filter:composer.help', helpText, callback);
		} else {
			callback(null, helpText);
		}
	});
};

Sockets.getFormattingOptions = function(socket, data, callback) {
	module.parent.exports.getFormattingOptions(callback);
};

Sockets.getCategoriesForSelect = async function (socket) {
	const cids = await categories.getAllCidsFromSet('categories:cid');
	let [allowed, categoriesData, isModerator, isAdmin] = await Promise.all([
		privileges.categories.isUserAllowedTo('topics:create', cids, socket.uid),
		categories.getCategoriesData(cids),
		user.isModerator(socket.uid, cids),
		user.isAdministrator(socket.uid),
	]);

	const filtered = await plugins.fireHook('filter:composer.getCategoriesForSelect', {
		uid: socket.uid,
		allowed: allowed,
		categoriesData: categoriesData,
		isModerator: isModerator,
		isAdmin: isAdmin,
	});
	({ allowed, categoriesData, isModerator, isAdmin } = filtered);

	categories.getTree(categoriesData);

	const cidToAllowed = _.zipObject(cids, allowed.map((allowed, i) => isAdmin || isModerator[i] || allowed));
	const cidToCategory = _.zipObject(cids, categoriesData);

	const visibleCategories = categoriesData.filter(function (c) {
		if (!c) {
			return false;
		}

		const hasPostableChildren = checkPostableChildren(c, cidToAllowed);
		const shouldBeRemoved = !hasPostableChildren && (!cidToAllowed[c.cid] || c.link || c.disabled);
		const shouldBeDisaplayedAsDisabled = hasPostableChildren && (!cidToAllowed[c.cid] || c.link || c.disabled);
		if (shouldBeDisaplayedAsDisabled) {
			c.disabledClass = true;
		}

		if (shouldBeRemoved && c.parent && c.parent.cid && cidToCategory[c.parent.cid]) {
			cidToCategory[c.parent.cid].children = cidToCategory[c.parent.cid].children.filter(child => child.cid !== c.cid);
		}

		return !shouldBeRemoved;
	});

	return categories.buildForSelectCategories(visibleCategories, ['disabledClass']);
};

function checkPostableChildren(category, cidToAllowed) {
	if (!Array.isArray(category.children) || !category.children.length) {
		return false;
	}
	return category.children.some(c => c && !c.disabled && (cidToAllowed[c.cid] || checkPostableChildren(c, cidToAllowed)));
}
<|endoftext|>