<commit_before>import classNames from "classnames";
import { Dropdown, Table, Tooltip } from "reactjs-components";
import { injectIntl } from "react-intl";
import { Link } from "react-router";
import React, { PropTypes } from "react";

import Icon from "#SRC/js/components/Icon";
import Links from "#SRC/js/constants/Links";
import NestedServiceLinks from "#SRC/js/components/NestedServiceLinks";
import ResourceTableUtil from "#SRC/js/utils/ResourceTableUtil";
import StringUtil from "#SRC/js/utils/StringUtil";
import TableUtil from "#SRC/js/utils/TableUtil";
import Units from "#SRC/js/utils/Units";
import UserActions from "#SRC/js/constants/UserActions";

import HealthBar from "../../components/HealthBar";
import Pod from "../../structs/Pod";
import Service from "../../structs/Service";
import ServiceActionItem from "../../constants/ServiceActionItem";
import ServiceStatusWarning from "../../components/ServiceStatusWarning";
import ServiceTableHeaderLabels from "../../constants/ServiceTableHeaderLabels";
import ServiceTableUtil from "../../utils/ServiceTableUtil";
import ServiceTree from "../../structs/ServiceTree";

const StatusMapping = {
  Running: "running-state"
};

const columnClasses = {
  name: "service-table-column-name",
  status: "service-table-column-status",
  cpus: "service-table-column-cpus",
  mem: "service-table-column-mem",
  disk: "service-table-column-disk",
  actions: "service-table-column-actions"
};

const METHODS_TO_BIND = [
  "onActionsItemSelection",
  "renderHeadline",
  "renderStats",
  "renderStatus",
  "renderServiceActions"
];

class ServicesTable extends React.Component {
  constructor() {
    super(...arguments);

    METHODS_TO_BIND.forEach(method => {
      this[method] = this[method].bind(this);
    });
  }

  getOpenInNewWindowLink(service) {
    // This might be a serviceTree and therefore we need this check
    // And getWebURL might therefore not be available
    if (!this.hasWebUI(service)) {
      return null;
    }

    return (

        <Icon
          color="neutral"
          className="icon-margin-left"
          id="open-external"
          size="mini"
        />

    );
  }

  onActionsItemSelection(service, actionItem) {
    const { modalHandlers } = this.context;

    switch (actionItem.id) {
      case ServiceActionItem.SCALE:
        modalHandlers.scaleService({ service });
        break;
      case ServiceActionItem.OPEN:
        modalHandlers.openServiceUI({ service });
        break;
      case ServiceActionItem.RESTART:
        modalHandlers.restartService({ service });
        break;
      case ServiceActionItem.RESUME:
        modalHandlers.resumeService({ service });
        break;
      case ServiceActionItem.SUSPEND:
        modalHandlers.suspendService({ service });
        break;
      case ServiceActionItem.DELETE:
        modalHandlers.deleteService({ service });
        break;
    }
  }

  getServiceLink(service) {
    const id = encodeURIComponent(service.getId());
    const isGroup = service instanceof ServiceTree;
    const serviceLink = isGroup
      ? `/services/overview/${id}`
      : `/services/detail/${id}`;

    if (this.props.isFiltered) {
      return (
        <NestedServiceLinks
          serviceID={id}
          className="service-breadcrumb"
          majorLinkClassName="service-breadcrumb-service-id"
          minorLinkWrapperClassName="service-breadcrumb-crumb"
        />
      );
    }

    return (
      <Link className="table-cell-link-primary text-overflow" to={serviceLink}>
        {service.getName()}
      </Link>
    );
  }

  getImage(service) {
    if (service instanceof ServiceTree) {
      // Get serviceTree image/icon
      return (
        <Icon
          className="icon-margin-right"
          color="grey"
          id="folder"
          size="mini"
        />
      );
    }

    // Get service image/icon
    return (

    );
  }

  hasWebUI(service) {
    return (
      service instanceof Service &&
      service.getWebURL() != null &&
      service.getWebURL() !== ""
    );
  }

  renderHeadline(prop, service) {
    const id = encodeURIComponent(service.getId());
    const isGroup = service instanceof ServiceTree;
    const serviceLink = isGroup
      ? `/services/overview/${id}`
      : `/services/detail/${id}`;

    return (

        <Link className="table-cell-icon" to={serviceLink}>
          {this.getImage(service)}
        </Link>

          {this.getServiceLink(service)}
          {this.getOpenInNewWindowLink(service)}

    );
  }

  renderServiceActions(prop, service) {
    const isGroup = service instanceof ServiceTree;
    const isPod = service instanceof Pod;
    const isSingleInstanceApp = service.getLabels()
      .MARATHON_SINGLE_INSTANCE_APP;
    const instancesCount = service.getInstancesCount();
    const scaleText = isGroup ? "Scale By" : "Scale";

    const dropdownItems = [
      {
        className: "hidden",
        id: ServiceActionItem.MORE,
        html: "",
        selectedHtml: <Icon id="ellipsis-vertical" size="mini" />
      },
      {
        className: classNames({
          hidden: !this.hasWebUI(service)
        }),
        id: ServiceActionItem.OPEN,
        html: this.props.intl.formatMessage({
          id: "SERVICE_ACTIONS.OPEN_SERVICE"
        })
      },
      {
        className: classNames({
          hidden: (isGroup && instancesCount === 0) || isSingleInstanceApp
        }),
        id: ServiceActionItem.SCALE,
        html: scaleText
      },
      {
        className: classNames({
          hidden: isPod || isGroup || instancesCount === 0
        }),
        id: ServiceActionItem.RESTART,
        html: "Restart"
      },
      {
        className: classNames({
          hidden: instancesCount === 0
        }),
        id: ServiceActionItem.SUSPEND,
        html: "Suspend"
      },
      {
        className: classNames({
          hidden: isGroup || instancesCount > 0
        }),
        id: ServiceActionItem.RESUME,
        html: "Resume"
      },
      {
        id: ServiceActionItem.DELETE,
        html: (

            {StringUtil.capitalize(UserActions.DELETE)}

        )
      }
    ];

    return (
      <Tooltip content="More actions">
        <Dropdown
          anchorRight={true}
          buttonClassName="button button-mini button-link"
          dropdownMenuClassName="dropdown-menu"
          dropdownMenuListClassName="dropdown-menu-list"
          dropdownMenuListItemClassName="clickable"
          wrapperClassName="dropdown flush-bottom table-cell-icon"
          items={dropdownItems}
          persistentID={ServiceActionItem.MORE}
          onItemSelection={this.onActionsItemSelection.bind(this, service)}
          scrollContainer=".gm-scroll-view"
          scrollContainerParentSelector=".gm-prevented"
          title="More actions"
          transition={true}
          transitionName="dropdown-menu"
        />
      </Tooltip>
    );
  }

  renderStatus(prop, service) {
    const instancesCount = service.getInstancesCount();
    const serviceId = service.getId();
    const serviceStatus = service.getStatus();
    const serviceStatusClassSet = StatusMapping[serviceStatus] || "";
    const tasksSummary = service.getTasksSummary();
    const { tasksRunning } = tasksSummary;

    const isDeploying = serviceStatus === "Deploying";

    const conciseOverview = ` (${tasksRunning}/${instancesCount})`;
    let verboseOverview = ` (${tasksRunning} ${StringUtil.pluralize("Instance", tasksRunning)})`;
    if (tasksRunning !== instancesCount) {
      verboseOverview = ` (${tasksRunning} of ${instancesCount} Instances)`;
    }

    return (

          <HealthBar
            isDeploying={isDeploying}
            key={serviceId}
            tasksSummary={tasksSummary}
            instancesCount={instancesCount}
          />

          {serviceStatus}
          {verboseOverview}
          {conciseOverview}
          <ServiceStatusWarning item={service} />

    );
  }

  renderStats(prop, service) {
    const resource = service.getResources()[prop];

    return (

        {Units.formatResource(prop, resource)}

    );
  }

  getCellClasses(prop, sortBy, row) {
    const isHeader = row == null;

    return classNames(columnClasses[prop], {
      active: prop === sortBy.prop,
      clickable: isHeader
    });
  }

  getColumns() {
    const heading = ResourceTableUtil.renderHeading(ServiceTableHeaderLabels);

    return [
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "name",
        render: this.renderHeadline,
        sortable: true,
        sortFunction: ServiceTableUtil.propCompareFunctionFactory,
        heading
      },
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "status",
        helpText: (

            {"At-a-glance overview of the global application or group state. "}

              Read more
            .

        ),
        render: this.renderStatus,
        sortable: true,
        sortFunction: ServiceTableUtil.propCompareFunctionFactory,
        heading
      },
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "cpus",
        render: this.renderStats,
        sortable: true,
        sortFunction: ServiceTableUtil.propCompareFunctionFactory,
        heading
      },
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "mem",
        render: this.renderStats,
        sortable: true,
        sortFunction: ServiceTableUtil.propCompareFunctionFactory,
        heading
      },
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "disk",
        render: this.renderStats,
        sortable: true,
        sortFunction: ServiceTableUtil.propCompareFunctionFactory,
        heading
      },
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "actions",
        render: this.renderServiceActions,
        sortable: false,
        heading() {
          return null;
        }
      }
    ];
  }

  getColGroup() {
    return (
      <colgroup>
        <col className={columnClasses.name} />
        <col className={columnClasses.status} />
        <col className={columnClasses.cpus} />
        <col className={columnClasses.mem} />
        <col className={columnClasses.disk} />
        <col className={columnClasses.actions} />
      </colgroup>
    );
  }

  render() {
    return (

    );
  }
}

ServicesTable.contextTypes = {
  modalHandlers: PropTypes.shape({
    scaleService: PropTypes.func,
    restartService: PropTypes.func,
    resumeService: PropTypes.func,
    suspendService: PropTypes.func,
    deleteService: PropTypes.func
  }).isRequired
};

ServicesTable.defaultProps = {
  isFiltered: false,
  services: []
};

ServicesTable.propTypes = {
  isFiltered: PropTypes.bool,
  services: PropTypes.array
};

module.exports = injectIntl(ServicesTable);
<commit_msg>bug(ServicesTable): Replace running count with number of running tasks<commit_after>import classNames from "classnames";
import { Dropdown, Table, Tooltip } from "reactjs-components";
import { injectIntl } from "react-intl";
import { Link } from "react-router";
import React, { PropTypes } from "react";

import Icon from "#SRC/js/components/Icon";
import Links from "#SRC/js/constants/Links";
import NestedServiceLinks from "#SRC/js/components/NestedServiceLinks";
import ResourceTableUtil from "#SRC/js/utils/ResourceTableUtil";
import StringUtil from "#SRC/js/utils/StringUtil";
import TableUtil from "#SRC/js/utils/TableUtil";
import Units from "#SRC/js/utils/Units";
import UserActions from "#SRC/js/constants/UserActions";

import HealthBar from "../../components/HealthBar";
import Pod from "../../structs/Pod";
import Service from "../../structs/Service";
import ServiceActionItem from "../../constants/ServiceActionItem";
import ServiceStatusWarning from "../../components/ServiceStatusWarning";
import ServiceTableHeaderLabels from "../../constants/ServiceTableHeaderLabels";
import ServiceTableUtil from "../../utils/ServiceTableUtil";
import ServiceTree from "../../structs/ServiceTree";

const StatusMapping = {
  Running: "running-state"
};

const columnClasses = {
  name: "service-table-column-name",
  status: "service-table-column-status",
  cpus: "service-table-column-cpus",
  mem: "service-table-column-mem",
  disk: "service-table-column-disk",
  actions: "service-table-column-actions"
};

const METHODS_TO_BIND = [
  "onActionsItemSelection",
  "renderHeadline",
  "renderStats",
  "renderStatus",
  "renderServiceActions"
];

class ServicesTable extends React.Component {
  constructor() {
    super(...arguments);

    METHODS_TO_BIND.forEach(method => {
      this[method] = this[method].bind(this);
    });
  }

  getOpenInNewWindowLink(service) {
    // This might be a serviceTree and therefore we need this check
    // And getWebURL might therefore not be available
    if (!this.hasWebUI(service)) {
      return null;
    }

    return (

        <Icon
          color="neutral"
          className="icon-margin-left"
          id="open-external"
          size="mini"
        />

    );
  }

  onActionsItemSelection(service, actionItem) {
    const { modalHandlers } = this.context;

    switch (actionItem.id) {
      case ServiceActionItem.SCALE:
        modalHandlers.scaleService({ service });
        break;
      case ServiceActionItem.OPEN:
        modalHandlers.openServiceUI({ service });
        break;
      case ServiceActionItem.RESTART:
        modalHandlers.restartService({ service });
        break;
      case ServiceActionItem.RESUME:
        modalHandlers.resumeService({ service });
        break;
      case ServiceActionItem.SUSPEND:
        modalHandlers.suspendService({ service });
        break;
      case ServiceActionItem.DELETE:
        modalHandlers.deleteService({ service });
        break;
    }
  }

  getServiceLink(service) {
    const id = encodeURIComponent(service.getId());
    const isGroup = service instanceof ServiceTree;
    const serviceLink = isGroup
      ? `/services/overview/${id}`
      : `/services/detail/${id}`;

    if (this.props.isFiltered) {
      return (
        <NestedServiceLinks
          serviceID={id}
          className="service-breadcrumb"
          majorLinkClassName="service-breadcrumb-service-id"
          minorLinkWrapperClassName="service-breadcrumb-crumb"
        />
      );
    }

    return (
      <Link className="table-cell-link-primary text-overflow" to={serviceLink}>
        {service.getName()}
      </Link>
    );
  }

  getImage(service) {
    if (service instanceof ServiceTree) {
      // Get serviceTree image/icon
      return (
        <Icon
          className="icon-margin-right"
          color="grey"
          id="folder"
          size="mini"
        />
      );
    }

    // Get service image/icon
    return (

    );
  }

  hasWebUI(service) {
    return (
      service instanceof Service &&
      service.getWebURL() != null &&
      service.getWebURL() !== ""
    );
  }

  renderHeadline(prop, service) {
    const id = encodeURIComponent(service.getId());
    const isGroup = service instanceof ServiceTree;
    const serviceLink = isGroup
      ? `/services/overview/${id}`
      : `/services/detail/${id}`;

    return (

        <Link className="table-cell-icon" to={serviceLink}>
          {this.getImage(service)}
        </Link>

          {this.getServiceLink(service)}
          {this.getOpenInNewWindowLink(service)}

    );
  }

  renderServiceActions(prop, service) {
    const isGroup = service instanceof ServiceTree;
    const isPod = service instanceof Pod;
    const isSingleInstanceApp = service.getLabels()
      .MARATHON_SINGLE_INSTANCE_APP;
    const instancesCount = service.getInstancesCount();
    const scaleText = isGroup ? "Scale By" : "Scale";

    const dropdownItems = [
      {
        className: "hidden",
        id: ServiceActionItem.MORE,
        html: "",
        selectedHtml: <Icon id="ellipsis-vertical" size="mini" />
      },
      {
        className: classNames({
          hidden: !this.hasWebUI(service)
        }),
        id: ServiceActionItem.OPEN,
        html: this.props.intl.formatMessage({
          id: "SERVICE_ACTIONS.OPEN_SERVICE"
        })
      },
      {
        className: classNames({
          hidden: (isGroup && instancesCount === 0) || isSingleInstanceApp
        }),
        id: ServiceActionItem.SCALE,
        html: scaleText
      },
      {
        className: classNames({
          hidden: isPod || isGroup || instancesCount === 0
        }),
        id: ServiceActionItem.RESTART,
        html: "Restart"
      },
      {
        className: classNames({
          hidden: instancesCount === 0
        }),
        id: ServiceActionItem.SUSPEND,
        html: "Suspend"
      },
      {
        className: classNames({
          hidden: isGroup || instancesCount > 0
        }),
        id: ServiceActionItem.RESUME,
        html: "Resume"
      },
      {
        id: ServiceActionItem.DELETE,
        html: (

            {StringUtil.capitalize(UserActions.DELETE)}

        )
      }
    ];

    return (
      <Tooltip content="More actions">
        <Dropdown
          anchorRight={true}
          buttonClassName="button button-mini button-link"
          dropdownMenuClassName="dropdown-menu"
          dropdownMenuListClassName="dropdown-menu-list"
          dropdownMenuListItemClassName="clickable"
          wrapperClassName="dropdown flush-bottom table-cell-icon"
          items={dropdownItems}
          persistentID={ServiceActionItem.MORE}
          onItemSelection={this.onActionsItemSelection.bind(this, service)}
          scrollContainer=".gm-scroll-view"
          scrollContainerParentSelector=".gm-prevented"
          title="More actions"
          transition={true}
          transitionName="dropdown-menu"
        />
      </Tooltip>
    );
  }

  renderStatus(prop, service) {
    const instancesCount = service.getInstancesCount();
    const serviceId = service.getId();
    const serviceStatus = service.getStatus();
    const serviceStatusClassSet = StatusMapping[serviceStatus] || "";
    const tasksSummary = service.getTasksSummary();
    const tasksRunning = service.getTaskCount();
    const isDeploying = serviceStatus === "Deploying";

    const conciseOverview = ` (${tasksRunning}/${instancesCount})`;
    let verboseOverview = ` (${tasksRunning} ${StringUtil.pluralize("Instance", tasksRunning)})`;
    if (tasksRunning !== instancesCount) {
      verboseOverview = ` (${tasksRunning} of ${instancesCount} Instances)`;
    }

    return (

          <HealthBar
            isDeploying={isDeploying}
            key={serviceId}
            tasksSummary={tasksSummary}
            instancesCount={instancesCount}
          />

          {serviceStatus}
          {verboseOverview}
          {conciseOverview}
          <ServiceStatusWarning item={service} />

    );
  }

  renderStats(prop, service) {
    const resource = service.getResources()[prop];

    return (

        {Units.formatResource(prop, resource)}

    );
  }

  getCellClasses(prop, sortBy, row) {
    const isHeader = row == null;

    return classNames(columnClasses[prop], {
      active: prop === sortBy.prop,
      clickable: isHeader
    });
  }

  getColumns() {
    const heading = ResourceTableUtil.renderHeading(ServiceTableHeaderLabels);

    return [
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "name",
        render: this.renderHeadline,
        sortable: true,
        sortFunction: ServiceTableUtil.propCompareFunctionFactory,
        heading
      },
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "status",
        helpText: (

            {"At-a-glance overview of the global application or group state. "}

              Read more
            .

        ),
        render: this.renderStatus,
        sortable: true,
        sortFunction: ServiceTableUtil.propCompareFunctionFactory,
        heading
      },
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "cpus",
        render: this.renderStats,
        sortable: true,
        sortFunction: ServiceTableUtil.propCompareFunctionFactory,
        heading
      },
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "mem",
        render: this.renderStats,
        sortable: true,
        sortFunction: ServiceTableUtil.propCompareFunctionFactory,
        heading
      },
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "disk",
        render: this.renderStats,
        sortable: true,
        sortFunction: ServiceTableUtil.propCompareFunctionFactory,
        heading
      },
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "actions",
        render: this.renderServiceActions,
        sortable: false,
        heading() {
          return null;
        }
      }
    ];
  }

  getColGroup() {
    return (
      <colgroup>
        <col className={columnClasses.name} />
        <col className={columnClasses.status} />
        <col className={columnClasses.cpus} />
        <col className={columnClasses.mem} />
        <col className={columnClasses.disk} />
        <col className={columnClasses.actions} />
      </colgroup>
    );
  }

  render() {
    return (

    );
  }
}

ServicesTable.contextTypes = {
  modalHandlers: PropTypes.shape({
    scaleService: PropTypes.func,
    restartService: PropTypes.func,
    resumeService: PropTypes.func,
    suspendService: PropTypes.func,
    deleteService: PropTypes.func
  }).isRequired
};

ServicesTable.defaultProps = {
  isFiltered: false,
  services: []
};

ServicesTable.propTypes = {
  isFiltered: PropTypes.bool,
  services: PropTypes.array
};

module.exports = injectIntl(ServicesTable);
<|endoftext|>


<commit_before>import classNames from "classnames";
import { Dropdown, Table, Tooltip } from "reactjs-components";
import { injectIntl } from "react-intl";
import { Link, routerShape } from "react-router";
import React, { PropTypes } from "react";
import { Hooks } from "PluginSDK";

import Icon from "#SRC/js/components/Icon";
import Links from "#SRC/js/constants/Links";
import NestedServiceLinks from "#SRC/js/components/NestedServiceLinks";
import ResourceTableUtil from "#SRC/js/utils/ResourceTableUtil";
import StringUtil from "#SRC/js/utils/StringUtil";
import TableUtil from "#SRC/js/utils/TableUtil";
import Units from "#SRC/js/utils/Units";
import { isSDKService } from "#SRC/js/utils/ServiceUtil";

import HealthBar from "../../components/HealthBar";
import Pod from "../../structs/Pod";
import Service from "../../structs/Service";
import ServiceActionDisabledModal
  from "../../components/modals/ServiceActionDisabledModal";
import {
  DELETE,
  EDIT,
  MORE,
  OPEN,
  RESTART,
  RESUME,
  SCALE,
  SUSPEND
} from "../../constants/ServiceActionItem";
import ServiceActionLabels from "../../constants/ServiceActionLabels";
import ServiceStatusTypes from "../../constants/ServiceStatusTypes";
import ServiceStatusWarning from "../../components/ServiceStatusWarning";
import ServiceTableHeaderLabels from "../../constants/ServiceTableHeaderLabels";
import ServiceTableUtil from "../../utils/ServiceTableUtil";
import ServiceTree from "../../structs/ServiceTree";

const StatusMapping = {
  Running: "running-state"
};

const columnClasses = {
  name: "service-table-column-name",
  status: "service-table-column-status",
  instances: "service-table-column-instances",
  cpus: "service-table-column-cpus",
  mem: "service-table-column-mem",
  disk: "service-table-column-disk",
  actions: "service-table-column-actions"
};

const METHODS_TO_BIND = [
  "onActionsItemSelection",
  "handleServiceAction",
  "handleActionDisabledModalOpen",
  "handleActionDisabledModalClose",
  "renderHeadline",
  "renderStats",
  "renderStatus",
  "renderServiceActions"
];

class ServicesTable extends React.Component {
  constructor() {
    super(...arguments);

    this.state = { actionDisabledService: null };

    METHODS_TO_BIND.forEach(method => {
      this[method] = this[method].bind(this);
    });
  }

  onActionsItemSelection(service, actionItem) {
    const isGroup = service instanceof ServiceTree;
    let containsSDKService = false;

    if (isGroup) {
      containsSDKService =
        // #findItem will flatten the service tree
        service.findItem(function(item) {
          return item instanceof Service && isSDKService(item);
        }) != null;
    }

    // We still want to support the `open` action to display the web view
    if (
      actionItem.id !== DELETE &&
      (containsSDKService || isSDKService(service)) &&
      !Hooks.applyFilter(
        "isEnabledSDKAction",
        actionItem.id === EDIT || actionItem.id === OPEN,
        actionItem.id
      )
    ) {
      this.handleActionDisabledModalOpen(service, actionItem.id);
    } else {
      this.handleServiceAction(service, actionItem.id);
    }
  }

  handleServiceAction(service, actionID) {
    const { modalHandlers, router } = this.context;

    switch (actionID) {
      case EDIT:
        router.push(
          `/services/detail/${encodeURIComponent(service.getId())}/edit/`
        );
        break;
      case SCALE:
        modalHandlers.scaleService({ service });
        break;
      case OPEN:
        modalHandlers.openServiceUI({ service });
        break;
      case RESTART:
        modalHandlers.restartService({ service });
        break;
      case RESUME:
        modalHandlers.resumeService({ service });
        break;
      case SUSPEND:
        modalHandlers.suspendService({ service });
        break;
      case DELETE:
        modalHandlers.deleteService({ service });
        break;
    }
  }

  handleActionDisabledModalOpen(actionDisabledService, actionDisabledID) {
    this.setState({ actionDisabledService, actionDisabledID });
  }

  handleActionDisabledModalClose() {
    this.setState({ actionDisabledService: null, actionDisabledID: null });
  }

  getOpenInNewWindowLink(service) {
    // This might be a serviceTree and therefore we need this check
    // And getWebURL might therefore not be available
    if (!this.hasWebUI(service)) {
      return null;
    }

    return (

        <Icon
          color="neutral"
          className="icon-margin-left"
          id="open-external"
          size="mini"
        />

    );
  }

  getServiceLink(service) {
    const id = encodeURIComponent(service.getId());
    const isGroup = service instanceof ServiceTree;
    const serviceLink = isGroup
      ? `/services/overview/${id}`
      : `/services/detail/${id}`;

    if (this.props.isFiltered) {
      return (
        <NestedServiceLinks
          serviceID={id}
          className="service-breadcrumb"
          majorLinkClassName="service-breadcrumb-service-id"
          minorLinkWrapperClassName="service-breadcrumb-crumb"
        />
      );
    }

    return (
      <Link className="table-cell-link-primary text-overflow" to={serviceLink}>
        {service.getName()}
      </Link>
    );
  }

  getImage(service) {
    if (service instanceof ServiceTree) {
      // Get serviceTree image/icon
      return (
        <Icon
          className="icon-margin-right"
          color="grey"
          id="folder"
          size="mini"
        />
      );
    }

    // Get service image/icon
    return (

    );
  }

  hasWebUI(service) {
    return (
      service instanceof Service &&
      !isSDKService(service) &&
      service.getWebURL() != null &&
      service.getWebURL() !== ""
    );
  }

  renderHeadline(prop, service) {
    const id = encodeURIComponent(service.getId());
    const isGroup = service instanceof ServiceTree;
    const serviceLink = isGroup
      ? `/services/overview/${id}`
      : `/services/detail/${id}`;

    return (

        <Link className="table-cell-icon" to={serviceLink}>
          {this.getImage(service)}
        </Link>

          {this.getServiceLink(service)}
          {this.getOpenInNewWindowLink(service)}

    );
  }

  renderServiceActions(prop, service) {
    const isGroup = service instanceof ServiceTree;
    const isPod = service instanceof Pod;
    const isSingleInstanceApp = service.getLabels()
      .MARATHON_SINGLE_INSTANCE_APP;
    const instancesCount = service.getInstancesCount();
    const scaleTextID = isGroup
      ? ServiceActionLabels.scale_by
      : ServiceActionLabels[SCALE];
    const isSDK = isSDKService(service);

    const actions = [];

    actions.push({
      className: "hidden",
      id: MORE,
      html: "",
      selectedHtml: <Icon id="ellipsis-vertical" size="mini" />
    });

    if (this.hasWebUI(service)) {
      actions.push({
        id: OPEN,
        html: this.props.intl.formatMessage({ id: ServiceActionLabels.open })
      });
    }

    if (!isGroup) {
      actions.push({
        id: EDIT,
        html: this.props.intl.formatMessage({ id: ServiceActionLabels.edit })
      });
    }

    // isSingleInstanceApp = Framework main scheduler
    // instancesCount = service instances
    if ((isGroup && instancesCount > 0) || (!isGroup && !isSingleInstanceApp)) {
      actions.push({
        id: SCALE,
        html: this.props.intl.formatMessage({ id: scaleTextID })
      });
    }

    if (!isPod && !isGroup && instancesCount > 0 && !isSDK) {
      actions.push({
        id: RESTART,
        html: this.props.intl.formatMessage({
          id: ServiceActionLabels[RESTART]
        })
      });
    }

    if (instancesCount > 0 && !isSDK) {
      actions.push({
        id: SUSPEND,
        html: this.props.intl.formatMessage({
          id: ServiceActionLabels[SUSPEND]
        })
      });
    }

    if (!isGroup && instancesCount === 0 && !isSDK) {
      actions.push({
        id: RESUME,
        html: this.props.intl.formatMessage({
          id: ServiceActionLabels[RESUME]
        })
      });
    }

    actions.push({
      id: DELETE,
      html: (

          {this.props.intl.formatMessage({
            id: ServiceActionLabels[DELETE]
          })}

      )
    });

    return (
      <Tooltip content="More actions">
        <Dropdown
          anchorRight={true}
          buttonClassName="button button-mini button-link"
          dropdownMenuClassName="dropdown-menu"
          dropdownMenuListClassName="dropdown-menu-list"
          dropdownMenuListItemClassName="clickable"
          wrapperClassName="dropdown flush-bottom table-cell-icon"
          items={actions}
          persistentID={MORE}
          onItemSelection={this.onActionsItemSelection.bind(this, service)}
          scrollContainer=".gm-scroll-view"
          scrollContainerParentSelector=".gm-prevented"
          title="More actions"
          transition={true}
          transitionName="dropdown-menu"
        />
      </Tooltip>
    );
  }

  renderStatus(prop, service) {
    const instancesCount = service.getInstancesCount();
    const serviceId = service.getId();
    const serviceStatusText = service.getStatus();
    const serviceStatusClassSet = StatusMapping[serviceStatusText] || "";
    const { key: serviceStatusKey } = service.getServiceStatus();
    const tasksSummary = service.getTasksSummary();
    const tasksRunning = service.getTaskCount();
    const isDeploying =
      serviceStatusKey === ServiceStatusTypes.WAITING ||
      serviceStatusKey === ServiceStatusTypes.DEPLOYING;

    const conciseOverview = tasksRunning === instancesCount
      ? ` (${tasksRunning})`
      : ` (${tasksRunning}/${instancesCount})`;

    const verboseOverview = tasksRunning === instancesCount
      ? ` (${tasksRunning} ${StringUtil.pluralize("Instance", tasksRunning)})`
      : ` (${tasksRunning} of ${instancesCount} Instances)`;

    return (

          <HealthBar
            isDeploying={isDeploying}
            key={serviceId}
            tasksSummary={tasksSummary}
            instancesCount={instancesCount}
          />

            {serviceStatusText}

          {verboseOverview}
          {conciseOverview}
          <ServiceStatusWarning item={service} />

    );
  }

  renderStats(prop, service) {
    const resource = service.getResources()[prop];

    return (

        {Units.formatResource(prop, resource)}

    );
  }

  renderInstances(prop, service) {
    const instancesCount = service.getInstancesCount();
    const tasksRunning = service.getTaskCount();
    const overview = tasksRunning === instancesCount
      ? ` ${tasksRunning}`
      : ` ${tasksRunning}/${instancesCount}`;

    const content = isNaN(instancesCount) ? "\u2014" : overview;

    return (

        {content}

    );
  }

  getCellClasses(prop, sortBy, row) {
    const isHeader = row == null;

    return classNames(columnClasses[prop], {
      active: prop === sortBy.prop,
      clickable: isHeader
    });
  }

  getColumns() {
    const heading = ResourceTableUtil.renderHeading(ServiceTableHeaderLabels);

    return [
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "name",
        render: this.renderHeadline,
        sortable: true,
        sortFunction: ServiceTableUtil.propCompareFunctionFactory,
        heading
      },
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "status",
        helpText: (

            {"At-a-glance overview of the global application or group state. "}

              Read more
            .

        ),
        render: this.renderStatus,
        sortable: true,
        sortFunction: ServiceTableUtil.propCompareFunctionFactory,
        heading
      },
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "instances",
        render: this.renderInstances,
        sortable: true,
        sortFunction: ServiceTableUtil.propCompareFunctionFactory,
        heading
      },
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "cpus",
        render: this.renderStats,
        sortable: true,
        sortFunction: ServiceTableUtil.propCompareFunctionFactory,
        heading
      },
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "mem",
        render: this.renderStats,
        sortable: true,
        sortFunction: ServiceTableUtil.propCompareFunctionFactory,
        heading
      },
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "disk",
        render: this.renderStats,
        sortable: true,
        sortFunction: ServiceTableUtil.propCompareFunctionFactory,
        heading
      },
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "actions",
        render: this.renderServiceActions,
        sortable: false,
        heading() {
          return null;
        }
      }
    ];
  }

  getColGroup() {
    return (
      <colgroup>
        <col className={columnClasses.name} />
        <col className={columnClasses.status} />
        <col className={columnClasses.instances} />
        <col className={columnClasses.cpus} />
        <col className={columnClasses.mem} />
        <col className={columnClasses.disk} />
        <col className={columnClasses.actions} />
      </colgroup>
    );
  }

  render() {
    const { actionDisabledService, actionDisabledID } = this.state;

    return (

        <ServiceActionDisabledModal
          actionID={actionDisabledID}
          open={actionDisabledService != null}
          onClose={this.handleActionDisabledModalClose}
          service={actionDisabledService}
        />

    );
  }
}

ServicesTable.contextTypes = {
  modalHandlers: PropTypes.shape({
    scaleService: PropTypes.func,
    restartService: PropTypes.func,
    resumeService: PropTypes.func,
    suspendService: PropTypes.func,
    deleteService: PropTypes.func
  }).isRequired,
  router: routerShape
};

ServicesTable.defaultProps = {
  isFiltered: false,
  services: []
};

ServicesTable.propTypes = {
  isFiltered: PropTypes.bool,
  services: PropTypes.array
};

module.exports = injectIntl(ServicesTable);
<commit_msg>feat(ServicesTable): ensure type Ensure instancesCount is the type expected<commit_after>import classNames from "classnames";
import { Dropdown, Table, Tooltip } from "reactjs-components";
import { injectIntl } from "react-intl";
import { Link, routerShape } from "react-router";
import React, { PropTypes } from "react";
import { Hooks } from "PluginSDK";

import Icon from "#SRC/js/components/Icon";
import Links from "#SRC/js/constants/Links";
import NestedServiceLinks from "#SRC/js/components/NestedServiceLinks";
import ResourceTableUtil from "#SRC/js/utils/ResourceTableUtil";
import StringUtil from "#SRC/js/utils/StringUtil";
import TableUtil from "#SRC/js/utils/TableUtil";
import Units from "#SRC/js/utils/Units";
import { isSDKService } from "#SRC/js/utils/ServiceUtil";

import HealthBar from "../../components/HealthBar";
import Pod from "../../structs/Pod";
import Service from "../../structs/Service";
import ServiceActionDisabledModal
  from "../../components/modals/ServiceActionDisabledModal";
import {
  DELETE,
  EDIT,
  MORE,
  OPEN,
  RESTART,
  RESUME,
  SCALE,
  SUSPEND
} from "../../constants/ServiceActionItem";
import ServiceActionLabels from "../../constants/ServiceActionLabels";
import ServiceStatusTypes from "../../constants/ServiceStatusTypes";
import ServiceStatusWarning from "../../components/ServiceStatusWarning";
import ServiceTableHeaderLabels from "../../constants/ServiceTableHeaderLabels";
import ServiceTableUtil from "../../utils/ServiceTableUtil";
import ServiceTree from "../../structs/ServiceTree";

const StatusMapping = {
  Running: "running-state"
};

const columnClasses = {
  name: "service-table-column-name",
  status: "service-table-column-status",
  instances: "service-table-column-instances",
  cpus: "service-table-column-cpus",
  mem: "service-table-column-mem",
  disk: "service-table-column-disk",
  actions: "service-table-column-actions"
};

const METHODS_TO_BIND = [
  "onActionsItemSelection",
  "handleServiceAction",
  "handleActionDisabledModalOpen",
  "handleActionDisabledModalClose",
  "renderHeadline",
  "renderStats",
  "renderStatus",
  "renderServiceActions"
];

class ServicesTable extends React.Component {
  constructor() {
    super(...arguments);

    this.state = { actionDisabledService: null };

    METHODS_TO_BIND.forEach(method => {
      this[method] = this[method].bind(this);
    });
  }

  onActionsItemSelection(service, actionItem) {
    const isGroup = service instanceof ServiceTree;
    let containsSDKService = false;

    if (isGroup) {
      containsSDKService =
        // #findItem will flatten the service tree
        service.findItem(function(item) {
          return item instanceof Service && isSDKService(item);
        }) != null;
    }

    // We still want to support the `open` action to display the web view
    if (
      actionItem.id !== DELETE &&
      (containsSDKService || isSDKService(service)) &&
      !Hooks.applyFilter(
        "isEnabledSDKAction",
        actionItem.id === EDIT || actionItem.id === OPEN,
        actionItem.id
      )
    ) {
      this.handleActionDisabledModalOpen(service, actionItem.id);
    } else {
      this.handleServiceAction(service, actionItem.id);
    }
  }

  handleServiceAction(service, actionID) {
    const { modalHandlers, router } = this.context;

    switch (actionID) {
      case EDIT:
        router.push(
          `/services/detail/${encodeURIComponent(service.getId())}/edit/`
        );
        break;
      case SCALE:
        modalHandlers.scaleService({ service });
        break;
      case OPEN:
        modalHandlers.openServiceUI({ service });
        break;
      case RESTART:
        modalHandlers.restartService({ service });
        break;
      case RESUME:
        modalHandlers.resumeService({ service });
        break;
      case SUSPEND:
        modalHandlers.suspendService({ service });
        break;
      case DELETE:
        modalHandlers.deleteService({ service });
        break;
    }
  }

  handleActionDisabledModalOpen(actionDisabledService, actionDisabledID) {
    this.setState({ actionDisabledService, actionDisabledID });
  }

  handleActionDisabledModalClose() {
    this.setState({ actionDisabledService: null, actionDisabledID: null });
  }

  getOpenInNewWindowLink(service) {
    // This might be a serviceTree and therefore we need this check
    // And getWebURL might therefore not be available
    if (!this.hasWebUI(service)) {
      return null;
    }

    return (

        <Icon
          color="neutral"
          className="icon-margin-left"
          id="open-external"
          size="mini"
        />

    );
  }

  getServiceLink(service) {
    const id = encodeURIComponent(service.getId());
    const isGroup = service instanceof ServiceTree;
    const serviceLink = isGroup
      ? `/services/overview/${id}`
      : `/services/detail/${id}`;

    if (this.props.isFiltered) {
      return (
        <NestedServiceLinks
          serviceID={id}
          className="service-breadcrumb"
          majorLinkClassName="service-breadcrumb-service-id"
          minorLinkWrapperClassName="service-breadcrumb-crumb"
        />
      );
    }

    return (
      <Link className="table-cell-link-primary text-overflow" to={serviceLink}>
        {service.getName()}
      </Link>
    );
  }

  getImage(service) {
    if (service instanceof ServiceTree) {
      // Get serviceTree image/icon
      return (
        <Icon
          className="icon-margin-right"
          color="grey"
          id="folder"
          size="mini"
        />
      );
    }

    // Get service image/icon
    return (

    );
  }

  hasWebUI(service) {
    return (
      service instanceof Service &&
      !isSDKService(service) &&
      service.getWebURL() != null &&
      service.getWebURL() !== ""
    );
  }

  renderHeadline(prop, service) {
    const id = encodeURIComponent(service.getId());
    const isGroup = service instanceof ServiceTree;
    const serviceLink = isGroup
      ? `/services/overview/${id}`
      : `/services/detail/${id}`;

    return (

        <Link className="table-cell-icon" to={serviceLink}>
          {this.getImage(service)}
        </Link>

          {this.getServiceLink(service)}
          {this.getOpenInNewWindowLink(service)}

    );
  }

  renderServiceActions(prop, service) {
    const isGroup = service instanceof ServiceTree;
    const isPod = service instanceof Pod;
    const isSingleInstanceApp = service.getLabels()
      .MARATHON_SINGLE_INSTANCE_APP;
    const instancesCount = service.getInstancesCount();
    const scaleTextID = isGroup
      ? ServiceActionLabels.scale_by
      : ServiceActionLabels[SCALE];
    const isSDK = isSDKService(service);

    const actions = [];

    actions.push({
      className: "hidden",
      id: MORE,
      html: "",
      selectedHtml: <Icon id="ellipsis-vertical" size="mini" />
    });

    if (this.hasWebUI(service)) {
      actions.push({
        id: OPEN,
        html: this.props.intl.formatMessage({ id: ServiceActionLabels.open })
      });
    }

    if (!isGroup) {
      actions.push({
        id: EDIT,
        html: this.props.intl.formatMessage({ id: ServiceActionLabels.edit })
      });
    }

    // isSingleInstanceApp = Framework main scheduler
    // instancesCount = service instances
    if ((isGroup && instancesCount > 0) || (!isGroup && !isSingleInstanceApp)) {
      actions.push({
        id: SCALE,
        html: this.props.intl.formatMessage({ id: scaleTextID })
      });
    }

    if (!isPod && !isGroup && instancesCount > 0 && !isSDK) {
      actions.push({
        id: RESTART,
        html: this.props.intl.formatMessage({
          id: ServiceActionLabels[RESTART]
        })
      });
    }

    if (instancesCount > 0 && !isSDK) {
      actions.push({
        id: SUSPEND,
        html: this.props.intl.formatMessage({
          id: ServiceActionLabels[SUSPEND]
        })
      });
    }

    if (!isGroup && instancesCount === 0 && !isSDK) {
      actions.push({
        id: RESUME,
        html: this.props.intl.formatMessage({
          id: ServiceActionLabels[RESUME]
        })
      });
    }

    actions.push({
      id: DELETE,
      html: (

          {this.props.intl.formatMessage({
            id: ServiceActionLabels[DELETE]
          })}

      )
    });

    return (
      <Tooltip content="More actions">
        <Dropdown
          anchorRight={true}
          buttonClassName="button button-mini button-link"
          dropdownMenuClassName="dropdown-menu"
          dropdownMenuListClassName="dropdown-menu-list"
          dropdownMenuListItemClassName="clickable"
          wrapperClassName="dropdown flush-bottom table-cell-icon"
          items={actions}
          persistentID={MORE}
          onItemSelection={this.onActionsItemSelection.bind(this, service)}
          scrollContainer=".gm-scroll-view"
          scrollContainerParentSelector=".gm-prevented"
          title="More actions"
          transition={true}
          transitionName="dropdown-menu"
        />
      </Tooltip>
    );
  }

  renderStatus(prop, service) {
    const instancesCount = service.getInstancesCount();
    const serviceId = service.getId();
    const serviceStatusText = service.getStatus();
    const serviceStatusClassSet = StatusMapping[serviceStatusText] || "";
    const { key: serviceStatusKey } = service.getServiceStatus();
    const tasksSummary = service.getTasksSummary();
    const tasksRunning = service.getTaskCount();
    const isDeploying =
      serviceStatusKey === ServiceStatusTypes.WAITING ||
      serviceStatusKey === ServiceStatusTypes.DEPLOYING;

    const conciseOverview = tasksRunning === instancesCount
      ? ` (${tasksRunning})`
      : ` (${tasksRunning}/${instancesCount})`;

    const verboseOverview = tasksRunning === instancesCount
      ? ` (${tasksRunning} ${StringUtil.pluralize("Instance", tasksRunning)})`
      : ` (${tasksRunning} of ${instancesCount} Instances)`;

    return (

          <HealthBar
            isDeploying={isDeploying}
            key={serviceId}
            tasksSummary={tasksSummary}
            instancesCount={instancesCount}
          />

            {serviceStatusText}

          {verboseOverview}
          {conciseOverview}
          <ServiceStatusWarning item={service} />

    );
  }

  renderStats(prop, service) {
    const resource = service.getResources()[prop];

    return (

        {Units.formatResource(prop, resource)}

    );
  }

  renderInstances(prop, service) {
    const instancesCount = service.getInstancesCount();
    const tasksRunning = service.getTaskCount();
    const overview = tasksRunning === instancesCount
      ? ` ${tasksRunning}`
      : ` ${tasksRunning}/${instancesCount}`;

    const content = !Number.isInteger(instancesCount) ? "\u2014" : overview;

    return (

        {content}

    );
  }

  getCellClasses(prop, sortBy, row) {
    const isHeader = row == null;

    return classNames(columnClasses[prop], {
      active: prop === sortBy.prop,
      clickable: isHeader
    });
  }

  getColumns() {
    const heading = ResourceTableUtil.renderHeading(ServiceTableHeaderLabels);

    return [
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "name",
        render: this.renderHeadline,
        sortable: true,
        sortFunction: ServiceTableUtil.propCompareFunctionFactory,
        heading
      },
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "status",
        helpText: (

            {"At-a-glance overview of the global application or group state. "}

              Read more
            .

        ),
        render: this.renderStatus,
        sortable: true,
        sortFunction: ServiceTableUtil.propCompareFunctionFactory,
        heading
      },
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "instances",
        render: this.renderInstances,
        sortable: true,
        sortFunction: ServiceTableUtil.propCompareFunctionFactory,
        heading
      },
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "cpus",
        render: this.renderStats,
        sortable: true,
        sortFunction: ServiceTableUtil.propCompareFunctionFactory,
        heading
      },
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "mem",
        render: this.renderStats,
        sortable: true,
        sortFunction: ServiceTableUtil.propCompareFunctionFactory,
        heading
      },
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "disk",
        render: this.renderStats,
        sortable: true,
        sortFunction: ServiceTableUtil.propCompareFunctionFactory,
        heading
      },
      {
        className: this.getCellClasses,
        headerClassName: this.getCellClasses,
        prop: "actions",
        render: this.renderServiceActions,
        sortable: false,
        heading() {
          return null;
        }
      }
    ];
  }

  getColGroup() {
    return (
      <colgroup>
        <col className={columnClasses.name} />
        <col className={columnClasses.status} />
        <col className={columnClasses.instances} />
        <col className={columnClasses.cpus} />
        <col className={columnClasses.mem} />
        <col className={columnClasses.disk} />
        <col className={columnClasses.actions} />
      </colgroup>
    );
  }

  render() {
    const { actionDisabledService, actionDisabledID } = this.state;

    return (

        <ServiceActionDisabledModal
          actionID={actionDisabledID}
          open={actionDisabledService != null}
          onClose={this.handleActionDisabledModalClose}
          service={actionDisabledService}
        />

    );
  }
}

ServicesTable.contextTypes = {
  modalHandlers: PropTypes.shape({
    scaleService: PropTypes.func,
    restartService: PropTypes.func,
    resumeService: PropTypes.func,
    suspendService: PropTypes.func,
    deleteService: PropTypes.func
  }).isRequired,
  router: routerShape
};

ServicesTable.defaultProps = {
  isFiltered: false,
  services: []
};

ServicesTable.propTypes = {
  isFiltered: PropTypes.bool,
  services: PropTypes.array
};

module.exports = injectIntl(ServicesTable);
<|endoftext|>


<commit_before>const assert = require('assert')

const reducer = require('../../src/reducers/reducer')

describe('reducers', () => {
  it('uses an empty list as the initial state', () => {
    const defaultValue = reducer(undefined, {})
    assert.deepEqual([], defaultValue)
  })

  it('TASKS_ADD', () => {
    const action = {
      type: 'TASKS_ADD',
      task: {
        title: 'This is the title',
      },
    }
    const state = reducer(undefined, action)
    assert.deepEqual([action.task], state)
  })
})
<commit_msg>Don’t actually write the reducer tests yet<commit_after>describe('reducers', () => {
  it('uses an empty list as the initial state')
  it('TASKS_ADD')
})
<|endoftext|>


<commit_before><commit_msg>Updated version.<commit_after><|endoftext|>


<commit_before>import config from '../../config.babel'
import handleErrors from '../../lib/handleErrors.babel'
import env from '../../lib/env.babel'

import gulp from 'gulp'
import sourcemaps from 'gulp-sourcemaps'
import postcss from 'gulp-postcss'
import autoprefixer from 'autoprefixer'
import cssnext from 'postcss-cssnext'
import nested from 'postcss-nested'
import atImport from 'postcss-import'
import stylelint from 'stylelint'
import mqpacker from 'css-mqpacker'
import minify from 'cssnano'
import rename from 'gulp-rename'
import syntax_scss from 'postcss-scss'
import gulpif from 'gulp-if'
import browserSync from 'browser-sync'
import plumber from 'gulp-plumber'
import notify from 'gulp-notify'
import rucksack from 'gulp-rucksack'

import sass from 'gulp-sass'

const dest = gulp.dest
const dev = env
const stylelintConfig = {
    "rules": {
    "block-no-empty": true,
    "color-no-invalid-hex": true,
    "declaration-colon-space-after": "always",
    "declaration-colon-space-before": "never",
    "function-comma-space-after": "always",
    "media-feature-colon-space-after": "always",
    "media-feature-colon-space-before": "never",
    "max-empty-lines": 5,
    "number-no-trailing-zeros": true,
    "declaration-block-no-single-line": true,
    "rule-trailing-semicolon": "always",
    "selector-list-comma-space-before": "never",
    "selector-list-comma-newline-after": "always"
    }
}

const cssTools = [
    atImport,
    cssnext,
    nested,
    stylelint( stylelintConfig ),
    autoprefixer( config.styles.autoprefixer ),
    mqpacker
]
const min = [ minify ]

let generateStyles = () => {
    let styleFile = config.styles.src.development
    let options = {
        sourcemap: true,
        style: 'expanded',
        unixNewlines: true,
        defaultEncoding: 'UTF-8'
    }

    let renameFile = ( path ) => {
        path.basename = config.styles.destFileName
    }

    return gulp.src(styleFile, options)
        .pipe(plumber( { handleErrors: handleErrors } ))
        .pipe(sass())
        .pipe(rucksack())
        .pipe(postcss( cssTools ), { syntax: syntax_scss })
        .pipe(gulpif(dev == config.build.prod, postcss( min )))
        .pipe(gulpif(dev == config.build.dev, sourcemaps.write()))
        .pipe(rename( renameFile ))
        .pipe(dest( config.styles.dest ))
        .pipe(gulpif(dev == config.build.dev, browserSync.reload( config.browserSync.reload )))
}

gulp.task('styles', generateStyles)
<commit_msg>update styles tasks<commit_after>import config from '../../config.babel'
import handleErrors from '../../lib/handleErrors.babel'
import env from '../../lib/env.babel'
import stylelintConfig from '../../lib/styleLintConfig.babel'

import gulp from 'gulp'
import sourcemaps from 'gulp-sourcemaps'
import postcss from 'gulp-postcss'
import minify from 'cssnano'
import stylelint from 'stylelint'
import mqpacker from 'css-mqpacker'
import rename from 'gulp-rename'
import syntax_scss from 'postcss-scss'
import gulpif from 'gulp-if'
import browserSync from 'browser-sync'
import plumber from 'gulp-plumber'
import notify from 'gulp-notify'
import rucksack from 'gulp-rucksack'

import sass from 'gulp-sass'

const dest = gulp.dest
const dev = env

const cssTools = [
    stylelint( stylelintConfig ),
    mqpacker
]

const min = [ minify ]

let generateStyles = () => {
    let styleFile = config.styles.src.development
    let options = {
        sourcemap: true,
        outputStyle: 'expanded',
        unixNewlines: true,
        defaultEncoding: 'UTF-8',
        includePaths: require('node-bourbon').includePaths
    }

    let renameFile = ( path ) => {
        path.basename = config.styles.destFileName
    }

    return gulp.src( styleFile )
        .pipe(plumber({ handleErrors: handleErrors }))
        .pipe(sass( options ).on('error', sass.logError))
        .pipe(rucksack())
        .pipe(postcss( cssTools ), { syntax: syntax_scss })
        .pipe(gulpif(dev == config.build.prod, postcss( min )))
        .pipe(gulpif(dev == config.build.dev, sourcemaps.write()))
        .pipe(rename( renameFile ))
        .pipe(dest( config.styles.dest ))
        .pipe(gulpif(dev == config.build.dev, browserSync.reload( config.browserSync.reload )))
}

gulp.task('styles', generateStyles)
<|endoftext|>


<commit_before>var expect = require('chai').expect;
var _ = require('underscore');
var values = require('../../lib/runtime/values');

describe('Values tests', function () {
    describe('compare', function() {
        describe('invalid comparison', function() {
            it('throws error', function() {
                var tests = [
                    { left: 1, right: [] },
                    { left: [], right: true }
                ];

                _.each(tests, function(test) {
                    expect(values.compare.bind(test.left, test.right)).to.throw.error;
                });
            });
        });

        it('array', function() {
            var tests = [
                { left: [], right: [], result: 0 },
                { left: [], right: [[]], result: -1 },
                { left: [[]], right: [], result: 1 },
                { left: [1], right: [2], result: -1 },
                { left: [1], right: [1], result: 0 },
                { left: [2], right: [1], result: 1 },
                { left: [1, 2], right: [1, 1], result: 1 },
                { left: [1, 1], right: [1, 1], result: 0 },
                { left: [1, 1], right: [1, 2], result: -1 },
                { left: [2, 1], right: [1, 1], result: 1 },
                { left: [1, 1], right: [2, 1], result: -1 },
                { left: [[1]], right: [[2]], result: -1 },
                { left: [[1]], right: [[1]], result: 0 },
                { left: [[2]], right: [[1]], result: 1 },
            ];

            _.each(tests, function(test) {
                expect(values.compare(test.left, test.right)).to.equal(
                    test.result,
                    JSON.stringify(test.left) + ' <=> ' + JSON.stringify(test.right)
                );
            });
        });
    });
});
<commit_msg>Add test for juttle moment comparison<commit_after>var expect = require('chai').expect;
var _ = require('underscore');
var values = require('../../lib/runtime/values');
var JuttleMoment = require('../../lib/moment').JuttleMoment;

describe('Values tests', function () {
    describe('compare', function() {
        describe('invalid comparison', function() {
            it('throws error', function() {
                var tests = [
                    { left: 1, right: [] },
                    { left: [], right: true }
                ];

                _.each(tests, function(test) {
                    expect(values.compare.bind(test.left, test.right)).to.throw.error;
                });
            });
        });

        it('array', function() {
            var tests = [
                { left: [], right: [], result: 0 },
                { left: [], right: [[]], result: -1 },
                { left: [[]], right: [], result: 1 },
                { left: [1], right: [2], result: -1 },
                { left: [1], right: [1], result: 0 },
                { left: [2], right: [1], result: 1 },
                { left: [1, 2], right: [1, 1], result: 1 },
                { left: [1, 1], right: [1, 1], result: 0 },
                { left: [1, 1], right: [1, 2], result: -1 },
                { left: [2, 1], right: [1, 1], result: 1 },
                { left: [1, 1], right: [2, 1], result: -1 },
                { left: [[1]], right: [[2]], result: -1 },
                { left: [[1]], right: [[1]], result: 0 },
                { left: [[2]], right: [[1]], result: 1 },
            ];

            _.each(tests, function(test) {
                expect(values.compare(test.left, test.right)).to.equal(
                    test.result,
                    JSON.stringify(test.left) + ' <=> ' + JSON.stringify(test.right)
                );
            });
        });

        it('moment', function() {
            var tests = [
                {
                    left: new JuttleMoment(1),
                    right: new JuttleMoment(0),
                    result: 1
                },
                {
                    left: new JuttleMoment(0),
                    right: new JuttleMoment(0),
                    result: 0
                },
                {
                    left: new JuttleMoment(0),
                    right: new JuttleMoment(1),
                    result: -1
                },
                {
                    left: new JuttleMoment({ raw: 0, epsilon: true }),
                    right: new JuttleMoment({ raw: 0, epsilon: true  }),
                    result: 0
                },
                {
                    left: new JuttleMoment({ raw: 0, epsilon: false }),
                    right: new JuttleMoment({ raw: 0, epsilon: false }),
                    result: 0
                },
                {
                    left: new JuttleMoment({ raw: 0, epsilon: true }),
                    right: new JuttleMoment({ raw: 0, epsilon: false }),
                    result:  -1
                },
                {
                    left: new JuttleMoment({ raw: 0, epsilon: false }),
                    right: new JuttleMoment({ raw: 0, epsilon: true }),
                    result: 1
                },
                {
                    left: new JuttleMoment(Infinity),
                    right: new JuttleMoment(Infinity),
                    result: 0
                },
                {
                    left: new JuttleMoment(-Infinity),
                    right: new JuttleMoment(-Infinity),
                    result: 0
                },
                {
                    left: new JuttleMoment(Infinity),
                    right: new JuttleMoment(-Infinity),
                    result: 1
                },
                {
                    left: new JuttleMoment(-Infinity),
                    right: new JuttleMoment(Infinity),
                    result: -1
                },
                {
                    left: new JuttleMoment({ raw: Infinity, epsilon: true }),
                    right: new JuttleMoment({ raw: Infinity, epsilon: false }),
                    result: -1
                },
                {
                    left: new JuttleMoment({ raw: Infinity, epsilon: false }),
                    right: new JuttleMoment({ raw: Infinity, epsilon: true }),
                    result: 1
                },
                {
                    left: new JuttleMoment({ raw: -Infinity, epsilon: true }),
                    right: new JuttleMoment({ raw: -Infinity, epsilon: false }),
                    result: -1
                },
                {
                    left: new JuttleMoment({ raw: -Infinity, epsilon: false }),
                    right: new JuttleMoment({ raw: -Infinity, epsilon: true }),
                    result: 1
                },
            ];

            _.each(tests, function(test) {
                expect(values.compare(test.left, test.right)).to.equal(
                    test.result,
                    test.left.valueOf() + ' (eps: ' + test.left.epsilon + ')'
                    + ' <=> '
                    + test.right.valueOf() + ' (eps: ' + test.right.epsilon + ')'
                );
            });
        });
    });
});
<|endoftext|>


<commit_before><commit_msg>NNTP: Only show completed downloads.<commit_after><|endoftext|>


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

    var bindHtmlCompile = angular.module('angular-bind-html-compile', []);

    bindHtmlCompile.directive('bindHtmlCompile', ['$compile', function ($compile) {
        return {
            restrict: 'A',
            link: function (scope, element, attrs) {
                scope.$watch(function () {
                    return scope.$eval(attrs.bindHtmlCompile);
                }, function (value) {
                    // In case value is a TrustedValueHolderType, sometimes it
                    // needs to be explicitly called into a string in order to
                    // get the HTML string.
                    element.html(value && value.toString());
                    // If scope is provided use it, otherwise use parent scope
                    var compileScope = scope;
                    if (attrs.bindHtmlScope) {
                        compileScope = scope.$eval(attrs.bindHtmlScope);
                    }
                    $compile(element.contents())(compileScope);
                });
            }
        };
    }]);

    if (module && module.exports) {
        module.exports = bindHtmlCompile.name;
    }
}(window.angular));
<commit_msg>fix: module existence check<commit_after>(function (angular) {
    'use strict';

    var bindHtmlCompile = angular.module('angular-bind-html-compile', []);

    bindHtmlCompile.directive('bindHtmlCompile', ['$compile', function ($compile) {
        return {
            restrict: 'A',
            link: function (scope, element, attrs) {
                scope.$watch(function () {
                    return scope.$eval(attrs.bindHtmlCompile);
                }, function (value) {
                    // In case value is a TrustedValueHolderType, sometimes it
                    // needs to be explicitly called into a string in order to
                    // get the HTML string.
                    element.html(value && value.toString());
                    // If scope is provided use it, otherwise use parent scope
                    var compileScope = scope;
                    if (attrs.bindHtmlScope) {
                        compileScope = scope.$eval(attrs.bindHtmlScope);
                    }
                    $compile(element.contents())(compileScope);
                });
            }
        };
    }]);

    if ('undefined' !== typeof module) {
        module.exports = bindHtmlCompile.name;
    }
}(window.angular));
<|endoftext|>


<commit_before>// Prevent double lazyload script on same page
// We NEED to use string as closure compiler would otherwise compile this statement badly
if (!window['Lazyload']) {
  (function(window, document){

    var pageHasLoaded = false;

    // retro compatibility old lazyload
    // will automatically create an instance at first lzld call
    window['lzld'] = (function() {
      var instance;
      return function() {
        if (instance === undefined) instance = new Lazyload;
        instance['lzld'].apply(instance, arguments)
      }
    }());

    window['Lazyload'] = Lazyload;

    addEvent(window, 'load', function onLoadFired() {
      pageHasLoaded = true;
    });

    /**
     * A new lazyloader, watching the whole window right now
     * @constructor
     * @param {Object=} opts Provide offset or lazyAttr or none
     */
    function Lazyload(opts) {
      this.opts = merge({
        offset: 200,
        lazyAttr: 'data-src'
      }, opts || {});

      this.imgs = [];
      this.listening = false;
      this.showImagesT = throttle(bind(this.showImages, this), 20)

      // init
      if (this.opts['container'] === document) {
        this.scrollContainer = window;
        domready(bind(this.setBodyContainer, this));
      } else {
        this.scrollContainer = this.opts['container'];
      }
      domready(bind(this.findImages, this));
      domready(bind(this.showImages, this));
      setTimeout(this.showImagesT, 25);

      addEvent(window, 'load', this.showImagesT);

      // Ok, cannot use setTimeout.apply in IE7/8 LOL, you don't wanna know why, wait 3 years
      // http://stackoverflow.com/questions/11619826/internet-explorer-7-8-and-window-functions-are-empty-objects
      addEvent(window, 'load', bind(function() {
        setTimeout(this.showImagesT, 25);
      }, this));

      // Bind events
      this.subscribe();

      this['lzld'] = bind(this.onDataSrcImgLoad, this);

      // Override image element .getAttribute globally so that we give the real src
      // does not works for ie < 8: http://perfectionkills.com/whats-wrong-with-extending-the-dom/
      // Internet Explorer 7 (and below) [...] does not expose global Node, Element, HTMLElement, HTMLParagraphElement
      if ('HTMLImageElement' in window) {
        this.replaceGetAttribute();
      }
    }

    Lazyload.prototype.replaceGetAttribute = function replaceGetAttribute() {
      var original = HTMLImageElement.prototype.getAttribute;
      var lazyload = this;
      HTMLImageElement.prototype.getAttribute = function(name) {
        if(name === 'src') {
          var realSrc = original.call(this, lazyload.opts.lazyAttr);
          return realSrc || original.call(this, name);
        } else {
          // our own lazyloader will go through theses lines
          // because we use getAttribute(opts.lazyAttr)
          return original.call(this, name);
        }
      }
    }

    // called by img onload= or onerror= for IE6/7
    Lazyload.prototype.onDataSrcImgLoad = function onDataSrcImgLoad(img) {
      // if image is not already in the imgs array
      // it can already be in it if domready was fast and img onload slow

      // For the first image of the stack, it is possible we already shown it
      // But we still got the onload event
      if (img.getAttribute(this.opts.lazyAttr) === null) return;

      if (indexOf.call(this.imgs, img) === -1) {
        // this case happens when the page had loaded but we inserted more lazyload images with
        // javascript (ajax). We need to re-watch scroll/resize
        if (!this.listening) {
          this.subscribe();
        }
        this.showIfVisible(img, this.imgs.push(img) - 1);
      }
    }

    // find and merge images on domready with possibly already present onload= onerror= imgs
    Lazyload.prototype.findImages = function findImages() {
      var
        domreadyImgs = document.getElementsByTagName('img'),
        currentImg;

      // merge them with already self onload registered imgs
      for (var imgIndex = 0, max = domreadyImgs.length; imgIndex < max; imgIndex += 1) {
        currentImg = domreadyImgs[imgIndex];
        if (currentImg.getAttribute(this.opts.lazyAttr) && indexOf.call(this.imgs, currentImg) === -1) {
          this.imgs.push(currentImg);
        }
      }
    }

    // img = dom element
    // index = imgs array index
    Lazyload.prototype.showIfVisible = function showIfVisible(img, index) {
      // We have to check that the current node is in the DOM
      // It could be a detached() dom node
      // http://bugs.jquery.com/ticket/4996
      if (contains(document.documentElement, img)
        && hasScrolled(this.opts['container'], img, this.opts['offset'])) {
        // To avoid onload loop calls
        // removeAttribute on IE is not enough to prevent the event to fire

        img.onload = null;
        img.removeAttribute('onload');
        // on IE < 8 we get an onerror event instead of an onload event
        img.onerror = null;
        img.removeAttribute('onerror');

        img.src = img.getAttribute(this.opts.lazyAttr);
        img.removeAttribute(this.opts.lazyAttr);
        this.imgs[index] = null;

        return true; // img shown
      } else {
        return false; // img to be shown
      }
    }

    // Loop through an images array to find to-be-shown images
    Lazyload.prototype.showImages = function showImages() {
      var
        last = this.imgs.length,
        current,
        allImagesDone = true;

      for (current = 0; current < last; current++) {
        var img = this.imgs[current];
        // if showIfVisible is false, it means we have some waiting images to be
        // shown
        if(img !== null && !this.showIfVisible(img, current)) {
          allImagesDone = false;
        }
      }

      if (allImagesDone && pageHasLoaded) {
        this.unsubscribe();
      }
    }

    Lazyload.prototype.unsubscribe = function unsubscribe() {
      removeEvent(this.scrollContainer, 'scroll', this.showImagesT);
      this.listening = false;
    }

    Lazyload.prototype.subscribe = function subscribe() {
      addEvent(this.scrollContainer, 'scroll', this.showImagesT);
      this.listening = true;
    }

    /* Lazyload utility belt *\!/* SHINY *\!/* */
    function bind (method, context){
      return function(){
        method.apply(context, arguments);
      }
    }

    function merge(defaults, opts) {
      for (var name in defaults) {
        if (opts[name] === undefined) {
          opts[name] = defaults[name];
        }
      }

      return opts;
    }

    function throttle(fn, minDelay) {
      var lastCall = 0;
      return function() {
        var now = +new Date();
        if (now - lastCall < minDelay) {
          return;
        }
        lastCall = now;
        // we do not return anything as
        // https://github.com/documentcloud/underscore/issues/387
        fn.apply(this, arguments);
      }
    }

    // X-browser
    function addEvent( el, type, fn ) {
      if (el.attachEvent) {
        el.attachEvent && el.attachEvent( 'on' + type, fn );
      } else {
        el.addEventListener( type, fn, false );
      }
    }

    // X-browser
    function removeEvent( el, type, fn ) {
      if (el.detachEvent) {
        el.detachEvent && el.detachEvent( 'on' + type, fn );
      } else {
        el.removeEventListener( type, fn, false );
      }
    }

    // custom domready function
    // ripped from https://github.com/dperini/ContentLoaded/blob/master/src/contentloaded.js
    // http://javascript.nwbox.com/ContentLoaded/
    // http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE
    // kept the inner logic, merged with our helpers/variables
    function domready(callback) {
      var
        done = false,
        top = true;

      function init(e) {
        if (e.type === 'readystatechange' && document.readyState !== 'complete') return;
        removeEvent((e.type === 'load' ? window : document), e.type, init);
        if (!done) {
          done = true;
          callback();
        }
      }

      function poll() {
        try { document.documentElement.doScroll('left'); } catch(e) { setTimeout(poll, 50); return; }
        init('poll');
      }

      if (document.readyState === 'complete') callback();
      else {
        if (document.createEventObject && document.documentElement.doScroll) {
          try { top = !window.frameElement; } catch(e) { }
          if (top) poll();
        }
        addEvent(document, 'DOMContentLoaded', init);
        addEvent(document, 'readystatechange', init);
        addEvent(window, 'load', init);
      }

    }

    function hasScrolled(container, element, offset) {
      // when no offset given, default to plaholder height or width
      // it means you should set your width= height=
      if (!offset) offset = Math.max(element.clientHeight, element.clientWidth);

      var rect = element.getBoundingClientRect();
      var pos = {
        x: rect.left - offset,
        y: rect.top - offset
      }
      var viewport = {
        x: 0,
        y: 0
      }

      if (container === document.body) {
        viewport.x += document.documentElement.clientWidth;
        viewport.y += document.documentElement.clientHeight;
      } else {
        pos.x += scrollOffset().x;
        pos.y += scrollOffset().y;
        viewport.x += container.clientWidth;
        viewport.y += container.clientHeight;
      }

      return pos.y <= viewport.y && pos.x <= viewport.x;
    }

    function scrollOffset() {
      return {
        x: Math.max(window.pageXOffset || 0, document.body.scrollLeft, document.documentElement.scrollLeft),
        y: Math.max(window.pageYOffset || 0, document.body.scrollTop, document.documentElement.scrollTop)
      }
    }

    // https://github.com/jquery/sizzle/blob/3136f48b90e3edc84cbaaa6f6f7734ef03775a07/sizzle.js#L708
    var contains = document.documentElement.compareDocumentPosition ?
      function( a, b ) {
        return !!(a.compareDocumentPosition( b ) & 16);
      } :
      document.documentElement.contains ?
      function( a, b ) {
        return a !== b && ( a.contains ? a.contains( b ) : false );
      } :
      function( a, b ) {
        while ( (b = b.parentNode) ) {
          if ( b === a ) {
            return true;
          }
        }
        return false;
      };

    // as suggested by http://webreflection.blogspot.fr/2011/06/partial-polyfills.html
    var indexOf = [].indexOf || function (value) {
        for (var i = this.length; i-- && this[i] !== value;);
        return i;
    };

  }(this, document))
}
<commit_msg>Empty image array on unsubscribe, means we can start over<commit_after>// Prevent double lazyload script on same page
// We NEED to use string as closure compiler would otherwise compile this statement badly
if (!window['Lazyload']) {
  (function(window, document){

    var pageHasLoaded = false;

    // retro compatibility old lazyload
    // will automatically create an instance at first lzld call
    window['lzld'] = (function() {
      var instance;
      return function() {
        if (instance === undefined) instance = new Lazyload;
        instance['lzld'].apply(instance, arguments)
      }
    }());

    window['Lazyload'] = Lazyload;

    addEvent(window, 'load', function onLoadFired() {
      pageHasLoaded = true;
    });

    /**
     * A new lazyloader, watching the whole window right now
     * @constructor
     * @param {Object=} opts Provide offset or lazyAttr or none
     */
    function Lazyload(opts) {
      this.opts = merge({
        offset: 200,
        lazyAttr: 'data-src'
      }, opts || {});

      this.imgs = [];
      this.listening = false;
      this.showImagesT = throttle(bind(this.showImages, this), 20)

      // init
      if (this.opts['container'] === document) {
        this.scrollContainer = window;
        domready(bind(this.setBodyContainer, this));
      } else {
        this.scrollContainer = this.opts['container'];
      }
      domready(bind(this.findImages, this));
      domready(bind(this.showImages, this));
      setTimeout(this.showImagesT, 25);

      addEvent(window, 'load', this.showImagesT);

      // Ok, cannot use setTimeout.apply in IE7/8 LOL, you don't wanna know why, wait 3 years
      // http://stackoverflow.com/questions/11619826/internet-explorer-7-8-and-window-functions-are-empty-objects
      addEvent(window, 'load', bind(function() {
        setTimeout(this.showImagesT, 25);
      }, this));

      // Bind events
      this.subscribe();

      this['lzld'] = bind(this.onDataSrcImgLoad, this);

      // Override image element .getAttribute globally so that we give the real src
      // does not works for ie < 8: http://perfectionkills.com/whats-wrong-with-extending-the-dom/
      // Internet Explorer 7 (and below) [...] does not expose global Node, Element, HTMLElement, HTMLParagraphElement
      if ('HTMLImageElement' in window) {
        this.replaceGetAttribute();
      }
    }

    Lazyload.prototype.replaceGetAttribute = function replaceGetAttribute() {
      var original = HTMLImageElement.prototype.getAttribute;
      var lazyload = this;
      HTMLImageElement.prototype.getAttribute = function(name) {
        if(name === 'src') {
          var realSrc = original.call(this, lazyload.opts.lazyAttr);
          return realSrc || original.call(this, name);
        } else {
          // our own lazyloader will go through theses lines
          // because we use getAttribute(opts.lazyAttr)
          return original.call(this, name);
        }
      }
    }

    // called by img onload= or onerror= for IE6/7
    Lazyload.prototype.onDataSrcImgLoad = function onDataSrcImgLoad(img) {
      // if image is not already in the imgs array
      // it can already be in it if domready was fast and img onload slow

      // For the first image of the stack, it is possible we already shown it
      // But we still got the onload event
      if (img.getAttribute(this.opts.lazyAttr) === null) return;

      if (indexOf.call(this.imgs, img) === -1) {
        // this case happens when the page had loaded but we inserted more lazyload images with
        // javascript (ajax). We need to re-watch scroll/resize
        if (!this.listening) {
          this.subscribe();
        }
        this.showIfVisible(img, this.imgs.push(img) - 1);
      }
    }

    // find and merge images on domready with possibly already present onload= onerror= imgs
    Lazyload.prototype.findImages = function findImages() {
      var
        domreadyImgs = document.getElementsByTagName('img'),
        currentImg;

      // merge them with already self onload registered imgs
      for (var imgIndex = 0, max = domreadyImgs.length; imgIndex < max; imgIndex += 1) {
        currentImg = domreadyImgs[imgIndex];
        if (currentImg.getAttribute(this.opts.lazyAttr) && indexOf.call(this.imgs, currentImg) === -1) {
          this.imgs.push(currentImg);
        }
      }
    }

    // img = dom element
    // index = imgs array index
    Lazyload.prototype.showIfVisible = function showIfVisible(img, index) {
      // We have to check that the current node is in the DOM
      // It could be a detached() dom node
      // http://bugs.jquery.com/ticket/4996
      if (contains(document.documentElement, img)
        && hasScrolled(this.opts['container'], img, this.opts['offset'])) {
        // To avoid onload loop calls
        // removeAttribute on IE is not enough to prevent the event to fire

        img.onload = null;
        img.removeAttribute('onload');
        // on IE < 8 we get an onerror event instead of an onload event
        img.onerror = null;
        img.removeAttribute('onerror');

        img.src = img.getAttribute(this.opts.lazyAttr);
        img.removeAttribute(this.opts.lazyAttr);
        this.imgs[index] = null;

        return true; // img shown
      } else {
        return false; // img to be shown
      }
    }

    // Loop through an images array to find to-be-shown images
    Lazyload.prototype.showImages = function showImages() {
      var
        last = this.imgs.length,
        current,
        allImagesDone = true;

      for (current = 0; current < last; current++) {
        var img = this.imgs[current];
        // if showIfVisible is false, it means we have some waiting images to be
        // shown
        if(img !== null && !this.showIfVisible(img, current)) {
          allImagesDone = false;
        }
      }

      if (allImagesDone && pageHasLoaded) {
        this.unsubscribe();
      }
    }

    Lazyload.prototype.unsubscribe = function unsubscribe() {
      removeEvent(this.scrollContainer, 'scroll', this.showImagesT);
      this.listening = false;
      this.imgs = [];
    }

    Lazyload.prototype.subscribe = function subscribe() {
      addEvent(this.scrollContainer, 'scroll', this.showImagesT);
      this.listening = true;
    }

    /* Lazyload utility belt *\!/* SHINY *\!/* */
    function bind (method, context){
      return function(){
        method.apply(context, arguments);
      }
    }

    function merge(defaults, opts) {
      for (var name in defaults) {
        if (opts[name] === undefined) {
          opts[name] = defaults[name];
        }
      }

      return opts;
    }

    function throttle(fn, minDelay) {
      var lastCall = 0;
      return function() {
        var now = +new Date();
        if (now - lastCall < minDelay) {
          return;
        }
        lastCall = now;
        // we do not return anything as
        // https://github.com/documentcloud/underscore/issues/387
        fn.apply(this, arguments);
      }
    }

    // X-browser
    function addEvent( el, type, fn ) {
      if (el.attachEvent) {
        el.attachEvent && el.attachEvent( 'on' + type, fn );
      } else {
        el.addEventListener( type, fn, false );
      }
    }

    // X-browser
    function removeEvent( el, type, fn ) {
      if (el.detachEvent) {
        el.detachEvent && el.detachEvent( 'on' + type, fn );
      } else {
        el.removeEventListener( type, fn, false );
      }
    }

    // custom domready function
    // ripped from https://github.com/dperini/ContentLoaded/blob/master/src/contentloaded.js
    // http://javascript.nwbox.com/ContentLoaded/
    // http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE
    // kept the inner logic, merged with our helpers/variables
    function domready(callback) {
      var
        done = false,
        top = true;

      function init(e) {
        if (e.type === 'readystatechange' && document.readyState !== 'complete') return;
        removeEvent((e.type === 'load' ? window : document), e.type, init);
        if (!done) {
          done = true;
          callback();
        }
      }

      function poll() {
        try { document.documentElement.doScroll('left'); } catch(e) { setTimeout(poll, 50); return; }
        init('poll');
      }

      if (document.readyState === 'complete') callback();
      else {
        if (document.createEventObject && document.documentElement.doScroll) {
          try { top = !window.frameElement; } catch(e) { }
          if (top) poll();
        }
        addEvent(document, 'DOMContentLoaded', init);
        addEvent(document, 'readystatechange', init);
        addEvent(window, 'load', init);
      }

    }

    function hasScrolled(container, element, offset) {
      // when no offset given, default to plaholder height or width
      // it means you should set your width= height=
      if (!offset) offset = Math.max(element.clientHeight, element.clientWidth);

      var rect = element.getBoundingClientRect();
      var pos = {
        x: rect.left - offset,
        y: rect.top - offset
      }
      var viewport = {
        x: 0,
        y: 0
      }

      if (container === document.body) {
        viewport.x += document.documentElement.clientWidth;
        viewport.y += document.documentElement.clientHeight;
      } else {
        pos.x += scrollOffset().x;
        pos.y += scrollOffset().y;
        viewport.x += container.clientWidth;
        viewport.y += container.clientHeight;
      }

      return pos.y <= viewport.y && pos.x <= viewport.x;
    }

    function scrollOffset() {
      return {
        x: Math.max(window.pageXOffset || 0, document.body.scrollLeft, document.documentElement.scrollLeft),
        y: Math.max(window.pageYOffset || 0, document.body.scrollTop, document.documentElement.scrollTop)
      }
    }

    // https://github.com/jquery/sizzle/blob/3136f48b90e3edc84cbaaa6f6f7734ef03775a07/sizzle.js#L708
    var contains = document.documentElement.compareDocumentPosition ?
      function( a, b ) {
        return !!(a.compareDocumentPosition( b ) & 16);
      } :
      document.documentElement.contains ?
      function( a, b ) {
        return a !== b && ( a.contains ? a.contains( b ) : false );
      } :
      function( a, b ) {
        while ( (b = b.parentNode) ) {
          if ( b === a ) {
            return true;
          }
        }
        return false;
      };

    // as suggested by http://webreflection.blogspot.fr/2011/06/partial-polyfills.html
    var indexOf = [].indexOf || function (value) {
        for (var i = this.length; i-- && this[i] !== value;);
        return i;
    };

  }(this, document))
}
<|endoftext|>