<commit_before>'use strict';
/*eslint-disable no-extra-parens*/
/**
 * @file
 * Currently the main entrypoint served on /work.
 * Provides the work view for the enduser.
 */
import React from 'react';

// import components
// import {WorkDisplay} from 'dbc-react-workview';
// import Loader from '../Loader.component.js';
// import reflux actions and stores
import workAction from '../../actions/Work.action.js';
import workStore from '../../stores/Work.store.js';
import Covers from '../Cover.component.js';

const Work = React.createClass({
  propTypes: {
    id: React.PropTypes.string,
    work: React.PropTypes.object
  },

  getInitialState() {
    return {
      id: this.props.id,
      work: workStore.getStore()
    };
  },

  getWork() {
    let result = {
      id: this.state.id
    };
    workAction(result);
  },

  updateWork(work) {
    this.setState({work});
  },

  componentDidMount: function () {
    this.getWork();
    workStore.listen(this.updateWork);
  },

  render() {
    const {id, work, info} = this.state; // eslint-disable-line
    let general,
      title,
      creators,
      description,
      parts,
      issns,
      extents,
      actors,
      series,
      subjects,
      tracks,
      languages,
      specific,
      specifics,
      types,
      publishers,
      dates;

    if (work.info.hits === '0') {
      return (
Værket blev ikke fundet
);
    }

    if (work.result.length === 0) {
      return (
);
    }
    general = work.result.general;
    title = general.title;
    if (general.hasOwnProperty('creators')) {
      creators = general.creators.map((creator) => {
        let search_creator = '/search?phrase.creator=' + encodeURIComponent(creator);
        return (
{creator}
);
      });
    }
    if (general.hasOwnProperty('description')) {
      description = general.description;
    }
    if (general.hasOwnProperty('actors')) {
      actors = general.actors.map((actor) => {
        let search_actor = '/search?phrase.creator=' + encodeURIComponent(actor);
        return (
{actor}
);
      });
    }
    if (general.hasOwnProperty('series')) {
      let series_title = general.series[0].replace(/ ; .*/, '');
      series_title = series_title.replace(/Samhørende: /, '');
      series_title = series_title.replace(/.* del af: /, '');
      let series_link = '/search?phrase.titleSeries=' + encodeURIComponent(series_title);
      series = {general.series};
    }
    if (general.hasOwnProperty('subjects')) {
      subjects = general.subjects.map((subject) => {
        let search_subject = '/search?phrase.subject=' + encodeURIComponent(subject);
        return (
{subject}
);
      });
    }
    if (general.hasOwnProperty('tracks')) {
      tracks = general.tracks.map((track) => {
        return (
{track}
);
      });
    }
    if (general.hasOwnProperty('languages')) {
      languages = general.languages.map((language) => {
        return (
{language}
);
      });
    }
    if (general.hasOwnProperty('partOf')) {
      parts = general.partOf.map((partOf) => {
        return (
{partOf}
);
      });
    }
    if (general.hasOwnProperty('issn')) {
      issns = general.issn.map((issn) => {
        return (
{issn}
);
      });
    }
    if (general.hasOwnProperty('extent')) {
      extents = general.extent.map((extent) => {
        return (
{extent}
);
      });
    }
    specific = work.result.specific;
    let order_button = specific.map((tw) => {
      if (tw.accessType === 'physical') {
        let order_ids = [];
        order_ids.push(tw.identifiers);
        let order_link = '/order?ids=' + order_ids + '&title=' + encodeURIComponent(title) + '&type=' + tw.type;
        return (Bestil {tw.type});
      }
      if (tw.accessType === 'online') {
        let online_link = 'Se ' + tw.type + ' online';
        return ({online_link});
      }
    });
    specifics = specific.map((tw) => {
      let identifiers = [];
      identifiers.push(tw.identifiers);
      if (tw.dates[0] !== null) {
        dates = tw.dates.map((date) => {
          return (
{date}
);
        });
      }
      return (

            {tw.type}

          {dates}

      );
    });
    const publications = work.result.publications;
    let editions = publications.map((publ) => {
      let className = 'publication-details';
      if (publ.hasOwnProperty('types')) {
        types = publ.types.map((t) => {
          className += ' ' + t.toLowerCase().replace(/ .*/, '');
          return (
{t}
);
        });
      }
      if (publ.hasOwnProperty('publishers')) {
        publishers = publ.publishers.map((p) => {
          return (
{p}
);
        });
      }
      if (publ.hasOwnProperty('dates')) {
        dates = publ.dates.map((d) => {
          return (
{d}
);
        });
      }
      if (publ.hasOwnProperty('editions')) {
        editions = publ.editions.map((ed) => {
          return (
{ed}
);
        });
      }
      if (publ.hasOwnProperty('extents')) {
        extents = publ.extents.map((e) => {
          return (
{e}
);
        });
      }
      return (

      {types}

      {publishers}
      {editions}
      {dates}
      {extents}

);
    });
    return (

        <Covers pids={work.result.specific[0].identifiers} />

        {order_button}

{title}

{creators}

{description}

{parts}

{issns}

{extents}

{actors}

{series}

{subjects}

{tracks}

{languages}

        {specifics}

{editions}

    );
  }
});

export default Work;
<commit_msg>undoing: ignoring eslint no-extra-parens rule<commit_after>'use strict';
/**
 * @file
 * Currently the main entrypoint served on /work.
 * Provides the work view for the enduser.
 */
import React from 'react';

// import components
// import {WorkDisplay} from 'dbc-react-workview';
// import Loader from '../Loader.component.js';
// import reflux actions and stores
import workAction from '../../actions/Work.action.js';
import workStore from '../../stores/Work.store.js';
import Covers from '../Cover.component.js';

const Work = React.createClass({
  propTypes: {
    id: React.PropTypes.string,
    work: React.PropTypes.object
  },

  getInitialState() {
    return {
      id: this.props.id,
      work: workStore.getStore()
    };
  },

  getWork() {
    let result = {
      id: this.state.id
    };
    workAction(result);
  },

  updateWork(work) {
    this.setState({work});
  },

  componentDidMount: function () {
    this.getWork();
    workStore.listen(this.updateWork);
  },

  render() {
    const {id, work, info} = this.state; // eslint-disable-line
    let general,
      title,
      creators,
      description,
      parts,
      issns,
      extents,
      actors,
      series,
      subjects,
      tracks,
      languages,
      specific,
      specifics,
      types,
      publishers,
      dates;

    if (work.info.hits === '0') {
      return (
Værket blev ikke fundet
);
    }

    if (work.result.length === 0) {
      return (
);
    }
    general = work.result.general;
    title = general.title;
    if (general.hasOwnProperty('creators')) {
      creators = general.creators.map((creator) => {
        let search_creator = '/search?phrase.creator=' + encodeURIComponent(creator);
        return (
{creator}
);
      });
    }
    if (general.hasOwnProperty('description')) {
      description = general.description;
    }
    if (general.hasOwnProperty('actors')) {
      actors = general.actors.map((actor) => {
        let search_actor = '/search?phrase.creator=' + encodeURIComponent(actor);
        return (
{actor}
);
      });
    }
    if (general.hasOwnProperty('series')) {
      let series_title = general.series[0].replace(/ ; .*/, '');
      series_title = series_title.replace(/Samhørende: /, '');
      series_title = series_title.replace(/.* del af: /, '');
      let series_link = '/search?phrase.titleSeries=' + encodeURIComponent(series_title);
      series = {general.series};
    }
    if (general.hasOwnProperty('subjects')) {
      subjects = general.subjects.map((subject) => {
        let search_subject = '/search?phrase.subject=' + encodeURIComponent(subject);
        return (
{subject}
);
      });
    }
    if (general.hasOwnProperty('tracks')) {
      tracks = general.tracks.map((track) => {
        return (
{track}
);
      });
    }
    if (general.hasOwnProperty('languages')) {
      languages = general.languages.map((language) => {
        return (
{language}
);
      });
    }
    if (general.hasOwnProperty('partOf')) {
      parts = general.partOf.map((partOf) => {
        return (
{partOf}
);
      });
    }
    if (general.hasOwnProperty('issn')) {
      issns = general.issn.map((issn) => {
        return (
{issn}
);
      });
    }
    if (general.hasOwnProperty('extent')) {
      extents = general.extent.map((extent) => {
        return (
{extent}
);
      });
    }
    specific = work.result.specific;
    let order_button = specific.map((tw) => {
      if (tw.accessType === 'physical') {
        let order_ids = [];
        order_ids.push(tw.identifiers);
        let order_link = '/order?ids=' + order_ids + '&title=' + encodeURIComponent(title) + '&type=' + tw.type;
        return (Bestil {tw.type});
      }
      if (tw.accessType === 'online') {
        let online_link = 'Se ' + tw.type + ' online';
        return ({online_link});
      }
    });
    specifics = specific.map((tw) => {
      let identifiers = [];
      identifiers.push(tw.identifiers);
      if (tw.dates[0] !== null) {
        dates = tw.dates.map((date) => {
          return (
{date}
);
        });
      }
      return (

            {tw.type}

          {dates}

      );
    });
    const publications = work.result.publications;
    let editions = publications.map((publ) => {
      let className = 'publication-details';
      if (publ.hasOwnProperty('types')) {
        types = publ.types.map((t) => {
          className += ' ' + t.toLowerCase().replace(/ .*/, '');
          return (
{t}
);
        });
      }
      if (publ.hasOwnProperty('publishers')) {
        publishers = publ.publishers.map((p) => {
          return (
{p}
);
        });
      }
      if (publ.hasOwnProperty('dates')) {
        dates = publ.dates.map((d) => {
          return (
{d}
);
        });
      }
      if (publ.hasOwnProperty('editions')) {
        editions = publ.editions.map((ed) => {
          return (
{ed}
);
        });
      }
      if (publ.hasOwnProperty('extents')) {
        extents = publ.extents.map((e) => {
          return (
{e}
);
        });
      }
      return (

      {types}

      {publishers}
      {editions}
      {dates}
      {extents}

);
    });
    return (

        <Covers pids={work.result.specific[0].identifiers} />

        {order_button}

{title}

{creators}

{description}

{parts}

{issns}

{extents}

{actors}

{series}

{subjects}

{tracks}

{languages}

        {specifics}

{editions}

    );
  }
});

export default Work;
<|endoftext|>


<commit_before>import React from 'react'
import styled from 'styled-components'
import { Link } from 'gatsby'
import Img from 'gatsby-image'

const StyledLink = styled(Link)`
  position: relative;
  width: 100%;
  height: 100%;
  text-decoration: none;
  &:hover div {
    @supports (object-fit: cover) {
      opacity: 1;
      visibility: visible;
    }
  }
  h2,
  h3 {
    margin: 0;
  }
`

const Cover = styled.div`
  position: relative;
  transition: none;
  margin-bottom: 1rem;
  height: 100%;
  div {
    height: 100% !important;
    object-fit: cover !important;
  }
  &::before {
    transition: all 0.3s;
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    top: 0;
    height: 100%;
    width: 100%;
    z-index: 2;
    background: var(--color-base-75);
  }
  @media screen and (min-width: 52em) {
    position: fixed !important;
    pointer-events: none;
    transition: opacity 0.3s, visibility 0.3s;
    width: 50%;
    height: 100vh;
    top: 0;
    right: 0;
    z-index: 2;
    opacity: 0;
    visibility: hidden;
    div {
      height: 100% !important;
      object-fit: cover !important;
    }
    &::before {
      display: none;
    }
  }
  @media screen and (min-width: 64em) {
    position: fixed !important;
    pointer-events: none;
    transition: opacity 0.3s, visibility 0.3s;
    width: 66.666%;
    height: 100vh;
    top: 0;
    right: 0;
    z-index: 2;
    opacity: 0;
    visibility: hidden;
    div {
      height: 100% !important;
      object-fit: cover !important;
    }
  }
`
const Overflow = styled.div`
  display: inline-block;
  overflow: none;
  position: absolute;
  bottom: 0.5em;
  padding: 0.75rem;
  z-index: 2;
  font-size: 38.2%;
  @media screen and (min-width: 52em) {
    font-size: 100%;
    bottom: 0;
    display: block;
    position: relative;
    padding: 0.25rem 0;
  }
`

const ContentList = props => {
  return (

  )
}

export default ContentList
<commit_msg>Font-size<commit_after>import React from 'react'
import styled from 'styled-components'
import { Link } from 'gatsby'
import Img from 'gatsby-image'

const StyledLink = styled(Link)`
  position: relative;
  width: 100%;
  height: 100%;
  text-decoration: none;
  &:hover div {
    @supports (object-fit: cover) {
      opacity: 1;
      visibility: visible;
    }
  }
  h2,
  h3 {
    margin: 0;
  }
`

const Cover = styled.div`
  position: relative;
  transition: none;
  margin-bottom: 1rem;
  height: 100%;
  div {
    height: 100% !important;
    object-fit: cover !important;
  }
  &::before {
    transition: all 0.3s;
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    top: 0;
    height: 100%;
    width: 100%;
    z-index: 2;
    background: var(--color-base-75);
  }
  @media screen and (min-width: 52em) {
    position: fixed !important;
    pointer-events: none;
    transition: opacity 0.3s, visibility 0.3s;
    width: 50%;
    height: 100vh;
    top: 0;
    right: 0;
    z-index: 2;
    opacity: 0;
    visibility: hidden;
    div {
      height: 100% !important;
      object-fit: cover !important;
    }
    &::before {
      display: none;
    }
  }
  @media screen and (min-width: 64em) {
    position: fixed !important;
    pointer-events: none;
    transition: opacity 0.3s, visibility 0.3s;
    width: 66.666%;
    height: 100vh;
    top: 0;
    right: 0;
    z-index: 2;
    opacity: 0;
    visibility: hidden;
    div {
      height: 100% !important;
      object-fit: cover !important;
    }
  }
`
const Overflow = styled.div`
  display: inline-block;
  overflow: none;
  position: absolute;
  bottom: 0.5em;
  padding: 0.75rem;
  z-index: 2;
  font-size: 61.8%;
  @media screen and (min-width: 52em) {
    font-size: 100%;
    bottom: 0;
    display: block;
    position: relative;
    padding: 0.25rem 0;
  }
`

const ContentList = props => {
  return (

  )
}

export default ContentList
<|endoftext|>


<commit_before>'use strict' /* @flow */

import React from 'react'
import ReactCSS from 'reactcss'

export class SliderSwatch extends ReactCSS.Component {

  constructor() {
    super()

    this.handleClick = this.handleClick.bind(this)
  }

  classes(): any {
    return {
      'default': {
        swatch: {
          height: '12px',
          background: 'hsl(' + this.props.hsl.h + ', 50%, ' + (this.props.offset * 100) + '%)',
          cursor: 'pointer',
        },
      },
      'first': {
        swatch: {
          borderRadius: '2px 0 0 2px',
        },
      },
      'last': {
        swatch: {
          borderRadius: '0 2px 2px 0',
        },
      },
      active: {
        swatch: {
          transform: 'scaleY(1.8)',
          borderRadius: '3.6px/2px',
        },
      },
    }
  }

  handleClick() {
    this.props.onClick({ h: this.props.hsl.h, s: .5, l: this.props.offset })
  }

  render(): any {
    return (

    )
  }

}

export default SliderSwatch
<commit_msg>Slider Source<commit_after>'use strict' /* @flow */

import React from 'react'
import ReactCSS from 'reactcss'

export class SliderSwatch extends ReactCSS.Component {

  constructor() {
    super()

    this.handleClick = this.handleClick.bind(this)
  }

  classes(): any {
    return {
      'default': {
        swatch: {
          height: '12px',
          background: 'hsl(' + this.props.hsl.h + ', 50%, ' + (this.props.offset * 100) + '%)',
          cursor: 'pointer',
        },
      },
      'first': {
        swatch: {
          borderRadius: '2px 0 0 2px',
        },
      },
      'last': {
        swatch: {
          borderRadius: '0 2px 2px 0',
        },
      },
      active: {
        swatch: {
          transform: 'scaleY(1.8)',
          borderRadius: '3.6px/2px',
        },
      },
    }
  }

  handleClick() {
    this.props.onClick({
      h: this.props.hsl.h,
      s: .5,
      l: this.props.offset,
      source: 'hsl',
    })
  }

  render(): any {
    return (

    )
  }

}

export default SliderSwatch
<|endoftext|>


<commit_before>/*** Vector functions - vectors provided as [x, y, z] arrays ***/

export var Vector = {};

Vector.set = function (v) {
    var V = [];
    var lim = v.length;
    for (var i = 0; i < lim; i++) {
        V[i] = v[i];
    }
    return V;
};

Vector.neg = function (v) {
    var V = [];
    var lim = v.length;
    for (var i = 0; i < lim; i++) {
        V[i] = v[i] * -1;
    }
    return V;
};

// Addition of two vectors
Vector.add = function (v1, v2) {
    var v = [];
    var lim = Math.min(v1.length,v2.length);
    for (var i = 0; i < lim; i++) {
        v[i] = v1[i] + v2[i];
    }
    return v;
};

// Substraction of two vectors
Vector.sub = function (v1, v2) {
    var v = [];
    var lim = Math.min(v1.length,v2.length);

    for (var i = 0; i < lim; i++) {
        v[i] = v1[i] - v2[i];
    }
    return v;
};

// Multiplication of two vectors
Vector.mult = function (v1, v2) {
    var v = [],
        len = v1.length,
        i;

    if (typeof v2 === 'number') {
        // Mulitply by scalar
        for (i = 0; i < len; i++) {
            v[i] = v1[i] * v2;
        }
    }
    else {
        // Multiply two vectors
        len = Math.min(v1.length,v2.length);
        for (i = 0; i < len; i++) {
            v[i] = v1[i] * v2[i];
        }
    }
    return v;
};

// Division of two vectors
Vector.div = function (v1, v2) {
    var v = [],
        i;
    if(typeof v2 === 'number'){
        // Divide by scalar
        for (i = 0; i < v1.length; i++){
            v[i] = v1[i] / v2;
        }
    } else {
        // Divide to vectors
        var len = Math.min(v1.length,v2.length);
        for (i = 0; i < len; i++) {
            v[i] = v1[i] / v2[i];
        }
    }
    return v;
};

// Get 2D perpendicular
Vector.perp = function (v1, v2) {
    return [ v2[1] - v1[1],
             v1[0] - v2[0] ];
};

// Get 2D vector rotated
Vector.rot = function (v, a) {
    var vr = Vector.length(v);
    var va = Vector.angle(v);
    return [vr * Math.cos(va+a),
            vr * Math.sin(va+a)];
};

// Get 2D heading angle
Vector.angle = function ([x, y]) {
    return Math.atan2(y,x);
};

// Compare two points
Vector.isEqual = function (v1, v2) {
    var len = v1.length;
    for (var i = 0; i < len; i++) {
        if (v1[i] != v2[i]){
            return false;
        }
    }
    return true;
};

// Vector length squared
Vector.lengthSq = function (v)
{
    if (v.length === 2) {
        return (v[0]*v[0] + v[1]*v[1]);
    }
    else {
        return (v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
    }
};

// Vector length
Vector.length = function (v)
{
    return Math.sqrt(Vector.lengthSq(v));
};

// Normalize a vector
Vector.normalize = function (v)
{
    var d;
    if (v.length === 2) {
        d = v[0]*v[0] + v[1]*v[1];
        d = Math.sqrt(d);

        if (d !== 0) {
            return [v[0] / d, v[1] / d];
        }
        return [0, 0];
    } else {
        d = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
        d = Math.sqrt(d);

        if (d !== 0) {
            return [v[0] / d, v[1] / d, v[2] / d];
        }
        return [0, 0, 0];
    }
};

// Cross product of two vectors
Vector.cross  = function (v1, v2)
{
    return [
        (v1[1] * v2[2]) - (v1[2] * v2[1]),
        (v1[2] * v2[0]) - (v1[0] * v2[2]),
        (v1[0] * v2[1]) - (v1[1] * v2[0])
    ];
};

// Dot product of two vectors
Vector.dot = function (v1, v2)
{
    var n = 0;
    var lim = Math.min(v1.length, v2.length);
    for (var i = 0; i < lim; i++) {
        n += v1[i] * v2[i];
    }
    return n;
};

// Find the intersection of two lines specified as segments from points (p1, p2) and (p3, p4)
// http://en.wikipedia.org/wiki/Line-line_intersection
// http://en.wikipedia.org/wiki/Cramer's_rule
Vector.lineIntersection = function (p1, p2, p3, p4, parallel_tolerance)
{
    parallel_tolerance = parallel_tolerance || 0.01;

    // a1*x + b1*y = c1 for line (x1, y1) to (x2, y2)
    // a2*x + b2*y = c2 for line (x3, y3) to (x4, y4)
    var a1 = p1[1] - p2[1]; // y1 - y2
    var b1 = p1[0] - p2[0]; // x1 - x2
    var a2 = p3[1] - p4[1]; // y3 - y4
    var b2 = p3[0] - p4[0]; // x3 - x4
    var c1 = (p1[0] * p2[1]) - (p1[1] * p2[0]); // x1*y2 - y1*x2
    var c2 = (p3[0] * p4[1]) - (p3[1] * p4[0]); // x3*y4 - y3*x4
    var denom = (b1 * a2) - (a1 * b2);

    if (Math.abs(denom) > parallel_tolerance) {
        return [
            ((c1 * b2) - (b1 * c2)) / denom,
            ((c1 * a2) - (a1 * c2)) / denom
        ];
    }
    return null; // return null if lines are (close to) parallel
};
<commit_msg>lint aproval<commit_after>/*** Vector functions - vectors provided as [x, y, z] arrays ***/

export var Vector = {};

Vector.set = function (v) {
    var V = [];
    var lim = v.length;
    for (var i = 0; i < lim; i++) {
        V[i] = v[i];
    }
    return V;
};

Vector.neg = function (v) {
    var V = [];
    var lim = v.length;
    for (var i = 0; i < lim; i++) {
        V[i] = v[i] * -1;
    }
    return V;
};

// Addition of two vectors
Vector.add = function (v1, v2) {
    var v = [];
    var lim = Math.min(v1.length,v2.length);
    for (var i = 0; i < lim; i++) {
        v[i] = v1[i] + v2[i];
    }
    return v;
};

// Substraction of two vectors
Vector.sub = function (v1, v2) {
    var v = [];
    var lim = Math.min(v1.length,v2.length);

    for (var i = 0; i < lim; i++) {
        v[i] = v1[i] - v2[i];
    }
    return v;
};

// Multiplication of two vectors
Vector.mult = function (v1, v2) {
    var v = [],
        len = v1.length,
        i;

    if (typeof v2 === 'number') {
        // Mulitply by scalar
        for (i = 0; i < len; i++) {
            v[i] = v1[i] * v2;
        }
    }
    else {
        // Multiply two vectors
        len = Math.min(v1.length,v2.length);
        for (i = 0; i < len; i++) {
            v[i] = v1[i] * v2[i];
        }
    }
    return v;
};

// Division of two vectors
Vector.div = function (v1, v2) {
    var v = [],
        i;
    if(typeof v2 === 'number'){
        // Divide by scalar
        for (i = 0; i < v1.length; i++){
            v[i] = v1[i] / v2;
        }
    } else {
        // Divide to vectors
        var len = Math.min(v1.length,v2.length);
        for (i = 0; i < len; i++) {
            v[i] = v1[i] / v2[i];
        }
    }
    return v;
};

// Get 2D perpendicular
Vector.perp = function (v1, v2) {
    return [ v2[1] - v1[1],
             v1[0] - v2[0] ];
};

// Get 2D vector rotated
Vector.rot = function (v, a) {
    var vr = Vector.length(v);
    var va = Vector.angle(v);
    return [vr * Math.cos(va+a),
            vr * Math.sin(va+a)];
};

// Get 2D heading angle
Vector.angle = function ([x, y]) {
    return Math.atan2(y,x);
};

// Compare two points
Vector.isEqual = function (v1, v2) {
    var len = v1.length;
    for (var i = 0; i < len; i++) {
        if (v1[i] !== v2[i]){
            return false;
        }
    }
    return true;
};

// Vector length squared
Vector.lengthSq = function (v)
{
    if (v.length === 2) {
        return (v[0]*v[0] + v[1]*v[1]);
    }
    else {
        return (v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
    }
};

// Vector length
Vector.length = function (v)
{
    return Math.sqrt(Vector.lengthSq(v));
};

// Normalize a vector
Vector.normalize = function (v)
{
    var d;
    if (v.length === 2) {
        d = v[0]*v[0] + v[1]*v[1];
        d = Math.sqrt(d);

        if (d !== 0) {
            return [v[0] / d, v[1] / d];
        }
        return [0, 0];
    } else {
        d = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
        d = Math.sqrt(d);

        if (d !== 0) {
            return [v[0] / d, v[1] / d, v[2] / d];
        }
        return [0, 0, 0];
    }
};

// Cross product of two vectors
Vector.cross  = function (v1, v2)
{
    return [
        (v1[1] * v2[2]) - (v1[2] * v2[1]),
        (v1[2] * v2[0]) - (v1[0] * v2[2]),
        (v1[0] * v2[1]) - (v1[1] * v2[0])
    ];
};

// Dot product of two vectors
Vector.dot = function (v1, v2)
{
    var n = 0;
    var lim = Math.min(v1.length, v2.length);
    for (var i = 0; i < lim; i++) {
        n += v1[i] * v2[i];
    }
    return n;
};

// Find the intersection of two lines specified as segments from points (p1, p2) and (p3, p4)
// http://en.wikipedia.org/wiki/Line-line_intersection
// http://en.wikipedia.org/wiki/Cramer's_rule
Vector.lineIntersection = function (p1, p2, p3, p4, parallel_tolerance)
{
    parallel_tolerance = parallel_tolerance || 0.01;

    // a1*x + b1*y = c1 for line (x1, y1) to (x2, y2)
    // a2*x + b2*y = c2 for line (x3, y3) to (x4, y4)
    var a1 = p1[1] - p2[1]; // y1 - y2
    var b1 = p1[0] - p2[0]; // x1 - x2
    var a2 = p3[1] - p4[1]; // y3 - y4
    var b2 = p3[0] - p4[0]; // x3 - x4
    var c1 = (p1[0] * p2[1]) - (p1[1] * p2[0]); // x1*y2 - y1*x2
    var c2 = (p3[0] * p4[1]) - (p3[1] * p4[0]); // x3*y4 - y3*x4
    var denom = (b1 * a2) - (a1 * b2);

    if (Math.abs(denom) > parallel_tolerance) {
        return [
            ((c1 * b2) - (b1 * c2)) / denom,
            ((c1 * a2) - (a1 * c2)) / denom
        ];
    }
    return null; // return null if lines are (close to) parallel
};
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { push } from 'react-router-redux';
import moment from 'moment';
import { ascendingBy } from 'utils';
// STORE
import { loadUsers } from 'redux/modules/usersModule';
// COMPONENTS
import { LoadingScreen } from 'components';
// LAYOUT
import Grid from 'react-bootstrap/lib/Grid';
import { List, ListItem } from 'material-ui/List';
import Avatar from 'material-ui/Avatar';
import styles from './UsersPage.scss';

const mappedState = ({ users }) => ({
  users: users.all,
  loadingUsers: users.loadingUsers,
  usersLoaded: users.usersLoaded,
});

const mappedActions = {
  loadUsers,
  pushState: push
};

@connect(mappedState, mappedActions)
class UsersPage extends Component {
  static propTypes = {
    users: PropTypes.array.isRequired,
    loadingUsers: PropTypes.bool.isRequired,
    usersLoaded: PropTypes.bool.isRequired,
    loadUsers: PropTypes.func.isRequired,
    pushState: PropTypes.func.isRequired
  }

  componentWillMount() {
    if (!this.props.usersLoaded) this.props.loadUsers();
  }

  handleUserClick(userId) {
    this.props.pushState(`/user/${userId}`);
  }

  render() {
    return (
      <LoadingScreen loading={this.props.loadingUsers}>
        <Grid className={styles.UsersPage}>

Users

          <List>
            {
              this.props.users.sort(ascendingBy('firstName')).map((user) => {
                const date = moment(user.createdAt).format('MMMM Do YYYY');
                return (
                  <ListItem
                    key={user.id}
                    primaryText={`${user.firstName} ${user.lastName}`}
                    secondaryText={`User since: ${date}`}
                    leftAvatar={<Avatar src={user.pictureURL} />}
                    onClick={() => this.handleUserClick(user.id)}
                  />
                );
              })
            }
          </List>
        </Grid>
      </LoadingScreen>
    );
  }
}

export default UsersPage;
<commit_msg>Fix redirections from users' page<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { push } from 'react-router-redux';
import moment from 'moment';
import { ascendingBy } from 'utils';
// STORE
import { loadUsers } from 'redux/modules/usersModule';
// COMPONENTS
import { LoadingScreen } from 'components';
// LAYOUT
import Grid from 'react-bootstrap/lib/Grid';
import { List, ListItem } from 'material-ui/List';
import Avatar from 'material-ui/Avatar';
import styles from './UsersPage.scss';

const mappedState = ({ auth, users }) => ({
  user: auth.user,
  users: users.all,
  loadingUsers: users.loadingUsers,
  usersLoaded: users.usersLoaded,
});

const mappedActions = {
  loadUsers,
  pushState: push
};

@connect(mappedState, mappedActions)
class UsersPage extends Component {
  static propTypes = {
    user: PropTypes.object,
    users: PropTypes.array.isRequired,
    loadingUsers: PropTypes.bool.isRequired,
    usersLoaded: PropTypes.bool.isRequired,
    loadUsers: PropTypes.func.isRequired,
    pushState: PropTypes.func.isRequired
  }

  componentWillMount() {
    if (!this.props.usersLoaded) this.props.loadUsers();
  }

  handleUserClick(userId) {
    // If user clicked on himself, redirect him to his profile page. Otherwise show user details.
    const route = userId === this.props.user.id ? '/profile' : `/user/${userId}`;
    this.props.pushState(route);
  }

  render() {
    return (
      <LoadingScreen loading={this.props.loadingUsers}>
        <Grid className={styles.UsersPage}>

Users

          <List>
            {
              this.props.users.sort(ascendingBy('firstName')).map((user) => {
                const date = moment(user.createdAt).format('MMMM Do YYYY');
                return (
                  <ListItem
                    key={user.id}
                    primaryText={`${user.firstName} ${user.lastName}`}
                    secondaryText={`User since: ${date}`}
                    leftAvatar={<Avatar src={user.pictureURL} />}
                    onClick={() => this.handleUserClick(user.id)}
                  />
                );
              })
            }
          </List>
        </Grid>
      </LoadingScreen>
    );
  }
}

export default UsersPage;
<|endoftext|>


<commit_before>'use strict';
/**
 * Handles matching page URL with defined connectors and injecting scripts into content document
 */
define((require) => {
	const browser = require('webextension-polyfill');
	const Options = require('storage/options');
	const UrlMatch = require('util/url-match');
	const connectors = require('connectors');
	const InjectResult = require('object/inject-result');
	const CustomPatterns = require('storage/custom-patterns');

	/**
	 * Ping the loaded page and checks if there is already loaded connector.
	 * If no connector is loaded, inject content scripts.
	 *
	 * @param {Number} tabId Tab ID
	 * @param {Object} connector Connector match object
	 *
	 * @return {Object} InjectResult value
	 */
	async function pingAndInject(tabId, connector) {
		// Ping the content page to see if the script is already in place.
		try {
			await browser.tabs.sendMessage(tabId, { type: 'EVENT_PING' });
		} catch (e) {
			return injectScripts(tabId, connector);
		}

		console.log(
			'Subsequent ajax navigation, the scripts are already injected');

		return new InjectResult(InjectResult.ALREADY_INJECTED, connector);
	}

	/**
	 * Inject content scripts into the page.
	 *
	 * @param {Number} tabId Tab ID
	 * @param {Object} connector Connector match object
	 *
	 * @return {Object} InjectResult value
	 */
	async function injectScripts(tabId, connector) {
		const scripts = connector.js.slice(0);
		scripts.unshift('core/content/connector.js');
		scripts.unshift('core/content/filter.js');
		scripts.unshift('core/content/reactor.js');
		// @ifdef DEBUG
		scripts.unshift('core/content/reporter.js');
		// @endif
		scripts.unshift('core/content/util.js');
		scripts.unshift('vendor/browser-polyfill.min.js');
		scripts.unshift('vendor/jquery.min.js');
		// Needs to be the last script injected
		scripts.push('core/content/starter.js');

		for (const script of scripts) {
			const injectDetails = {
				file: script,
				allFrames: connector.allFrames || false
			};

			console.log(`Injecting ${script}`);
			try {
				await browser.tabs.executeScript(tabId, injectDetails);
			} catch (e) {
				// Firefox throws an error if a content script returns no value.
				console.error(e);
			}
		}

		const isEnabled = await Options.isConnectorEnabled(connector.label);
		if (isEnabled) {
			return new InjectResult(
				InjectResult.MATCHED_AND_INJECTED, connector);
		}

		return new InjectResult(InjectResult.MATCHED_BUT_DISABLED, connector);
	}

	/**
	 * Is triggered by browser.tabs.onUpdated event
	 * Check for available connectors and injects matching connector into
	 * loaded page while returning info about the connector.
	 *
	 * @param  {Object} tab Tab object
	 * @return {Object} InjectResult value
	 */
	async function onTabsUpdated(tab) {
		// Asynchronously preload all custom patterns and then start matching
		let customPatterns = await CustomPatterns.getAllPatterns();
		for (let connector of connectors) {
			let matchOk = false;
			let patterns = connector.matches || [];

			if (customPatterns[connector.label]) {
				patterns = patterns.concat(customPatterns[connector.label]);
			}

			for (let pattern of patterns) {
				matchOk = matchOk || UrlMatch.test(tab.url, pattern);
			}

			if (matchOk) {
				/*
				 * Checks if there's already injected connector
				 * and injects it if needed.
				 */
				return pingAndInject(tab.id, connector);

			}
		}

		return new InjectResult(InjectResult.NO_MATCH, null);
	}

	return { onTabsUpdated };
});
<commit_msg>Use spread syntax to construct array of content scripts<commit_after>'use strict';
/**
 * Handles matching page URL with defined connectors and injecting scripts into content document
 */
define((require) => {
	const browser = require('webextension-polyfill');
	const Options = require('storage/options');
	const UrlMatch = require('util/url-match');
	const connectors = require('connectors');
	const InjectResult = require('object/inject-result');
	const CustomPatterns = require('storage/custom-patterns');

	const CONTENT_SCRIPTS = [
		'vendor/jquery.min.js',
		'vendor/browser-polyfill.min.js',
		'core/content/util.js',
		// @ifdef DEBUG
		'core/content/reporter.js',
		// @endif
		'core/content/reactor.js',
		'core/content/filter.js',
		'core/content/connector.js',
	];
	const STARTER_SCRIPT = 'core/content/starter.js';

	/**
	 * Ping the loaded page and checks if there is already loaded connector.
	 * If no connector is loaded, inject content scripts.
	 *
	 * @param {Number} tabId Tab ID
	 * @param {Object} connector Connector match object
	 *
	 * @return {Object} InjectResult value
	 */
	async function pingAndInject(tabId, connector) {
		// Ping the content page to see if the script is already in place.
		try {
			await browser.tabs.sendMessage(tabId, { type: 'EVENT_PING' });
		} catch (e) {
			return injectScripts(tabId, connector);
		}

		console.log(
			'Subsequent ajax navigation, the scripts are already injected');

		return new InjectResult(InjectResult.ALREADY_INJECTED, connector);
	}

	/**
	 * Inject content scripts into the page.
	 *
	 * @param {Number} tabId Tab ID
	 * @param {Object} connector Connector match object
	 *
	 * @return {Object} InjectResult value
	 */
	async function injectScripts(tabId, connector) {
		const scripts = [
			...CONTENT_SCRIPTS, ...connector.js, STARTER_SCRIPT
		];
		console.log(scripts);

		for (const script of scripts) {
			const injectDetails = {
				file: script,
				allFrames: connector.allFrames || false
			};

			console.log(`Injecting ${script}`);
			try {
				await browser.tabs.executeScript(tabId, injectDetails);
			} catch (e) {
				// Firefox throws an error if a content script returns no value.
				console.error(e);
			}
		}

		const isEnabled = await Options.isConnectorEnabled(connector.label);
		if (isEnabled) {
			return new InjectResult(
				InjectResult.MATCHED_AND_INJECTED, connector);
		}

		return new InjectResult(InjectResult.MATCHED_BUT_DISABLED, connector);
	}

	/**
	 * Is triggered by browser.tabs.onUpdated event
	 * Check for available connectors and injects matching connector into
	 * loaded page while returning info about the connector.
	 *
	 * @param  {Object} tab Tab object
	 * @return {Object} InjectResult value
	 */
	async function onTabsUpdated(tab) {
		// Asynchronously preload all custom patterns and then start matching
		let customPatterns = await CustomPatterns.getAllPatterns();
		for (let connector of connectors) {
			let matchOk = false;
			let patterns = connector.matches || [];

			if (customPatterns[connector.label]) {
				patterns = patterns.concat(customPatterns[connector.label]);
			}

			for (let pattern of patterns) {
				matchOk = matchOk || UrlMatch.test(tab.url, pattern);
			}

			if (matchOk) {
				/*
				 * Checks if there's already injected connector
				 * and injects it if needed.
				 */
				return pingAndInject(tab.id, connector);

			}
		}

		return new InjectResult(InjectResult.NO_MATCH, null);
	}

	return { onTabsUpdated };
});
<|endoftext|>


<commit_before>/*
**  ComponentJS -- Component System for JavaScript <http://componentjs.com>
**  Copyright (c) 2009-2014 Ralf S. Engelschall <http://engelschall.com>
**
**  This Source Code Form is subject to the terms of the Mozilla Public
**  License (MPL), version 2.0. If a copy of the MPL was not distributed
**  with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

/*  utility function: no operation (for passing as dummy callback)  */
_cs.nop = function () {};

/*  utility function: annotate an object  */
_cs.annotation = function (obj, name, value) {
    var result = null;
    var __name__ = "__ComponentJS_" + name + "__";
    if (typeof obj !== "undefined" && obj !== null) {
        /*  get annotation value  */
        if (typeof obj[__name__] !== "undefined")
            result = obj[__name__];
        if (typeof value !== "undefined") {
            /*  set annotation value  */
            if (value !== null)
                obj[__name__] = value;
            else
                delete obj[__name__];
        }
    }
    return result;
};

/*  utility function: conveniently check for defined variable  */
_cs.isdefined = function (obj) {
    return (typeof obj !== "undefined");
};

/*  utility function: check whether a field is directly owned by object
    (instead of implicitly resolved through the constructor's prototype object)  */
_cs.isown = function (obj, field) {
    var isown = Object.hasOwnProperty.call(obj, field);
    if (field === "constructor" || field === "prototype") {
        isown = isown && Object.propertyIsEnumerable.call(obj, field);
        if (obj[field].toString().indexOf("[native code]") !== -1)
            isown = false;
    }
    return isown;
};

/*  utility function: determine type of anything,
    an improved version of the built-in "typeof" operator  */
_cs.istypeof = function (obj) {
    var type = typeof obj;
    if (type === "object") {
        if (obj === null)
            /*  JavaScript nasty special case: null object  */
            type = "null";
        else if (Object.prototype.toString.call(obj) === "[object String]")
            /*  JavaScript nasty special case: String object  */
            type = "string";
        else if (Object.prototype.toString.call(obj) === "[object Number]")
            /*  JavaScript nasty special case: Number object  */
            type = "number";
        else if (Object.prototype.toString.call(obj) === "[object Boolean]")
            /*  JavaScript nasty special case: Boolean object  */
            type = "boolean";
        else if (Object.prototype.toString.call(obj) === "[object Function]")
            /*  JavaScript nasty special case: Function object  */
            type = "function";
        else if (Object.prototype.toString.call(obj) === "[object Array]")
            /*  JavaScript nasty special case: Array object  */
            type = "array";
        else if (_cs.annotation(obj, "type") !== null)
            /*  ComponentJS special case: "component"  */
            type = _cs.annotation(obj, "type");
    }
    else if (type === "function") {
        /*  ComponentJS special case: "{clazz,trait}"  */
        if (_cs.annotation(obj, "type") !== null)
            type = _cs.annotation(obj, "type");
    }
    return type;
};

/*  utility function: retrieve keys of object  */
_cs.keysof = function (obj) {
    var keys = [];
    for (var key in obj) {
        if (Object.hasOwnProperty.call(obj, key))
            keys.push(key);
    }
    return keys;
};

/*  utility function: JSON encoding of object  */
_cs.json = (function () {
    var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
    var meta = { "\b": "\\b", "\t": "\\t", "\n": "\\n", "\f": "\\f", "\r": "\\r", "\"": "\\\"", "\\": "\\\\" };
    var quote = function (string) {
        escapable.lastIndex = 0;
        return (
            escapable.test(string)
            ? "\"" + string.replace(escapable, function (a) {
                  var c = meta[a];
                  return typeof c === "string"
                      ? c
                      : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
              }) + "\""
            : "\"" + string + "\""
        );
    };
    var encode = function (value, seen) {
        if (typeof value !== "boolean" && typeof value !== "number" && typeof value !== "string") {
            if (typeof seen[value] !== "undefined")
                return "null /* CYCLE! */";
            else
                seen[value] = true;
        }
        switch (typeof value) {
            case "boolean":  value = String(value); break;
            case "number":   value = (isFinite(value) ? String(value) : "NaN"); break;
            case "string":   value = quote(value); break;
            case "function":
                if (_cs.annotation(value, "type") !== null)
                    value = "<" + _cs.annotation(value, "type") + ">";
                else
                    value = "<function>";
                break;
            case "object":
                var a = [];
                if (value === null)
                    value = "null";
                else if (_cs.annotation(value, "type") !== null)
                    value = "<" + _cs.annotation(value, "type") + ">";
                else if (Object.prototype.toString.call(value) === "[object Function]")
                    value = "<function>";
                else if (   Object.prototype.toString.call(value) === "[object Array]"
                         || value instanceof Array) {
                    for (var i = 0; i < value.length; i++)
                        a[i] = arguments.callee(value[i], seen); /* RECURSION */
                    value = (a.length === 0 ? "[]" : "[" + a.join(",") + "]");
                }
                else {
                    for (var k in value) {
                        if (Object.hasOwnProperty.call(value, k)) {
                            var v = arguments.callee(value[k], seen); /* RECURSION */
                            a.push(quote(k) + ":" + v);
                        }
                    }
                    value = (a.length === 0 ? "{}" : "{" + a.join(",") + "}");
                }
                break;
            default:
                value = "<unknown>";
                break;
        }
        return value;
    };
    return function (value) {
        return encode(value, {});
    };
})();

/*  utility function: deep cloning of arbitrary data-structure  */
_cs.clone = function (source, continue_recursion) {
    /*  allow recursive cloning to be controlled  */
    if (typeof continue_recursion === "undefined")
        continue_recursion = function (/* name, value */) { return true; };
    else if (typeof continue_recursion === "string") {
        var pattern = continue_recursion;
        continue_recursion = function (name /*, value */) { return name.match(pattern); };
    }

    /*  helper functions  */
    var myself = arguments.callee;
    var clone_func = function (f, continue_recursion) {
        /* eslint no-unused-vars: 0 */
        var g = function ComponentJS_function_clone () {
            return f.apply(this, arguments);
        };
        g.prototype = f.prototype;
        for (var prop in f) {
            if (_cs.isown(f, prop)) {
                if (continue_recursion(prop, f))
                    g[prop] = myself(f[prop], continue_recursion); /* RECURSION */
                else
                    g[prop] = f[prop];
            }
        }
        _cs.annotation(g, "clone", true);
        return g;
    };

    var target; target = undefined;
    if (typeof source === "function")
        /*  special case: primitive function  */
        target = clone_func(source, continue_recursion);
    else if (typeof source === "object") {
        if (source === null)
            /*  special case: null object  */
            target = null;
        else if (Object.prototype.toString.call(source) === "[object String]")
            /*  special case: String object  */
            target = "" + source.valueOf();
        else if (Object.prototype.toString.call(source) === "[object Number]")
            /*  special case: Number object  */
            target = 0 + source.valueOf();
        else if (Object.prototype.toString.call(source) === "[object Boolean]")
            /*  special case: Boolean object  */
            target = !!source.valueOf();
        else if (Object.prototype.toString.call(source) === "[object Function]")
            /*  special case: Function object  */
            target = clone_func(source, continue_recursion);
        else if (Object.prototype.toString.call(source) === "[object Date]")
            /*  special case: Date object  */
            target = new Date(source.getTime());
        else if (Object.prototype.toString.call(source) === "[object RegExp]")
            /*  special case: RegExp object  */
            target = new RegExp(source.source);
        else if (Object.prototype.toString.call(source) === "[object Array]") {
            /*  special case: array object  */
            var len = source.length;
            target = [];
            for (var i = 0; i < len; i++)
                target.push(myself(source[i], continue_recursion)); /* RECURSION */
        }
        else {
            /*  special case: hash object  */
            target = {};
            for (var key in source) {
                if (key !== "constructor" && _cs.isown(source, key)) {
                    if (continue_recursion(key, source))
                        target[key] = myself(source[key], continue_recursion); /* RECURSION */
                    else
                        target[key] = source[key];
                }
            }
            if (typeof source.constructor === "function")
                target.constructor = source.constructor;
            if (typeof source.prototype === "object")
                target.prototype = source.prototype;
        }
    }
    else
        /*  regular case: anything else
            (just primitive data types and undefined value)  */
        target = source;
    return target;
};

/*  utility function: extend an object with other object(s)  */
_cs.extend = function (target, source, filter) {
    if (typeof filter === "undefined")
        filter = function (/* name, value */) { return true; };
    else if (typeof filter === "string") {
        var pattern = filter;
        filter = function (name /*, value */) { return name.match(pattern); };
    }
    for (var key in source)
        if (_cs.isown(source, key))
            if (filter(key, source[key]))
                target[key] = source[key];
    return target;
};

/*  utility function: mixin objects into another object by chaining methods  */
_cs.mixin = function (target, source, filter) {
    if (typeof filter === "undefined")
        filter = function (/* name, value */) { return true; };
    else if (typeof filter === "string") {
        var pattern = filter;
        filter = function (name /*, value */) { return name.match(pattern); };
    }
    for (var key in source) {
        if (_cs.isown(source, key)) {
            if (filter(key, source[key])) {
                if (_cs.istypeof(source[key]) === "function") {
                    /*  method/function  */
                    var src = _cs.clone(source[key], filter);
                    _cs.annotation(src, "name", key);
                    if (   _cs.istypeof(target[key]) === "function"
                        && _cs.isown(target, key)                  )
                        _cs.annotation(src, "base", target[key]);
                    target[key] = src;
                }
                else {
                    /*  property/field  */
                    target[key] = source[key];
                }
            }
        }
    }
    return target;
};

<commit_msg>fix internal function keysof() by correctly skipping also 'constructor', etc<commit_after>/*
**  ComponentJS -- Component System for JavaScript <http://componentjs.com>
**  Copyright (c) 2009-2014 Ralf S. Engelschall <http://engelschall.com>
**
**  This Source Code Form is subject to the terms of the Mozilla Public
**  License (MPL), version 2.0. If a copy of the MPL was not distributed
**  with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

/*  utility function: no operation (for passing as dummy callback)  */
_cs.nop = function () {};

/*  utility function: annotate an object  */
_cs.annotation = function (obj, name, value) {
    var result = null;
    var __name__ = "__ComponentJS_" + name + "__";
    if (typeof obj !== "undefined" && obj !== null) {
        /*  get annotation value  */
        if (typeof obj[__name__] !== "undefined")
            result = obj[__name__];
        if (typeof value !== "undefined") {
            /*  set annotation value  */
            if (value !== null)
                obj[__name__] = value;
            else
                delete obj[__name__];
        }
    }
    return result;
};

/*  utility function: conveniently check for defined variable  */
_cs.isdefined = function (obj) {
    return (typeof obj !== "undefined");
};

/*  utility function: check whether a field is directly owned by object
    (instead of implicitly resolved through the constructor's prototype object)  */
_cs.isown = function (obj, field) {
    var isown = Object.hasOwnProperty.call(obj, field);
    if (field === "constructor" || field === "prototype") {
        isown = isown && Object.propertyIsEnumerable.call(obj, field);
        if (obj[field].toString().indexOf("[native code]") !== -1)
            isown = false;
    }
    return isown;
};

/*  utility function: determine type of anything,
    an improved version of the built-in "typeof" operator  */
_cs.istypeof = function (obj) {
    var type = typeof obj;
    if (type === "object") {
        if (obj === null)
            /*  JavaScript nasty special case: null object  */
            type = "null";
        else if (Object.prototype.toString.call(obj) === "[object String]")
            /*  JavaScript nasty special case: String object  */
            type = "string";
        else if (Object.prototype.toString.call(obj) === "[object Number]")
            /*  JavaScript nasty special case: Number object  */
            type = "number";
        else if (Object.prototype.toString.call(obj) === "[object Boolean]")
            /*  JavaScript nasty special case: Boolean object  */
            type = "boolean";
        else if (Object.prototype.toString.call(obj) === "[object Function]")
            /*  JavaScript nasty special case: Function object  */
            type = "function";
        else if (Object.prototype.toString.call(obj) === "[object Array]")
            /*  JavaScript nasty special case: Array object  */
            type = "array";
        else if (_cs.annotation(obj, "type") !== null)
            /*  ComponentJS special case: "component"  */
            type = _cs.annotation(obj, "type");
    }
    else if (type === "function") {
        /*  ComponentJS special case: "{clazz,trait}"  */
        if (_cs.annotation(obj, "type") !== null)
            type = _cs.annotation(obj, "type");
    }
    return type;
};

/*  utility function: retrieve keys of object  */
_cs.keysof = function (obj) {
    var keys = [];
    for (var key in obj) {
        if (_cs.isown(obj, key))
            keys.push(key);
    }
    return keys;
};

/*  utility function: JSON encoding of object  */
_cs.json = (function () {
    var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
    var meta = { "\b": "\\b", "\t": "\\t", "\n": "\\n", "\f": "\\f", "\r": "\\r", "\"": "\\\"", "\\": "\\\\" };
    var quote = function (string) {
        escapable.lastIndex = 0;
        return (
            escapable.test(string)
            ? "\"" + string.replace(escapable, function (a) {
                  var c = meta[a];
                  return typeof c === "string"
                      ? c
                      : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
              }) + "\""
            : "\"" + string + "\""
        );
    };
    var encode = function (value, seen) {
        if (typeof value !== "boolean" && typeof value !== "number" && typeof value !== "string") {
            if (typeof seen[value] !== "undefined")
                return "null /* CYCLE! */";
            else
                seen[value] = true;
        }
        switch (typeof value) {
            case "boolean":  value = String(value); break;
            case "number":   value = (isFinite(value) ? String(value) : "NaN"); break;
            case "string":   value = quote(value); break;
            case "function":
                if (_cs.annotation(value, "type") !== null)
                    value = "<" + _cs.annotation(value, "type") + ">";
                else
                    value = "<function>";
                break;
            case "object":
                var a = [];
                if (value === null)
                    value = "null";
                else if (_cs.annotation(value, "type") !== null)
                    value = "<" + _cs.annotation(value, "type") + ">";
                else if (Object.prototype.toString.call(value) === "[object Function]")
                    value = "<function>";
                else if (   Object.prototype.toString.call(value) === "[object Array]"
                         || value instanceof Array) {
                    for (var i = 0; i < value.length; i++)
                        a[i] = arguments.callee(value[i], seen); /* RECURSION */
                    value = (a.length === 0 ? "[]" : "[" + a.join(",") + "]");
                }
                else {
                    for (var k in value) {
                        if (Object.hasOwnProperty.call(value, k)) {
                            var v = arguments.callee(value[k], seen); /* RECURSION */
                            a.push(quote(k) + ":" + v);
                        }
                    }
                    value = (a.length === 0 ? "{}" : "{" + a.join(",") + "}");
                }
                break;
            default:
                value = "<unknown>";
                break;
        }
        return value;
    };
    return function (value) {
        return encode(value, {});
    };
})();

/*  utility function: deep cloning of arbitrary data-structure  */
_cs.clone = function (source, continue_recursion) {
    /*  allow recursive cloning to be controlled  */
    if (typeof continue_recursion === "undefined")
        continue_recursion = function (/* name, value */) { return true; };
    else if (typeof continue_recursion === "string") {
        var pattern = continue_recursion;
        continue_recursion = function (name /*, value */) { return name.match(pattern); };
    }

    /*  helper functions  */
    var myself = arguments.callee;
    var clone_func = function (f, continue_recursion) {
        /* eslint no-unused-vars: 0 */
        var g = function ComponentJS_function_clone () {
            return f.apply(this, arguments);
        };
        g.prototype = f.prototype;
        for (var prop in f) {
            if (_cs.isown(f, prop)) {
                if (continue_recursion(prop, f))
                    g[prop] = myself(f[prop], continue_recursion); /* RECURSION */
                else
                    g[prop] = f[prop];
            }
        }
        _cs.annotation(g, "clone", true);
        return g;
    };

    var target; target = undefined;
    if (typeof source === "function")
        /*  special case: primitive function  */
        target = clone_func(source, continue_recursion);
    else if (typeof source === "object") {
        if (source === null)
            /*  special case: null object  */
            target = null;
        else if (Object.prototype.toString.call(source) === "[object String]")
            /*  special case: String object  */
            target = "" + source.valueOf();
        else if (Object.prototype.toString.call(source) === "[object Number]")
            /*  special case: Number object  */
            target = 0 + source.valueOf();
        else if (Object.prototype.toString.call(source) === "[object Boolean]")
            /*  special case: Boolean object  */
            target = !!source.valueOf();
        else if (Object.prototype.toString.call(source) === "[object Function]")
            /*  special case: Function object  */
            target = clone_func(source, continue_recursion);
        else if (Object.prototype.toString.call(source) === "[object Date]")
            /*  special case: Date object  */
            target = new Date(source.getTime());
        else if (Object.prototype.toString.call(source) === "[object RegExp]")
            /*  special case: RegExp object  */
            target = new RegExp(source.source);
        else if (Object.prototype.toString.call(source) === "[object Array]") {
            /*  special case: array object  */
            var len = source.length;
            target = [];
            for (var i = 0; i < len; i++)
                target.push(myself(source[i], continue_recursion)); /* RECURSION */
        }
        else {
            /*  special case: hash object  */
            target = {};
            for (var key in source) {
                if (key !== "constructor" && _cs.isown(source, key)) {
                    if (continue_recursion(key, source))
                        target[key] = myself(source[key], continue_recursion); /* RECURSION */
                    else
                        target[key] = source[key];
                }
            }
            if (typeof source.constructor === "function")
                target.constructor = source.constructor;
            if (typeof source.prototype === "object")
                target.prototype = source.prototype;
        }
    }
    else
        /*  regular case: anything else
            (just primitive data types and undefined value)  */
        target = source;
    return target;
};

/*  utility function: extend an object with other object(s)  */
_cs.extend = function (target, source, filter) {
    if (typeof filter === "undefined")
        filter = function (/* name, value */) { return true; };
    else if (typeof filter === "string") {
        var pattern = filter;
        filter = function (name /*, value */) { return name.match(pattern); };
    }
    for (var key in source)
        if (_cs.isown(source, key))
            if (filter(key, source[key]))
                target[key] = source[key];
    return target;
};

/*  utility function: mixin objects into another object by chaining methods  */
_cs.mixin = function (target, source, filter) {
    if (typeof filter === "undefined")
        filter = function (/* name, value */) { return true; };
    else if (typeof filter === "string") {
        var pattern = filter;
        filter = function (name /*, value */) { return name.match(pattern); };
    }
    for (var key in source) {
        if (_cs.isown(source, key)) {
            if (filter(key, source[key])) {
                if (_cs.istypeof(source[key]) === "function") {
                    /*  method/function  */
                    var src = _cs.clone(source[key], filter);
                    _cs.annotation(src, "name", key);
                    if (   _cs.istypeof(target[key]) === "function"
                        && _cs.isown(target, key)                  )
                        _cs.annotation(src, "base", target[key]);
                    target[key] = src;
                }
                else {
                    /*  property/field  */
                    target[key] = source[key];
                }
            }
        }
    }
    return target;
};

<|endoftext|>


<commit_before>import Realm from 'realm';

import { complement } from 'set-manipulator';

import {
  addBatchToParent,
  createRecord,
  getTotal,
  reuseNumber as reuseSerialNumber,
  NUMBER_SEQUENCE_KEYS,
} from '../utilities';

/**
 * A transaction.
 *
 * @property  {string}                  id
 * @property  {string}                  serialNumber
 * @property  {Name}                    otherParty
 * @property  {string}                  comment
 * @property  {Date}                    entryDate
 * @property  {string}                  type
 * @property  {string}                  status
 * @property  {Date}                    confirmDate
 * @property  {User}                    enteredBy
 * @property  {string}                  theirRef           External reference code.
 * @property  {TransactionCategory}     category
 * @property  {List.<TransactionItem>}  items
 * @property  {Requisition}             linkedRequisition
 */
export class Transaction extends Realm.Object {
  /**
   * Create new transaction.
   */
  constructor() {
    super();
    this.addItemsFromMasterList = this.addItemsFromMasterList.bind(this);
  }

  /**
   * Delete unfinalised transaction and associated transaction items. Throw error if transaction is
   * finalised.
   *
   * @param  {Realm}  database
   */
  destructor(database) {
    if (this.isFinalised) {
      throw new Error('Cannot delete finalised transaction');
    }

    // Recycle invoice serial number to be reused.
    if (this.isCustomerInvoice) {
      reuseSerialNumber(database, NUMBER_SEQUENCE_KEYS.CUSTOMER_INVOICE_NUMBER, this.serialNumber);
    }
    if (this.isSupplierInvoice) {
      reuseSerialNumber(database, NUMBER_SEQUENCE_KEYS.SUPPLIER_INVOICE_NUMBER, this.serialNumber);
    }
    database.delete('TransactionItem', this.items);
  }

  /**
   * Get if transaction is finalised.
   *
   * @return  {boolean}
   */
  get isFinalised() {
    return this.status === 'finalised';
  }

  /**
   * Get if transaction is confirmed.
   *
   * @return  {boolean}
   */
  get isConfirmed() {
    return this.status === 'confirmed';
  }

  /**
   * Get if transaction is incoming (i.e. a supplier invoice).
   *
   * @return  {boolean}
   */
  get isIncoming() {
    return this.type === 'supplier_invoice' || this.type === 'customer_credit';
  }

  /**
   * Get if transaction is outgoing (i.e. a customer invoice or supplier credit).
   *
   * @return  {boolean}
   */
  get isOutgoing() {
    // TODO: replace string matching with already defined getter methods.
    return this.type === 'customer_invoice' || this.type === 'supplier_credit';
  }

  /**
   * Get if transaction is a customer invoice.
   *
   * @return  {boolean}
   */
  get isCustomerInvoice() {
    return this.type === 'customer_invoice';
  }

  /**
   * Get if transaction is a supplier invoice.
   *
   * @return  {boolean]}
   */
  get isSupplierInvoice() {
    return this.type === 'supplier_invoice';
  }

  get isPrescription() {
    return this.mode === 'dispensary' && this.type === 'customer_invoice';
  }

  get isCashTransaction() {
    return this.type === 'payment' || this.type === 'receipt';
  }

  get isCredit() {
    return this.type === 'supplier_credit' || this.type === 'customer_credit';
  }

  /**
   * Get if transaction is an external supplier invoice.
   *
   * @return  {boolean}
   */
  get isExternalSupplierInvoice() {
    return this.isSupplierInvoice && this.otherParty && this.otherParty.isExternalSupplier;
  }

  /**
   * Get if transaction is an internal supplier invoice.
   *
   * @return  {boolean}
   */
  get isInternalSupplierInvoice() {
    return this.isSupplierInvoice && this.otherParty && this.otherParty.isInternalSupplier;
  }

  /**
   * Get if transaction is an inventory adjustment.
   *
   * @return  {boolean}
   */
  get isInventoryAdjustment() {
    return this.otherParty && this.otherParty.type === 'inventory_adjustment';
  }

  /**
   * Get name of other party to transaction.
   *
   * @return  {string}
   */
  get otherPartyName() {
    return this.otherParty ? this.otherParty.name : '';
  }

  /**
   * @return {String} This transaction reason title, or an empty string.
   */
  get reasonTitle() {
    return (this.option && this.option.title) || '';
  }

  /**
   * Set other party to this transaction.
   *
   * @param  {Name}  name
   */
  setOtherParty(name) {
    this.otherParty = name;
  }

  /**
   * Get total price of items in this transaction.
   *
   * @return  {number}
   */
  get totalPrice() {
    return getTotal(this.items, 'totalPrice');
  }

  /**
   * Get total quantity of items in this transaction.
   *
   * @return  {number}
   */
  get totalQuantity() {
    return getTotal(this.items, 'totalQuantity');
  }

  /**
   * Get total number of batches in this transaction.
   *
   * @return  {number}
   */
  get numberOfBatches() {
    return getTotal(this.items, 'numberOfBatches');
  }

  /**
   * Get total number of items in this transaction.
   *
   * @return  {number}
   */
  get numberOfItems() {
    return this.items.length;
  }

  /**
   * Get if this transaction is linked to a requisition.
   *
   * @return  {boolean}
   */
  get isLinkedToRequisition() {
    return !!this.linkedRequisition;
  }

  /**
   * Get if this transaction includes a given item.
   *
   * @param   {Item}  item
   * @return  {boolean}
   */
  hasItem(item) {
    const itemId = item.realItem.id;
    return this.items.filtered('item.id == $0', itemId).length > 0;
  }

  /**
   * Add an item to this transaction. If item already exists, do nothing.
   *
   * @param  {TransactionItem}  transactionItem
   */
  addItem(transactionItem) {
    this.items.push(transactionItem);
  }

  /**
   * Add all items from the customer's master list to this transaction. Transaction
   * must be an unfinalised customer invoice.
   *
   * @param  {Realm}            database
   * @param  {Array.<string>}   selected masterlists from multiselect
   */
  addItemsFromMasterList({ database, selected }) {
    if (!this.isCustomerInvoice) {
      throw new Error(`Cannot add master lists to ${this.type}`);
    }
    if (this.isFinalised) {
      throw new Error('Cannot add items to a finalised transaction');
    }
    if (this.otherParty) {
      // Filter through masterList ids that are on multiselect list
      this.otherParty.masterLists
        .filter(masterList => selected.indexOf(masterList.id) !== -1)
        .forEach(masterList => {
          const itemsToAdd = complement(masterList.items, this.items, item => item.itemId);
          itemsToAdd.forEach(masterListItem => {
            if (!masterListItem.item.crossReferenceItem && masterListItem.item.isVisible) {
              // Do not add cross reference items as will cause duplicates.
              createRecord(database, 'TransactionItem', this, masterListItem.item);
            }
          });
        });
    }
  }

  /**
   * Remove items with the given ids from this transaction.
   *
   * @param   {Realm}           database
   * @param   {Array.<string>}  itemIds
   */
  removeItemsById(database, itemIds) {
    const itemsToDelete = [];
    for (let i = 0; i < itemIds.length; i += 1) {
      const transactionItem = this.items.find(testItem => testItem.id === itemIds[i]);
      if (transactionItem.isValid()) {
        itemsToDelete.push(transactionItem);
      }
    }
    database.delete('transactionItem', itemsToDelete);
  }

  /**
   * Remove batches with given ids from this transaction. Prune any items with no remaining
   * associated batches.
   *
   * @param   {Realm}           database
   * @param   {Array.<string>}  transactionBatchIds
   */
  removeTransactionBatchesById(database, transactionBatchIds) {
    if (this.isFinalised) {
      throw new Error('Cannot modify finalised transaction');
    }
    const transactionBatches = this.getTransactionBatches(database);
    const transactionBatchesToDelete = [];
    transactionBatchIds.forEach(transactionBatchId => {
      const transactionBatch = transactionBatches.find(
        matchTransactionBatch => matchTransactionBatch.id === transactionBatchId
      );
      transactionBatchesToDelete.push(transactionBatch);
    });
    database.delete('TransactionBatch', transactionBatchesToDelete);
    this.pruneBatchlessTransactionItems(database);
  }

  /**
   * Remove given item from transaction.
   *
   * @param   {Realm}            database
   * @param   {TransactionItem}  TransactionItem
   */
  // eslint-disable-next-line class-methods-use-this
  removeTransactionItem(database, transactionItem) {
    database.delete('TransactionItem', transactionItem);
  }

  /**
   * Adds a batch to transaction, incorporating it into a matching item or creating a new
   * item if none already exist.
   *
   * @param  {Realm}             database
   * @param  {TransactionBatch}  transactionBatch
   */
  addBatchIfUnique(database, transactionBatch) {
    const { itemBatch } = transactionBatch;
    const { item } = itemBatch;

    addBatchToParent(transactionBatch, this, () =>
      createRecord(database, 'TransactionItem', this, item)
    );
  }

  /**
   * Delete any batches and items that are not contributing to this transaction.
   *
   * @param  {Realm}  database
   */
  pruneRedundantBatchesAndItems(database) {
    const batchesToRemove = this.getTransactionBatches(database).filtered('numberOfPacks = 0');

    database.delete('TransactionBatch', batchesToRemove);
    this.pruneBatchlessTransactionItems(database);
  }

  /**
   * Delete any items with no associated transaction batches.
   *
   * @param  {Realm}  database
   */
  pruneBatchlessTransactionItems(database) {
    const itemsToRemove = [];
    this.items.forEach(transactionItem => {
      if (transactionItem.batches.length === 0) {
        itemsToRemove.push(transactionItem);
      }
    });
    database.delete('TransactionItem', itemsToRemove);
  }

  /**
   * Returns all transaction batches for this transaction as
   * a realm collection (can be filtered).
   *
   * @param   {Realm}                               database
   * @return  {RealmCollection.<TransactionBatch>}
   */
  getTransactionBatches(database) {
    return database.objects('TransactionBatch').filtered('transaction.id == $0', this.id);
  }

  /**
   * Confirm this transaction, generating the associated item batches, linking them
   * to their items, and setting the status to confirmed.
   *
   * @param  {Realm}  database
   */
  confirm(database) {
    if (this.isConfirmed) {
      throw new Error('Cannot confirm as transaction is already confirmed');
    }
    if (this.isFinalised) {
      throw new Error('Cannot confirm as transaction is already finalised');
    }
    const isIncomingInvoice = this.isIncoming;

    this.getTransactionBatches(database).forEach(transactionBatch => {
      const {
        itemBatch,
        batch,
        packSize,
        numberOfPacks,
        expiryDate,
        costPrice,
        sellPrice,
      } = transactionBatch;

      // Pack to one all transactions in mobile, so multiply by |packSize| to get
      // quantity and price.
      const packedToOneQuantity = numberOfPacks * packSize;
      const packedToOneCostPrice = costPrice / packSize;
      const packedToOneSellPrice = sellPrice / packSize;

      const newNumberOfPacks = isIncomingInvoice
        ? itemBatch.numberOfPacks + packedToOneQuantity
        : itemBatch.numberOfPacks - packedToOneQuantity;
      itemBatch.packSize = 1;
      itemBatch.numberOfPacks = newNumberOfPacks;
      itemBatch.expiryDate = expiryDate;
      itemBatch.batch = this.adjustBatchName(batch);
      itemBatch.costPrice = packedToOneCostPrice;
      itemBatch.sellPrice = packedToOneSellPrice;
      if (isIncomingInvoice) itemBatch.supplier = this.otherParty;

      database.save('ItemBatch', itemBatch);
    });

    this.confirmDate = new Date();
    this.status = 'confirmed';
  }

  /**
   * Get `supplierinvoice_{invoiceNumber}` of batch. Name is empty and
   * transaction is supplier invoice.
   *
   * @param   {string}  batchName
   * @return  {string}
   */
  adjustBatchName(batchName) {
    if (this.isSupplierInvoice && (!batchName || batchName === '')) {
      return `supplierinvoice_${this.serialNumber}`;
    }
    return batchName;
  }

  /**
   * Finalise this transaction, setting the status so that this transaction is
   * locked down. If it has not already been confirmed (i.e. adjustments to inventory
   * made), confirm it first.
   *
   * @param  {Realm}  database
   */
  finalise(database) {
    if (this.isFinalised) {
      throw new Error('Cannot finalise as transaction is already finalised');
    }
    // Prune all invoices except internal supplier invoices.
    if (!this.isInternalSupplierInvoice) {
      this.pruneRedundantBatchesAndItems(database);
    }
    if (!this.isConfirmed) this.confirm(database);

    // Finding the totalPrice propogates through `TransactionItem` records down to the batch
    // level, deriving the full cost of the Transaction. Cash transactions and credits are
    // created finalised, having their total already set.
    if (!this.isCashTransaction || !this.isCredit) {
      this.total = this.totalPrice;
    }

    this.status = 'finalised';
    database.save('Transaction', this);
  }
}

Transaction.schema = {
  name: 'Transaction',
  primaryKey: 'id',
  properties: {
    id: 'string',
    serialNumber: { type: 'string', default: 'placeholderSerialNumber' },
    otherParty: { type: 'Name', optional: true },
    comment: { type: 'string', optional: true },
    entryDate: { type: 'date', optional: true },
    type: { type: 'string', default: 'placeholderType' },
    status: { type: 'string', default: 'new' },
    confirmDate: { type: 'date', optional: true },
    enteredBy: { type: 'User', optional: true },
    theirRef: { type: 'string', optional: true },
    category: { type: 'TransactionCategory', optional: true },
    items: { type: 'list', objectType: 'TransactionItem' },
    mode: { type: 'string', default: 'store' },
    prescriber: { type: 'Prescriber', optional: true },
    linkedRequisition: { type: 'Requisition', optional: true },
    total: { type: 'float', optional: true },
    outstanding: { type: 'float', optional: true },
    insurancePolicy: { type: 'InsurancePolicy', optional: true },
    option: { type: 'Options', optional: true },
    linkedTransaction: { type: 'Transaction', optional: true },
    user1: { type: 'string', optional: true },
    insuranceDiscountRate: { type: 'double', optional: true },
    insuranceDiscountAmount: { type: 'double', optional: true },
  },
};

export default Transaction;
<commit_msg>Add fix corrected conditional<commit_after>import Realm from 'realm';

import { complement } from 'set-manipulator';

import {
  addBatchToParent,
  createRecord,
  getTotal,
  reuseNumber as reuseSerialNumber,
  NUMBER_SEQUENCE_KEYS,
} from '../utilities';

/**
 * A transaction.
 *
 * @property  {string}                  id
 * @property  {string}                  serialNumber
 * @property  {Name}                    otherParty
 * @property  {string}                  comment
 * @property  {Date}                    entryDate
 * @property  {string}                  type
 * @property  {string}                  status
 * @property  {Date}                    confirmDate
 * @property  {User}                    enteredBy
 * @property  {string}                  theirRef           External reference code.
 * @property  {TransactionCategory}     category
 * @property  {List.<TransactionItem>}  items
 * @property  {Requisition}             linkedRequisition
 */
export class Transaction extends Realm.Object {
  /**
   * Create new transaction.
   */
  constructor() {
    super();
    this.addItemsFromMasterList = this.addItemsFromMasterList.bind(this);
  }

  /**
   * Delete unfinalised transaction and associated transaction items. Throw error if transaction is
   * finalised.
   *
   * @param  {Realm}  database
   */
  destructor(database) {
    if (this.isFinalised) {
      throw new Error('Cannot delete finalised transaction');
    }

    // Recycle invoice serial number to be reused.
    if (this.isCustomerInvoice) {
      reuseSerialNumber(database, NUMBER_SEQUENCE_KEYS.CUSTOMER_INVOICE_NUMBER, this.serialNumber);
    }
    if (this.isSupplierInvoice) {
      reuseSerialNumber(database, NUMBER_SEQUENCE_KEYS.SUPPLIER_INVOICE_NUMBER, this.serialNumber);
    }
    database.delete('TransactionItem', this.items);
  }

  /**
   * Get if transaction is finalised.
   *
   * @return  {boolean}
   */
  get isFinalised() {
    return this.status === 'finalised';
  }

  /**
   * Get if transaction is confirmed.
   *
   * @return  {boolean}
   */
  get isConfirmed() {
    return this.status === 'confirmed';
  }

  /**
   * Get if transaction is incoming (i.e. a supplier invoice).
   *
   * @return  {boolean}
   */
  get isIncoming() {
    return this.type === 'supplier_invoice' || this.type === 'customer_credit';
  }

  /**
   * Get if transaction is outgoing (i.e. a customer invoice or supplier credit).
   *
   * @return  {boolean}
   */
  get isOutgoing() {
    // TODO: replace string matching with already defined getter methods.
    return this.type === 'customer_invoice' || this.type === 'supplier_credit';
  }

  /**
   * Get if transaction is a customer invoice.
   *
   * @return  {boolean}
   */
  get isCustomerInvoice() {
    return this.type === 'customer_invoice';
  }

  /**
   * Get if transaction is a supplier invoice.
   *
   * @return  {boolean]}
   */
  get isSupplierInvoice() {
    return this.type === 'supplier_invoice';
  }

  get isPrescription() {
    return this.mode === 'dispensary' && this.type === 'customer_invoice';
  }

  get isCashTransaction() {
    return this.type === 'payment' || this.type === 'receipt';
  }

  get isCredit() {
    return this.type === 'supplier_credit' || this.type === 'customer_credit';
  }

  /**
   * Get if transaction is an external supplier invoice.
   *
   * @return  {boolean}
   */
  get isExternalSupplierInvoice() {
    return this.isSupplierInvoice && this.otherParty && this.otherParty.isExternalSupplier;
  }

  /**
   * Get if transaction is an internal supplier invoice.
   *
   * @return  {boolean}
   */
  get isInternalSupplierInvoice() {
    return this.isSupplierInvoice && this.otherParty && this.otherParty.isInternalSupplier;
  }

  /**
   * Get if transaction is an inventory adjustment.
   *
   * @return  {boolean}
   */
  get isInventoryAdjustment() {
    return this.otherParty && this.otherParty.type === 'inventory_adjustment';
  }

  /**
   * Get name of other party to transaction.
   *
   * @return  {string}
   */
  get otherPartyName() {
    return this.otherParty ? this.otherParty.name : '';
  }

  /**
   * @return {String} This transaction reason title, or an empty string.
   */
  get reasonTitle() {
    return (this.option && this.option.title) || '';
  }

  /**
   * Set other party to this transaction.
   *
   * @param  {Name}  name
   */
  setOtherParty(name) {
    this.otherParty = name;
  }

  /**
   * Get total price of items in this transaction.
   *
   * @return  {number}
   */
  get totalPrice() {
    return getTotal(this.items, 'totalPrice');
  }

  /**
   * Get total quantity of items in this transaction.
   *
   * @return  {number}
   */
  get totalQuantity() {
    return getTotal(this.items, 'totalQuantity');
  }

  /**
   * Get total number of batches in this transaction.
   *
   * @return  {number}
   */
  get numberOfBatches() {
    return getTotal(this.items, 'numberOfBatches');
  }

  /**
   * Get total number of items in this transaction.
   *
   * @return  {number}
   */
  get numberOfItems() {
    return this.items.length;
  }

  /**
   * Get if this transaction is linked to a requisition.
   *
   * @return  {boolean}
   */
  get isLinkedToRequisition() {
    return !!this.linkedRequisition;
  }

  /**
   * Get if this transaction includes a given item.
   *
   * @param   {Item}  item
   * @return  {boolean}
   */
  hasItem(item) {
    const itemId = item.realItem.id;
    return this.items.filtered('item.id == $0', itemId).length > 0;
  }

  /**
   * Add an item to this transaction. If item already exists, do nothing.
   *
   * @param  {TransactionItem}  transactionItem
   */
  addItem(transactionItem) {
    this.items.push(transactionItem);
  }

  /**
   * Add all items from the customer's master list to this transaction. Transaction
   * must be an unfinalised customer invoice.
   *
   * @param  {Realm}            database
   * @param  {Array.<string>}   selected masterlists from multiselect
   */
  addItemsFromMasterList({ database, selected }) {
    if (!this.isCustomerInvoice) {
      throw new Error(`Cannot add master lists to ${this.type}`);
    }
    if (this.isFinalised) {
      throw new Error('Cannot add items to a finalised transaction');
    }
    if (this.otherParty) {
      // Filter through masterList ids that are on multiselect list
      this.otherParty.masterLists
        .filter(masterList => selected.indexOf(masterList.id) !== -1)
        .forEach(masterList => {
          const itemsToAdd = complement(masterList.items, this.items, item => item.itemId);
          itemsToAdd.forEach(masterListItem => {
            if (!masterListItem.item.crossReferenceItem && masterListItem.item.isVisible) {
              // Do not add cross reference items as will cause duplicates.
              createRecord(database, 'TransactionItem', this, masterListItem.item);
            }
          });
        });
    }
  }

  /**
   * Remove items with the given ids from this transaction.
   *
   * @param   {Realm}           database
   * @param   {Array.<string>}  itemIds
   */
  removeItemsById(database, itemIds) {
    const itemsToDelete = [];
    for (let i = 0; i < itemIds.length; i += 1) {
      const transactionItem = this.items.find(testItem => testItem.id === itemIds[i]);
      if (transactionItem.isValid()) {
        itemsToDelete.push(transactionItem);
      }
    }
    database.delete('transactionItem', itemsToDelete);
  }

  /**
   * Remove batches with given ids from this transaction. Prune any items with no remaining
   * associated batches.
   *
   * @param   {Realm}           database
   * @param   {Array.<string>}  transactionBatchIds
   */
  removeTransactionBatchesById(database, transactionBatchIds) {
    if (this.isFinalised) {
      throw new Error('Cannot modify finalised transaction');
    }
    const transactionBatches = this.getTransactionBatches(database);
    const transactionBatchesToDelete = [];
    transactionBatchIds.forEach(transactionBatchId => {
      const transactionBatch = transactionBatches.find(
        matchTransactionBatch => matchTransactionBatch.id === transactionBatchId
      );
      transactionBatchesToDelete.push(transactionBatch);
    });
    database.delete('TransactionBatch', transactionBatchesToDelete);
    this.pruneBatchlessTransactionItems(database);
  }

  /**
   * Remove given item from transaction.
   *
   * @param   {Realm}            database
   * @param   {TransactionItem}  TransactionItem
   */
  // eslint-disable-next-line class-methods-use-this
  removeTransactionItem(database, transactionItem) {
    database.delete('TransactionItem', transactionItem);
  }

  /**
   * Adds a batch to transaction, incorporating it into a matching item or creating a new
   * item if none already exist.
   *
   * @param  {Realm}             database
   * @param  {TransactionBatch}  transactionBatch
   */
  addBatchIfUnique(database, transactionBatch) {
    const { itemBatch } = transactionBatch;
    const { item } = itemBatch;

    addBatchToParent(transactionBatch, this, () =>
      createRecord(database, 'TransactionItem', this, item)
    );
  }

  /**
   * Delete any batches and items that are not contributing to this transaction.
   *
   * @param  {Realm}  database
   */
  pruneRedundantBatchesAndItems(database) {
    const batchesToRemove = this.getTransactionBatches(database).filtered('numberOfPacks = 0');

    database.delete('TransactionBatch', batchesToRemove);
    this.pruneBatchlessTransactionItems(database);
  }

  /**
   * Delete any items with no associated transaction batches.
   *
   * @param  {Realm}  database
   */
  pruneBatchlessTransactionItems(database) {
    const itemsToRemove = [];
    this.items.forEach(transactionItem => {
      if (transactionItem.batches.length === 0) {
        itemsToRemove.push(transactionItem);
      }
    });
    database.delete('TransactionItem', itemsToRemove);
  }

  /**
   * Returns all transaction batches for this transaction as
   * a realm collection (can be filtered).
   *
   * @param   {Realm}                               database
   * @return  {RealmCollection.<TransactionBatch>}
   */
  getTransactionBatches(database) {
    return database.objects('TransactionBatch').filtered('transaction.id == $0', this.id);
  }

  /**
   * Confirm this transaction, generating the associated item batches, linking them
   * to their items, and setting the status to confirmed.
   *
   * @param  {Realm}  database
   */
  confirm(database) {
    if (this.isConfirmed) {
      throw new Error('Cannot confirm as transaction is already confirmed');
    }
    if (this.isFinalised) {
      throw new Error('Cannot confirm as transaction is already finalised');
    }
    const isIncomingInvoice = this.isIncoming;

    this.getTransactionBatches(database).forEach(transactionBatch => {
      const {
        itemBatch,
        batch,
        packSize,
        numberOfPacks,
        expiryDate,
        costPrice,
        sellPrice,
      } = transactionBatch;

      // Pack to one all transactions in mobile, so multiply by |packSize| to get
      // quantity and price.
      const packedToOneQuantity = numberOfPacks * packSize;
      const packedToOneCostPrice = costPrice / packSize;
      const packedToOneSellPrice = sellPrice / packSize;

      const newNumberOfPacks = isIncomingInvoice
        ? itemBatch.numberOfPacks + packedToOneQuantity
        : itemBatch.numberOfPacks - packedToOneQuantity;
      itemBatch.packSize = 1;
      itemBatch.numberOfPacks = newNumberOfPacks;
      itemBatch.expiryDate = expiryDate;
      itemBatch.batch = this.adjustBatchName(batch);
      itemBatch.costPrice = packedToOneCostPrice;
      itemBatch.sellPrice = packedToOneSellPrice;
      if (isIncomingInvoice) itemBatch.supplier = this.otherParty;

      database.save('ItemBatch', itemBatch);
    });

    this.confirmDate = new Date();
    this.status = 'confirmed';
  }

  /**
   * Get `supplierinvoice_{invoiceNumber}` of batch. Name is empty and
   * transaction is supplier invoice.
   *
   * @param   {string}  batchName
   * @return  {string}
   */
  adjustBatchName(batchName) {
    if (this.isSupplierInvoice && (!batchName || batchName === '')) {
      return `supplierinvoice_${this.serialNumber}`;
    }
    return batchName;
  }

  /**
   * Finalise this transaction, setting the status so that this transaction is
   * locked down. If it has not already been confirmed (i.e. adjustments to inventory
   * made), confirm it first.
   *
   * @param  {Realm}  database
   */
  finalise(database) {
    if (this.isFinalised) {
      throw new Error('Cannot finalise as transaction is already finalised');
    }
    // Prune all invoices except internal supplier invoices.
    if (!this.isInternalSupplierInvoice) {
      this.pruneRedundantBatchesAndItems(database);
    }
    if (!this.isConfirmed) this.confirm(database);

    // Finding the totalPrice propogates through `TransactionItem` records down to the batch
    // level, deriving the full cost of the Transaction. Cash transactions and credits are
    // created finalised, having their total already set.
    if (!(this.isCashTransaction || this.isCredit)) {
      this.total = this.totalPrice;
    }

    this.status = 'finalised';
    database.save('Transaction', this);
  }
}

Transaction.schema = {
  name: 'Transaction',
  primaryKey: 'id',
  properties: {
    id: 'string',
    serialNumber: { type: 'string', default: 'placeholderSerialNumber' },
    otherParty: { type: 'Name', optional: true },
    comment: { type: 'string', optional: true },
    entryDate: { type: 'date', optional: true },
    type: { type: 'string', default: 'placeholderType' },
    status: { type: 'string', default: 'new' },
    confirmDate: { type: 'date', optional: true },
    enteredBy: { type: 'User', optional: true },
    theirRef: { type: 'string', optional: true },
    category: { type: 'TransactionCategory', optional: true },
    items: { type: 'list', objectType: 'TransactionItem' },
    mode: { type: 'string', default: 'store' },
    prescriber: { type: 'Prescriber', optional: true },
    linkedRequisition: { type: 'Requisition', optional: true },
    total: { type: 'float', optional: true },
    outstanding: { type: 'float', optional: true },
    insurancePolicy: { type: 'InsurancePolicy', optional: true },
    option: { type: 'Options', optional: true },
    linkedTransaction: { type: 'Transaction', optional: true },
    user1: { type: 'string', optional: true },
    insuranceDiscountRate: { type: 'double', optional: true },
    insuranceDiscountAmount: { type: 'double', optional: true },
  },
};

export default Transaction;
<|endoftext|>