<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import { StaticQuery, graphql } from 'gatsby'

import {
    LandingHeader,
    LandingTitle,
    LandingSubtitle,
    LandingGrid,
    LandingCard,
    LandingCol,
    LandingDemo,
    LandingBannerGrid,
    LandingBanner,
} from '../components/landing'
import { H2 } from '../components/typography'
import { InlineCode } from '../components/code'
import Button from '../components/button'
import Link from '../components/link'

import QuickstartTraining from './quickstart-training'
import Project from './project'
import Features from './features'
import courseImage from '../../docs/images/course.jpg'
import prodigyImage from '../../docs/images/prodigy_overview.jpg'
import projectsImage from '../../docs/images/projects.png'
import irlBackground from '../images/spacy-irl.jpg'

import Benchmarks from 'usage/_benchmarks-models.md'

function getCodeExample(nightly) {
    return `# pip install -U ${nightly ? 'spacy-nightly --pre' : 'spacy'}
# python -m spacy download en_core_web_sm
import spacy

# Load English tokenizer, tagger, parser and NER
nlp = spacy.load("en_core_web_sm")

# Process whole documents
text = ("When Sebastian Thrun started working on self-driving cars at "
        "Google in 2007, few people outside of the company took him "
        "seriously. “I can tell you very senior CEOs of major American "
        "car companies would shake my hand and turn away because I wasn’t "
        "worth talking to,” said Thrun, in an interview with Recode earlier "
        "this week.")
doc = nlp(text)

# Analyze syntax
print("Noun phrases:", [chunk.text for chunk in doc.noun_chunks])
print("Verbs:", [token.lemma_ for token in doc if token.pos_ == "VERB"])

# Find named entities, phrases and concepts
for entity in doc.ents:
    print(entity.text, entity.label_)
`
}

const Landing = ({ data }) => {
    const { nightly } = data
    const codeExample = getCodeExample(nightly)
    return (
        <>
            <LandingHeader nightly={data.nightly}>
                <LandingTitle>
                    Industrial-Strength

                    Natural Language

                    Processing
                </LandingTitle>
                <LandingSubtitle>in Python</LandingSubtitle>
            </LandingHeader>
            <LandingGrid blocks>
                <LandingCard title="Get things done" url="/usage/spacy-101" button="Get started">
                    spaCy is designed to help you do real work — to build real products, or gather
                    real insights. The library respects your time, and tries to avoid wasting it.
                    It's easy to install, and its API is simple and productive.
                </LandingCard>
                <LandingCard
                    title="Blazing fast"
                    url="/usage/facts-figures"
                    button="Facts & Figures"
                >
                    spaCy excels at large-scale information extraction tasks. It's written from the
                    ground up in carefully memory-managed Cython. If your application needs to
                    process entire web dumps, spaCy is the library you want to be using.
                </LandingCard>

                <LandingCard title="Awesome ecosystem" url="/usage/projects" button="Read more">
                    In the five years since its release, spaCy has become an industry standard with
                    a huge ecosystem. Choose from a variety of plugins, integrate with your machine
                    learning stack and build custom components and workflows.
                </LandingCard>
            </LandingGrid>

            <LandingGrid>
                <LandingDemo title="Edit the code & try spaCy">{codeExample}</LandingDemo>

                <LandingCol>

Features

                    <Features />
                </LandingCol>
            </LandingGrid>

            <LandingBannerGrid>
                <LandingBanner
                    label="New in v3.0"
                    title="Transformer-based pipelines, new training system, project templates & more"
                    to="/usage/v3"
                    button="See what's new"
                    small
                >
                    spaCy v3.0 features all new transformer-based pipelines that
                    bring spaCy's accuracy right up to the current state-of-the-art
                    . You can use any pretrained transformer to train your own pipelines, and even
                    share one transformer between multiple components with{' '}
                    multi-task learning. Training is now fully configurable and
                    extensible, and you can define your own custom models using{' '}
                    PyTorch, TensorFlow and other frameworks. The
                    new spaCy projects system lets you describe whole{' '}
                    end-to-end workflows in a single file, giving you an easy path
                    from prototype to production, and making it easy to clone and adapt
                    best-practice projects for your own use cases.
                </LandingBanner>

                <LandingBanner
                    title="Prodigy: Radically efficient machine teaching"
                    label="From the makers of spaCy"
                    to="https://prodi.gy"
                    button="Try it out"
                    background="#f6f6f6"
                    color="#000"
                    small
                >
                    <Link to="https://prodi.gy" hidden>
                        {/** Update image */}

                    </Link>

                    Prodigy is an annotation tool so efficient that data scientists
                    can do the annotation themselves, enabling a new level of rapid iteration.
                    Whether you're working on entity recognition, intent detection or image
                    classification, Prodigy can help you train and evaluate your
                    models faster.
                </LandingBanner>
            </LandingBannerGrid>

            <LandingGrid cols={2} style={{ gridTemplateColumns: '1fr calc(80ch + 14rem)' }}>
                <LandingCol>

Reproducible training for custom pipelines

                        spaCy v3.0 introduces a comprehensive and extensible system for{' '}
                        configuring your training runs. Your configuration file
                        will describe every detail of your training run, with no hidden defaults,
                        making it easy to rerun your experiments and track changes.
                        You can use the quickstart widget or the{' '}
                        <Link to="/api/cli#init-config">
                            <InlineCode>init config</InlineCode>
                        </Link>{' '}
                        command to get started, or clone a project template for an end-to-end
                        workflow.

                        <Button to="/usage/training">Get started</Button>

                </LandingCol>
                <LandingCol>
                    <QuickstartTraining />
                </LandingCol>
            </LandingGrid>

            <LandingGrid cols={2}>
                <LandingCol>
                    <Link to="/usage/projects" hidden>

                    </Link>

                    <Project id="pipelines/tagger_parser_ud" title="Get started">
                        The easiest way to get started is to clone a project template and run it
                        – for example, this template for training a{' '}
                        part-of-speech tagger and{' '}
                        dependency parser on a Universal Dependencies treebank.
                    </Project>
                </LandingCol>
                <LandingCol>

End-to-end workflows from prototype to production

                        spaCy's new project system gives you a smooth path from prototype to
                        production. It lets you keep track of all those{' '}
                        data transformation, preprocessing and{' '}
                        training steps, so you can make sure your project is always
                        ready to hand over for automation. It features source asset download,
                        command execution, checksum verification, and caching with a variety of
                        backends and integrations.

                        <Button to="/usage/projects">Try it out</Button>

                </LandingCol>
            </LandingGrid>

            <LandingBannerGrid>
                <LandingBanner
                    title="spaCy v3.0 nightly: Transformer-based pipelines, new training system, project templates & more"
                    label="Try the pre-release"
                    to="https://nightly.spacy.io"
                    button="See what's new"
                    background="#8758fe"
                    color="#ffffff"
                    small
                >
                    spaCy v3.0 features all new transformer-based pipelines that
                    bring spaCy's accuracy right up to the current state-of-the-art
                    . You can use any pretrained transformer to train your own pipelines, and even
                    share one transformer between multiple components with{' '}
                    multi-task learning. Training is now fully configurable and
                    extensible, and you can define your own custom models using{' '}
                    PyTorch, TensorFlow and other frameworks. The
                    new spaCy projects system lets you describe whole{' '}
                    end-to-end workflows in a single file, giving you an easy path
                    from prototype to production, and making it easy to clone and adapt
                    best-practice projects for your own use cases.
                </LandingBanner>

                <LandingBanner
                    title="Prodigy: Radically efficient machine teaching"
                    label="From the makers of spaCy"
                    to="https://prodi.gy"
                    button="Try it out"
                    background="#f6f6f6"
                    color="#000"
                    small
                >
                    <Link to="https://prodi.gy" hidden>

                    </Link>

                    Prodigy is an annotation tool so efficient that data scientists
                    can do the annotation themselves, enabling a new level of rapid iteration.
                    Whether you're working on entity recognition, intent detection or image
                    classification, Prodigy can help you train and evaluate your
                    models faster.
                </LandingBanner>
            </LandingBannerGrid>

            <LandingLogos title="spaCy is trusted by" logos={data.logosUsers}>
                <Button to={`https://github.com/${data.repo}/stargazers`}>and many more</Button>
            </LandingLogos>
            <LandingLogos title="Featured on" logos={data.logosPublications} />

            <LandingBannerGrid>
                <LandingBanner
                    to="https://course.spacy.io"
                    button="Start the course"
                    background="#f6f6f6"
                    color="#252a33"
                    small
                >
                    <Link to="https://course.spacy.io" hidden>

                    </Link>

                    In this free and interactive online course you’ll learn how to
                    use spaCy to build advanced natural language understanding systems, using both
                    rule-based and machine learning approaches. It includes{' '}
                    55 exercises featuring videos, slide decks, multiple-choice
                    questions and interactive coding practice in the browser.
                </LandingBanner>
                <LandingBanner
                    title="spaCy IRL: Two days of NLP"
                    label="Watch the videos"
                    to="https://www.youtube.com/playlist?list=PLBmcuObd5An4UC6jvK_-eSl6jCvP1gwXc"
                    button="Watch the videos"
                    background="#ffc194"
                    backgroundImage={irlBackground}
                    color="#1a1e23"
                    small
                >
                    We were pleased to invite the spaCy community and other folks working on NLP to
                    Berlin for a small and intimate event. We booked a beautiful venue, hand-picked
                    an awesome lineup of speakers and scheduled plenty of social time to get to know
                    each other. The YouTube playlist includes 12 talks about NLP research,
                    development and applications, with keynotes by Sebastian Ruder (DeepMind) and
                    Yoav Goldberg (Allen AI).
                </LandingBanner>
            </LandingBannerGrid>

            <LandingGrid cols={2} style={{ gridTemplateColumns: '1fr 60%' }}>
                <LandingCol>

Benchmarks

                        spaCy v3.0 introduces transformer-based pipelines that bring spaCy's
                        accuracy right up to the current state-of-the-art. You can
                        also use a CPU-optimized pipeline, which is less accurate but much cheaper
                        to run.

                        <Button to="/usage/facts-figures#benchmarks">More results</Button>

                </LandingCol>

                <LandingCol>
                    <Benchmarks />
                </LandingCol>
            </LandingGrid>
        </>
    )
}

Landing.propTypes = {
    data: PropTypes.shape({
        repo: PropTypes.string,
        languages: PropTypes.arrayOf(
            PropTypes.shape({
                models: PropTypes.arrayOf(PropTypes.string),
            })
        ),
    }),
}

export default () => (
    <StaticQuery query={landingQuery} render={({ site }) => <Landing data={site.siteMetadata} />} />
)

const landingQuery = graphql`
    query LandingQuery {
        site {
            siteMetadata {
                nightly
                repo
            }
        }
    }
`
<commit_msg>Revert landing [ci skip]<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import { StaticQuery, graphql } from 'gatsby'

import {
    LandingHeader,
    LandingTitle,
    LandingSubtitle,
    LandingGrid,
    LandingCard,
    LandingCol,
    LandingDemo,
    LandingBannerGrid,
    LandingBanner,
} from '../components/landing'
import { H2 } from '../components/typography'
import { InlineCode } from '../components/code'
import Button from '../components/button'
import Link from '../components/link'

import QuickstartTraining from './quickstart-training'
import Project from './project'
import Features from './features'
import courseImage from '../../docs/images/course.jpg'
import prodigyImage from '../../docs/images/prodigy_overview.jpg'
import projectsImage from '../../docs/images/projects.png'
import irlBackground from '../images/spacy-irl.jpg'

import Benchmarks from 'usage/_benchmarks-models.md'

function getCodeExample(nightly) {
    return `# pip install -U ${nightly ? 'spacy-nightly --pre' : 'spacy'}
# python -m spacy download en_core_web_sm
import spacy

# Load English tokenizer, tagger, parser and NER
nlp = spacy.load("en_core_web_sm")

# Process whole documents
text = ("When Sebastian Thrun started working on self-driving cars at "
        "Google in 2007, few people outside of the company took him "
        "seriously. “I can tell you very senior CEOs of major American "
        "car companies would shake my hand and turn away because I wasn’t "
        "worth talking to,” said Thrun, in an interview with Recode earlier "
        "this week.")
doc = nlp(text)

# Analyze syntax
print("Noun phrases:", [chunk.text for chunk in doc.noun_chunks])
print("Verbs:", [token.lemma_ for token in doc if token.pos_ == "VERB"])

# Find named entities, phrases and concepts
for entity in doc.ents:
    print(entity.text, entity.label_)
`
}

const Landing = ({ data }) => {
    const { nightly } = data
    const codeExample = getCodeExample(nightly)
    return (
        <>
            <LandingHeader nightly={data.nightly}>
                <LandingTitle>
                    Industrial-Strength

                    Natural Language

                    Processing
                </LandingTitle>
                <LandingSubtitle>in Python</LandingSubtitle>
            </LandingHeader>
            <LandingGrid blocks>
                <LandingCard title="Get things done" url="/usage/spacy-101" button="Get started">
                    spaCy is designed to help you do real work — to build real products, or gather
                    real insights. The library respects your time, and tries to avoid wasting it.
                    It's easy to install, and its API is simple and productive.
                </LandingCard>
                <LandingCard
                    title="Blazing fast"
                    url="/usage/facts-figures"
                    button="Facts & Figures"
                >
                    spaCy excels at large-scale information extraction tasks. It's written from the
                    ground up in carefully memory-managed Cython. If your application needs to
                    process entire web dumps, spaCy is the library you want to be using.
                </LandingCard>

                <LandingCard title="Awesome ecosystem" url="/usage/projects" button="Read more">
                    In the five years since its release, spaCy has become an industry standard with
                    a huge ecosystem. Choose from a variety of plugins, integrate with your machine
                    learning stack and build custom components and workflows.
                </LandingCard>
            </LandingGrid>

            <LandingGrid>
                <LandingDemo title="Edit the code & try spaCy">{codeExample}</LandingDemo>

                <LandingCol>

Features

                    <Features />
                </LandingCol>
            </LandingGrid>

            <LandingBannerGrid>
                <LandingBanner
                    label="New in v3.0"
                    title="Transformer-based pipelines, new training system, project templates & more"
                    to="/usage/v3"
                    button="See what's new"
                    small
                >
                    spaCy v3.0 features all new transformer-based pipelines that
                    bring spaCy's accuracy right up to the current state-of-the-art
                    . You can use any pretrained transformer to train your own pipelines, and even
                    share one transformer between multiple components with{' '}
                    multi-task learning. Training is now fully configurable and
                    extensible, and you can define your own custom models using{' '}
                    PyTorch, TensorFlow and other frameworks. The
                    new spaCy projects system lets you describe whole{' '}
                    end-to-end workflows in a single file, giving you an easy path
                    from prototype to production, and making it easy to clone and adapt
                    best-practice projects for your own use cases.
                </LandingBanner>

                <LandingBanner
                    title="Prodigy: Radically efficient machine teaching"
                    label="From the makers of spaCy"
                    to="https://prodi.gy"
                    button="Try it out"
                    background="#f6f6f6"
                    color="#000"
                    small
                >
                    <Link to="https://prodi.gy" hidden>
                        {/** Update image */}

                    </Link>

                    Prodigy is an annotation tool so efficient that data scientists
                    can do the annotation themselves, enabling a new level of rapid iteration.
                    Whether you're working on entity recognition, intent detection or image
                    classification, Prodigy can help you train and evaluate your
                    models faster.
                </LandingBanner>
            </LandingBannerGrid>

            <LandingGrid cols={2} style={{ gridTemplateColumns: '1fr calc(80ch + 14rem)' }}>
                <LandingCol>

Reproducible training for custom pipelines

                        spaCy v3.0 introduces a comprehensive and extensible system for{' '}
                        configuring your training runs. Your configuration file
                        will describe every detail of your training run, with no hidden defaults,
                        making it easy to rerun your experiments and track changes.
                        You can use the quickstart widget or the{' '}
                        <Link to="/api/cli#init-config">
                            <InlineCode>init config</InlineCode>
                        </Link>{' '}
                        command to get started, or clone a project template for an end-to-end
                        workflow.

                        <Button to="/usage/training">Get started</Button>

                </LandingCol>
                <LandingCol>
                    <QuickstartTraining />
                </LandingCol>
            </LandingGrid>

            <LandingGrid cols={2}>
                <LandingCol>
                    <Link to="/usage/projects" hidden>

                    </Link>

                    <Project id="pipelines/tagger_parser_ud" title="Get started">
                        The easiest way to get started is to clone a project template and run it
                        – for example, this template for training a{' '}
                        part-of-speech tagger and{' '}
                        dependency parser on a Universal Dependencies treebank.
                    </Project>
                </LandingCol>
                <LandingCol>

End-to-end workflows from prototype to production

                        spaCy's new project system gives you a smooth path from prototype to
                        production. It lets you keep track of all those{' '}
                        data transformation, preprocessing and{' '}
                        training steps, so you can make sure your project is always
                        ready to hand over for automation. It features source asset download,
                        command execution, checksum verification, and caching with a variety of
                        backends and integrations.

                        <Button to="/usage/projects">Try it out</Button>

                </LandingCol>
            </LandingGrid>

            <LandingBannerGrid>
                <LandingBanner
                    title="spaCy v3.0 nightly: Transformer-based pipelines, new training system, project templates & more"
                    label="Try the pre-release"
                    to="https://nightly.spacy.io"
                    button="See what's new"
                    background="#8758fe"
                    color="#ffffff"
                    small
                >
                    spaCy v3.0 features all new transformer-based pipelines that
                    bring spaCy's accuracy right up to the current state-of-the-art
                    . You can use any pretrained transformer to train your own pipelines, and even
                    share one transformer between multiple components with{' '}
                    multi-task learning. Training is now fully configurable and
                    extensible, and you can define your own custom models using{' '}
                    PyTorch, TensorFlow and other frameworks. The
                    new spaCy projects system lets you describe whole{' '}
                    end-to-end workflows in a single file, giving you an easy path
                    from prototype to production, and making it easy to clone and adapt
                    best-practice projects for your own use cases.
                </LandingBanner>

                <LandingBanner
                    title="Prodigy: Radically efficient machine teaching"
                    label="From the makers of spaCy"
                    to="https://prodi.gy"
                    button="Try it out"
                    background="#f6f6f6"
                    color="#000"
                    small
                >
                    <Link to="https://prodi.gy" hidden>

                    </Link>

                    Prodigy is an annotation tool so efficient that data scientists
                    can do the annotation themselves, enabling a new level of rapid iteration.
                    Whether you're working on entity recognition, intent detection or image
                    classification, Prodigy can help you train and evaluate your
                    models faster.
                </LandingBanner>
            </LandingBannerGrid>

            <LandingGrid cols={2} style={{ gridTemplateColumns: '1fr 60%' }}>
                <LandingCol>

Benchmarks

                        spaCy v3.0 introduces transformer-based pipelines that bring spaCy's
                        accuracy right up to the current state-of-the-art. You can
                        also use a CPU-optimized pipeline, which is less accurate but much cheaper
                        to run.

                        <Button to="/usage/facts-figures#benchmarks">More results</Button>

                </LandingCol>

                <LandingCol>
                    <Benchmarks />
                </LandingCol>
            </LandingGrid>
        </>
    )
}

Landing.propTypes = {
    data: PropTypes.shape({
        repo: PropTypes.string,
        languages: PropTypes.arrayOf(
            PropTypes.shape({
                models: PropTypes.arrayOf(PropTypes.string),
            })
        ),
    }),
}

export default () => (
    <StaticQuery query={landingQuery} render={({ site }) => <Landing data={site.siteMetadata} />} />
)

const landingQuery = graphql`
    query LandingQuery {
        site {
            siteMetadata {
                nightly
                repo
            }
        }
    }
`
<|endoftext|>


<commit_before>import Ember from 'ember';
import config from 'ember-get-config';

var getProvidersPayload = '{"from": 0,"query": {"bool": {"must": {"query_string": {"query": "*"}}, "filter": [{"term": {"type.raw": "preprint"}}]}},"aggregations": {"sources": {"terms": {"field": "sources.raw","size": 200}}}}';

var filterMap = {
    providers: 'sources.raw',
    subjects: 'subjects.raw'
};

// Regex for checking url from osf repo website/static/js/profile.js
var urlRule = '^(https?:\\/\\/)?' + // protocol
           '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
           '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
           '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
           '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
           '(\\#[-a-z\\d_]*)?$';

function isHyperLink(link) {
    var urlexp = new RegExp(urlRule, 'i');
    return urlexp.test(link);
}

export default Ember.Controller.extend({
    // TODO: either remove or add functionality to info icon on "Refine your search panel"

    // Many pieces taken from: https://github.com/CenterForOpenScience/ember-share/blob/develop/app/controllers/discover.js
    queryParams: {
        page: 'page',
        queryString: 'q',
        subjectFilter: 'subject',
        providerFilter: 'provider',
    },

    activeFilters: { providers: [], subjects: [] },
    osfProviders: ['OSF', 'PsyArXiv', 'SocArXiv', 'engrXiv'],

    whiteListedProviders: ['OSF', 'arXiv', 'bioRxiv', 'Cogprints', 'engrXiv', 'PeerJ', 'PsyArXiv', 'Research Papers in Economics', 'SocArXiv'],
    page: 1,
    size: 10,
    numberOfResults: 0,
    queryString: '',
    subjectFilter: null,
    queryBody: {},
    providersPassed: false,

    sortByOptions: ['Relevance', 'Upload date (oldest to newest)', 'Upload date (newest to oldest)'],

    treeSubjects: Ember.computed('activeFilters', function() {
        return this.get('activeFilters.subjects').slice();
    }),
    // chosenOption is always the first element in the list
    chosenSortByOption: Ember.computed('sortByOptions', function() {
        return this.get('sortByOptions')[0];
    }),

    showActiveFilters: true, //should always have a provider, don't want to mix osfProviders and non-osf
    showPrev: Ember.computed.gt('page', 1),
    showNext: Ember.computed('page', 'size', 'numberOfResults', function() {
        return this.get('page') * this.get('size') < this.get('numberOfResults');
    }),

    results: Ember.ArrayProxy.create({ content: [] }),

    searchUrl: config.SHARE.searchUrl,

    init() {
        var _this = this;
        this._super(...arguments);
        this.set('facetFilters', Ember.Object.create());
        Ember.$.ajax({
            type: 'POST',
            url: this.get('searchUrl'),
            data: getProvidersPayload,
            contentType: 'application/json',
            crossDomain: true,
        }).then(function(results) {
            var hits = results.aggregations.sources.buckets;
            var providers = [];
            var whiteList = _this.get('whiteListedProviders');
            hits.map(function(each) {
                each = each.key;
                console.log(each);
                if (whiteList.indexOf(each) !== -1) {
                    providers.push(each);
                }
            });
            _this.get('osfProviders').slice().map(function(each) {
                if (providers.indexOf(each) === -1) {
                    providers.push(each);
                }
            });
            providers.splice(providers.indexOf('OSF'), 1);
            providers.sort((a, b) => a.toLowerCase() < b.toLowerCase() ? -1 : 1);
            providers.unshift('OSF');
            _this.set('otherProviders', providers);
            _this.notifyPropertyChange('otherProviders');
        });
        this.loadPage();
    },
    subjectChanged: Ember.observer('subjectFilter', function() {
        Ember.run.once(() => {
            let filter = this.get('subjectFilter');
            if (!filter) return;
            this.set('activeFilters.subjects', filter.split('AND'));
            this.notifyPropertyChange('activeFilters');
            this.loadPage();
        });
    }),
    providerChanged: Ember.observer('providerFilter', function() {
        Ember.run.once(() => {
            let filter = this.get('providerFilter');
            if (!filter) return;
            this.set('activeFilters.providers', filter.split('AND'));
            this.notifyPropertyChange('activeFilters');
            this.set('providersPassed', true);
            this.loadPage();
        });
    }),
    loadPage() {
        this.set('loading', true);
        Ember.run.debounce(this, this._loadPage, 500);
    },
    _loadPage() {
        let queryBody = JSON.stringify(this.getQueryBody());

        return Ember.$.ajax({
            url: this.get('searchUrl'),
            crossDomain: true,
            type: 'POST',
            contentType: 'application/json',
            data: queryBody
        }).then(json => {
            if (this.isDestroyed || this.isDestroying) return;

            this.set('numberOfResults', json.hits.total);

            let results = json.hits.hits.map(hit => {
                // HACK: Make share data look like apiv2 preprints data
                let result = Ember.merge(hit._source, {
                    id: hit._id,
                    type: 'elastic-search-result',
                    workType: hit._source['@type'],
                    abstract: hit._source.description,
                    subjects: hit._source.subjects.map(each => ({text: each})),
                    providers: hit._source.sources.map(item => ({name: item})),
                    osfProvider: hit._source.sources.reduce((acc, source) => (acc || this.get('osfProviders').indexOf(source) !== -1), false),
                    hyperLinks: [// Links that are hyperlinks from hit._source.lists.links
                        {
                            type: 'share',
                            url: config.SHARE.baseUrl + 'curate/preprint/' + hit._id
                        }
                    ],
                    infoLinks: [] // Links that are not hyperlinks  hit._source.lists.links
                });

                hit._source.lists.links.forEach(function(linkItem) {
                    if (isHyperLink(linkItem.url)) {
                        result.hyperLinks.push(linkItem);
                    } else {
                        result.infoLinks.push(linkItem);
                    }
                });

                result.contributors = result.lists.contributors.map(contributor => ({
                    users: Object.keys(contributor).reduce((acc, key) => Ember.merge(acc, {[key.camelize()]: contributor[key]}), {})
                }));

                // Temporary fix to handle half way migrated SHARE ES
                // Only false will result in a false here.
                result.contributors.map(contributor => contributor.users.bibliographic = !(contributor.users.bibliographic === false));  // jshint ignore:line

                return result;
            });

            this.set('loading', false);
            return this.set('results', results);
        });
    },
    maxPages: Ember.computed('numberOfResults', function() {
        return ((this.get('numberOfResults') / this.get('size')) | 0) + (this.get('numberOfResults') % 10 === 0 ? 0 : 1);
    }),
    getQueryBody() {
        let facetFilters = this.get('activeFilters');
        this.set('subjectFilter', facetFilters.subjects.slice().join('AND'));
        this.set('providerFilter', facetFilters.providers.slice().join('AND'));
        let filters = {};
        for (let k of Object.keys(facetFilters)) {
            let key = filterMap[k];
            if (key && facetFilters[k].length) {
                filters[key] = facetFilters[k];
            }
        }
        let query = {
            query_string: {
                query: this.get('queryString') || '*'
            }
        };

        let filters_ = [];
        for (let k of Object.keys(filters)) {
            let terms = {};
            terms[k] = filters[k];
            filters_.push({
                terms: terms
            });
        }
        filters_.push({
            terms: {'type.raw': ['preprint']}
        });
        query = {
            bool: {
                must: query,
                filter: filters_
            }
        };

        let queryBody = {
            query,
            from: (this.get('page') - 1) * this.get('size'),
        };

        let sortByOption = this.get('chosenSortByOption');
        if (sortByOption === 'Upload date (oldest to newest)') {
            queryBody.sort = {};
            queryBody.sort.date_updated = 'asc';
        } else if (sortByOption === 'Upload date (newest to oldest)') {
            queryBody.sort = {};
            queryBody.sort.date_updated = 'desc';
        }

        return this.set('queryBody', queryBody);
    },

    reloadSearch: Ember.observer('activeFilters', function() {
        this.set('page', 1);
        this.loadPage();
    }),
    otherProviders: [],
    actions: {
        search(val, event) {
            if (event && (event.keyCode < 49 || [91, 92, 93].indexOf(event.keyCode) !== -1) && [8, 32, 48].indexOf(event.keyCode) === -1) return;

            this.set('page', 1);
            this.loadPage();
        },

        previous() {
            if (this.get('page') > 1) {
                this.decrementProperty('page');
                this.loadPage();
            }
        },

        next() {
            if (this.get('page') * this.get('size') <= this.get('numberOfResults')) {
                this.incrementProperty('page');
                this.loadPage();
            }
        },

        clearFilters() {
            this.set('activeFilters',  { providers: [], subjects: [] });
        },

        sortBySelect(index) {
            // Selecting an option just swaps it with whichever option is first
            let copy = this.get('sortByOptions').slice(0);
            let temp = copy[0];
            copy[0] = copy[index];
            copy[index] = temp;
            this.set('sortByOptions', copy);
            this.set('page', 1);
            this.loadPage();
        },

        selectSubjectFilter(subject) {
            if (typeof subject === 'object') {
                subject = subject.text;
            }
            if (this.get('activeFilters.subjects').indexOf(subject) === -1) {
                this.get('activeFilters.subjects').pushObject(subject);
            } else {
                this.get('activeFilters.subjects').removeObject(subject);
            }
            this.notifyPropertyChange('activeFilters');
        },

        selectProvider(provider) {
            let currentProviders = this.get('activeFilters.providers').slice();
            if (currentProviders.indexOf(provider) !== -1) {
                this.get('activeFilters.providers').removeObject(provider);
            } else {
                this.get('activeFilters.providers').pushObject(provider);
            }
            this.notifyPropertyChange('activeFilters');
        },
    },
});
<commit_msg>clean up hits iteration logic<commit_after>import Ember from 'ember';
import config from 'ember-get-config';

var getProvidersPayload = '{"from": 0,"query": {"bool": {"must": {"query_string": {"query": "*"}}, "filter": [{"term": {"type.raw": "preprint"}}]}},"aggregations": {"sources": {"terms": {"field": "sources.raw","size": 200}}}}';

var filterMap = {
    providers: 'sources.raw',
    subjects: 'subjects.raw'
};

// Regex for checking url from osf repo website/static/js/profile.js
var urlRule = '^(https?:\\/\\/)?' + // protocol
           '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // domain name
           '((\\d{1,3}\\.){3}\\d{1,3}))' + // OR ip (v4) address
           '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // port and path
           '(\\?[;&a-z\\d%_.~+=-]*)?' + // query string
           '(\\#[-a-z\\d_]*)?$';

function isHyperLink(link) {
    var urlexp = new RegExp(urlRule, 'i');
    return urlexp.test(link);
}

export default Ember.Controller.extend({
    // TODO: either remove or add functionality to info icon on "Refine your search panel"

    // Many pieces taken from: https://github.com/CenterForOpenScience/ember-share/blob/develop/app/controllers/discover.js
    queryParams: {
        page: 'page',
        queryString: 'q',
        subjectFilter: 'subject',
        providerFilter: 'provider',
    },

    activeFilters: { providers: [], subjects: [] },
    osfProviders: ['OSF', 'PsyArXiv', 'SocArXiv', 'engrXiv'],

    whiteListedProviders: ['OSF', 'arXiv', 'bioRxiv', 'Cogprints', 'engrXiv', 'PeerJ', 'PsyArXiv', 'Research Papers in Economics', 'SocArXiv'],
    page: 1,
    size: 10,
    numberOfResults: 0,
    queryString: '',
    subjectFilter: null,
    queryBody: {},
    providersPassed: false,

    sortByOptions: ['Relevance', 'Upload date (oldest to newest)', 'Upload date (newest to oldest)'],

    treeSubjects: Ember.computed('activeFilters', function() {
        return this.get('activeFilters.subjects').slice();
    }),
    // chosenOption is always the first element in the list
    chosenSortByOption: Ember.computed('sortByOptions', function() {
        return this.get('sortByOptions')[0];
    }),

    showActiveFilters: true, //should always have a provider, don't want to mix osfProviders and non-osf
    showPrev: Ember.computed.gt('page', 1),
    showNext: Ember.computed('page', 'size', 'numberOfResults', function() {
        return this.get('page') * this.get('size') < this.get('numberOfResults');
    }),

    results: Ember.ArrayProxy.create({ content: [] }),

    searchUrl: config.SHARE.searchUrl,

    init() {
        var _this = this;
        this._super(...arguments);
        this.set('facetFilters', Ember.Object.create());
        Ember.$.ajax({
            type: 'POST',
            url: this.get('searchUrl'),
            data: getProvidersPayload,
            contentType: 'application/json',
            crossDomain: true,
        }).then(function(results) {
            var hits = results.aggregations.sources.buckets;
            var providers = [];
            var whiteList = _this.get('whiteListedProviders');
            var providers = hits.map(each => each.key).filter(each => whiteList.indexOf(each) !== -1);
            _this.get('osfProviders').slice().map(function(each) {
                if (providers.indexOf(each) === -1) {
                    providers.push(each);
                }
            });
            providers.splice(providers.indexOf('OSF'), 1);
            providers.sort((a, b) => a.toLowerCase() < b.toLowerCase() ? -1 : 1);
            providers.unshift('OSF');
            _this.set('otherProviders', providers);
            _this.notifyPropertyChange('otherProviders');
        });
        this.loadPage();
    },
    subjectChanged: Ember.observer('subjectFilter', function() {
        Ember.run.once(() => {
            let filter = this.get('subjectFilter');
            if (!filter) return;
            this.set('activeFilters.subjects', filter.split('AND'));
            this.notifyPropertyChange('activeFilters');
            this.loadPage();
        });
    }),
    providerChanged: Ember.observer('providerFilter', function() {
        Ember.run.once(() => {
            let filter = this.get('providerFilter');
            if (!filter) return;
            this.set('activeFilters.providers', filter.split('AND'));
            this.notifyPropertyChange('activeFilters');
            this.set('providersPassed', true);
            this.loadPage();
        });
    }),
    loadPage() {
        this.set('loading', true);
        Ember.run.debounce(this, this._loadPage, 500);
    },
    _loadPage() {
        let queryBody = JSON.stringify(this.getQueryBody());

        return Ember.$.ajax({
            url: this.get('searchUrl'),
            crossDomain: true,
            type: 'POST',
            contentType: 'application/json',
            data: queryBody
        }).then(json => {
            if (this.isDestroyed || this.isDestroying) return;

            this.set('numberOfResults', json.hits.total);

            let results = json.hits.hits.map(hit => {
                // HACK: Make share data look like apiv2 preprints data
                let result = Ember.merge(hit._source, {
                    id: hit._id,
                    type: 'elastic-search-result',
                    workType: hit._source['@type'],
                    abstract: hit._source.description,
                    subjects: hit._source.subjects.map(each => ({text: each})),
                    providers: hit._source.sources.map(item => ({name: item})),
                    osfProvider: hit._source.sources.reduce((acc, source) => (acc || this.get('osfProviders').indexOf(source) !== -1), false),
                    hyperLinks: [// Links that are hyperlinks from hit._source.lists.links
                        {
                            type: 'share',
                            url: config.SHARE.baseUrl + 'curate/preprint/' + hit._id
                        }
                    ],
                    infoLinks: [] // Links that are not hyperlinks  hit._source.lists.links
                });

                hit._source.lists.links.forEach(function(linkItem) {
                    if (isHyperLink(linkItem.url)) {
                        result.hyperLinks.push(linkItem);
                    } else {
                        result.infoLinks.push(linkItem);
                    }
                });

                result.contributors = result.lists.contributors.map(contributor => ({
                    users: Object.keys(contributor).reduce((acc, key) => Ember.merge(acc, {[key.camelize()]: contributor[key]}), {})
                }));

                // Temporary fix to handle half way migrated SHARE ES
                // Only false will result in a false here.
                result.contributors.map(contributor => contributor.users.bibliographic = !(contributor.users.bibliographic === false));  // jshint ignore:line

                return result;
            });

            this.set('loading', false);
            return this.set('results', results);
        });
    },
    maxPages: Ember.computed('numberOfResults', function() {
        return ((this.get('numberOfResults') / this.get('size')) | 0) + (this.get('numberOfResults') % 10 === 0 ? 0 : 1);
    }),
    getQueryBody() {
        let facetFilters = this.get('activeFilters');
        this.set('subjectFilter', facetFilters.subjects.slice().join('AND'));
        this.set('providerFilter', facetFilters.providers.slice().join('AND'));
        let filters = {};
        for (let k of Object.keys(facetFilters)) {
            let key = filterMap[k];
            if (key && facetFilters[k].length) {
                filters[key] = facetFilters[k];
            }
        }
        let query = {
            query_string: {
                query: this.get('queryString') || '*'
            }
        };

        let filters_ = [];
        for (let k of Object.keys(filters)) {
            let terms = {};
            terms[k] = filters[k];
            filters_.push({
                terms: terms
            });
        }
        filters_.push({
            terms: {'type.raw': ['preprint']}
        });
        query = {
            bool: {
                must: query,
                filter: filters_
            }
        };

        let queryBody = {
            query,
            from: (this.get('page') - 1) * this.get('size'),
        };

        let sortByOption = this.get('chosenSortByOption');
        if (sortByOption === 'Upload date (oldest to newest)') {
            queryBody.sort = {};
            queryBody.sort.date_updated = 'asc';
        } else if (sortByOption === 'Upload date (newest to oldest)') {
            queryBody.sort = {};
            queryBody.sort.date_updated = 'desc';
        }

        return this.set('queryBody', queryBody);
    },

    reloadSearch: Ember.observer('activeFilters', function() {
        this.set('page', 1);
        this.loadPage();
    }),
    otherProviders: [],
    actions: {
        search(val, event) {
            if (event && (event.keyCode < 49 || [91, 92, 93].indexOf(event.keyCode) !== -1) && [8, 32, 48].indexOf(event.keyCode) === -1) return;

            this.set('page', 1);
            this.loadPage();
        },

        previous() {
            if (this.get('page') > 1) {
                this.decrementProperty('page');
                this.loadPage();
            }
        },

        next() {
            if (this.get('page') * this.get('size') <= this.get('numberOfResults')) {
                this.incrementProperty('page');
                this.loadPage();
            }
        },

        clearFilters() {
            this.set('activeFilters',  { providers: [], subjects: [] });
        },

        sortBySelect(index) {
            // Selecting an option just swaps it with whichever option is first
            let copy = this.get('sortByOptions').slice(0);
            let temp = copy[0];
            copy[0] = copy[index];
            copy[index] = temp;
            this.set('sortByOptions', copy);
            this.set('page', 1);
            this.loadPage();
        },

        selectSubjectFilter(subject) {
            if (typeof subject === 'object') {
                subject = subject.text;
            }
            if (this.get('activeFilters.subjects').indexOf(subject) === -1) {
                this.get('activeFilters.subjects').pushObject(subject);
            } else {
                this.get('activeFilters.subjects').removeObject(subject);
            }
            this.notifyPropertyChange('activeFilters');
        },

        selectProvider(provider) {
            let currentProviders = this.get('activeFilters.providers').slice();
            if (currentProviders.indexOf(provider) !== -1) {
                this.get('activeFilters.providers').removeObject(provider);
            } else {
                this.get('activeFilters.providers').pushObject(provider);
            }
            this.notifyPropertyChange('activeFilters');
        },
    },
});
<|endoftext|>


<commit_before>/* formating.js
 * Written by Dominic Shelton.
 * Provides a number of functions for formatting text, cards and decks on Slack using the Slack API and Markdown.
 */
'use strict';

module.exports = {cardHelpMessage, deckHelpMessage, cardNoHitsMessage, deckNoHitsMessage, formatTitle, formatDecklist, formatCards, formatText};

var colours = require('./colours.json');
var packs = require('./datapacks.json');

var thumbsURL = process.env.THUMBS_URL;
var messages = {
    noCardHits: [
        "The run was successful but you didn't access _\u200b[cards]\u200b_.",
        "I was unable to find _\u200b[cards]\u200b_ in any of my remotes.",
        "Despite 822 Medium counters, _\u200b[cards]\u200b_ wasn't found.",
        "The Near-Earth Hub couldn't locate _\u200b[cards]\u200b_."
    ],
    noDeckHits: [
        "The archetype of that deck would be _\u200bnon-existant\u200b_."
    ],
    helpBrackets:
        "Search for a card by (partial) name, or acronym, or a decklist by its netrunnerdb link e.g.\`\`\`[sneakdoor] [hiemdal] [etf]\n[netrunnerdb\u200b.com/en/decklist/17055/example]\`\`\`",
    helpDeck:
        "Search for a decklist by its netrunnerdb link or ID number e.g.\`\`\`[command] 12345, [command] netrunnerdb\u200b.com/en/decklist/17055/example\`\`\`",
    helpCard:
        "Search for a card by (partial) name, approximation or acronym e.g.\`\`\`[command]sneakdoor, [command]hiemdal, [command]etf\`\`\`"
};
var headings = [
    ['Event', 'Hardware', 'Resource', 'Agenda', 'Asset', 'Upgrade', 'Operation'],
    ['Icebreaker', 'Program', 'Barrier', 'Code Gate', 'Sentry', 'Multi', 'Other']
];
var stats = [
    ['baselink', ' :_link:'],
    ['cost', ':_credit:'],
    ['memoryunits', ' :_mu:'],
    ['strength', ' str'],
    ['trash', ' :_trash:'],
    ['advancementcost', ' :_advance:'],
    ['minimumdecksize', ' :_deck:'],
    ['influencelimit', '•'],
    ['agendapoints', ' :_agenda:']
];

function influenceDots(influence) {
    return '•'.repeat(influence);
}

function getPack(code) {
    var cycle = packs[Math.floor(code/1000)];
    if (Array.isArray(cycle)) {
        cycle = cycle[Math.floor(((code % 1000) - 1) / 20)];
    }
    return '_\u200b' + cycle + '\u200b_';
}

function formatLink(text, url) {
    return '<' + url + '|' + text + '>';
}

function getFactionEmoji(faction) {
    return ':_' + faction.replace(/\s.*/, '').toLowerCase() + ':';
}

function getCardNoHitsMessage(text) {
    var r = Math.floor(Math.random() * messages.noCardHits.length);
    return messages.noCardHits[r].replace(/\[cards\]/g, text);
}

function cardHelpMessage(command) {
    if (command) {
        return {
            text: messages.helpCard.replace(/\[command\]/g, command + ' ')
        };
    }
    return {
        text: messages.helpBrackets
    };
};

function deckHelpMessage(command) {
    return {
        text: messages.helpDeck.replace(/\[command\]/g, command)
    };
};

// Generates a message indicating that cards weren't found, by concatenating the names with commas and 'or'
function cardNoHitsMessage(cards) {
    var text;
    if (cards.length >= 2) {
        text = cards.slice(0, cards.length - 1).join(', ');
        text += ' or ' + cards[cards.length - 1];
    } else {
        text = cards[0];
    }
    var message = getCardNoHitsMessage(text);
    return {
        text: message
    };
};

function deckNoHitsMessage() {
    var r = Math.floor(Math.random() * messages.noDeckHits.length);
    return {
        text: messages.noDeckHits[r]
    };
};

// Makes the title text bold and makes a link if requested
function formatTitle(title, url) {
    title = '*\u200b' + title + '\u200b*';
    if (url && url !== '') {
        return formatLink(title, url);
    }
    return title;
};

function formatDecklist(decklist) {
    // Initialise the return object.
    var o = {text: '', attachments:[{mrkdwn_in: ['pretext', 'fields']}]};
    var faction = decklist.cards.Identity[0].card.faction;
    var usedInfluence = 0;
    var decksize = 0;
    var agendapoints = 0;
    var fields = [];
    // Initialise the newestCard var to a card that is guaranteed to be in every deck.
    var newestCard = parseInt(decklist.cards.Identity[0].card.code);
    o.text = formatTitle(decklist.name, decklist.url);
    o.text += ' - _\u200b' + decklist.creator + '\u200b_';
    for (let column in headings) {
        // Create the columns as Slack 'fields'.
        fields[column] = {title: '', value: '', short: true};
        for (let heading in headings[column]) {
            var type = headings[column][heading];
            // Check if the deck actually contains cards of the specified heading
            if (decklist.cards[type]) {
                // If this is not the first heading, add padding after the previous.
                if (heading != 0) {
                    fields[column].value += '\n\n';
                }
                fields[column].value += formatTitle(type);
                // Iterate through all the cards of the type in the decklist.
                for (let i in decklist.cards[type]) {
                    var card = decklist.cards[type][i];
                    var code = parseInt(card.card.code);
                    fields[column].value += '\n' + card.quantity;
                    fields[column].value += ' × ' + formatLink(card.card.title, card.card.url);
                    decksize += card.quantity;
                    // Check that the card is not newer than the previous newest card.
                    if (code > newestCard) {
                        newestCard = code;
                    }
                    if (card.card.agendapoints) {
                        agendapoints += card.card.agendapoints * card.quantity;
                    }
                    // Add influence dots after the card name if required.
                    if (card.card.faction !== faction) {
                        var inf = card.quantity * card.card.factioncost;
                        fields[column].value += ' ' + influenceDots(inf);
                        usedInfluence += inf;
                    }
                }
            }
        }
    }
    o.attachments[0].color = colours[faction];
    o.attachments[0].fields = fields;
    // The identity of the decklist is displayed before the decklist.
    o.attachments[0].pretext = formatLink(decklist.cards.Identity[0].card.title,
            decklist.cards.Identity[0].card.url);
    o.attachments[0].pretext += '\n' + decksize + ' :_deck: (min ';
    o.attachments[0].pretext +=  decklist.cards.Identity[0].card.minimumdecksize;
    o.attachments[0].pretext += ') - ' + usedInfluence + '/';
    o.attachments[0].pretext += (decklist.cards.Identity[0].card.influencelimit || '∞') + '•';
    if (decklist.cards.Identity[0].card.side !== 'Runner') {
        o.attachments[0].pretext += ' - ' + agendapoints + ' :_agenda:';
    }
    o.attachments[0].pretext += '\nCards up to ' + getPack(newestCard);
    return o;
};

function formatCards(cards, missing) {
    var o;
    // If there are cards that could not be found, tell the user.
    if (missing && missing.length > 0) {
        o = cardNoHitsMessage(missing);
        o.attachments = [];
    } else {
        o = {text:'', attachments:[]};
    }
    // Display the cards that were found either way.
    for (var i = 0; i < cards.length; i++) {
        var a = {pretext: '', mrkdwn_in: ['pretext', 'text']};
        var faction = cards[i].faction;
        var title = cards[i].title;
        if (cards[i].uniqueness){
            title = '◆ ' + title;
        }
        // If the Slack message is blank, put the title of the first card there.
        // The Slack API won't display messages with blank text even when there are attachments.
        if (o.text === '') {
            o.text = formatTitle(title, cards[0].url);
        } else {
            a.pretext = formatTitle(title, cards[i].url) + '\n';
        }
        // Append the rest of the card details to the attachment.
        a.pretext += '*\u200b' + cards[i].type;
        if (cards[i].subtype ) {
            a.pretext += ':\u200b* ' + cards[i].subtype;
        } else {
            a.pretext += '\u200b*';
        }
        a.pretext += ' - ' + getFactionEmoji(faction);
        if (cards[i].factioncost) {
            a.pretext += influenceDots(cards[i].factioncost);
        }
        a.pretext += '\n';
        var first = true;
        // For some cards, the cost is actually a rez cost, this should be reflected in the emoji.
        if (cards[i].type === 'Asset' || cards[i].type === 'Upgrade' || cards[i].type === 'ICE') {
            stats[1][1] = ' :_rez:';
        } else {
            stats[1][1] = ':_credit:';
        }
        // Iterate through the possible card stats adding them to the text where present.
        for (var j = 0; j < stats.length; j++) {
            if (cards[i][stats[j][0]] || cards[i][stats[j][0]] === 0) {
                if (!first) {
                    a.pretext += ' - ';
                }
                a.pretext += cards[i][stats[j][0]] + stats[j][1];
                first = false;
            // Special case for draft IDs with infinite influence limit
            } else if (cards[i].type === 'Identity' && stats[j][0] === 'influencelimit' && !cards[i].influencelimit) {
                a.pretext += ' - ∞•';
            }
        }
        // Replace memory units with the slack emoji
        a.pretext = a.pretext.replace(/(\d|X)\s*:_mu:/gi, function (x) {
            return x.replace(/(.).*/, ':_$1mu:').toLowerCase();
        });
        a.pretext += ' - ' + getPack(parseInt(cards[i].code));
        a.color = colours[faction];
        // Add the card text to the attachment if present.
        if (cards[i].text) {
            a.text = formatText(cards[i].text);
        }
        a.thumb_url = thumbsURL + cards[i].code + '.png';
        o.attachments.push(a);
    }
    return o;
};

// Replace all the html and nrdb text with the Slack equivalent.
function formatText(text) {
    if (!text) return text;

    text = text.replace(/\r\n/g, '\n');

    // NRDB symbols to Slack emoji.
    text = text.replace(/\[Credits\]/g, ':_credit:');
    text = text.replace(/\[Recurring\ Credits\]/g, ':_recurringcredit:');
    text = text.replace(/\[Click\]/g, ':_click:');
    text = text.replace(/\ *\[Link\]/g, ' :_link:');
    text = text.replace(/\[Trash\]/g, ':_trash:');
    text = text.replace(/\[Subroutine\]/g, ':_subroutine:');
    // Individual mu emoji for numbers and 'Xmu'
    text = text.replace(/(\d|X)\s*\[Memory\ Unit\]/gi, function (x) {
        return x.replace(/(.).*/, ':_$1mu:').toLowerCase();
    });
    text = text.replace(/\[Memory Unit\]/g, ':_mu:');

    // HTML bold to Slack bold
    text = text.replace(/<strong>/g, '*\u200b');
    text = text.replace(/<\/strong>/g, '\u200b*');
    // HTML superscript to unicode superscript since Slack markdown doesn't support it.
    text = text.replace(/<sup>(?:\d|X)+<\/sup>/gi, function(x){
        x = x.replace(/<sup>|<\/sup>/g, '');
        x = x.replace(/X/i,'ˣ');
        x = x.replace(/\d/g, function(d){
            return ['⁰','¹','²','³','⁴','⁵','⁶','⁷','⁸','⁹'][parseInt(d)];
        });
        return x;
    });

    text = text.replace(/&/g, '&');
    text = text.replace(/</g, '<');
    text = text.replace(/>/g, '>');

    return text;
};

<commit_msg>Faction logos in card text now replaced with slack emoji<commit_after>/* formating.js
 * Written by Dominic Shelton.
 * Provides a number of functions for formatting text, cards and decks on Slack using the Slack API and Markdown.
 */
'use strict';

module.exports = {cardHelpMessage, deckHelpMessage, cardNoHitsMessage, deckNoHitsMessage, formatTitle, formatDecklist, formatCards, formatText};

var colours = require('./colours.json');
var packs = require('./datapacks.json');

var thumbsURL = process.env.THUMBS_URL;
var messages = {
    noCardHits: [
        "The run was successful but you didn't access _\u200b[cards]\u200b_.",
        "I was unable to find _\u200b[cards]\u200b_ in any of my remotes.",
        "Despite 822 Medium counters, _\u200b[cards]\u200b_ wasn't found.",
        "The Near-Earth Hub couldn't locate _\u200b[cards]\u200b_."
    ],
    noDeckHits: [
        "The archetype of that deck would be _\u200bnon-existant\u200b_."
    ],
    helpBrackets:
        "Search for a card by (partial) name, or acronym, or a decklist by its netrunnerdb link e.g.\`\`\`[sneakdoor] [hiemdal] [etf]\n[netrunnerdb\u200b.com/en/decklist/17055/example]\`\`\`",
    helpDeck:
        "Search for a decklist by its netrunnerdb link or ID number e.g.\`\`\`[command] 12345, [command] netrunnerdb\u200b.com/en/decklist/17055/example\`\`\`",
    helpCard:
        "Search for a card by (partial) name, approximation or acronym e.g.\`\`\`[command]sneakdoor, [command]hiemdal, [command]etf\`\`\`"
};
var headings = [
    ['Event', 'Hardware', 'Resource', 'Agenda', 'Asset', 'Upgrade', 'Operation'],
    ['Icebreaker', 'Program', 'Barrier', 'Code Gate', 'Sentry', 'Multi', 'Other']
];
var stats = [
    ['baselink', ' :_link:'],
    ['cost', ':_credit:'],
    ['memoryunits', ' :_mu:'],
    ['strength', ' str'],
    ['trash', ' :_trash:'],
    ['advancementcost', ' :_advance:'],
    ['minimumdecksize', ' :_deck:'],
    ['influencelimit', '•'],
    ['agendapoints', ' :_agenda:']
];

function influenceDots(influence) {
    return '•'.repeat(influence);
}

function getPack(code) {
    var cycle = packs[Math.floor(code/1000)];
    if (Array.isArray(cycle)) {
        cycle = cycle[Math.floor(((code % 1000) - 1) / 20)];
    }
    return '_\u200b' + cycle + '\u200b_';
}

function formatLink(text, url) {
    return '<' + url + '|' + text + '>';
}

function getFactionEmoji(faction) {
    return ':_' + faction.replace(/\s.*/, '').toLowerCase() + ':';
}

function getCardNoHitsMessage(text) {
    var r = Math.floor(Math.random() * messages.noCardHits.length);
    return messages.noCardHits[r].replace(/\[cards\]/g, text);
}

function cardHelpMessage(command) {
    if (command) {
        return {
            text: messages.helpCard.replace(/\[command\]/g, command + ' ')
        };
    }
    return {
        text: messages.helpBrackets
    };
};

function deckHelpMessage(command) {
    return {
        text: messages.helpDeck.replace(/\[command\]/g, command)
    };
};

// Generates a message indicating that cards weren't found, by concatenating the names with commas and 'or'
function cardNoHitsMessage(cards) {
    var text;
    if (cards.length >= 2) {
        text = cards.slice(0, cards.length - 1).join(', ');
        text += ' or ' + cards[cards.length - 1];
    } else {
        text = cards[0];
    }
    var message = getCardNoHitsMessage(text);
    return {
        text: message
    };
};

function deckNoHitsMessage() {
    var r = Math.floor(Math.random() * messages.noDeckHits.length);
    return {
        text: messages.noDeckHits[r]
    };
};

// Makes the title text bold and makes a link if requested
function formatTitle(title, url) {
    title = '*\u200b' + title + '\u200b*';
    if (url && url !== '') {
        return formatLink(title, url);
    }
    return title;
};

function formatDecklist(decklist) {
    // Initialise the return object.
    var o = {text: '', attachments:[{mrkdwn_in: ['pretext', 'fields']}]};
    var faction = decklist.cards.Identity[0].card.faction;
    var usedInfluence = 0;
    var decksize = 0;
    var agendapoints = 0;
    var fields = [];
    // Initialise the newestCard var to a card that is guaranteed to be in every deck.
    var newestCard = parseInt(decklist.cards.Identity[0].card.code);
    o.text = formatTitle(decklist.name, decklist.url);
    o.text += ' - _\u200b' + decklist.creator + '\u200b_';
    for (let column in headings) {
        // Create the columns as Slack 'fields'.
        fields[column] = {title: '', value: '', short: true};
        for (let heading in headings[column]) {
            var type = headings[column][heading];
            // Check if the deck actually contains cards of the specified heading
            if (decklist.cards[type]) {
                // If this is not the first heading, add padding after the previous.
                if (heading != 0) {
                    fields[column].value += '\n\n';
                }
                fields[column].value += formatTitle(type);
                // Iterate through all the cards of the type in the decklist.
                for (let i in decklist.cards[type]) {
                    var card = decklist.cards[type][i];
                    var code = parseInt(card.card.code);
                    fields[column].value += '\n' + card.quantity;
                    fields[column].value += ' × ' + formatLink(card.card.title, card.card.url);
                    decksize += card.quantity;
                    // Check that the card is not newer than the previous newest card.
                    if (code > newestCard) {
                        newestCard = code;
                    }
                    if (card.card.agendapoints) {
                        agendapoints += card.card.agendapoints * card.quantity;
                    }
                    // Add influence dots after the card name if required.
                    if (card.card.faction !== faction) {
                        var inf = card.quantity * card.card.factioncost;
                        fields[column].value += ' ' + influenceDots(inf);
                        usedInfluence += inf;
                    }
                }
            }
        }
    }
    o.attachments[0].color = colours[faction];
    o.attachments[0].fields = fields;
    // The identity of the decklist is displayed before the decklist.
    o.attachments[0].pretext = formatLink(decklist.cards.Identity[0].card.title,
            decklist.cards.Identity[0].card.url);
    o.attachments[0].pretext += '\n' + decksize + ' :_deck: (min ';
    o.attachments[0].pretext +=  decklist.cards.Identity[0].card.minimumdecksize;
    o.attachments[0].pretext += ') - ' + usedInfluence + '/';
    o.attachments[0].pretext += (decklist.cards.Identity[0].card.influencelimit || '∞') + '•';
    if (decklist.cards.Identity[0].card.side !== 'Runner') {
        o.attachments[0].pretext += ' - ' + agendapoints + ' :_agenda:';
    }
    o.attachments[0].pretext += '\nCards up to ' + getPack(newestCard);
    return o;
};

function formatCards(cards, missing) {
    var o;
    // If there are cards that could not be found, tell the user.
    if (missing && missing.length > 0) {
        o = cardNoHitsMessage(missing);
        o.attachments = [];
    } else {
        o = {text:'', attachments:[]};
    }
    // Display the cards that were found either way.
    for (var i = 0; i < cards.length; i++) {
        var a = {pretext: '', mrkdwn_in: ['pretext', 'text']};
        var faction = cards[i].faction;
        var title = cards[i].title;
        if (cards[i].uniqueness){
            title = '◆ ' + title;
        }
        // If the Slack message is blank, put the title of the first card there.
        // The Slack API won't display messages with blank text even when there are attachments.
        if (o.text === '') {
            o.text = formatTitle(title, cards[0].url);
        } else {
            a.pretext = formatTitle(title, cards[i].url) + '\n';
        }
        // Append the rest of the card details to the attachment.
        a.pretext += '*\u200b' + cards[i].type;
        if (cards[i].subtype ) {
            a.pretext += ':\u200b* ' + cards[i].subtype;
        } else {
            a.pretext += '\u200b*';
        }
        a.pretext += ' - ' + getFactionEmoji(faction);
        if (cards[i].factioncost) {
            a.pretext += influenceDots(cards[i].factioncost);
        }
        a.pretext += '\n';
        var first = true;
        // For some cards, the cost is actually a rez cost, this should be reflected in the emoji.
        if (cards[i].type === 'Asset' || cards[i].type === 'Upgrade' || cards[i].type === 'ICE') {
            stats[1][1] = ' :_rez:';
        } else {
            stats[1][1] = ':_credit:';
        }
        // Iterate through the possible card stats adding them to the text where present.
        for (var j = 0; j < stats.length; j++) {
            if (cards[i][stats[j][0]] || cards[i][stats[j][0]] === 0) {
                if (!first) {
                    a.pretext += ' - ';
                }
                a.pretext += cards[i][stats[j][0]] + stats[j][1];
                first = false;
            // Special case for draft IDs with infinite influence limit
            } else if (cards[i].type === 'Identity' && stats[j][0] === 'influencelimit' && !cards[i].influencelimit) {
                a.pretext += ' - ∞•';
            }
        }
        // Replace memory units with the slack emoji
        a.pretext = a.pretext.replace(/(\d|X)\s*:_mu:/gi, function (x) {
            return x.replace(/(.).*/, ':_$1mu:').toLowerCase();
        });
        a.pretext += ' - ' + getPack(parseInt(cards[i].code));
        a.color = colours[faction];
        // Add the card text to the attachment if present.
        if (cards[i].text) {
            a.text = formatText(cards[i].text);
        }
        a.thumb_url = thumbsURL + cards[i].code + '.png';
        o.attachments.push(a);
    }
    return o;
};

// Replace all the html and nrdb text with the Slack equivalent.
function formatText(text) {
    if (!text) return text;

    text = text.replace(/\r\n/g, '\n');

    // NRDB symbols to Slack emoji.
    text = text.replace(/\[Credits\]/g, ':_credit:');
    text = text.replace(/\[Recurring\ Credits\]/g, ':_recurringcredit:');
    text = text.replace(/\[Click\]/g, ':_click:');
    text = text.replace(/\ *\[Link\]/g, ' :_link:');
    text = text.replace(/\[Trash\]/g, ':_trash:');
    text = text.replace(/\[Subroutine\]/g, ':_subroutine:');
    // Individual mu emoji for numbers and 'Xmu'
    text = text.replace(/(\d|X)\s*\[Memory\ Unit\]/gi, function (x) {
        return x.replace(/(.).*/, ':_$1mu:').toLowerCase();
    });
    text = text.replace(/\[Memory Unit\]/g, ':_mu:');

    // HTML bold to Slack bold
    text = text.replace(/<strong>/g, '*\u200b');
    text = text.replace(/<\/strong>/g, '\u200b*');
    // HTML superscript to unicode superscript since Slack markdown doesn't support it.
    text = text.replace(/<sup>(?:\d|X)+<\/sup>/gi, function(x){
        x = x.replace(/<sup>|<\/sup>/g, '');
        x = x.replace(/X/i,'ˣ');
        x = x.replace(/\d/g, function(d){
            return ['⁰','¹','²','³','⁴','⁵','⁶','⁷','⁸','⁹'][parseInt(d)];
        });
        return x;
    });

    // Replace nrdb faction symbols with Slack emoji
    text = text.replace(/\[(jinteki|weyland-consortium|nbn|haas-bioroid)\]/, (x) => {
        return ":_" + x.replace(/(\[|\]|-.*)/g, '') + ":";
    });

    text = text.replace(/&/g, '&');
    text = text.replace(/</g, '<');
    text = text.replace(/>/g, '>');

    return text;
};

<|endoftext|>


<commit_before>'use strict';

var webspeechapi, vibrateWebApi, DerReader;

const env = require('./env/env.json').env;

if (env === 'dev') {
  webspeechapi = require('./../modules/tts.webapi/tts.webapi.js');
  vibrateWebApi = require('./../modules/vibrate.webapi/vibrate.webapi.js');
  DerReader = require('./../modules/der-reader/der-reader.js');
} else {
  webspeechapi = require('tts.webapi');
  vibrateWebApi = require('vibrate.webapi');
  DerReader = require('der-reader');
}

var remote = require('electron').remote;

window.nodeRequire = require;
delete window.require;
delete window.exports;
delete window.module;

function exit() {
  var window = remote.getCurrentWindow();
  window.close();
}

DerReader.init({
  container: 'der-reader',
  derFile: null,
  tts: webspeechapi,
  vibrate: vibrateWebApi,
  defaultMode: 0,
  format: 'A3',
  exit: exit
});

window.app = {
  env: env,
  version: process.env.npm_package_version
};<commit_msg>Fix electron file to der-reader dist<commit_after>'use strict';

var webspeechapi, vibrateWebApi, DerReader;

const env = require('./env/env.json').env;

if (env === 'dev') {
  webspeechapi = require('../modules/tts.webapi/tts.webapi.js');
  vibrateWebApi = require('../modules/vibrate.webapi/vibrate.webapi.js');
  DerReader = require('../modules/der-reader/dist/der-reader.js');
} else {
  webspeechapi = require('tts.webapi');
  vibrateWebApi = require('vibrate.webapi');
  DerReader = require('der-reader');
}

var remote = require('electron').remote;

window.nodeRequire = require;
delete window.require;
delete window.exports;
delete window.module;

function exit() {
  var window = remote.getCurrentWindow();
  window.close();
}

DerReader.init({
  container: 'der-reader',
  derFile: null,
  tts: webspeechapi,
  vibrate: vibrateWebApi,
  defaultMode: 0,
  format: 'A3',
  exit: exit
});

window.app = {
  env: env,
  version: process.env.npm_package_version
};<|endoftext|>


<commit_before>var app = angular.module('ACM-Dash',[]);

app.config(function($interpolateProvider) {
	$interpolateProvider.startSymbol('//');
    $interpolateProvider.endSymbol('//');
});

app.controller('ACM-Controller', ['$scope', '$http', '$interpolate', function($scope, $http, $interpolate) {
	$scope.master = {};
	$scope.tab = [ "active", "", "" ];
	$scope.tabPane = [ "tab-pane active", "tab-pane", "tab-pane"];
	$scope.progress = { isWaiting: false };
	$scope.user = {
		isNew: true,
		uid: "",
		memberName: "",
		memberType: "0",
		startDay: "",
		laserA: "0",
		laserB: "1",
		shopbot: "0",
		wood: "0",
		metal: "0",
		textile: "0",
		threeD: "0"
	};
	var PATH = "../static/img/users/";
	$scope.display = {
			id: "",
			name: "",
			image: PATH + "GUEST.jpg",
			startDay: "",
			studio: "",
			access: ["red", "red", "red", "red", "red", "red", "red"],
			lifeTime: "",
			monthTime: ""
	};

	$scope.activeTab = function(clicked) {
		$scope.tab = [ "", "", ""];
		$scope.tabPane = [ "tab-pane", "tab-pane", "tab-pane" ];
		$scope.tab[clicked] = "active";
		$scope.tabPane[clicked] += " " + $scope.tab[clicked];
	};

	$scope.time = new laserTime(123456);
	function laserTime(rawTime) {
		this.rawTime = rawTime;
		this.set = function(newTime) { this.rawTime = newTime };

		this.hours = function() { return parseInt(this.rawTime / 3600) };
		this.minutes = function() { return parseInt(this.rawTime % 3600 / 60) };
		this.seconds = function() { return this.rawTime % 60 };

		this.charge = function() { return (1.5 * parseFloat(this.rawTime / 60)).toFixed(2) };
	};

	$scope.getTime = function() {
		$scope.progress.isWaiting = true;
		$http.get("../getTime").success(function(res) {
			if (res == 2) {
				$scope.isError = true;
				$scope.time.set(0);
				clock.setTime(0);
			}
			else {
				console.log("Raw Time: " + res);
				$scope.isError = false;
				$scope.time.set(res);
				clock.setTime(res);
			}
			$scope.progress.isWaiting = false;
		});
	};

	$scope.resetTime = function() {
		$scope.progress.isWaiting = true;
		$http.get("../resetTime").success(function(res) {
			if (res[0] == 1) {
				$scope.isError = false;
				//$scope.getTime();
			}
			else {
				$scope.isError = true;
			}
			$scope.progress.isWaiting = false;
		});
	};

	$scope.registerCard = function(isNew) {
		$scope.user.isNew = isNew;

		if ($scope.user.uid && $scope.user.memberName) {
			console.log(JSON.stringify($scope.user));
			var input = $scope.user.uid;

			$http({
				url: "../registerCard",
				method: "POST",
				headers: { 'Content-Type': 'application/json' },
				data: JSON.stringify($scope.user)})
				.success(function(data) {

					if (data != input) {
						$scope.entered = "Error";
						console.log($scope.entered);
					}
					else {
						$scope.entered = "User " + data + " registered.";
						console.log("ID registered: " + parseInt(data));
					}

				});

			$scope.user.uid = "";
			$scope.user.memberName = "";
			$scope.user.memberType = "0";
		}
	}

	var socket = io.connect("http://localhost:5000");
	console.log("Socket Connected.");
	socket.on('scan', function(msg) {
		$scope.activeTab(0);
		$scope.display.id = msg;
		$scope.display.name = "";
		$scope,display.image = PATH + "GUEST.jpg";
		$scope.refresh = "";
		$scope.$apply();
	});
	socket.on('data', function(msg) {
		$scope.activeTab(0);
		$scope.display.id = msg[1];
		$scope.display.name = msg[2];
		$scope.display.image = PATH + msg[2] + ".jpg";
		$scope.display.startDay = msg[4];
		$scope.display.studio = msg[8];

		for (var i = 0; i < $scope.display.access.length; i++) {
			$scope.display.access[i] = parseInt(msg[i + 9]) ? "green" : "red";
		}

		$scope.display.lifeTime = (parseInt(msg[16]) / 60).toFixed(0);
		$scope.display.monthTime = (parseInt(msg[17]) / 60).toFixed(0);

		$scope.display.lifeTime = $scope.display.lifeTime.toString() + " minutes"
		$scope.display.monthTime = $scope.display.monthTime.toString() + " minutes"

		$scope.$apply();
    });
	socket.on('refresh', function(msg) {
		$scope.refresh = msg;
		$scope.$apply();
	})

	var clock = $('.clock').FlipClock({
		autoStart: false,
		clockFace: 'HourlyCounter'
	});

	clock.setTime($scope.time.rawTime);

	/*$scope.drawDonut = function() {
		var vis = d3.select("#time-display").append("g").append("svg");
		var pi = Math.PI;

		var containerWidth = d3.select("#content").node().getBoundingClientRect().width;
		var containerHeight = d3.select("#content").node().getBoundingClientRect().height;
		var j = 0.25;
		var x = j * containerWidth;
		var y = containerHeight/2;
		console.log(y);

		vis.attr("width", containerWidth).attr("height", 200);

		var arc = d3.arc()
			.innerRadius(75)
			.outerRadius(100)
			.startAngle(0)
			.endAngle(2 * pi);

		for (i = 0; i < 3; i++) {
			vis.append("path")
				.attr("d", arc)
				.attr("fill", "grey")
				.attr("transform", "translate(" + x + "," + 100 + ")");
				j += 0.25;
				x = j * containerWidth;
		};
		// ---------------------------------------------------------------
		j = 0.25;
		x = j * containerWidth;

		var data = [ $scope.hours, $scope.minutes, $scope.seconds ];
		var radianScale = d3.scaleLinear().domain([0, 59]).range([0, 2*pi]);
		data[0] = radianScale($scope.hours);
		data[1] = radianScale($scope.minutes);
		data[2] = radianScale($scope.seconds);

		var arc2 = d3.arc()
			.innerRadius(75)
			.outerRadius(100)
			.startAngle(0);

		var arc3 = d3.arc()
			.innerRadius(75)
			.outerRadius(100)
			.startAngle(0)
			.endAngle(pi/180);

		for (i = 0; i < 3; i++) {
			arc2.endAngle(data[i]);
			vis.append("path")
				.attr("fill", "blue")
				.attr("transform", "translate(" + x + "," + 100 + ")")
				.attr('d', arc3)
				.transition().delay(250)
				.attr("d", arc2);
				j += 0.25;
				x = j * containerWidth;
		};

	};*/

}]);<commit_msg>Fixed typo<commit_after>var app = angular.module('ACM-Dash',[]);

app.config(function($interpolateProvider) {
	$interpolateProvider.startSymbol('//');
    $interpolateProvider.endSymbol('//');
});

app.controller('ACM-Controller', ['$scope', '$http', '$interpolate', function($scope, $http, $interpolate) {
	$scope.master = {};
	$scope.tab = [ "active", "", "" ];
	$scope.tabPane = [ "tab-pane active", "tab-pane", "tab-pane"];
	$scope.progress = { isWaiting: false };
	$scope.user = {
		isNew: true,
		uid: "",
		memberName: "",
		memberType: "0",
		startDay: "",
		laserA: "0",
		laserB: "1",
		shopbot: "0",
		wood: "0",
		metal: "0",
		textile: "0",
		threeD: "0"
	};
	var PATH = "../static/img/users/";
	$scope.display = {
			id: "",
			name: "",
			image: PATH + "GUEST.jpg",
			startDay: "",
			studio: "",
			access: ["red", "red", "red", "red", "red", "red", "red"],
			lifeTime: "",
			monthTime: ""
	};

	$scope.activeTab = function(clicked) {
		$scope.tab = [ "", "", ""];
		$scope.tabPane = [ "tab-pane", "tab-pane", "tab-pane" ];
		$scope.tab[clicked] = "active";
		$scope.tabPane[clicked] += " " + $scope.tab[clicked];
	};

	$scope.time = new laserTime(123456);
	function laserTime(rawTime) {
		this.rawTime = rawTime;
		this.set = function(newTime) { this.rawTime = newTime };

		this.hours = function() { return parseInt(this.rawTime / 3600) };
		this.minutes = function() { return parseInt(this.rawTime % 3600 / 60) };
		this.seconds = function() { return this.rawTime % 60 };

		this.charge = function() { return (1.5 * parseFloat(this.rawTime / 60)).toFixed(2) };
	};

	$scope.getTime = function() {
		$scope.progress.isWaiting = true;
		$http.get("../getTime").success(function(res) {
			if (res == 2) {
				$scope.isError = true;
				$scope.time.set(0);
				clock.setTime(0);
			}
			else {
				console.log("Raw Time: " + res);
				$scope.isError = false;
				$scope.time.set(res);
				clock.setTime(res);
			}
			$scope.progress.isWaiting = false;
		});
	};

	$scope.resetTime = function() {
		$scope.progress.isWaiting = true;
		$http.get("../resetTime").success(function(res) {
			if (res[0] == 1) {
				$scope.isError = false;
				//$scope.getTime();
			}
			else {
				$scope.isError = true;
			}
			$scope.progress.isWaiting = false;
		});
	};

	$scope.registerCard = function(isNew) {
		$scope.user.isNew = isNew;

		if ($scope.user.uid && $scope.user.memberName) {
			console.log(JSON.stringify($scope.user));
			var input = $scope.user.uid;

			$http({
				url: "../registerCard",
				method: "POST",
				headers: { 'Content-Type': 'application/json' },
				data: JSON.stringify($scope.user)})
				.success(function(data) {

					if (data != input) {
						$scope.entered = "Error";
						console.log($scope.entered);
					}
					else {
						$scope.entered = "User " + data + " registered.";
						console.log("ID registered: " + parseInt(data));
					}

				});

			$scope.user.uid = "";
			$scope.user.memberName = "";
			$scope.user.memberType = "0";
		}
	}

	var socket = io.connect("http://localhost:5000");
	console.log("Socket Connected.");
	socket.on('scan', function(msg) {
		$scope.activeTab(0);
		$scope.display.id = msg;
		$scope.display.name = "";
		$scope.display.image = PATH + "GUEST.jpg";
		$scope.refresh = "";
		$scope.$apply();
	});
	socket.on('data', function(msg) {
		$scope.activeTab(0);
		$scope.display.id = msg[1];
		$scope.display.name = msg[2];
		$scope.display.image = PATH + msg[2] + ".jpg";
		$scope.display.startDay = msg[4];
		$scope.display.studio = msg[8];

		for (var i = 0; i < $scope.display.access.length; i++) {
			$scope.display.access[i] = parseInt(msg[i + 9]) ? "green" : "red";
		}

		$scope.display.lifeTime = (parseInt(msg[16]) / 60).toFixed(0);
		$scope.display.monthTime = (parseInt(msg[17]) / 60).toFixed(0);

		$scope.display.lifeTime = $scope.display.lifeTime.toString() + " minutes"
		$scope.display.monthTime = $scope.display.monthTime.toString() + " minutes"

		$scope.$apply();
    });
	socket.on('refresh', function(msg) {
		$scope.refresh = msg;
		$scope.$apply();
	})

	var clock = $('.clock').FlipClock({
		autoStart: false,
		clockFace: 'HourlyCounter'
	});

	clock.setTime($scope.time.rawTime);

	/*$scope.drawDonut = function() {
		var vis = d3.select("#time-display").append("g").append("svg");
		var pi = Math.PI;

		var containerWidth = d3.select("#content").node().getBoundingClientRect().width;
		var containerHeight = d3.select("#content").node().getBoundingClientRect().height;
		var j = 0.25;
		var x = j * containerWidth;
		var y = containerHeight/2;
		console.log(y);

		vis.attr("width", containerWidth).attr("height", 200);

		var arc = d3.arc()
			.innerRadius(75)
			.outerRadius(100)
			.startAngle(0)
			.endAngle(2 * pi);

		for (i = 0; i < 3; i++) {
			vis.append("path")
				.attr("d", arc)
				.attr("fill", "grey")
				.attr("transform", "translate(" + x + "," + 100 + ")");
				j += 0.25;
				x = j * containerWidth;
		};
		// ---------------------------------------------------------------
		j = 0.25;
		x = j * containerWidth;

		var data = [ $scope.hours, $scope.minutes, $scope.seconds ];
		var radianScale = d3.scaleLinear().domain([0, 59]).range([0, 2*pi]);
		data[0] = radianScale($scope.hours);
		data[1] = radianScale($scope.minutes);
		data[2] = radianScale($scope.seconds);

		var arc2 = d3.arc()
			.innerRadius(75)
			.outerRadius(100)
			.startAngle(0);

		var arc3 = d3.arc()
			.innerRadius(75)
			.outerRadius(100)
			.startAngle(0)
			.endAngle(pi/180);

		for (i = 0; i < 3; i++) {
			arc2.endAngle(data[i]);
			vis.append("path")
				.attr("fill", "blue")
				.attr("transform", "translate(" + x + "," + 100 + ")")
				.attr('d', arc3)
				.transition().delay(250)
				.attr("d", arc2);
				j += 0.25;
				x = j * containerWidth;
		};

	};*/

}]);<|endoftext|>


<commit_before>export default class TreeMap {
    constructor() {
        this.w = 960;
        this.h = 700;
        this.paddingAllowance = 2;
        //this.color = d3.scale.category10();
        this.redish = d3.rgb("#E60D0D");
        this.blueish = d3.rgb("#0E34E0");
        this.parentStrokeColor = d3.rgb("#4E4545");
        this.parentFillColor = d3.rgb("#7D7E8C");

        this.colorRedToBlueLinearScale = d3.scale.linear()
            .domain([0, 13])
            //.domain([0, 24])
            .range([this.redish, this.blueish]);
        this.darkerRedToBlueLinearScale = this.colorRedToBlueLinearScale
            .copy()
            .range([this.redish.darker(), this.blueish.darker()]);

        this.treemap = d3.layout.treemap()
            .size([this.w, this.h])
            .padding([20, 4, 4, 4])
            .value(d => d.data.cloc ? d.data.cloc.code : null);

    }

    //outputType can be "age" or "authors"
    render(outputType) {
        var self = this;

        outputType = outputType || "age";

        var svg = d3.select("body").append("svg")
            .style("position", "relative")
            .style("width", `${this.w}px`)
            .style("height", `${this.h}px`)
            .append("g")
            .attr("transform", "translate(-.5,-.5)");

        var tooltip = d3
            .select("body")
            .append("div")
            .attr("class", "tooltip")
            .style("opacity", 0);

        var getColor = outputType == "age"
            ? this.getColorByAge
            : this.getColorByNumberOfAuthors;

        this.setScalesBy(outputType);

        d3.json("/data/metrics.json", function(json) {
            var cell = svg.data([json]).selectAll("g")
                .data(self.treemap)
                .enter().append("g")
                .attr("class", "cell")
                .attr("transform", d => "translate(" + d.x + "," + d.y + ")");

            cell.on("mouseover", mouseover)
                .on("mouseout", mouseout);

            cell.append("rect")
                .attr("width", d => d.dx)
                .attr("height", d => d.dy)
                .style("fill", d => getCellFill(d))
                .style("stroke", d => getCellStroke(d))
                .style("z-index", d => -d.depth);

            cell.append("foreignObject")
                .attr("class", "foreignObject")
                .attr("width", d => Math.max(d.dx - self.paddingAllowance, 2))
                .attr("height", d => Math.max(d.dy - self.paddingAllowance, 2))
                .append("xhtml:body")
                .attr("class", "labelbody")
                .append("div")
                .attr("class", "label")
                .text(d => d.name)
                .attr("text-anchor", "middle");

            function formatTooltip(d) {
                return `${d.name}<pre>${JSON.stringify(d.data, null, 2)}</pre>`
            }

            function mouseover(d) {
                const text = Object.keys(d.data).map(key => `${key}:${d.data[key]}`).join("
");
                tooltip.transition()
                    .duration(200);
                tooltip
                    .style("opacity", 0.9);
                tooltip.html(formatTooltip(d))
                    .style("left", (d3.event.pageX) + "px")
                    .style("top", (d3.event.pageY) + "px");
            }

            function mouseout(d) {
                tooltip.transition()
                    .duration(500)
                    .style("opacity", 0);
            }

            function getCellStroke(d) {
                return getColor(d, self.parentStrokeColor, self.darkerRedToBlueLinearScale);
            }

            function getCellFill(d) {
                return getColor(d, self.parentFillColor, self.colorRedToBlueLinearScale);
            }
        });
    }

    getDomainBy(outputType) {
        return outputType == "age"
                ? [0, 24]
                : [0, 13];
    }

    setScalesBy(outputType) {
        this.colorRedToBlueLinearScale
            .domain(this.getDomainBy(outputType));
        this.darkerRedToBlueLinearScale
            .domain(this.getDomainBy(outputType));
    }

    getColorByAge(d, parentColor, scale) {
        return d.children
            ? parentColor
            : scale(24 - (d.data['code-maat'] && d.data['code-maat'].ageMonths ? d.data['code-maat'].ageMonths : 24));
    }

    getColorByNumberOfAuthors(d, parentColor, scale) {
        return d.children
            ? parentColor
            : scale(d.data['code-maat'] && d.data['code-maat'].nAuthors ? d.data['code-maat'].nAuthors : 0);
    }

}

<commit_msg>now using stragegies to switch what we render on<commit_after>class AgeStrategy {
    constructor() {
        this.domain = [0, 24];
    }

    getColor(d, parentColor, scale) {
        return d.children
            ? parentColor
            : scale(24 - (d.data['code-maat'] && d.data['code-maat'].ageMonths ? d.data['code-maat'].ageMonths : 24));
    }

}

class AuthorsStrategy {
    constructor() {
        this.domain = [0, 13];
    }

    getColor(d, parentColor, scale) {
        return d.children
            ? parentColor
            : scale(d.data['code-maat'] && d.data['code-maat'].nAuthors ? d.data['code-maat'].nAuthors : 0);
    }

}

export default class TreeMap {
    constructor() {
        this.w = 960;
        this.h = 700;
        this.paddingAllowance = 2;
        //this.color = d3.scale.category10();
        this.redish = d3.rgb("#E60D0D");
        this.blueish = d3.rgb("#0E34E0");
        this.parentStrokeColor = d3.rgb("#4E4545");
        this.parentFillColor = d3.rgb("#7D7E8C");

        this.colorRedToBlueLinearScale = d3.scale.linear()
            .range([this.redish, this.blueish]);
        this.darkerRedToBlueLinearScale = this.colorRedToBlueLinearScale
            .copy()
            .range([this.redish.darker(), this.blueish.darker()]);

        this.treemap = d3.layout.treemap()
            .size([this.w, this.h])
            .padding([20, 4, 4, 4])
            .value(d => d.data.cloc ? d.data.cloc.code : null);

    }

    //outputType can be "age" or "authors"
    render(outputType) {
        var self = this;

        outputType = outputType || "age";

        var strategy = outputType == "age"
            ? new AgeStrategy()
            : new AuthorsStrategy();

        var svg = d3.select("body").append("svg")
            .style("position", "relative")
            .style("width", `${this.w}px`)
            .style("height", `${this.h}px`)
            .append("g")
            .attr("transform", "translate(-.5,-.5)");

        var tooltip = d3
            .select("body")
            .append("div")
            .attr("class", "tooltip")
            .style("opacity", 0);

        var getColor = strategy.getColor;
        this.setScalesBy(strategy);

        d3.json("/data/metrics.json", function(json) {
            var cell = svg.data([json]).selectAll("g")
                .data(self.treemap)
                .enter().append("g")
                .attr("class", "cell")
                .attr("transform", d => "translate(" + d.x + "," + d.y + ")");

            cell.on("mouseover", mouseover)
                .on("mouseout", mouseout);

            cell.append("rect")
                .attr("width", d => d.dx)
                .attr("height", d => d.dy)
                .style("fill", d => getCellFill(d))
                .style("stroke", d => getCellStroke(d))
                .style("z-index", d => -d.depth);

            cell.append("foreignObject")
                .attr("class", "foreignObject")
                .attr("width", d => Math.max(d.dx - self.paddingAllowance, 2))
                .attr("height", d => Math.max(d.dy - self.paddingAllowance, 2))
                .append("xhtml:body")
                .attr("class", "labelbody")
                .append("div")
                .attr("class", "label")
                .text(d => d.name)
                .attr("text-anchor", "middle");

            function formatTooltip(d) {
                return `${d.name}<pre>${JSON.stringify(d.data, null, 2)}</pre>`
            }

            function mouseover(d) {
                const text = Object.keys(d.data).map(key => `${key}:${d.data[key]}`).join("
");
                tooltip.transition()
                    .duration(200);
                tooltip
                    .style("opacity", 0.9);
                tooltip.html(formatTooltip(d))
                    .style("left", (d3.event.pageX) + "px")
                    .style("top", (d3.event.pageY) + "px");
            }

            function mouseout(d) {
                tooltip.transition()
                    .duration(500)
                    .style("opacity", 0);
            }

            function getCellStroke(d) {
                return getColor(d, self.parentStrokeColor, self.darkerRedToBlueLinearScale);
            }

            function getCellFill(d) {
                return getColor(d, self.parentFillColor, self.colorRedToBlueLinearScale);
            }
        });
    }

    setScalesBy(strategy) {
        this.colorRedToBlueLinearScale
            .domain(strategy.domain);
        this.darkerRedToBlueLinearScale
            .domain(strategy.domain);
    }
}

<|endoftext|>