<commit_before>import React from 'react';
import {connect} from 'react-redux';
import { Link } from 'react-router-dom';
import ConnectedLink from '../ConnectedLink';
import isEmpty from 'lodash/isEmpty';

import { Body, ReviewHeader, PrivateInfo } from '../../../../shared/SellerProfile';

import styles from './ApplicationPreview.css'; // eslint-disable-line no-unused-vars

const ApplicationPreview = ({ header, body, privateInfo, onClick }) => (

      {onClick &&

Preview your profile

Buyers will see the information below when they visit your seller profile. If correct, continue to the legal disclosures and agreement.

          <Link
          to="/submit"
          role="button"
          onClick={(e) => onClick(e)}>
          Continue to next step
        </Link>
        <Link to="/review">Go back and edit</Link>

}
    <ReviewHeader
      {...header}
    />

        {!onClick && privateInfo.disclosures && <PrivateInfo {...privateInfo} />}

);

const mapStateToProps = ({ application }, { documentsUrl, onClick, ...rest }) => {
    let {
      name,
      seller_type,
      summary,
      website,
      twitter,
      contact_email,
      contact_phone,
      contact_name,
      services = {},
      domains = {},
      case_studies,
      travel: interstate,
      linkedin,
      number_of_employees,
      government_experience,
      other_panels,
      disclosures,
      documents,
      documents_url = '../documents/',
      case_study_url,
      public_profile,
      signed_agreements,
      recruiter,
      recruiter_info,
      ...body
    } = application;

    let assessed = application.assessed_domains;
    let unassessed = domains.unassessed;
    // If unassessed is falsy, assume we are on preview
    // Where we just want to show the current selected
    // services. Filter out falsy services and convert
    // to an Array to keep type consistent
    if (!unassessed) {
        unassessed = Object
            .keys(services)
            .filter(key => services[key])
            .filter(key => !assessed.includes(key));
    }

    // calculate badges
    var calculated_seller_types = new Object();
    Object.assign(calculated_seller_types, seller_type);
    calculated_seller_types['recruitment'] = (recruiter === 'yes');
    calculated_seller_types['product'] = !isEmpty(application.products);
    seller_type = calculated_seller_types;

    let caseStudyLink = null;
    if (typeof case_study_url !== 'undefined') {
      caseStudyLink = ({id, children}) => (<a href={`${case_study_url}${id}`}>{children});
    } else {
      caseStudyLink = ({id, children}) => (
        <ConnectedLink to={`${rest.match.url}/case-study/${id}`}>{children}</ConnectedLink>
      );
    }

    // FIXME this is a workaround, somehow python returns empty case studies as a list.
    // Either there is bad data floating around or something is turning empty objects
    // into an array.
    // Body expects object not array.
    if (Array.isArray(case_studies)) {
      if (isEmpty(case_studies)) {
        case_studies = {};
      } else {
        // Convert case study array to object format.
        case_studies = case_studies.reduce((cso, study) => {
          cso[study.id] = study;
          return cso;
        }, {});
      }
    }

    return {
        header: {
          name,
          seller_type,
          summary,
          website,
          twitter,
          contact_email,
          contact_phone,
          contact_name,
          linkedin,
          public_profile
        },
        body: {
          assessed,
          unassessed,
          case_studies,
          interstate,
          CaseStudyLink: caseStudyLink,
          documents,
          documentsUrl: documents_url,
          public_profile,
          ...body
        },
        privateInfo: (disclosures ? {
            documents,
            documentsUrl: documents_url,
            case_studies,
            number_of_employees,
            government_experience,
            other_panels,
            disclosures,
            signed_agreements,
            recruiter_info
        } : {}),
        onClick
    }
}

export {
    mapStateToProps
}

export default connect(mapStateToProps)(ApplicationPreview);
<commit_msg>Only exclude assessed domains where assessed domains are present (#309)<commit_after>import React from 'react';
import {connect} from 'react-redux';
import { Link } from 'react-router-dom';
import ConnectedLink from '../ConnectedLink';
import isEmpty from 'lodash/isEmpty';

import { Body, ReviewHeader, PrivateInfo } from '../../../../shared/SellerProfile';

import styles from './ApplicationPreview.css'; // eslint-disable-line no-unused-vars

const ApplicationPreview = ({ header, body, privateInfo, onClick }) => (

      {onClick &&

Preview your profile

Buyers will see the information below when they visit your seller profile. If correct, continue to the legal disclosures and agreement.

          <Link
          to="/submit"
          role="button"
          onClick={(e) => onClick(e)}>
          Continue to next step
        </Link>
        <Link to="/review">Go back and edit</Link>

}
    <ReviewHeader
      {...header}
    />

        {!onClick && privateInfo.disclosures && <PrivateInfo {...privateInfo} />}

);

const mapStateToProps = ({ application }, { documentsUrl, onClick, ...rest }) => {
    let {
      name,
      seller_type,
      summary,
      website,
      twitter,
      contact_email,
      contact_phone,
      contact_name,
      services = {},
      domains = {},
      case_studies,
      travel: interstate,
      linkedin,
      number_of_employees,
      government_experience,
      other_panels,
      disclosures,
      documents,
      documents_url = '../documents/',
      case_study_url,
      public_profile,
      signed_agreements,
      recruiter,
      recruiter_info,
      ...body
    } = application;

    let assessed = application.assessed_domains;
    let unassessed = domains.unassessed;
    // If unassessed is falsy, assume we are on preview
    // Where we just want to show the current selected
    // services. Filter out falsy services and convert
    // to an Array to keep type consistent
    if (!unassessed) {
        unassessed = Object
            .keys(services)
            .filter(key => services[key]);
        if (assessed) {
            unassessed = unassessed.filter(key => !assessed.includes(key));
        }
    }

    // calculate badges
    var calculated_seller_types = new Object();
    Object.assign(calculated_seller_types, seller_type);
    calculated_seller_types['recruitment'] = (recruiter === 'yes');
    calculated_seller_types['product'] = !isEmpty(application.products);
    seller_type = calculated_seller_types;

    let caseStudyLink = null;
    if (typeof case_study_url !== 'undefined') {
      caseStudyLink = ({id, children}) => (<a href={`${case_study_url}${id}`}>{children});
    } else {
      caseStudyLink = ({id, children}) => (
        <ConnectedLink to={`${rest.match.url}/case-study/${id}`}>{children}</ConnectedLink>
      );
    }

    // FIXME this is a workaround, somehow python returns empty case studies as a list.
    // Either there is bad data floating around or something is turning empty objects
    // into an array.
    // Body expects object not array.
    if (Array.isArray(case_studies)) {
      if (isEmpty(case_studies)) {
        case_studies = {};
      } else {
        // Convert case study array to object format.
        case_studies = case_studies.reduce((cso, study) => {
          cso[study.id] = study;
          return cso;
        }, {});
      }
    }

    return {
        header: {
          name,
          seller_type,
          summary,
          website,
          twitter,
          contact_email,
          contact_phone,
          contact_name,
          linkedin,
          public_profile
        },
        body: {
          assessed,
          unassessed,
          case_studies,
          interstate,
          CaseStudyLink: caseStudyLink,
          documents,
          documentsUrl: documents_url,
          public_profile,
          ...body
        },
        privateInfo: (disclosures ? {
            documents,
            documentsUrl: documents_url,
            case_studies,
            number_of_employees,
            government_experience,
            other_panels,
            disclosures,
            signed_agreements,
            recruiter_info
        } : {}),
        onClick
    }
}

export {
    mapStateToProps
}

export default connect(mapStateToProps)(ApplicationPreview);
<|endoftext|>


<commit_before>'use strict';

import React from 'react';
import PropTypes from 'prop-types';
import _ from 'underscore';
import { console, object, ajax } from '@hms-dbmi-bgm/shared-portal-components/es/components/util';
import { BasicUserContentBody } from './../components/BasicUserContentBody';
import { HiGlassLoadingIndicator } from './../../item-pages/components/HiGlass/HiGlassPlainContainer';

export class HiGlassEmbeddedInstance extends React.PureComponent {

    static defaultProps = {
        "uuid": null
    };

    static propTypes = {
        "uuid": PropTypes.string.isRequired
    };

    constructor(props) {
        super(props);
        this.state = {
            'loading': false,
            'higlassItem': null
        };
    }

    componentDidMount() {
        const { higlassItem } = this.state;
        const { uuid } = this.props;
        if (!higlassItem && uuid && typeof uuid === 'string' && uuid.length > 0) {
            this.setState({ 'loading': true }, () => {
                // Use the @id to get the item, then remove the loading message
                ajax.load('/higlass-view-configs/' + uuid, (r) => {
                    this.setState({ 'higlassItem': r, 'loading': false });
                });
            });
        }
    }

    render() {
        const { higlassItem, loading } = this.state;
        // If HiGlass item is loaded, then display viewer
        if (higlassItem) {
            return <BasicUserContentBody context={higlassItem} />;
        }
        // If we're loading, show a loading screen
        if (loading) {
            return
<HiGlassLoadingIndicator title="Loading" />
;
        }
        return null;
    }
}<commit_msg>higlass collapse and title updates<commit_after>'use strict';

import React from 'react';
import PropTypes from 'prop-types';
import _ from 'underscore';
import { console, object, ajax } from '@hms-dbmi-bgm/shared-portal-components/es/components/util';
import { BasicUserContentBody, ExpandableStaticHeader } from './../components/BasicUserContentBody';
import { HiGlassLoadingIndicator } from './../../item-pages/components/HiGlass/HiGlassPlainContainer';

export class HiGlassEmbeddedInstance extends React.PureComponent {

    static defaultProps = {
        "headerElement": "h3",
        "headerClassName": "section-title"
    };

    static propTypes = {
        "uuid": PropTypes.string.isRequired,
        "headerElement": PropTypes.string,
        "headerClassName": PropTypes.string,
    };

    constructor(props) {
        super(props);
        this.state = {
            'loading': false,
            'higlassItem': null
        };
    }

    componentDidMount() {
        const { higlassItem } = this.state;
        const { uuid } = this.props;
        if (!higlassItem && uuid && typeof uuid === 'string' && uuid.length > 0) {
            this.setState({ 'loading': true }, () => {
                // Use the @id to get the item, then remove the loading message
                ajax.load('/higlass-view-configs/' + uuid, (r) => {
                    this.setState({ 'higlassItem': r, 'loading': false });
                });
            });
        }
    }

    render() {
        const { headerElement = 'h3', headerClassName = "section-title" } = this.props;
        const { higlassItem, loading } = this.state;
        // If HiGlass item is loaded, then display viewer
        if (higlassItem) {
            const { title, options: { collapsible: isCollapsible = false, default_open = true } = {} } = higlassItem;
            const headerProps = { className: headerClassName };
            return (
                <React.Fragment>
                    {title && !isCollapsible ? React.createElement(headerElement, headerProps, title) : null}
                    {
                        isCollapsible ?
                            <ExpandableStaticHeader context={higlassItem} defaultOpen={default_open} title={title} href={null} titleElement={headerElement} titleTip={higlassItem.description} />
                            : <BasicUserContentBody context={higlassItem} href={null} />
                    }
                </React.Fragment>
            );
        }
        // If we're loading, show a loading screen
        if (loading) {
            return
<HiGlassLoadingIndicator title="Loading" />
;
        }
        return null;
    }
}<|endoftext|>


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

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

  FormController.$inject = ['$document', '$ionicModal', '$ionicPopup', '$log', '$scope', 'FormFactory',
    'ProjectFactory'];

  function FormController($document, $ionicModal, $ionicPopup, $log, $scope, FormFactory, ProjectFactory) {
    var vm = this;

    vm.choices = {};
    vm.field = undefined;
    vm.showFieldInfoModal = {};
    vm.survey = {};

    vm.closeModal = closeModal;
    vm.getMax = getMax;
    vm.getMin = getMin;
    vm.getSurvey = getSurvey;
    vm.isOptionChecked = isOptionChecked;
    vm.setSelMultClass = setSelMultClass;
    vm.showField = showField;
    vm.showFieldInfo = showFieldInfo;
    vm.toggleAcknowledgeChecked = toggleAcknowledgeChecked;
    vm.toggleChecked = toggleChecked;

    activate();

    function activate() {
      vm.survey = FormFactory.getForm().survey;
      vm.choices = FormFactory.getForm().choices;
      $scope.$on('formUpdated', function (e, form) {
        //$log.log('Form updated to', form);
        vm.survey = form.survey;
        vm.choices = form.choices;
      });

      $ionicModal.fromTemplateUrl('app/form/field-info-modal.html', {
        'scope': $scope,
        'animation': 'slide-in-up',
        'backdropClickToClose': false
      }).then(function (modal) {
        vm.fieldInfoModal = modal;
      });
    }

    /**
     * Public Functions
     */

    function closeModal(modal) {
      vm[modal].hide();
    }

    function getMax(constraint) {
      return FormFactory.getMax(constraint);
    }

    function getMin(constraint) {
      return FormFactory.getMin(constraint);
    }

    function getSurvey() {
      return FormFactory.getForm().survey
    }

    function isOptionChecked(field, choice, data) {
      return _.contains(data[field], choice);
    }

    // Set the class for the select_multiple fields here because it is not working
    // to set the class in the html the same way as for the other fields
    function setSelMultClass(field, data) {
      if (field.required === 'true') {
        if (data[field.name]) {
          if (data[field.name].length > 0) {
            return 'no-errors';
          }
        }
        else {
          return 'has-errors';
        }
      }
      return 'no-errors';
    }

    // Determine if the field should be shown or not by looking at the relevant key-value pair
    function showField(field, data) {
      var show = FormFactory.isRelevant(field.relevant, data);
      if (show && field.default) {
        if (!data[field.name]) data[field.name] = field.default;
      }
      if (!show) {
        if (data[field.name]) delete data[field.name];
      }
      var project = ProjectFactory.getCurrentProject();
      if (!project.is_testing_mode) {
        if (field.name === 'group_sed_tab_control' || field.name === 'sed_lithologies' ||
          field.name === 'sed_structures' || field.name === 'sed_interpretations' ||
          field.name === 'group_other_modes' || field.name === 'strat_mode' || field.name === 'minerals' ||
          field.name === 'experimental_results' || field.name === 'experimental_set_up') {
          show = false;
        }
      }
      return show;
    }

    function showFieldInfo(field) {
      vm.field = field;
      vm.fieldInfoModal.show();
    }

    function toggleAcknowledgeChecked(field, data) {
      if (data.trace_feature) {
        var confirmPopup = $ionicPopup.confirm({
          'title': 'Close Group?',
          'template': 'By closing this group you will be clearing any data in this group. Continue?'
        });
        confirmPopup.then(function (res) {
          if (res) data = FormFactory.toggleAcknowledgeChecked(data, field);
          else $document[0].getElementById(field + 'Toggle').checked = true;
        });
      }
      else data = FormFactory.toggleAcknowledgeChecked(data, field);
    }

    function toggleChecked(field, choice, data) {
      var i = -1;
      if (data[field]) i = data[field].indexOf(choice);
      else data[field] = [];

      // If choice not already selected
      if (i === -1) data[field].push(choice);
      // Choice has been unselected so remove it and delete if empty
      else {
        data[field].splice(i, 1);
        if (data[field].length === 0) delete data[field];
      }
    }
  }
}());
<commit_msg>Micro: Fixed Project Preferences to not show group header for Experimental if Testing Mode off<commit_after>(function () {
  'use strict';

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

  FormController.$inject = ['$document', '$ionicModal', '$ionicPopup', '$log', '$scope', 'FormFactory',
    'ProjectFactory'];

  function FormController($document, $ionicModal, $ionicPopup, $log, $scope, FormFactory, ProjectFactory) {
    var vm = this;

    vm.choices = {};
    vm.field = undefined;
    vm.showFieldInfoModal = {};
    vm.survey = {};

    vm.closeModal = closeModal;
    vm.getMax = getMax;
    vm.getMin = getMin;
    vm.getSurvey = getSurvey;
    vm.isOptionChecked = isOptionChecked;
    vm.setSelMultClass = setSelMultClass;
    vm.showField = showField;
    vm.showFieldInfo = showFieldInfo;
    vm.toggleAcknowledgeChecked = toggleAcknowledgeChecked;
    vm.toggleChecked = toggleChecked;

    activate();

    function activate() {
      vm.survey = FormFactory.getForm().survey;
      vm.choices = FormFactory.getForm().choices;
      $scope.$on('formUpdated', function (e, form) {
        //$log.log('Form updated to', form);
        vm.survey = form.survey;
        vm.choices = form.choices;
      });

      $ionicModal.fromTemplateUrl('app/form/field-info-modal.html', {
        'scope': $scope,
        'animation': 'slide-in-up',
        'backdropClickToClose': false
      }).then(function (modal) {
        vm.fieldInfoModal = modal;
      });
    }

    /**
     * Public Functions
     */

    function closeModal(modal) {
      vm[modal].hide();
    }

    function getMax(constraint) {
      return FormFactory.getMax(constraint);
    }

    function getMin(constraint) {
      return FormFactory.getMin(constraint);
    }

    function getSurvey() {
      return FormFactory.getForm().survey
    }

    function isOptionChecked(field, choice, data) {
      return _.contains(data[field], choice);
    }

    // Set the class for the select_multiple fields here because it is not working
    // to set the class in the html the same way as for the other fields
    function setSelMultClass(field, data) {
      if (field.required === 'true') {
        if (data[field.name]) {
          if (data[field.name].length > 0) {
            return 'no-errors';
          }
        }
        else {
          return 'has-errors';
        }
      }
      return 'no-errors';
    }

    // Determine if the field should be shown or not by looking at the relevant key-value pair
    function showField(field, data) {
      var show = FormFactory.isRelevant(field.relevant, data);
      if (show && field.default) {
        if (!data[field.name]) data[field.name] = field.default;
      }
      if (!show) {
        if (data[field.name]) delete data[field.name];
      }
      var project = ProjectFactory.getCurrentProject();
      if (!project.is_testing_mode) {
        if (field.name === 'group_sed_tab_control' || field.name === 'sed_lithologies' ||
          field.name === 'sed_structures' || field.name === 'sed_interpretations' ||
          field.name === 'group_other_modes' || field.name === 'strat_mode' || field.name === 'minerals' ||
          field.name === 'group_experimental_tab_control' || field.name === 'experimental_results' ||
          field.name === 'experimental_set_up') {
          show = false;
        }
      }
      return show;
    }

    function showFieldInfo(field) {
      vm.field = field;
      vm.fieldInfoModal.show();
    }

    function toggleAcknowledgeChecked(field, data) {
      if (data.trace_feature) {
        var confirmPopup = $ionicPopup.confirm({
          'title': 'Close Group?',
          'template': 'By closing this group you will be clearing any data in this group. Continue?'
        });
        confirmPopup.then(function (res) {
          if (res) data = FormFactory.toggleAcknowledgeChecked(data, field);
          else $document[0].getElementById(field + 'Toggle').checked = true;
        });
      }
      else data = FormFactory.toggleAcknowledgeChecked(data, field);
    }

    function toggleChecked(field, choice, data) {
      var i = -1;
      if (data[field]) i = data[field].indexOf(choice);
      else data[field] = [];

      // If choice not already selected
      if (i === -1) data[field].push(choice);
      // Choice has been unselected so remove it and delete if empty
      else {
        data[field].splice(i, 1);
        if (data[field].length === 0) delete data[field];
      }
    }
  }
}());
<|endoftext|>


<commit_before>function openRuleDetailsDialog(rule_result_id)
{
    $("#detail-modal").remove();

    var closebutton = $('<button type="button" class="close btn btn-sm btn-default" data-dismiss="modal" aria-hidden="true" title="Close">❌</button>');
    var modal = $('

');

    $("body").prepend(modal);

    var clone = $("#rule-detail-" + rule_result_id).clone();
    clone.attr("id", "");
    clone.children(".panel-heading").append(closebutton);
    closebutton.css( { "float" : "right" } );
    closebutton.css( { "margin-top" : "-=23px" } );
    $("#detail-modal-body").append(clone);

    $("#detail-modal").modal();

    return false;
}

function toggleRuleDisplay(checkbox)
{
    var result = checkbox.value;

    if (checkbox.checked)
    {
        $(".rule-overview-leaf-" + result).removeClass("rule-result-filtered");
        $(".rule-detail-" + result).removeClass("rule-result-filtered");
    }
    else
    {
        $(".rule-overview-leaf-" + result).addClass("rule-result-filtered");
        $(".rule-detail-" + result).addClass("rule-result-filtered");
    }
}

function toggleResultDetails(button)
{
    var result_details = $("#result-details");

    if (result_details.is(":visible"))
    {
        result_details.hide();
        $(button).html("Show all result details");
    }
    else
    {
        result_details.show();
        $(button).html("Hide all result details");
    }

    return false;
}

function ruleSearchMatches(detail_leaf, keywords)
{
    if (keywords.length == 0)
        return true;

    var match = true;
    var checked_keywords = detail_leaf.children(".keywords").text().toLowerCase();

    var index;
    for (index = 0; index < keywords.length; ++index)
    {
        if (checked_keywords.indexOf(keywords[index].toLowerCase()) < 0)
        {
            match = false;
            break;
        }
    }

    return match;
}

function ruleSearch()
{
    var search_input = $("#search-input").val();
    var keywords = search_input.split(/[\s,\.;]+/);
    var matches = 0;

    $(".rule-detail").each(function(){
        // rule-detail-rrid, that's offset 12
        var rrid = $(this).attr("id").substring(12);

        var overview_leaf = $("#rule-overview-leaf-" + rrid);
        var detail_leaf = $(this);

        if (ruleSearchMatches(detail_leaf, keywords))
        {
            overview_leaf.removeClass("search-no-match");
            detail_leaf.removeClass("search-no-match");
            ++matches;
        }
        else
        {
            overview_leaf.addClass("search-no-match");
            detail_leaf.addClass("search-no-match");
        }
    });

    if (!search_input)
        $("#search-matches").html("");
    else if (matches > 0)
        $("#search-matches").html(matches.toString() + " rules match.");
    else
        $("#search-matches").html("No rules match your search criteria!");
}

var is_original = true;
var original_treetable = null;

$(document).ready( function() {
    $("#result-details").hide();
    $(".js-only").show();
    $(".toggle-rule-display").each(function(){
        toggleRuleDisplay(this);
    });
    original_treetable = $(".treetable").clone();
    $(".treetable").treetable({ column: 0, expandable: true, clickableNodeNames: true, initialState : "expanded", indent : 0 });
    is_original = true;
});

function Reset() {
	if (!is_original) {
		$(".treetable").remove();
		$("#rule-overview").append(original_treetable.clone());
		$(".treetable").treetable({ column: 0, expandable: true, clickableNodeNames: true, initialState : "expanded", indent : 0 });
		$(".toggle-rule-display").each(function(){
			toggleRuleDisplay(this);
		});
		is_original = true;
	}
}

function GroupBy(group_class) {
	/* We must process grouping upon the original table.
	 * Otherwise, we would have unwanted duplicties in new table. */
	Reset();

	var lines = {};
	$(".rule-overview-leaf").each(function() {
		$(this).children("td:first").css("padding-left","0px");
		var id = $(this).attr("data-tt-id");
		var ref_list = $(this).attr(group_class);
		if (!ref_list) {
			ref_list = "unknown";
		}
		var target_groups = ref_list.split(",");
		for (i = 0; i < target_groups.length; i++) {
			var target_group = target_groups[i];
			if (!lines.hasOwnProperty(target_group)) {
				/* Create a new group */
				var new_group_line = "
" +
					"" + target_group + "
";
				lines[target_group] = [new_group_line];
			}
			var clone = $(this).clone();
			clone.attr("data-tt-id", id + "copy" + i);
			clone.attr("data-tt-parent-id", target_group);
			var new_line = clone.wrap("
").parent().html();
			lines[target_group].push(new_line);
		}
	});
	$(".treetable").remove();
	var html_text = "";
	for (x in lines) {
		html_text += lines[x].join("\n");
	}
	new_table ="

Title	 Severity	Result

" + html_text + "

";
	$("#rule-overview").append(new_table);
	is_original = false;
	$(".treetable").treetable({ column: 0, expandable: true, initialState : "expanded", indent : 0 });

}
<commit_msg>Refactor: Extract function to create new table row for a group<commit_after>function openRuleDetailsDialog(rule_result_id)
{
    $("#detail-modal").remove();

    var closebutton = $('<button type="button" class="close btn btn-sm btn-default" data-dismiss="modal" aria-hidden="true" title="Close">❌</button>');
    var modal = $('

');

    $("body").prepend(modal);

    var clone = $("#rule-detail-" + rule_result_id).clone();
    clone.attr("id", "");
    clone.children(".panel-heading").append(closebutton);
    closebutton.css( { "float" : "right" } );
    closebutton.css( { "margin-top" : "-=23px" } );
    $("#detail-modal-body").append(clone);

    $("#detail-modal").modal();

    return false;
}

function toggleRuleDisplay(checkbox)
{
    var result = checkbox.value;

    if (checkbox.checked)
    {
        $(".rule-overview-leaf-" + result).removeClass("rule-result-filtered");
        $(".rule-detail-" + result).removeClass("rule-result-filtered");
    }
    else
    {
        $(".rule-overview-leaf-" + result).addClass("rule-result-filtered");
        $(".rule-detail-" + result).addClass("rule-result-filtered");
    }
}

function toggleResultDetails(button)
{
    var result_details = $("#result-details");

    if (result_details.is(":visible"))
    {
        result_details.hide();
        $(button).html("Show all result details");
    }
    else
    {
        result_details.show();
        $(button).html("Hide all result details");
    }

    return false;
}

function ruleSearchMatches(detail_leaf, keywords)
{
    if (keywords.length == 0)
        return true;

    var match = true;
    var checked_keywords = detail_leaf.children(".keywords").text().toLowerCase();

    var index;
    for (index = 0; index < keywords.length; ++index)
    {
        if (checked_keywords.indexOf(keywords[index].toLowerCase()) < 0)
        {
            match = false;
            break;
        }
    }

    return match;
}

function ruleSearch()
{
    var search_input = $("#search-input").val();
    var keywords = search_input.split(/[\s,\.;]+/);
    var matches = 0;

    $(".rule-detail").each(function(){
        // rule-detail-rrid, that's offset 12
        var rrid = $(this).attr("id").substring(12);

        var overview_leaf = $("#rule-overview-leaf-" + rrid);
        var detail_leaf = $(this);

        if (ruleSearchMatches(detail_leaf, keywords))
        {
            overview_leaf.removeClass("search-no-match");
            detail_leaf.removeClass("search-no-match");
            ++matches;
        }
        else
        {
            overview_leaf.addClass("search-no-match");
            detail_leaf.addClass("search-no-match");
        }
    });

    if (!search_input)
        $("#search-matches").html("");
    else if (matches > 0)
        $("#search-matches").html(matches.toString() + " rules match.");
    else
        $("#search-matches").html("No rules match your search criteria!");
}

var is_original = true;
var original_treetable = null;

$(document).ready( function() {
    $("#result-details").hide();
    $(".js-only").show();
    $(".toggle-rule-display").each(function(){
        toggleRuleDisplay(this);
    });
    original_treetable = $(".treetable").clone();
    $(".treetable").treetable({ column: 0, expandable: true, clickableNodeNames: true, initialState : "expanded", indent : 0 });
    is_original = true;
});

function Reset() {
	if (!is_original) {
		$(".treetable").remove();
		$("#rule-overview").append(original_treetable.clone());
		$(".treetable").treetable({ column: 0, expandable: true, clickableNodeNames: true, initialState : "expanded", indent : 0 });
		$(".toggle-rule-display").each(function(){
			toggleRuleDisplay(this);
		});
		is_original = true;
	}
}

function NewGroupLine(group_name)
{
	return "
" +
		"" + group_name + "
";
}

function GroupBy(group_class) {
	/* We must process grouping upon the original table.
	 * Otherwise, we would have unwanted duplicties in new table. */
	Reset();

	var lines = {};
	$(".rule-overview-leaf").each(function() {
		$(this).children("td:first").css("padding-left","0px");
		var id = $(this).attr("data-tt-id");
		var ref_list = $(this).attr(group_class);
		if (!ref_list) {
			ref_list = "unknown";
		}
		var target_groups = ref_list.split(",");
		for (i = 0; i < target_groups.length; i++) {
			var target_group = target_groups[i];
			if (!lines.hasOwnProperty(target_group)) {
				/* Create a new group */
				lines[target_group] = [NewGroupLine(target_group)];
			}
			var clone = $(this).clone();
			clone.attr("data-tt-id", id + "copy" + i);
			clone.attr("data-tt-parent-id", target_group);
			var new_line = clone.wrap("
").parent().html();
			lines[target_group].push(new_line);
		}
	});
	$(".treetable").remove();
	var html_text = "";
	for (x in lines) {
		html_text += lines[x].join("\n");
	}
	new_table ="

Title	 Severity	Result

" + html_text + "

";
	$("#rule-overview").append(new_table);
	is_original = false;
	$(".treetable").treetable({ column: 0, expandable: true, initialState : "expanded", indent : 0 });

}
<|endoftext|>


<commit_before>/**
 * Text history window builder in text editor.
 *
 * @author Serhii Maksymchuk from Ubrainians for imCode
 * 29.01.18
 */
Imcms.define("imcms-text-history-window-builder",
    [
        "imcms-window-builder", "imcms-bem-builder", "imcms-components-builder", "jquery",
        "imcms-texts-history-rest-api", "tinyMCE"
    ],
    function (WindowBuilder, BEM, components, $, textsHistoryRestAPI, tinyMCE) {

        var $historyListContainer, $textHistoryView;

        function onWriteToTextField() {
            var textButton = textHistoryWindowBuilder.$editor.find(".view-text-button")[0];
            viewText.call(textButton);

            tinyMCE.activeEditor.setContent($textHistoryView.html());
            textHistoryWindowBuilder.closeWindow();
        }

        function onCancel() {
            textHistoryWindowBuilder.closeWindow();
        }

        function buildFooter() {
            return textHistoryWindowBuilder.buildFooter([
                components.buttons.negativeButton({
                    text: "Cancel",
                    click: onCancel
                }),
                components.buttons.saveButton({
                    text: "Write to text field",
                    click: onWriteToTextField
                })
            ]);
        }

        function isClickAllowed(element) {
            var $element = $(element);

            if ($element.hasClass("imcms-button--disabled")) {
                return false;
            }

            textHistoryWindowBuilder.$editor.find(".imcms-button--disabled")
                .removeClass("imcms-button--disabled");

            $element.addClass("imcms-button--disabled");

            return true;
        }

        function viewSource() {
            isClickAllowed(this) && $textHistoryView.text($textHistoryView.html());
        }

        function viewText() {
            isClickAllowed(this) && $textHistoryView.html($textHistoryView.text());
        }

        function buildTextHistoryControlButtons() {
            return components.buttons.buttonsContainer("<div>", [
                components.buttons.negativeButton({
                    "class": "view-source-button",
                    text: "View Source",
                    click: viewSource
                }),
                components.buttons.negativeButton({
                    "class": "view-text-button",
                    text: "View Text",
                    click: viewText
                })
            ]);
        }

        function buildHistoryView() {
            return new BEM({
                block: "imcms-right-side",
                elements: {
                    "text-history-view": $textHistoryView = $("<div>"),
                    "buttons": buildTextHistoryControlButtons()
                }
            }).buildBlockStructure("<div>");
        }

        function buildHistoryListContainer() {
            return $("<div>", {"class": "imcms-left-side text-history-list"});
        }

        function buildTextHistory() {
            return new BEM({
                block: "imcms-pop-up-modal",
                elements: {
                    "head": textHistoryWindowBuilder.buildHead("Text history"),
                    "left-side": $historyListContainer = buildHistoryListContainer(),
                    "right-side": buildHistoryView(),
                    "footer": buildFooter()
                }
            }).buildBlockStructure("<div>", {"class": "text-history"});
        }

        function buildTextHistoriesForDateContainer(date) {
            var $date = $("<div>", {
                "class": "text-history-date",
                text: date
            });

            var $separator = $("<div>", {"class": "text-history-date-separator"});

            return new BEM({
                block: "text-history-date-container",
                elements: {
                    "date": $date,
                    "separator": $separator
                }
            }).buildBlockStructure("<div>");
        }

        function showTextHistoryUnit(text) {
            $textHistoryView.html(text);
        }

        function onTextHistoryUnitClicked(unit) {
            $(".text-history-unit").removeClass("text-history-date-unit__unit--active");
            $(unit).addClass("text-history-date-unit__unit--active");

            textHistoryWindowBuilder.$editor.find(".imcms-buttons")
                .find(".imcms-button")
                .css("display", "block");

            textHistoryWindowBuilder.$editor.find(".imcms-button--disabled")
                .removeClass("imcms-button--disabled");

            textHistoryWindowBuilder.$editor.find(".view-text-button")
                .addClass("imcms-button--disabled");
        }

        function buildTextHistoriesForDate(textHistoriesForDate) {
            return textHistoriesForDate.map(function (textHistory) {
                var $textHistoryUnit = $("<div>", {
                    "class": "text-history-unit",
                    text: textHistory.modified.time + " | " + textHistory.modifiedBy.username,
                    click: function () {
                        onTextHistoryUnitClicked(this);
                        showTextHistoryUnit(textHistory.text);
                    }
                });

                return {"unit": $textHistoryUnit};
            });
        }

        function buildTextHistoryUnit(date, textHistoriesForDate) {
            var elements = [{"date": buildTextHistoriesForDateContainer(date)}];

            new BEM({
                block: "text-history-date-unit",
                elements: elements.concat(buildTextHistoriesForDate(textHistoriesForDate))
            }).buildBlockStructure("<div>").appendTo($historyListContainer);
        }

        function loadData(textDTO) {
            textsHistoryRestAPI.read(textDTO).done(function (textsHistory) {
                var dateToTextHistoryUnits = {};

                textsHistory.forEach(function (textHistory) {
                    var date = textHistory.modified.date;
                    dateToTextHistoryUnits[date] = (dateToTextHistoryUnits[date] || []);
                    dateToTextHistoryUnits[date].push(textHistory);
                });

                $.each(dateToTextHistoryUnits, buildTextHistoryUnit);

                $historyListContainer.find(".text-history-date-unit__unit")
                    .first()
                    .click();
            });
        }

        function clearData() {
            $historyListContainer.empty();
            $textHistoryView.html('');
            textHistoryWindowBuilder.$editor.find(".imcms-buttons")
                .find(".imcms-button")
                .css("display", "none");
        }

        var textHistoryWindowBuilder = new WindowBuilder({
            factory: buildTextHistory,
            loadDataStrategy: loadData,
            clearDataStrategy: clearData
        });

        return {
            buildTextHistory: function (textData) {
                textHistoryWindowBuilder.buildWindowWithShadow.applyAsync(arguments, textHistoryWindowBuilder);
            }
        };
    }
);
<commit_msg>IMCMS-255 - Upgrade server side to work with new client: - Setting editor as able to save after inserting history; - View history text/html buttons hidden until history unit is selected.<commit_after>/**
 * Text history window builder in text editor.
 *
 * @author Serhii Maksymchuk from Ubrainians for imCode
 * 29.01.18
 */
Imcms.define("imcms-text-history-window-builder",
    [
        "imcms-window-builder", "imcms-bem-builder", "imcms-components-builder", "jquery",
        "imcms-texts-history-rest-api", "tinyMCE"
    ],
    function (WindowBuilder, BEM, components, $, textsHistoryRestAPI, tinyMCE) {

        var $historyListContainer, $textHistoryView;

        function onWriteToTextField() {
            var textButton = textHistoryWindowBuilder.$editor.find(".view-text-button")[0];
            viewText.call(textButton);

            tinyMCE.activeEditor.setContent($textHistoryView.html());
            tinyMCE.activeEditor.setDirty(true);
            textHistoryWindowBuilder.closeWindow();
        }

        function onCancel() {
            textHistoryWindowBuilder.closeWindow();
        }

        function buildFooter() {
            return textHistoryWindowBuilder.buildFooter([
                components.buttons.negativeButton({
                    text: "Cancel",
                    click: onCancel
                }),
                components.buttons.saveButton({
                    text: "Write to text field",
                    click: onWriteToTextField
                })
            ]);
        }

        function isClickAllowed(element) {
            var $element = $(element);

            if ($element.hasClass("imcms-button--disabled")) {
                return false;
            }

            textHistoryWindowBuilder.$editor.find(".imcms-button--disabled")
                .removeClass("imcms-button--disabled");

            $element.addClass("imcms-button--disabled");

            return true;
        }

        function viewSource() {
            isClickAllowed(this) && $textHistoryView.text($textHistoryView.html());
        }

        function viewText() {
            isClickAllowed(this) && $textHistoryView.html($textHistoryView.text());
        }

        function buildTextHistoryControlButtons() {
            return components.buttons.buttonsContainer("<div>", [
                components.buttons.negativeButton({
                    "class": "view-source-button",
                    "style": "display: none;",
                    text: "View Source",
                    click: viewSource
                }),
                components.buttons.negativeButton({
                    "class": "view-text-button",
                    "style": "display: none;",
                    text: "View Text",
                    click: viewText
                })
            ]);
        }

        function buildHistoryView() {
            return new BEM({
                block: "imcms-right-side",
                elements: {
                    "text-history-view": $textHistoryView = $("<div>"),
                    "buttons": buildTextHistoryControlButtons()
                }
            }).buildBlockStructure("<div>");
        }

        function buildHistoryListContainer() {
            return $("<div>", {"class": "imcms-left-side text-history-list"});
        }

        function buildTextHistory() {
            return new BEM({
                block: "imcms-pop-up-modal",
                elements: {
                    "head": textHistoryWindowBuilder.buildHead("Text history"),
                    "left-side": $historyListContainer = buildHistoryListContainer(),
                    "right-side": buildHistoryView(),
                    "footer": buildFooter()
                }
            }).buildBlockStructure("<div>", {"class": "text-history"});
        }

        function buildTextHistoriesForDateContainer(date) {
            var $date = $("<div>", {
                "class": "text-history-date",
                text: date
            });

            var $separator = $("<div>", {"class": "text-history-date-separator"});

            return new BEM({
                block: "text-history-date-container",
                elements: {
                    "date": $date,
                    "separator": $separator
                }
            }).buildBlockStructure("<div>");
        }

        function showTextHistoryUnit(text) {
            $textHistoryView.html(text);
        }

        function onTextHistoryUnitClicked(unit) {
            $(".text-history-unit").removeClass("text-history-date-unit__unit--active");
            $(unit).addClass("text-history-date-unit__unit--active");

            textHistoryWindowBuilder.$editor.find(".imcms-buttons")
                .find(".imcms-button")
                .css("display", "block");

            textHistoryWindowBuilder.$editor.find(".imcms-button--disabled")
                .removeClass("imcms-button--disabled");

            textHistoryWindowBuilder.$editor.find(".view-text-button")
                .addClass("imcms-button--disabled");
        }

        function buildTextHistoriesForDate(textHistoriesForDate) {
            return textHistoriesForDate.map(function (textHistory) {
                var $textHistoryUnit = $("<div>", {
                    "class": "text-history-unit",
                    text: textHistory.modified.time + " | " + textHistory.modifiedBy.username,
                    click: function () {
                        onTextHistoryUnitClicked(this);
                        showTextHistoryUnit(textHistory.text);
                    }
                });

                return {"unit": $textHistoryUnit};
            });
        }

        function buildTextHistoryUnit(date, textHistoriesForDate) {
            var elements = [{"date": buildTextHistoriesForDateContainer(date)}];

            new BEM({
                block: "text-history-date-unit",
                elements: elements.concat(buildTextHistoriesForDate(textHistoriesForDate))
            }).buildBlockStructure("<div>").appendTo($historyListContainer);
        }

        function loadData(textDTO) {
            textsHistoryRestAPI.read(textDTO).done(function (textsHistory) {
                var dateToTextHistoryUnits = {};

                textsHistory.forEach(function (textHistory) {
                    var date = textHistory.modified.date;
                    dateToTextHistoryUnits[date] = (dateToTextHistoryUnits[date] || []);
                    dateToTextHistoryUnits[date].push(textHistory);
                });

                $.each(dateToTextHistoryUnits, buildTextHistoryUnit);

                $historyListContainer.find(".text-history-date-unit__unit")
                    .first()
                    .click();
            });
        }

        function clearData() {
            $historyListContainer.empty();
            $textHistoryView.html('');
            textHistoryWindowBuilder.$editor.find(".imcms-buttons")
                .find(".imcms-button")
                .css("display", "none");
        }

        var textHistoryWindowBuilder = new WindowBuilder({
            factory: buildTextHistory,
            loadDataStrategy: loadData,
            clearDataStrategy: clearData
        });

        return {
            buildTextHistory: function (textData) {
                textHistoryWindowBuilder.buildWindowWithShadow.applyAsync(arguments, textHistoryWindowBuilder);
            }
        };
    }
);
<|endoftext|>


<commit_before>var stream_list = (function () {

var exports = {};

var last_private_message_count = 0;
var last_mention_count = 0;
var previous_sort_order;

exports.sort_narrow_list = function () {
    var streams = subs.subscribed_streams();
    if (streams.length === 0) {
        return;
    }

    var sort_recent = (streams.length > 40);

    streams.sort(function(a, b) {
        if (sort_recent) {
            if (recent_subjects[b] !== undefined &&
                recent_subjects[a] === undefined) {
                return 1;
            } else if (recent_subjects[b] === undefined &&
                       recent_subjects[a] !== undefined) {
                return -1;
            }
        }
        return util.strcmp(a, b);
    });

    if (previous_sort_order !== undefined
        && util.array_compare(previous_sort_order, streams)) {
        return;
    }

    previous_sort_order = streams;

    var parent = $('#stream_filters');
    parent.empty();

    var elems = [];
    $.each(streams, function(i, stream) {
        var li = $(subs.get(stream).sidebar_li);
        if (sort_recent) {
            if (recent_subjects[stream] === undefined) {
                li.addClass('inactive_stream');
            } else {
                li.removeClass('inactive_stream');
            }
        }
        elems.push(li.get(0));
    });
    $(elems).appendTo(parent);
};

function iterate_to_find(selector, name_to_find, context) {
    var retval = $();
    $(selector, context).each(function (idx, elem) {
        var jelem = $(elem);
        var elem_name = jelem.attr('data-name');
        if (elem_name.toLowerCase() === name_to_find.toLowerCase()) {
            retval = jelem;
            return false;
        }
    });
    return retval;
}

// TODO: Now that the unread count functions support the user sidebar
// as well, we probably should consider moving them to a different file.
function get_filter_li(type, name) {
    if (type === 'stream') {
        return $("#stream_sidebar_" + subs.stream_id(name));
    } else if (type === "private") {
        return $(".user_sidebar_entry > a[data-email='" + name + "']");
    }
    return iterate_to_find("#" + type + "_filters > li", name);
}

function get_subject_filter_li(stream, subject) {
    var stream_li = get_filter_li('stream', stream);
    return iterate_to_find(".expanded_subjects li", subject, stream_li);
}

// Adds the sidebar stream name that, when clicked,
// narrows to that stream
function add_narrow_filter(name, type) {
    if (get_filter_li(type, name).length) {
        // already exists
        return false;
    }

    var args = {name: name,
                id: subs.stream_id(name),
                uri: narrow.by_stream_uri(name),
                not_in_home_view: (subs.in_home_view(name) === false),
                invite_only: subs.get(name).invite_only,
                color: subs.get_color(name)};
    var list_item = templates.render('stream_sidebar_row', args);
    $("#" + type + "_filters").append(list_item);
    return list_item;
}

exports.get_count = function (type, name) {
    return get_filter_li(type, name).find('.count .value').text();
};

function update_count_in_dom(count_span, value_span, count) {
    if (count === 0) {
        count_span.hide();
        value_span.text('');
        return;
    }

    count_span.show();
    value_span.text(count);
}

exports.set_count = function (type, name, count) {
    var count_span = get_filter_li(type, name).find('.count');
    var value_span = count_span.find('.value');
    update_count_in_dom(count_span, value_span, count);
};

exports.set_subject_count = function (stream, subject, count) {
    var subject_li = get_subject_filter_li(stream, subject);
    var count_span = subject_li.find('.subject_count');
    var value_span = count_span.find('.value');

    if (count_span.length === 0 || value_span.length === 0) {
        return;
    }

    count_span.removeClass("zero_count");
    update_count_in_dom(count_span, value_span, count);
};

exports.remove_narrow_filter = function (name, type) {
    get_filter_li(type, name).remove();
};

exports.remove_all_narrow_filters = function () {
    $("#stream_filters").children().remove();
};

function rebuild_recent_subjects(stream, subject) {
    // TODO: Call rebuild_recent_subjects less, not on every new
    // message.
    $('.expanded_subjects').remove();
    var stream_li = get_filter_li('stream', stream);
    var subjects = recent_subjects[stream] || [];
    var active_orig_subject = subject;
    $.each(subjects, function (idx, subject_obj) {
        var num_unread = unread.num_unread_for_subject(stream, subject_obj.canon_subject);
        subject_obj.unread = num_unread;
        subject_obj.is_zero = num_unread === 0;

        if (subject === subject_obj.canon_subject) {
            active_orig_subject = subject_obj.subject;
        }
        subject_obj.url = narrow.by_stream_subject_uri(stream, subject_obj.subject);
    });

    stream_li.append(templates.render('sidebar_subject_list',
                                      {subjects: subjects,
                                       stream: stream}));

    if (active_orig_subject !== undefined) {
        get_subject_filter_li(stream, active_orig_subject).addClass('active-subject-filter');
    }
}

exports.update_streams_sidebar = function () {
    exports.sort_narrow_list();

    if (! narrow.active()) {
        return;
    }

    var op_stream = narrow.filter().operands('stream');
    var op_subject = narrow.filter().operands('subject');
    var subject;
    if (op_stream.length !== 0) {
        if (op_subject.length !== 0) {
            subject = op_subject[0];
        }
        if (subs.is_subscribed(op_stream[0])) {
            rebuild_recent_subjects(op_stream[0], subject);
        }
    }
};

function do_new_messages_animation(message_type) {
    var li = get_filter_li("global", message_type);
    li.addClass("new_messages");
    function mid_animation() {
        li.removeClass("new_messages");
        li.addClass("new_messages_fadeout");
    }
    function end_animation() {
        li.removeClass("new_messages_fadeout");
    }
    setTimeout(mid_animation, 3000);
    setTimeout(end_animation, 6000);
}

function animate_private_message_changes(new_private_message_count) {
    if (new_private_message_count > last_private_message_count) {
        do_new_messages_animation('private-message');
    }
    last_private_message_count = new_private_message_count;
}

function animate_mention_changes(new_mention_count) {
    if (new_mention_count > last_mention_count) {
        do_new_messages_animation('mentioned-message');
    }
    last_mention_count = new_mention_count;
}

exports.update_dom_with_unread_counts = function (counts) {
    // counts is just a data object that gets calculated elsewhere
    // Our job is to update some DOM elements.

    // counts.stream_count maps streams to counts
    $.each(counts.stream_count, function(stream, count) {
        exports.set_count("stream", stream, count);
    });

    // counts.subject_count maps streams to hashes of subjects to counts
    $.each(counts.subject_count, function(stream, subject_hash) {
        $.each(subject_hash, function(subject, count) {
            exports.set_subject_count(stream, subject, count);
        });
    });

    // counts.pm_count maps people to counts
    $.each(counts.pm_count, function(person, count) {
        exports.set_count("private", person, count);
    });

    // integer counts
    exports.set_count("global", "private-message", counts.private_message_count);
    exports.set_count("global", "mentioned-message", counts.mentioned_message_count);
    exports.set_count("global", "home", counts.home_unread_messages);

    animate_private_message_changes(counts.private_message_count);
    animate_mention_changes(counts.mentioned_message_count);
};

$(function () {
    $(document).on('narrow_activated.zephyr', function (event) {
        $("ul.filters li").removeClass('active-filter active-subject-filter');
        $('.expanded_subjects').remove();

        // TODO: handle confused filters like "in:all stream:foo"
        var op_in = event.filter.operands('in');
        if (op_in.length !== 0) {
            if (['all', 'home'].indexOf(op_in[0]) !== -1) {
                $("#global_filters li[data-name='" + op_in[0] + "']").addClass('active-filter');
            }
        }
        var op_is = event.filter.operands('is');
        if (op_is.length !== 0) {
            if (['private-message', 'starred', 'mentioned'].indexOf(op_is[0]) !== -1) {
                $("#global_filters li[data-name='" + op_is[0] + "']").addClass('active-filter');
            }
        }
        var op_stream = event.filter.operands('stream');
        if (op_stream.length !== 0 && subs.is_subscribed(op_stream[0])) {
            var stream_li = get_filter_li('stream', op_stream[0]);
            var op_subject = event.filter.operands('subject');
            var subject;
            if (op_subject.length !== 0) {
                subject = op_subject[0];
            } else {
                stream_li.addClass('active-filter');
            }
            rebuild_recent_subjects(op_stream[0], subject);
        }
        process_visible_unread_messages();
    });

    $(document).on('narrow_deactivated.zephyr', function (event) {
        $("ul.filters li").removeClass('active-filter active-subject-filter');
        $("ul.expanded_subjects").remove();
        $("#global_filters li[data-name='home']").addClass('active-filter');
    });

    $(document).on('sub_obj_created.zephyr', function (event) {
        if (event.sub.subscribed) {
            var stream_name = event.sub.name;
            var li = add_narrow_filter(stream_name, "stream");
            if (li) {
                event.sub.sidebar_li = li;
            }
        }
    });

    $(document).on('subscription_add_done.zephyr', function (event) {
        var stream_name = event.sub.name;
        var li = add_narrow_filter(stream_name, "stream");
        if (li) {
            event.sub.sidebar_li = li;
        }
        exports.sort_narrow_list();
    });

    $(document).on('subscription_remove_done.zephyr', function (event) {
        var stream_name = event.sub.name;
        exports.remove_narrow_filter(stream_name, 'stream');
        // We need to make sure we resort if the removed sub gets added again
        previous_sort_order = undefined;
    });
});

return exports;
}());
<commit_msg>Set sidebar_li attribute when updating stream colors<commit_after>var stream_list = (function () {

var exports = {};

var last_private_message_count = 0;
var last_mention_count = 0;
var previous_sort_order;

exports.sort_narrow_list = function () {
    var streams = subs.subscribed_streams();
    if (streams.length === 0) {
        return;
    }

    var sort_recent = (streams.length > 40);

    streams.sort(function(a, b) {
        if (sort_recent) {
            if (recent_subjects[b] !== undefined &&
                recent_subjects[a] === undefined) {
                return 1;
            } else if (recent_subjects[b] === undefined &&
                       recent_subjects[a] !== undefined) {
                return -1;
            }
        }
        return util.strcmp(a, b);
    });

    if (previous_sort_order !== undefined
        && util.array_compare(previous_sort_order, streams)) {
        return;
    }

    previous_sort_order = streams;

    var parent = $('#stream_filters');
    parent.empty();

    var elems = [];
    $.each(streams, function(i, stream) {
        var li = $(subs.get(stream).sidebar_li);
        if (sort_recent) {
            if (recent_subjects[stream] === undefined) {
                li.addClass('inactive_stream');
            } else {
                li.removeClass('inactive_stream');
            }
        }
        elems.push(li.get(0));
    });
    $(elems).appendTo(parent);
};

function iterate_to_find(selector, name_to_find, context) {
    var retval = $();
    $(selector, context).each(function (idx, elem) {
        var jelem = $(elem);
        var elem_name = jelem.attr('data-name');
        if (elem_name.toLowerCase() === name_to_find.toLowerCase()) {
            retval = jelem;
            return false;
        }
    });
    return retval;
}

// TODO: Now that the unread count functions support the user sidebar
// as well, we probably should consider moving them to a different file.
function get_filter_li(type, name) {
    if (type === 'stream') {
        return $("#stream_sidebar_" + subs.stream_id(name));
    } else if (type === "private") {
        return $(".user_sidebar_entry > a[data-email='" + name + "']");
    }
    return iterate_to_find("#" + type + "_filters > li", name);
}

function get_subject_filter_li(stream, subject) {
    var stream_li = get_filter_li('stream', stream);
    return iterate_to_find(".expanded_subjects li", subject, stream_li);
}

// Adds the sidebar stream name that, when clicked,
// narrows to that stream
function add_narrow_filter(name, type) {
    if (get_filter_li(type, name).length) {
        // already exists
        return false;
    }

    var args = {name: name,
                id: subs.stream_id(name),
                uri: narrow.by_stream_uri(name),
                not_in_home_view: (subs.in_home_view(name) === false),
                invite_only: subs.get(name).invite_only,
                color: subs.get_color(name)};
    var list_item = $(templates.render('stream_sidebar_row', args));
    $("#" + type + "_filters").append(list_item);
    return list_item;
}

exports.get_count = function (type, name) {
    return get_filter_li(type, name).find('.count .value').text();
};

function update_count_in_dom(count_span, value_span, count) {
    if (count === 0) {
        count_span.hide();
        value_span.text('');
        return;
    }

    count_span.show();
    value_span.text(count);
}

exports.set_count = function (type, name, count) {
    var count_span = get_filter_li(type, name).find('.count');
    var value_span = count_span.find('.value');
    update_count_in_dom(count_span, value_span, count);
};

exports.set_subject_count = function (stream, subject, count) {
    var subject_li = get_subject_filter_li(stream, subject);
    var count_span = subject_li.find('.subject_count');
    var value_span = count_span.find('.value');

    if (count_span.length === 0 || value_span.length === 0) {
        return;
    }

    count_span.removeClass("zero_count");
    update_count_in_dom(count_span, value_span, count);
};

exports.remove_narrow_filter = function (name, type) {
    get_filter_li(type, name).remove();
};

exports.remove_all_narrow_filters = function () {
    $("#stream_filters").children().remove();
};

function rebuild_recent_subjects(stream, subject) {
    // TODO: Call rebuild_recent_subjects less, not on every new
    // message.
    $('.expanded_subjects').remove();
    var stream_li = get_filter_li('stream', stream);
    var subjects = recent_subjects[stream] || [];
    var active_orig_subject = subject;
    $.each(subjects, function (idx, subject_obj) {
        var num_unread = unread.num_unread_for_subject(stream, subject_obj.canon_subject);
        subject_obj.unread = num_unread;
        subject_obj.is_zero = num_unread === 0;

        if (subject === subject_obj.canon_subject) {
            active_orig_subject = subject_obj.subject;
        }
        subject_obj.url = narrow.by_stream_subject_uri(stream, subject_obj.subject);
    });

    stream_li.append(templates.render('sidebar_subject_list',
                                      {subjects: subjects,
                                       stream: stream}));

    if (active_orig_subject !== undefined) {
        get_subject_filter_li(stream, active_orig_subject).addClass('active-subject-filter');
    }
}

exports.update_streams_sidebar = function () {
    exports.sort_narrow_list();

    if (! narrow.active()) {
        return;
    }

    var op_stream = narrow.filter().operands('stream');
    var op_subject = narrow.filter().operands('subject');
    var subject;
    if (op_stream.length !== 0) {
        if (op_subject.length !== 0) {
            subject = op_subject[0];
        }
        if (subs.is_subscribed(op_stream[0])) {
            rebuild_recent_subjects(op_stream[0], subject);
        }
    }
};

function do_new_messages_animation(message_type) {
    var li = get_filter_li("global", message_type);
    li.addClass("new_messages");
    function mid_animation() {
        li.removeClass("new_messages");
        li.addClass("new_messages_fadeout");
    }
    function end_animation() {
        li.removeClass("new_messages_fadeout");
    }
    setTimeout(mid_animation, 3000);
    setTimeout(end_animation, 6000);
}

function animate_private_message_changes(new_private_message_count) {
    if (new_private_message_count > last_private_message_count) {
        do_new_messages_animation('private-message');
    }
    last_private_message_count = new_private_message_count;
}

function animate_mention_changes(new_mention_count) {
    if (new_mention_count > last_mention_count) {
        do_new_messages_animation('mentioned-message');
    }
    last_mention_count = new_mention_count;
}

exports.update_dom_with_unread_counts = function (counts) {
    // counts is just a data object that gets calculated elsewhere
    // Our job is to update some DOM elements.

    // counts.stream_count maps streams to counts
    $.each(counts.stream_count, function(stream, count) {
        exports.set_count("stream", stream, count);
    });

    // counts.subject_count maps streams to hashes of subjects to counts
    $.each(counts.subject_count, function(stream, subject_hash) {
        $.each(subject_hash, function(subject, count) {
            exports.set_subject_count(stream, subject, count);
        });
    });

    // counts.pm_count maps people to counts
    $.each(counts.pm_count, function(person, count) {
        exports.set_count("private", person, count);
    });

    // integer counts
    exports.set_count("global", "private-message", counts.private_message_count);
    exports.set_count("global", "mentioned-message", counts.mentioned_message_count);
    exports.set_count("global", "home", counts.home_unread_messages);

    animate_private_message_changes(counts.private_message_count);
    animate_mention_changes(counts.mentioned_message_count);
};

$(function () {
    $(document).on('narrow_activated.zephyr', function (event) {
        $("ul.filters li").removeClass('active-filter active-subject-filter');
        $('.expanded_subjects').remove();

        // TODO: handle confused filters like "in:all stream:foo"
        var op_in = event.filter.operands('in');
        if (op_in.length !== 0) {
            if (['all', 'home'].indexOf(op_in[0]) !== -1) {
                $("#global_filters li[data-name='" + op_in[0] + "']").addClass('active-filter');
            }
        }
        var op_is = event.filter.operands('is');
        if (op_is.length !== 0) {
            if (['private-message', 'starred', 'mentioned'].indexOf(op_is[0]) !== -1) {
                $("#global_filters li[data-name='" + op_is[0] + "']").addClass('active-filter');
            }
        }
        var op_stream = event.filter.operands('stream');
        if (op_stream.length !== 0 && subs.is_subscribed(op_stream[0])) {
            var stream_li = get_filter_li('stream', op_stream[0]);
            var op_subject = event.filter.operands('subject');
            var subject;
            if (op_subject.length !== 0) {
                subject = op_subject[0];
            } else {
                stream_li.addClass('active-filter');
            }
            rebuild_recent_subjects(op_stream[0], subject);
        }
        process_visible_unread_messages();
    });

    $(document).on('narrow_deactivated.zephyr', function (event) {
        $("ul.filters li").removeClass('active-filter active-subject-filter');
        $("ul.expanded_subjects").remove();
        $("#global_filters li[data-name='home']").addClass('active-filter');
    });

    $(document).on('sub_obj_created.zephyr', function (event) {
        if (event.sub.subscribed) {
            var stream_name = event.sub.name;
            var li = add_narrow_filter(stream_name, "stream");
            if (li) {
                event.sub.sidebar_li = li;
            }
        }
    });

    $(document).on('subscription_add_done.zephyr', function (event) {
        var stream_name = event.sub.name;
        var li = add_narrow_filter(stream_name, "stream");
        if (li) {
            event.sub.sidebar_li = li;
        }
        exports.sort_narrow_list();
    });

    $(document).on('subscription_remove_done.zephyr', function (event) {
        var stream_name = event.sub.name;
        exports.remove_narrow_filter(stream_name, 'stream');
        // We need to make sure we resort if the removed sub gets added again
        previous_sort_order = undefined;
    });
});

return exports;
}());
<|endoftext|>


<commit_before>/*
 * Copyright 2018 Crown Copyright
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import React from 'react';
import PropTypes from 'prop-types';
import { path } from 'ramda';
import { connect } from 'react-redux';
import { compose, lifecycle, withProps, withHandlers } from 'recompose';
import Mousetrap from 'mousetrap';
import { Progress } from 'react-sweet-progress';
import 'react-sweet-progress/lib/style.css';
import ReactTable from 'react-table';
import 'react-table/react-table.css';
import { Button } from 'semantic-ui-react';

import { actionCreators, Directions } from '../redux';
import { fetchTrackers, fetchMore, TrackerSelection } from '../streamTasksResourceClient';

const {
  updateSort, moveSelection, updateSearchCriteria, pageRight, pageLeft,
} = actionCreators;

const enhance = compose(
  connect(
    ({
      processing: {
        trackers, sortBy, sortDirection, selectedTrackerId, pageSize, totalTrackers,
      },
    }) => ({
      trackers,
      sortBy,
      sortDirection,
      selectedTrackerId,
      pageSize,
      totalTrackers,
    }),
    {
      fetchTrackers,
      fetchMore,
      updateSort,
      moveSelection,
      updateSearchCriteria,
    },
  ),
  withHandlers({
    onMoveSelection: ({ moveSelection }) => (direction) => {
      moveSelection(direction);
    },
    onHandleSort: ({ updateSort, fetchTrackers }) => (sort) => {
      if (sort !== undefined) {
        const direction = sort.desc ? Directions.descending : Directions.ascending;
        const sortBy = sort.id === 'pipelineName' ? 'pipeline' : sort.id;
        updateSort(sortBy, direction);
        fetchTrackers();
      }
    },
    onHandleLoadMoreRows: ({ fetchMore }) => () => {
      fetchMore();
    },
  }),
  withProps(({
    trackers, selectedTrackerId, onHandleLoadMoreRows, totalTrackers,
  }) => {
    // We add an empty 'load more' row, but we need to make sure it's not there when we re-render.
    trackers = trackers.filter(tracker => tracker.filterId !== undefined);
    const allRecordsRetrieved = totalTrackers === trackers.length;
    trackers.push({});

    const tableData = trackers.map(({ filterId, priority, trackerPercent }) => ({
      filterId,
      pipelineName: 'TODO: awaiting backend re-write. Sorting broken too.',
      priority,
      progress: trackerPercent,
    }));

    return {
      selectedTracker: trackers.find(tracker => tracker.filterId === selectedTrackerId),
      tableColumns: [
        {
          Header: '',
          accessor: 'filterId',
          show: false,
        },
        {
          Header: 'Pipeline name',
          accessor: 'pipelineName',
          Cell: row => (row.original.filterId ? row.original.pipelineName : undefined),
        },
        {
          Header: 'Priority',
          accessor: 'priority',
          Cell: row =>
            (row.original.filterId ? (
              row.original.priority
            ) : (
              <Button
                disabled={allRecordsRetrieved}
                size="tiny"
                compact
                className="button border hoverable infinite-processing-list__load-more-button"
                onClick={() => onHandleLoadMoreRows()}
              >
                {allRecordsRetrieved ? All rows loaded : Load more rows}
              </Button>
            )),
        },
        {
          Header: 'Progress',
          accessor: 'progress',
          Cell: row =>
            (row.original.filterId ? (
              <Progress percent={row.original.progress} symbolClassName="flat-text" />
            ) : (
              undefined
            )),
        },
      ],
      tableData,
    };
  }),
  lifecycle({
    componentDidMount() {
      const { onMoveSelection, onHandlePageRight, onHandlePageLeft } = this.props;

      Mousetrap.bind('up', () => onMoveSelection('up'));
      Mousetrap.bind('down', () => onMoveSelection('down'));
      Mousetrap.bind('right', () => onHandlePageRight());
      Mousetrap.bind('left', () => onHandlePageLeft());
    },
  }),
);

const InfiniteProcessingList = ({
  sortBy,
  sortDirection,
  trackers,
  tableColumns,
  tableData,
  selectedTrackerId,
  pageSize,
  onHandleSort,
  onSelection,
  onHandleLoadMoreRows,
}) => (
  <ReactTable
    manual
    className="table__reactTable"
    sortable
    showPagination={false}
    pageSize={pageSize + 1}
    data={tableData}
    columns={tableColumns}
    onFetchData={(state, instance) => onHandleSort(state.sorted[0])}
    getTdProps={(state, rowInfo, column, instance) => ({
      onClick: (e, handleOriginal) => {
        if (rowInfo !== undefined) {
          onSelection(rowInfo.original.filterId, trackers);
        }

        // IMPORTANT! React-Table uses onClick internally to trigger
        // events like expanding SubComponents and pivots.
        // By default a custom 'onClick' handler will override this functionality.
        // If you want to fire the original onClick handler, call the
        // 'handleOriginal' function.
        if (handleOriginal) {
          handleOriginal();
        }
      },
    })}
    getTrProps={(state, rowInfo, column) => {
      // We don't want to see a hover on a row without data.
      // If a row is selected we want to see the selected color.
      const isSelected =
        selectedTrackerId !== undefined &&
        path(['original', 'filterId'], rowInfo) === selectedTrackerId;
      const hasData = path(['original', 'filterId'], rowInfo) !== undefined;
      let className;
      if (hasData) {
        className = isSelected ? 'selected hoverable' : 'hoverable';
      }
      return {
        className,
      };
    }}
  />
);

InfiniteProcessingList.propTypes = {
  onSelection: PropTypes.func.isRequired,
};

export default enhance(InfiniteProcessingList);
<commit_msg>Tidy up<commit_after>/*
 * Copyright 2018 Crown Copyright
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import React from 'react';
import PropTypes from 'prop-types';
import { path } from 'ramda';
import { connect } from 'react-redux';
import { compose, lifecycle, withProps, withHandlers } from 'recompose';
import Mousetrap from 'mousetrap';
import { Progress } from 'react-sweet-progress';
import 'react-sweet-progress/lib/style.css';
import ReactTable from 'react-table';
import 'react-table/react-table.css';
import { Button } from 'semantic-ui-react';

import { actionCreators, Directions } from '../redux';
import { fetchTrackers, fetchMore, TrackerSelection } from '../streamTasksResourceClient';

const {
  updateSort, moveSelection, updateSearchCriteria, pageRight, pageLeft,
} = actionCreators;

const enhance = compose(
  connect(
    ({
      processing: {
        trackers, sortBy, sortDirection, selectedTrackerId, pageSize, totalTrackers,
      },
    }) => ({
      trackers,
      sortBy,
      sortDirection,
      selectedTrackerId,
      pageSize,
      totalTrackers,
    }),
    {
      fetchTrackers,
      fetchMore,
      updateSort,
      moveSelection,
      updateSearchCriteria,
    },
  ),
  withHandlers({
    onMoveSelection: ({ moveSelection }) => (direction) => {
      moveSelection(direction);
    },
    onHandleSort: ({ updateSort, fetchTrackers }) => (sort) => {
      if (sort !== undefined) {
        const direction = sort.desc ? Directions.descending : Directions.ascending;
        const sortBy = sort.id === 'pipelineName' ? 'pipeline' : sort.id;
        updateSort(sortBy, direction);
        fetchTrackers();
      }
    },
    onHandleLoadMoreRows: ({ fetchMore }) => () => {
      fetchMore();
    },
  }),
  withProps(({
    trackers, selectedTrackerId, onHandleLoadMoreRows, totalTrackers,
  }) => {
    // We add an empty 'load more' row, but we need to make sure it's not there when we re-render.
    trackers = trackers.filter(tracker => tracker.filterId !== undefined);
    const allRecordsRetrieved = totalTrackers === trackers.length;
    trackers.push({});

    const tableData = trackers.map(({ filterId, priority, trackerPercent }) => ({
      filterId,
      pipelineName: 'TODO: awaiting backend re-write. Sorting broken too.',
      priority,
      progress: trackerPercent,
    }));

    return {
      selectedTracker: trackers.find(tracker => tracker.filterId === selectedTrackerId),
      tableColumns: [
        {
          Header: '',
          accessor: 'filterId',
          show: false,
        },
        {
          Header: 'Pipeline name',
          accessor: 'pipelineName',
          Cell: row => (row.original.filterId ? row.original.pipelineName : undefined),
        },
        {
          Header: 'Priority',
          accessor: 'priority',
          Cell: row =>
            (row.original.filterId ? (
              row.original.priority
            ) : (
              <Button
                disabled={allRecordsRetrieved}
                size="tiny"
                compact
                className="button border hoverable infinite-processing-list__load-more-button"
                onClick={() => onHandleLoadMoreRows()}
              >
                {allRecordsRetrieved ? All rows loaded : Load more rows}
              </Button>
            )),
        },
        {
          Header: 'Progress',
          accessor: 'progress',
          Cell: row =>
            (row.original.filterId ? (
              <Progress percent={row.original.progress} symbolClassName="flat-text" />
            ) : (
              undefined
            )),
        },
      ],
      tableData,
    };
  }),
  lifecycle({
    componentDidMount() {
      const { onMoveSelection } = this.props;

      Mousetrap.bind('up', () => onMoveSelection('up'));
      Mousetrap.bind('down', () => onMoveSelection('down'));
    },
  }),
);

const InfiniteProcessingList = ({
  sortBy,
  sortDirection,
  trackers,
  tableColumns,
  tableData,
  selectedTrackerId,
  pageSize,
  onHandleSort,
  onSelection,
  onHandleLoadMoreRows,
}) => (
  <ReactTable
    manual
    className="table__reactTable"
    sortable
    showPagination={false}
    pageSize={pageSize + 1}
    data={tableData}
    columns={tableColumns}
    onFetchData={(state, instance) => onHandleSort(state.sorted[0])}
    getTdProps={(state, rowInfo, column, instance) => ({
      onClick: (e, handleOriginal) => {
        if (rowInfo !== undefined) {
          onSelection(rowInfo.original.filterId, trackers);
        }

        // IMPORTANT! React-Table uses onClick internally to trigger
        // events like expanding SubComponents and pivots.
        // By default a custom 'onClick' handler will override this functionality.
        // If you want to fire the original onClick handler, call the
        // 'handleOriginal' function.
        if (handleOriginal) {
          handleOriginal();
        }
      },
    })}
    getTrProps={(state, rowInfo, column) => {
      // We don't want to see a hover on a row without data.
      // If a row is selected we want to see the selected color.
      const isSelected =
        selectedTrackerId !== undefined &&
        path(['original', 'filterId'], rowInfo) === selectedTrackerId;
      const hasData = path(['original', 'filterId'], rowInfo) !== undefined;
      let className;
      if (hasData) {
        className = isSelected ? 'selected hoverable' : 'hoverable';
      }
      return {
        className,
      };
    }}
  />
);

InfiniteProcessingList.propTypes = {
  onSelection: PropTypes.func.isRequired,
};

export default enhance(InfiniteProcessingList);
<|endoftext|>