<commit_before>define(["dojo/_base/declare", "ppwcode/contracts/_Mixin", "dojo/_base/lang", "dijit/registry", "dojo/dom-style", "dojo/dom-class", "dojo/has",
        "dijit/_WidgetBase", "../SemanticObject",
        "ppwcode/contracts/doh"], // MUDO REMOVE temp for testing invariants in the field
    function(declare, _ContractMixin, lang, registry, domStyle, domClass, has,
             _WidgetBase, SemanticObject,
             doh) {

      var editModes = [
        // editMode for when there is no target
        "NOTARGET",

        // editMode for viewing the data. No interaction allowed.
        "VIEW",

        // editMode for editing the data. Interaction allowed.
        "EDIT",

        // editMode while the SemanticObject is busy. No interaction allowed.
        "BUSY",

        // editMode representing unacceptable data (the object is not civilized,
        // or we have other means of knowing the data is unacceptable).
        // Interaction must be allowed to reset the object, or ammeliorate the data/
        // This means this is a sub-state of EDIT.
        "WILD",

        // editMode representing an error occured. We cannot proceed. The widget
        // and its object must be destroyed.
        "ERROR"];

      function setEditModeOnWidgets(domNode, editMode) {
        if (has("dojo-debug-messages")) {
          // make ERROR and WILD mode very clear in debug mode
          if (editMode === _EditableSemanticObjectDetail.prototype.NOTARGET) {
            domStyle.set(domNode, "backgroundColor", "gray");
          }
          else if (editMode === _EditableSemanticObjectDetail.prototype.ERROR) {
            domStyle.set(domNode, "backgroundColor", "red");
          }
          else if (editMode === _EditableSemanticObjectDetail.prototype.WILD) {
            domStyle.set(domNode, "backgroundColor", "yellow");
          }
          else {
            domStyle.set(domNode, "backgroundColor", "transparent");
          }
        }
        var innerWidgets = registry.findWidgets(domNode);
        var widgetState = null;
        switch (editMode) {
          case _EditableSemanticObjectDetail.prototype.NOTARGET:
            widgetState = { readOnly:false, disabled:true };
            break;
          case _EditableSemanticObjectDetail.prototype.EDIT:
            widgetState = { readOnly:false, disabled:false };
            break;
          case _EditableSemanticObjectDetail.prototype.BUSY:
            widgetState = { readOnly:false, disabled:true };
            break;
          case _EditableSemanticObjectDetail.prototype.ERROR:
            widgetState = { readOnly:false, disabled:true };
            break;
          default:
            widgetState = { readOnly:true, disabled:false };
        }
        innerWidgets.forEach(function (w) {
          if (!w.isInstanceOf(_EditableSemanticObjectDetail)) {
            w.set("readOnly", widgetState.readOnly);
            w.set("disabled", widgetState.disabled);
          }
        });
      }

      function setEditModeOnBlock(domNode, editMode) {
        if (editMode === _EditableSemanticObjectDetail.prototype.VIEW ||
            editMode === _EditableSemanticObjectDetail.prototype.EDIT ||
            editMode === _EditableSemanticObjectDetail.prototype.WILD) {
          domClass.replace(domNode,
            "editableSemanticObjectDetail_blockEnabled",
            "editableSemanticObjectDetail_blockDisabled editableSemanticObjectDetail_blockInvisible"
          );
        }
        else if (editMode === _EditableSemanticObjectDetail.prototype.BUSY ||
                 editMode === _EditableSemanticObjectDetail.prototype.ERROR) {
          domClass.replace(domNode,
            "editableSemanticObjectDetail_blockDisabled",
            "editableSemanticObjectDetail_blockEnabled editableSemanticObjectDetail_blockInvisible"
          );
        }
        else { // no target
          domClass.replace(domNode,
            "editableSemanticObjectDetail_blockInvisible",
            "editableSemanticObjectDetail_blockEnabled editableSemanticObjectDetail_blockDisabled"
          );
        }
      }

      var _EditableSemanticObjectDetail = declare([_WidgetBase, _ContractMixin], {
        // summary:
        //   Widget that represents a SemanticObject in detail, and that gives the opportunity
        //   to the user the view the details, edit the details, and create a new object.
        //   Abstract class to be extended for particular subtypes of SemanticObject.
        // description:
        //   What is common to all subtypes is that the widget:
        //   - has a `target` that is a SemanticObject; getTargetType() returns the top-most supported
        //     Constructor of supported targets. All targets must be instances of this type.
        //   - has an `editMode` (view, edit, busy, wild, error)
        //
        //   The representation should reflect the editMode in a clear visual way to the user.
        //   Even when the information shown is completely read-only, widgets should extend
        //   this class, to change the representation of the displayed information consistently
        //   with other information in different editModes, e.g., in "busy" mode.
        //   The available edit modes are defined in _EditableSemanticObjectDetail.prototype.editModes.
        //
        //   Instances can be wrapped around zero or more _EditableSemanticObjectDetails, recursively (no loops!).
        //   We take care of propagating `editMode`. Subclasses need to override
        //   getWrappedDetails to return the wrapped details. All instances need to be instances of this class.
        //   The default implementation returns an empty array.
        //   The target of a wrapped detail might be the same target as our target, but it might be
        //   another related object, of a different type. set("target", ...) will call _propagateTarget, which is
        //   chained, so that subclasses can add propagation of setting the target. Our implementation only sets our
        //   target.

        _c_invar: [
          function() {return this.get("editMode");},
          function() {return _EditableSemanticObjectDetail.prototype.editModes.indexOf(this.get("editMode")) >= 0;},
          function() {return this.getTargetType() != null;},
          // TODO getTargetType is a subtype of SemanticObject
          function() {return this.get("target") == null ||
                        (this.get("target").isInstanceOf && this.get("target").isInstanceOf(this.getTargetType()));},
          function() {return (this.get("editMode") === this.NOTARGET) === (this.get("target") === null);},
          function() {return this._wrappedDetails() != null;},
          function() {return lang.isArray(this._wrappedDetails());},
          {
            objectSelector: function() {
              return this._wrappedDetails();
            },
            invars: [
              function(wd) {
                return wd && wd != null &&
                  wd.isInstanceOf && wd.isInstanceOf(_EditableSemanticObjectDetail);
              },
              // wrapped details might contain other objects of other types as target,
              // but the editMode needs to be in sync
              function(wd) {
                return wd.get("editMode") === this.get("editMode");
              }
            ]
          }
        ],

        "-chains-": {
          _propagateTarget: "after",
          _localEditModeChange: "after"
        },

        // target: SemanticObject
        //    SemanticObject that is represented.
        //    Access in declarative template with  ... data-dojo-props="value: at('rel:', BINDING_PROPERTY_NAME)
        target: null,

        // editMode: String
        //    The edit mode is either
        editMode: editModes[0], // default value

        isInEditMode: function() {
          return this.editMode === this.EDIT || this.editMode === this.BUSY || this.editMode === this.WILD;
        },

        _wrappedDetails: function() {
          // summary:
          //   Array containing the wrapped details, a subclass wants the target and editMode propagated to.
          // tags:
          //   protected

          return []; // return Array
        },

        getTargetType: function() {
          // summary:
          //    The supported type of SemanticObjects. All targets must be an instance of this type.
          // description:
          //    Subclasses should overwrite to return the correct type.

          return SemanticObject;
        },

        _propagateTarget: function(/*SemanticObject*/ so) {
          // summary:
          //   Propagate the target as appropriate to wrapped details.
          //   Chained. Subtypes could add propagation to wrapped details.
          // tags:
          //   protected
          // description:
          //   Does nothing in _EditableSemanticObjectDetail

          this._c_NOP();
        },

        _setTargetAttr: function(so) {
          // summary:
          //    Sets the target of this instance. Chained. Subtypes could add propagation to wrapped details.
          this._c_pre(function() {return so == null || (so.isInstanceOf && so.isInstanceOf(this.getTargetType()));});

          if (so !== this.get("target")) {
            this._set("target", so);
          }
          this._propagateTarget(so);
          this.set("editMode", so ? this.VIEW : this.NOTARGET);

          doh.validateInvariants(this); // MUDO REMOVE
        },

        _setEditModeAttr: function(value) {
          // summary:
          //    Set the editMode, and propagate to the wrapped details.
          //    Also makes all innerWidgets read-only in editMode VIEW,
          //    disabled in editMode BUSY, and not-read-only and enabled
          //    in the other editModes.

          // Called during create by _WidgetBase with default value automatically
          this._c_pre(function() {return value != null});
          this._c_pre(function() {return _EditableSemanticObjectDetail.prototype.editModes.indexOf(value) >= 0});

          this._set("editMode", value);
          this._wrappedDetails().forEach(function(wd) {
            wd.set("editMode", value);
          });
          setEditModeOnBlock(this.get("domNode"), value);
          setEditModeOnWidgets(this.get("domNode"), value);
          this._localEditModeChange(value);
          doh.validateInvariants(this); // MUDO REMOVE
        },

        _localEditModeChange: function(/*String*/ editMode) {
          // summary:
          //   Make changes to representation to represent the
          //   new edit mode. Chained. Overwrite when needed.
          // tags:
          //   protected
          // description:
          //   Does nothing in _EditableSemanticObjectDetail

          this._c_NOP();
        }

      });

      // All supported editModes
      _EditableSemanticObjectDetail.prototype.editModes = editModes;
      editModes.forEach(function(em) {
        _EditableSemanticObjectDetail.prototype[em] = em;
      });

      return _EditableSemanticObjectDetail; // return _EditableSemanticObjectDetail

    });
<commit_msg>Debug style for "ok editMode" better now.<commit_after>define(["dojo/_base/declare", "ppwcode/contracts/_Mixin", "dojo/_base/lang", "dijit/registry", "dojo/dom-style", "dojo/dom-class", "dojo/has",
        "dijit/_WidgetBase", "../SemanticObject",
        "ppwcode/contracts/doh"], // MUDO REMOVE temp for testing invariants in the field
    function(declare, _ContractMixin, lang, registry, domStyle, domClass, has,
             _WidgetBase, SemanticObject,
             doh) {

      var editModes = [
        // editMode for when there is no target
        "NOTARGET",

        // editMode for viewing the data. No interaction allowed.
        "VIEW",

        // editMode for editing the data. Interaction allowed.
        "EDIT",

        // editMode while the SemanticObject is busy. No interaction allowed.
        "BUSY",

        // editMode representing unacceptable data (the object is not civilized,
        // or we have other means of knowing the data is unacceptable).
        // Interaction must be allowed to reset the object, or ammeliorate the data/
        // This means this is a sub-state of EDIT.
        "WILD",

        // editMode representing an error occured. We cannot proceed. The widget
        // and its object must be destroyed.
        "ERROR"];

      function setEditModeOnWidgets(domNode, editMode) {
        if (has("dojo-debug-messages")) {
          // make ERROR and WILD mode very clear in debug mode
          if (editMode === _EditableSemanticObjectDetail.prototype.NOTARGET) {
            domStyle.set(domNode, "backgroundColor", "gray");
          }
          else if (editMode === _EditableSemanticObjectDetail.prototype.ERROR) {
            domStyle.set(domNode, "backgroundColor", "red");
          }
          else if (editMode === _EditableSemanticObjectDetail.prototype.WILD) {
            domStyle.set(domNode, "backgroundColor", "yellow");
          }
          else {
            domStyle.set(domNode, "backgroundColor", "");
          }
        }
        var innerWidgets = registry.findWidgets(domNode);
        var widgetState = null;
        switch (editMode) {
          case _EditableSemanticObjectDetail.prototype.NOTARGET:
            widgetState = { readOnly:false, disabled:true };
            break;
          case _EditableSemanticObjectDetail.prototype.EDIT:
            widgetState = { readOnly:false, disabled:false };
            break;
          case _EditableSemanticObjectDetail.prototype.BUSY:
            widgetState = { readOnly:false, disabled:true };
            break;
          case _EditableSemanticObjectDetail.prototype.ERROR:
            widgetState = { readOnly:false, disabled:true };
            break;
          default:
            widgetState = { readOnly:true, disabled:false };
        }
        innerWidgets.forEach(function (w) {
          if (!w.isInstanceOf(_EditableSemanticObjectDetail)) {
            w.set("readOnly", widgetState.readOnly);
            w.set("disabled", widgetState.disabled);
          }
        });
      }

      function setEditModeOnBlock(domNode, editMode) {
        if (editMode === _EditableSemanticObjectDetail.prototype.VIEW ||
            editMode === _EditableSemanticObjectDetail.prototype.EDIT ||
            editMode === _EditableSemanticObjectDetail.prototype.WILD) {
          domClass.replace(domNode,
            "editableSemanticObjectDetail_blockEnabled",
            "editableSemanticObjectDetail_blockDisabled editableSemanticObjectDetail_blockInvisible"
          );
        }
        else if (editMode === _EditableSemanticObjectDetail.prototype.BUSY ||
                 editMode === _EditableSemanticObjectDetail.prototype.ERROR) {
          domClass.replace(domNode,
            "editableSemanticObjectDetail_blockDisabled",
            "editableSemanticObjectDetail_blockEnabled editableSemanticObjectDetail_blockInvisible"
          );
        }
        else { // no target
          domClass.replace(domNode,
            "editableSemanticObjectDetail_blockInvisible",
            "editableSemanticObjectDetail_blockEnabled editableSemanticObjectDetail_blockDisabled"
          );
        }
      }

      var _EditableSemanticObjectDetail = declare([_WidgetBase, _ContractMixin], {
        // summary:
        //   Widget that represents a SemanticObject in detail, and that gives the opportunity
        //   to the user the view the details, edit the details, and create a new object.
        //   Abstract class to be extended for particular subtypes of SemanticObject.
        // description:
        //   What is common to all subtypes is that the widget:
        //   - has a `target` that is a SemanticObject; getTargetType() returns the top-most supported
        //     Constructor of supported targets. All targets must be instances of this type.
        //   - has an `editMode` (view, edit, busy, wild, error)
        //
        //   The representation should reflect the editMode in a clear visual way to the user.
        //   Even when the information shown is completely read-only, widgets should extend
        //   this class, to change the representation of the displayed information consistently
        //   with other information in different editModes, e.g., in "busy" mode.
        //   The available edit modes are defined in _EditableSemanticObjectDetail.prototype.editModes.
        //
        //   Instances can be wrapped around zero or more _EditableSemanticObjectDetails, recursively (no loops!).
        //   We take care of propagating `editMode`. Subclasses need to override
        //   getWrappedDetails to return the wrapped details. All instances need to be instances of this class.
        //   The default implementation returns an empty array.
        //   The target of a wrapped detail might be the same target as our target, but it might be
        //   another related object, of a different type. set("target", ...) will call _propagateTarget, which is
        //   chained, so that subclasses can add propagation of setting the target. Our implementation only sets our
        //   target.

        _c_invar: [
          function() {return this.get("editMode");},
          function() {return _EditableSemanticObjectDetail.prototype.editModes.indexOf(this.get("editMode")) >= 0;},
          function() {return this.getTargetType() != null;},
          // TODO getTargetType is a subtype of SemanticObject
          function() {return this.get("target") == null ||
                        (this.get("target").isInstanceOf && this.get("target").isInstanceOf(this.getTargetType()));},
          function() {return (this.get("editMode") === this.NOTARGET) === (this.get("target") === null);},
          function() {return this._wrappedDetails() != null;},
          function() {return lang.isArray(this._wrappedDetails());},
          {
            objectSelector: function() {
              return this._wrappedDetails();
            },
            invars: [
              function(wd) {
                return wd && wd != null &&
                  wd.isInstanceOf && wd.isInstanceOf(_EditableSemanticObjectDetail);
              },
              // wrapped details might contain other objects of other types as target,
              // but the editMode needs to be in sync
              function(wd) {
                return wd.get("editMode") === this.get("editMode");
              }
            ]
          }
        ],

        "-chains-": {
          _propagateTarget: "after",
          _localEditModeChange: "after"
        },

        // target: SemanticObject
        //    SemanticObject that is represented.
        //    Access in declarative template with  ... data-dojo-props="value: at('rel:', BINDING_PROPERTY_NAME)
        target: null,

        // editMode: String
        //    The edit mode is either
        editMode: editModes[0], // default value

        isInEditMode: function() {
          return this.editMode === this.EDIT || this.editMode === this.BUSY || this.editMode === this.WILD;
        },

        _wrappedDetails: function() {
          // summary:
          //   Array containing the wrapped details, a subclass wants the target and editMode propagated to.
          // tags:
          //   protected

          return []; // return Array
        },

        getTargetType: function() {
          // summary:
          //    The supported type of SemanticObjects. All targets must be an instance of this type.
          // description:
          //    Subclasses should overwrite to return the correct type.

          return SemanticObject;
        },

        _propagateTarget: function(/*SemanticObject*/ so) {
          // summary:
          //   Propagate the target as appropriate to wrapped details.
          //   Chained. Subtypes could add propagation to wrapped details.
          // tags:
          //   protected
          // description:
          //   Does nothing in _EditableSemanticObjectDetail

          this._c_NOP();
        },

        _setTargetAttr: function(so) {
          // summary:
          //    Sets the target of this instance. Chained. Subtypes could add propagation to wrapped details.
          this._c_pre(function() {return so == null || (so.isInstanceOf && so.isInstanceOf(this.getTargetType()));});

          if (so !== this.get("target")) {
            this._set("target", so);
          }
          this._propagateTarget(so);
          this.set("editMode", so ? this.VIEW : this.NOTARGET);

          doh.validateInvariants(this); // MUDO REMOVE
        },

        _setEditModeAttr: function(value) {
          // summary:
          //    Set the editMode, and propagate to the wrapped details.
          //    Also makes all innerWidgets read-only in editMode VIEW,
          //    disabled in editMode BUSY, and not-read-only and enabled
          //    in the other editModes.

          // Called during create by _WidgetBase with default value automatically
          this._c_pre(function() {return value != null});
          this._c_pre(function() {return _EditableSemanticObjectDetail.prototype.editModes.indexOf(value) >= 0});

          this._set("editMode", value);
          this._wrappedDetails().forEach(function(wd) {
            wd.set("editMode", value);
          });
          setEditModeOnBlock(this.get("domNode"), value);
          setEditModeOnWidgets(this.get("domNode"), value);
          this._localEditModeChange(value);
          doh.validateInvariants(this); // MUDO REMOVE
        },

        _localEditModeChange: function(/*String*/ editMode) {
          // summary:
          //   Make changes to representation to represent the
          //   new edit mode. Chained. Overwrite when needed.
          // tags:
          //   protected
          // description:
          //   Does nothing in _EditableSemanticObjectDetail

          this._c_NOP();
        }

      });

      // All supported editModes
      _EditableSemanticObjectDetail.prototype.editModes = editModes;
      editModes.forEach(function(em) {
        _EditableSemanticObjectDetail.prototype[em] = em;
      });

      return _EditableSemanticObjectDetail; // return _EditableSemanticObjectDetail

    });
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import { Grid, Row, Col } from 'react-flexbox-grid'
import FontIcon from 'material-ui/FontIcon'
import { Link } from 'react-router'
import { Card, CardHeader, CardText } from 'material-ui/Card'
import SelectField from '../SelectField/SelectField'
import TextField from 'material-ui/TextField';
import MenuItem from 'material-ui/MenuItem'
import { Table, Column, Cell } from 'fixed-data-table-2';
import AllocationStatusIcon from '../AllocationStatusIcon/AllocationStatusIcon'
import AllocationLink from '../AllocationLink/AllocationLink'
import JobLink from '../JobLink/JobLink'
import ClientLink from '../ClientLink/ClientLink'
import FormatTime from '../FormatTime/FormatTime'

const nodeIdToNameCache = {}
const allocIdRegexp = /\[(\d+)\]/

const getAllocationNumberFromName = (allocationName) => {
  const match = allocIdRegexp.exec(allocationName)
  return match[1]
}

/* eslint-disable react/prop-types */

const AllocationStatusIconCell = ({ rowIndex, data, ...props }) => (
  <Cell { ...props }>
    <AllocationStatusIcon allocation={ data[rowIndex] } />
  </Cell>
);

const AllocationLinkCell = ({ rowIndex, data, ...props }) => (
  <Cell { ...props }>
    <AllocationLink allocationId={ data[rowIndex].ID } />
  </Cell>
);

const JobLinkCell = ({ rowIndex, data, ...props }) => (
  <Cell { ...props }>
    <JobLink jobId={ data[rowIndex].JobID } />
  </Cell>
);

const JobTaskGroupLinkCell = ({ rowIndex, data, ...props }) => (
  <Cell { ...props }>
    <JobLink jobId={ data[rowIndex].JobID } taskGroupId={ data[rowIndex].TaskGroupId }>
      { data[rowIndex].TaskGroup } (#{ getAllocationNumberFromName(data[rowIndex].Name) })
    </JobLink>
  </Cell>
);

const ClientLinkCell = ({ rowIndex, data, clients, ...props }) => (
  <Cell { ...props }>
    <ClientLink clientId={ data[rowIndex].NodeID } clients={ clients } short />
  </Cell>
);

const AgeCell = ({ rowIndex, data, ...props }) => (
  <Cell { ...props }>
    <FormatTime identifier={ data[rowIndex].ID } time={ data[rowIndex].CreateTime } />
  </Cell>
);

const StatusCell = ({ rowIndex, data, ...props }) => (
  <Cell { ...props }>
    { data[rowIndex].ClientStatus }
  </Cell>
);

const ActionsCell = ({ rowIndex, data, ...props }) => (
  <Cell { ...props }>
    <AllocationLink allocationId={ data[rowIndex].ID } linkAppend='/files' linkQuery={{ path: '/alloc/logs/' }}>
      <FontIcon className='material-icons'>format_align_left</FontIcon>
    </AllocationLink>
  </Cell>
);

/* eslint-disable react/prop-types */

const jobColumn = (allocations, display) =>
  (display
    ?
      <Column
        header={ <Cell>Job</Cell> }
        cell={ <JobLinkCell data={ allocations } /> }
        flexGrow={ 2 }
        width={ 200 }
      />
    : null
  )

const clientColumn = (allocations, display, clients) =>
  (display
    ?
      <Column
        header={ <Cell>Client</Cell> }
        cell={ <ClientLinkCell data={ allocations } clients={ clients } /> }
        width={ 200 }
      />
    : null
  )

class AllocationList extends Component {

  constructor (props) {
    super(props)

    this.resizeHandler = this.updateDimensions.bind(this)
  }

  findNodeNameById (nodeId) {
    if (this.props.nodes.length === 0) {
      return nodeId
    }

    if (nodeId in nodeIdToNameCache) {
      return nodeIdToNameCache[nodeId]
    }

    const r = Object.keys(this.props.nodes).filter(node => this.props.nodes[node].ID === nodeId)

    if (r.length !== 0) {
      nodeIdToNameCache[nodeId] = this.props.nodes[r].Name
    } else {
      nodeIdToNameCache[nodeId] = nodeId
    }

    return nodeIdToNameCache[nodeId]
  }

  filteredAllocations () {
    const query = this.props.location.query || {}
    let allocations = this.props.allocations

    if ('allocation_id' in query) {
      allocations = allocations.filter(allocation => allocation.ID.indexOf(query.allocation_id) != -1)
    }

    if ('allocation_id' in this.state) {
      allocations = allocations.filter(allocation => allocation.ID.indexOf(this.state.allocation_id) != -1)
    }

    if ('status' in query) {
      allocations = allocations.filter(allocation => allocation.ClientStatus === query.status)
    }

    if ('client' in query) {
      allocations = allocations.filter(allocation => allocation.NodeID === query.client)
    }

    if ('job' in query) {
      allocations = allocations.filter(allocation => allocation.JobID === query.job)
    }

    return allocations
  }

  clientStatusFilter () {
    const location = this.props.location
    const query = this.props.location.query || {}

    let title = 'Client Status'
    if ('status' in query) {
      title = {title}: { query.status }
    }

    return (
      <Col key='client-status-filter-pane' xs={ 12 } sm={ 6 } md={ 6 } lg={ 3 }>
        <SelectField floatingLabelText={ title } maxHeight={ 200 }>
          <MenuItem>
            <Link to={{ pathname: location.pathname, query: { ...query, status: undefined } }}>- Any -</Link>
          </MenuItem>
          <MenuItem>
            <Link to={{ pathname: location.pathname, query: { ...query, status: 'running' } }}>Running</Link>
          </MenuItem>
          <MenuItem>
            <Link to={{ pathname: location.pathname, query: { ...query, status: 'complete' } }}>Complete</Link>
          </MenuItem>
          <MenuItem>
            <Link to={{ pathname: location.pathname, query: { ...query, status: 'pending' } }}>Pending</Link>
          </MenuItem>
          <MenuItem>
            <Link to={{ pathname: location.pathname, query: { ...query, status: 'lost' } }}>Lost</Link>
          </MenuItem>
          <MenuItem>
            <Link to={{ pathname: location.pathname, query: { ...query, status: 'failed' } }}>Failed</Link>
          </MenuItem>
        </SelectField>
      </Col>
    )
  }

  allocationIdFilter () {
    return (
      <Col key='allocation-id-filter-pane' xs={ 12 } sm={ 6 } md={ 6 } lg={ 3 }>
        <TextField
          hintText='Allocation ID'
          onChange={ (proxy, value) => { this.setState({ ...this.state, allocation_id: value }) } }
        />
      </Col>
    )
  }

  jobIdFilter () {
    const location = this.props.location
    const query = this.props.location.query || {}

    let title = 'Job'
    if ('job' in query) {
      title = {title}: { query.job }
    }

    const jobs = this.props.allocations
          .map((allocation) => {
            return allocation.JobID
          })
          .filter((v, i, a) => {
            return a.indexOf(v) === i
          })
          .map((job) => {
            return (
              <MenuItem key={ job }>
                <Link to={{ pathname: location.pathname, query: { ...query, job } }}>{ job }</Link>
              </MenuItem>
            )
          })

    jobs.unshift(
      <MenuItem key='any-job'>
        <Link to={{ pathname: location.pathname, query: { ...query, job: undefined } }}>- Any -</Link>
      </MenuItem>
    )

    return (
      <Col key='job-filter-pane' xs={ 12 } sm={ 6 } md={ 6 } lg={ 3 }>
        <SelectField floatingLabelText={ title } maxHeight={ 200 }>{ jobs }</SelectField>
      </Col>
    )
  }

  clientFilter () {
    const location = this.props.location
    const query = this.props.location.query || {}

    let title = 'Client'

    if ('client' in query) {
      title = {title}: { this.findNodeNameById(query.client) }
    }

    const clients = this.props.allocations
          .map((allocation) => {
            return allocation.NodeID
          })
          .filter((v, i, a) => {
            return a.indexOf(v) === i
          })
          .map((client) => {
            return (
              <MenuItem key={ client }>
                <Link to={{ pathname: location.pathname, query: { ...query, client } }}>
                  { this.findNodeNameById(client) }
                </Link>
              </MenuItem>
            )
          })

    clients.unshift(
      <MenuItem key='any-client'>
        <Link to={{ pathname: location.pathname, query: { ...query, client: undefined } }}>- Any -</Link>
      </MenuItem>
    )

    return (
      <Col key='client-filter-pane' xs={ 12 } sm={ 6 } md={ 6 } lg={ 3 }>
        <SelectField floatingLabelText={ title } maxHeight={ 200 }>
          { clients }
        </SelectField>
      </Col>
    )
  }

  updateDimensions() {
    this.setState({
      width: window.innerWidth,
      height: window.innerHeight
    });
  }

  componentWillMount() {
    this.updateDimensions();
  }

  componentDidMount() {
    window.addEventListener("resize", this.resizeHandler);
  }

  componentWillUnmount() {
    window.removeEventListener("resize", this.resizeHandler);
  }

  render () {
    const showJobColumn = this.props.showJobColumn
    const showClientColumn = this.props.showClientColumn
    const allocations = this.filteredAllocations();

    const width = this.state.width - 30
    let height = this.state.height - 240

    if (!showJobColumn || !showClientColumn || this.props.nested) {
      height = height - 120
    }

    if (height < 300) {
      height = 300
    }

    return (

        <Card>
          <CardHeader title='Filter list' actAsExpander showExpandableButton />
          <CardText expandable>
            <Grid fluid style={{ padding: 0 }}>
              <Row>
                { this.allocationIdFilter() }
                { showClientColumn ? this.clientFilter() : null }
                { this.clientStatusFilter() }
                { showJobColumn ? this.jobIdFilter() : null }
              </Row>
            </Grid>
          </CardText>
        </Card>

        <Card style={{ marginTop: '1rem' }}>
          <CardText>

              <Column
                header={ <Cell /> }
                cell={ <AllocationStatusIconCell data={ allocations } /> }
                width={ 40 }
              />
              <Column
                header={ <Cell>ID</Cell> }
                cell={ <AllocationLinkCell data={ allocations } /> }
                width={ 100 }
              />
              { jobColumn(allocations, this.props.showJobColumn) }
              <Column
                header={ <Cell>Task Group</Cell> }
                cell={ <JobTaskGroupLinkCell data={ allocations } /> }
                flexGrow={ 2 }
                width={ 200 }
              />
              <Column
                header={ <Cell>Status</Cell> }
                cell={ <StatusCell data={ allocations } /> }
                width={ 200 }
              />
              { clientColumn(allocations, this.props.showClientColumn, this.props.nodes) }
              <Column
                header={ <Cell>Age</Cell> }
                cell={ <AgeCell data={ allocations } /> }
                width={ 100 }
              />
              <Column
                header={ <Cell>Actions</Cell> }
                cell={ <ActionsCell data={ allocations } /> }
                width={ 100 }
              />

          </CardText>
        </Card>

)
  }
}

AllocationList.defaultProps = {
  allocations: [],
  nodes: [],
  location: {},

  showJobColumn: true,
  showClientColumn: true,
  nested: false
}

AllocationList.propTypes = {
  allocations: PropTypes.array.isRequired,
  nodes: PropTypes.array.isRequired,
  location: PropTypes.object.isRequired,

  nested: PropTypes.bool.isRequired,

  showJobColumn: PropTypes.bool.isRequired,
  showClientColumn: PropTypes.bool.isRequired
}

export default AllocationList
<commit_msg>allocation: use "Allocation Status" instead of "Client Status"<commit_after>import React, { Component, PropTypes } from 'react'
import { Grid, Row, Col } from 'react-flexbox-grid'
import FontIcon from 'material-ui/FontIcon'
import { Link } from 'react-router'
import { Card, CardHeader, CardText } from 'material-ui/Card'
import SelectField from '../SelectField/SelectField'
import TextField from 'material-ui/TextField';
import MenuItem from 'material-ui/MenuItem'
import { Table, Column, Cell } from 'fixed-data-table-2';
import AllocationStatusIcon from '../AllocationStatusIcon/AllocationStatusIcon'
import AllocationLink from '../AllocationLink/AllocationLink'
import JobLink from '../JobLink/JobLink'
import ClientLink from '../ClientLink/ClientLink'
import FormatTime from '../FormatTime/FormatTime'

const nodeIdToNameCache = {}
const allocIdRegexp = /\[(\d+)\]/

const getAllocationNumberFromName = (allocationName) => {
  const match = allocIdRegexp.exec(allocationName)
  return match[1]
}

/* eslint-disable react/prop-types */

const AllocationStatusIconCell = ({ rowIndex, data, ...props }) => (
  <Cell { ...props }>
    <AllocationStatusIcon allocation={ data[rowIndex] } />
  </Cell>
);

const AllocationLinkCell = ({ rowIndex, data, ...props }) => (
  <Cell { ...props }>
    <AllocationLink allocationId={ data[rowIndex].ID } />
  </Cell>
);

const JobLinkCell = ({ rowIndex, data, ...props }) => (
  <Cell { ...props }>
    <JobLink jobId={ data[rowIndex].JobID } />
  </Cell>
);

const JobTaskGroupLinkCell = ({ rowIndex, data, ...props }) => (
  <Cell { ...props }>
    <JobLink jobId={ data[rowIndex].JobID } taskGroupId={ data[rowIndex].TaskGroupId }>
      { data[rowIndex].TaskGroup } (#{ getAllocationNumberFromName(data[rowIndex].Name) })
    </JobLink>
  </Cell>
);

const ClientLinkCell = ({ rowIndex, data, clients, ...props }) => (
  <Cell { ...props }>
    <ClientLink clientId={ data[rowIndex].NodeID } clients={ clients } short />
  </Cell>
);

const AgeCell = ({ rowIndex, data, ...props }) => (
  <Cell { ...props }>
    <FormatTime identifier={ data[rowIndex].ID } time={ data[rowIndex].CreateTime } />
  </Cell>
);

const StatusCell = ({ rowIndex, data, ...props }) => (
  <Cell { ...props }>
    { data[rowIndex].ClientStatus }
  </Cell>
);

const ActionsCell = ({ rowIndex, data, ...props }) => (
  <Cell { ...props }>
    <AllocationLink allocationId={ data[rowIndex].ID } linkAppend='/files' linkQuery={{ path: '/alloc/logs/' }}>
      <FontIcon className='material-icons'>format_align_left</FontIcon>
    </AllocationLink>
  </Cell>
);

/* eslint-disable react/prop-types */

const jobColumn = (allocations, display) =>
  (display
    ?
      <Column
        header={ <Cell>Job</Cell> }
        cell={ <JobLinkCell data={ allocations } /> }
        flexGrow={ 2 }
        width={ 200 }
      />
    : null
  )

const clientColumn = (allocations, display, clients) =>
  (display
    ?
      <Column
        header={ <Cell>Client</Cell> }
        cell={ <ClientLinkCell data={ allocations } clients={ clients } /> }
        width={ 200 }
      />
    : null
  )

class AllocationList extends Component {

  constructor (props) {
    super(props)

    this.resizeHandler = this.updateDimensions.bind(this)
  }

  findNodeNameById (nodeId) {
    if (this.props.nodes.length === 0) {
      return nodeId
    }

    if (nodeId in nodeIdToNameCache) {
      return nodeIdToNameCache[nodeId]
    }

    const r = Object.keys(this.props.nodes).filter(node => this.props.nodes[node].ID === nodeId)

    if (r.length !== 0) {
      nodeIdToNameCache[nodeId] = this.props.nodes[r].Name
    } else {
      nodeIdToNameCache[nodeId] = nodeId
    }

    return nodeIdToNameCache[nodeId]
  }

  filteredAllocations () {
    const query = this.props.location.query || {}
    let allocations = this.props.allocations

    if ('allocation_id' in query) {
      allocations = allocations.filter(allocation => allocation.ID.indexOf(query.allocation_id) != -1)
    }

    if ('allocation_id' in this.state) {
      allocations = allocations.filter(allocation => allocation.ID.indexOf(this.state.allocation_id) != -1)
    }

    if ('status' in query) {
      allocations = allocations.filter(allocation => allocation.ClientStatus === query.status)
    }

    if ('client' in query) {
      allocations = allocations.filter(allocation => allocation.NodeID === query.client)
    }

    if ('job' in query) {
      allocations = allocations.filter(allocation => allocation.JobID === query.job)
    }

    return allocations
  }

  allocationStatusFilter () {
    const location = this.props.location
    const query = this.props.location.query || {}

    let title = 'Allocation Status'
    if ('status' in query) {
      title = {title}: { query.status }
    }

    return (
      <Col key='allocation-status-filter-pane' xs={ 12 } sm={ 6 } md={ 6 } lg={ 3 }>
        <SelectField floatingLabelText={ title } maxHeight={ 200 }>
          <MenuItem>
            <Link to={{ pathname: location.pathname, query: { ...query, status: undefined } }}>- Any -</Link>
          </MenuItem>
          <MenuItem>
            <Link to={{ pathname: location.pathname, query: { ...query, status: 'running' } }}>Running</Link>
          </MenuItem>
          <MenuItem>
            <Link to={{ pathname: location.pathname, query: { ...query, status: 'complete' } }}>Complete</Link>
          </MenuItem>
          <MenuItem>
            <Link to={{ pathname: location.pathname, query: { ...query, status: 'pending' } }}>Pending</Link>
          </MenuItem>
          <MenuItem>
            <Link to={{ pathname: location.pathname, query: { ...query, status: 'lost' } }}>Lost</Link>
          </MenuItem>
          <MenuItem>
            <Link to={{ pathname: location.pathname, query: { ...query, status: 'failed' } }}>Failed</Link>
          </MenuItem>
        </SelectField>
      </Col>
    )
  }

  allocationIdFilter () {
    return (
      <Col key='allocation-id-filter-pane' xs={ 12 } sm={ 6 } md={ 6 } lg={ 3 }>
        <TextField
          hintText='Allocation ID'
          onChange={ (proxy, value) => { this.setState({ ...this.state, allocation_id: value }) } }
        />
      </Col>
    )
  }

  jobIdFilter () {
    const location = this.props.location
    const query = this.props.location.query || {}

    let title = 'Job'
    if ('job' in query) {
      title = {title}: { query.job }
    }

    const jobs = this.props.allocations
          .map((allocation) => {
            return allocation.JobID
          })
          .filter((v, i, a) => {
            return a.indexOf(v) === i
          })
          .map((job) => {
            return (
              <MenuItem key={ job }>
                <Link to={{ pathname: location.pathname, query: { ...query, job } }}>{ job }</Link>
              </MenuItem>
            )
          })

    jobs.unshift(
      <MenuItem key='any-job'>
        <Link to={{ pathname: location.pathname, query: { ...query, job: undefined } }}>- Any -</Link>
      </MenuItem>
    )

    return (
      <Col key='job-filter-pane' xs={ 12 } sm={ 6 } md={ 6 } lg={ 3 }>
        <SelectField floatingLabelText={ title } maxHeight={ 200 }>{ jobs }</SelectField>
      </Col>
    )
  }

  clientFilter () {
    const location = this.props.location
    const query = this.props.location.query || {}

    let title = 'Client'

    if ('client' in query) {
      title = {title}: { this.findNodeNameById(query.client) }
    }

    const clients = this.props.allocations
          .map((allocation) => {
            return allocation.NodeID
          })
          .filter((v, i, a) => {
            return a.indexOf(v) === i
          })
          .map((client) => {
            return (
              <MenuItem key={ client }>
                <Link to={{ pathname: location.pathname, query: { ...query, client } }}>
                  { this.findNodeNameById(client) }
                </Link>
              </MenuItem>
            )
          })

    clients.unshift(
      <MenuItem key='any-client'>
        <Link to={{ pathname: location.pathname, query: { ...query, client: undefined } }}>- Any -</Link>
      </MenuItem>
    )

    return (
      <Col key='client-filter-pane' xs={ 12 } sm={ 6 } md={ 6 } lg={ 3 }>
        <SelectField floatingLabelText={ title } maxHeight={ 200 }>
          { clients }
        </SelectField>
      </Col>
    )
  }

  updateDimensions() {
    this.setState({
      width: window.innerWidth,
      height: window.innerHeight
    });
  }

  componentWillMount() {
    this.updateDimensions();
  }

  componentDidMount() {
    window.addEventListener("resize", this.resizeHandler);
  }

  componentWillUnmount() {
    window.removeEventListener("resize", this.resizeHandler);
  }

  render () {
    const showJobColumn = this.props.showJobColumn
    const showClientColumn = this.props.showClientColumn
    const allocations = this.filteredAllocations();

    const width = this.state.width - 30
    let height = this.state.height - 240

    if (!showJobColumn || !showClientColumn || this.props.nested) {
      height = height - 120
    }

    if (height < 300) {
      height = 300
    }

    return (

        <Card>
          <CardHeader title='Filter list' actAsExpander showExpandableButton />
          <CardText expandable>
            <Grid fluid style={{ padding: 0 }}>
              <Row>
                { this.allocationIdFilter() }
                { showClientColumn ? this.clientFilter() : null }
                { this.allocationStatusFilter() }
                { showJobColumn ? this.jobIdFilter() : null }
              </Row>
            </Grid>
          </CardText>
        </Card>

        <Card style={{ marginTop: '1rem' }}>
          <CardText>

              <Column
                header={ <Cell /> }
                cell={ <AllocationStatusIconCell data={ allocations } /> }
                width={ 40 }
              />
              <Column
                header={ <Cell>ID</Cell> }
                cell={ <AllocationLinkCell data={ allocations } /> }
                width={ 100 }
              />
              { jobColumn(allocations, this.props.showJobColumn) }
              <Column
                header={ <Cell>Task Group</Cell> }
                cell={ <JobTaskGroupLinkCell data={ allocations } /> }
                flexGrow={ 2 }
                width={ 200 }
              />
              <Column
                header={ <Cell>Status</Cell> }
                cell={ <StatusCell data={ allocations } /> }
                width={ 200 }
              />
              { clientColumn(allocations, this.props.showClientColumn, this.props.nodes) }
              <Column
                header={ <Cell>Age</Cell> }
                cell={ <AgeCell data={ allocations } /> }
                width={ 100 }
              />
              <Column
                header={ <Cell>Actions</Cell> }
                cell={ <ActionsCell data={ allocations } /> }
                width={ 100 }
              />

          </CardText>
        </Card>

)
  }
}

AllocationList.defaultProps = {
  allocations: [],
  nodes: [],
  location: {},

  showJobColumn: true,
  showClientColumn: true,
  nested: false
}

AllocationList.propTypes = {
  allocations: PropTypes.array.isRequired,
  nodes: PropTypes.array.isRequired,
  location: PropTypes.object.isRequired,

  nested: PropTypes.bool.isRequired,

  showJobColumn: PropTypes.bool.isRequired,
  showClientColumn: PropTypes.bool.isRequired
}

export default AllocationList
<|endoftext|>


<commit_before>// Parse some local storage objects!
// onNetwork now comes from an LSO
parseLocalStorage();

var danforthProxyURL = '.libproxy.wustl.edu';
var beckerProxyURL = '.beckerproxy.wustl.edu';

// Once per session, append "?holding=wustlmlib" to PubMed.
var rewrotePubMedThisSession = false;

// For debugging & options page
var networkTag = '';

// Set a 15 minute timeout interval to redetect the network status.
// Per poissoncdf(), this is probably friendly. Hell, MSVPN is way more active.
var refreshTimeoutHandle;
var refreshTimeoutMilliseconds = 900000;

// Are we at WashU? SLCH? WUSM? Off campus!?!
// TODO: At start, guess state from last session. If we're restoring lots of closed tabs on campus at restart, we might incorrectly assume off-campus.
function detectNetworkState() {
    // This will pass an HTTP_ORIGIN flag with the Chrome Extension ID so Becker
    // can contact me or block the extension if it causes any problems.
    var xhr = new XMLHttpRequest();
    xhr.open("GET", "https://becker.wustl.edu/sites/all/themes/bml_theme2/js/network-indicator-script3.php", true);
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
            if (xhr.status == 200) {
                var resp = JSON.parse(xhr.responseText.substring(1, xhr.responseText.length - 1));
                if (resp.networkTag == "WUSTL" || resp.networkTag == "WUSM") {
                    onNetwork = true;
                    localStorage.onNetwork = true; // I don't quite trust the LSO API yet, which is why we have this duplication.
                    beckerProxyURL = '.beckerproxy.wustl.edu';
                    networkTag = resp.networkTag;
                } else if (resp.networkTag == "SLCH" || resp.networkTag == "BJH" || resp.networkTag == "BJC") {
                    onNetwork = true; // TODO: Switch to false after excluding the short list of open journals at Becker.
                    localStorage.onNetwork = true;
                    beckerProxyURL = '.beckerproxy2a.wucon.wustl.edu';
                    networkTag = resp.networkTag;
                } else if (resp.networkTag == "OFF") {
                    onNetwork = false;
                    localStorage.onNetwork = false;
                    beckerProxyURL = '.beckerproxy.wustl.edu';
                    networkTag = resp.networkTag;
                } else {
                    // Something weird happened. We don't update onNetwork, since this could be
                    // a timed redetection that failed, so we'll preserve the previous state.
                    console.warn('Error: Unknown response from Becker:' + resp.networkTag);
                    writeToRemoteLog('Becker-Unknown-Response_' + resp.networkTag);
                    setTimeout(function(){ detectNetworkState(); }, 15000);
                }
                console.info('Detection event: onNetwork: ' + onNetwork + ', networkTag: ' + resp.networkTag);
                clearTimeout(refreshTimeoutHandle);
                refreshTimeoutHandle = setTimeout(function(){ detectNetworkState(); }, refreshTimeoutMilliseconds);
            } else {
                // Becker XHR returned status != 200. Maybe a network error?
                console.warn('Error from Becker: Retrying in 15s.');
                setTimeout(function(){ detectNetworkState(); }, 15000);
            }
        }
    };
    xhr.send();
}
detectNetworkState();

// Listener for the options page
chrome.extension.onRequest.addListener(
    function(request, sender, sendResponse) {
        if (request.operation == 'getstate') {
            sendResponse({networkTag: networkTag});
        } else if (request.operation == 'redetect') {
            console.info('Manual network redetection.');
            detectNetworkState();
            sendResponse({networkTag: networkTag});
        } else {
            console.warn('Unknown background listener request.');
            sendResponse({});
        }
    });

// The user wants auto-redirection and is off-network,
// so we perform URL parsing and (maybe) redirection
function checkURLforRedirection(tabId, parsedURL) {
    if (intersect_journals.hasOwnProperty(parsedURL.host)) {
        if (optPreferDanforth || (optEnableDanforth && !optEnableBecker)) {
            doRedirectToProxy(tabId, parsedURL, danforthProxyURL);
            return true;
        } else {
            doRedirectToProxy(tabId, parsedURL, beckerProxyURL);
            return true;
        }
    }

    // We're still here, so the journal is not in intersect_journals.
    if (optEnableDanforth) {
        if (danforth_journals.hasOwnProperty(parsedURL.host)) {
            doRedirectToProxy(tabId, parsedURL, danforthProxyURL);
            return true;
        }
    }
    if (optEnableBecker) {
        if (becker_journals.hasOwnProperty(parsedURL.host)) {
            doRedirectToProxy(tabId, parsedURL, beckerProxyURL);
            return true;
        }
    }
    return false;
}

// Redirect the page using the auto-determined proxy URL
// Modularize so we only parse the URL once (parsing is expensive!)
function doRedirectToProxy(tabId, parsed, proxyURL, appendString) {
    if (!appendString) {
        appendString = '';
    }
    var proxifiedURL = parsed.protocol + "://" + parsed.host + proxyURL + parsed.relative + appendString;
    try {
        chrome.tabs.update(tabId, {url: proxifiedURL});
    } catch(e) {
        // Pre-rendered magic tab that we can't update.
    }
}

// Send log data if there's a missed URL (someone had to manually click)
// or there's a Becker network detection error.
function writeToRemoteLog(theData) {
    var uploader = new XMLHttpRequest();
    var logString = "url:" + encodeURIComponent(theData) + ",auto:" + optAutoRedirect +
        ",onNet:" + onNetwork + ",tag:" + networkTag;
    uploader.open("GET", "https://update.epoxate.com/becker-extension/log.py?" + logString, true);
    uploader.send();
}

// Listen for pending URL loads
// WARNING: Pre-rendered tabs from URL prediction (or link hints) cause oddities with onBeforeNavigate,
//          like inaccessible tabs. Be careful here!
function checkNavObject(frameId, tabId, url) {
    // Only listen in the main frame at frameId=0
    // Should we check for redirect loops, like with web.mit.edu?
    // frameId will match "0" for webNavigation object, and "Undefined" for tabs
    if (optAutoRedirect && frameId === 0) {
        var parsedURL = parseUri(url);
        if (optEnableBecker && parsedURL.host == "www.ncbi.nlm.nih.gov") {
            // Redirect PubMed journals to add ?holding flag.
            // This is useful whether we are on or off network.
            // TODO: Redirect for PMC articles, too?
            if (!rewrotePubMedThisSession) {
                if (parsedURL.relative == "/pubmed/" || parsedURL.relative == "/pubmed"
                    || parsedURL.relative == "/pmc/" || parsedURL.relative.match(/^\/pubmed\/\d{6,}$/)) {
                    // Redirect with no proxy url, but an appendString
                    doRedirectToProxy(tabId, parsedURL, "", "?holding=wustlmlib");
                    rewrotePubMedThisSession = true;
                    setTimeout(function(){ rewrotePubMedThisSession = false; }, 10800000);
                }
            }
        }  else if (!onNetwork) {
            checkURLforRedirection(tabId, parsedURL);
        }
    }
}

chrome.webNavigation.onBeforeNavigate.addListener(function(navObject) {
    checkNavObject(navObject.frameId, navObject.tabId, navObject.url); } );
chrome.tabs.onCreated.addListener(function(navObject) {
    checkNavObject(0, navObject.id, navObject.url); } );
// Deal with pre-rendered tabs.
chrome.webNavigation.onTabReplaced.addListener(function(navObject) {
    chrome.tabs.get(navObject.tabId, function(tab) {
        checkNavObject(0, navObject.id, tab.url);
    }); });

// Alert users if they're trying to unnecessarily proxify things.
function showUserHint(warning) {
    chrome.notifications.create(
        'note',
        {   type: 'basic',
            iconUrl: '../images/schollidesign48.png',
            title: 'Unnecessary Click',
            message: warning
        },
        function(noteId) { setTimeout(function(){ chrome.notifications.clear(noteId, function() {}) }, 8000); }
    );
}

// Listen to clicking on our button.
chrome.browserAction.onClicked.addListener(function (tab) {
    // Parse and redirect.
    var parsedURL = parseUri(tab.url);

    // We check for HTTP/HTTPS only (no chrome://), and that "proxy.wustl.edu" isn't at the end of the
    // string, otherwise we're probably already at [becker|lib]proxy.wustl.edu.
    if (parsedURL.protocol == 'http' || parsedURL.protocol == 'https') {
        if (parsedURL.host.substring(parsedURL.host.length - 15) != 'proxy.wustl.edu') {
            if (hint_urls.hasOwnProperty(parsedURL.host)) {
                // Warn users if they're doing something unnecessary.
                // In this case, do NOT redirect these URLs.
                showUserHint(hint_urls[parsedURL.host]);
            } else {
                // Warn on-network redirects, but don't block them
                if (onNetwork) {
                    showUserHint('You\'re on a BJC/WashU network, so you probably don\'t need a proxy.');
                }
                // TODO: Handle Danforth differently, since they don't seem to like SSL as much.
                // TODO: Consider dropping HTTPS support? Is this useful?
                if (parsedURL.protocol == 'https') {
                    parsedURL.host = parsedURL.host.replace(/\./g, '-');
                }
                if (optPreferDanforth || (!optEnableBecker && optEnableDanforth)) {
                    doRedirectToProxy(tab.id, parsedURL, danforthProxyURL);
                } else {
                    doRedirectToProxy(tab.id, parsedURL, beckerProxyURL);
                }
            }
        } else {
            // User clicked even though we're already proxified
            showUserHint('Looks like you\'re already using the proxy. No need to click again!');
        }
    } else {
        showUserHint('This extension only works on websites starting with http:// or https://');
    }

    // If we've been manually clicked (and auto-triggering is enabled), the journal
    // is missing from our list. Let's send the journal URL to the cloud logger.
    // We log non-http/https here in case we're missing something (ftp sites for journal data?)
    if (!tab.incognito && !optUsageOptOut) {
        writeToRemoteLog(parsedURL.host);
    }
});
<commit_msg>Apparently beckerproxy2a doesn't really work :/<commit_after>// Parse some local storage objects!
// onNetwork now comes from an LSO
parseLocalStorage();

var danforthProxyURL = '.libproxy.wustl.edu';
var beckerProxyURL = '.beckerproxy.wustl.edu';

// Once per session, append "?holding=wustlmlib" to PubMed.
var rewrotePubMedThisSession = false;

// For debugging & options page
var networkTag = '';

// Set a 15 minute timeout interval to redetect the network status.
// Per poissoncdf(), this is probably friendly. Hell, MSVPN is way more active.
var refreshTimeoutHandle;
var refreshTimeoutMilliseconds = 900000;

// Are we at WashU? SLCH? WUSM? Off campus!?!
// TODO: At start, guess state from last session. If we're restoring lots of closed tabs on campus at restart, we might incorrectly assume off-campus.
function detectNetworkState() {
    // This will pass an HTTP_ORIGIN flag with the Chrome Extension ID so Becker
    // can contact me or block the extension if it causes any problems.
    var xhr = new XMLHttpRequest();
    xhr.open("GET", "https://becker.wustl.edu/sites/all/themes/bml_theme2/js/network-indicator-script3.php", true);
    xhr.onreadystatechange = function() {
        if (xhr.readyState == 4) {
            if (xhr.status == 200) {
                var resp = JSON.parse(xhr.responseText.substring(1, xhr.responseText.length - 1));
                if (resp.networkTag == "WUSTL" || resp.networkTag == "WUSM") {
                    onNetwork = true;
                    localStorage.onNetwork = true; // I don't quite trust the LSO API yet, which is why we have this duplication.
                    beckerProxyURL = '.beckerproxy.wustl.edu';
                    networkTag = resp.networkTag;
                } else if (resp.networkTag == "SLCH" || resp.networkTag == "BJH" || resp.networkTag == "BJC") {
                    onNetwork = true; // TODO: Switch to false after excluding the short list of open journals at Becker.
                    localStorage.onNetwork = true;
                    beckerProxyURL = '.beckerproxy.wucon.wustl.edu';
                    networkTag = resp.networkTag;
                } else if (resp.networkTag == "OFF") {
                    onNetwork = false;
                    localStorage.onNetwork = false;
                    beckerProxyURL = '.beckerproxy.wustl.edu';
                    networkTag = resp.networkTag;
                } else {
                    // Something weird happened. We don't update onNetwork, since this could be
                    // a timed redetection that failed, so we'll preserve the previous state.
                    console.warn('Error: Unknown response from Becker:' + resp.networkTag);
                    writeToRemoteLog('Becker-Unknown-Response_' + resp.networkTag);
                    setTimeout(function(){ detectNetworkState(); }, 15000);
                }
                console.info('Detection event: onNetwork: ' + onNetwork + ', networkTag: ' + resp.networkTag);
                clearTimeout(refreshTimeoutHandle);
                refreshTimeoutHandle = setTimeout(function(){ detectNetworkState(); }, refreshTimeoutMilliseconds);
            } else {
                // Becker XHR returned status != 200. Maybe a network error?
                console.warn('Error from Becker: Retrying in 15s.');
                setTimeout(function(){ detectNetworkState(); }, 15000);
            }
        }
    };
    xhr.send();
}
detectNetworkState();

// Listener for the options page
chrome.extension.onRequest.addListener(
    function(request, sender, sendResponse) {
        if (request.operation == 'getstate') {
            sendResponse({networkTag: networkTag});
        } else if (request.operation == 'redetect') {
            console.info('Manual network redetection.');
            detectNetworkState();
            sendResponse({networkTag: networkTag});
        } else {
            console.warn('Unknown background listener request.');
            sendResponse({});
        }
    });

// The user wants auto-redirection and is off-network,
// so we perform URL parsing and (maybe) redirection
function checkURLforRedirection(tabId, parsedURL) {
    if (intersect_journals.hasOwnProperty(parsedURL.host)) {
        if (optPreferDanforth || (optEnableDanforth && !optEnableBecker)) {
            doRedirectToProxy(tabId, parsedURL, danforthProxyURL);
            return true;
        } else {
            doRedirectToProxy(tabId, parsedURL, beckerProxyURL);
            return true;
        }
    }

    // We're still here, so the journal is not in intersect_journals.
    if (optEnableDanforth) {
        if (danforth_journals.hasOwnProperty(parsedURL.host)) {
            doRedirectToProxy(tabId, parsedURL, danforthProxyURL);
            return true;
        }
    }
    if (optEnableBecker) {
        if (becker_journals.hasOwnProperty(parsedURL.host)) {
            doRedirectToProxy(tabId, parsedURL, beckerProxyURL);
            return true;
        }
    }
    return false;
}

// Redirect the page using the auto-determined proxy URL
// Modularize so we only parse the URL once (parsing is expensive!)
function doRedirectToProxy(tabId, parsed, proxyURL, appendString) {
    if (!appendString) {
        appendString = '';
    }
    var proxifiedURL = parsed.protocol + "://" + parsed.host + proxyURL + parsed.relative + appendString;
    try {
        chrome.tabs.update(tabId, {url: proxifiedURL});
    } catch(e) {
        // Pre-rendered magic tab that we can't update.
    }
}

// Send log data if there's a missed URL (someone had to manually click)
// or there's a Becker network detection error.
function writeToRemoteLog(theData) {
    var uploader = new XMLHttpRequest();
    var logString = "url:" + encodeURIComponent(theData) + ",auto:" + optAutoRedirect +
        ",onNet:" + onNetwork + ",tag:" + networkTag;
    uploader.open("GET", "https://update.epoxate.com/becker-extension/log.py?" + logString, true);
    uploader.send();
}

// Listen for pending URL loads
// WARNING: Pre-rendered tabs from URL prediction (or link hints) cause oddities with onBeforeNavigate,
//          like inaccessible tabs. Be careful here!
function checkNavObject(frameId, tabId, url) {
    // Only listen in the main frame at frameId=0
    // Should we check for redirect loops, like with web.mit.edu?
    // frameId will match "0" for webNavigation object, and "Undefined" for tabs
    if (optAutoRedirect && frameId === 0) {
        var parsedURL = parseUri(url);
        if (optEnableBecker && parsedURL.host == "www.ncbi.nlm.nih.gov") {
            // Redirect PubMed journals to add ?holding flag.
            // This is useful whether we are on or off network.
            // TODO: Redirect for PMC articles, too?
            if (!rewrotePubMedThisSession) {
                if (parsedURL.relative == "/pubmed/" || parsedURL.relative == "/pubmed"
                    || parsedURL.relative == "/pmc/" || parsedURL.relative.match(/^\/pubmed\/\d{6,}$/)) {
                    // Redirect with no proxy url, but an appendString
                    doRedirectToProxy(tabId, parsedURL, "", "?holding=wustlmlib");
                    rewrotePubMedThisSession = true;
                    setTimeout(function(){ rewrotePubMedThisSession = false; }, 10800000);
                }
            }
        }  else if (!onNetwork) {
            checkURLforRedirection(tabId, parsedURL);
        }
    }
}

chrome.webNavigation.onBeforeNavigate.addListener(function(navObject) {
    checkNavObject(navObject.frameId, navObject.tabId, navObject.url); } );
chrome.tabs.onCreated.addListener(function(navObject) {
    checkNavObject(0, navObject.id, navObject.url); } );
// Deal with pre-rendered tabs.
chrome.webNavigation.onTabReplaced.addListener(function(navObject) {
    chrome.tabs.get(navObject.tabId, function(tab) {
        checkNavObject(0, navObject.id, tab.url);
    }); });

// Alert users if they're trying to unnecessarily proxify things.
function showUserHint(warning) {
    chrome.notifications.create(
        'note',
        {   type: 'basic',
            iconUrl: '../images/schollidesign48.png',
            title: 'Unnecessary Click',
            message: warning
        },
        function(noteId) { setTimeout(function(){ chrome.notifications.clear(noteId, function() {}) }, 8000); }
    );
}

// Listen to clicking on our button.
chrome.browserAction.onClicked.addListener(function (tab) {
    // Parse and redirect.
    var parsedURL = parseUri(tab.url);

    // We check for HTTP/HTTPS only (no chrome://), and that "proxy.wustl.edu" isn't at the end of the
    // string, otherwise we're probably already at [becker|lib]proxy.wustl.edu.
    if (parsedURL.protocol == 'http' || parsedURL.protocol == 'https') {
        if (parsedURL.host.substring(parsedURL.host.length - 15) != 'proxy.wustl.edu') {
            if (hint_urls.hasOwnProperty(parsedURL.host)) {
                // Warn users if they're doing something unnecessary.
                // In this case, do NOT redirect these URLs.
                showUserHint(hint_urls[parsedURL.host]);
            } else {
                // Warn on-network redirects, but don't block them
                if (onNetwork) {
                    showUserHint('You\'re on a BJC/WashU network, so you probably don\'t need a proxy.');
                }
                // TODO: Handle Danforth differently, since they don't seem to like SSL as much.
                // TODO: Consider dropping HTTPS support? Is this useful?
                if (parsedURL.protocol == 'https') {
                    parsedURL.host = parsedURL.host.replace(/\./g, '-');
                }
                if (optPreferDanforth || (!optEnableBecker && optEnableDanforth)) {
                    doRedirectToProxy(tab.id, parsedURL, danforthProxyURL);
                } else {
                    doRedirectToProxy(tab.id, parsedURL, beckerProxyURL);
                }
            }
        } else {
            // User clicked even though we're already proxified
            showUserHint('Looks like you\'re already using the proxy. No need to click again!');
        }
    } else {
        showUserHint('This extension only works on websites starting with http:// or https://');
    }

    // If we've been manually clicked (and auto-triggering is enabled), the journal
    // is missing from our list. Let's send the journal URL to the cloud logger.
    // We log non-http/https here in case we're missing something (ftp sites for journal data?)
    if (!tab.incognito && !optUsageOptOut) {
        writeToRemoteLog(parsedURL.host);
    }
});
<|endoftext|>


<commit_before>Behaviour.register({
	'#BBCodeHint': {
		onclick: function() {
			if($('BBTagsHolder').style.display == "none") {
				Effect.BlindDown('BBTagsHolder');
			} else{
				Effect.BlindUp('BBTagsHolder');
			}
			return false;
		}
	}
});
<commit_msg>BUGFIX: Refactored bbcodehelp.js to use entwine.<commit_after>(function($) {
$.entwine('ss', function($){

	$('#BBCodeHint').entwine({
		onclick: function() {
			$('#BBTagsHolder').toggle();
		}
	});

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


<commit_before>'use strict';

angular.module('mean.icu.ui.taskdetails', [])
.controller('TaskDetailsController', function ($scope, entity, tags, projects, $state, TasksService, context) {
    $scope.task = entity || context.entity;
    $scope.tags = tags;
    $scope.projects = projects;

    TasksService.getStarred().then(function(starred) {
        $scope.task.star = _(starred).any(function(s) {
            return s._id === $scope.task._id;
        });
    });

    if (!$scope.task) {
        $state.go('main.tasks.byentity', {
            entity: context.entityName,
            entityId: context.entityId
        });
    }

    $scope.tagInputVisible = false;

    $scope.statuses = ['New', 'Agenda', 'Assigned', 'In progress', 'Review', 'Rejected', 'Done'];

    $scope.getUnusedTags = function () {
        return _.chain($scope.tags).reject(function (t) {
            return $scope.task.tags.indexOf(t.term) >= 0;
        }).sortBy(function (a, b) {
            return b.count - a.count;
        }).pluck('term').value();
    };

    $scope.$watchGroup(['task.description', 'task.title'], function (nVal, oVal) {
        if (nVal !== oVal && oVal) {
            $scope.delayedUpdate($scope.task);
        }
    });

    $scope.addTag = function (tag) {
        $scope.task.tags.push(tag);
        $scope.update($scope.task);
        $scope.tagInputVisible = false;
    };

    $scope.removeTag = function (tag) {
        $scope.task.tags = _($scope.task.tags).without(tag);
        $scope.update($scope.task);
    };

    $scope.options = {
        theme: 'bootstrap',
        buttons: ['bold', 'italic', 'underline', 'anchor', 'quote', 'orderedlist', 'unorderedlist']
    };

    $scope.dueOptions = {
        onSelect: function () {
            $scope.update($scope.task);
        },
        dateFormat: 'd.m.yy'
    };

    $scope.star = function (task) {
        TasksService.star(task).then(function () {
            $state.reload('main.tasks.byentity.details');
        });
    };

    $scope.deleteProject = function (event, task) {
        event.stopPropagation();
        delete task.project;
        $scope.update(task);
    };

    $scope.deleteTask = function (task) {
        TasksService.remove(task._id).then(function () {
            $state.go('main.tasks.byentity', {
                entity: context.entityName === 'all' ? 'task' : context.entityName,
                entityId: context.entityId
            }, {reload: true});
        });
    };

    $scope.update = function (task) {
        if (context.entityName === 'discussion') {
            task.discussion = context.entityId;
        }

        TasksService.update(task).then(function (result) {
            if (context.entityName === 'all') {
                task.project = result.project;
            } else if (context.entityName === 'project') {
                var projId = result.project ? result.project._id : undefined;
                if (projId !== context.entityId) {
                    $state.go('main.tasks.byentity', {
                        entity: context.entityName,
                        entityId: context.entityId
                    }, {reload: true});
                }
            }
        });
    };

    $scope.delayedUpdate = _.debounce($scope.update, 500);

    if ($scope.task &&
            ($state.current.name === 'main.tasks.byentity.details' ||
            $state.current.name === 'main.search.task' ||
            $state.current.name === 'main.tasks.all.details')) {
        $state.go('.activities');
    }
});
<commit_msg>Fix project adding<commit_after>'use strict';

angular.module('mean.icu.ui.taskdetails', [])
.controller('TaskDetailsController', function ($scope, entity, tags, projects, $state, TasksService, context) {
    $scope.task = entity || context.entity;
    $scope.tags = tags;
    $scope.projects = projects;

    TasksService.getStarred().then(function(starred) {
        $scope.task.star = _(starred).any(function(s) {
            return s._id === $scope.task._id;
        });
    });

    if (!$scope.task) {
        $state.go('main.tasks.byentity', {
            entity: context.entityName,
            entityId: context.entityId
        });
    }

    $scope.tagInputVisible = false;

    $scope.statuses = ['New', 'Agenda', 'Assigned', 'In progress', 'Review', 'Rejected', 'Done'];

    $scope.getUnusedTags = function () {
        return _.chain($scope.tags).reject(function (t) {
            return $scope.task.tags.indexOf(t.term) >= 0;
        }).sortBy(function (a, b) {
            return b.count - a.count;
        }).pluck('term').value();
    };

    $scope.$watchGroup(['task.description', 'task.title'], function (nVal, oVal) {
        if (nVal !== oVal && oVal) {
            $scope.delayedUpdate($scope.task);
        }
    });

    $scope.addTag = function (tag) {
        $scope.task.tags.push(tag);
        $scope.update($scope.task);
        $scope.tagInputVisible = false;
    };

    $scope.removeTag = function (tag) {
        $scope.task.tags = _($scope.task.tags).without(tag);
        $scope.update($scope.task);
    };

    $scope.options = {
        theme: 'bootstrap',
        buttons: ['bold', 'italic', 'underline', 'anchor', 'quote', 'orderedlist', 'unorderedlist']
    };

    $scope.dueOptions = {
        onSelect: function () {
            $scope.update($scope.task);
        },
        dateFormat: 'd.m.yy'
    };

    $scope.star = function (task) {
        TasksService.star(task).then(function () {
            $state.reload('main.tasks.byentity.details');
        });
    };

    $scope.deleteProject = function (event, task) {
        event.stopPropagation();
        delete task.project;
        $scope.update(task);
    };

    $scope.deleteTask = function (task) {
        TasksService.remove(task._id).then(function () {
            $state.go('main.tasks.byentity', {
                entity: context.entityName === 'all' ? 'task' : context.entityName,
                entityId: context.entityId
            }, {reload: true});
        });
    };

    $scope.update = function (task) {
        if (context.entityName === 'discussion') {
            task.discussion = context.entityId;
        }

        TasksService.update(task).then(function (result) {
            if (context.entityName === 'project') {
                var projId = result.project ? result.project._id : undefined;
                if (projId !== context.entityId) {
                    $state.go('main.tasks.byentity', {
                        entity: context.entityName,
                        entityId: context.entityId
                    }, {reload: true});
                }
            }
        });
    };

    $scope.delayedUpdate = _.debounce($scope.update, 500);

    if ($scope.task &&
            ($state.current.name === 'main.tasks.byentity.details' ||
            $state.current.name === 'main.search.task' ||
            $state.current.name === 'main.tasks.all.details')) {
        $state.go('.activities');
    }
});
<|endoftext|>


<commit_before>var Fluxxor = require("fluxxor");
var Immutable = require("immutable");

var constants = {
  SET_STEP_RUNNING: 'SET_STEP_RUNNING',
  SET_STEP_ERRORED: 'SET_STEP_ERRORED',
  SET_STEP_VERSION_INFO: 'SET_STEP_VERSION_INFO',
  SET_STEP_SUCCESSFUL: 'SET_STEP_SUCCESSFUL',
  TOGGLE_STEP_LOGS: 'TOGGLE_STEP_LOGS',
};

var Store = Fluxxor.createStore({
  initialize: function() {
    this.steps = Immutable.Map();

    this.bindActions(
      constants.SET_STEP_RUNNING, this.onSetStepRunning,
      constants.SET_STEP_ERRORED, this.onSetStepErrored,
      constants.SET_STEP_VERSION_INFO, this.onSetStepVersionInfo,
      constants.SET_STEP_SUCCESSFUL, this.onSetStepSuccessful,
      constants.TOGGLE_STEP_LOGS, this.onToggleStepLogs
    );
  },

  setStep: function(origin, changes) {
    this.steps = this.steps.updateIn(origin.location, function(stepModel) {
      if (stepModel === undefined) {
        return new StepModel(origin).merge(changes);
      } else {
        return stepModel.merge(changes);
      }
    });

    this.emit("change");
  },

  onSetStepVersionInfo: function(data) {
    this.setStep(data.origin, { version: data.version, metadata: data.metadata });
  },

  onSetStepSuccessful: function(data) {
    this.setStep(data.origin, { successful: data.successful });
  },

  onSetStepRunning: function(data) {
    this.setStep(data.origin, { running: data.running });
  },

  onSetStepErrored: function(data) {
    this.setStep(data.origin, { errored: data.errored });
  },

  onToggleStepLogs: function(data) {
    var step = this.steps.getIn(data.origin.location);
    this.setStep(data.origin, { showLogs: !step.isShowingLogs(), userToggled: true });
  },

  getState: function() {
    return this.steps;
  },
});

function StepModel(origin) {
  this._map = new Immutable.Map({
    origin: origin,

    showLogs: true,
    userToggled: false,

    running: false,
    errored: false,

    version: undefined,
    metadata: undefined,

    successful: undefined,
  });

  this.merge = function(attrs) {
    var newModel = new StepModel(this.origin());
    newModel._map = this._map.merge(attrs);
    return newModel;
  }

  this.origin = function() {
    return this._map.get("origin");
  }

  this.isShowingLogs = function() {
    var showLogs = this._map.get("showLogs");
    if (this.wasToggled()) {
      return showLogs
    }

    return showLogs && (this.isRunning() || this.isSuccessful() === false);
  }

  this.isRunning = function() {
    return this._map.get("running");
  }

  this.isErrored = function() {
    return this._map.get("errored");
  }

  this.isSuccessful = function() {
    return this._map.get("successful");
  }

  this.isFirstOccurrence = function() {
    // currently not supported
    return false;
  }

  this.wasToggled = function() {
    return this._map.get("userToggled");
  }

  this.version = function() {
    var x = this._map.get("version");
    if (x === undefined) {
      return undefined;
    }

    return x.toJS();
  }

  this.metadata = function() {
    var meta = this._map.get("metadata");
    if (meta === undefined) {
      return undefined;
    }

    return meta.toJS();
  }
}

module.exports = {
  Store: Store
};

for (var k in constants) {
  module.exports[k] = constants[k];
}
<commit_msg>don't auto-collapse errored steps<commit_after>var Fluxxor = require("fluxxor");
var Immutable = require("immutable");

var constants = {
  SET_STEP_RUNNING: 'SET_STEP_RUNNING',
  SET_STEP_ERRORED: 'SET_STEP_ERRORED',
  SET_STEP_VERSION_INFO: 'SET_STEP_VERSION_INFO',
  SET_STEP_SUCCESSFUL: 'SET_STEP_SUCCESSFUL',
  TOGGLE_STEP_LOGS: 'TOGGLE_STEP_LOGS',
};

var Store = Fluxxor.createStore({
  initialize: function() {
    this.steps = Immutable.Map();

    this.bindActions(
      constants.SET_STEP_RUNNING, this.onSetStepRunning,
      constants.SET_STEP_ERRORED, this.onSetStepErrored,
      constants.SET_STEP_VERSION_INFO, this.onSetStepVersionInfo,
      constants.SET_STEP_SUCCESSFUL, this.onSetStepSuccessful,
      constants.TOGGLE_STEP_LOGS, this.onToggleStepLogs
    );
  },

  setStep: function(origin, changes) {
    this.steps = this.steps.updateIn(origin.location, function(stepModel) {
      if (stepModel === undefined) {
        return new StepModel(origin).merge(changes);
      } else {
        return stepModel.merge(changes);
      }
    });

    this.emit("change");
  },

  onSetStepVersionInfo: function(data) {
    this.setStep(data.origin, { version: data.version, metadata: data.metadata });
  },

  onSetStepSuccessful: function(data) {
    this.setStep(data.origin, { successful: data.successful });
  },

  onSetStepRunning: function(data) {
    this.setStep(data.origin, { running: data.running });
  },

  onSetStepErrored: function(data) {
    this.setStep(data.origin, { errored: data.errored });
  },

  onToggleStepLogs: function(data) {
    var step = this.steps.getIn(data.origin.location);
    this.setStep(data.origin, { showLogs: !step.isShowingLogs(), userToggled: true });
  },

  getState: function() {
    return this.steps;
  },
});

function StepModel(origin) {
  this._map = new Immutable.Map({
    origin: origin,

    showLogs: true,
    userToggled: false,

    running: false,
    errored: false,

    version: undefined,
    metadata: undefined,

    successful: undefined,
  });

  this.merge = function(attrs) {
    var newModel = new StepModel(this.origin());
    newModel._map = this._map.merge(attrs);
    return newModel;
  }

  this.origin = function() {
    return this._map.get("origin");
  }

  this.isShowingLogs = function() {
    var showLogs = this._map.get("showLogs");
    if (this.wasToggled()) {
      return showLogs
    }

    return showLogs && (this.isRunning() || this.isErrored() || this.isSuccessful() === false);
  }

  this.isRunning = function() {
    return this._map.get("running");
  }

  this.isErrored = function() {
    return this._map.get("errored");
  }

  this.isSuccessful = function() {
    return this._map.get("successful");
  }

  this.isFirstOccurrence = function() {
    // currently not supported
    return false;
  }

  this.wasToggled = function() {
    return this._map.get("userToggled");
  }

  this.version = function() {
    var x = this._map.get("version");
    if (x === undefined) {
      return undefined;
    }

    return x.toJS();
  }

  this.metadata = function() {
    var meta = this._map.get("metadata");
    if (meta === undefined) {
      return undefined;
    }

    return meta.toJS();
  }
}

module.exports = {
  Store: Store
};

for (var k in constants) {
  module.exports[k] = constants[k];
}
<|endoftext|>


<commit_before>import React, {PropTypes, Component} from 'react'
import {push} from 'react-router-redux'
import {connect} from 'react-redux'
import Helmet from 'react-helmet'
import moment from 'moment'
import {Link} from 'react-router'
import {FormattedMessage} from 'react-intl'

import loader from '../../loader'
import {loadUser, sendReview} from '../../actions/users'
import ProficiencyRating from '../ProficiencyRating'

import Review from './Review'
import SendReview from './SendReview'

import css from './ProfilePage.style'

function roleToTitle (role) {
  switch (role) {
    case 'student': return 'Daheim SchülerIn'
    case 'teacher': return 'Daheim SprachcoachIn'
    default: return 'Daheim BenutzerIn'
  }
}

function seitToText (seit) {
  switch (seit) {
    case '2017': return '2017'
    case '2016': return '2016'
    case '2015': return '2015'
    case '2014': return '2014'
    default: return 'Früher als 2014'
  }
}

class ProfilePage extends Component {

  static propTypes = {
    user: PropTypes.object,
    userMeta: PropTypes.object,
    me: PropTypes.bool,
    reviewEditable: PropTypes.bool.isRequired
  }

  static defaultProps = {
    reviewEditable: true
  }

  state = {
    editorOpen: false
  }

  handleEditorRequestClose = () => {
    this.setState({editorOpen: false})
  }

  handleOpenEditor = () => {
    this.setState({editorOpen: true})
  }

  render () {
    const {user, userMeta, me, reviewEditable} = this.props

    const userNotFound = userMeta && userMeta.error && userMeta.error.code === 'user_not_found'
    if (userNotFound) {
      return
Benuzter Konto abgeschlossen

    }

    if (!user) {
      return
Wird geladen...

    }

    const {id, name, picture, role, introduction, inGermanySince, userSince, germanLevel, topics, languages, myReview, receivedReviews} = user

    const editorOpen = !me && reviewEditable && (!myReview || this.state.editorOpen)
    const userSinceText = moment(userSince).format('LL')
    const showStudentFields = role !== 'teacher'

    const topicsArr = Object.keys(topics)
    const languagesArr = Object.keys(languages)

    const otherReviews = receivedReviews
      .filter((review) => !(myReview && review.from === myReview.from))
      .slice(0, 3)
      .map((review) => <Review key={review.from} {...this.props} review={review} />)

    return (

        <Helmet title={name} />

              {name}
              {me ? <Link to='/profile' className={css.editButton}><FormattedMessage id='profile.edit' /></Link> : null}
              {!me ? <Link to={`/users/${id}/report`} className={css.editButton}><FormattedMessage id='profile.reportUser' /></Link> : null}

{roleToTitle(role)}

          {(myReview || editorOpen) ? (

Mein Feedback

                {editorOpen ? (

                      <SendReview {...this.props} onRequestClose={this.handleEditorRequestClose} />

                ) : undefined}

                {myReview ? <Review key={myReview.from} {...this.props} reviewEditable={reviewEditable} review={myReview} onRequestEdit={this.handleOpenEditor} /> : undefined}

          ) : null}

Personendaten

Ein Paar Worte über mich

                  {introduction || Es gibt noch keine Informationen.}

              {showStudentFields ? (

Ich wohne in Deutschland seit

{seitToText(inGermanySince)}

              ) : null}

Ich spreche gern über...

                  {topicsArr.length === 0 ? Noch keine Themen : (
                    topicsArr.map((topic) => {topic})
                  )}

Erfahrung

              {showStudentFields ? (

Deutschkenntnis

<ProficiencyRating value={'' + germanLevel} readOnly />

              ) : null}

Ich spreche auch...

                  {languagesArr.length === 0 ? Keine andere Sprachen : (
                    languagesArr.map((language) => {language})
                  )}

Ich nutze Daheim seit

{userSinceText}

Bisherige Gespräche auf Daheim

4 Gespräche, 1 Stunde 47 Minuten

Feedback

                {otherReviews.length ? otherReviews : Es gibt noch keine Reviews}

    )
  }
}

const loaded = loader({
  shouldReload (prevProps, nextProps) {
    return prevProps.userId !== nextProps.userId
  },

  isLoaded (props) {
    return props.user || (props.userMeta && !props.userMeta.loading)
  },

  load (nextProps) {
    const {userMeta} = nextProps
    const {loading} = userMeta || {}

    if (!loading) nextProps.loadUser({id: nextProps.userId}).catch((err) => err)
  },

  key (props) {
    return props.user ? props.user.id : props.userId
  }
})(ProfilePage)

export default connect((state, props) => {
  let {userId} = props.params
  userId = userId.toLowerCase()

  const user = state.users.users[userId]
  const userMeta = state.users.usersMeta[userId]
  const {profile: {profile}} = state
  const me = profile.id === userId
  return {me, user, userMeta, userId}
}, {push, loadUser, sendReview})(loaded)
<commit_msg>Made other reviews read-only<commit_after>import React, {PropTypes, Component} from 'react'
import {push} from 'react-router-redux'
import {connect} from 'react-redux'
import Helmet from 'react-helmet'
import moment from 'moment'
import {Link} from 'react-router'
import {FormattedMessage} from 'react-intl'

import loader from '../../loader'
import {loadUser, sendReview} from '../../actions/users'
import ProficiencyRating from '../ProficiencyRating'

import Review from './Review'
import SendReview from './SendReview'

import css from './ProfilePage.style'

function roleToTitle (role) {
  switch (role) {
    case 'student': return 'Daheim SchülerIn'
    case 'teacher': return 'Daheim SprachcoachIn'
    default: return 'Daheim BenutzerIn'
  }
}

function seitToText (seit) {
  switch (seit) {
    case '2017': return '2017'
    case '2016': return '2016'
    case '2015': return '2015'
    case '2014': return '2014'
    default: return 'Früher als 2014'
  }
}

class ProfilePage extends Component {

  static propTypes = {
    user: PropTypes.object,
    userMeta: PropTypes.object,
    me: PropTypes.bool,
    reviewEditable: PropTypes.bool.isRequired
  }

  static defaultProps = {
    reviewEditable: true
  }

  state = {
    editorOpen: false
  }

  handleEditorRequestClose = () => {
    this.setState({editorOpen: false})
  }

  handleOpenEditor = () => {
    this.setState({editorOpen: true})
  }

  render () {
    const {user, userMeta, me, reviewEditable} = this.props

    const userNotFound = userMeta && userMeta.error && userMeta.error.code === 'user_not_found'
    if (userNotFound) {
      return
Benuzter Konto abgeschlossen

    }

    if (!user) {
      return
Wird geladen...

    }

    const {id, name, picture, role, introduction, inGermanySince, userSince, germanLevel, topics, languages, myReview, receivedReviews} = user

    const editorOpen = !me && reviewEditable && (!myReview || this.state.editorOpen)
    const userSinceText = moment(userSince).format('LL')
    const showStudentFields = role !== 'teacher'

    const topicsArr = Object.keys(topics)
    const languagesArr = Object.keys(languages)

    const otherReviews = receivedReviews
      .filter((review) => !(myReview && review.from === myReview.from))
      .slice(0, 3)
      .map((review) => <Review key={review.from} {...this.props} review={review} reviewEditable={false} />)

    return (

        <Helmet title={name} />

              {name}
              {me ? <Link to='/profile' className={css.editButton}><FormattedMessage id='profile.edit' /></Link> : null}
              {!me ? <Link to={`/users/${id}/report`} className={css.editButton}><FormattedMessage id='profile.reportUser' /></Link> : null}

{roleToTitle(role)}

          {(myReview || editorOpen) ? (

Mein Feedback

                {editorOpen ? (

                      <SendReview {...this.props} onRequestClose={this.handleEditorRequestClose} />

                ) : undefined}

                {myReview ? <Review key={myReview.from} {...this.props} reviewEditable={reviewEditable} review={myReview} onRequestEdit={this.handleOpenEditor} /> : undefined}

          ) : null}

Personendaten

Ein Paar Worte über mich

                  {introduction || Es gibt noch keine Informationen.}

              {showStudentFields ? (

Ich wohne in Deutschland seit

{seitToText(inGermanySince)}

              ) : null}

Ich spreche gern über...

                  {topicsArr.length === 0 ? Noch keine Themen : (
                    topicsArr.map((topic) => {topic})
                  )}

Erfahrung

              {showStudentFields ? (

Deutschkenntnis

<ProficiencyRating value={'' + germanLevel} readOnly />

              ) : null}

Ich spreche auch...

                  {languagesArr.length === 0 ? Keine andere Sprachen : (
                    languagesArr.map((language) => {language})
                  )}

Ich nutze Daheim seit

{userSinceText}

Bisherige Gespräche auf Daheim

4 Gespräche, 1 Stunde 47 Minuten

Feedback

                {otherReviews.length ? otherReviews : Es gibt noch keine Reviews}

    )
  }
}

const loaded = loader({
  shouldReload (prevProps, nextProps) {
    return prevProps.userId !== nextProps.userId
  },

  isLoaded (props) {
    return props.user || (props.userMeta && !props.userMeta.loading)
  },

  load (nextProps) {
    const {userMeta} = nextProps
    const {loading} = userMeta || {}

    if (!loading) nextProps.loadUser({id: nextProps.userId}).catch((err) => err)
  },

  key (props) {
    return props.user ? props.user.id : props.userId
  }
})(ProfilePage)

export default connect((state, props) => {
  let {userId} = props.params
  userId = userId.toLowerCase()

  const user = state.users.users[userId]
  const userMeta = state.users.usersMeta[userId]
  const {profile: {profile}} = state
  const me = profile.id === userId
  return {me, user, userMeta, userId}
}, {push, loadUser, sendReview})(loaded)
<|endoftext|>


<commit_before>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .
<commit_msg>Stop trying to be fast.<commit_after>// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require_tree .
<|endoftext|>