<commit_before>/*
Copyright 2015, 2016 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict';

var React = require('react');
var sdk = require('matrix-react-sdk');
var Matrix = require("matrix-js-sdk");
var dis = require('matrix-react-sdk/lib/dispatcher');
var MatrixClientPeg = require("matrix-react-sdk/lib/MatrixClientPeg");
var rate_limited_func = require('matrix-react-sdk/lib/ratelimitedfunc');

module.exports = React.createClass({
    displayName: 'RightPanel',

    propTypes: {
        userId: React.PropTypes.string, // if showing an orphaned MemberInfo page, this is set
        roomId: React.PropTypes.string, // if showing panels for a given room, this is set
        collapsed: React.PropTypes.bool,
    },

    Phase : {
        MemberList: 'MemberList',
        FilePanel: 'FilePanel',
        NotificationPanel: 'NotificationPanel',
        MemberInfo: 'MemberInfo',
    },

    componentWillMount: function() {
        this.dispatcherRef = dis.register(this.onAction);
        var cli = MatrixClientPeg.get();
        cli.on("RoomState.members", this.onRoomStateMember);
    },

    componentWillUnmount: function() {
        dis.unregister(this.dispatcherRef);
        if (MatrixClientPeg.get()) {
            MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
        }
    },

    getInitialState: function() {
        if (this.props.userId) {
            var member = new Matrix.RoomMember(null, this.props.userId);
            return {
                phase: this.Phase.MemberInfo,
                member: member,
            }
        }
        else {
            return {
                phase: this.Phase.MemberList
            }
        }
    },

    onMemberListButtonClick: function() {
        if (this.props.collapsed || this.state.phase !== this.Phase.MemberList) {
            this.setState({ phase: this.Phase.MemberList });
            dis.dispatch({
                action: 'show_right_panel',
            });
        }
        else {
            dis.dispatch({
                action: 'hide_right_panel',
            });
        }
    },

    onFileListButtonClick: function() {
        if (this.props.collapsed || this.state.phase !== this.Phase.FilePanel) {
            this.setState({ phase: this.Phase.FilePanel });
            dis.dispatch({
                action: 'show_right_panel',
            });
        }
        else {
            dis.dispatch({
                action: 'hide_right_panel',
            });
        }
    },

    onRoomStateMember: function(ev, state, member) {
        // redraw the badge on the membership list
        if (this.state.phase == this.Phase.MemberList && member.roomId === this.props.roomId) {
            this._delayedUpdate();
        }
        else if (this.state.phase === this.Phase.MemberInfo && member.roomId === this.props.roomId &&
                member.userId === this.state.member.userId) {
            // refresh the member info (e.g. new power level)
            this._delayedUpdate();
        }
    },

    _delayedUpdate: new rate_limited_func(function() {
        this.forceUpdate();
    }, 500),

    onAction: function(payload) {
        if (payload.action === "view_user") {
            dis.dispatch({
                action: 'show_right_panel',
            });
            if (payload.member) {
                this.setState({
                    phase: this.Phase.MemberInfo,
                    member: payload.member,
                });
            }
            else {
                this.setState({
                    phase: this.Phase.MemberList
                });
            }
        }
        else if (payload.action === "view_room") {
            if (this.state.phase === this.Phase.MemberInfo) {
                this.setState({
                    phase: this.Phase.MemberList
                });
            }
        }
    },

    render: function() {
        var MemberList = sdk.getComponent('rooms.MemberList');
        var NotificationPanel = sdk.getComponent('structures.NotificationPanel');
        var FilePanel = sdk.getComponent('structures.FilePanel');
        var TintableSvg = sdk.getComponent("elements.TintableSvg");
        var buttonGroup;
        var panel;

        var filesHighlight;
        var membersHighlight;
        var notificationsHighlight;
        if (!this.props.collapsed) {
            if (this.state.phase == this.Phase.MemberList || this.state.phase === this.Phase.MemberInfo) {
                membersHighlight =

;
            }
            else if (this.state.phase == this.Phase.FilePanel) {
                filesHighlight =

;
            }
            else if (this.state.phase == this.Phase.NotificationPanel) {
                notificationsHighlight =

;
            }
        }

        var membersBadge;
        if ((this.state.phase == this.Phase.MemberList || this.state.phase === this.Phase.MemberInfo) && this.props.roomId) {
            var cli = MatrixClientPeg.get();
            var room = cli.getRoom(this.props.roomId);
            if (room) {
                membersBadge = room.getJoinedMembers().length;
            }
        }

        if (this.props.roomId) {
            buttonGroup =

{ membersBadge ? membersBadge :  }

                            <TintableSvg src="img/icons-people.svg" width="25" height="25"/>
                            { membersHighlight }

                            <TintableSvg src="img/icons-files.svg" width="25" height="25"/>
                            { filesHighlight }

                            <TintableSvg src="img/icons-notifications.svg" width="25" height="25"/>
                            { notificationsHighlight }

;
        }

        if (!this.props.collapsed) {
            if(this.props.roomId && this.state.phase == this.Phase.MemberList) {
                panel = <MemberList roomId={this.props.roomId} key={this.props.roomId} />
            }
            else if(this.state.phase == this.Phase.MemberInfo) {
                var MemberInfo = sdk.getComponent('rooms.MemberInfo');
                panel = <MemberInfo member={this.state.member} key={this.props.roomId || this.props.userId} />
            }
            else if (this.state.phase == this.Phase.NotificationPanel) {
                panel = <NotificationPanel />
            }
            else if (this.state.phase == this.Phase.FilePanel) {
                panel = <FilePanel roomId={this.props.roomId} />
            }
        }

        if (!panel) {
            panel =

;
        }

        var classes = "mx_RightPanel mx_fadable";
        if (this.props.collapsed) {
            classes += " collapsed";
        }

        return (

                    { buttonGroup }

                { panel }

        );
    }
});

<commit_msg>hook up the NotifPanel button<commit_after>/*
Copyright 2015, 2016 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict';

var React = require('react');
var sdk = require('matrix-react-sdk');
var Matrix = require("matrix-js-sdk");
var dis = require('matrix-react-sdk/lib/dispatcher');
var MatrixClientPeg = require("matrix-react-sdk/lib/MatrixClientPeg");
var rate_limited_func = require('matrix-react-sdk/lib/ratelimitedfunc');

module.exports = React.createClass({
    displayName: 'RightPanel',

    propTypes: {
        userId: React.PropTypes.string, // if showing an orphaned MemberInfo page, this is set
        roomId: React.PropTypes.string, // if showing panels for a given room, this is set
        collapsed: React.PropTypes.bool,
    },

    Phase : {
        MemberList: 'MemberList',
        FilePanel: 'FilePanel',
        NotificationPanel: 'NotificationPanel',
        MemberInfo: 'MemberInfo',
    },

    componentWillMount: function() {
        this.dispatcherRef = dis.register(this.onAction);
        var cli = MatrixClientPeg.get();
        cli.on("RoomState.members", this.onRoomStateMember);
    },

    componentWillUnmount: function() {
        dis.unregister(this.dispatcherRef);
        if (MatrixClientPeg.get()) {
            MatrixClientPeg.get().removeListener("RoomState.members", this.onRoomStateMember);
        }
    },

    getInitialState: function() {
        if (this.props.userId) {
            var member = new Matrix.RoomMember(null, this.props.userId);
            return {
                phase: this.Phase.MemberInfo,
                member: member,
            }
        }
        else {
            return {
                phase: this.Phase.MemberList
            }
        }
    },

    onMemberListButtonClick: function() {
        if (this.props.collapsed || this.state.phase !== this.Phase.MemberList) {
            this.setState({ phase: this.Phase.MemberList });
            dis.dispatch({
                action: 'show_right_panel',
            });
        }
        else {
            dis.dispatch({
                action: 'hide_right_panel',
            });
        }
    },

    onFileListButtonClick: function() {
        if (this.props.collapsed || this.state.phase !== this.Phase.FilePanel) {
            this.setState({ phase: this.Phase.FilePanel });
            dis.dispatch({
                action: 'show_right_panel',
            });
        }
        else {
            dis.dispatch({
                action: 'hide_right_panel',
            });
        }
    },

    onNotificationListButtonClick: function() {
        if (this.props.collapsed || this.state.phase !== this.Phase.NotificationPanel) {
            this.setState({ phase: this.Phase.NotificationPanel });
            dis.dispatch({
                action: 'show_right_panel',
            });
        }
        else {
            dis.dispatch({
                action: 'hide_right_panel',
            });
        }
    },

    onRoomStateMember: function(ev, state, member) {
        // redraw the badge on the membership list
        if (this.state.phase == this.Phase.MemberList && member.roomId === this.props.roomId) {
            this._delayedUpdate();
        }
        else if (this.state.phase === this.Phase.MemberInfo && member.roomId === this.props.roomId &&
                member.userId === this.state.member.userId) {
            // refresh the member info (e.g. new power level)
            this._delayedUpdate();
        }
    },

    _delayedUpdate: new rate_limited_func(function() {
        this.forceUpdate();
    }, 500),

    onAction: function(payload) {
        if (payload.action === "view_user") {
            dis.dispatch({
                action: 'show_right_panel',
            });
            if (payload.member) {
                this.setState({
                    phase: this.Phase.MemberInfo,
                    member: payload.member,
                });
            }
            else {
                this.setState({
                    phase: this.Phase.MemberList
                });
            }
        }
        else if (payload.action === "view_room") {
            if (this.state.phase === this.Phase.MemberInfo) {
                this.setState({
                    phase: this.Phase.MemberList
                });
            }
        }
    },

    render: function() {
        var MemberList = sdk.getComponent('rooms.MemberList');
        var NotificationPanel = sdk.getComponent('structures.NotificationPanel');
        var FilePanel = sdk.getComponent('structures.FilePanel');
        var TintableSvg = sdk.getComponent("elements.TintableSvg");
        var buttonGroup;
        var panel;

        var filesHighlight;
        var membersHighlight;
        var notificationsHighlight;
        if (!this.props.collapsed) {
            if (this.state.phase == this.Phase.MemberList || this.state.phase === this.Phase.MemberInfo) {
                membersHighlight =

;
            }
            else if (this.state.phase == this.Phase.FilePanel) {
                filesHighlight =

;
            }
            else if (this.state.phase == this.Phase.NotificationPanel) {
                notificationsHighlight =

;
            }
        }

        var membersBadge;
        if ((this.state.phase == this.Phase.MemberList || this.state.phase === this.Phase.MemberInfo) && this.props.roomId) {
            var cli = MatrixClientPeg.get();
            var room = cli.getRoom(this.props.roomId);
            if (room) {
                membersBadge = room.getJoinedMembers().length;
            }
        }

        if (this.props.roomId) {
            buttonGroup =

{ membersBadge ? membersBadge :  }

                            <TintableSvg src="img/icons-people.svg" width="25" height="25"/>
                            { membersHighlight }

                            <TintableSvg src="img/icons-files.svg" width="25" height="25"/>
                            { filesHighlight }

                            <TintableSvg src="img/icons-notifications.svg" width="25" height="25"/>
                            { notificationsHighlight }

;
        }

        if (!this.props.collapsed) {
            if(this.props.roomId && this.state.phase == this.Phase.MemberList) {
                panel = <MemberList roomId={this.props.roomId} key={this.props.roomId} />
            }
            else if(this.state.phase == this.Phase.MemberInfo) {
                var MemberInfo = sdk.getComponent('rooms.MemberInfo');
                panel = <MemberInfo member={this.state.member} key={this.props.roomId || this.props.userId} />
            }
            else if (this.state.phase == this.Phase.NotificationPanel) {
                panel = <NotificationPanel />
            }
            else if (this.state.phase == this.Phase.FilePanel) {
                panel = <FilePanel roomId={this.props.roomId} />
            }
        }

        if (!panel) {
            panel =

;
        }

        var classes = "mx_RightPanel mx_fadable";
        if (this.props.collapsed) {
            classes += " collapsed";
        }

        return (

                    { buttonGroup }

                { panel }

        );
    }
});

<|endoftext|>


<commit_before>'use strict';

const express = ('express');
const router = express.Router(); //eslint-disable-line
const bodyParser = require('body-parser').json();
const token = require('../auth/token');
const User = require('../models/user');

router
  .post('/signup', bodyParser, (req, res, next) => {
    const { username, password, roles } = req.body;
    delete req.body.password;

    if(!username || !password) {
      return next({
        code: 400,
        error: 'username and password required'
      });
    }

    User.find({ username })
      .count()
      .then(count => {
        if(count > 0) throw {
          code: 400,
          error: `username ${username} already exists`
        };
        const user = new User(req.body);
        user.generateHash(password);
        return user.save();
      })
      .then(user => token.sign(user))
      .then(token => res.send({ token }))
      .catch(next);
  });<commit_msg>fixed linter error<commit_after>'use strict';

const express = ('express');
const router = express.Router(); //eslint-disable-line
const bodyParser = require('body-parser').json();
const token = require('../auth/token');
const User = require('../models/user');

router
  .post('/signup', bodyParser, (req, res, next) => {
    const { username, password, roles } = req.body; //eslint-disable-line
    delete req.body.password;

    if(!username || !password) {
      return next({
        code: 400,
        error: 'username and password required'
      });
    }

    User.find({ username })
      .count()
      .then(count => {
        if(count > 0) throw {
          code: 400,
          error: `username ${username} already exists`
        };
        const user = new User(req.body);
        user.generateHash(password);
        return user.save();
      })
      .then(user => token.sign(user))
      .then(token => res.send({ token }))
      .catch(next);
  });<|endoftext|>


<commit_before>import React from "react";
import { isValueValid } from "../../../util/globals";
import { infoPanelStyles } from "../../../globalStyles";
import { numericToCalendar } from "../../../util/dateHelpers";
import { getTraitFromNode, getFullAuthorInfoFromNode, getVaccineFromNode,
  getAccessionFromNode, getUrlFromNode, collectMutations } from "../../../util/treeMiscHelpers";

export const styles = {
  container: {
    position: "absolute",
    width: "100%",
    height: "100%",
    pointerEvents: "all",
    top: 0,
    left: 0,
    zIndex: 2000,
    backgroundColor: "rgba(80, 80, 80, .20)",
    /* FLEXBOX */
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    wordWrap: "break-word",
    wordBreak: "break-word"
  }
};

export const stopProp = (e) => {
  if (!e) {e = window.event;} // eslint-disable-line no-param-reassign
  e.cancelBubble = true;
  if (e.stopPropagation) {e.stopPropagation();}
};

/* min width to get "collection date" on 1 line: 120 */
const item = (key, value, href) => (

    {key}
    {href ? (
      {value}
    ) :
      value
    }

);

const formatURL = (url) => {
  if (url !== undefined && url.startsWith("https_")) {
    return url.replace("https_", "https:");
  } else if (url !== undefined && url.startsWith("http_")) {
    return url.replace("http_", "http:");
  }
  return url;
};

const Link = ({url, title, value}) => (

    {title}

      {value}

);

/**
 * Render a 2-column table of gene -> mutations.
 * Rows are sorted by gene name, alphabetically, with "nuc" last.
 * Mutations are sorted by genomic position.
 * todo: sort genes by position in genome
 * todo: provide in-app links from mutations to color-bys? filters?
 */
const MutationTable = ({mutations}) => {
  const geneSortFn = (a, b) => {
    if (a[0]==="nuc") return 1;
    if (b[0]==="nuc") return -1;
    return a[0] {
    const [aa, bb] = [parseInt(a.slice(1, -1), 10), parseInt(b.slice(1, -1), 10)];
    return aa<bb ? -1 : 1;
  };
  // we encode the table here (rather than via `item()`) to set component keys appropriately
  return (
    Object.entries(mutations)
      .sort(geneSortFn)
      .map(([gene, muts], index) => (

          {index===0 ? "Mutations from root" : ""}
          {`${gene}:${muts.sort(mutSortFn).join(", ").substring(0, 200)}`}

      ))
  );
};

const AccessionAndUrl = ({node}) => {
  const accession = getAccessionFromNode(node);
  const url = getUrlFromNode(node);
  const genbank_accession = getTraitFromNode(node, "genbank_accession");

  /* `gisaid_epi_isl` is a special value attached to nodes introduced during the 2019 nCoV outbreak.
  If set, the display is different from the normal behavior */
  const gisaid_epi_isl = getTraitFromNode(node, "gisaid_epi_isl");
  if (isValueValid(gisaid_epi_isl)) {
    return (
      <>
        <Link title={"GISAID EPI ISL"} value={gisaid_epi_isl.split("_")[2]} url={"https://gisaid.org"}/>
        {isValueValid(genbank_accession) ?
          <Link title={"Genbank accession"} value={genbank_accession} url={"https://www.ncbi.nlm.nih.gov/nuccore/" + genbank_accession}/> :
          null
        }
      </>
    );
  }

  if (isValueValid(genbank_accession)) {
    return (
      <Link title={"Genbank accession"} value={genbank_accession} url={"https://www.ncbi.nlm.nih.gov/nuccore/" + genbank_accession}/>
    );
  } else if (isValueValid(accession) && isValueValid(url)) {
    return (
      <Link url={formatURL(url)} value={accession} title={"Accession"}/>
    );
  } else if (isValueValid(accession)) {
    return (
      item("Accession", accession)
    );
  } else if (isValueValid(url)) {
    return (
      <Link title={"Strain URL"} url={formatURL(url)} value={"click here"}/>
    );
  }
  return null;
};

const VaccineInfo = ({node, t}) => {
  const vaccineInfo = getVaccineFromNode(node);
  if (!vaccineInfo) return null;
  const renderElements = [];
  if (vaccineInfo.selection_date) {
    renderElements.push(

        {t("Vaccine selected")}
        {vaccineInfo.selection_date}

    );
  }
  if (vaccineInfo.start_date) {
    renderElements.push(

        {t("Vaccine start date")}
        {vaccineInfo.start_date}

    );
  }
  if (vaccineInfo.end_date) {
    renderElements.push(

        {t("Vaccine end date")}
        {vaccineInfo.end_date}

    );
  }
  if (vaccineInfo.serum) {
    renderElements.push(

        {t("Serum strain")}

    );
  }
  return renderElements;
};

const PublicationInfo = ({node, t}) => {
  const info = getFullAuthorInfoFromNode(node);
  if (!info) return null;

  const itemsToRender = [];
  itemsToRender.push(item(t("Authors"), info.value));
  if (info.title && info.title !== "?") {
    if (info.paper_url && info.paper_url !== "?") {
      itemsToRender.push(item(t("Title"), info.title, info.paper_url));
    } else {
      itemsToRender.push(item(t("Title"), info.title));
    }
  }
  if (info.journal && info.journal !== "?") {
    itemsToRender.push(item(t("Journal"), info.journal));
  }
  return (itemsToRender.length === 1 ? itemsToRender[0] : itemsToRender);
};

const StrainName = ({children}) => (

{children}

);

const SampleDate = ({node, t}) => {
  const date = getTraitFromNode(node, "num_date");
  if (!date) return null;

  const dateUncertainty = getTraitFromNode(node, "num_date", {confidence: true});
  if (date && dateUncertainty && dateUncertainty[0] !== dateUncertainty[1]) {
    return (
      <>
        {item(t("Inferred collection date"), numericToCalendar(date))}
        {item(t("Date Confidence Interval"), `(${numericToCalendar(dateUncertainty[0])}, ${numericToCalendar(dateUncertainty[1])})`)}
      </>
    );
  }

  return item(t("Collection date"), numericToCalendar(date));
};

const getTraitsToDisplay = (node) => {
  // TODO -- this should be centralised somewhere
  if (!node.node_attrs) return [];
  const ignore = ["author", "div", "num_date", "gisaid_epi_isl", "genbank_accession"];
  return Object.keys(node.node_attrs).filter((k) => !ignore.includes(k));
};

const Trait = ({node, trait, colorings}) => {
  const value_tmp = getTraitFromNode(node, trait);
  let value = value_tmp;
  if (typeof value_tmp === "number") {
    if (!Number.isInteger(value_tmp)) {
      value = Number.parseFloat(value_tmp).toPrecision(3);
    }
  }
  const name = (colorings && colorings[trait] && colorings[trait].title) ?
    colorings[trait].title :
    trait;
  return isValueValid(value) ? item(name, value) : null;
};

/**
 * A React component to display information about a tree tip in a modal-overlay style
 * @param  {Object}   props
 * @param  {Object}   props.tip              tip node selected
 * @param  {function} props.goAwayCallback
 * @param  {object}   props.colorings
 */
const TipClickedPanel = ({tip, goAwayCallback, colorings, t}) => {
  if (!tip) {return null;}
  const panelStyle = { ...infoPanelStyles.panel};
  panelStyle.maxHeight = "70%";
  const node = tip.n;
  const mutationsToRoot = collectMutations(node);
  return (

 goAwayCallback(tip)}>

 stopProp(e)}>
        <StrainName>{node.name}</StrainName>

            <VaccineInfo node={node} t={t}/>
            <SampleDate node={node} t={t}/>
            <PublicationInfo node={node} t={t}/>
            {getTraitsToDisplay(node).map((trait) => (
              <Trait node={node} trait={trait} colorings={colorings} key={trait}/>
            ))}
            <AccessionAndUrl node={node}/>
            {item("", "")}
            <MutationTable mutations={mutationsToRoot}/>

          {t("Click outside this box to go back to the tree")}

  );
};

export default TipClickedPanel;
<commit_msg>Report all root-to-selected-tip mutations<commit_after>import React from "react";
import { isValueValid } from "../../../util/globals";
import { infoPanelStyles } from "../../../globalStyles";
import { numericToCalendar } from "../../../util/dateHelpers";
import { getTraitFromNode, getFullAuthorInfoFromNode, getVaccineFromNode,
  getAccessionFromNode, getUrlFromNode, collectMutations } from "../../../util/treeMiscHelpers";

export const styles = {
  container: {
    position: "absolute",
    width: "100%",
    height: "100%",
    pointerEvents: "all",
    top: 0,
    left: 0,
    zIndex: 2000,
    backgroundColor: "rgba(80, 80, 80, .20)",
    /* FLEXBOX */
    display: "flex",
    justifyContent: "center",
    alignItems: "center",
    wordWrap: "break-word",
    wordBreak: "break-word"
  }
};

export const stopProp = (e) => {
  if (!e) {e = window.event;} // eslint-disable-line no-param-reassign
  e.cancelBubble = true;
  if (e.stopPropagation) {e.stopPropagation();}
};

/* min width to get "collection date" on 1 line: 120 */
const item = (key, value, href) => (

    {key}
    {href ? (
      {value}
    ) :
      value
    }

);

const formatURL = (url) => {
  if (url !== undefined && url.startsWith("https_")) {
    return url.replace("https_", "https:");
  } else if (url !== undefined && url.startsWith("http_")) {
    return url.replace("http_", "http:");
  }
  return url;
};

const Link = ({url, title, value}) => (

    {title}

      {value}

);

/**
 * Render a 2-column table of gene -> mutations.
 * Rows are sorted by gene name, alphabetically, with "nuc" last.
 * Mutations are sorted by genomic position.
 * todo: sort genes by position in genome
 * todo: provide in-app links from mutations to color-bys? filters?
 */
const MutationTable = ({mutations}) => {
  const geneSortFn = (a, b) => {
    if (a[0]==="nuc") return 1;
    if (b[0]==="nuc") return -1;
    return a[0] {
    const [aa, bb] = [parseInt(a.slice(1, -1), 10), parseInt(b.slice(1, -1), 10)];
    return aa<bb ? -1 : 1;
  };
  // we encode the table here (rather than via `item()`) to set component keys appropriately
  return (
    Object.entries(mutations)
      .sort(geneSortFn)
      .map(([gene, muts], index) => (

          {index===0 ? "Mutations from root" : ""}
          {`${gene}:${muts.sort(mutSortFn).join(", ")}`}

      ))
  );
};

const AccessionAndUrl = ({node}) => {
  const accession = getAccessionFromNode(node);
  const url = getUrlFromNode(node);
  const genbank_accession = getTraitFromNode(node, "genbank_accession");

  /* `gisaid_epi_isl` is a special value attached to nodes introduced during the 2019 nCoV outbreak.
  If set, the display is different from the normal behavior */
  const gisaid_epi_isl = getTraitFromNode(node, "gisaid_epi_isl");
  if (isValueValid(gisaid_epi_isl)) {
    return (
      <>
        <Link title={"GISAID EPI ISL"} value={gisaid_epi_isl.split("_")[2]} url={"https://gisaid.org"}/>
        {isValueValid(genbank_accession) ?
          <Link title={"Genbank accession"} value={genbank_accession} url={"https://www.ncbi.nlm.nih.gov/nuccore/" + genbank_accession}/> :
          null
        }
      </>
    );
  }

  if (isValueValid(genbank_accession)) {
    return (
      <Link title={"Genbank accession"} value={genbank_accession} url={"https://www.ncbi.nlm.nih.gov/nuccore/" + genbank_accession}/>
    );
  } else if (isValueValid(accession) && isValueValid(url)) {
    return (
      <Link url={formatURL(url)} value={accession} title={"Accession"}/>
    );
  } else if (isValueValid(accession)) {
    return (
      item("Accession", accession)
    );
  } else if (isValueValid(url)) {
    return (
      <Link title={"Strain URL"} url={formatURL(url)} value={"click here"}/>
    );
  }
  return null;
};

const VaccineInfo = ({node, t}) => {
  const vaccineInfo = getVaccineFromNode(node);
  if (!vaccineInfo) return null;
  const renderElements = [];
  if (vaccineInfo.selection_date) {
    renderElements.push(

        {t("Vaccine selected")}
        {vaccineInfo.selection_date}

    );
  }
  if (vaccineInfo.start_date) {
    renderElements.push(

        {t("Vaccine start date")}
        {vaccineInfo.start_date}

    );
  }
  if (vaccineInfo.end_date) {
    renderElements.push(

        {t("Vaccine end date")}
        {vaccineInfo.end_date}

    );
  }
  if (vaccineInfo.serum) {
    renderElements.push(

        {t("Serum strain")}

    );
  }
  return renderElements;
};

const PublicationInfo = ({node, t}) => {
  const info = getFullAuthorInfoFromNode(node);
  if (!info) return null;

  const itemsToRender = [];
  itemsToRender.push(item(t("Authors"), info.value));
  if (info.title && info.title !== "?") {
    if (info.paper_url && info.paper_url !== "?") {
      itemsToRender.push(item(t("Title"), info.title, info.paper_url));
    } else {
      itemsToRender.push(item(t("Title"), info.title));
    }
  }
  if (info.journal && info.journal !== "?") {
    itemsToRender.push(item(t("Journal"), info.journal));
  }
  return (itemsToRender.length === 1 ? itemsToRender[0] : itemsToRender);
};

const StrainName = ({children}) => (

{children}

);

const SampleDate = ({node, t}) => {
  const date = getTraitFromNode(node, "num_date");
  if (!date) return null;

  const dateUncertainty = getTraitFromNode(node, "num_date", {confidence: true});
  if (date && dateUncertainty && dateUncertainty[0] !== dateUncertainty[1]) {
    return (
      <>
        {item(t("Inferred collection date"), numericToCalendar(date))}
        {item(t("Date Confidence Interval"), `(${numericToCalendar(dateUncertainty[0])}, ${numericToCalendar(dateUncertainty[1])})`)}
      </>
    );
  }

  return item(t("Collection date"), numericToCalendar(date));
};

const getTraitsToDisplay = (node) => {
  // TODO -- this should be centralised somewhere
  if (!node.node_attrs) return [];
  const ignore = ["author", "div", "num_date", "gisaid_epi_isl", "genbank_accession"];
  return Object.keys(node.node_attrs).filter((k) => !ignore.includes(k));
};

const Trait = ({node, trait, colorings}) => {
  const value_tmp = getTraitFromNode(node, trait);
  let value = value_tmp;
  if (typeof value_tmp === "number") {
    if (!Number.isInteger(value_tmp)) {
      value = Number.parseFloat(value_tmp).toPrecision(3);
    }
  }
  const name = (colorings && colorings[trait] && colorings[trait].title) ?
    colorings[trait].title :
    trait;
  return isValueValid(value) ? item(name, value) : null;
};

/**
 * A React component to display information about a tree tip in a modal-overlay style
 * @param  {Object}   props
 * @param  {Object}   props.tip              tip node selected
 * @param  {function} props.goAwayCallback
 * @param  {object}   props.colorings
 */
const TipClickedPanel = ({tip, goAwayCallback, colorings, t}) => {
  if (!tip) {return null;}
  const panelStyle = { ...infoPanelStyles.panel};
  panelStyle.maxHeight = "70%";
  const node = tip.n;
  const mutationsToRoot = collectMutations(node);
  return (

 goAwayCallback(tip)}>

 stopProp(e)}>
        <StrainName>{node.name}</StrainName>

            <VaccineInfo node={node} t={t}/>
            <SampleDate node={node} t={t}/>
            <PublicationInfo node={node} t={t}/>
            {getTraitsToDisplay(node).map((trait) => (
              <Trait node={node} trait={trait} colorings={colorings} key={trait}/>
            ))}
            <AccessionAndUrl node={node}/>
            {item("", "")}
            <MutationTable mutations={mutationsToRoot}/>

          {t("Click outside this box to go back to the tree")}

  );
};

export default TipClickedPanel;
<|endoftext|>


<commit_before>angular.module('umbraco.services').factory('archetypeLabelService', function (archetypeCacheService) {
    //private

    var isEntityLookupLoading = false;
    var entityCache = [];
    var isDatatypeLookupLoading = false;
    var datatypeCache = [];

    function executeFunctionByName(functionName, context) {
        var args = Array.prototype.slice.call(arguments).splice(2);

        var namespaces = functionName.split(".");
        var func = namespaces.pop();

        for(var i = 0; i < namespaces.length; i++) {
            context = context[namespaces[i]];
        }

        if(context && context[func]) {
            return context[func].apply(this, args);
        }

        return "";
    }

    function getNativeLabel(datatype, value, scope) {
    	switch (datatype.selectedEditor) {
    		case "Imulus.UrlPicker":
    			return imulusUrlPicker(value, scope, {});
    		case "Umbraco.TinyMCEv3":
    			return coreTinyMce(value, scope, {});
            case "Umbraco.MultiNodeTreePicker":
                return coreMntp(value, scope, datatype);
            case "Umbraco.MediaPicker":
                return coreMediaPicker(value, scope, datatype);
    		default:
    			return "";
    	}
    }

    function coreMntp(value, scope, args) {
        var ids = value.split(',');
        var type = "Document";

        switch(args.preValues[0].value.type) {
            case 'content':
                type = 'Document';
                break;
            case 'media':
                type = 'media';
                break;
            case 'member':
                type = 'member';
                break;

            default:
                break;
        }

        var entityArray = [];

        _.each(ids, function(id){
            if(id) {

                var entity = archetypeCacheService.getEntityById(scope, id, type);

                if(entity) {
                    entityArray.push(entity.name);
                }
            }
        });

        return entityArray.join(', ');
    }

    function coreMediaPicker(value, scope, args) {
        if(value) {
             var entity = archetypeCacheService.getEntityById(scope, value, "media");

            if(entity) {
                return entity.name;
            }
        }

        return "";
    }

    function imulusUrlPicker(value, scope, args) {

        if(!args.propertyName) {
            args = {propertyName: "name"}
        }

        var entity;

        switch (value.type) {
            case "content":
                if(value.typeData.contentId) {
                    entity = archetypeCacheService.getEntityById(scope, value.typeData.contentId, "Document");
                }
                break;

            case "media":
                if(value.typeData.mediaId) {
                    entity = archetypeCacheService.getEntityById(scope, value.typeData.mediaId, "Media");
                }
                break;

            case "url":
                return value.typeData.url;

            default:
                break;

        }

        if(entity) {
            return entity[args.propertyName];
        }

        return "";
    }

    function coreTinyMce(value, scope, args) {

        if(!args.contentLength) {
            args = {contentLength: 50}
        }

        var suffix = "";
        var strippedText = $(value).text();

        if(strippedText.length > args.contentLength) {
        	suffix = "…";
        }

        return strippedText.substring(0, args.contentLength) + suffix;
    }

	return {
		getFieldsetTitle: function(scope, fieldsetConfigModel, fieldsetIndex) {

            //console.log(scope.model.config);

            if(!fieldsetConfigModel)
                return "";

            var fieldset = scope.model.value.fieldsets[fieldsetIndex];
            var fieldsetConfig = scope.getConfigFieldsetByAlias(fieldset.alias);
            var template = fieldsetConfigModel.labelTemplate;

            if (template.length < 1)
                return fieldsetConfig.label;

            var rgx = /{{([^)].*)}}/g;
            var results;
            var parsedTemplate = template;

            while ((results = rgx.exec(template)) !== null) {

                // split the template in case it consists of multiple property aliases and/or functions
                var templates = results[0].replace("{{", '').replace("}}", '').split("|");
                var templateLabelValue = "";

                for(var i = 0; i < templates.length; i++) {
                    // stop looking for a template label value if a previous template part already yielded a value
                    if(templateLabelValue != "") {
                        break;
                    }

                    var template = templates[i];

                    //test for function
                    var beginParamsIndexOf = template.indexOf("(");
                    var endParamsIndexOf = template.indexOf(")");

                    //if passed a function
                    if(beginParamsIndexOf != -1 && endParamsIndexOf != -1)
                    {
                        var functionName = template.substring(0, beginParamsIndexOf);
                        var propertyAlias = template.substring(beginParamsIndexOf + 1, endParamsIndexOf).split(',')[0];

                        var args = {};

                        var beginArgsIndexOf = template.indexOf(',');

                        if(beginArgsIndexOf != -1) {

                            var argsString = template.substring(beginArgsIndexOf + 1, endParamsIndexOf).trim();

                            var normalizedJsonString = argsString.replace(/(\w+)\s*:/g, '"$1":');

                            args = JSON.parse(normalizedJsonString);
                        }

                        templateLabelValue = executeFunctionByName(functionName, window, scope.getPropertyValueByAlias(fieldset, propertyAlias), scope, args);
                    }
                    //normal {{foo}} syntax
                    else {
                        propertyAlias = template;
                        var rawValue = scope.getPropertyValueByAlias(fieldset, propertyAlias);

                        templateLabelValue = rawValue;

                        //determine the type of editor
                        var propertyConfig = _.find(fieldsetConfigModel.properties, function(property){
                            return property.alias == propertyAlias;
                        });

                        if(propertyConfig) {
                        	var datatype = archetypeCacheService.getDatatypeByGuid(propertyConfig.dataTypeGuid);

                        	if(datatype) {

                            	//try to get built-in label
                            	var label = getNativeLabel(datatype, templateLabelValue, scope);

                            	if(label) {
                        			templateLabelValue = label;
                        		}
                        		else {
                        			templateLabelValue = templateLabelValue;
                        		}
                        	}
                        }
                        else {
                        	return templateLabelValue;
                        }

                    }
                }
                parsedTemplate = parsedTemplate.replace(results[0], templateLabelValue);
            }

            return parsedTemplate;
        }
	}
});<commit_msg>Fix #276<commit_after>angular.module('umbraco.services').factory('archetypeLabelService', function (archetypeCacheService) {
    //private

    var isEntityLookupLoading = false;
    var entityCache = [];
    var isDatatypeLookupLoading = false;
    var datatypeCache = [];

    function executeFunctionByName(functionName, context) {
        var args = Array.prototype.slice.call(arguments).splice(2);

        var namespaces = functionName.split(".");
        var func = namespaces.pop();

        for(var i = 0; i < namespaces.length; i++) {
            context = context[namespaces[i]];
        }

        if(context && context[func]) {
            return context[func].apply(this, args);
        }

        return "";
    }

    function getNativeLabel(datatype, value, scope) {
    	switch (datatype.selectedEditor) {
    		case "Imulus.UrlPicker":
    			return imulusUrlPicker(value, scope, {});
    		case "Umbraco.TinyMCEv3":
    			return coreTinyMce(value, scope, {});
            case "Umbraco.MultiNodeTreePicker":
                return coreMntp(value, scope, datatype);
            case "Umbraco.MediaPicker":
                return coreMediaPicker(value, scope, datatype);
    		default:
    			return "";
    	}
    }

    function coreMntp(value, scope, args) {
        var ids = value.split(',');
        var type = "Document";

        switch(args.preValues[0].value.type) {
            case 'content':
                type = 'Document';
                break;
            case 'media':
                type = 'media';
                break;
            case 'member':
                type = 'member';
                break;

            default:
                break;
        }

        var entityArray = [];

        _.each(ids, function(id){
            if(id) {

                var entity = archetypeCacheService.getEntityById(scope, id, type);

                if(entity) {
                    entityArray.push(entity.name);
                }
            }
        });

        return entityArray.join(', ');
    }

    function coreMediaPicker(value, scope, args) {
        if(value) {
             var entity = archetypeCacheService.getEntityById(scope, value, "media");

            if(entity) {
                return entity.name;
            }
        }

        return "";
    }

    function imulusUrlPicker(value, scope, args) {

        if(!args.propertyName) {
            args = {propertyName: "name"}
        }

        var entity;

        switch (value.type) {
            case "content":
                if(value.typeData.contentId) {
                    entity = archetypeCacheService.getEntityById(scope, value.typeData.contentId, "Document");
                }
                break;

            case "media":
                if(value.typeData.mediaId) {
                    entity = archetypeCacheService.getEntityById(scope, value.typeData.mediaId, "Media");
                }
                break;

            case "url":
                return value.typeData.url;

            default:
                break;

        }

        if(entity) {
            return entity[args.propertyName];
        }

        return "";
    }

    function coreTinyMce(value, scope, args) {

        if(!args.contentLength) {
            args = {contentLength: 50}
        }

        var suffix = "";
        var strippedText = $(value).text();

        if(strippedText.length > args.contentLength) {
        	suffix = "…";
        }

        return strippedText.substring(0, args.contentLength) + suffix;
    }

	return {
		getFieldsetTitle: function(scope, fieldsetConfigModel, fieldsetIndex) {

            //console.log(scope.model.config);

            if(!fieldsetConfigModel)
                return "";

            var fieldset = scope.model.value.fieldsets[fieldsetIndex];
            var fieldsetConfig = scope.getConfigFieldsetByAlias(fieldset.alias);
            var template = fieldsetConfigModel.labelTemplate;

            if (template.length < 1)
                return fieldsetConfig.label;

            var rgx = /({{(.*?)}})*/g;
            var results;
            var parsedTemplate = template;

            var rawMatches = template.match(rgx);

            var matches = [];

            _.each(rawMatches, function(match){
                if(match) {
                    matches.push(match);
                }
            });

            _.each(matches, function (match) {

                // split the template in case it consists of multiple property aliases and/or functions
                var templates = match.replace("{{", '').replace("}}", '').split("|");
                var templateLabelValue = "";

                for(var i = 0; i < templates.length; i++) {
                    // stop looking for a template label value if a previous template part already yielded a value
                    if(templateLabelValue != "") {
                        break;
                    }

                    var template = templates[i];

                    //test for function
                    var beginParamsIndexOf = template.indexOf("(");
                    var endParamsIndexOf = template.indexOf(")");

                    //if passed a function
                    if(beginParamsIndexOf != -1 && endParamsIndexOf != -1)
                    {
                        var functionName = template.substring(0, beginParamsIndexOf);
                        var propertyAlias = template.substring(beginParamsIndexOf + 1, endParamsIndexOf).split(',')[0];

                        var args = {};

                        var beginArgsIndexOf = template.indexOf(',');

                        if(beginArgsIndexOf != -1) {

                            var argsString = template.substring(beginArgsIndexOf + 1, endParamsIndexOf).trim();

                            var normalizedJsonString = argsString.replace(/(\w+)\s*:/g, '"$1":');

                            args = JSON.parse(normalizedJsonString);
                        }

                        templateLabelValue = executeFunctionByName(functionName, window, scope.getPropertyValueByAlias(fieldset, propertyAlias), scope, args);
                    }
                    //normal {{foo}} syntax
                    else {
                        propertyAlias = template;
                        var rawValue = scope.getPropertyValueByAlias(fieldset, propertyAlias);

                        templateLabelValue = rawValue;

                        //determine the type of editor
                        var propertyConfig = _.find(fieldsetConfigModel.properties, function(property){
                            return property.alias == propertyAlias;
                        });

                        if(propertyConfig) {
                        	var datatype = archetypeCacheService.getDatatypeByGuid(propertyConfig.dataTypeGuid);

                        	if(datatype) {

                            	//try to get built-in label
                            	var label = getNativeLabel(datatype, templateLabelValue, scope);

                            	if(label) {
                        			templateLabelValue = label;
                        		}
                        		else {
                        			templateLabelValue = templateLabelValue;
                        		}
                        	}
                        }
                        else {
                        	return templateLabelValue;
                        }

                    }
                }

                parsedTemplate = parsedTemplate.replace(match, templateLabelValue);
            });

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


<commit_before>const ErrorHandler = require('./error_handler');
const pgErrorCodes = require('../postgresql/error_codes');

module.exports = function ErrorHandlerFactory () {
    if (isTimeoutError(err)) {
        return createTimeoutError();
    } else {
        return createGenericError(err);
    }
};

function isTimeoutError(err) {
    return err.message && (
        err.message.indexOf('statement timeout') > -1 ||
        err.message.indexOf('RuntimeError: Execution of function interrupted by signal') > -1
    );
}

function createTimeoutError() {
    return new ErrorHandler(
        'You are over platform\'s limits. Please contact us to know more details',
        'limit',
        'datasource',
        undefined,
        429
    );
}

function createGenericError(err) {
    return new ErrorHandler(
        err.message,
        err.context,
        err.detail,
        err.hint,
        err.http_status,
        pgErrorCodes.codeToCondition[err.code] || err.name
    );
}
<commit_msg>fix error<commit_after>const ErrorHandler = require('./error_handler');
const { codeToCondition } = require('../postgresql/error_codes');

module.exports = function ErrorHandlerFactory (err) {
    if (isTimeoutError(err)) {
        return createTimeoutError();
    } else {
        return createGenericError(err);
    }
};

function isTimeoutError(err) {
    return err.message && (
        err.message.indexOf('statement timeout') > -1 ||
        err.message.indexOf('RuntimeError: Execution of function interrupted by signal') > -1
    );
}

function createTimeoutError() {
    return new ErrorHandler(
        'You are over platform\'s limits. Please contact us to know more details',
        'limit',
        'datasource',
        undefined,
        429
    );
}

function createGenericError(err) {
    return new ErrorHandler(
        err.message,
        err.context,
        err.detail,
        err.hint,
        err.http_status,
        codeToCondition[err.code] || err.name
    );
}
<|endoftext|>


<commit_before>import React, { Component } from "react";
import gql from "graphql-tag";
import { graphql, withApollo } from "react-apollo";
import { Container, Row, Col, Input, FormGroup } from "reactstrap";
import "./style.css";

const OBJECTIVE_SUB = gql`
  subscription ObjectivesUpdate($simulatorId: ID!) {
    objectiveUpdate(simulatorId: $simulatorId) {
      id
      title
      description
      station
      completed
    }
  }
`;

const Objective = ({ id, title, description, completed, client }) => {
  const complete = () => {
    const mutation = gql`
      mutation CompleteObjective($id: ID!) {
        completeObjective(id: $id)
      }
    `;
    const variables = {
      id
    };
    client.mutate({
      mutation,
      variables
    });
  };
  return (
    <Row className="objective">
      <Col sm={2}>
        <FormGroup check>
          <Input
            type="checkbox"
            checked={completed}
            disabled={completed}
            onClick={complete}
          />
        </FormGroup>
      </Col>
      <Col sm={10}>
        {title}

{description}

      </Col>
    </Row>
  );
};

class Objectives extends Component {
  subscription = null;
  componentWillReceiveProps(nextProps) {
    if (!this.subscription && !nextProps.data.loading) {
      this.subscription = nextProps.data.subscribeToMore({
        document: OBJECTIVE_SUB,
        variables: {
          simulatorId: nextProps.simulator.id
        },
        updateQuery: (previousResult, { subscriptionData }) => {
          return Object.assign({}, previousResult, {
            objective: subscriptionData.data.objectiveUpdate
          });
        }
      });
    }
  }
  componentWillUnmount() {
    this.subscription && this.subscription();
  }
  render() {
    const { data: { loading, objective }, client } = this.props;
    if (loading || !objective) return null;
    return (
      <Container className="objective-core">
        <Row>
          <Col sm={12}>
            {objective
              .concat()
              .reverse()
              .map(o => <Objective key={o.id} {...o} client={client} />)}
          </Col>
        </Row>
      </Container>
    );
  }
}

const OBJECTIVE_QUERY = gql`
  query Objectives($simulatorId: ID!) {
    objective(simulatorId: $simulatorId) {
      id
      title
      description
      station
      completed
    }
  }
`;
export default graphql(OBJECTIVE_QUERY, {
  options: ownProps => ({
    fetchPolicy: "cache-and-network",
    variables: {
      simulatorId: ownProps.simulator.id
    }
  })
})(withApollo(Objectives));
<commit_msg>Add a button to add new objectives<commit_after>import React, { Component } from "react";
import gql from "graphql-tag";
import { graphql, withApollo } from "react-apollo";
import { Container, Row, Col, Input, FormGroup, Button } from "reactstrap";
import "./style.css";

const OBJECTIVE_SUB = gql`
  subscription ObjectivesUpdate($simulatorId: ID!) {
    objectiveUpdate(simulatorId: $simulatorId) {
      id
      title
      description
      station
      completed
    }
  }
`;

const Objective = ({ id, title, description, completed, client }) => {
  const complete = () => {
    const mutation = gql`
      mutation CompleteObjective($id: ID!) {
        completeObjective(id: $id)
      }
    `;
    const variables = {
      id
    };
    client.mutate({
      mutation,
      variables
    });
  };
  return (
    <Row className="objective">
      <Col sm={2}>
        <FormGroup check>
          <Input
            type="checkbox"
            checked={completed}
            disabled={completed}
            onClick={complete}
          />
        </FormGroup>
      </Col>
      <Col sm={10}>
        {title}

{description}

      </Col>
    </Row>
  );
};

class Objectives extends Component {
  subscription = null;
  componentWillReceiveProps(nextProps) {
    if (!this.subscription && !nextProps.data.loading) {
      this.subscription = nextProps.data.subscribeToMore({
        document: OBJECTIVE_SUB,
        variables: {
          simulatorId: nextProps.simulator.id
        },
        updateQuery: (previousResult, { subscriptionData }) => {
          return Object.assign({}, previousResult, {
            objective: subscriptionData.data.objectiveUpdate
          });
        }
      });
    }
  }
  addObjective = () => {
    const title = window.prompt("What is the title of the objective?");
    if (!title) return;
    const description = window.prompt(
      "What is the description of the objective? (May be blank)"
    );
    const mutation = gql`
      mutation CreateObjective(
        $simulatorId: ID!
        $title: String!
        $description: String
      ) {
        addObjective(
          objective: {
            title: $title
            description: $description
            simulatorId: $simulatorId
          }
        )
      }
    `;
    const variables = {
      simulatorId: this.props.simulator.id,
      title,
      description
    };
    this.props.client.mutate({
      mutation,
      variables
    });
  };
  componentWillUnmount() {
    this.subscription && this.subscription();
  }
  render() {
    const { data: { loading, objective }, client } = this.props;
    if (loading || !objective) return null;
    return (
      <Container className="objective-core">
        <Row>
          <Col sm={12}>
            <Button color="success" size="sm" block onClick={this.addObjective}>
              Add New Objective
            </Button>
            {objective
              .concat()
              .reverse()
              .map(o => <Objective key={o.id} {...o} client={client} />)}
          </Col>
        </Row>
      </Container>
    );
  }
}

const OBJECTIVE_QUERY = gql`
  query Objectives($simulatorId: ID!) {
    objective(simulatorId: $simulatorId) {
      id
      title
      description
      station
      completed
    }
  }
`;
export default graphql(OBJECTIVE_QUERY, {
  options: ownProps => ({
    fetchPolicy: "cache-and-network",
    variables: {
      simulatorId: ownProps.simulator.id
    }
  })
})(withApollo(Objectives));
<|endoftext|>


<commit_before>/**
 * @constructor
 * @mixes bindUIElementsMixin
 */
var MessageBox = Backbone.View.extend({

    tagName: 'div',

    className: 'modal fade messagebox',

    UI: {
        firstfocuselementinmodal: '.firstfocuselementinmodal',
        lastfocuselementinmodal: '.lastfocuselementinmodal'
    },

    events: {
        'click .btn': 'onClickButtonHandler',
        'focusin .lastfocuselementinmodal': 'onFocusinLastElement',
        'keydown': 'onKeydownHandler'
    },

    template: InfinniUI.Template["services/messageBox/template/default.tpl.html"],

    initialize: function (options) {

        this.setOptions(options);

        // чтобы пользователь случайно не обратился к элементу в фокусе,
        // пока диалоговое окно создается и ещё не перехватило фокус,
        // необходимо старую фокусировку снять
        $(document.activeElement).blur();
        this.render();
        this.bindUIElements();
        this.$el
            .modal({show: true});
    },

    setOptions: function (config) {
        this.options = this.applyDefaultOptions(config);
    },

    onFocusinLastElement: function () {
        this.ui.firstfocuselementinmodal.focus();
    },

    onKeydownHandler: function (event) {
        if(document.activeElement === this.ui.lastfocuselementinmodal[0] && (event.which || event.keyCode) == 9) {
            event.preventDefault();
            this.ui.firstfocuselementinmodal.focus();
        }

        if(document.activeElement === this.ui.firstfocuselementinmodal[0] && (event.which || event.keyCode) == 9 && event.shiftKey){
            event.preventDefault();
            this.ui.lastfocuselementinmodal.focus();
        }
    },

    render: function () {
        var $parent = this.options.$parent || $('body');

        var html = this.template(this.options);
        this.$el.html(html);

        this.subscribeToDialog();
        $parent.append(this.$el);

        return this;
    },

    subscribeToDialog: function (){
        var view = this;
        this.$el.on('shown.bs.modal', function (e) {
            view.ui.firstfocuselementinmodal.focus();
        });

        this.$el.on('hidden.bs.modal', function () {
            view.remove();
        });
    },

    onClickButtonHandler: function (event) {
        event.preventDefault();

        var $el = $(event.target),
            i = parseInt( $el.data('index'), 10),
            handler = this.options.buttons[i].onClick;

        if(handler){
            handler.apply(null);
        }

        this.close();
    },

    close: function () {
        if (typeof this.options.onClose === 'function') {
            this.options.onClose.call(null);
        }

        this.$el.modal('hide');
    },

    applyDefaultOptions: function (config) {
        var options = _.defaults({}, config, {
            type: 'default',
            buttons: []
        });
        this.applyDefaultButtonsOptions(options);

        return options;
    },

    applyDefaultButtonsOptions: function (options) {
        options.buttons
            .filter(function (button) {
                return typeof button.type === 'undefined';
            })
            .forEach(function (button) {
                button.type = 'default';
            });

        return options;
    }
});

_.extend(MessageBox.prototype, bindUIElementsMixin);

InfinniUI.MessageBox = MessageBox;

/*new MessageBox({
    type: 'error',
    text:'asdasd',
    buttons:[
        {
            name:'Ok',
            onClick: function(){
                alert('ckicked');
            }
        },
        {
            name:'Error btn',
            type: 'action',
            onClick: function(){
                alert('error ckicked');
            }
        }
    ]
});*/<commit_msg>UI-2261: Не получается навесить ссылку на кнопку в MessageBox #resolved<commit_after>/**
 * @constructor
 * @mixes bindUIElementsMixin
 */
var MessageBox = Backbone.View.extend({

    tagName: 'div',

    className: 'modal fade messagebox',

    UI: {
        firstfocuselementinmodal: '.firstfocuselementinmodal',
        lastfocuselementinmodal: '.lastfocuselementinmodal'
    },

    events: {
        'click .btn': 'onClickButtonHandler',
        'focusin .lastfocuselementinmodal': 'onFocusinLastElement',
        'keydown': 'onKeydownHandler'
    },

    template: InfinniUI.Template["services/messageBox/template/default.tpl.html"],

    initialize: function (options) {

        this.setOptions(options);

        // чтобы пользователь случайно не обратился к элементу в фокусе,
        // пока диалоговое окно создается и ещё не перехватило фокус,
        // необходимо старую фокусировку снять
        $(document.activeElement).blur();
        this.render();
        this.bindUIElements();
        this.$el
            .modal({show: true});
    },

    setOptions: function (config) {
        this.options = this.applyDefaultOptions(config);
    },

    onFocusinLastElement: function () {
        this.ui.firstfocuselementinmodal.focus();
    },

    onKeydownHandler: function (event) {
        if(document.activeElement === this.ui.lastfocuselementinmodal[0] && (event.which || event.keyCode) == 9) {
            event.preventDefault();
            this.ui.firstfocuselementinmodal.focus();
        }

        if(document.activeElement === this.ui.firstfocuselementinmodal[0] && (event.which || event.keyCode) == 9 && event.shiftKey){
            event.preventDefault();
            this.ui.lastfocuselementinmodal.focus();
        }
    },

    render: function () {
        var $parent = this.options.$parent || $('body');

        var html = this.template(this.options);
        this.$el.html(html);

        this.subscribeToDialog();
        $parent.append(this.$el);

        return this;
    },

    subscribeToDialog: function (){
        var view = this;
        this.$el.on('shown.bs.modal', function (e) {
            view.ui.firstfocuselementinmodal.focus();
        });

        this.$el.on('hidden.bs.modal', function () {
            view.remove();
        });
    },

    onClickButtonHandler: function (event) {
        var $el = $(event.target),
            i = parseInt( $el.data('index'), 10),
            handler = this.options.buttons[i].onClick;

        if(handler){
            handler.apply(null);
        }

        this.close();
    },

    close: function () {
        if (typeof this.options.onClose === 'function') {
            this.options.onClose.call(null);
        }

        this.$el.modal('hide');
    },

    applyDefaultOptions: function (config) {
        var options = _.defaults({}, config, {
            type: 'default',
            buttons: []
        });
        this.applyDefaultButtonsOptions(options);

        return options;
    },

    applyDefaultButtonsOptions: function (options) {
        options.buttons
            .filter(function (button) {
                return typeof button.type === 'undefined';
            })
            .forEach(function (button) {
                button.type = 'default';
            });

        return options;
    }
});

_.extend(MessageBox.prototype, bindUIElementsMixin);

InfinniUI.MessageBox = MessageBox;

/*new MessageBox({
    type: 'error',
    text:'asdasd',
    buttons:[
        {
            name:'Ok',
            onClick: function(){
                alert('ckicked');
            }
        },
        {
            name:'Error btn',
            type: 'action',
            onClick: function(){
                alert('error ckicked');
            }
        }
    ]
});*/<|endoftext|>


<commit_before>var Pac = require('node-pac');
var Rules = require('./rules');
var util = require('../util');
var rules = new Rules();
var pacRules = new Rules();
var cachedPacs = {};
var pacCount = 0;

exports.Rules = Rules;
exports.parse = rules.parse.bind(rules);
exports.append = rules.append.bind(rules);
exports.resolveHost = rules.resolveHost.bind(rules);
exports.resolveFilter = rules.resolveFilter.bind(rules);
exports.resolveDisable = rules.resolveDisable.bind(rules);
exports.resolveProxy = rules.resolveProxy.bind(rules);
exports.resolveRules = rules.resolveRules.bind(rules);
exports.resolveRule = rules.resolveRule.bind(rules);
exports.clearAppend = rules.clearAppend.bind(rules);

exports.disableDnsCache = function() {
  Rules.disableDnsCache();
};

function getProxy(url, req, callback) {
  var reqRules = req && req.rules;
  if (!reqRules) {
    return callback();
  }

  delete reqRules.proxy;
  var proxy = (req.pluginRules && req.pluginRules.resolveProxy(url)) || rules.resolveProxy(url);
  if (proxy) {
    reqRules.proxy = proxy;
    return callback();
  }

  var pacRule = reqRules && reqRules.pac;
  var pacUrl = util.getMatcherValue(pacRule);
  if (!pacUrl) {
    return callback();
  }

  var pac = cachedPacs[pacUrl];
  if (pac) {
    delete cachedPacs[pacUrl];
    cachedPacs[pacUrl] = pac;
  } else {
    pacCount++;
    var list = Object.keys(cachedPacs);
    if (list.length >= 10) {
      pacCount--;
      delete cachedPacs[list[0]];
    }
    pacUrl = /^https?\:\/\//.test(pacUrl) ? pacUrl : util.join(pacRule.root, pacUrl);
    cachedPacs[pacUrl] = pac = new Pac(pacUrl);
  }
  return pac.findWhistleProxyForURL(url.replace('tunnel:', 'https:'), function(err, rule) {
    if (rule) {
      pacRules.parse(pacRule.rawPattern + ' ' + rule);
      rule = pacRules.resolveRules(url);
      if (rule && rule.proxy) {
        reqRules.proxy = rule.proxy;
        reqRules.proxy.raw = pacRule.raw;
      }
    }
    callback();
  });
}

exports.getProxy = getProxy;

<commit_msg>chore: Log the error of finding proxy from pac<commit_after>var Pac = require('node-pac');
var Rules = require('./rules');
var util = require('../util');
var logger = require('../util/logger');
var rules = new Rules();
var pacRules = new Rules();
var cachedPacs = {};
var pacCount = 0;

exports.Rules = Rules;
exports.parse = rules.parse.bind(rules);
exports.append = rules.append.bind(rules);
exports.resolveHost = rules.resolveHost.bind(rules);
exports.resolveFilter = rules.resolveFilter.bind(rules);
exports.resolveDisable = rules.resolveDisable.bind(rules);
exports.resolveProxy = rules.resolveProxy.bind(rules);
exports.resolveRules = rules.resolveRules.bind(rules);
exports.resolveRule = rules.resolveRule.bind(rules);
exports.clearAppend = rules.clearAppend.bind(rules);

exports.disableDnsCache = function() {
  Rules.disableDnsCache();
};

function getProxy(url, req, callback) {
  var reqRules = req && req.rules;
  if (!reqRules) {
    return callback();
  }

  delete reqRules.proxy;
  var proxy = (req.pluginRules && req.pluginRules.resolveProxy(url)) || rules.resolveProxy(url);
  if (proxy) {
    reqRules.proxy = proxy;
    return callback();
  }

  var pacRule = reqRules && reqRules.pac;
  var pacUrl = util.getMatcherValue(pacRule);
  if (!pacUrl) {
    return callback();
  }

  var pac = cachedPacs[pacUrl];
  if (pac) {
    delete cachedPacs[pacUrl];
    cachedPacs[pacUrl] = pac;
  } else {
    pacCount++;
    var list = Object.keys(cachedPacs);
    if (list.length >= 10) {
      pacCount--;
      delete cachedPacs[list[0]];
    }
    pacUrl = /^https?\:\/\//.test(pacUrl) ? pacUrl : util.join(pacRule.root, pacUrl);
    cachedPacs[pacUrl] = pac = new Pac(pacUrl);
  }
  return pac.findWhistleProxyForURL(url.replace('tunnel:', 'https:'), function(err, rule) {
    if (rule) {
      pacRules.parse(pacRule.rawPattern + ' ' + rule);
      rule = pacRules.resolveRules(url);
      if (rule && rule.proxy) {
        reqRules.proxy = rule.proxy;
        reqRules.proxy.raw = pacRule.raw;
      }
    }
    callback();
    logger.error(err);
  });
}

exports.getProxy = getProxy;

<|endoftext|>


<commit_before>var util = require('../../util');
var rulesUtil = require('./util');
var url = require('url');
var net = require('net');
var dns = require('dns');
var extend = require('util')._extend;

var rules, hosts, heads, req, res, proxy, weinre;
rules = hosts = heads = req = res = proxy = weinre = [];

function parse(text) {
	rules = [];
	hosts = [];
	heads = [];
	req = [];
	res = [];
	weinre = [];
	proxy = [];
	if (!text || !(text = text.trim())) {
		return;
	}

	text.split(/\n|\r\n|\r/g).forEach(pareLine);
}

function pareLine(line) {
	var raw = line;
	line = line.replace(/#.*$/, '').trim();
	if (!line) {
		return;
	}

	line = line.split(/\s+/);
	var pattern = line[0];
	if (net.isIP(pattern) || (util.hasProtocol(pattern) && !/^https?:\/\//.test(pattern))) {
		line.slice(1).forEach(function(matcher) {
			parseRule(matcher, pattern, raw);
		});
	} else if (!util.isRegExp(pattern) && util.isRegExp(line[1])) {
		parseRule(line[1], pattern, raw);
	} else {
		parseRule(pattern, line[1], raw);
	}
}

function parseRule(pattern, matcher, raw) {
	var isRegExp;
	if (!pattern || !matcher ||
			((isRegExp = util.isRegExp(pattern)) && !(pattern = util.toRegExp(pattern)))) {
		return;
	}

	var rule = {
			isRegExp: isRegExp,
			protocol: util.getProtocol(pattern),
			pattern: isRegExp ? pattern : formatUrl(pattern),
			matcher: matcher,
			raw: raw
		};

	if (net.isIP(matcher)) {
		hosts.push(rule);
	} else if (/^head:\/\//.test(matcher)) {
		heads.push(rule);
	} else if (/^req:\/\//.test(matcher)) {
		req.push(rule);
	} else if (/^res:\/\//.test(matcher)) {
		res.push(rule);
	} else if (/^proxy:\/\//.test(matcher)) {
		proxy.push(rule);
	} else if (/^weinre:\/\//.test(matcher)) {
		weinre.push(rule);
	} else {
		rules.push(rule);
	}
}

exports.parse = parse;

function resolveHost(_url, callback) {
	_url = formatUrl(util.setProtocol(_url));
	var options = url.parse(_url);
	if (!util.isWebProtocol(options.protocol)) {

		return callback(null, null);
	}

	_url = _url.replace(/^(https:\/\/[^\/]+):443(\/.*)/i, '$1$2');

	var host = getRule(_url, hosts);
	if (host) {

		return callback(null, host.matcher);
	}

	try {
		dns.lookup(options.hostname, function(err, ip, addressType) {
		      callback(err, err ? null : (ip || (!addressType || addressType === 4 ? '127.0.0.1' : '0:0:0:0:0:0:0:1')));
		  });
	} catch(err) {//如果断网，可能直接抛异常，https代理没有用到error-handler
		callback(err);
	}
}

function formatUrl(pattern) {

	return pattern.indexOf('/', util.getProtocol(pattern) == null ? 0 : pattern.indexOf('://') + 3) == -1 ?
			pattern + '/' : pattern;
}

exports.resolveHost = resolveHost;

function getRule(url, rules) {
	rules = _getRule(url, rules);

	if (rules) {
		url = rules.url;
		var index = url.indexOf('://') + 3;
		var protocol = url.substring(0, index);
		url = url.substring(index);
		var key = getKey(url);
		if (key) {
			rules.key = key;
		}
		var value = key && rulesUtil.getValue(key);
		if (value == null) {
			value = false;
		}
		if (value !== false || (value = getValue(url)) !== false) {
			rules.value = protocol + value;
		} else if ((value = getPath(url)) !== false) {
			rules.path = protocol + value;
		}
	}

	return rules;
}

function getKey(url) {
	if (url.indexOf('{') == 0) {
		var index = url.lastIndexOf('}');
		return index > 1 && url.substring(1, index);
	}

	return false;
}

function getValue(url) {
	if (url.indexOf('(') == 0) {
		var index = url.lastIndexOf(')');
		return index != -1 && url.substring(1, index) || '';
	}

	return false;
}

function getPath(url) {
	if (url.indexOf('<') == 0) {
		var index = url.lastIndexOf('>');
		return index != -1 && url.substring(1, index) || '';
	}

	return false;
}

function _getRule(url, _rules) {
	_rules = _rules || rules;
	if (util.removeProtocol(url, true).indexOf('/') == -1) {
		url += '/';
	}
	var _url = url.replace(/(?:\?|#).*$/, '');
	for (var i = 0, rule; rule = _rules[i]; i++) {
		var pattern = rule.pattern;
		if (rule.isRegExp) {
			if (pattern.test(url)) {
				var regExp = {};
				for (var i = 0; i < 10; i++) {
					regExp['$' + i] = RegExp['$' + i] || '';
				}

				return extend({
					url: setProtocol(rule.matcher.replace(/(^|.)?(\$[1-9])/g,
							function(matched, $1, $2) {
						return $1 == '\\' ? $2 : ($1 || '') + regExp[$2];
					}), url)
				}, rule);;
			}
		} else {
			pattern = setProtocol(pattern, url);
			if (_url.indexOf(pattern) === 0) {
				var len = pattern.length;
				if (pattern == _url || isPathSeparator(_url[len]) || isPathSeparator(pattern[len - 1])) {
					return extend({
						url: join(setProtocol(rule.matcher, url), url.substring(len))
					}, rule);
				}
			}
		}
	}
}

function setProtocol(target, source) {
	if (util.hasProtocol(target)) {
		return target;
	}

	var protocol = util.getProtocol(source);
	if (protocol == null) {
		return target;
	}

	return protocol + '//' + target;
}

function resolveRule(url) {
	return getRule(url);
}

function resolveHead(url) {
	return getRule(url, heads);
}

function resolveReq(url) {
	return getRule(url, req);
}

function resolveRes(url) {
	return getRule(url, res);
}

function resolveProxy(url) {
	return getRule(url, proxy);
}

function resolveWeinre(url) {
	return getRule(url, weinre);
}

function isPathSeparator(ch) {
	return ch == '/' || ch == '\\';
}

function join(first, last) {
	if (!first || !last) {
		return first + last;
	}

	var len = first.length - 1;
	if (isPathSeparator(first[len])) {
		return isPathSeparator(last[0]) ? first.substring(0, len) + last : first + last;
	}

	return isPathSeparator(last[0]) ? first + last : first + '/' + last;
}

exports.resolveRules = function resolveRules(url) {
	var rules = {};
	var rule = resolveRule(url);
	if (rule) {
		rules.rule = rule;
	}
	if (rule = resolveHead(url)) {
		rules.head = rule;
	}
	if (rule = resolveReq(url)) {
		rules.req = rule;
	}
	if (rule = resolveRes(url)) {
		rules.res = rule;
	}
	if (rule = resolveProxy(url)) {
		rules.proxy = rule;
	}
	if (rule = resolveWeinre(url)) {
		rules.weinre = rule;
	}

	return rules;
};
<commit_msg>hosts<commit_after>var util = require('../../util');
var rulesUtil = require('./util');
var url = require('url');
var net = require('net');
var dns = require('dns');
var extend = require('util')._extend;

var rules, hosts, heads, req, res, proxy, weinre;
rules = hosts = heads = req = res = proxy = weinre = [];

function parse(text) {
	rules = [];
	hosts = [];
	heads = [];
	req = [];
	res = [];
	weinre = [];
	proxy = [];
	if (!text || !(text = text.trim())) {
		return;
	}

	text.split(/\n|\r\n|\r/g).forEach(pareLine);
}

function pareLine(line) {
	var raw = line;
	line = line.replace(/#.*$/, '').trim();
	if (!line) {
		return;
	}

	line = line.split(/\s+/);
	var pattern = line[0];
	if (net.isIP(pattern) || (util.hasProtocol(pattern) && !/^https?:\/\//.test(pattern))) {
		line.slice(1).forEach(function(matcher) {
			parseRule(matcher, pattern, raw);
		});
	} else if (!util.isRegExp(pattern) && util.isRegExp(line[1])) {
		parseRule(line[1], pattern, raw);
	} else {
		parseRule(pattern, line[1], raw);
	}
}

function parseRule(pattern, matcher, raw) {
	var isRegExp;
	if (!pattern || !matcher ||
			((isRegExp = util.isRegExp(pattern)) && !(pattern = util.toRegExp(pattern)))) {
		return;
	}

	var rule = {
			isRegExp: isRegExp,
			protocol: isRegExp ? pattern : util.getProtocol(pattern),
			pattern: isRegExp ? pattern : formatUrl(pattern),
			matcher: matcher,
			raw: raw
		};

	if (net.isIP(matcher)) {
		hosts.push(rule);
	} else if (/^head:\/\//.test(matcher)) {
		heads.push(rule);
	} else if (/^req:\/\//.test(matcher)) {
		req.push(rule);
	} else if (/^res:\/\//.test(matcher)) {
		res.push(rule);
	} else if (/^proxy:\/\//.test(matcher)) {
		proxy.push(rule);
	} else if (/^weinre:\/\//.test(matcher)) {
		weinre.push(rule);
	} else {
		rules.push(rule);
	}
}

exports.parse = parse;

function resolveHost(_url, callback) {
	_url = formatUrl(util.setProtocol(_url));
	var options = url.parse(_url);
	if (!util.isWebProtocol(options.protocol)) {

		return callback(null, null);
	}

	_url = _url.replace(/^(https:\/\/[^\/]+):443(\/.*)/i, '$1$2');

	var host = getRule(_url, hosts);
	if (host) {

		return callback(null, host.matcher);
	}

	try {
		dns.lookup(options.hostname, function(err, ip, addressType) {
		      callback(err, err ? null : (ip || (!addressType || addressType === 4 ? '127.0.0.1' : '0:0:0:0:0:0:0:1')));
		  });
	} catch(err) {//如果断网，可能直接抛异常，https代理没有用到error-handler
		callback(err);
	}
}

function formatUrl(pattern) {

	return pattern.indexOf('/', util.getProtocol(pattern) == null ? 0 : pattern.indexOf('://') + 3) == -1 ?
			pattern + '/' : pattern;
}

exports.resolveHost = resolveHost;

function getRule(url, rules) {
	rules = _getRule(url, rules);

	if (rules) {
		url = rules.url;
		var index = url.indexOf('://') + 3;
		var protocol = url.substring(0, index);
		url = url.substring(index);
		var key = getKey(url);
		if (key) {
			rules.key = key;
		}
		var value = key && rulesUtil.getValue(key);
		if (value == null) {
			value = false;
		}
		if (value !== false || (value = getValue(url)) !== false) {
			rules.value = protocol + value;
		} else if ((value = getPath(url)) !== false) {
			rules.path = protocol + value;
		}
	}

	return rules;
}

function getKey(url) {
	if (url.indexOf('{') == 0) {
		var index = url.lastIndexOf('}');
		return index > 1 && url.substring(1, index);
	}

	return false;
}

function getValue(url) {
	if (url.indexOf('(') == 0) {
		var index = url.lastIndexOf(')');
		return index != -1 && url.substring(1, index) || '';
	}

	return false;
}

function getPath(url) {
	if (url.indexOf('<') == 0) {
		var index = url.lastIndexOf('>');
		return index != -1 && url.substring(1, index) || '';
	}

	return false;
}

function _getRule(url, _rules) {
	_rules = _rules || rules;
	if (util.removeProtocol(url, true).indexOf('/') == -1) {
		url += '/';
	}
	var _url = url.replace(/(?:\?|#).*$/, '');
	for (var i = 0, rule; rule = _rules[i]; i++) {
		var pattern = rule.pattern;
		if (rule.isRegExp) {
			if (pattern.test(url)) {
				var regExp = {};
				for (var i = 0; i < 10; i++) {
					regExp['$' + i] = RegExp['$' + i] || '';
				}

				return extend({
					url: setProtocol(rule.matcher.replace(/(^|.)?(\$[1-9])/g,
							function(matched, $1, $2) {
						return $1 == '\\' ? $2 : ($1 || '') + regExp[$2];
					}), url)
				}, rule);;
			}
		} else {
			pattern = setProtocol(pattern, url);
			if (_url.indexOf(pattern) === 0) {
				var len = pattern.length;
				if (pattern == _url || isPathSeparator(_url[len]) || isPathSeparator(pattern[len - 1])) {
					return extend({
						url: join(setProtocol(rule.matcher, url), url.substring(len))
					}, rule);
				}
			}
		}
	}
}

function setProtocol(target, source) {
	if (util.hasProtocol(target)) {
		return target;
	}

	var protocol = util.getProtocol(source);
	if (protocol == null) {
		return target;
	}

	return protocol + '//' + target;
}

function resolveRule(url) {
	return getRule(url);
}

function resolveHead(url) {
	return getRule(url, heads);
}

function resolveReq(url) {
	return getRule(url, req);
}

function resolveRes(url) {
	return getRule(url, res);
}

function resolveProxy(url) {
	return getRule(url, proxy);
}

function resolveWeinre(url) {
	return getRule(url, weinre);
}

function isPathSeparator(ch) {
	return ch == '/' || ch == '\\';
}

function join(first, last) {
	if (!first || !last) {
		return first + last;
	}

	var len = first.length - 1;
	if (isPathSeparator(first[len])) {
		return isPathSeparator(last[0]) ? first.substring(0, len) + last : first + last;
	}

	return isPathSeparator(last[0]) ? first + last : first + '/' + last;
}

exports.resolveRules = function resolveRules(url) {
	var rules = {};
	var rule = resolveRule(url);
	if (rule) {
		rules.rule = rule;
	}
	if (rule = resolveHead(url)) {
		rules.head = rule;
	}
	if (rule = resolveReq(url)) {
		rules.req = rule;
	}
	if (rule = resolveRes(url)) {
		rules.res = rule;
	}
	if (rule = resolveProxy(url)) {
		rules.proxy = rule;
	}
	if (rule = resolveWeinre(url)) {
		rules.weinre = rule;
	}

	return rules;
};
<|endoftext|>


<commit_before>var util = require('../util');
var values = require('./util').values;
var url = require('url');
var net = require('net');
var lookup = require('./dns');
var extend = require('util')._extend;
var protoMgr = require('./protocols');
var allowDnsCache = true;
var WEB_PROTOCOL_RE = /^(?:http|ws)s?:\/\//;
var PORT_RE = /^(.*):(\d*)$/;
var protocols = protoMgr.protocols;

function isRule(str) {
	return util.hasProtocol(str) && !WEB_PROTOCOL_RE.test(str);
}

function detactFileUri(url) {

	return /^(?:[a-z]:(?:\\|\/(?!\/))|\/)/i.test(url) && !util.isRegExp(url) ? 'file://' + url : url;
}

function formatUrl(pattern) {
	var queryString = '';
	var queryIndex = pattern.indexOf('?');
	if (queryIndex != -1) {
		queryString = pattern.substring(queryIndex);
		pattern = pattern.substring(0, queryIndex);
	}
	var index = pattern.indexOf('://');
	return (pattern.indexOf('/', index == -1 ? 0 : index + 3) == -1 ?
			pattern + '/' : pattern) + queryString;
}

function getKey(url) {
	if (url.indexOf('{') == 0) {
		var index = url.lastIndexOf('}');
		return index > 1 && url.substring(1, index);
	}

	return false;
}

function getValue(url) {
	if (url.indexOf('(') == 0) {
		var index = url.lastIndexOf(')');
		return index != -1 ? url.substring(1, index) : url;
	}

	return false;
}

function getPath(url) {
	if (url.indexOf('<') == 0) {
		var index = url.lastIndexOf('>');
		return index != -1 ? url.substring(1, index) : url;
	}

	return false;
}

function getFiles(path) {

	return /^x?((raw)?file|tpl|jsonp|dust):\/\//.test(path) ? util.removeProtocol(path, true).split('|') : null;
}

function setProtocol(target, source) {
	if (util.hasProtocol(target)) {
		return target;
	}

	var protocol = util.getProtocol(source);
	if (protocol == null) {
		return target;
	}

	return protocol + '//' + target;
}

function isPathSeparator(ch) {
	return ch == '/' || ch == '\\' || ch == '?';
}

/**
 * first: xxxx, xxxx?, xxx?xxxx
 * second: ?xxx, xxx?xxxx
 * @param first
 * @param second
 * @returns
 */
function join(first, second) {
	if (!first || !second) {
		return first + second;
	}

	var firstIndex = first.indexOf('?');
	var secondIndex = second.indexOf('?');
	var firstQuery = '';
	var secondQuery = '';

	if (firstIndex != -1) {
		firstQuery = first.substring(firstIndex);
		first = first.substring(0, firstIndex);
	}

	if (secondIndex != -1) {
		secondQuery = second.substring(secondIndex);
		second = second.substring(0, secondIndex);
	}

	var query = firstQuery && secondQuery ? firstQuery + secondQuery.substring(1) : (firstQuery || secondQuery);
	if (second) {
		var lastIndex = first.length - 1;
		var startWithSep = isPathSeparator(second[0]);
		if (isPathSeparator(first[lastIndex])) {
			first = startWithSep ? first.substring(0, lastIndex) + second : first + second;
		} else {
			first = first + (startWithSep ? '' : '/') + second;
		}
	}

	return WEB_PROTOCOL_RE.test(first) ? formatUrl(first + query) : first + query;
}

function getLines(text) {
	if (!text || !(text = text.trim())) {
		return [];
	}

	return text.split(/\n|\r\n|\r/g);
}

function getRule(url, list, index) {
	var rule = _getRule(url, list, index);
	//支持域名匹配
	if (!rule && (rule = _getRule(formatUrl(url).replace(/^((?:http|ws)s?:\/\/[^\/]+):\d*(\/.*)/i, '$1$2'), list), index)
			&& !rule.isDomain) {
		rule = null;
	}
	if (rule) {
		var matcher = rule.matcher;
		var index = matcher.indexOf('://') + 3;
		var protocol = matcher.substring(0, index);
		matcher = matcher.substring(index);
		var key = getKey(matcher);
		if (key) {
			rule.key = key;
		}
		var value = key && values.get(key);
		if (value == null) {
			value = false;
		}
		if (value !== false || (value = getValue(matcher)) !== false) {
			rule.value = protocol + value;
		} else if ((value = getPath(matcher)) !== false) {
			rule.path = protocol + value;
			rule.files = getFiles(rule.path);
		}
	}

	return rule;
}

function _getRule(url, list, index) {
	url = formatUrl(url);
	index = index || 0;
	var _url = url.replace(/[?#].*$/, '');
	for (var i = 0, rule; rule = list[i]; i++) {
		var pattern = rule.pattern;

		if (rule.isRegExp) {
			if (pattern.test(url) && --index < 0) {
				var regExp = {};
				for (var i = 1; i < 10; i++) {
					regExp['$' + i] = RegExp['$' + i] || '';
				}

				function replaceSubMatcher(url) {
					return url.replace(/(^|.)?(\$[1-9])/g,
							function(matched, $1, $2) {
						return $1 == '\\' ? $2 : ($1 || '') + regExp[$2];
					});
				}
				var matcher = rule.matcher;
				var files = getFiles(matcher);
				matcher = setProtocol(replaceSubMatcher(matcher), url);
				var result = extend({
					url: matcher,
					files: files && files.map(function(file) {
						return replaceSubMatcher(file);
					})
				}, rule);
				result.matcher = matcher;
				return result;
			}
		} else if (url.indexOf(pattern = setProtocol(pattern, url)) === 0) {
			var len = pattern.length;
			var matcher = setProtocol(rule.matcher, url);
			var files = getFiles(matcher);
			var hasQuery = pattern.indexOf('?') != -1;
			if ((hasQuery || pattern == _url || isPathSeparator(_url[len]) || isPathSeparator(pattern[len - 1])) && --index < 0) {
				var filePath = url.substring(len);
				if (hasQuery && filePath) {
					filePath = '?' + filePath;
				}
				return extend({
					files: files && files.map(function(file) {
						return join(file, filePath);
					}),
					url: join(matcher, filePath)
				}, rule);
			}
		}
	}
}

function resolveProperties(url, rules) {
	var rule = getRule(url, rules);
	var result = {};
	if (rule) {
		util.getMatcherValue(rule)
				.split('|').forEach(function(action) {
			result[action] = true;
		});
	}

	return result;
}

function parseRule(rules, pattern, matcher, raw, root) {
	var isRegExp;
	var rawPattern = pattern;
	if (!pattern || !matcher ||
			((isRegExp = util.isRegExp(pattern)) && !(pattern = util.toRegExp(pattern)))) {
		return;
	}

	var list, port;
	if (net.isIP(matcher)) {
		list = rules.host;
	} else {
		var protocol = matcher.match(/^([\w\-]+):\/\//);
		protocol = protocol && protocol[1];
		list = rules[protocol] || rules.rule;
		if (protocol == 'host' && PORT_RE.test(matcher)) {
			matcher = RegExp.$1;
			port = RegExp.$2;
		}
	}

	list.push({
		root: root,
		isRegExp: isRegExp,
		protocol: isRegExp ? null : util.getProtocol(pattern),
		pattern: isRegExp ? pattern : formatUrl(pattern),
		matcher: matcher,
		port: port,
		raw: raw,
		isDomain: !isRegExp && util.removeProtocol(rawPattern, true).indexOf('/') == -1,
		rawPattern: rawPattern
	});
}

function parse(rules, text, root, append) {
	!append && protoMgr.resetRules(rules);
	getLines(text).forEach(function(line) {
		var raw = line;
		line = line.replace(/#.*$/, '').trim();
		if (!line) {
			return;
		}

		line = line.split(/\s+/).map(detactFileUri);
		var pattern = line[0];
		if ((net.isIP(pattern) && !isRule(line[1])) || isRule(pattern)
				|| (!util.isRegExp(pattern) && util.isRegExp(line[1]))) {
			//supports: operator-uri pattern1 pattern2 ... patternN
			line.slice(1).forEach(function(matcher) {
				parseRule(rules, matcher, pattern, raw, root);
			});
		} else {
			//supports: pattern operator-uri1 operator-uri2 ... operator-uriN
			line.slice(1).forEach(function(matcher) {
				parseRule(rules, pattern, matcher, raw, root);
			});
		}
	});
}

function Rules(text) {
	this._rules = protoMgr.getRules();
}

var proto = Rules.prototype;

proto.parse = function(text, root) {
	var item = {
			first: true,
			text: text,
			root: root
		};
	this.disabled = !arguments.length;
	if (this._rawText) {
		if (this._rawText[0].first) {
			this._rawText.shift();
		}
		this._rawText.unshift(item);
	} else {
		this._rawText = [item];
	}
	parse(this._rules, text, root);
	if (!this.disabled) {
		for (var i = 1, len = this._rawText.length; i < len; i++) {
			item = this._rawText[i];
			parse(this._rules, item.text, item.root, true);
		}
	}
};

proto.clearAppend = function() {
	if (this._rawText && this._rawText[0].first) {
		var item = this._rawText[0];
		!this.disabled && parse(this._rules, item.text, item.root);
		this._rawText = [item];
	} else {
		this._rawText = null;
	}
};

proto.append = function(text, root) {
	var item = {
			text: text,
			root: root
		};
	if (this._rawText) {
		this._rawText.push(item);
		!this.disabled && parse(this._rules, text, root, true);
	} else {
		this._rawText = [item];
	}
};

proto.resolveHost = function(_url, callback, pluginRules) {
	_url = formatUrl(util.setProtocol(_url));
	var options = url.parse(_url);
	if (!options.hostname || (!protoMgr.isWebsocketProtocol(options.protocol)
			&& !protoMgr.isWebProtocol(options.protocol))) {

		return callback(null, null);
	}

	var filterHost = this.resolveFilter(_url).host || (pluginRules && pluginRules.resolveFilter(_url).host);
	if (!filterHost) {
		var host = getRule(_url, this._rules.host);
		if (host) {
			return callback(null, util.removeProtocol(host.matcher, true), host.port);
		}
		if (pluginRules) {
			return pluginRules.resolveHost(_url, callback);
		}
	}
	lookup(options.hostname, callback, allowDnsCache && !this.resolveDisable(_url).dnsCache);
};

proto.resolveFilter = function(url) {
	var filter = resolveProperties(url, this._rules.filter);
	delete filter.filter;
	return filter;
};
proto.resolveDisable = function(url) {
	return resolveProperties(url, this._rules.disable);
};
proto.resolveRules = function resolveRules(url) {
	url = formatUrl(util.setProtocol(url));

	var rule;
	var rules = this._rules;
	var _rules = {};
	var filter = this.resolveFilter(url);
	protocols.forEach(function(protocol) {
		if ((protocol == 'filter' || !filter[protocol]) && (rule = getRule(url, rules[protocol]))) {
			_rules[protocol] = rule;
		}
	});

	return _rules;
};

proto.resolveRule = function(url, index) {
	if (index = index || 0) {
		index = parseInt(index, 10);
	}
	return getRule(url, this._rules.rule, index);
};

Rules.disableDnsCache = function() {
	allowDnsCache = false;
};

module.exports = Rules;
<commit_msg>fix: getPath, getValue<commit_after>var util = require('../util');
var values = require('./util').values;
var url = require('url');
var net = require('net');
var lookup = require('./dns');
var extend = require('util')._extend;
var protoMgr = require('./protocols');
var allowDnsCache = true;
var WEB_PROTOCOL_RE = /^(?:http|ws)s?:\/\//;
var PORT_RE = /^(.*):(\d*)$/;
var protocols = protoMgr.protocols;

function isRule(str) {
	return util.hasProtocol(str) && !WEB_PROTOCOL_RE.test(str);
}

function detactFileUri(url) {

	return /^(?:[a-z]:(?:\\|\/(?!\/))|\/)/i.test(url) && !util.isRegExp(url) ? 'file://' + url : url;
}

function formatUrl(pattern) {
	var queryString = '';
	var queryIndex = pattern.indexOf('?');
	if (queryIndex != -1) {
		queryString = pattern.substring(queryIndex);
		pattern = pattern.substring(0, queryIndex);
	}
	var index = pattern.indexOf('://');
	return (pattern.indexOf('/', index == -1 ? 0 : index + 3) == -1 ?
			pattern + '/' : pattern) + queryString;
}

function getKey(url) {
	if (url.indexOf('{') == 0) {
		var index = url.lastIndexOf('}');
		return index > 1 && url.substring(1, index);
	}

	return false;
}

function getValue(url) {
	if (url.indexOf('(') == 0) {
		var index = url.lastIndexOf(')');
		return index != -1 ? url.substring(1, index) : url;
	}

	return false;
}

function getPath(url) {
	if (url.indexOf('<') == 0) {
		var index = url.lastIndexOf('>');
		return index != -1 ? url.substring(1, index) : '';
	}

	return false;
}

function getFiles(path) {

	return /^x?((raw)?file|tpl|jsonp|dust):\/\//.test(path) ? util.removeProtocol(path, true).split('|') : null;
}

function setProtocol(target, source) {
	if (util.hasProtocol(target)) {
		return target;
	}

	var protocol = util.getProtocol(source);
	if (protocol == null) {
		return target;
	}

	return protocol + '//' + target;
}

function isPathSeparator(ch) {
	return ch == '/' || ch == '\\' || ch == '?';
}

/**
 * first: xxxx, xxxx?, xxx?xxxx
 * second: ?xxx, xxx?xxxx
 * @param first
 * @param second
 * @returns
 */
function join(first, second) {
	if (!first || !second) {
		return first + second;
	}

	var firstIndex = first.indexOf('?');
	var secondIndex = second.indexOf('?');
	var firstQuery = '';
	var secondQuery = '';

	if (firstIndex != -1) {
		firstQuery = first.substring(firstIndex);
		first = first.substring(0, firstIndex);
	}

	if (secondIndex != -1) {
		secondQuery = second.substring(secondIndex);
		second = second.substring(0, secondIndex);
	}

	var query = firstQuery && secondQuery ? firstQuery + secondQuery.substring(1) : (firstQuery || secondQuery);
	if (second) {
		var lastIndex = first.length - 1;
		var startWithSep = isPathSeparator(second[0]);
		if (isPathSeparator(first[lastIndex])) {
			first = startWithSep ? first.substring(0, lastIndex) + second : first + second;
		} else {
			first = first + (startWithSep ? '' : '/') + second;
		}
	}

	return WEB_PROTOCOL_RE.test(first) ? formatUrl(first + query) : first + query;
}

function getLines(text) {
	if (!text || !(text = text.trim())) {
		return [];
	}

	return text.split(/\n|\r\n|\r/g);
}

function getRule(url, list, index) {
	var rule = _getRule(url, list, index);
	//支持域名匹配
	if (!rule && (rule = _getRule(formatUrl(url).replace(/^((?:http|ws)s?:\/\/[^\/]+):\d*(\/.*)/i, '$1$2'), list), index)
			&& !rule.isDomain) {
		rule = null;
	}
	if (rule) {
		var matcher = rule.matcher;
		var index = matcher.indexOf('://') + 3;
		var protocol = matcher.substring(0, index);
		matcher = matcher.substring(index);
		var key = getKey(matcher);
		if (key) {
			rule.key = key;
		}
		var value = key && values.get(key);
		if (value == null) {
			value = false;
		}
		if (value !== false || (value = getValue(matcher)) !== false) {
			rule.value = protocol + value;
		} else if ((value = getPath(matcher)) !== false) {
			rule.path = protocol + value;
			rule.files = getFiles(rule.path);
		}
	}

	return rule;
}

function _getRule(url, list, index) {
	url = formatUrl(url);
	index = index || 0;
	var _url = url.replace(/[?#].*$/, '');
	for (var i = 0, rule; rule = list[i]; i++) {
		var pattern = rule.pattern;

		if (rule.isRegExp) {
			if (pattern.test(url) && --index < 0) {
				var regExp = {};
				for (var i = 1; i < 10; i++) {
					regExp['$' + i] = RegExp['$' + i] || '';
				}

				function replaceSubMatcher(url) {
					return url.replace(/(^|.)?(\$[1-9])/g,
							function(matched, $1, $2) {
						return $1 == '\\' ? $2 : ($1 || '') + regExp[$2];
					});
				}
				var matcher = rule.matcher;
				var files = getFiles(matcher);
				matcher = setProtocol(replaceSubMatcher(matcher), url);
				var result = extend({
					url: matcher,
					files: files && files.map(function(file) {
						return replaceSubMatcher(file);
					})
				}, rule);
				result.matcher = matcher;
				return result;
			}
		} else if (url.indexOf(pattern = setProtocol(pattern, url)) === 0) {
			var len = pattern.length;
			var matcher = setProtocol(rule.matcher, url);
			var files = getFiles(matcher);
			var hasQuery = pattern.indexOf('?') != -1;
			if ((hasQuery || pattern == _url || isPathSeparator(_url[len]) || isPathSeparator(pattern[len - 1])) && --index < 0) {
				var filePath = url.substring(len);
				if (hasQuery && filePath) {
					filePath = '?' + filePath;
				}
				return extend({
					files: files && files.map(function(file) {
						return join(file, filePath);
					}),
					url: join(matcher, filePath)
				}, rule);
			}
		}
	}
}

function resolveProperties(url, rules) {
	var rule = getRule(url, rules);
	var result = {};
	if (rule) {
		util.getMatcherValue(rule)
				.split('|').forEach(function(action) {
			result[action] = true;
		});
	}

	return result;
}

function parseRule(rules, pattern, matcher, raw, root) {
	var isRegExp;
	var rawPattern = pattern;
	if (!pattern || !matcher ||
			((isRegExp = util.isRegExp(pattern)) && !(pattern = util.toRegExp(pattern)))) {
		return;
	}

	var list, port;
	if (net.isIP(matcher)) {
		list = rules.host;
	} else {
		var protocol = matcher.match(/^([\w\-]+):\/\//);
		protocol = protocol && protocol[1];
		list = rules[protocol] || rules.rule;
		if (protocol == 'host' && PORT_RE.test(matcher)) {
			matcher = RegExp.$1;
			port = RegExp.$2;
		}
	}

	list.push({
		root: root,
		isRegExp: isRegExp,
		protocol: isRegExp ? null : util.getProtocol(pattern),
		pattern: isRegExp ? pattern : formatUrl(pattern),
		matcher: matcher,
		port: port,
		raw: raw,
		isDomain: !isRegExp && util.removeProtocol(rawPattern, true).indexOf('/') == -1,
		rawPattern: rawPattern
	});
}

function parse(rules, text, root, append) {
	!append && protoMgr.resetRules(rules);
	getLines(text).forEach(function(line) {
		var raw = line;
		line = line.replace(/#.*$/, '').trim();
		if (!line) {
			return;
		}

		line = line.split(/\s+/).map(detactFileUri);
		var pattern = line[0];
		if ((net.isIP(pattern) && !isRule(line[1])) || isRule(pattern)
				|| (!util.isRegExp(pattern) && util.isRegExp(line[1]))) {
			//supports: operator-uri pattern1 pattern2 ... patternN
			line.slice(1).forEach(function(matcher) {
				parseRule(rules, matcher, pattern, raw, root);
			});
		} else {
			//supports: pattern operator-uri1 operator-uri2 ... operator-uriN
			line.slice(1).forEach(function(matcher) {
				parseRule(rules, pattern, matcher, raw, root);
			});
		}
	});
}

function Rules(text) {
	this._rules = protoMgr.getRules();
}

var proto = Rules.prototype;

proto.parse = function(text, root) {
	var item = {
			first: true,
			text: text,
			root: root
		};
	this.disabled = !arguments.length;
	if (this._rawText) {
		if (this._rawText[0].first) {
			this._rawText.shift();
		}
		this._rawText.unshift(item);
	} else {
		this._rawText = [item];
	}
	parse(this._rules, text, root);
	if (!this.disabled) {
		for (var i = 1, len = this._rawText.length; i < len; i++) {
			item = this._rawText[i];
			parse(this._rules, item.text, item.root, true);
		}
	}
};

proto.clearAppend = function() {
	if (this._rawText && this._rawText[0].first) {
		var item = this._rawText[0];
		!this.disabled && parse(this._rules, item.text, item.root);
		this._rawText = [item];
	} else {
		this._rawText = null;
	}
};

proto.append = function(text, root) {
	var item = {
			text: text,
			root: root
		};
	if (this._rawText) {
		this._rawText.push(item);
		!this.disabled && parse(this._rules, text, root, true);
	} else {
		this._rawText = [item];
	}
};

proto.resolveHost = function(_url, callback, pluginRules) {
	_url = formatUrl(util.setProtocol(_url));
	var options = url.parse(_url);
	if (!options.hostname || (!protoMgr.isWebsocketProtocol(options.protocol)
			&& !protoMgr.isWebProtocol(options.protocol))) {

		return callback(null, null);
	}

	var filterHost = this.resolveFilter(_url).host || (pluginRules && pluginRules.resolveFilter(_url).host);
	if (!filterHost) {
		var host = getRule(_url, this._rules.host);
		if (host) {
			return callback(null, util.removeProtocol(host.matcher, true), host.port);
		}
		if (pluginRules) {
			return pluginRules.resolveHost(_url, callback);
		}
	}
	lookup(options.hostname, callback, allowDnsCache && !this.resolveDisable(_url).dnsCache);
};

proto.resolveFilter = function(url) {
	var filter = resolveProperties(url, this._rules.filter);
	delete filter.filter;
	return filter;
};
proto.resolveDisable = function(url) {
	return resolveProperties(url, this._rules.disable);
};
proto.resolveRules = function resolveRules(url) {
	url = formatUrl(util.setProtocol(url));

	var rule;
	var rules = this._rules;
	var _rules = {};
	var filter = this.resolveFilter(url);
	protocols.forEach(function(protocol) {
		if ((protocol == 'filter' || !filter[protocol]) && (rule = getRule(url, rules[protocol]))) {
			_rules[protocol] = rule;
		}
	});

	return _rules;
};

proto.resolveRule = function(url, index) {
	if (index = index || 0) {
		index = parseInt(index, 10);
	}
	return getRule(url, this._rules.rule, index);
};

Rules.disableDnsCache = function() {
	allowDnsCache = false;
};

module.exports = Rules;
<|endoftext|>