<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Plato - components/Logbook/List/EventList.js</title>

  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta name="description" content="">
  <meta name="author" content="">

  <!--[if lt IE 9]>
  <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
  <![endif]-->

  <link href="../../assets/css/vendor/morris.css" rel="stylesheet" type="text/css">
  <link href="../../assets/css/vendor/bootstrap.css" rel="stylesheet" type="text/css">
  <link href="../../assets/css/vendor/font-awesome.css" rel="stylesheet" type="text/css">
  <link href="../../assets/css/vendor/codemirror.css" rel="stylesheet" type="text/css">
  <link href="../../assets/css/plato.css" rel="stylesheet" type="text/css">
  <link href="../../assets/css/plato-file.css" rel="stylesheet" type="text/css">

</head>

<body>

<div class="navbar navbar-fixed-top">
  <div class="container">
    <a class="navbar-brand" href="https://github.com/the-simian/es6-plato">ES6 Plato on Github</a>
    <ul class="nav navbar-nav">
      <li>
        <a href="../../index.html">Report Home</a>
      </li>
      <li class="active">
        <a href="display.html">Summary Display</a>
      </li>
    </ul>
  </div>
</div>

<div class="jumbotron">
  <div class="container">
    <h1>components/Logbook/List/EventList.js</h1>
  </div>
</div>

<div class="container aggregate-stats">
  <div class="row">
    <div class="col-md-6">
      <h2 class="header">Maintainability <a href="http://blogs.msdn.com/b/codeanalysis/archive/2007/11/20/maintainability-index-range-and-meaning.aspx"><i class="icon icon-info-sign" rel="popover" data-placement="top" data-trigger="hover" data-content="A value between 0 and 100 that represents the relative ease of maintaining the code. A high value means better maintainability." data-original-title="Maintainability Index"  data-container="body"></i></a></h2>
      <p class="stat">63.70</p>
    </div>
    <div class="col-md-6">
      <h2 class="header">Lines of code <i class="icon icon-info-sign" rel="popover" data-placement="top" data-trigger="hover" data-content="Source Lines of Code / Logical Lines of Code" data-original-title="SLOC/LSLOC" data-container="body"></i></h2>
      <p class="stat">249</p>
    </div>
  </div>
  <div class="row historical">
    <div class="col-md-6">
      <p id="chart_historical_maint" class="chart"></p>
    </div>
    <div class="col-md-6">
      <p id="chart_historical_sloc" class="chart"></p>
    </div>
  </div>
  <div class="row">
    <div class="col-md-6">
      <h2 class="header">Difficulty  <a href="http://en.wikipedia.org/wiki/Halstead_complexity_measures"><i class="icon icon-info-sign" rel="popover" data-placement="top" data-trigger="hover" data-content="The difficulty measure is related to the difficulty of the program to write or understand." data-original-title="Difficulty" data-container="body"></i></a></h2>
      <p class="stat">46.54</p>
    </div>
    <div class="col-md-6">
      <h2 class="header">Estimated Errors  <a href="http://en.wikipedia.org/wiki/Halstead_complexity_measures"><i class="icon icon-info-sign" rel="popover" data-placement="top" data-trigger="hover" data-content="Halstead's delivered bugs is an estimate for the number of errors in the implementation." data-original-title="Delivered Bugs" data-container="body"></i></a></h2>
      <p class="stat">2.56</p>
    </div>
  </div>
</div>

<div class="container charts">
  <div class="row">
    <h2 class="header">Function weight</h2>
  </div>
  <div class="row">
    <div class="col-md-6">
      <h3 class="chart-header">By Complexity <a href="http://en.wikipedia.org/wiki/Cyclomatic_complexity"><i class="icon icon-info-sign" rel="popover" data-placement="top" data-trigger="hover" data-content="This metric counts the number of distinct paths through a block of code. Lower values are better." data-original-title="Cyclomatic Complexity" data-container="body"></i></a></h3>
      <div id="fn-by-complexity" class="stat"></div>
    </div>
    <div class="col-md-6">
      <h3 class="chart-header">By SLOC  <i class="icon icon-info-sign" rel="popover" data-placement="top" data-trigger="hover" data-content="Source Lines of Code / Logical Lines of Code" data-original-title="SLOC/LSLOC" data-container="body"></i></h3>
      <div id="fn-by-sloc" class="stat"></div>
    </div>
  </div>
</div>

<div class="container">
  <div class="row">
    <textarea id="file-source" class="col-md-12">import React from &#039;react&#039;;
import PropTypes from &#039;prop-types&#039;
import _ from &#039;lodash&#039;
import Moment from &#039;moment&#039;
import { Table, OverlayTrigger, Tooltip, Glyphicon, Button, Label } from &#039;react-bootstrap&#039;
import { getContent, convertImagesToThumbnails, getPreviousVersionNumber } from &#039;../../../helpers/EventHelpers&#039;;
import { getOriginalEvent } from &#039;../../../helpers/EventHelpers&#039;
import EventIcon from &#039;./EventIcon.js&#039;;
import LazyLoad from &#039;react-lazyload&#039;;
import { EVENT_CATEGORY_COMMANDLINE, NOTIFICATION } from &#039;../../../constants/EventTypes.js&#039;;
import TagListInLine from &#039;../Tag/TagListInLine&#039;;
import LogbookPager from &#039;../LogbookPager&#039;;
require(&quot;./eventList.css&quot;);

/**
 * The list of the all events
 */
class EventList extends React.Component {
    collapse(items) {
        let collapsed = [];
        for (let i = 0; i &lt; items.length; i++) {
            const event = items[i];
            if (event.category) {
                if (event.category.toLowerCase() === EVENT_CATEGORY_COMMANDLINE &amp;&amp; !event.previousVersionEvent) {
                    let lastEvent = collapsed[collapsed.length - 1];
                    if (lastEvent &amp;&amp; lastEvent.category) {
                        if (lastEvent.category.toLowerCase() === EVENT_CATEGORY_COMMANDLINE) {
                            if (!lastEvent.events) {
                                lastEvent.events = [event];
                            } else {
                                lastEvent.events.push(event);
                            }
                            continue;
                        }
                    } else {
                        lastEvent.events = [event];
                    }
                }
            }
            collapsed.push(event);
        }
        return collapsed;
    }

    /** Returns the list of items to be displayed in the table: events + days */
    getItems() {
        let eventsCopy = _.cloneDeep(this.props.events);
        let items = [];
        let lastDate = null; // format DDMMYYYY
        for (let i = 0; i &lt; eventsCopy.length; i++) {
            let date = Moment(getOriginalEvent(eventsCopy[i]).creationDate).format(&quot;MMMM Do YYYY&quot;);
            if (date !== lastDate) {
                lastDate = date;
                items.push({ text: date, type: &quot;date&quot;, anchor: date });
            }

            items.push(eventsCopy[i]);
        }
        /** Add shadow to events that are before a date */
        for (let i = 0; i &lt; items.length; i++) {
            if (items[i].type === &quot;date&quot;) {
                if (items[i - 1]) {
                    items[i - 1].shadowBottomBorder = true;
                }
            }
        }
        /** Add shadow to the last one */
        if (items.length &gt; 0) {
            items[items.length - 1].shadowBottomBorder = true;
        }
        return this.collapse(items);
    }

    render() {

        if (!this.props.events || this.props.events.length === 0) {
            return null;
        }

        return &lt;div&gt;
            &lt;Table responsive style={{ fontSize: &#039;12px&#039;, borderCollapse: &#039;collapse&#039; }}&gt;
                &lt;tbody&gt;
                    {this.getItems().map((event, index) =&gt; {
                        if (event.type === &quot;date&quot;) {
                            return &lt;tr key={index} style={{ backgroundColor: &#039;#F0F0F6&#039;, color: &quot;gray&quot;, height: &#039;50px&#039; }} &gt;&lt;td style={{ textAlign: &#039;center&#039;, fontSize: &#039;18px&#039;, fontWeight: &#039;bold&#039; }} colSpan={6} &gt;&lt;a name={event.anchor}&gt;&lt;/a&gt;  {event.text}&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;;
                        }

                        return &lt;Event key={index} event={event} logbookContext={this.props.logbookContext} onEventClicked={this.props.onEventClicked} &gt;&lt;/Event&gt;
                    })}
                &lt;/tbody&gt;
            &lt;/Table&gt;

            &lt;div style={{ position: &#039;relative&#039;, height: &#039;45px&#039; }}&gt;
                &lt;div style={{ position: &#039;absolute&#039;, left: &#039;50%&#039;, marginLeft: &#039;-125px&#039;, width: &#039;250px&#039; }}&gt;
                    &lt;LogbookPager
                        activePage={this.props.activePage}
                        eventCount={this.props.eventCountBySelectionFilter}
                        onPageClicked={this.props.onPageClicked} /&gt;
                &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;
    }
}

EventList.propTypes = {
    /** the array of unsorted events as provided by the ICAT+ server */
    events: PropTypes.array.isRequired,
    /** the logbook context */
    logbookContext: PropTypes.object,
    /** Callback function triggered when the user clicks the edit icon */
    onEventCliked: PropTypes.func
}

export default EventList;


/** React componnent which renders an event. Here &#039;event can be the classical event as found in the logbook but 
 * could also be a list of event corresponding to a collapsed line containing several events 
 */
export class Event extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            collapsed: true
        }
        this.handleClick = this.handleClick.bind(this);
    }

    render() {

        let events = [this.props.event];

        if (this.props.event.events &amp;&amp; !this.state.collapsed) {
            events = events.concat(this.props.event.events);
        };


        /** Will print bottom shadow when it is the last event */
        var getShadow = (event) =&gt; {
            if (event.shadowBottomBorder) {
                return &#039;0px 0 15px -15px rgba(100,100,100, 0.8)&#039;;
            }
            return &#039; 0px 0px 9px 0px white, 0 9px 0px 0px white, 0 -9px 0px 0px white, 0px 0 15px -4px rgba(100,100,100, 0.8), 0px 0 15px -4px rgba(100,100,100, 0.8)&#039;;
        }

        var getButtonIcon = (event) =&gt; {
            if (this.props.logbookContext.isReleased === true) {
                return &lt;Glyphicon glyph=&#039;eye-open&#039; /&gt;;
            }
            else {
                if (getPreviousVersionNumber(event) === 0) {
                    return &lt;div&gt;&lt;Glyphicon glyph=&#039;comment&#039; style={{ width: &#039;30px&#039; }} /&gt;Comment&lt;/div&gt;;
                }
                return &lt;span&gt;&lt;Glyphicon glyph=&#039;edit&#039; style={{ width: &#039;30px&#039; }} /&gt; Edit&lt;/span&gt;;
            }
        }
        return events.map((event, index) =&gt; {
            return &lt;tr key={index} style={{ backgroundColor: &#039;#F0F0F6&#039; }}&gt;
                &lt;td style={{ width: &#039;16px&#039;, border: 0 }}&gt;
                    &lt;EventIcon event={event} /&gt;
                &lt;/td&gt;
                &lt;td className=&#039;borderTopSeparatorBetweenEvents&#039; style={{ width: &#039;16px&#039; }}&gt;
                    &lt;OverlayTrigger placement=&quot;right&quot; overlay={&lt;Tooltip id=&quot;tooltip&quot;&gt; &lt;p&gt; Events created on {Moment(getOriginalEvent(event).creationDate).format(&quot;MMMM Do YYYY, h:mm:ss a&quot;)} &lt;/p&gt; &lt;/Tooltip&gt;}&gt;
                        &lt;span style={{ cursor: &#039;pointer&#039;, color: &#039;black&#039;, margin: &#039;0px&#039; }}&gt;{Moment(getOriginalEvent(event).creationDate).format(&quot;HH:mm:ss&quot;)} &lt;/span&gt;
                    &lt;/OverlayTrigger&gt;
                &lt;/td&gt;
                &lt;td className=&#039;borderTopSeparatorBetweenEvents&#039; style={{ width: &#039;16px&#039; }}&gt;
                &lt;/td&gt;
                &lt;td className=&#039;borderTopSeparatorBetweenEventsInline&#039; style={{ paddingBottom: 0, backgroundColor: &#039;white&#039;, boxShadow: getShadow(event) }}&gt;
                    &lt;div style={{ marginLeft: &#039;20px&#039;, backgroundColor: &#039;white&#039; }}&gt;
                        &lt;LazyContentEvent event={event} /&gt;
                        {event.events &amp;&amp; this.state.collapsed ? &lt;Label style={{ color: &quot;blue&quot;, backgroundColor: &quot;#F8F8F8&quot;, cursor: &quot;pointer&quot; }} onClick={this.handleClick}  &gt;.... {event.events.length} command lines more&lt;/Label&gt; : null}
                    &lt;/div&gt;
                &lt;/td&gt;
                &lt;td className=&#039;borderTopSeparatorBetweenEvents&#039; style={{ width: &#039;200px&#039; }}&gt;
                    &lt;TagListInLine tags={event.tag} /&gt;
                &lt;/td&gt;
                &lt;td className=&#039;borderTopSeparatorBetweenEvents&#039; style={{ width: &#039;50px&#039; }}&gt;
                    &lt;Button bsStyle=&quot;default&quot; bsSize=&quot;small&quot; style={{ width: &#039;100px&#039;, position: &#039;static&#039;, padding: &#039;0px&#039; }} onClick={() =&gt; this.props.onEventClicked(event)}&gt;
                        {getButtonIcon(event)}
                    &lt;/Button&gt;
                &lt;/td&gt;
            &lt;/tr&gt;;
        });
    }

    handleClick(e) {
        this.setState({ collapsed: !this.state.collapsed });
    }

    getUncollapsedEvents() {
        return (&lt;tbody&gt; {this.props.event.events.map((event, index) =&gt; {
            return this.getEventContentBody(event);
        })}&lt;/tbody&gt;);
    }
}

Event.protypes = {
    /** A classical event or a structure representing a collapsed line containing several similar events */
    event: PropTypes.object,
    /** Context in which the logbook is run */
    logbookContext: PropTypes.object,
    /** Callback function triggered which the user clicks a link to edit/consult the detailed event */
    onEventClicked: PropTypes.func,
}


class LazyContentEvent extends React.Component {
    getHTMLContent(event) {
        return getContent(event.content, &#039;html&#039;) ? convertImagesToThumbnails(getContent(event.content, &#039;html&#039;)) : convertImagesToThumbnails(getContent(event.content, &#039;plainText&#039;));
    }

    getClassName(event) {
        if (event.type.toLowerCase() === &quot;annotation&quot;) {
            return &#039;text-primary&#039;;
        }
        if (event.category.toLowerCase() === &quot;error&quot;) {
            return &#039;text-danger&#039;;
        }
        if (event.category.toLowerCase() === &quot;commandline&quot;) {
            return &#039;text-muted&#039;;
        }
        return &#039;text-primary&#039;;
    }

    render() {
        let content = this.getHTMLContent(this.props.event)
        if (content) {
            content = content.replace(/\&lt;img/g, &quot;&lt;img onClick=&#039;makeFullScreen(this)&#039;  &quot;);
            /** For performance only events with images are lazy loaded */
            if ((this.props.event.type === NOTIFICATION) &amp;&amp; this.props.event.previousVersionEvent) {
                return &lt;LazyLoad&gt;
                    &lt;div className={this.getClassName(this.props.event)} dangerouslySetInnerHTML={{ __html: this.getHTMLContent(getOriginalEvent(this.props.event)) }} /&gt;
                    &lt;div className={this.getClassName(this.props.event)} dangerouslySetInnerHTML={{ __html: content }} /&gt;

                &lt;/LazyLoad&gt;;
            }
            /** For performance only events with images are lazy loaded */
            if (content.indexOf(&quot;img&quot;) !== -1) {
                return &lt;LazyLoad once&gt;
                    &lt;div className={this.getClassName(this.props.event)} dangerouslySetInnerHTML={{ __html: content }} /&gt;
                &lt;/LazyLoad&gt;;
            }
            return &lt;div className={this.getClassName(this.props.event)} dangerouslySetInnerHTML={{ __html: content }} /&gt;;
        } else {
            return &lt;div style={{ fontStyle: &#039;italic&#039;, color: &#039;#888888&#039; }}&gt;  There is no content &lt;/div&gt;
        }
    }
}</textarea>
  </div>
</div>

<footer class="footer">
  <div class="container">
    <p>.</p>
  </div>
</footer>

<script type="text/html" id="complexity-popover-template">
  <div class="complexity-notice">
    Complexity : {{ complexity.cyclomatic }} <br>
    Length : {{ complexity.halstead.length }} <br>
    Difficulty : {{ complexity.halstead.difficulty.toFixed(2) }} <br>
    Est # bugs : {{ complexity.halstead.bugs.toFixed(2) }}<br>
  </div>
</script>

<script type="text/javascript" src="../../assets/scripts/bundles/core-bundle.js"></script>
<script type="text/javascript" src="../../assets/scripts/bundles/codemirror.js"></script>
<script type="text/javascript" src="../../assets/scripts/codemirror.markpopovertext.js"></script>
<script type="text/javascript" src="report.js"></script>
<script type="text/javascript" src="report.history.js"></script>
<script type="text/javascript" src="../../assets/scripts/plato-file.js"></script>
</body>
</html>
