<commit_before>import {int, isBlank} from 'angular2/src/facade/lang';
import {Element} from 'angular2/src/facade/dom';
import {MapWrapper} from 'angular2/src/facade/collection';

import {Parser, Lexer, ChangeDetector, ChangeDetection}
    from 'angular2/change_detection';
import {bootstrap, Component, Template, TemplateConfig, ViewPort, Compiler}
    from 'angular2/angular2';
import {reflector} from 'angular2/src/reflection/reflection';
import {CompilerCache} from 'angular2/src/core/compiler/compiler';
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
import {TemplateLoader} from 'angular2/src/core/compiler/template_loader';
import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';

import {NgIf, NgRepeat} from 'angular2/directives';
import {App, setupReflectorForApp} from './app';
import {ScrollAreaComponent, setupReflectorForScrollArea} from './scroll_area';
import {ScrollItemComponent, setupReflectorForScrollItem} from './scroll_item';
import {CompanyNameComponent, OpportunityNameComponent, OfferingNameComponent,
    AccountCellComponent, StageButtonsComponent, FormattedCellComponent,
    setupReflectorForCells}
        from './cells';

export function main() {
  setupReflector();
  bootstrap(App);
}

export function setupReflector() {
  setupReflectorForAngular();
  setupReflectorForApp();
  setupReflectorForScrollArea();
  setupReflectorForScrollItem();
  setupReflectorForCells();

  // TODO: the transpiler is not able to compiles templates used as keys
  var evt = `$event`;

  reflector.registerGetters({
    'scrollAreas': (o) => o.scrollAreas,
    'length': (o) => o.length,
    'iterable': (o) => o.iterable,
    'scrollArea': (o) => o.scrollArea,
    'item': (o) => o.item,
    'visibleItems': (o) => o.visibleItems,
    'condition': (o) => o.condition,
    'width': (o) => o.width,
    'value': (o) => o.value,
    'href': (o) => o.href,
    'company': (o) => o.company,
    'formattedValue': (o) => o.formattedValue,
    'name': (o) => o.name,
    'style': (o) => o.style,
    'offering': (o) => o.offering,
    'account': (o) => o.account,
    'accountId': (o) => o.accountId,
    'companyNameWidth': (o) => o.companyNameWidth,
    'opportunityNameWidth': (o) => o.opportunityNameWidth,
    'offeringNameWidth': (o) => o.offeringNameWidth,
    'accountCellWidth': (o) => o.accountCellWidth,
    'basePointsWidth': (o) => o.basePointsWidth,
    'scrollDivStyle': (o) => o.scrollDivStyle,
    'paddingStyle': (o) => o.paddingStyle,
    'innerStyle': (o) => o.innerStyle,
    'opportunity': (o) => o.opportunity,
    'itemStyle': (o) => o.itemStyle,
    'dueDateWidth': (o) => o.dueDateWidth,
    'basePoints': (o) => o.basePoints,
    'kickerPoints': (o) => o.kickerPoints,
    'kickerPointsWidth': (o) => o.kickerPointsWidth,
    'bundles': (o) => o.bundles,
    'stageButtonsWidth': (o) => o.stageButtonsWidth,
    'bundlesWidth': (o) => o.bundlesWidth,
    'disabled': (o) => o.disabled,
    'isDisabled': (o) => o.isDisabled,
    'dueDate': (o) => o.dueDate,
    'endDate': (o) => o.endDate,
    'aatStatus': (o) => o.aatStatus,
    'stage': (o) => o.stage,
    'stages': (o) => o.stages,
    'aatStatusWidth': (o) => o.aatStatusWidth,
    'endDateWidth': (o) => o.endDateWidth,
    evt: (o) => null
  });

  reflector.registerSetters({
    'scrollAreas': (o, v) => o.scrollAreas = v,
    'length': (o, v) => o.length = v,
    'condition': (o, v) => o.condition = v,
    'scrollArea': (o, v) => o.scrollArea = v,
    'item': (o, v) => o.item = v,
    'visibleItems': (o, v) => o.visibleItems = v,
    'iterable': (o, v) => o.iterable = v,
    'width': (o, v) => o.width = v,
    'value': (o, v) => o.value = v,
    'company': (o, v) => o.company = v,
    'name': (o, v) => o.name = v,
    'offering': (o, v) => o.offering = v,
    'account': (o, v) => o.account = v,
    'accountId': (o, v) => o.accountId = v,
    'formattedValue': (o, v) => o.formattedValue = v,
    'stage': (o, v) => o.stage = v,
    'stages': (o, v) => o.stages = v,
    'disabled': (o, v) => o.disabled = v,
    'isDisabled': (o, v) => o.isDisabled = v,
    'href': (o, v) => o.href = v,
    'companyNameWidth': (o, v) => o.companyNameWidth = v,
    'opportunityNameWidth': (o, v) => o.opportunityNameWidth = v,
    'offeringNameWidth': (o, v) => o.offeringNameWidth = v,
    'accountCellWidth': (o, v) => o.accountCellWidth = v,
    'basePointsWidth': (o, v) => o.basePointsWidth = v,
    'scrollDivStyle': (o, v) => o.scrollDivStyle = v,
    'paddingStyle': (o, v) => o.paddingStyle = v,
    'innerStyle': (o, v) => o.innerStyle = v,
    'opportunity': (o, v) => o.opportunity = v,
    'itemStyle': (o, v) => o.itemStyle = v,
    'basePoints': (o, v) => o.basePoints = v,
    'kickerPoints': (o, v) => o.kickerPoints = v,
    'kickerPointsWidth': (o, v) => o.kickerPointsWidth = v,
    'stageButtonsWidth': (o, v) => o.stageButtonsWidth = v,
    'dueDate': (o, v) => o.dueDate = v,
    'dueDateWidth': (o, v) => o.dueDateWidth = v,
    'endDate': (o, v) => o.endDate = v,
    'endDateWidth': (o, v) => o.endDate = v,
    'aatStatus': (o, v) => o.aatStatus = v,
    'aatStatusWidth': (o, v) => o.aatStatusWidth = v,
    'bundles': (o, v) => o.bundles = v,
    'bundlesWidth': (o, v) => o.bundlesWidth = v,
    evt: (o, v) => null,
    'style': (o, m) => {
      //if (isBlank(m)) return;
      // HACK
      MapWrapper.forEach(m, function(v, k) {
        o.style.setProperty(k, v);
      });
    }
  });

  reflector.registerMethods({
    'onScroll': (o, args) => {
      // HACK
      o.onScroll(args[0]);
    },
    'setStage': (o, args) => o.setStage(args[0])
  });
}

export function setupReflectorForAngular() {
  reflector.registerType(NgIf, {
    'factory': (vp) => new NgIf(vp),
    'parameters': [[ViewPort]],
    'annotations' : [new Template({
      selector: '[ng-if]',
      bind: {
        'ng-if': 'condition'
      }
    })]
  });

  reflector.registerType(NgRepeat, {
    'factory': (vp) => new NgRepeat(vp),
    'parameters': [[ViewPort]],
    'annotations' : [new Template({
      selector: '[ng-repeat]',
      bind: {
        'in': 'iterable[]'
      }
    })]
  });

  reflector.registerType(Compiler, {
    'factory': (changeDetection, templateLoader, reader, parser, compilerCache) => new Compiler(changeDetection, templateLoader, reader, parser, compilerCache),
    'parameters': [[ChangeDetection], [TemplateLoader], [DirectiveMetadataReader], [Parser], [CompilerCache]],
    'annotations': []
  });

  reflector.registerType(CompilerCache, {
    'factory': () => new CompilerCache(),
    'parameters': [],
    'annotations': []
  });

  reflector.registerType(Parser, {
    'factory': (lexer) => new Parser(lexer),
    'parameters': [[Lexer]],
    'annotations': []
  });

  reflector.registerType(TemplateLoader, {
    'factory': () => new TemplateLoader(),
    'parameters': [],
    'annotations': []
  });

  reflector.registerType(DirectiveMetadataReader, {
    'factory': () => new DirectiveMetadataReader(),
    'parameters': [],
    'annotations': []
  });

  reflector.registerType(Lexer, {
    'factory': () => new Lexer(),
    'parameters': [],
    'annotations': []
  });

  reflector.registerType(LifeCycle, {
    "factory": (cd) => new LifeCycle(cd),
    "parameters": [[ChangeDetector]],
    "annotations": []
  });
}
<commit_msg>fix(benchmarks): fix infinite scroll benchmark.<commit_after>import {int, isBlank} from 'angular2/src/facade/lang';
import {Element} from 'angular2/src/facade/dom';
import {MapWrapper} from 'angular2/src/facade/collection';

import {Parser, Lexer, ChangeDetector, ChangeDetection}
    from 'angular2/change_detection';
import {bootstrap, Component, Template, TemplateConfig, ViewPort, Compiler}
    from 'angular2/angular2';
import {reflector} from 'angular2/src/reflection/reflection';
import {CompilerCache} from 'angular2/src/core/compiler/compiler';
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
import {ShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
import {TemplateLoader} from 'angular2/src/core/compiler/template_loader';
import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';
import {XHR} from 'angular2/src/core/compiler/xhr/xhr';
import {XHRImpl} from 'angular2/src/core/compiler/xhr/xhr_impl';

import {NgIf, NgRepeat} from 'angular2/directives';
import {App, setupReflectorForApp} from './app';
import {ScrollAreaComponent, setupReflectorForScrollArea} from './scroll_area';
import {ScrollItemComponent, setupReflectorForScrollItem} from './scroll_item';
import {CompanyNameComponent, OpportunityNameComponent, OfferingNameComponent,
    AccountCellComponent, StageButtonsComponent, FormattedCellComponent,
    setupReflectorForCells}
        from './cells';

export function main() {
  setupReflector();
  bootstrap(App);
}

export function setupReflector() {
  setupReflectorForAngular();
  setupReflectorForApp();
  setupReflectorForScrollArea();
  setupReflectorForScrollItem();
  setupReflectorForCells();

  // TODO: the transpiler is not able to compiles templates used as keys
  var evt = `$event`;

  reflector.registerGetters({
    'scrollAreas': (o) => o.scrollAreas,
    'length': (o) => o.length,
    'iterable': (o) => o.iterable,
    'scrollArea': (o) => o.scrollArea,
    'item': (o) => o.item,
    'visibleItems': (o) => o.visibleItems,
    'condition': (o) => o.condition,
    'width': (o) => o.width,
    'value': (o) => o.value,
    'href': (o) => o.href,
    'company': (o) => o.company,
    'formattedValue': (o) => o.formattedValue,
    'name': (o) => o.name,
    'style': (o) => o.style,
    'offering': (o) => o.offering,
    'account': (o) => o.account,
    'accountId': (o) => o.accountId,
    'companyNameWidth': (o) => o.companyNameWidth,
    'opportunityNameWidth': (o) => o.opportunityNameWidth,
    'offeringNameWidth': (o) => o.offeringNameWidth,
    'accountCellWidth': (o) => o.accountCellWidth,
    'basePointsWidth': (o) => o.basePointsWidth,
    'scrollDivStyle': (o) => o.scrollDivStyle,
    'paddingStyle': (o) => o.paddingStyle,
    'innerStyle': (o) => o.innerStyle,
    'opportunity': (o) => o.opportunity,
    'itemStyle': (o) => o.itemStyle,
    'dueDateWidth': (o) => o.dueDateWidth,
    'basePoints': (o) => o.basePoints,
    'kickerPoints': (o) => o.kickerPoints,
    'kickerPointsWidth': (o) => o.kickerPointsWidth,
    'bundles': (o) => o.bundles,
    'stageButtonsWidth': (o) => o.stageButtonsWidth,
    'bundlesWidth': (o) => o.bundlesWidth,
    'disabled': (o) => o.disabled,
    'isDisabled': (o) => o.isDisabled,
    'dueDate': (o) => o.dueDate,
    'endDate': (o) => o.endDate,
    'aatStatus': (o) => o.aatStatus,
    'stage': (o) => o.stage,
    'stages': (o) => o.stages,
    'aatStatusWidth': (o) => o.aatStatusWidth,
    'endDateWidth': (o) => o.endDateWidth,
    evt: (o) => null
  });

  reflector.registerSetters({
    'scrollAreas': (o, v) => o.scrollAreas = v,
    'length': (o, v) => o.length = v,
    'condition': (o, v) => o.condition = v,
    'scrollArea': (o, v) => o.scrollArea = v,
    'item': (o, v) => o.item = v,
    'visibleItems': (o, v) => o.visibleItems = v,
    'iterable': (o, v) => o.iterable = v,
    'width': (o, v) => o.width = v,
    'value': (o, v) => o.value = v,
    'company': (o, v) => o.company = v,
    'name': (o, v) => o.name = v,
    'offering': (o, v) => o.offering = v,
    'account': (o, v) => o.account = v,
    'accountId': (o, v) => o.accountId = v,
    'formattedValue': (o, v) => o.formattedValue = v,
    'stage': (o, v) => o.stage = v,
    'stages': (o, v) => o.stages = v,
    'disabled': (o, v) => o.disabled = v,
    'isDisabled': (o, v) => o.isDisabled = v,
    'href': (o, v) => o.href = v,
    'companyNameWidth': (o, v) => o.companyNameWidth = v,
    'opportunityNameWidth': (o, v) => o.opportunityNameWidth = v,
    'offeringNameWidth': (o, v) => o.offeringNameWidth = v,
    'accountCellWidth': (o, v) => o.accountCellWidth = v,
    'basePointsWidth': (o, v) => o.basePointsWidth = v,
    'scrollDivStyle': (o, v) => o.scrollDivStyle = v,
    'paddingStyle': (o, v) => o.paddingStyle = v,
    'innerStyle': (o, v) => o.innerStyle = v,
    'opportunity': (o, v) => o.opportunity = v,
    'itemStyle': (o, v) => o.itemStyle = v,
    'basePoints': (o, v) => o.basePoints = v,
    'kickerPoints': (o, v) => o.kickerPoints = v,
    'kickerPointsWidth': (o, v) => o.kickerPointsWidth = v,
    'stageButtonsWidth': (o, v) => o.stageButtonsWidth = v,
    'dueDate': (o, v) => o.dueDate = v,
    'dueDateWidth': (o, v) => o.dueDateWidth = v,
    'endDate': (o, v) => o.endDate = v,
    'endDateWidth': (o, v) => o.endDate = v,
    'aatStatus': (o, v) => o.aatStatus = v,
    'aatStatusWidth': (o, v) => o.aatStatusWidth = v,
    'bundles': (o, v) => o.bundles = v,
    'bundlesWidth': (o, v) => o.bundlesWidth = v,
    evt: (o, v) => null,
    'style': (o, m) => {
      //if (isBlank(m)) return;
      // HACK
      MapWrapper.forEach(m, function(v, k) {
        o.style.setProperty(k, v);
      });
    }
  });

  reflector.registerMethods({
    'onScroll': (o, args) => {
      // HACK
      o.onScroll(args[0]);
    },
    'setStage': (o, args) => o.setStage(args[0])
  });
}

export function setupReflectorForAngular() {
  reflector.registerType(NgIf, {
    'factory': (vp) => new NgIf(vp),
    'parameters': [[ViewPort]],
    'annotations' : [new Template({
      selector: '[ng-if]',
      bind: {
        'ng-if': 'condition'
      }
    })]
  });

  reflector.registerType(NgRepeat, {
    'factory': (vp) => new NgRepeat(vp),
    'parameters': [[ViewPort]],
    'annotations' : [new Template({
      selector: '[ng-repeat]',
      bind: {
        'in': 'iterable[]'
      }
    })]
  });

  reflector.registerType(Compiler, {
    "factory": (changeDetection, templateLoader, reader, parser, compilerCache, shadowDomStrategy) =>
      new Compiler(changeDetection, templateLoader, reader, parser, compilerCache, shadowDomStrategy),
    "parameters": [[ChangeDetection], [TemplateLoader], [DirectiveMetadataReader], [Parser],
                   [CompilerCache], [ShadowDomStrategy]],
    "annotations": []
  });

  reflector.registerType(CompilerCache, {
    'factory': () => new CompilerCache(),
    'parameters': [],
    'annotations': []
  });

  reflector.registerType(Parser, {
    'factory': (lexer) => new Parser(lexer),
    'parameters': [[Lexer]],
    'annotations': []
  });

  reflector.registerType(TemplateLoader, {
    "factory": (xhr) => new TemplateLoader(xhr),
    "parameters": [[XHR]],
    "annotations": []
  });

  reflector.registerType(XHR, {
    "factory": () => new XHRImpl(),
    "parameters": [],
    "annotations": []
  });

  reflector.registerType(DirectiveMetadataReader, {
    'factory': () => new DirectiveMetadataReader(),
    'parameters': [],
    'annotations': []
  });

  reflector.registerType(Lexer, {
    'factory': () => new Lexer(),
    'parameters': [],
    'annotations': []
  });

  reflector.registerType(LifeCycle, {
    "factory": (cd) => new LifeCycle(cd),
    "parameters": [[ChangeDetector]],
    "annotations": []
  });

  reflector.registerType(ShadowDomStrategy, {
    "factory": () => new NativeShadowDomStrategy(),
    "parameters": [],
    "annotations": []
  });

}
<|endoftext|>


<commit_before>/* global describe:true, it:true */

// Utilities:
import chai from 'chai';

// Test setup:
const expect = chai.expect;

// Under test:
import serve from './serve';

describe('tractor-plugin-screen-size - serve:', () => {
    it('should do nothing if there is no config', () => {
        let config = {};

        expect(() => {
            serve(null, config);
        }).to.not.throw();
    });

    it('should add tags for each screen size in the config', () => {
        let config = {
            tags: [],
            screenSizes: {
                sm: 360,
                md: 768
            }
        };

        serve(null, config);

        expect(config.tags.length).to.equal(2);

        let [smTag, mdTag] = config.tags;

        expect(smTag).to.equal('@screen-size:sm');
        expect(mdTag).to.equal('@screen-size:md');
    });
});
<commit_msg>(tests :rotating_light:) - fixing broken unit tests<commit_after>/* global describe:true, it:true */

// Utilities:
import chai from 'chai';

// Test setup:
const expect = chai.expect;

// Under test:
import serve from './serve';

describe('tractor-plugin-screen-size - serve:', () => {
    it('should do nothing if there is no config', () => {
        let config = {};

        expect(() => {
            serve(null, null, config);
        }).to.not.throw();
    });

    it('should add tags for each screen size in the config', () => {
        let config = {
            tags: [],
            screenSizes: {
                sm: 360,
                md: 768
            }
        };

        serve(null, null, config);

        expect(config.tags.length).to.equal(2);

        let [smTag, mdTag] = config.tags;

        expect(smTag).to.equal('@screen-size:sm');
        expect(mdTag).to.equal('@screen-size:md');
    });
});
<|endoftext|>


<commit_before>import React, { useEffect } from 'react'
import classnames from 'classnames'
import Layout from '@theme/Layout'
import Link from '@docusaurus/Link'
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'
import useBaseUrl from '@docusaurus/useBaseUrl'
import useThemeContext from '@theme/hooks/useThemeContext'
import styles from './styles.module.css'

const features = [
  {
    content: (

        React Redux is maintained by the Redux team, and kept up-to-date with the latest APIs from Redux and React.

    ),
    image: ,
    imageAlign: 'top',
    title: 'Official'
  },
  {
    content: (

        Designed to work with React's component model.
        {' '} You define how to extract the values your component needs from Redux, and your component receives them as props.",

    ),
    image: ,
    imageAlign: 'top',
    title: 'Predictable'
  },
  {
    content: (

        Creates wrapper components that manage the store interaction logic for you, so you don't have to write it yourself.

    ),
    image: ,
    imageAlign: 'top',
    title: 'Encapsulated'
  },
  {
    content: (

        Automatically implements complex performance optimizations, so that your own component only re-renders when the data it needs has actually changed.',

    ),
    image: ,
    imageAlign: 'top',
    title: 'Optimized'
  },
]

const otherLibraries = [
  {
    content: 'A predictable state container for JavaScript applications',
    title: 'Redux',
    link: 'https://redux.js.org',
    image: (
      <svg
        xmlns="http://www.w3.org/2000/svg"
        aria-hidden="true"
        data-icon="external-link-square-alt"
        data-prefix="fas"
        viewBox="0 0 448 512"
      >
        <path d="M448 80v352c0 26.51-21.49 48-48 48H48c-26.51 0-48-21.49-48-48V80c0-26.51 21.49-48 48-48h352c26.51 0 48 21.49 48 48zm-88 16H248.029c-21.313 0-32.08 25.861-16.971 40.971l31.984 31.987L67.515 364.485c-4.686 4.686-4.686 12.284 0 16.971l31.029 31.029c4.687 4.686 12.285 4.686 16.971 0l195.526-195.526 31.988 31.991C358.058 263.977 384 253.425 384 231.979V120c0-13.255-10.745-24-24-24z"></path>
      </svg>
    ),
  },
  {
    content: 'The official, opinionated, batteries-included toolset for efficient Redux development',
    title: 'Redux Toolkit',
    link: 'https://redux-toolkit.js.org',
    image: (
      <svg
        xmlns="http://www.w3.org/2000/svg"
        aria-hidden="true"
        data-icon="external-link-square-alt"
        data-prefix="fas"
        viewBox="0 0 448 512"
      >
        <path d="M448 80v352c0 26.51-21.49 48-48 48H48c-26.51 0-48-21.49-48-48V80c0-26.51 21.49-48 48-48h352c26.51 0 48 21.49 48 48zm-88 16H248.029c-21.313 0-32.08 25.861-16.971 40.971l31.984 31.987L67.515 364.485c-4.686 4.686-4.686 12.284 0 16.971l31.029 31.029c4.687 4.686 12.285 4.686 16.971 0l195.526-195.526 31.988 31.991C358.058 263.977 384 253.425 384 231.979V120c0-13.255-10.745-24-24-24z"></path>
      </svg>
    ),
  },
]

function Home() {
  const context = useDocusaurusContext()
  const { siteConfig = {} } = context
  useEffect(() => {
    const script = document.createElement("script");
    script.src = "https://buttons.github.io/buttons.js";
    script.async = true;
    document.body.appendChild(script);
  }, [])

  return (
    <Layout title={siteConfig.title} description={siteConfig.tagline}>

            Black Lives Matter.

              Support the Equal Justice Initiative.

            <h1 className={`${styles.projectTitle} hero__title`}>
              {siteConfig.title}

{siteConfig.tagline}

            <Link
              className={classnames(
                'button button--secondary button--lg',
                styles.getStarted,
              )}
              to={useBaseUrl('introduction/quick-start')}
            >
              Get Started
            </Link>

        {features && features.length && (

                {features.map(({ image, title, content }, idx) => (

                    {image && (
                      <div className={`text--center ${styles.blockImage}`}>
                        {image}

                    )}
                    <h2 className={`text--center ${styles.featureTitle}`}>
                      {title}

{content}

                ))}

        )}
        {otherLibraries && otherLibraries.length && (

                  <h2 className={`text--center ${styles.secondTitle}`}>
                    Other Libraries from the Redux Team

                {otherLibraries.map(({ image, title, content, link }, idx) => (

                        {title}
                        {image}

{content}

                ))}

        )}

    </Layout>
  )
}

export default Home;
<commit_msg>Site: Remove extra chars at main page (#1658)<commit_after>import React, { useEffect } from 'react'
import classnames from 'classnames'
import Layout from '@theme/Layout'
import Link from '@docusaurus/Link'
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'
import useBaseUrl from '@docusaurus/useBaseUrl'
import useThemeContext from '@theme/hooks/useThemeContext'
import styles from './styles.module.css'

const features = [
  {
    content: (

        React Redux is maintained by the Redux team, and kept up-to-date with the latest APIs from Redux and React.

    ),
    image: ,
    imageAlign: 'top',
    title: 'Official'
  },
  {
    content: (

        Designed to work with React's component model.
        {' '} You define how to extract the values your component needs from Redux, and your component receives them as props.

    ),
    image: ,
    imageAlign: 'top',
    title: 'Predictable'
  },
  {
    content: (

        Creates wrapper components that manage the store interaction logic for you, so you don't have to write it yourself.

    ),
    image: ,
    imageAlign: 'top',
    title: 'Encapsulated'
  },
  {
    content: (

        Automatically implements complex performance optimizations, so that your own component only re-renders when the data it needs has actually changed.

    ),
    image: ,
    imageAlign: 'top',
    title: 'Optimized'
  },
]

const otherLibraries = [
  {
    content: 'A predictable state container for JavaScript applications',
    title: 'Redux',
    link: 'https://redux.js.org',
    image: (
      <svg
        xmlns="http://www.w3.org/2000/svg"
        aria-hidden="true"
        data-icon="external-link-square-alt"
        data-prefix="fas"
        viewBox="0 0 448 512"
      >
        <path d="M448 80v352c0 26.51-21.49 48-48 48H48c-26.51 0-48-21.49-48-48V80c0-26.51 21.49-48 48-48h352c26.51 0 48 21.49 48 48zm-88 16H248.029c-21.313 0-32.08 25.861-16.971 40.971l31.984 31.987L67.515 364.485c-4.686 4.686-4.686 12.284 0 16.971l31.029 31.029c4.687 4.686 12.285 4.686 16.971 0l195.526-195.526 31.988 31.991C358.058 263.977 384 253.425 384 231.979V120c0-13.255-10.745-24-24-24z"></path>
      </svg>
    ),
  },
  {
    content: 'The official, opinionated, batteries-included toolset for efficient Redux development',
    title: 'Redux Toolkit',
    link: 'https://redux-toolkit.js.org',
    image: (
      <svg
        xmlns="http://www.w3.org/2000/svg"
        aria-hidden="true"
        data-icon="external-link-square-alt"
        data-prefix="fas"
        viewBox="0 0 448 512"
      >
        <path d="M448 80v352c0 26.51-21.49 48-48 48H48c-26.51 0-48-21.49-48-48V80c0-26.51 21.49-48 48-48h352c26.51 0 48 21.49 48 48zm-88 16H248.029c-21.313 0-32.08 25.861-16.971 40.971l31.984 31.987L67.515 364.485c-4.686 4.686-4.686 12.284 0 16.971l31.029 31.029c4.687 4.686 12.285 4.686 16.971 0l195.526-195.526 31.988 31.991C358.058 263.977 384 253.425 384 231.979V120c0-13.255-10.745-24-24-24z"></path>
      </svg>
    ),
  },
]

function Home() {
  const context = useDocusaurusContext()
  const { siteConfig = {} } = context
  useEffect(() => {
    const script = document.createElement("script");
    script.src = "https://buttons.github.io/buttons.js";
    script.async = true;
    document.body.appendChild(script);
  }, [])

  return (
    <Layout title={siteConfig.title} description={siteConfig.tagline}>

            Black Lives Matter.

              Support the Equal Justice Initiative.

            <h1 className={`${styles.projectTitle} hero__title`}>
              {siteConfig.title}

{siteConfig.tagline}

            <Link
              className={classnames(
                'button button--secondary button--lg',
                styles.getStarted,
              )}
              to={useBaseUrl('introduction/quick-start')}
            >
              Get Started
            </Link>

        {features && features.length && (

                {features.map(({ image, title, content }, idx) => (

                    {image && (
                      <div className={`text--center ${styles.blockImage}`}>
                        {image}

                    )}
                    <h2 className={`text--center ${styles.featureTitle}`}>
                      {title}

{content}

                ))}

        )}
        {otherLibraries && otherLibraries.length && (

                  <h2 className={`text--center ${styles.secondTitle}`}>
                    Other Libraries from the Redux Team

                {otherLibraries.map(({ image, title, content, link }, idx) => (

                        {title}
                        {image}

{content}

                ))}

        )}

    </Layout>
  )
}

export default Home;
<|endoftext|>


<commit_before>'use strict';

const req = require('req');

const config = require('../config'),
    templates = require('./templates');

const daysEnum = {
    TODAY: 0,
    TOMORROW: 1,
    IN_2_DAYS: 2,
    IN_3_DAYS: 3,
    IN_4_DAYS: 4,
    IN_5_DAYS: 5,
    IN_6_DAYS: 6,
    IN_7_DAYS: 7,
    IN_8_DAYS: 8
};

const callAPI = async (city, countryCode) => {
    const api = `http://api.openweathermap.org/data/2.5/forecast/daily/?q=${city},${countryCode}&APPID=${config.weatherApiKey}&units=metric&lang=es`;
    const body = await req(api);
    return JSON.parse(body);
};

const getForecast = async (city, countryCode, day) => {
    const result = await callAPI(city, countryCode);
    return day === null ? result.list[daysEnum.TOMORROW] : result.list[day];
};

const getForecastMessage = async (city, countryCode, day) => {
    const forecast = await getForecast(city, countryCode, day);
    let message;
    try {
        message = templates.weatherReport(forecast, city);
    } catch (err) {
        message = templates.errorMessage;
    }

    return message;
};

const getForecastWarnMessage = async (city, countryCode) => {
    const forecast = await getForecast(city, countryCode, daysEnum.TOMORROW);
    const [ weatherCode ] = forecast.weather[0].id.toString();

    let message = null;
    if (weatherCode === '8' || weatherCode === '9') {
        try {
            message = templates.weatherReport(forecast, city);
        } catch (err) {
            message = templates.errorMessage;
        }
    }

    return message;
};

module.exports = { daysEnum, getForecast, getForecastMessage, getForecastWarnMessage };
<commit_msg>:bug: Fixed bug where notifications where being sent on clear sky instead of bad weather.<commit_after>'use strict';

const req = require('req');

const config = require('../config'),
    templates = require('./templates');

const daysEnum = {
    TODAY: 0,
    TOMORROW: 1,
    IN_2_DAYS: 2,
    IN_3_DAYS: 3,
    IN_4_DAYS: 4,
    IN_5_DAYS: 5,
    IN_6_DAYS: 6,
    IN_7_DAYS: 7,
    IN_8_DAYS: 8
};

const callAPI = async (city, countryCode) => {
    const api = `http://api.openweathermap.org/data/2.5/forecast/daily/?q=${city},${countryCode}&APPID=${config.weatherApiKey}&units=metric&lang=es`;
    const body = await req(api);
    return JSON.parse(body);
};

const getForecast = async (city, countryCode, day) => {
    const result = await callAPI(city, countryCode);
    return day === null ? result.list[daysEnum.TOMORROW] : result.list[day];
};

const getForecastMessage = async (city, countryCode, day) => {
    const forecast = await getForecast(city, countryCode, day);
    let message;
    try {
        message = templates.weatherReport(forecast, city);
    } catch (err) {
        message = templates.errorMessage;
    }

    return message;
};

const getForecastWarnMessage = async (city, countryCode) => {
    const forecast = await getForecast(city, countryCode, daysEnum.TOMORROW);
    const [ weatherCode ] = forecast.weather[0].id.toString();

    let message = null;
    // Note: Codes such as 8xx represent clear sky. https://openweathermap.org/weather-conditions
    if (weatherCode !== '8') {
        try {
            message = templates.weatherReport(forecast, city);
        } catch (err) {
            message = templates.errorMessage;
        }
    }

    return message;
};

module.exports = { daysEnum, getForecast, getForecastMessage, getForecastWarnMessage };
<|endoftext|>


<commit_before>import { GET, POST, PUT } from '../util/requests'
import JsContext from '../js-context/JsContext'
import ContextHttpClient from '../context/ContextHttpClient'
import MemoryBuffer from '../backend/MemoryBuffer'

/**
 * Each Stencila process has a single instance of the `Host` class which
 * orchestrates instances of other classes.
 */
export default class Host {

  constructor (options = {}) {
    /**
     * Instances managed by this host
     *
     * @type {object}
     */
    this._instances = {}

    /**
     * Peer manifests which detail the capabilities
     * of each of this host's peers
     *
     * @type {object}
     */
    this._peers = {}

    // Add the Host who served this file as a peer
    if (options.mode !== 'dev' && typeof window !== 'undefined') this.pokePeer(window.location.origin)

    // Discover other peers
    if (options.discover) this.discoverPeers(options.discover)
  }

  /**
   * Create a new instance
   *
   * @param  {string} type - Name of class of instance
   * @param  {string} name - Name for new instance
   * @return {Promise} Resolves to an instance
   */
  post (type, name) {
    for (let url of Object.keys(this._peers)) {
      let manifest = this._peers[url]

      // Gather an object of types from the peer and it's peers
      let types = []
      let addTypes = function (manifest) {
        if (manifest.schemes && manifest.schemes.new) {
          for (let type of Object.keys(manifest.schemes.new)) {
            types.push(manifest.schemes.new[type])
          }
        }
      }
      addTypes(manifest)
      for (let peer of manifest.peers) addTypes(peer)

      // Find a type spec that has a matching name or alias
      let spec
      for (let trial of types) {
        if (trial.name === type) {
          spec = trial
        } else if (trial.aliases) {
          if (trial.aliases.indexOf(type) >= 0) {
            spec = trial
            break
          }
        }
        if (spec) break
      }

      // Type found so request a new one
      if (spec) {
        return POST(url + '/' + spec.name, { name: name }).then(address => {
          let instance
          if (spec.base === 'Context') {
            instance = new ContextHttpClient(url + '/' + address)
          } else {
            throw new Error(`Unsupported type: %{path}`)
          }
          this._instances[address] = instance
          return instance
        })
      }
    }

    // Fallback to providing an in-browser Javascript context
    // (if one is available in a Node.js peer then it will be used instead)
    if (type === 'JsContext' || type === 'js') {
      let instance = new JsContext()
      let address = `name://${name || Math.floor((1 + Math.random()) * 1e6).toString(16)}`
      this._instances[address] = instance
      return Promise.resolve(instance)
    }

    return Promise.reject(new Error(`No peers able to provide: ${type}`))
  }

  /**
   * Get an instance
   * @param  {string} address - Address of the instance
   * @return {Promise} Resolves to an instance
   */
  get (address) {
    return Promise.resolve(this._instances[address])
  }

  /**
   * Get this host's peers
   */
  get peers () {
    return this._peers
  }

  /**
   * Register a peer
   *
   * Peers may have several URLs (listed in the manifest's `urls` property; e.g. http://, ws://).
   * The URL used to register a peer is a unique identier used by this host and is
   * usually the URL that the peer was discoved on.
   *
   * @param {string} url - The identifying URL for the peer
   * @param {object} manifest - The peer's manifest
   */
  registerPeer (url, manifest) {
    this._peers[url] = manifest
  }

  /**
   * Pokes a URL to see if it is a Stencila Host
   *
   * @param {string} url - A URL for the peer
   */
  pokePeer (url) {
    GET(url).then(manifest => {
      // Register if this is a Stencila Host manifest
      if (manifest.stencila) this.registerPeer(url, manifest)
    })
  }

  /**
   * Discover peers
   *
   * Currently, this method just does a port scan on the localhost to find
   * peers. More sophisticated peer discovery mechanisms for remote peers
   * will be implemented later.
   *
   * Unfortunately if a port is not open then you'll get a console error like
   * `GET http://127.0.0.1:2040/ net::ERR_CONNECTION_REFUSED`. In Chrome, this can
   * not be avoided programatically (see http://stackoverflow.com/a/43056626/4625911).
   * The easiest approach is silence these errors in Chrome is to check the
   * 'Hide network' checkbox in the console filter.
   *
   * Set the `interval` parameter to trigger ongoing discovery.
   *
   * @param {number} interval - The interval (seconds) between discovery attempts
   */
  discoverPeers (interval=10) {
    for (let port=2000; port<=2100; port+=10) {
      this.pokePeer(`http://127.0.0.1:${port}`)
    }
    if (interval) setTimeout(() => this.discoverPeers(interval), interval*1000)
  }

  // Experimental
  // Implements methods of `Backend` so that this `Host` can serve as a backend
  // Towards merging these two classes

  getBuffer(address) {
    // TODO this PUTs to the current server but it could be some other peer
    return PUT(`/${address}!buffer`).then(data => {
      let buffer = new MemoryBuffer()

      buffer.writeFile('stencila-manifest.json', 'application/json', JSON.stringify({
        type: 'document',
        title: 'Untitled',
        createdAt: '2017-03-10T00:03:12.060Z',
        updatedAt: '2017-03-10T00:03:12.060Z',
        storage: {
          storerType: "filesystem",
          contentType: "html",
          archivePath: "",
          mainFilePath: "index.html"
        }
      }))

      buffer.writeFile('index.html', 'text/html', data['index.html'])

      return buffer
    })
  }

  storeBuffer(/*buffer*/) {
    return Promise.resolve()
  }

  updateManifest(/* documentId, props */) {
    return Promise.resolve()
  }

}
<commit_msg>Alternative peer discovery mechanism<commit_after>import { GET, POST, PUT } from '../util/requests'
import JsContext from '../js-context/JsContext'
import ContextHttpClient from '../context/ContextHttpClient'
import MemoryBuffer from '../backend/MemoryBuffer'

/**
 * Each Stencila process has a single instance of the `Host` class which
 * orchestrates instances of other classes.
 */
export default class Host {

  constructor (options = {}) {
    /**
     * Instances managed by this host
     *
     * @type {object}
     */
    this._instances = {}

    /**
     * Peer manifests which detail the capabilities
     * of each of this host's peers
     *
     * @type {object}
     */
    this._peers = {}

    // Add the Host who served this file as a peer
    if (options.mode !== 'dev' && typeof window !== 'undefined') this.pokePeer(window.location.origin)

    // Discover other peers
    if (options.discover) this.discoverPeers(options.discover)
  }

  /**
   * Create a new instance
   *
   * @param  {string} type - Name of class of instance
   * @param  {string} name - Name for new instance
   * @return {Promise} Resolves to an instance
   */
  post (type, name) {
    // Search for the type amongst peers or peers-of-peers
    let find = (peersOfPeers) => {
      for (let url of Object.keys(this._peers)) {
        let manifest = this._peers[url]

        // Gather an object of types from the peer and it's peers
        let specs = []
        let addSpecs = (manifest) => {
          if (manifest.schemes && manifest.schemes.new) {
            for (let type of Object.keys(manifest.schemes.new)) {
              specs.push(manifest.schemes.new[type])
            }
          }
        }
        if (!peersOfPeers) {
          addSpecs(manifest)
        } else if (manifest.peers) {
          for (let peer of manifest.peers) addSpecs(peer)
        }

        // Find a type that has a matching name or alias
        for (let spec of specs) {
          if (spec.name === type) {
            return {url, spec}
          } else if (spec.aliases) {
            if (spec.aliases.indexOf(type) >= 0) {
              return {url, spec}
            }
          }
        }

        return null
      }
    }

    // Request a new instance from peer (or peer or peer)
    let request = (url, spec) => {
      return POST(url + '/' + spec.name, { name: name }).then(address => {
        let instance
        if (spec.base === 'Context') {
          instance = new ContextHttpClient(url + '/' + address)
        } else {
          throw new Error(`Unsupported type: %{path}`)
        }
        this._instances[address] = instance
        return instance
      })
    }

    // Attempt to find type
    let found = find(false)
    if (!found) found = find(true)
    if (found) return request(found.url, found.spec)

    // Fallback to providing an in-browser Javascript context
    // (if one is available in a Node.js peer then it will be used instead)
    if (type === 'JsContext' || type === 'js') {
      let instance = new JsContext()
      let address = `name://${name || Math.floor((1 + Math.random()) * 1e6).toString(16)}`
      this._instances[address] = instance
      return Promise.resolve(instance)
    }

    return Promise.reject(new Error(`No peers able to provide: ${type}`))
  }

  /**
   * Get an instance
   * @param  {string} address - Address of the instance
   * @return {Promise} Resolves to an instance
   */
  get (address) {
    return Promise.resolve(this._instances[address])
  }

  /**
   * Get this host's peers
   */
  get peers () {
    return this._peers
  }

  /**
   * Register a peer
   *
   * Peers may have several URLs (listed in the manifest's `urls` property; e.g. http://, ws://).
   * The URL used to register a peer is a unique identier used by this host and is
   * usually the URL that the peer was discoved on.
   *
   * @param {string} url - The identifying URL for the peer
   * @param {object} manifest - The peer's manifest
   */
  registerPeer (url, manifest) {
    this._peers[url] = manifest
  }

  /**
   * Pokes a URL to see if it is a Stencila Host
   *
   * @param {string} url - A URL for the peer
   */
  pokePeer (url) {
    GET(url).then(manifest => {
      // Register if this is a Stencila Host manifest
      if (manifest.stencila) this.registerPeer(url, manifest)
    })
  }

  /**
   * Discover peers
   *
   * Currently, this method just does a port scan on the localhost to find
   * peers. More sophisticated peer discovery mechanisms for remote peers
   * will be implemented later.
   *
   * Unfortunately if a port is not open then you'll get a console error like
   * `GET http://127.0.0.1:2040/ net::ERR_CONNECTION_REFUSED`. In Chrome, this can
   * not be avoided programatically (see http://stackoverflow.com/a/43056626/4625911).
   * The easiest approach is silence these errors in Chrome is to check the
   * 'Hide network' checkbox in the console filter.
   *
   * Set the `interval` parameter to trigger ongoing discovery.
   *
   * @param {number} interval - The interval (seconds) between discovery attempts
   */
  discoverPeers (interval=10) {
    for (let port=2000; port<=2100; port+=10) {
      this.pokePeer(`http://127.0.0.1:${port}`)
    }
    if (interval) setTimeout(() => this.discoverPeers(interval), interval*1000)
  }

  // Experimental
  // Implements methods of `Backend` so that this `Host` can serve as a backend
  // Towards merging these two classes

  getBuffer(address) {
    // TODO this PUTs to the current server but it could be some other peer
    return PUT(`/${address}!buffer`).then(data => {
      let buffer = new MemoryBuffer()

      buffer.writeFile('stencila-manifest.json', 'application/json', JSON.stringify({
        type: 'document',
        title: 'Untitled',
        createdAt: '2017-03-10T00:03:12.060Z',
        updatedAt: '2017-03-10T00:03:12.060Z',
        storage: {
          storerType: "filesystem",
          contentType: "html",
          archivePath: "",
          mainFilePath: "index.html"
        }
      }))

      buffer.writeFile('index.html', 'text/html', data['index.html'])

      return buffer
    })
  }

  storeBuffer(/*buffer*/) {
    return Promise.resolve()
  }

  updateManifest(/* documentId, props */) {
    return Promise.resolve()
  }

}
<|endoftext|>


<commit_before>var queryView = Backbone.View.extend({
  el: '#content',
  init: function () {
  },
  events: {
    'click #submitQueryIcon'   : 'submitQuery',
    'click #submitQueryButton' : 'submitQuery',
    'click .clearicon': 'clearOutput',
  },
  clearOutput: function() {
    $('#queryOutput').empty();
  },

  template: new EJS({url: '/_admin/html/js/templates/queryView.ejs'}),

  render: function() {
    $(this.el).html(this.template.text);
    var editor = ace.edit("aqlEditor");
    var editor2 = ace.edit("queryOutput");

    editor2.setReadOnly(true);
    editor2.setHighlightActiveLine(false);

    editor.getSession().setMode("ace/mode/javascript");
    editor2.getSession().setMode("ace/mode/javascript");
    editor.resize();
    editor2.setValue('');
    editor2.resize();

    $('#queryOutput').resizable({
      handles: "n, s",
      ghost: true,
      stop: function () {
        var editor2 = ace.edit("queryOutput");
        editor2.resize();
      }
    });
    $('#aqlEditor').resizable({
      handles: "n, s",
      ghost: true,
      helper: "resizable-helper",
      stop: function () {
        var editor = ace.edit("aqlEditor");
        editor.resize();
      }
    });

    $('#aqlEditor .ace_text-input').focus();
    return this;
  },
  submitQuery: function() {
    var self = this;
    var editor = ace.edit("aqlEditor");
    var data = {query: editor.getValue()};

    var editor2 = ace.edit("queryOutput");

    $.ajax({
      type: "POST",
      url: "/_api/cursor",
      data: JSON.stringify(data),
      contentType: "application/json",
      processData: false,
      success: function(data) {
        editor2.setValue(self.FormatJSON(data.result));
      },
      error: function(data) {
        try {
          var temp = JSON.parse(data.responseText);
          editor2.setValue('[' + temp.errorNum + '] ' + temp.errorMessage);
        }
        catch (e) {
          editor2.setValue('ERROR');
        }
      }
    });
  },
  FormatJSON: function(oData, sIndent) {
    var self = this;
    if (arguments.length < 2) {
      var sIndent = "";
    }
    var sIndentStyle = "    ";
    var sDataType = self.RealTypeOf(oData);

    if (sDataType == "array") {
      if (oData.length == 0) {
        return "[]";
      }
      var sHTML = "[";
    } else {
      var iCount = 0;
      $.each(oData, function() {
        iCount++;
        return;
      });
      if (iCount == 0) { // object is empty
        return "{}";
      }
      var sHTML = "{";
    }

    // loop through items
    var iCount = 0;
    $.each(oData, function(sKey, vValue) {
      if (iCount > 0) {
        sHTML += ",";
      }
      if (sDataType == "array") {
        sHTML += ("\n" + sIndent + sIndentStyle);
      } else {
        sHTML += ("\n" + sIndent + sIndentStyle + JSON.stringify(sKey) + ": ");
      }

      // display relevant data type
      switch (self.RealTypeOf(vValue)) {
        case "array":
          case "object":
          sHTML += self.FormatJSON(vValue, (sIndent + sIndentStyle));
        break;
        case "boolean":
          case "number":
          sHTML += vValue.toString();
        break;
        case "null":
          sHTML += "null";
        break;
        case "string":
          sHTML += "\"" + vValue.replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\"";
        break;
        default:
          sHTML += ("TYPEOF: " + typeof(vValue));
      }

      // loop
      iCount++;
    });

    // close object
    if (sDataType == "array") {
      sHTML += ("\n" + sIndent + "]");
    } else {
      sHTML += ("\n" + sIndent + "}");
    }

    // return
    return sHTML;
  },
  RealTypeOf: function(v) {
    if (typeof(v) == "object") {
      if (v === null) return "null";
      if (v.constructor == (new Array).constructor) return "array";
      if (v.constructor == (new Date).constructor) return "date";
      if (v.constructor == (new RegExp).constructor) return "regex";
      return "object";
    }
    return typeof(v);
  }

});
<commit_msg>aql css<commit_after>var queryView = Backbone.View.extend({
  el: '#content',
  init: function () {
  },
  events: {
    'click #submitQueryIcon'   : 'submitQuery',
    'click #submitQueryButton' : 'submitQuery',
    'click .clearicon': 'clearOutput',
  },
  clearOutput: function() {
    $('#queryOutput').empty();
  },

  template: new EJS({url: '/_admin/html/js/templates/queryView.ejs'}),

  render: function() {
    $(this.el).html(this.template.text);
    var editor = ace.edit("aqlEditor");
    var editor2 = ace.edit("queryOutput");

    editor2.setReadOnly(true);
    editor2.setHighlightActiveLine(false);

    editor.getSession().setMode("ace/mode/javascript");
    editor2.getSession().setMode("ace/mode/javascript");
    editor.setTheme("ace/theme/merbivore_soft");
    editor.resize();
    editor2.setValue('');
    editor2.resize();

    $('#queryOutput').resizable({
      handles: "n, s",
      ghost: true,
      stop: function () {
        var editor2 = ace.edit("queryOutput");
        editor2.resize();
      }
    });
    $('#aqlEditor').resizable({
      handles: "n, s",
      ghost: true,
      helper: "resizable-helper",
      stop: function () {
        var editor = ace.edit("aqlEditor");
        editor.resize();
      }
    });

    $('#aqlEditor .ace_text-input').focus();
    return this;
  },
  submitQuery: function() {
    var self = this;
    var editor = ace.edit("aqlEditor");
    var data = {query: editor.getValue()};

    var editor2 = ace.edit("queryOutput");

    $.ajax({
      type: "POST",
      url: "/_api/cursor",
      data: JSON.stringify(data),
      contentType: "application/json",
      processData: false,
      success: function(data) {
        editor2.setValue(self.FormatJSON(data.result));
      },
      error: function(data) {
        try {
          var temp = JSON.parse(data.responseText);
          editor2.setValue('[' + temp.errorNum + '] ' + temp.errorMessage);
        }
        catch (e) {
          editor2.setValue('ERROR');
        }
      }
    });
  },
  FormatJSON: function(oData, sIndent) {
    var self = this;
    if (arguments.length < 2) {
      var sIndent = "";
    }
    var sIndentStyle = "    ";
    var sDataType = self.RealTypeOf(oData);

    if (sDataType == "array") {
      if (oData.length == 0) {
        return "[]";
      }
      var sHTML = "[";
    } else {
      var iCount = 0;
      $.each(oData, function() {
        iCount++;
        return;
      });
      if (iCount == 0) { // object is empty
        return "{}";
      }
      var sHTML = "{";
    }

    // loop through items
    var iCount = 0;
    $.each(oData, function(sKey, vValue) {
      if (iCount > 0) {
        sHTML += ",";
      }
      if (sDataType == "array") {
        sHTML += ("\n" + sIndent + sIndentStyle);
      } else {
        sHTML += ("\n" + sIndent + sIndentStyle + JSON.stringify(sKey) + ": ");
      }

      // display relevant data type
      switch (self.RealTypeOf(vValue)) {
        case "array":
          case "object":
          sHTML += self.FormatJSON(vValue, (sIndent + sIndentStyle));
        break;
        case "boolean":
          case "number":
          sHTML += vValue.toString();
        break;
        case "null":
          sHTML += "null";
        break;
        case "string":
          sHTML += "\"" + vValue.replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\"";
        break;
        default:
          sHTML += ("TYPEOF: " + typeof(vValue));
      }

      // loop
      iCount++;
    });

    // close object
    if (sDataType == "array") {
      sHTML += ("\n" + sIndent + "]");
    } else {
      sHTML += ("\n" + sIndent + "}");
    }

    // return
    return sHTML;
  },
  RealTypeOf: function(v) {
    if (typeof(v) == "object") {
      if (v === null) return "null";
      if (v.constructor == (new Array).constructor) return "array";
      if (v.constructor == (new Date).constructor) return "date";
      if (v.constructor == (new RegExp).constructor) return "regex";
      return "object";
    }
    return typeof(v);
  }

});
<|endoftext|>


<commit_before>define
([
    'jquery', 'jquery/superdesk', 'dust/core', 'utils/sha512', 'jquery/tmpl', 'jquery/rest', 'bootstrap',
    'tmpl!auth',
],
function($, superdesk, dust, jsSHA)
{
<<<<<<< HEAD
    var AuthLogin = function(username, password, logintoken){
=======
    var AuthDetails = function(username){
		var authDetails = new $.rest('Superdesk/User');
		authDetails.resetData().xfilter('Name,Id,EMail').select({ name: username }).done(function(users){
			var user = users.UserList[0];
			localStorage.setItem('superdesk.login.id', user.Id);
			localStorage.setItem('superdesk.login.name', user.Name);
			localStorage.setItem('superdesk.login.email', user.EMail);
		});
		return $(authDetails);
	},
	AuthLogin = function(username, password, logintoken){
>>>>>>> 718d8c014dc7f1c47606de731769286a6bcebb81
		var shaObj = new jsSHA(logintoken, "ASCII"),shaPassword = new jsSHA(password, "ASCII"),
			authLogin = new $.rest('Authentication');
			authLogin.resetData().insert({
			UserName: username,
			LoginToken: logintoken,
			HashedLoginToken: shaObj.getHMAC(username+shaPassword.getHash("SHA-512", "HEX"), "ASCII", "SHA-512", "HEX")
		}).done(function(user){
			localStorage.setItem('superdesk.login.session', user.Session);
			localStorage.setItem('superdesk.login.id', user.Id);
			localStorage.setItem('superdesk.login.name', user.UserName);
			localStorage.setItem('superdesk.login.email', user.EMail);
			$(authLogin).trigger('success');
		});
		return $(authLogin);
	},
	AuthToken = function(username, password) {
		var authToken = new $.rest('Authentication');
		authToken.resetData().select({ userName: username }).done(
			function(data){
				authLogin = AuthLogin(username, password, data.Token);
				authLogin.on('failed', function(){
					$(authToken).trigger('failed', 'authToken');
				}).on('success', function(){
					$(authToken).trigger('success');
				});
			}
		);
		return $(authToken);
	},
	AuthApp =
    {
        success: $.noop,
        require: function()
        {
			if(this.showed) return;
            var self = this; // rest
			self.showed = true;
            $.tmpl('auth', null, function(e, o)
            {
                var dialog = $(o).eq(0).dialog
                ({
                    draggable: false,
                    resizable: false,
                    modal: true,
                    width: "40.1709%",
                    buttons:
                    [
                         { text: "Login", click: function(){ $(form).trigger('submit'); }, class: "btn btn-primary"},
                         { text: "Close", click: function(){ $(this).dialog('close'); }, class: "btn"}
                    ]
                }),
                    form = dialog.find('form');
                form.off('submit.superdesk')//
                .on('submit.superdesk', function(event)
                {
                    var username = $(this).find('#username'), password=$(this).find('#password');
					AuthToken(username.val(), password.val()).on('failed',function(evt, type){
						username.val('');
						password.val('')
					}).on('success', function(){
                        AuthApp.success && AuthApp.success.apply();
						$(dialog).dialog('close');
						self.showed = false;
					});
                    event.preventDefault();

                });
            });
        }
    };
    return AuthApp;
});<commit_msg>NR-226: Create login functionality in Ally framework<commit_after>define
([
    'jquery', 'jquery/superdesk', 'dust/core', 'utils/sha512', 'jquery/tmpl', 'jquery/rest', 'bootstrap',
    'tmpl!auth',
],
function($, superdesk, dust, jsSHA)
{
    var AuthLogin = function(username, password, logintoken){
		var shaObj = new jsSHA(logintoken, "ASCII"),shaPassword = new jsSHA(password, "ASCII"),
			authLogin = new $.rest('Authentication');
			authLogin.resetData().insert({
			UserName: username,
			LoginToken: logintoken,
			HashedLoginToken: shaObj.getHMAC(username+shaPassword.getHash("SHA-512", "HEX"), "ASCII", "SHA-512", "HEX")
		}).done(function(user){
			localStorage.setItem('superdesk.login.session', user.Session);
			localStorage.setItem('superdesk.login.id', user.Id);
			localStorage.setItem('superdesk.login.name', user.UserName);
			localStorage.setItem('superdesk.login.email', user.EMail);
			$(authLogin).trigger('success');
		});
		return $(authLogin);
	},
	AuthToken = function(username, password) {
		var authToken = new $.rest('Authentication');
		authToken.resetData().select({ userName: username }).done(
			function(data){
				authLogin = AuthLogin(username, password, data.Token);
				authLogin.on('failed', function(){
					$(authToken).trigger('failed', 'authToken');
				}).on('success', function(){
					$(authToken).trigger('success');
				});
			}
		);
		return $(authToken);
	},
	AuthApp =
    {
        success: $.noop,
        require: function()
        {
			if(this.showed) return;
            var self = this; // rest
			self.showed = true;
            $.tmpl('auth', null, function(e, o)
            {
                var dialog = $(o).eq(0).dialog
                ({
                    draggable: false,
                    resizable: false,
                    modal: true,
                    width: "40.1709%",
                    buttons:
                    [
                         { text: "Login", click: function(){ $(form).trigger('submit'); }, class: "btn btn-primary"},
                         { text: "Close", click: function(){ $(this).dialog('close'); }, class: "btn"}
                    ]
                }),
                    form = dialog.find('form');
                form.off('submit.superdesk')//
                .on('submit.superdesk', function(event)
                {
                    var username = $(this).find('#username'), password=$(this).find('#password');
					AuthToken(username.val(), password.val()).on('failed',function(evt, type){
						username.val('');
						password.val('')
					}).on('success', function(){
                        AuthApp.success && AuthApp.success.apply();
						$(dialog).dialog('close');
						self.showed = false;
					});
                    event.preventDefault();

                });
            });
        }
    };
    return AuthApp;
});<|endoftext|>


<commit_before>var path = require('path'),
    graphviz = require('graphviz');

module.exports = function (targetFileName) {
    return function drawGraph(assetGraph) {
        var g = graphviz.digraph("G"),
            seenNodes = {};

        function addAssetAsNode(asset, namePrefix) {
            seenNodes[asset.id] = true;
            g.addNode(asset.id, {
                label: "\"" + (namePrefix || '') + (asset.url ? path.basename(asset.url) : 'i:' + asset) + "\""
            });
        }
        assetGraph.findAssets().forEach(function (asset) {
            addAssetAsNode(asset);
        });
        assetGraph.findRelations().forEach(function (relation) {
            if (!seenNodes[relation.from.id]) {
                addAssetAsNode(relation.from, 'o:');
            }
            if (!seenNodes[relation.to.id]) {
                addAssetAsNode(relation.to, 'o:');
            }
            g.addEdge(relation.from.id, relation.to.id);
        });
        g.output(path.extname(targetFileName).substr(1), targetFileName);
    }
};
<commit_msg>transforms.drawGraph: Prettier graph layout + annotations on edges.<commit_after>var path = require('path'),
    graphviz = require('graphviz');

var relationLabelByType = {
    HtmlScript: '<script>',
    HtmlStyle: '<style>',
    HtmlCacheManifest: '<html manifest>',
    HtmlIFrame: '<iframe>',
    HtmlFrame: '<frame>',
    HtmlAlternateLink: '<link rel=alternate>',
    HtmlConditionalComment: '<!--[if ...]>',
    HtmlImage: '<img>',
    HtmlAudio: '<audio>',
    HtmlVideo: '<video>',
    HtmlVideoPoster: '<video poster>',
    HtmlEmbed: '<embed>',
    HtmlApplet: '<applet>',
    HtmlObject: '<object>',
    HtmlEdgeSideInclude: '<esi:include>',
    HtmlAnchor: '<a>',
    CssImage: 'background-image',
    CssBehavior: 'behavior'
};

module.exports = function (targetFileName) {
    return function drawGraph(assetGraph) {
        var g = graphviz.digraph("G"),
            seenNodes = {};

        function addAssetAsNode(asset, namePrefix) {
            seenNodes[asset.id] = true;
            g.addNode(asset.id, {
                label: "\"" + (namePrefix || '') + (asset.url ? path.basename(asset.url) : 'i:' + asset) + "\""
            });
        }
        assetGraph.findAssets().forEach(function (asset) {
            addAssetAsNode(asset);
        });
        assetGraph.findRelations().forEach(function (relation) {
            if (!seenNodes[relation.from.id]) {
                addAssetAsNode(relation.from, 'o:');
            }
            if (!seenNodes[relation.to.id]) {
                addAssetAsNode(relation.to, 'o:');
            }
            g.addEdge(relation.from.id, relation.to.id, {
                fontsize: '9',
                arrowhead: 'empty',
                label: "\"" + (relationLabelByType[relation.type] || '').replace(/\"/g, "\\\"") + "\""
            });
        });
        g.output(path.extname(targetFileName).substr(1), targetFileName);
    }
};
<|endoftext|>


<commit_before>var buster = require("buster-node");

buster.testRunner.onCreate(function (runner) {
    buster.referee.on("pass", runner.assertionPass.bind(runner));

    runner.on("suite:end", function (results) {
        if (!results.ok) {
            setTimeout(function () {
                process.exit(1);
            }, 50);
        }
    });
});

buster.testContext.on("create", buster.autoRun());

require("./test/buster-autotest-test");
<commit_msg>Even simpler test runner<commit_after>var buster = require("buster-node");
require("./test/buster-autotest-test");
<|endoftext|>


<commit_before>import {BottomBar, Content, SectionLayout, TopBar} from 'widget/sectionLayout'
import {Button} from 'widget/button'
import {Buttons} from 'widget/buttons'
import {FieldSet} from 'widget/form'
import {PageControls, PageData, PageInfo, Pageable} from 'widget/pageable'
import {Scrollable, ScrollableContainer, Unscrollable} from 'widget/scrollable'
import {Shape} from 'widget/shape'
import {compose} from 'compose'
import {connect} from 'store'
import {map, share, zip} from 'rxjs/operators'
import {msg} from 'translate'
import Highlight from 'react-highlighter'
import Icon from 'widget/icon'
import Label from 'widget/label'
import Notifications from 'widget/notifications'
import React from 'react'
import UserDetails from './user'
import _ from 'lodash'
import api from 'api'
import escapeStringRegexp from 'escape-string-regexp'
import format from 'format'
import lookStyles from 'style/look.module.css'
import moment from 'moment'
import styles from './users.module.css'

const getUserList$ = () => api.user.getUserList$().pipe(
    share()
)

const getBudgetReport$ = () => api.user.getBudgetReport$().pipe(
    zip(getUserList$()),
    map(([budgetReport]) => budgetReport)
)

class Users extends React.Component {
    state = {
        users: [],
        sortingOrder: 'updateTime',
        sortingDirection: -1,
        textFilter: '',
        statusFilter: null,
        budgetFilter: false,
        userDetails: null
    }

    search = React.createRef()

    componentDidMount() {
        const setUserList = userList =>
            this.setState({
                users: this.getSortedUsers(userList)
            })

        const mergeBudgetReport = budgetReport =>
            this.setState(({users}) => ({
                users: _.map(users, user => ({
                    ...user,
                    report: budgetReport[user.username || {}]
                }))
            }))

        this.props.stream('LOAD_USER_LIST',
            getUserList$(),
            userList => setUserList(userList)
        )
        this.props.stream('LOAD_BUDGET_REPORT',
            getBudgetReport$(),
            budgetReport => mergeBudgetReport(budgetReport)
        )
    }

    setSorting(sortingOrder) {
        this.setState(prevState => {
            const sortingDirection = sortingOrder === prevState.sortingOrder ? -prevState.sortingDirection : 1
            return {
                ...prevState,
                sortingOrder,
                sortingDirection,
                users: this.getSortedUsers(prevState.users, sortingOrder, sortingDirection)
            }
        })
    }

    getSortedUsers(users, sortingOrder = this.state.sortingOrder, sortingDirection = this.state.sortingDirection) {
        return _.orderBy(users, user => {
            const item = _.get(user, sortingOrder)
            return _.isString(item) ? item.toUpperCase() : item
        }, sortingDirection === 1 ? 'asc' : 'desc')
    }

    setTextFilter(textFilter) {
        this.setState({
            textFilter,
            textFilterRegexp: RegExp(escapeStringRegexp(textFilter), 'i')
        })
    }

    setActiveFilter(activeFilter) {
        this.setState({activeFilter})
    }

    setBudgetFilter(budgetFilter) {
        this.setState({budgetFilter})
    }

    getFilteredUsers() {
        const {users} = this.state
        return users
            .filter(user => this.userMatchesTextFilter(user))
            .filter(user => this.userMatchesStatusFilter(user))
    }

    userMatchesTextFilter(user) {
        const {textFilter, textFilterRegexp} = this.state
        const searchProperties = ['name', 'username', 'email', 'organization']
        return textFilter
            ? _.find(searchProperties, searchProperty =>
                textFilterRegexp.test(user[searchProperty])
            )
            : true
    }

    userMatchesStatusFilter(user) {
        const {statusFilter} = this.state
        return statusFilter
            ? statusFilter === 'OVERBUDGET'
                ? this.isUserOverBudget(user)
                : user.status === statusFilter
            : true
    }

    isUserOverBudget({report: {budget, current}}) {
        return current.instanceSpending > budget.instanceSpending
            || current.storageSpending > budget.storageSpending
            || current.storageQuota > budget.storageQuota
    }

    onKeyDown({key}) {
        const keyMap = {
            Escape: () => {
                this.setTextFilter('')
            }
        }
        const keyAction = keyMap[key]
        keyAction && keyAction()
        this.search.current.focus()
    }

    editUser(user) {
        this.setState({
            userDetails: {
                username: user.username,
                name: user.name,
                email: user.email,
                organization: user.organization,
                monthlyBudgetInstanceSpending: user.report.budget.instanceSpending,
                monthlyBudgetStorageSpending: user.report.budget.storageSpending,
                monthlyBudgetStorageQuota: user.report.budget.storageQuota
            }
        })
    }

    inviteUser() {
        this.setState({
            userDetails: {
                newUser: true,
                monthlyBudgetInstanceSpending: 1,
                monthlyBudgetStorageSpending: 1,
                monthlyBudgetStorageQuota: 20
            }
        })
    }

    cancelUser() {
        this.setState({
            userDetails: null
        })
    }

    updateUser(userDetails) {

        const update$ = userDetails =>
            updateUserDetails$(userDetails).pipe(
                zip(updateUserBudget$(userDetails)),
                map(([userDetails, userBudget]) => ({
                    ...userDetails,
                    report: {
                        budget: userBudget
                    }
                }))
            )

        const updateUserDetails$ = ({newUser, username, name, email, organization}) =>
            newUser
                ? api.user.inviteUser$({username, name, email, organization})
                : api.user.updateUser$({username, name, email, organization})

        const updateUserBudget$ = ({
            username,
            monthlyBudgetInstanceSpending: instanceSpending,
            monthlyBudgetStorageSpending: storageSpending,
            monthlyBudgetStorageQuota: storageQuota
        }) => api.user.updateUserBudget$({
            username,
            instanceSpending,
            storageSpending,
            storageQuota
        })

        const updateLocalState = userDetails =>
            this.setState(({users}) => {
                if (userDetails) {
                    const index = users.findIndex(user => user.username === userDetails.username)
                    index === -1
                        ? users.push(userDetails)
                        : users[index] = userDetails
                }
                return {
                    users: this.getSortedUsers(users)
                }
            })

        const removeFromLocalState = userDetails =>
            this.setState(({users}) => {
                if (userDetails) {
                    _.remove(users, user => user.username === userDetails.username)
                }
                return {users}
            })

        this.cancelUser()

        updateLocalState({
            username: userDetails.username,
            name: userDetails.name,
            email: userDetails.email,
            organization: userDetails.organization,
            report: {
                budget: {
                    instanceSpending: userDetails.monthlyBudgetInstanceSpending,
                    storageSpending: userDetails.monthlyBudgetStorageSpending,
                    storageQuota: userDetails.monthlyBudgetStorageQuota
                }
            }
        })

        this.props.stream('UPDATE_USER',
            update$(userDetails),
            userDetails => {
                updateLocalState(userDetails)
                Notifications.success({message: msg('user.userDetails.update.success')})
            },
            error => {
                removeFromLocalState(userDetails)
                Notifications.error({message: msg('user.userDetails.update.error'), error})
            }
        )
    }

    renderSortingHandle(column) {
        return this.state.sortingOrder === column
            ? this.state.sortingDirection === 1
                ? <Icon name={'sort-down'}/>
                : <Icon name={'sort-up'}/>
            : <Icon name={'sort'}/>
    }

    renderColumnHeader(column, label, classNames = []) {
        const {sortingOrder} = this.state
        return (

                <Button
                    chromeless
                    shape='none'
                    additionalClassName='itemType'
                    onClick={() => this.setSorting(column)}>

                        {label}

                        {this.renderSortingHandle(column)}

                </Button>

        )
    }

    renderHeader() {
        return (

                    {this.renderInfo()}

                {this.renderColumnHeader('name', msg('user.userDetails.form.name.label'), [styles.name])}
                {this.renderColumnHeader('status', msg('user.userDetails.form.status.label'), [styles.status])}
                {this.renderColumnHeader('updateTime', msg('user.userDetails.form.updateTime.label'), [styles.updateTime])}
                {this.renderColumnHeader('report.budget.instanceSpending', msg('user.report.resources.quota'), [styles.instanceBudgetQuota, styles.number])}
                {this.renderColumnHeader('report.current.instanceSpending', msg('user.report.resources.used'), [styles.instanceBudgetUsed, styles.number])}
                {this.renderColumnHeader('report.budget.storageSpending', msg('user.report.resources.quota'), [styles.storageBudgetQuota, styles.number])}
                {this.renderColumnHeader('report.current.storageSpending', msg('user.report.resources.used'), [styles.storageBudgetUsed, styles.number])}
                {this.renderColumnHeader('report.budget.storageQuota', msg('user.report.resources.quota'), [styles.storageQuota, styles.number])}
                {this.renderColumnHeader('report.current.storageQuota', msg('user.report.resources.used'), [styles.storageUsed, styles.number])}

        )
    }

    renderTextFilter() {
        const {textFilter} = this.state
        return (
            <Shape
                look='transparent'
                size='large'
                shape='pill'
                icon='search'>
                <input
                    className={styles.search}
                    type='search'
                    ref={this.search}
                    value={textFilter}
                    placeholder={msg('users.filter.search.placeholder')}
                    onChange={e => this.setTextFilter(e.target.value)}/>
            </Shape>
        )
    }

    renderStatusFilter() {
        const {statusFilter} = this.state
        const options = [{
            label: msg('users.filter.status.ignore.label'),
            value: null
        }, {
            label: msg('users.filter.status.pending.label'),
            value: 'PENDING'
        }, {
            label: msg('users.filter.status.active.label'),
            value: 'ACTIVE'
        }, {
            label: msg('users.filter.budget.over.label'),
            value: 'OVERBUDGET'
        }]
        return (
            <Buttons
                chromeless
                type='horizontal-tight'
                options={options}
                selected={statusFilter}
                onChange={statusFilter => this.setState({statusFilter})}
            />
        )
    }

    renderInviteUser() {
        return (
            <Button
                additionalClassName={styles.inviteUser}
                look='add'
                size='xx-large'
                shape='circle'
                icon='plus'
                tooltip={msg('users.invite.label')}
                tooltipPlacement='left'
                onClick={() => this.inviteUser()}/>
        )
    }

    renderInfo() {
        const {textFilter, budgetFilter, users} = this.state
        const isFiltered = textFilter || budgetFilter
        const results = (count, total) =>
            msg(isFiltered ? 'users.countWithFilter' : 'users.countNoFilter', {count, total})
        return (
            <PageInfo>
                {({itemCount}) =>

                        {results(itemCount, users.length)}

                }
            </PageInfo>
        )
    }

    renderUsers() {
        const {textFilter} = this.state
        return (
            <PageData itemKey={user => user.username || user.id}>
                {user =>
                    <User
                        user={user}
                        highlight={textFilter}
                        onClick={() => this.editUser(user)}/>
                }
            </PageData>
        )
    }

    renderUserDetails() {
        const {userDetails} = this.state
        return userDetails ? (
            <UserDetails
                userDetails={userDetails}
                onCancel={() => this.cancelUser()}
                onSave={userDetails => this.updateUser(userDetails)}/>
        ) : null
    }

    render() {
        return (
            <React.Fragment>

 this.onKeyDown(e)}>
                    <Pageable items={this.getFilteredUsers()}>
                        <SectionLayout>
                            <TopBar label={msg('home.sections.users')}/>
                            <Content horizontalPadding verticalPadding menuPadding>
                                <ScrollableContainer>
                                    <Unscrollable>
                                        <FieldSet layout='horizontal' spacing='compact'>
                                            {this.renderTextFilter()}
                                            {this.renderStatusFilter()}
                                        </FieldSet>
                                    </Unscrollable>
                                    <Scrollable direction='x'>
                                        <ScrollableContainer className={styles.content}>
                                            <Unscrollable>
                                                {this.renderHeader()}
                                            </Unscrollable>
                                            <Scrollable direction='y' className={styles.users}>
                                                {this.renderUsers()}
                                            </Scrollable>
                                        </ScrollableContainer>
                                    </Scrollable>
                                </ScrollableContainer>
                                {this.renderInviteUser()}
                            </Content>
                            <BottomBar className={styles.bottomBar}>
                                <PageControls/>
                            </BottomBar>
                        </SectionLayout>
                    </Pageable>

                {this.renderUserDetails()}
            </React.Fragment>
        )
    }
}

export default compose(
    Users,
    connect()
)

class User extends React.Component {
    render() {
        const {
            user: {
                name,
                status,
                updateTime,
                report: {
                    budget = {},
                    current = {}
                } = {}
            },
            highlight,
            onClick
        } = this.props
        return (

 status ? onClick() : null}>

<Highlight search={highlight} matchClass={styles.highlight}>{name}</Highlight>

{status ? msg(`user.userDetails.form.status.${status}`) : <Icon name='spinner'/>}

{moment(updateTime).fromNow()}

{format.dollars(budget.instanceSpending)}

 budget.instanceSpending ? styles.overBudget : null].join(' ')}>
                    {format.dollars(current.instanceSpending)}

{format.dollars(budget.storageSpending)}

 budget.storageSpending ? styles.overBudget : null].join(' ')}>
                    {format.dollars(current.storageSpending)}

{format.fileSize(budget.storageQuota, {scale: 'G'})}

 budget.storageQuota ? styles.overBudget : null].join(' ')}>
                    {format.fileSize(current.storageQuota, {scale: 'G'})}

        )
    }
}
<commit_msg>Fixed users count.<commit_after>import {BottomBar, Content, SectionLayout, TopBar} from 'widget/sectionLayout'
import {Button} from 'widget/button'
import {Buttons} from 'widget/buttons'
import {FieldSet} from 'widget/form'
import {PageControls, PageData, PageInfo, Pageable} from 'widget/pageable'
import {Scrollable, ScrollableContainer, Unscrollable} from 'widget/scrollable'
import {Shape} from 'widget/shape'
import {compose} from 'compose'
import {connect} from 'store'
import {map, share, zip} from 'rxjs/operators'
import {msg} from 'translate'
import Highlight from 'react-highlighter'
import Icon from 'widget/icon'
import Label from 'widget/label'
import Notifications from 'widget/notifications'
import React from 'react'
import UserDetails from './user'
import _ from 'lodash'
import api from 'api'
import escapeStringRegexp from 'escape-string-regexp'
import format from 'format'
import lookStyles from 'style/look.module.css'
import moment from 'moment'
import styles from './users.module.css'

const getUserList$ = () => api.user.getUserList$().pipe(
    share()
)

const getBudgetReport$ = () => api.user.getBudgetReport$().pipe(
    zip(getUserList$()),
    map(([budgetReport]) => budgetReport)
)

class Users extends React.Component {
    state = {
        users: [],
        sortingOrder: 'updateTime',
        sortingDirection: -1,
        textFilter: '',
        statusFilter: null,
        budgetFilter: false,
        userDetails: null
    }

    search = React.createRef()

    componentDidMount() {
        const setUserList = userList =>
            this.setState({
                users: this.getSortedUsers(userList)
            })

        const mergeBudgetReport = budgetReport =>
            this.setState(({users}) => ({
                users: _.map(users, user => ({
                    ...user,
                    report: budgetReport[user.username || {}]
                }))
            }))

        this.props.stream('LOAD_USER_LIST',
            getUserList$(),
            userList => setUserList(userList)
        )
        this.props.stream('LOAD_BUDGET_REPORT',
            getBudgetReport$(),
            budgetReport => mergeBudgetReport(budgetReport)
        )
    }

    setSorting(sortingOrder) {
        this.setState(prevState => {
            const sortingDirection = sortingOrder === prevState.sortingOrder ? -prevState.sortingDirection : 1
            return {
                ...prevState,
                sortingOrder,
                sortingDirection,
                users: this.getSortedUsers(prevState.users, sortingOrder, sortingDirection)
            }
        })
    }

    getSortedUsers(users, sortingOrder = this.state.sortingOrder, sortingDirection = this.state.sortingDirection) {
        return _.orderBy(users, user => {
            const item = _.get(user, sortingOrder)
            return _.isString(item) ? item.toUpperCase() : item
        }, sortingDirection === 1 ? 'asc' : 'desc')
    }

    setTextFilter(textFilter) {
        this.setState({
            textFilter,
            textFilterRegexp: RegExp(escapeStringRegexp(textFilter), 'i')
        })
    }

    setActiveFilter(activeFilter) {
        this.setState({activeFilter})
    }

    setBudgetFilter(budgetFilter) {
        this.setState({budgetFilter})
    }

    getFilteredUsers() {
        const {users} = this.state
        return users
            .filter(user => this.userMatchesTextFilter(user))
            .filter(user => this.userMatchesStatusFilter(user))
    }

    userMatchesTextFilter(user) {
        const {textFilter, textFilterRegexp} = this.state
        const searchProperties = ['name', 'username', 'email', 'organization']
        return textFilter
            ? _.find(searchProperties, searchProperty =>
                textFilterRegexp.test(user[searchProperty])
            )
            : true
    }

    userMatchesStatusFilter(user) {
        const {statusFilter} = this.state
        return statusFilter
            ? statusFilter === 'OVERBUDGET'
                ? this.isUserOverBudget(user)
                : user.status === statusFilter
            : true
    }

    isUserOverBudget({report: {budget, current}}) {
        return current.instanceSpending > budget.instanceSpending
            || current.storageSpending > budget.storageSpending
            || current.storageQuota > budget.storageQuota
    }

    onKeyDown({key}) {
        const keyMap = {
            Escape: () => {
                this.setTextFilter('')
            }
        }
        const keyAction = keyMap[key]
        keyAction && keyAction()
        this.search.current.focus()
    }

    editUser(user) {
        this.setState({
            userDetails: {
                username: user.username,
                name: user.name,
                email: user.email,
                organization: user.organization,
                monthlyBudgetInstanceSpending: user.report.budget.instanceSpending,
                monthlyBudgetStorageSpending: user.report.budget.storageSpending,
                monthlyBudgetStorageQuota: user.report.budget.storageQuota
            }
        })
    }

    inviteUser() {
        this.setState({
            userDetails: {
                newUser: true,
                monthlyBudgetInstanceSpending: 1,
                monthlyBudgetStorageSpending: 1,
                monthlyBudgetStorageQuota: 20
            }
        })
    }

    cancelUser() {
        this.setState({
            userDetails: null
        })
    }

    updateUser(userDetails) {

        const update$ = userDetails =>
            updateUserDetails$(userDetails).pipe(
                zip(updateUserBudget$(userDetails)),
                map(([userDetails, userBudget]) => ({
                    ...userDetails,
                    report: {
                        budget: userBudget
                    }
                }))
            )

        const updateUserDetails$ = ({newUser, username, name, email, organization}) =>
            newUser
                ? api.user.inviteUser$({username, name, email, organization})
                : api.user.updateUser$({username, name, email, organization})

        const updateUserBudget$ = ({
            username,
            monthlyBudgetInstanceSpending: instanceSpending,
            monthlyBudgetStorageSpending: storageSpending,
            monthlyBudgetStorageQuota: storageQuota
        }) => api.user.updateUserBudget$({
            username,
            instanceSpending,
            storageSpending,
            storageQuota
        })

        const updateLocalState = userDetails =>
            this.setState(({users}) => {
                if (userDetails) {
                    const index = users.findIndex(user => user.username === userDetails.username)
                    index === -1
                        ? users.push(userDetails)
                        : users[index] = userDetails
                }
                return {
                    users: this.getSortedUsers(users)
                }
            })

        const removeFromLocalState = userDetails =>
            this.setState(({users}) => {
                if (userDetails) {
                    _.remove(users, user => user.username === userDetails.username)
                }
                return {users}
            })

        this.cancelUser()

        updateLocalState({
            username: userDetails.username,
            name: userDetails.name,
            email: userDetails.email,
            organization: userDetails.organization,
            report: {
                budget: {
                    instanceSpending: userDetails.monthlyBudgetInstanceSpending,
                    storageSpending: userDetails.monthlyBudgetStorageSpending,
                    storageQuota: userDetails.monthlyBudgetStorageQuota
                }
            }
        })

        this.props.stream('UPDATE_USER',
            update$(userDetails),
            userDetails => {
                updateLocalState(userDetails)
                Notifications.success({message: msg('user.userDetails.update.success')})
            },
            error => {
                removeFromLocalState(userDetails)
                Notifications.error({message: msg('user.userDetails.update.error'), error})
            }
        )
    }

    renderSortingHandle(column) {
        return this.state.sortingOrder === column
            ? this.state.sortingDirection === 1
                ? <Icon name={'sort-down'}/>
                : <Icon name={'sort-up'}/>
            : <Icon name={'sort'}/>
    }

    renderColumnHeader(column, label, classNames = []) {
        const {sortingOrder} = this.state
        return (

                <Button
                    chromeless
                    shape='none'
                    additionalClassName='itemType'
                    onClick={() => this.setSorting(column)}>

                        {label}

                        {this.renderSortingHandle(column)}

                </Button>

        )
    }

    renderHeader() {
        return (

                    {this.renderInfo()}

                {this.renderColumnHeader('name', msg('user.userDetails.form.name.label'), [styles.name])}
                {this.renderColumnHeader('status', msg('user.userDetails.form.status.label'), [styles.status])}
                {this.renderColumnHeader('updateTime', msg('user.userDetails.form.updateTime.label'), [styles.updateTime])}
                {this.renderColumnHeader('report.budget.instanceSpending', msg('user.report.resources.quota'), [styles.instanceBudgetQuota, styles.number])}
                {this.renderColumnHeader('report.current.instanceSpending', msg('user.report.resources.used'), [styles.instanceBudgetUsed, styles.number])}
                {this.renderColumnHeader('report.budget.storageSpending', msg('user.report.resources.quota'), [styles.storageBudgetQuota, styles.number])}
                {this.renderColumnHeader('report.current.storageSpending', msg('user.report.resources.used'), [styles.storageBudgetUsed, styles.number])}
                {this.renderColumnHeader('report.budget.storageQuota', msg('user.report.resources.quota'), [styles.storageQuota, styles.number])}
                {this.renderColumnHeader('report.current.storageQuota', msg('user.report.resources.used'), [styles.storageUsed, styles.number])}

        )
    }

    renderTextFilter() {
        const {textFilter} = this.state
        return (
            <Shape
                look='transparent'
                size='large'
                shape='pill'
                icon='search'>
                <input
                    className={styles.search}
                    type='search'
                    ref={this.search}
                    value={textFilter}
                    placeholder={msg('users.filter.search.placeholder')}
                    onChange={e => this.setTextFilter(e.target.value)}/>
            </Shape>
        )
    }

    renderStatusFilter() {
        const {statusFilter} = this.state
        const options = [{
            label: msg('users.filter.status.ignore.label'),
            value: null
        }, {
            label: msg('users.filter.status.pending.label'),
            value: 'PENDING'
        }, {
            label: msg('users.filter.status.active.label'),
            value: 'ACTIVE'
        }, {
            label: msg('users.filter.budget.over.label'),
            value: 'OVERBUDGET'
        }]
        return (
            <Buttons
                chromeless
                type='horizontal-tight'
                options={options}
                selected={statusFilter}
                onChange={statusFilter => this.setState({statusFilter})}
            />
        )
    }

    renderInviteUser() {
        return (
            <Button
                additionalClassName={styles.inviteUser}
                look='add'
                size='xx-large'
                shape='circle'
                icon='plus'
                tooltip={msg('users.invite.label')}
                tooltipPlacement='left'
                onClick={() => this.inviteUser()}/>
        )
    }

    renderInfo() {
        const {textFilter, statusFilter, users} = this.state
        const isFiltered = textFilter || statusFilter
        const results = (count, total) =>
            msg(isFiltered ? 'users.countWithFilter' : 'users.countNoFilter', {count, total})
        return (
            <PageInfo>
                {({count}) =>

                        {results(count, users.length)}

                }
            </PageInfo>
        )
    }

    renderUsers() {
        const {textFilter} = this.state
        return (
            <PageData itemKey={user => user.username || user.id}>
                {user =>
                    <User
                        user={user}
                        highlight={textFilter}
                        onClick={() => this.editUser(user)}/>
                }
            </PageData>
        )
    }

    renderUserDetails() {
        const {userDetails} = this.state
        return userDetails ? (
            <UserDetails
                userDetails={userDetails}
                onCancel={() => this.cancelUser()}
                onSave={userDetails => this.updateUser(userDetails)}/>
        ) : null
    }

    render() {
        return (
            <React.Fragment>

 this.onKeyDown(e)}>
                    <Pageable items={this.getFilteredUsers()}>
                        <SectionLayout>
                            <TopBar label={msg('home.sections.users')}/>
                            <Content horizontalPadding verticalPadding menuPadding>
                                <ScrollableContainer>
                                    <Unscrollable>
                                        <FieldSet layout='horizontal' spacing='compact'>
                                            {this.renderTextFilter()}
                                            {this.renderStatusFilter()}
                                        </FieldSet>
                                    </Unscrollable>
                                    <Scrollable direction='x'>
                                        <ScrollableContainer className={styles.content}>
                                            <Unscrollable>
                                                {this.renderHeader()}
                                            </Unscrollable>
                                            <Scrollable direction='y' className={styles.users}>
                                                {this.renderUsers()}
                                            </Scrollable>
                                        </ScrollableContainer>
                                    </Scrollable>
                                </ScrollableContainer>
                                {this.renderInviteUser()}
                            </Content>
                            <BottomBar className={styles.bottomBar}>
                                <PageControls/>
                            </BottomBar>
                        </SectionLayout>
                    </Pageable>

                {this.renderUserDetails()}
            </React.Fragment>
        )
    }
}

export default compose(
    Users,
    connect()
)

class User extends React.Component {
    render() {
        const {
            user: {
                name,
                status,
                updateTime,
                report: {
                    budget = {},
                    current = {}
                } = {}
            },
            highlight,
            onClick
        } = this.props
        return (

 status ? onClick() : null}>

<Highlight search={highlight} matchClass={styles.highlight}>{name}</Highlight>

{status ? msg(`user.userDetails.form.status.${status}`) : <Icon name='spinner'/>}

{moment(updateTime).fromNow()}

{format.dollars(budget.instanceSpending)}

 budget.instanceSpending ? styles.overBudget : null].join(' ')}>
                    {format.dollars(current.instanceSpending)}

{format.dollars(budget.storageSpending)}

 budget.storageSpending ? styles.overBudget : null].join(' ')}>
                    {format.dollars(current.storageSpending)}

{format.fileSize(budget.storageQuota, {scale: 'G'})}

 budget.storageQuota ? styles.overBudget : null].join(' ')}>
                    {format.fileSize(current.storageQuota, {scale: 'G'})}

        )
    }
}
<|endoftext|>


<commit_before>define(['knockout', 'jquery',
        "./main"
    ],
    function(ko, $) {

        describe('<%= model.name %> binding', function() {
            var viewModel;
            var element;
            var root;

            describe('when binging to an element', function() {
                beforeEach(function() {

                    root = document.createElement('div');
                    root.innerHTML = '<input id="input" data-bind="<%= model.name %>: obs">';
                    document.body.appendChild(root);
                    element = document.getElementById('input');

                    viewModel = {
                        obs: ko.observable()
                    };

                    ko.applyBindings(viewModel, root);
                });

                afterEach(function() {
                    ko.cleanNode(root);
                    $(root).remove();
                });

                it('should be bound', function() {
                    expect(ko.dataFor(element)).toBe(viewModel);
                });
            });

        });
    });
<commit_msg>fxiing the test template<commit_after>define(['knockout', 'jquery', './model',
        "./main"
    ],
    function(ko, $, ViewModel) {

        describe('<%= model.name %> binding', function() {
            var viewModel;
            var element;
            var root;

            describe('when binding to an element', function() {
                beforeEach(function() {

                    root = document.createElement('div');
                    root.innerHTML = '<div id="binding" data-bind="<%= model.name %>: obs">
';
                    document.body.appendChild(root);
                    element = document.getElementById('binding');

                    viewModel = {
                        obs: ko.observable()
                    };

                    ko.applyBindings(viewModel, root);
                });

                afterEach(function() {
                    ko.cleanNode(root);
                    $(root).remove();
                });

                it('should be bound', function() {
                    expect(ko.dataFor(element)).toBe(viewModel);
                });
            });

        });
    });
<|endoftext|>