<commit_before>/*
 * Copyright (C) 2015-2016 Actor LLC. <https://actor.im>
 */

import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import classnames from 'classnames';
import Tooltip from 'rc-tooltip';

import { escapeWithEmoji } from '../../utils/EmojiUtils';

import CallActionCreators from '../../actions/CallActionCreators';
import ActivityActionCreators from '../../actions/ActivityActionCreators';
import FavoriteActionCreators from '../../actions/FavoriteActionCreators';
import SearchMessagesActionCreators from '../../actions/SearchMessagesActionCreators';

import AvatarItem from '../common/AvatarItem.react';
import ToggleFavorite from '../common/ToggleFavorite.react';
import MoreDropdown from './header/MoreDropdown.react';
import SmartCallButton from '../call/SmartCallButton.react';

class DialogHeader extends Component {
  static contextTypes = {
    delegate: PropTypes.object.isRequired
  }

  static propTypes = {
    peer: PropTypes.object.isRequired,
    info: PropTypes.object.isRequired,
    call: PropTypes.object.isRequired,
    message: PropTypes.string,
    isFavorite: PropTypes.bool.isRequired,
    isActivityOpen: PropTypes.bool.isRequired,
    isDialogSearchOpen: PropTypes.bool.isRequired
  }

  constructor(props, context) {
    super(props, context);

    this.state = {
      isMoreDropdownOpen: false
    }

    this.onFavoriteToggle = this.onFavoriteToggle.bind(this);
    this.handleInfoButtonClick = this.handleInfoButtonClick.bind(this);
    this.handleMakeCallButtonClick = this.handleMakeCallButtonClick.bind(this);
    this.handleEndCallButtonClick = this.handleEndCallButtonClick.bind(this);
    this.handleSearchButtonClick = this.handleSearchButtonClick.bind(this);
    this.toggelMoreDropdownOpen = this.toggelMoreDropdownOpen.bind(this);
  }

  onFavoriteToggle() {
    const { peer, isFavorite } = this.props;

    if (isFavorite) {
      FavoriteActionCreators.unfavoriteChat(peer);
    } else {
      FavoriteActionCreators.favoriteChat(peer);
    }
  }

  handleInfoButtonClick() {
    const { call, isActivityOpen } = this.props;
    if (call.isCalling) {
      CallActionCreators.toggleFloating();
    } else if (isActivityOpen) {
      ActivityActionCreators.hide();
    } else {
      ActivityActionCreators.show();
    }
  }

  handleMakeCallButtonClick() {
    const { peer } = this.props;
    CallActionCreators.makePeerCall(peer);
  }

  handleEndCallButtonClick() {
    const { call } = this.props;
    CallActionCreators.endCall(call.id);
  }

  handleSearchButtonClick() {
    const { isDialogSearchOpen } = this.props;
    if (!isDialogSearchOpen) {
      SearchMessagesActionCreators.open();
    } else {
      SearchMessagesActionCreators.close();
    }
  }

  toggelMoreDropdownOpen() {
    const { isMoreDropdownOpen } = this.state;
    this.setState({ isMoreDropdownOpen: !isMoreDropdownOpen })
  }

  renderMessage() {
    const { message } = this.props;

    return message;
  }

  renderInfoButton() {
    const { call, isActivityOpen } = this.props;

    const className = classnames('button button--icon', {
      'active': isActivityOpen || (call.isCalling && !call.isFloating)
    });

    return (
      <Tooltip
        placement="left"
        mouseEnterDelay={0}
        mouseLeaveDelay={0}
        overlay={<FormattedMessage id="tooltip.toolbar.info"/>}
      >
        <button className={className} onClick={this.handleInfoButtonClick}>
          info
        </button>
      </Tooltip>
    )
  }

  renderVerified() {
    const { info } = this.props;

    if (!info.isVerified) {
      return null;
    }

    return (

        verified_user

    );
  }

  renderFavorite() {
    const { isFavorite } = this.props;
    const favoriteClassName = classnames('dialog__header__peer__favorite', {
      'dialog__header__peer__favorite--active': isFavorite
    });

    return (
      <Tooltip
        placement="bottom"
        mouseEnterDelay={0}
        mouseLeaveDelay={0}
        overlay={<FormattedMessage id="tooltip.toolbar.favorite"/>}
      >

          <ToggleFavorite value={isFavorite} onToggle={this.onFavoriteToggle} />

      </Tooltip>
    )
  }

  renderSearchButton() {
    const { delegate } = this.context;
    const { isDialogSearchOpen } = this.props;

    if (!delegate.features.search) {
      return null;
    }

    const callButtonClassName = classnames('button button--icon', {
      'active': isDialogSearchOpen
    });

    return (
      <button className={callButtonClassName} onClick={this.handleSearchButtonClick}>
        search
      </button>
    );
  }

  renderCallButton() {
    const { delegate } = this.context;

    if (!delegate.features.calls) {
      return null;
    }

    const { call } = this.props;
    return (
      <SmartCallButton
        call={call}
        onCallStart={this.handleMakeCallButtonClick}
        onCallEnd={this.handleEndCallButtonClick}
      />
    );
  }

  renderMoreDropdown() {
    const { isMoreDropdownOpen } = this.state;

    if (!isMoreDropdownOpen) {
      return null;
    }

    const { info, peer } = this.props;

    return (
      <MoreDropdown
        onClose={this.toggelMoreDropdownOpen}
        info={info}
        peer={peer}
      />
    );
  }

  renderMoreButton() {
    const { isMoreDropdownOpen } = this.state;

    const dropdownButtonClassNames = classnames('button button--icon', {
      'active': isMoreDropdownOpen
    })

    return (

        <button className={dropdownButtonClassNames} onClick={this.toggelMoreDropdownOpen}>
          more_vert
        </button>
        {this.renderMoreDropdown()}

    );
  }

  render() {
    const { info } = this.props;

    if (!info) {
      return ;
    }

    return (

        <AvatarItem
          className="dialog__header__avatar"
          size="medium"
          image={info.avatar}
          placeholder={info.placeholder}
          title={info.name}
        />

            {this.renderVerified()}
            {this.renderFavorite()}

            {this.renderMessage()}

          {this.renderSearchButton()}
          {this.renderCallButton()}
          {this.renderInfoButton()}
          {this.renderMoreButton()}

    );
  }
}

export default DialogHeader;
<commit_msg>fix(web): hide call button for large groups<commit_after>/*
 * Copyright (C) 2015-2016 Actor LLC. <https://actor.im>
 */

import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import classnames from 'classnames';
import Tooltip from 'rc-tooltip';
import { PeerTypes } from '../../constants/ActorAppConstants';

import { escapeWithEmoji } from '../../utils/EmojiUtils';

import CallActionCreators from '../../actions/CallActionCreators';
import ActivityActionCreators from '../../actions/ActivityActionCreators';
import FavoriteActionCreators from '../../actions/FavoriteActionCreators';
import SearchMessagesActionCreators from '../../actions/SearchMessagesActionCreators';

import AvatarItem from '../common/AvatarItem.react';
import ToggleFavorite from '../common/ToggleFavorite.react';
import MoreDropdown from './header/MoreDropdown.react';
import SmartCallButton from '../call/SmartCallButton.react';

const MAX_GROUP_CALL_SIZE = 25;

class DialogHeader extends Component {
  static contextTypes = {
    delegate: PropTypes.object.isRequired
  }

  static propTypes = {
    peer: PropTypes.object.isRequired,
    info: PropTypes.object.isRequired,
    call: PropTypes.object.isRequired,
    message: PropTypes.string,
    isFavorite: PropTypes.bool.isRequired,
    isActivityOpen: PropTypes.bool.isRequired,
    isDialogSearchOpen: PropTypes.bool.isRequired
  }

  constructor(props, context) {
    super(props, context);

    this.state = {
      isMoreDropdownOpen: false
    }

    this.onFavoriteToggle = this.onFavoriteToggle.bind(this);
    this.handleInfoButtonClick = this.handleInfoButtonClick.bind(this);
    this.handleMakeCallButtonClick = this.handleMakeCallButtonClick.bind(this);
    this.handleEndCallButtonClick = this.handleEndCallButtonClick.bind(this);
    this.handleSearchButtonClick = this.handleSearchButtonClick.bind(this);
    this.toggelMoreDropdownOpen = this.toggelMoreDropdownOpen.bind(this);
  }

  onFavoriteToggle() {
    const { peer, isFavorite } = this.props;

    if (isFavorite) {
      FavoriteActionCreators.unfavoriteChat(peer);
    } else {
      FavoriteActionCreators.favoriteChat(peer);
    }
  }

  handleInfoButtonClick() {
    const { call, isActivityOpen } = this.props;
    if (call.isCalling) {
      CallActionCreators.toggleFloating();
    } else if (isActivityOpen) {
      ActivityActionCreators.hide();
    } else {
      ActivityActionCreators.show();
    }
  }

  handleMakeCallButtonClick() {
    const { peer } = this.props;
    CallActionCreators.makePeerCall(peer);
  }

  handleEndCallButtonClick() {
    const { call } = this.props;
    CallActionCreators.endCall(call.id);
  }

  handleSearchButtonClick() {
    const { isDialogSearchOpen } = this.props;
    if (!isDialogSearchOpen) {
      SearchMessagesActionCreators.open();
    } else {
      SearchMessagesActionCreators.close();
    }
  }

  toggelMoreDropdownOpen() {
    const { isMoreDropdownOpen } = this.state;
    this.setState({ isMoreDropdownOpen: !isMoreDropdownOpen })
  }

  renderMessage() {
    const { message } = this.props;

    return message;
  }

  renderInfoButton() {
    const { call, isActivityOpen } = this.props;

    const className = classnames('button button--icon', {
      'active': isActivityOpen || (call.isCalling && !call.isFloating)
    });

    return (
      <Tooltip
        placement="left"
        mouseEnterDelay={0}
        mouseLeaveDelay={0}
        overlay={<FormattedMessage id="tooltip.toolbar.info"/>}
      >
        <button className={className} onClick={this.handleInfoButtonClick}>
          info
        </button>
      </Tooltip>
    )
  }

  renderVerified() {
    const { info } = this.props;

    if (!info.isVerified) {
      return null;
    }

    return (

        verified_user

    );
  }

  renderFavorite() {
    const { isFavorite } = this.props;
    const favoriteClassName = classnames('dialog__header__peer__favorite', {
      'dialog__header__peer__favorite--active': isFavorite
    });

    return (
      <Tooltip
        placement="bottom"
        mouseEnterDelay={0}
        mouseLeaveDelay={0}
        overlay={<FormattedMessage id="tooltip.toolbar.favorite"/>}
      >

          <ToggleFavorite value={isFavorite} onToggle={this.onFavoriteToggle} />

      </Tooltip>
    )
  }

  renderSearchButton() {
    const { delegate } = this.context;
    const { isDialogSearchOpen } = this.props;

    if (!delegate.features.search) {
      return null;
    }

    const callButtonClassName = classnames('button button--icon', {
      'active': isDialogSearchOpen
    });

    return (
      <button className={callButtonClassName} onClick={this.handleSearchButtonClick}>
        search
      </button>
    );
  }

  renderCallButton() {
    const { delegate } = this.context;
    const { peer, info } = this.props;

    if (!delegate.features.calls) {
      return null;
    }

    if (peer.type === PeerTypes.GROUP && info.members.length > MAX_GROUP_CALL_SIZE) {
      return null;
    }

    const { call } = this.props;
    return (
      <SmartCallButton
        call={call}
        onCallStart={this.handleMakeCallButtonClick}
        onCallEnd={this.handleEndCallButtonClick}
      />
    );
  }

  renderMoreDropdown() {
    const { isMoreDropdownOpen } = this.state;

    if (!isMoreDropdownOpen) {
      return null;
    }

    const { info, peer } = this.props;

    return (
      <MoreDropdown
        onClose={this.toggelMoreDropdownOpen}
        info={info}
        peer={peer}
      />
    );
  }

  renderMoreButton() {
    const { isMoreDropdownOpen } = this.state;

    const dropdownButtonClassNames = classnames('button button--icon', {
      'active': isMoreDropdownOpen
    })

    return (

        <button className={dropdownButtonClassNames} onClick={this.toggelMoreDropdownOpen}>
          more_vert
        </button>
        {this.renderMoreDropdown()}

    );
  }

  render() {
    const { info } = this.props;

    if (!info) {
      return ;
    }

    return (

        <AvatarItem
          className="dialog__header__avatar"
          size="medium"
          image={info.avatar}
          placeholder={info.placeholder}
          title={info.name}
        />

            {this.renderVerified()}
            {this.renderFavorite()}

            {this.renderMessage()}

          {this.renderSearchButton()}
          {this.renderCallButton()}
          {this.renderInfoButton()}
          {this.renderMoreButton()}

    );
  }
}

export default DialogHeader;
<|endoftext|>


<commit_before>var censorRegexp;

Discourse.Dialect.addPreProcessor(function(text) {
  var censored = Discourse.SiteSettings.censored_words;
  if (censored && censored.length) {
    if (!censorRegexp) {
      var split = censored.split("|");
      if (split && split.length) {
        censorRegexp = new RegExp("\\b" + split.map(function (t) { return "(" + t.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') + ")"; }).join("|") + "\\b", "ig");
      }
    }

    if (censorRegexp) {
      var m = censorRegexp.exec(text);
      while (m && m[0]) {
        var replacement = new Array(m[0].length+1).join('■');
        text = text.replace(new RegExp("\\b" + m[0] + "\\b", "ig"), replacement);
        m = censorRegexp.exec(text);
      }

    }
  }
  return text;
});
<commit_msg>FIX: Censored word match fail if earlier partial match<commit_after>var censorRegexp;

Discourse.Dialect.addPreProcessor(function(text) {
  var censored = Discourse.SiteSettings.censored_words;
  if (censored && censored.length) {
    if (!censorRegexp) {
      var split = censored.split("|");
      if (split && split.length) {
        censorRegexp = new RegExp("\\b(?:" + split.map(function (t) { return "(" + t.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') + ")"; }).join("|") + ")\\b", "ig");
      }
    }

    if (censorRegexp) {
      var m = censorRegexp.exec(text);
      while (m && m[0]) {
        var replacement = new Array(m[0].length+1).join('■');
        text = text.replace(new RegExp("\\b" + m[0] + "\\b", "ig"), replacement);
        m = censorRegexp.exec(text);
      }

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


<commit_before>var React           = require('react/addons');
var SequenceViewer  = require('../../partials/SequenceViewer');
var SequenceActions = require('../../../actions/SequenceActions');
var TwoDee          = require('two-dee');

var loners;       // these are nodes which are not part of lists
var lists;        // this is a list of linked lists.  links go forward in the lists
var _sequence;    // contains the sequence (passed through props)
var _editVal;     // contains details on teh node that is being edited
var _dragging;    // whether the user is moving the mouse around
var _clickedNode; // the node that a user initiated an action on
var _mouseDown = false;
var _arrowSet = false;
var _startNode;
var _startNodeVal;
var _startPoint;
var _endNode;
var _endNodeVal;
var _arrow;

/**
 * initializes the data for the editor
 */
function reset () {
  loners = [];
  lists = [];
  d3.select('g.loners').attr('transform', '');
}

/**
 * updates the sequence
 */
function updateSequence () {
  SequenceActions.update(_sequence);
}

/**
 * adds an operation to the sequence
 */
function addOperation (op) {
  _sequence.operations.push(op);
  updateSequence();
}

/**
 * centers all the groups
 */
function centerGroups (duration) {

  // center!
  d3.selectAll('g.loners, g.list').each(function () {
    var elem = d3.select(this);
    var elems = elem.selectAll('g.node');
    var size = elems[0].length;
    var translateX = -((size - 1) * 100 + 4) / 2;
    var cIndex = 0;
    var tIndex = 0;
    elems
      .selectAll('circle')
      .each(function () {
        var circle = d3.select(this);
        circle
          .transition('center')
          .duration(duration)
          .attr('cx', cIndex * 100);
        cIndex += 1;
      });

    elems
      .selectAll('text')
      .each(function () {
        var text = d3.select(this);
        text
          .transition('center')
          .duration(duration)
          .attr('x', tIndex * 100);
        tIndex += 1;
      });

    elems
      .transition('center')
      .duration(duration)
      .attr('opacity', '1')
      .attr('transform', 'translate(' + translateX + ', 0)');
  });
}

var Operations = {
  initialization: {
    label: 'initialization',
    operation: function (viewport, data, transitionDuration) {

      reset();

      /**
       * Handler for entry of the initial value field
       */
      function createNode (elem) {
        var d3elem = d3.select(elem);
        if (d3elem.classed('open')) {
          d3elem.classed('open', false);
          addOperation({
            type: 'createNode',
            data: {
              value: elem.value
            }
          });
          elem.value = '';
        }
      }

      /**
       * Handler for actions on the initial value field
       */
      d3.select("#initial-value")
        .on('blur', function () {
          createNode(this);
        })
        .on('keydown', function () {
          if (d3.event.keyCode === 13) {
            createNode(this);
          }
        });

      /**
       * Handler for entry of the edit node field
       */
      function editNode (elem) {
        var d3elem = d3.select(elem);
        if (d3elem.classed('open')) {
          d3elem.classed('open', false);
          _editVal.value = elem.value;
          addOperation({
            type: 'editValue',
            data: _editVal
          });
          elem.value = '';
        }
      }

      /**
       * Handler for actions on the edit node field
       */
      d3.select('#edit-node-value')
        .on('blur', function () {
          editNode(this);
        })
        .on('keydown', function () {
          if (d3.event.keyCode === 13) {
            editNode(this);
          }
        });

      /**
       * Append the loners group
       */
      viewport
        .append('g')
        .classed('loners', true);
    }
  },

  createNode: {
    label: 'Create Node',
    operation: function (viewport, data, transitionDuration) {
      var lonersElem = viewport
        .select('g.loners');

      var verticalOffset = 0;
      var horizontalOffset = 0;

      var group = lonersElem
        .append('g');

      group
        .classed('node', true)
        .attr('opacity', '0');

      loners.push(group);
      var index = loners.length - 1;

      group
        .append('circle')
        .attr('fill', '#ccc')
        .attr('cx', function (d, i) {
          return (index * 100) - horizontalOffset;
        })
        .attr('cy', verticalOffset)
        .attr('r', '20')
        .attr('stroke', '#aaa')
        .attr('stroke-width', '0')
        .style('cursor', 'pointer')
        .on('mouseover', function (d) {
          d3.select(this)
            .attr('stroke-width', '2');
          _endNode = d3.select(this).node();
          _endNodeVal = {
            list: 'loners',
            index: index
          };
        })
        .on('mouseout', function (d) {
          d3.select(this)
            .attr('stroke-width', '0');
          _endNode = null;
        })
        .on('mousedown', function (d, i) {
          d3.event.stopPropagation();
          _dragging = false;
          _mouseDown = true;
          _arrowSet = false;
          _startNode = d3.select(this).node();
          _startNodeVal = {
            list: 'loners',
            index: index
          };
        })
        .on('mouseup', function (d, i) {
          _mouseDown = false;
        });

      // add the value of the node
      group
        .append('text')
        .attr('fill', '#aaa')
        .style('font-size', '20px')
        .attr('x', function (d, i) {
          return (index * 100) - horizontalOffset;
        })
        .attr('y', verticalOffset + 8)
        .attr('text-anchor', 'middle')
        .text(function () {
          return data.value || '_';
        })
        .style('cursor', 'pointer')
        .on('mouseover', function (d) {
          d3.select(this)
            .attr('fill', 'black');

          _endNode = d3.select(this.parentNode)
            .select('circle')
            .node();
          _endNodeVal = {
            list: 'loners',
            index: index
          };
        })
        .on('mouseout', function (d) {
          d3.select(this)
            .attr('fill', '#aaa');
        })
        .on('mousedown', function (d, i) {
          d3.event.stopPropagation();
          _dragging = false;
          _mouseDown = true;
          _arrowSet = false;
          _startNode = d3.select(this).node();
          _startNodeVal = {
            list: 'loners',
            index: index
          };
        })
        .on('mouseup', function (d) {
          _mouseDown = false;
          if (!_dragging) {
            var elem = d3.select('#edit-node-value')
              .classed('open', true)
              .style('top', d3.event.clientY)
              .style('left', d3.event.clientX);

            _editVal = {
              list: 'loners',
              index: index
            };

            var node = elem.node();

            node.focus();
            node.value = '';
          }
        });

      centerGroups(transitionDuration);
    }
  },

  editValue: {
    label: 'Edit Value',
    operation: function (viewport, data, transitionDuration) {
      var group;
      if (data.list === 'loners') {
        group = d3.select('g.loners');
      } else {

      }

      var nodeGroup = group.selectAll('g.node')[0][data.index];
      d3.select(nodeGroup)
        .select('text')
        .text(data.value || '_')
        .transition()
        .duration(transitionDuration)
        .attr('fill', '#000')
        .transition()
        .duration(transitionDuration)
        .attr('fill', '#aaa');
    }
  },

  linkNodes: {
    label: 'Create Link',
    operation: function (viewport, data, transitionDuration) {
      var prev = data.prev;
      var next = data.next;
      var prevLength;
      var nodes = [];
      var nextLength;

      // populate lists
      if (prev.list === 'loners') {
        nodes.push(loners[prev.index]);
      }

      if (next.list === 'loners') {
        nodes.push(loners[next.index]);
      }

      // splice lists
      if (prev.list === 'loners') {
        loners.splice(prev.index, 1);
      }
      if (next.list === 'loners') {
        loners.splice(next.index, 1);
      }

      var group = viewport.append('g');
      group.classed('list', 'true');
      lists.push(group);

      var verticalOffset = lists.length * 70;

      nodes.forEach(function (node, index) {
        var currentOffsetLeft = parseInt(node.select('circle').attr('cx'));
        var currentOffsetTop = node.select('circle').attr('cy');
        var translateLeft = - ((nodes.length - 1) * 100 + 40) / 2;
        var nextOffsetLeft = index * 100;
        var nextOffsetTop = verticalOffset;

        var translateX = nextOffsetLeft - currentOffsetLeft;
        var translateY = nextOffsetTop - currentOffsetTop;
        var transform = 'translate(' + translateX + ',' + translateY + ')';

        node
          .transition()
          .duration(transitionDuration)
          .attr('transform', transform)
          .each('end', function () {

            var removed = node.remove();
            removed.attr('transform', '');
            removed.select('circle')
              .attr('cx', nextOffsetLeft)
              .attr('cy', nextOffsetTop);

            removed.select('text')
              .attr('x', nextOffsetLeft)
              .attr('y', nextOffsetTop + 8);

            if (index < nodes.length - 1) {

              var arrow = removed
                .insert('g', ':first-child')
                .classed('arrow', true);

              arrow
                .append('line')
                .attr('x1', nextOffsetLeft)
                .attr('y1', nextOffsetTop)
                .attr('x2', nextOffsetLeft + 72)
                .attr('y2', nextOffsetTop)
                .attr('stroke', '#555')
                .attr('stroke-width', '2')
                .style('opacity', '0')
                .transition()
                .duration(transitionDuration)
                .style('opacity', '1');

              var transformStr = 'translate(' + (nextOffsetLeft + 81) + ',' + nextOffsetTop + ')';

              arrow
                .append('polygon')
                .attr('points', '0,0 -10,-10 -10,10')
                .style('opacity', '0')
                .style('color', '#555')
                .attr('transform', transformStr)
                .transition()
                .duration(transitionDuration)
                .style('opacity', '1');
            }

            group.append(function () {
              return removed.node();
            });

            centerGroups(transitionDuration);
          });
      });

    }
  }
};

module.exports = React.createClass({

  addNode: function () {
    var elem = d3.select('#initial-value')
      .classed('open', true)
      .style('top', window.innerHeight / 2)
      .style('left', window.innerWidth / 2);

    var node = elem.node();
    node.focus();
    node.value = '';
  },

  reset: function () {
    reset();
  },

  onChangeStep: function () {

  },

  onMouseUp: function (e) {
    _mouseDown = false;
    if (_arrow) _arrow.remove();
    if (_endNode && _startNode != _endNode) {
      addOperation({
        type: 'linkNodes',
        data: {
          prev: _startNodeVal,
          next: _endNodeVal,
        }
      });
    }
  },

  onMouseMove: function (e) {
    if (_mouseDown) {
      _dragging = true;

      if (!_arrowSet) {
        var startBox = _startNode.getBBox();
        var startRect = _startNode.getBoundingClientRect();
        var offsetLeft = (startBox.width / 2) + startRect.left;
        var offsetTop = (startBox.height / 2) + startRect.top;
        _startPoint = new TwoDee.Point(offsetLeft, offsetTop);

        _arrow = d3.select('svg')
          .insert('g', ':first-child')
          .classed('arrow', true);

        _arrow
          .append('line')
          .attr('x1', _startPoint.x)
          .attr('y1', _startPoint.y)
          .attr('x2', _startPoint.x)
          .attr('y2', _startPoint.y)
          .attr('stroke', '#555')
          .attr('stroke-width', '2');

        _arrow
          .append('polygon')
          .attr('points', '0,0 -10,10 10,10')
          .style('color', '#555')
          .style('opacity', '0');

        _arrowSet = true;
      }

      // update line
      var destX;
      var destY;
      var line = _arrow.select('line');

      if (_endNode) {
        var circle = d3.select(_endNode);
        var circleNode = circle.node();
        var rect = circleNode.getBoundingClientRect();
        var centerX = rect.left + (rect.width / 2);
        var centerY = rect.top + (rect.height / 2);
        var radius = circle.attr('r');
        var circleCenter = new TwoDee.Point(centerX, centerY);
        var circleObj = new TwoDee.Circle(circleCenter, radius);
        var lineObj = new TwoDee.Line.fromPoints(circleCenter, _startPoint);
        var intersectionPoints = lineObj.intersectionWith(circleObj);
        var closestPoint = _startPoint.closest(intersectionPoints);
        destX = closestPoint.x;
        destY = closestPoint.y;
      } else {
        destX = e.clientX;
        destY = e.clientY;
      }

      if (!isNaN(destX)) {
        line
          .attr('x2', destX)
          .attr('y2', destY);

        // update arrow head
        var polygonTranslate = 'translate(' + destX + ',' + destY + ')';
        var lineX = parseInt(line.attr('x1')) - parseInt(line.attr('x2'));
        var lineY = parseInt(line.attr('y1')) - parseInt(line.attr('y2'));
        var rotatorCuff = Math.atan2(-lineX, lineY);
        var rotatorCuffInDegrees = (180 * rotatorCuff) / Math.PI;
        var rotate = rotatorCuffInDegrees;
        var polygonRotate = 'rotate(' + rotate + ')';

        _arrow
          .select('polygon')
          .attr('transform', polygonTranslate + polygonRotate)
          .style('opacity', '1');
      }
    }
  },

  componentDidMount: function () {
    window.addEventListener('mousemove', this.onMouseMove);
    window.addEventListener('mouseup', this.onMouseUp);
  },

  componentWillUnmount: function () {
    window.removeEventListener('mousemove', this.onMouseMove);
    window.removeEventListener('mouseup', this.onMouseUp);
  },

  render: function () {

    var viewerProps = {
      sequence:   this.props,
      structure:  Operations,
      options: [
        { label: '+', action: this.addNode }
      ],
      onChangeStep: this.onChangeStep,
      reset: this.reset
    };

    _sequence = this.props;

    return (
      <div className="singly-linked-list-editor">
        <input className="value-input" id="initial-value" placeholder="Enter initial node value" />
        <input className="value-input" id="edit-node-value" placeholder="Edit node value" />
        <SequenceViewer {...viewerProps} />
      </div>
    );
  }
});<commit_msg>fix bug creating links with self<commit_after>var React           = require('react/addons');
var SequenceViewer  = require('../../partials/SequenceViewer');
var SequenceActions = require('../../../actions/SequenceActions');
var TwoDee          = require('two-dee');

var loners;       // these are nodes which are not part of lists
var lists;        // this is a list of linked lists.  links go forward in the lists
var _sequence;    // contains the sequence (passed through props)
var _editVal;     // contains details on teh node that is being edited
var _dragging;    // whether the user is moving the mouse around
var _clickedNode; // the node that a user initiated an action on
var _mouseDown = false;
var _arrowSet = false;
var _startNode;
var _startNodeVal;
var _startPoint;
var _endNode;
var _endNodeVal;
var _arrow;

/**
 * initializes the data for the editor
 */
function reset () {
  loners = [];
  lists = [];
  d3.select('g.loners').attr('transform', '');
}

/**
 * updates the sequence
 */
function updateSequence () {
  SequenceActions.update(_sequence);
}

/**
 * adds an operation to the sequence
 */
function addOperation (op) {
  _sequence.operations.push(op);
  updateSequence();
}

/**
 * centers all the groups
 */
function centerGroups (duration) {

  // center!
  d3.selectAll('g.loners, g.list').each(function () {
    var elem = d3.select(this);
    var elems = elem.selectAll('g.node');
    var size = elems[0].length;
    var translateX = -((size - 1) * 100 + 4) / 2;
    var cIndex = 0;
    var tIndex = 0;
    elems
      .selectAll('circle')
      .each(function () {
        var circle = d3.select(this);
        circle
          .transition('center')
          .duration(duration)
          .attr('cx', cIndex * 100);
        cIndex += 1;
      });

    elems
      .selectAll('text')
      .each(function () {
        var text = d3.select(this);
        text
          .transition('center')
          .duration(duration)
          .attr('x', tIndex * 100);
        tIndex += 1;
      });

    elems
      .transition('center')
      .duration(duration)
      .attr('opacity', '1')
      .attr('transform', 'translate(' + translateX + ', 0)');
  });
}

var Operations = {
  initialization: {
    label: 'initialization',
    operation: function (viewport, data, transitionDuration) {

      reset();

      /**
       * Handler for entry of the initial value field
       */
      function createNode (elem) {
        var d3elem = d3.select(elem);
        if (d3elem.classed('open')) {
          d3elem.classed('open', false);
          addOperation({
            type: 'createNode',
            data: {
              value: elem.value
            }
          });
          elem.value = '';
        }
      }

      /**
       * Handler for actions on the initial value field
       */
      d3.select("#initial-value")
        .on('blur', function () {
          createNode(this);
        })
        .on('keydown', function () {
          if (d3.event.keyCode === 13) {
            createNode(this);
          }
        });

      /**
       * Handler for entry of the edit node field
       */
      function editNode (elem) {
        var d3elem = d3.select(elem);
        if (d3elem.classed('open')) {
          d3elem.classed('open', false);
          _editVal.value = elem.value;
          addOperation({
            type: 'editValue',
            data: _editVal
          });
          elem.value = '';
        }
      }

      /**
       * Handler for actions on the edit node field
       */
      d3.select('#edit-node-value')
        .on('blur', function () {
          editNode(this);
        })
        .on('keydown', function () {
          if (d3.event.keyCode === 13) {
            editNode(this);
          }
        });

      /**
       * Append the loners group
       */
      viewport
        .append('g')
        .classed('loners', true);
    }
  },

  createNode: {
    label: 'Create Node',
    operation: function (viewport, data, transitionDuration) {
      var lonersElem = viewport
        .select('g.loners');

      var verticalOffset = 0;
      var horizontalOffset = 0;

      var group = lonersElem
        .append('g');

      group
        .classed('node', true)
        .attr('opacity', '0');

      loners.push(group);
      var index = loners.length - 1;

      group
        .append('circle')
        .attr('fill', '#ccc')
        .attr('cx', function (d, i) {
          return (index * 100) - horizontalOffset;
        })
        .attr('cy', verticalOffset)
        .attr('r', '20')
        .attr('stroke', '#aaa')
        .attr('stroke-width', '0')
        .style('cursor', 'pointer')
        .on('mouseover', function (d) {
          d3.select(this)
            .attr('stroke-width', '2');
          _endNode = this.parentNode;
          _endNodeVal = {
            list: 'loners',
            index: index
          };
        })
        .on('mouseout', function (d) {
          d3.select(this)
            .attr('stroke-width', '0');
          _endNode = null;
        })
        .on('mousedown', function (d, i) {
          d3.event.stopPropagation();
          _dragging = false;
          _mouseDown = true;
          _arrowSet = false;
          _startNode = this.parentNode;
          _startNodeVal = {
            list: 'loners',
            index: index
          };
        })
        .on('mouseup', function (d, i) {
          _mouseDown = false;
        });

      // add the value of the node
      group
        .append('text')
        .attr('fill', '#aaa')
        .style('font-size', '20px')
        .attr('x', function (d, i) {
          return (index * 100) - horizontalOffset;
        })
        .attr('y', verticalOffset + 8)
        .attr('text-anchor', 'middle')
        .text(function () {
          return data.value || '_';
        })
        .style('cursor', 'pointer')
        .on('mouseover', function (d) {
          d3.select(this)
            .attr('fill', 'black');

          _endNode = this.parentNode;
          _endNodeVal = {
            list: 'loners',
            index: index
          };
        })
        .on('mouseout', function (d) {
          d3.select(this)
            .attr('fill', '#aaa');
        })
        .on('mousedown', function (d, i) {
          d3.event.stopPropagation();
          _dragging = false;
          _mouseDown = true;
          _arrowSet = false;
          _startNode = this.parentNode;
          _startNodeVal = {
            list: 'loners',
            index: index
          };
        })
        .on('mouseup', function (d) {
          _mouseDown = false;
          if (!_dragging) {
            var elem = d3.select('#edit-node-value')
              .classed('open', true)
              .style('top', d3.event.clientY)
              .style('left', d3.event.clientX);

            _editVal = {
              list: 'loners',
              index: index
            };

            var node = elem.node();

            node.focus();
            node.value = '';
          }
        });

      centerGroups(transitionDuration);
    }
  },

  editValue: {
    label: 'Edit Value',
    operation: function (viewport, data, transitionDuration) {
      var group;
      if (data.list === 'loners') {
        group = d3.select('g.loners');
      } else {

      }

      var nodeGroup = group.selectAll('g.node')[0][data.index];
      d3.select(nodeGroup)
        .select('text')
        .text(data.value || '_')
        .transition()
        .duration(transitionDuration)
        .attr('fill', '#000')
        .transition()
        .duration(transitionDuration)
        .attr('fill', '#aaa');
    }
  },

  linkNodes: {
    label: 'Create Link',
    operation: function (viewport, data, transitionDuration) {
      var prev = data.prev;
      var next = data.next;
      var prevLength;
      var nodes = [];
      var nextLength;

      // populate lists
      if (prev.list === 'loners') {
        nodes.push(loners[prev.index]);
      }

      if (next.list === 'loners') {
        nodes.push(loners[next.index]);
      }

      // splice lists
      if (prev.list === 'loners') {
        loners.splice(prev.index, 1);
      }
      if (next.list === 'loners') {
        loners.splice(next.index, 1);
      }

      var group = viewport.append('g');
      group.classed('list', 'true');
      lists.push(group);

      var verticalOffset = lists.length * 70;

      nodes.forEach(function (node, index) {
        var currentOffsetLeft = parseInt(node.select('circle').attr('cx'));
        var currentOffsetTop = node.select('circle').attr('cy');
        var translateLeft = - ((nodes.length - 1) * 100 + 40) / 2;
        var nextOffsetLeft = index * 100;
        var nextOffsetTop = verticalOffset;

        var translateX = nextOffsetLeft - currentOffsetLeft;
        var translateY = nextOffsetTop - currentOffsetTop;
        var transform = 'translate(' + translateX + ',' + translateY + ')';

        node
          .transition()
          .duration(transitionDuration)
          .attr('transform', transform)
          .each('end', function () {

            var removed = node.remove();
            removed.attr('transform', '');
            removed.select('circle')
              .attr('cx', nextOffsetLeft)
              .attr('cy', nextOffsetTop);

            removed.select('text')
              .attr('x', nextOffsetLeft)
              .attr('y', nextOffsetTop + 8);

            if (index < nodes.length - 1) {

              var arrow = removed
                .insert('g', ':first-child')
                .classed('arrow', true);

              arrow
                .append('line')
                .attr('x1', nextOffsetLeft)
                .attr('y1', nextOffsetTop)
                .attr('x2', nextOffsetLeft + 72)
                .attr('y2', nextOffsetTop)
                .attr('stroke', '#555')
                .attr('stroke-width', '2')
                .style('opacity', '0')
                .transition()
                .duration(transitionDuration)
                .style('opacity', '1');

              var transformStr = 'translate(' + (nextOffsetLeft + 81) + ',' + nextOffsetTop + ')';

              arrow
                .append('polygon')
                .attr('points', '0,0 -10,-10 -10,10')
                .style('opacity', '0')
                .style('color', '#555')
                .attr('transform', transformStr)
                .transition()
                .duration(transitionDuration)
                .style('opacity', '1');
            }

            group.append(function () {
              return removed.node();
            });

            centerGroups(transitionDuration);
          });
      });

    }
  }
};

module.exports = React.createClass({

  addNode: function () {
    var elem = d3.select('#initial-value')
      .classed('open', true)
      .style('top', window.innerHeight / 2)
      .style('left', window.innerWidth / 2);

    var node = elem.node();
    node.focus();
    node.value = '';
  },

  reset: function () {
    reset();
  },

  onChangeStep: function () {

  },

  onMouseUp: function (e) {
    _mouseDown = false;
    if (_arrow) _arrow.remove();
    if (_endNode && _startNode != _endNode) {
      addOperation({
        type: 'linkNodes',
        data: {
          prev: _startNodeVal,
          next: _endNodeVal,
        }
      });
    }
  },

  onMouseMove: function (e) {
    if (_mouseDown) {
      _dragging = true;

      if (!_arrowSet) {
        var startBox = _startNode.getBBox();
        var startRect = _startNode.getBoundingClientRect();
        var offsetLeft = (startBox.width / 2) + startRect.left;
        var offsetTop = (startBox.height / 2) + startRect.top;
        _startPoint = new TwoDee.Point(offsetLeft, offsetTop);

        _arrow = d3.select('svg')
          .insert('g', ':first-child')
          .classed('arrow', true);

        _arrow
          .append('line')
          .attr('x1', _startPoint.x)
          .attr('y1', _startPoint.y)
          .attr('x2', _startPoint.x)
          .attr('y2', _startPoint.y)
          .attr('stroke', '#555')
          .attr('stroke-width', '2');

        _arrow
          .append('polygon')
          .attr('points', '0,0 -10,10 10,10')
          .style('color', '#555')
          .style('opacity', '0');

        _arrowSet = true;
      }

      // update line
      var destX;
      var destY;
      var line = _arrow.select('line');

      if (_endNode) {
        var circle = d3.select(_endNode).select('circle');
        var circleNode = circle.node();
        var rect = circleNode.getBoundingClientRect();
        var centerX = rect.left + (rect.width / 2);
        var centerY = rect.top + (rect.height / 2);
        var radius = circle.attr('r');
        var circleCenter = new TwoDee.Point(centerX, centerY);
        var circleObj = new TwoDee.Circle(circleCenter, radius);
        var lineObj = new TwoDee.Line.fromPoints(circleCenter, _startPoint);
        var intersectionPoints = lineObj.intersectionWith(circleObj);
        var closestPoint = _startPoint.closest(intersectionPoints);
        destX = closestPoint.x;
        destY = closestPoint.y;
      } else {
        destX = e.clientX;
        destY = e.clientY;
      }

      if (!isNaN(destX)) {
        line
          .attr('x2', destX)
          .attr('y2', destY);

        // update arrow head
        var polygonTranslate = 'translate(' + destX + ',' + destY + ')';
        var lineX = parseInt(line.attr('x1')) - parseInt(line.attr('x2'));
        var lineY = parseInt(line.attr('y1')) - parseInt(line.attr('y2'));
        var rotatorCuff = Math.atan2(-lineX, lineY);
        var rotatorCuffInDegrees = (180 * rotatorCuff) / Math.PI;
        var rotate = rotatorCuffInDegrees;
        var polygonRotate = 'rotate(' + rotate + ')';

        _arrow
          .select('polygon')
          .attr('transform', polygonTranslate + polygonRotate)
          .style('opacity', '1');
      }
    }
  },

  componentDidMount: function () {
    window.addEventListener('mousemove', this.onMouseMove);
    window.addEventListener('mouseup', this.onMouseUp);
  },

  componentWillUnmount: function () {
    window.removeEventListener('mousemove', this.onMouseMove);
    window.removeEventListener('mouseup', this.onMouseUp);
  },

  render: function () {

    var viewerProps = {
      sequence:   this.props,
      structure:  Operations,
      options: [
        { label: '+', action: this.addNode }
      ],
      onChangeStep: this.onChangeStep,
      reset: this.reset
    };

    _sequence = this.props;

    return (
      <div className="singly-linked-list-editor">
        <input className="value-input" id="initial-value" placeholder="Enter initial node value" />
        <input className="value-input" id="edit-node-value" placeholder="Edit node value" />
        <SequenceViewer {...viewerProps} />
      </div>
    );
  }
});<|endoftext|>


<commit_before>'use strict';

angular.module('deckApp.pipelines.stage.bake')
  .config(function(pipelineConfigProvider) {
    pipelineConfigProvider.registerStage({
      label: 'Bake',
      description: 'Bakes an image in the specified region',
      key: 'bake',
      controller: 'BakeStageCtrl',
      controllerAs: 'bakeStageCtrl',
      templateUrl: 'scripts/modules/pipelines/config/stages/bake/bakeStage.html',
      executionDetailsUrl: 'scripts/modules/pipelines/config/stages/bake/bakeExecutionDetails.html',
      validators: [
        {
          type: 'requiredField',
          fieldName: 'package',
          message: 'Package is a required field on bake stages.',
        },
      ],
    });
  })
  .controller('BakeStageCtrl', function($scope, stage, bakeryService, $q, authenticationService, accountService) {
    var ctrl = this;

    $scope.stage = stage;

    if (!$scope.stage.user) {
      $scope.stage.user = authenticationService.getAuthenticatedUser().name;
    }

    $scope.viewState = {
      loading: true,
      providerSelected: !!$scope.stage.cloudProviderType,
      providerSelectionRequired: true,
    };

    accountService.listProviders().then(function(providers) {
      if (providers.length > 1) {
        $scope.viewState.providerSelectionRequired = true;
        $scope.viewState.loading = false;
      } else {
        $scope.viewState.providerSelectionRequired = false;
      }
      ctrl.providerSelected();
    });

    this.providerSelected = function() {
      if (!$scope.stage.cloudProviderType && $scope.viewState.providerSelectionRequired) {
        return;
      }
      $scope.viewState.providerSelected = true;
      $q.all({
        regions: bakeryService.getRegions($scope.stage.cloudProviderType),
        baseOsOptions: bakeryService.getBaseOsOptions(),
        baseLabelOptions: bakeryService.getBaseLabelOptions(),
        vmTypes: bakeryService.getVmTypes(),
        storeTypes: bakeryService.getStoreTypes(),
      }).then(function(results) {
        if (!$scope.stage.cloudProviderType || $scope.stage.cloudProviderType === 'aws') {
          $scope.regions = results.regions;
        } else {
          $scope.regions  = ["global"];
        }
        if ($scope.regions.length === 1) {
          $scope.stage.region = $scope.regions[0];
        } else if ($scope.regions.indexOf($scope.stage.region) === -1) {
          delete $scope.stage.region;
        }
        $scope.baseOsOptions = results.baseOsOptions;
        $scope.vmTypes = results.vmTypes;
        $scope.baseLabelOptions = results.baseLabelOptions;
        $scope.storeTypes = results.storeTypes;

        if (!$scope.stage.baseOs && $scope.baseOsOptions && $scope.baseOsOptions.length) {
          $scope.stage.baseOs = $scope.baseOsOptions[0];
        }
        if (!$scope.stage.baseLabel && $scope.baseLabelOptions && $scope.baseLabelOptions.length) {
          $scope.stage.baseLabel = $scope.baseLabelOptions[0];
        }
        if (!$scope.stage.vmType && $scope.vmTypes && $scope.vmTypes.length) {
          $scope.stage.vmType = $scope.vmTypes[0];
        }
        if (!$scope.stage.storeType && $scope.storeTypes && $scope.storeTypes.length) {
          $scope.stage.storeType = $scope.storeTypes[0];
        }
        $scope.viewState.loading = false;
      });
    };

    $scope.$watch('stage.cloudProviderType', this.providerSelected);

  });
<commit_msg>use single quotes<commit_after>'use strict';

angular.module('deckApp.pipelines.stage.bake')
  .config(function(pipelineConfigProvider) {
    pipelineConfigProvider.registerStage({
      label: 'Bake',
      description: 'Bakes an image in the specified region',
      key: 'bake',
      controller: 'BakeStageCtrl',
      controllerAs: 'bakeStageCtrl',
      templateUrl: 'scripts/modules/pipelines/config/stages/bake/bakeStage.html',
      executionDetailsUrl: 'scripts/modules/pipelines/config/stages/bake/bakeExecutionDetails.html',
      validators: [
        {
          type: 'requiredField',
          fieldName: 'package',
          message: 'Package is a required field on bake stages.',
        },
      ],
    });
  })
  .controller('BakeStageCtrl', function($scope, stage, bakeryService, $q, authenticationService, accountService) {
    var ctrl = this;

    $scope.stage = stage;

    if (!$scope.stage.user) {
      $scope.stage.user = authenticationService.getAuthenticatedUser().name;
    }

    $scope.viewState = {
      loading: true,
      providerSelected: !!$scope.stage.cloudProviderType,
      providerSelectionRequired: true,
    };

    accountService.listProviders().then(function(providers) {
      if (providers.length > 1) {
        $scope.viewState.providerSelectionRequired = true;
        $scope.viewState.loading = false;
      } else {
        $scope.viewState.providerSelectionRequired = false;
      }
      ctrl.providerSelected();
    });

    this.providerSelected = function() {
      if (!$scope.stage.cloudProviderType && $scope.viewState.providerSelectionRequired) {
        return;
      }
      $scope.viewState.providerSelected = true;
      $q.all({
        regions: bakeryService.getRegions($scope.stage.cloudProviderType),
        baseOsOptions: bakeryService.getBaseOsOptions(),
        baseLabelOptions: bakeryService.getBaseLabelOptions(),
        vmTypes: bakeryService.getVmTypes(),
        storeTypes: bakeryService.getStoreTypes(),
      }).then(function(results) {
        if (!$scope.stage.cloudProviderType || $scope.stage.cloudProviderType === 'aws') {
          $scope.regions = results.regions;
        } else {
          $scope.regions  = ['global'];
        }
        if ($scope.regions.length === 1) {
          $scope.stage.region = $scope.regions[0];
        } else if ($scope.regions.indexOf($scope.stage.region) === -1) {
          delete $scope.stage.region;
        }
        $scope.baseOsOptions = results.baseOsOptions;
        $scope.vmTypes = results.vmTypes;
        $scope.baseLabelOptions = results.baseLabelOptions;
        $scope.storeTypes = results.storeTypes;

        if (!$scope.stage.baseOs && $scope.baseOsOptions && $scope.baseOsOptions.length) {
          $scope.stage.baseOs = $scope.baseOsOptions[0];
        }
        if (!$scope.stage.baseLabel && $scope.baseLabelOptions && $scope.baseLabelOptions.length) {
          $scope.stage.baseLabel = $scope.baseLabelOptions[0];
        }
        if (!$scope.stage.vmType && $scope.vmTypes && $scope.vmTypes.length) {
          $scope.stage.vmType = $scope.vmTypes[0];
        }
        if (!$scope.stage.storeType && $scope.storeTypes && $scope.storeTypes.length) {
          $scope.stage.storeType = $scope.storeTypes[0];
        }
        $scope.viewState.loading = false;
      });
    };

    $scope.$watch('stage.cloudProviderType', this.providerSelected);

  });
<|endoftext|>


<commit_before>define(['app'], function (app) {
	app.directive('homeCountryDashboard', function($http){
		return{
			restrict: 'EAC',
			replace:true,
			templateUrl: '/app/views/directives/home-country-dashboard-directive.html',
			link : function($scope, $element){
				//$element.find("[data-toggle='tooltip']").tooltip({trigger:'hover'});
			},
			controller: ['$scope', '$filter','schemaTypes', 'realm', '$q', 'underscore', function($scope, $filter, schemaTypes, realm, $q, _){

				$scope.wellChanged = function(facet){
					$scope.currentFacet = facet;
				}
                $scope.options  = {
                      countries		: function() {
                        return $http.get("/api/v2013/thesaurus/domains/countries/terms", { cache: true }).then(function(o){
                          var countries = $filter("orderBy")(o.data, "name");
                          _.each(countries, function(element) {
                            element.__value = element.name;
                          });
                          return countries;
                        });
                      }
                }
                $scope.genericFilter = function($query, items) {
                  var matchedOptions = [];
                  for(var i=0; i!=items.length; ++i)
                    if(items[i].__value.toLowerCase().indexOf($query.toLowerCase()) !== -1)
                      matchedOptions.push(items[i]);

                  return matchedOptions;
                };

                $scope.genericMapping = function(item) {
                  return {identifier: item.identifier};
                };
                $scope.$watch('document.countryStats', function(newValue){
                    //console.log(newValue)
                    if(newValue && newValue.identifier){
                        $scope.loadFacets(newValue.identifier);
                    }
                    else if(newValue ==null)
                        $scope.loadFacets();
                })
    			$scope.loadFacets = function(country){

                    var countryQuery = '';
                    $scope.loadingFacets = true;

                    if(country){
                        countryQuery = ' AND government_s:' + country;
                    }

					var queryFacetsVLRParameters = {
                        'q': '(realm_ss:' + realm.value.toLowerCase() + ' or realm_ss:absch) AND NOT version_s:* AND (schema_s:resource)',
                        'fl': '', 		//fields for results.
                        'wt': 'json',
                        'rows': 0,		//limit
                        'facet': true,	//get counts back
						'facet.query': '(realm_ss:' + realm.value.toLowerCase() + ' or realm_ss:absch) AND NOT version_s:* AND (schema_s:resource)',
                        'facet.field': ['schema_s'],
                        'facet.limit': 512
                    };
                    var queryFacetsVLR = $http.get('/api/v2013/index/select', { params: queryFacetsVLRParameters })

    				var queryFacetsParameters = {
                        'q': 'realm_ss:' + realm.value.toLowerCase() + ' AND NOT version_s:* AND (schema_s:'
                                          + schemaTypes.join(' OR schema_s:') + ') '+ countryQuery ,
                        'fl': '', 		//fields for results.
                        'wt': 'json',
                        'rows': 0,		//limit
                        'facet': true,	//get counts back
                        'facet.pivot': 'government_s,schema_s',
                        'facet.limit': 512
                    };

                    var queryFacets = $http.get('/api/v2013/index/select', { params: queryFacetsParameters });
					var countries = $http.get('/api/v2013/countries');

					$q.all([queryFacets, queryFacetsVLR, countries]).then(function (results) {
						//console.log(results)
                        var tempFacets = {};
                        _.each(results[0].data.facet_counts.facet_pivot['government_s,schema_s'], function(data){
                            _.each(data.pivot, function(facets){
                                tempFacets[facets.value] = {"facetCount" : (tempFacets[facets.value] ? tempFacets[facets.value].facetCount : 0) + facets.count,
                                                "countryCount" : (tempFacets[facets.value] ? tempFacets[facets.value].countryCount : 0) + 1,
												"id": getSequence(facets.value)};
                            });

                        });

						var resourceFacets = results[1].data.facet_counts.facet_fields.schema_s;
						for (var i = 0; i < resourceFacets.length; i += 2) {
		                    var facet = resourceFacets[i];
							if(facet == 'resource')
		                    	tempFacets['resource'] = {"facetCount" : resourceFacets[i + 1],
								"id" : getSequence('resource')};
		                }

						var ratificationCount=0;
						_.each( results[2].data, function(country){
							if (   country.treaties.XXVII8b.instrument == "ratification"
			                	|| country.treaties.XXVII8b.instrument == "accession"
			                	|| country.treaties.XXVII8b.instrument == "acceptance"
			                  	|| country.treaties.XXVII8b.instrument == "approval"
			                  	|| country.code == 'EU') {

			                    	ratificationCount++;
			                }
						});
						tempFacets['RAT'] = {"facetCount" : ratificationCount,
								"id" : getSequence('ratification')};

                        $scope.commonFormatFacets = _.sortBy(_.pairs(tempFacets), function(format){ return format[1].id});
                        $scope.loadingFacets = false;

                    },function (error) {$scope.commonFormatFacets={};console.log(error); } )
                    .finally(function(){$scope.loadingFacets = false;})

    			}

				function getSequence(format){
					switch(format.toLowerCase()){

						case  'ratification':
							return 1;
						case  'authority':
							return 2;
						case  'measure':
							return 3;
						case  'abscheckpoint':
							return 4;
						case  'abspermit':
							return 5;
						case  'abscheckpointcommunique':
							return 6;
						case 'database':
							return 7;
						case  'resource':
							return 8;
					}
				}
                //$scope.loadFacets();

			}]
		};

	});
});
<commit_msg>remove default country filter from select permit popup<commit_after>define(['app'], function (app) {
	app.directive('homeCountryDashboard', function($http){
		return{
			restrict: 'EAC',
			replace:true,
			templateUrl: '/app/views/directives/home-country-dashboard-directive.html',
			link : function($scope, $element){
				//$element.find("[data-toggle='tooltip']").tooltip({trigger:'hover'});
			},
			controller: ['$scope', '$filter','schemaTypes', 'realm', '$q', 'underscore', function($scope, $filter, schemaTypes, realm, $q, _){

				$scope.wellChanged = function(facet){
					$scope.currentFacet = facet;
				}
                $scope.options  = {
                      countries		: function() {
                        return $http.get("/api/v2013/thesaurus/domains/countries/terms", { cache: true }).then(function(o){
                          var countries = $filter("orderBy")(o.data, "name");
                          _.each(countries, function(element) {
                            element.__value = element.name;
                          });
                          return countries;
                        });
                      }
                }
                $scope.genericFilter = function($query, items) {
                  var matchedOptions = [];
                  for(var i=0; i!=items.length; ++i)
                    if(items[i].__value.toLowerCase().indexOf($query.toLowerCase()) !== -1)
                      matchedOptions.push(items[i]);

                  return matchedOptions;
                };

                $scope.genericMapping = function(item) {
                  return {identifier: item.identifier};
                };
                $scope.$watch('document.countryStats', function(newValue){
                    //console.log(newValue)
                    if(newValue && newValue.identifier){
                        $scope.loadFacets(newValue.identifier);
                    }
                    else if(newValue ==null)
                        $scope.loadFacets();
                })
    			$scope.loadFacets = function(country){

                    var countryQuery = '';
                    $scope.loadingFacets = true;

                    // if(country){
                    //     countryQuery = ' AND government_s:' + country;
                    // }

					var queryFacetsVLRParameters = {
                        'q': '(realm_ss:' + realm.value.toLowerCase() + ' or realm_ss:absch) AND NOT version_s:* AND (schema_s:resource)',
                        'fl': '', 		//fields for results.
                        'wt': 'json',
                        'rows': 0,		//limit
                        'facet': true,	//get counts back
						'facet.query': '(realm_ss:' + realm.value.toLowerCase() + ' or realm_ss:absch) AND NOT version_s:* AND (schema_s:resource)',
                        'facet.field': ['schema_s'],
                        'facet.limit': 512
                    };
                    var queryFacetsVLR = $http.get('/api/v2013/index/select', { params: queryFacetsVLRParameters })

    				var queryFacetsParameters = {
                        'q': 'realm_ss:' + realm.value.toLowerCase() + ' AND NOT version_s:* AND (schema_s:'
                                          + schemaTypes.join(' OR schema_s:') + ') '+ countryQuery ,
                        'fl': '', 		//fields for results.
                        'wt': 'json',
                        'rows': 0,		//limit
                        'facet': true,	//get counts back
                        'facet.pivot': 'government_s,schema_s',
                        'facet.limit': 512
                    };

                    var queryFacets = $http.get('/api/v2013/index/select', { params: queryFacetsParameters });
					var countries = $http.get('/api/v2013/countries');

					$q.all([queryFacets, queryFacetsVLR, countries]).then(function (results) {
						//console.log(results)
                        var tempFacets = {};
                        _.each(results[0].data.facet_counts.facet_pivot['government_s,schema_s'], function(data){
                            _.each(data.pivot, function(facets){
                                tempFacets[facets.value] = {"facetCount" : (tempFacets[facets.value] ? tempFacets[facets.value].facetCount : 0) + facets.count,
                                                "countryCount" : (tempFacets[facets.value] ? tempFacets[facets.value].countryCount : 0) + 1,
												"id": getSequence(facets.value)};
                            });

                        });

						var resourceFacets = results[1].data.facet_counts.facet_fields.schema_s;
						for (var i = 0; i < resourceFacets.length; i += 2) {
		                    var facet = resourceFacets[i];
							if(facet == 'resource')
		                    	tempFacets['resource'] = {"facetCount" : resourceFacets[i + 1],
								"id" : getSequence('resource')};
		                }

						var ratificationCount=0;
						_.each( results[2].data, function(country){
							if (   country.treaties.XXVII8b.instrument == "ratification"
			                	|| country.treaties.XXVII8b.instrument == "accession"
			                	|| country.treaties.XXVII8b.instrument == "acceptance"
			                  	|| country.treaties.XXVII8b.instrument == "approval"
			                  	|| country.code == 'EU') {

			                    	ratificationCount++;
			                }
						});
						tempFacets['RAT'] = {"facetCount" : ratificationCount,
								"id" : getSequence('ratification')};

                        $scope.commonFormatFacets = _.sortBy(_.pairs(tempFacets), function(format){ return format[1].id});
                        $scope.loadingFacets = false;

                    },function (error) {$scope.commonFormatFacets={};console.log(error); } )
                    .finally(function(){$scope.loadingFacets = false;})

    			}

				function getSequence(format){
					switch(format.toLowerCase()){

						case  'ratification':
							return 1;
						case  'authority':
							return 2;
						case  'measure':
							return 3;
						case  'abscheckpoint':
							return 4;
						case  'abspermit':
							return 5;
						case  'abscheckpointcommunique':
							return 6;
						case 'database':
							return 7;
						case  'resource':
							return 8;
					}
				}
                //$scope.loadFacets();

			}]
		};

	});
});
<|endoftext|>


<commit_before>/*
 * Route controller.
 */

/*
 * Module dependencies.
 */
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var express = require('express');
var async = require('async');

/**
 * Main route controller class.
 *
 * @param {Application} application Application.
 * @param {Object} settings Application settings object.
 * @class RouteController
 */
var RouteController = module.exports = function(application, settings) {
  this.application = application;
  this.settings = settings;

  // Prepare route register params.
  var params = [this.settings.path];

  // Prepare the middleware, if available.
  if ('middleware' in this.settings) {
    params.push(this.settings.middleware);
  }

  // Prepare the route handler.
  var self = this;
  params.push(function(request, response) {
    self.handle(request, response);
  });

  // Register on express.
  var app = this.application.application;
  var method = this.settings.method || 'all';
  app[method].apply(app, params);
};

/**
 * Handle a request.
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object
 */
RouteController.prototype.handle = function(request, response) {
  var self = this;
  var settings = this.settings;

  this.access(request, response, function(error, allow) {
    if (error) {
      return RouteController.error(request, response, error);
    }

    if (allow !== true) {
      // Access denied.
      return RouteController.forbidden(request, response);
    }

    if (settings.content) {
      return RouteController.respond(request, response, settings.content);
    }

    if (settings.callback) {
      return settings.callback(request, response, function(error, content, code) {
        if (error) {
          return RouteController.error(request, response, error);
        }

        RouteController.respond(request, response, content, code);
      });
    }

    // If there's no content nor a callback, return 404 error.
    RouteController.notFound(request, response);
  });
};

/**
 * Access check.
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object.
 * @param {Function} callaback Function called with the access check results.
 */
RouteController.prototype.access = function(request, response, callback) {
  var settings = this.settings;

  // If this is an object, do access check and other mandatory stuff.
  if (settings.access) {
    if (typeof settings.access === 'boolean') {
      // Access check is a string/permission key.
      return callback(null, settings.access);
    }
    else if (typeof settings.access === 'string') {
      return this.application.access(request, settings.access, callback);
    }
    else if (typeof settings.access === 'function') {
      // Access check is a callback.
      return settings.access(request, response, callback);
    }
  }

  // Default to false. Deny access.
  return callback(null, false);
};

/**
 * Respond to a request.
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object.
 * @param {Object|String} content Content to send.
 * @param {Number} [code] HTTP status code.
 */
RouteController.respond = function(request, response, content, code) {
  // Default to 200 (success).
  var code = code || 200;

  var payload = {
    status: {
      code: code
    }
  };

  if (content) {
    payload.data = content;
  }

  response.status(code).send(payload);
};

/**
 * Respond to a request with a "server error".
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object.
 */
RouteController.error = function(request, response, error) {
  RouteController.respond(request, response, {
    title: 'Server error',
    description: "The server couldn't process the request.",
    error: error.toString()
  }, 500);
};

/**
 * Respond to a request with a "page not found" error.
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object.
 */
RouteController.notFound = function(request, response) {
  RouteController.respond(request, response, {
    title: 'Page not found',
    description: "The page you're looking for wasn't found."
  }, 404);
};

/**
 * Respond to a request with a "forbidden" error.
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object.
 */
RouteController.forbidden = function(request, response) {
  RouteController.respond(request, response, {
    title: 'Forbidden',
    description: "You don't have permission to access this page."
  }, 403);
};
<commit_msg>Introducing response() hook to provide a last opportunity for extensions to alter the response before it's sent to the client. Fixes #133.<commit_after>/*
 * Route controller.
 */

/*
 * Module dependencies.
 */
var util = require('util');
var EventEmitter = require('events').EventEmitter;
var express = require('express');
var async = require('async');

/**
 * Main route controller class.
 *
 * @param {Application} application Application.
 * @param {Object} settings Application settings object.
 * @class RouteController
 */
var RouteController = module.exports = function(application, settings) {
  this.application = application;
  this.settings = settings;

  // Prepare route register params.
  var params = [this.settings.path];

  // Prepare the middleware, if available.
  if ('middleware' in this.settings) {
    params.push(this.settings.middleware);
  }

  // Prepare the route handler.
  var self = this;
  params.push(function(request, response) {
    self.handle(request, response);
  });

  // Register on express.
  var app = this.application.application;
  var method = this.settings.method || 'all';
  app[method].apply(app, params);
};

/**
 * Handle a request.
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object
 */
RouteController.prototype.handle = function(request, response) {
  var self = this;
  var settings = this.settings;

  // Response decorator function, used to allow extensions to alter the response
  // for content responses via response() hook.
  var responseDecorator = function(payload, request, response, callback) {
    // Run response() hook on all extensions.
    self.application.invoke('response', payload, request, response, callback);
  };

  this.access(request, response, function(error, allow) {
    if (error) {
      return RouteController.error(request, response, error);
    }

    if (allow !== true) {
      // Access denied.
      return RouteController.forbidden(request, response);
    }

    if (settings.content) {
      return RouteController.respond(request, response, settings.content, responseDecorator);
    }

    if (settings.callback) {
      return settings.callback(request, response, function(error, content, code) {
        if (error) {
          return RouteController.error(request, response, error);
        }

        RouteController.respond(request, response, content, code, responseDecorator);
      });
    }

    // If there's no content nor a callback, return 404 error.
    RouteController.notFound(request, response);
  });
};

/**
 * Access check.
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object.
 * @param {Function} callaback Function called with the access check results.
 */
RouteController.prototype.access = function(request, response, callback) {
  var settings = this.settings;

  // If this is an object, do access check and other mandatory stuff.
  if (settings.access) {
    if (typeof settings.access === 'boolean') {
      // Access check is a string/permission key.
      return callback(null, settings.access);
    }
    else if (typeof settings.access === 'string') {
      return this.application.access(request, settings.access, callback);
    }
    else if (typeof settings.access === 'function') {
      // Access check is a callback.
      return settings.access(request, response, callback);
    }
  }

  // Default to false. Deny access.
  return callback(null, false);
};

/**
 * Respond to a request.
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object.
 * @param {Object|String} content Content to send.
 * @param {Number} [code] HTTP status code.
 * @param {Function} [decorator] A function to run on the response data before
 * sending it.
 */
RouteController.respond = function(request, response, content, code, decorator) {
  // Default to 200 (success).
  var code = code || 200;

  var payload = {
    status: {
      code: code
    }
  };

  if (content) {
    payload.data = content;
  }

  if (decorator) {
    return decorator(payload, request, response, function() {
      response.status(payload.status.code).send(payload);
    });
  }

  response.status(code).send(payload);
};

/**
 * Respond to a request with a "server error".
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object.
 */
RouteController.error = function(request, response, error) {
  RouteController.respond(request, response, {
    title: 'Server error',
    description: "The server couldn't process the request.",
    error: error.toString()
  }, 500);
};

/**
 * Respond to a request with a "page not found" error.
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object.
 */
RouteController.notFound = function(request, response) {
  RouteController.respond(request, response, {
    title: 'Page not found',
    description: "The page you're looking for wasn't found."
  }, 404);
};

/**
 * Respond to a request with a "forbidden" error.
 *
 * @param {Request} request Request object.
 * @param {Response} response Response object.
 */
RouteController.forbidden = function(request, response) {
  RouteController.respond(request, response, {
    title: 'Forbidden',
    description: "You don't have permission to access this page."
  }, 403);
};
<|endoftext|>


<commit_before>(function () {
    'use strict';

    angular
        .module('app')
        .controller('relatedVocabularyCtrl', relatedVocabularyCtrl);

    /** Controller for modal dialog for entering/editing
     * related internal vocabularies.
     * This is a much-simplified version of relatedCtrl.
     * @param $scope
     * @param $uibModalInstance
     * @param $log
     * @param $timeout
     * @param $templateCache
     * @param entity
     * @param confluenceTip
     * @returns
     */
    function relatedVocabularyCtrl($scope, $uibModalInstance, $log, $timeout,
                         $templateCache,
                         entity, confluenceTip) {

        // Make the modal dialog (at least, temporarily) movable,
        // to be consistent with the version modal (which was
        // as per request in SD-11572, CC-2050).
        $timeout(function(){
            $('.modal-content').draggable({ revert: true });
        });
        // Set up access to the Registry API.
        var VocabularyRegistryApi = require('vocabulary_registry_api');
        var api = new VocabularyRegistryApi.ResourcesApi();

        /* List the relationships allowed. */
        $scope.relatedVocabularyRelations = [
            {"value": "hasAssociationWith", "text": "Associated with"},
            {"value": "isDerivedFrom", "text": "Derived from"},
            {"value": "enriches", "text": "Enriches"},
            {"value": "isPartOf", "text": "Part of"}
        ];

        /** A list of the multi-valued elements that are the elements
            of $scope.entity. Useful when iterating over all of these. */
        $scope.multi_valued_lists = [ 'relationship' ];

        // Some utility functions used by initialization (and by save).
        /** Ensure that a multi-value field has a minimal content, ready
            for editing. For some types, this could be an empty list;
            for others, a list with one (blank) element. */
        $scope.ensure_minimal_list = function (type) {
            if ((!(type in $scope.entity)) ||
                    ($scope.entity[type].length == 0)) {
                // Now an empty list. Do we put back a placeholder?
                switch (type) {
                case 'relationship':
                    $scope.entity[type] = [null];
                    break;
                default:
                    $scope.entity[type] = [];
                }
            }
        }

        /** Ensure that all multi-value fields have minimal content, ready
            for editing. For some types, this could be an empty list;
            for others, a list with one (blank) element. */
        $scope.ensure_all_minimal_lists = function () {
            angular.forEach($scope.multi_valued_lists, function (type) {
                $scope.ensure_minimal_list(type);
            });
        }

        // Initialization proper starts here.

        $scope.entity = false;
        // "Intent" is derived from what the user did on the main CMS page.
        // If they clicked "Add", then the intent is "add".
        // If they clicked a vocabulary title already in the list
        // in the "Related" section of the CMS page, then the intent is "save".
        $scope.intent = 'add';
        $scope.confluenceTip = confluenceTip;

        if (entity) {
            $scope.entity = entity;
            $scope.intent = 'save';
        } else {
            // Ensure minimum details to enable the form to work.
            $scope.entity = {};
            $scope.ensure_all_minimal_lists();
//            $scope.entity = {'relationship':[null]};
        }

        /* Set valid relations for this type. */
        $scope.relatedEntityRelations = $scope.relatedVocabularyRelations;

        $scope.form = {
            reForm:{}
        }

        $scope.onFocus = function (e) {
            $timeout(function () {
              $(e.target).trigger('input');
              $(e.target).trigger('change'); // for IE
            });
          };

        $scope.populate = function (item) {
            $scope.entity.id = item.getId();
            $scope.entity.title = item.getTitle();
        };

        $scope.list_add = function (type, obj) {
            if (!$scope.entity) $scope.entity = {};
            if (!$scope.entity[type]) $scope.entity[type] = [];
            if (obj) {
                $scope.entity[type].push(obj);
            }
        };

        /**
         * Add an item to a multi-valued list.
         * @param name of list: for now, should be 'relationship'.
         */
        $scope.addtolist = function (list) {
            if (!$scope.entity[list]) $scope.entity[list] = [];

            var newValue;
            newValue = null;

            // Add new blank item to list.
            $scope.entity[list].push(newValue);
        };

        /**
         * Remove an item from a multi-valued list. The list
         * is left in good condition: specifically,
         * $scope.ensure_minimal_list is called after the item
         * is removed.
         * @param name of list: one of the values in $scope.multi_valued_lists,
         *   e.g., 'top_concept'.
         * @param index of the item to be removed.
         */
        $scope.list_remove = function (type, index) {
            if (index > 0) {
                $scope.entity[type].splice(index, 1);
            } else {
                $scope.entity[type].splice(0, 1);
            }
            $scope.ensure_minimal_list(type);
        };

        /** Utility function for validation of fields that can have
            multiple entries. The list is supposed to have at least one
            element that is a non-empty string. This method returns true
            if this is not the case. */
        $scope.array_has_no_nonempty_strings = function (list) {
            return list === undefined || list.filter(Boolean).length == 0;
        }

        /** Tidy up all empty fields. To be used before saving.
            Note that this does not guarantee validity.
         */
        $scope.tidy_empty = function() {
            $scope.entity.relationship = $scope.entity.relationship.filter(Boolean);
        }

        $scope.save = function () {
            if ($scope.validateVocabulary()) {
                var ret = {
                    'intent': $scope.intent,
                    'data': $scope.entity
                };
                $uibModalInstance.close(ret);
            } else {
                // Put back the multi-value lists ready for more editing.
                $scope.ensure_all_minimal_lists();
                return false;
            }
        };

        $scope.validateVocabulary = function () {
            delete $scope.error_message;

            // Tidy up empty fields before validation.
            $scope.tidy_empty();

            if ($scope.form.reForm.$valid) {

                //at least 1 relationship
                if (!$scope.entity || !$scope.entity.relationship || $scope.entity.relationship.length == 0) {
                    $scope.error_message = 'At least 1 relationship is required';
                    return false
                }
                return true;
            } else {
                $scope.error_message = 'Form Validation Failed';
                return false;
            }
        };

        $scope.dismiss = function () {
            $uibModalInstance.dismiss();
        };

        // Get all existing vocabularies, in order
        // to be able to provide suggestions.
        api.getVocabularies().
        then(function (data) {
            $scope.suggestions = data.getVocabulary();
            // Results come back from the API unsorted, so sort
            // them by title.
            // Need $scope.$apply() to force an update if the dialog
            // appears before the API call returns.
            $scope.$apply(function() {$scope.suggestions.sort(function(a,b) {
                return a.getTitle().localeCompare(b.getTitle());});
            });
        });

        // Override the Bootstrap templates defined
        // at the end of assets/js/lib/ui-select/dist/select.js.

        // In select.tpl.html,
        // <div class="ui-select-container ... dropdown"
        // is changed to
        // <div class="ui-select-container ... dropdown swatch-white"
        $templateCache.put("bootstrap/select.tpl.html","

 <input type=\"search\" autocomplete=\"off\" tabindex=\"-1\" aria-expanded=\"true\" aria-label=\"{{ $select.baseTitle }}\" aria-owns=\"ui-select-choices-{{ $select.generatedId }}\" class=\"form-control ui-select-search\" ng-class=\"{ \'ui-select-search-hidden\' : !$select.searchEnabled }\" placeholder=\"{{$select.placeholder}}\" ng-model=\"$select.search\" ng-show=\"$select.open\">

");

        // In match.tpl.html,
        // <span ... class="btn btn-default..."
        // is changed to
        // <span ... class="btn btn-primary..."
        $templateCache.put("bootstrap/match.tpl.html","
{{$select.placeholder}}
");

    }

})();
<commit_msg>CC-2441 CMS: Disallow related-internal-vocabulary self references<commit_after>(function () {
    'use strict';

    angular
        .module('app')
        .controller('relatedVocabularyCtrl', relatedVocabularyCtrl);

    /** Controller for modal dialog for entering/editing
     * related internal vocabularies.
     * This is a much-simplified version of relatedCtrl.
     * @param $scope
     * @param $uibModalInstance
     * @param $log
     * @param $timeout
     * @param $templateCache
     * @param entity
     * @param confluenceTip
     * @returns
     */
    function relatedVocabularyCtrl($scope, $uibModalInstance, $log, $timeout,
                         $templateCache,
                         entity, confluenceTip) {

        // Make the modal dialog (at least, temporarily) movable,
        // to be consistent with the version modal (which was
        // as per request in SD-11572, CC-2050).
        $timeout(function(){
            $('.modal-content').draggable({ revert: true });
        });
        // Set up access to the Registry API.
        var VocabularyRegistryApi = require('vocabulary_registry_api');
        var api = new VocabularyRegistryApi.ResourcesApi();

        /* List the relationships allowed. */
        $scope.relatedVocabularyRelations = [
            {"value": "hasAssociationWith", "text": "Associated with"},
            {"value": "isDerivedFrom", "text": "Derived from"},
            {"value": "enriches", "text": "Enriches"},
            {"value": "isPartOf", "text": "Part of"}
        ];

        /** A list of the multi-valued elements that are the elements
            of $scope.entity. Useful when iterating over all of these. */
        $scope.multi_valued_lists = [ 'relationship' ];

        // Some utility functions used by initialization (and by save).
        /** Ensure that a multi-value field has a minimal content, ready
            for editing. For some types, this could be an empty list;
            for others, a list with one (blank) element. */
        $scope.ensure_minimal_list = function (type) {
            if ((!(type in $scope.entity)) ||
                    ($scope.entity[type].length == 0)) {
                // Now an empty list. Do we put back a placeholder?
                switch (type) {
                case 'relationship':
                    $scope.entity[type] = [null];
                    break;
                default:
                    $scope.entity[type] = [];
                }
            }
        }

        /** Ensure that all multi-value fields have minimal content, ready
            for editing. For some types, this could be an empty list;
            for others, a list with one (blank) element. */
        $scope.ensure_all_minimal_lists = function () {
            angular.forEach($scope.multi_valued_lists, function (type) {
                $scope.ensure_minimal_list(type);
            });
        }

        // Initialization proper starts here.

        $scope.entity = false;
        // "Intent" is derived from what the user did on the main CMS page.
        // If they clicked "Add", then the intent is "add".
        // If they clicked a vocabulary title already in the list
        // in the "Related" section of the CMS page, then the intent is "save".
        $scope.intent = 'add';
        $scope.confluenceTip = confluenceTip;

        if (entity) {
            $scope.entity = entity;
            $scope.intent = 'save';
        } else {
            // Ensure minimum details to enable the form to work.
            $scope.entity = {};
            $scope.ensure_all_minimal_lists();
//            $scope.entity = {'relationship':[null]};
        }

        /* Set valid relations for this type. */
        $scope.relatedEntityRelations = $scope.relatedVocabularyRelations;

        $scope.form = {
            reForm:{}
        }

        $scope.onFocus = function (e) {
            $timeout(function () {
              $(e.target).trigger('input');
              $(e.target).trigger('change'); // for IE
            });
          };

        $scope.populate = function (item) {
            $scope.entity.id = item.getId();
            $scope.entity.title = item.getTitle();
        };

        $scope.list_add = function (type, obj) {
            if (!$scope.entity) $scope.entity = {};
            if (!$scope.entity[type]) $scope.entity[type] = [];
            if (obj) {
                $scope.entity[type].push(obj);
            }
        };

        /**
         * Add an item to a multi-valued list.
         * @param name of list: for now, should be 'relationship'.
         */
        $scope.addtolist = function (list) {
            if (!$scope.entity[list]) $scope.entity[list] = [];

            var newValue;
            newValue = null;

            // Add new blank item to list.
            $scope.entity[list].push(newValue);
        };

        /**
         * Remove an item from a multi-valued list. The list
         * is left in good condition: specifically,
         * $scope.ensure_minimal_list is called after the item
         * is removed.
         * @param name of list: one of the values in $scope.multi_valued_lists,
         *   e.g., 'top_concept'.
         * @param index of the item to be removed.
         */
        $scope.list_remove = function (type, index) {
            if (index > 0) {
                $scope.entity[type].splice(index, 1);
            } else {
                $scope.entity[type].splice(0, 1);
            }
            $scope.ensure_minimal_list(type);
        };

        /** Utility function for validation of fields that can have
            multiple entries. The list is supposed to have at least one
            element that is a non-empty string. This method returns true
            if this is not the case. */
        $scope.array_has_no_nonempty_strings = function (list) {
            return list === undefined || list.filter(Boolean).length == 0;
        }

        /** Tidy up all empty fields. To be used before saving.
            Note that this does not guarantee validity.
         */
        $scope.tidy_empty = function() {
            $scope.entity.relationship = $scope.entity.relationship.filter(Boolean);
        }

        $scope.save = function () {
            if ($scope.validateVocabulary()) {
                var ret = {
                    'intent': $scope.intent,
                    'data': $scope.entity
                };
                $uibModalInstance.close(ret);
            } else {
                // Put back the multi-value lists ready for more editing.
                $scope.ensure_all_minimal_lists();
                return false;
            }
        };

        $scope.validateVocabulary = function () {
            delete $scope.error_message;

            // Tidy up empty fields before validation.
            $scope.tidy_empty();

            if ($scope.form.reForm.$valid) {

                //at least 1 relationship
                if (!$scope.entity || !$scope.entity.relationship || $scope.entity.relationship.length == 0) {
                    $scope.error_message = 'At least 1 relationship is required';
                    return false
                }
                return true;
            } else {
                $scope.error_message = 'Form Validation Failed';
                return false;
            }
        };

        $scope.dismiss = function () {
            $uibModalInstance.dismiss();
        };

        // Get all existing vocabularies, in order
        // to be able to provide suggestions.
        api.getVocabularies().
        then(function (data) {
            // See if we are creating, or updating this vocabulary.
            // Easiest way is to see if there is a vocab_id element.
            var vocab_id = $('#vocab_id').val();
            if (vocab_id) {
                // We're updating. Filter out the current vocabulary,
                // so as to disallow self-references.
                $scope.suggestions = data.getVocabulary().filter(
                    function(v) { return v.getId() != vocab_id; }
                );
            } else {
                // We're creating a vocabulary. OK to offer
                // all vocabularies.
                $scope.suggestions = data.getVocabulary();
            }
            // Results come back from the API unsorted, so sort
            // them by title.
            // Need $scope.$apply() to force an update if the dialog
            // appears before the API call returns.
            $scope.$apply(function() {$scope.suggestions.sort(function(a,b) {
                return a.getTitle().localeCompare(b.getTitle());});
            });
        });

        // Override the Bootstrap templates defined
        // at the end of assets/js/lib/ui-select/dist/select.js.

        // In select.tpl.html,
        // <div class="ui-select-container ... dropdown"
        // is changed to
        // <div class="ui-select-container ... dropdown swatch-white"
        $templateCache.put("bootstrap/select.tpl.html","

 <input type=\"search\" autocomplete=\"off\" tabindex=\"-1\" aria-expanded=\"true\" aria-label=\"{{ $select.baseTitle }}\" aria-owns=\"ui-select-choices-{{ $select.generatedId }}\" class=\"form-control ui-select-search\" ng-class=\"{ \'ui-select-search-hidden\' : !$select.searchEnabled }\" placeholder=\"{{$select.placeholder}}\" ng-model=\"$select.search\" ng-show=\"$select.open\">

");

        // In match.tpl.html,
        // <span ... class="btn btn-default..."
        // is changed to
        // <span ... class="btn btn-primary..."
        $templateCache.put("bootstrap/match.tpl.html","
{{$select.placeholder}}
");

    }

})();
<|endoftext|>


<commit_before>import Immutable from 'immutable';
import React, { PureComponent } from 'react';

// This file and its related dependencies needs to be downloaded into your app's `node_modules`.
// See the README for instructions.
//
// eslint-disable-next-line import/no-unresolved, import/extensions
import VirtualizedList from 'react-native/Libraries/CustomComponents/Lists/VirtualizedList';

import utils from './utils';

/**
 * A VirtualizedList capable of displaying {@link https://facebook.github.io/immutable-js/ Immutable} data
 * out of the box.
 */
// eslint-disable-next-line react/prefer-stateless-function
class ImmutableVirtualizedList extends PureComponent {

  static propTypes = {
    // Pass through any props that VirtualizedList would normally take.
    ...VirtualizedList.propTypes,

    /**
     * The immutable data to be rendered in a VirtualizedList.
     */
    // eslint-disable-next-line consistent-return
    immutableData: (props, propName, componentName) => {
      // Note: It's not enough to simply validate PropTypes.instanceOf(Immutable.Iterable),
      // because different imports of Immutable.js across files have different class prototypes.
      // TODO: Add support for Immutable.Map, etc.
      if (Immutable.Map.isMap(props[propName])) {
        return new Error(`Invalid prop ${propName} supplied to ${componentName}: Support for Immutable.Map is coming soon. For now, try an Immutable List, Set, or Range.`);
      } else if (!utils.isImmutableIterable(props[propName])) {
        return new Error(`Invalid prop ${propName} supplied to ${componentName}: Must be instance of Immutable.Iterable.`);
      }
    },
  };

  render() {
    const { immutableData } = this.props;

    return (
      <VirtualizedList
        data={immutableData}
        getItem={(items, index) => utils.getValueFromKey(index, items)}
        getItemCount={(items) => (items.size || items.length)}
        keyExtractor={(item, index) => String(index)}
        {...this.props}
      />
    );
  }

}

export default ImmutableVirtualizedList;
<commit_msg>:bug: Never return 'undefined' for getItemCount()<commit_after>import Immutable from 'immutable';
import React, { PureComponent } from 'react';

// This file and its related dependencies needs to be downloaded into your app's `node_modules`.
// See the README for instructions.
//
// eslint-disable-next-line import/no-unresolved, import/extensions
import VirtualizedList from 'react-native/Libraries/CustomComponents/Lists/VirtualizedList';

import utils from './utils';

/**
 * A VirtualizedList capable of displaying {@link https://facebook.github.io/immutable-js/ Immutable} data
 * out of the box.
 */
// eslint-disable-next-line react/prefer-stateless-function
class ImmutableVirtualizedList extends PureComponent {

  static propTypes = {
    // Pass through any props that VirtualizedList would normally take.
    ...VirtualizedList.propTypes,

    /**
     * The immutable data to be rendered in a VirtualizedList.
     */
    // eslint-disable-next-line consistent-return
    immutableData: (props, propName, componentName) => {
      // Note: It's not enough to simply validate PropTypes.instanceOf(Immutable.Iterable),
      // because different imports of Immutable.js across files have different class prototypes.
      // TODO: Add support for Immutable.Map, etc.
      if (Immutable.Map.isMap(props[propName])) {
        return new Error(`Invalid prop ${propName} supplied to ${componentName}: Support for Immutable.Map is coming soon. For now, try an Immutable List, Set, or Range.`);
      } else if (!utils.isImmutableIterable(props[propName])) {
        return new Error(`Invalid prop ${propName} supplied to ${componentName}: Must be instance of Immutable.Iterable.`);
      }
    },
  };

  render() {
    const { immutableData } = this.props;

    return (
      <VirtualizedList
        data={immutableData}
        getItem={(items, index) => utils.getValueFromKey(index, items)}
        getItemCount={(items) => (items.size || items.length || 0)}
        keyExtractor={(item, index) => String(index)}
        {...this.props}
      />
    );
  }

}

export default ImmutableVirtualizedList;
<|endoftext|>