<commit_before>// Dependencies

let builder = require('focus').component.builder;
let React = require('react');
let assign = require('object-assign');

// Components

let FacetBox = require('../../../search/facet-box').component;
let ListActionBar = require('../../../list/action-bar/index').component;
let ListSummary = require('../../../list/summary/index').component;

// Store

let SearchStore = require('focus').store.SearchStore;

// Mixins

let ScrollInfoMixin = require('../common/scroll-info-mixin').mixin;
let GroupByMixin = require('../common/group-by-mixin').mixin;
let SearchMixin = require('../common/search-mixin').mixin;
let CartridgeBehaviour = require('../../mixin/cartridge-behaviour');
let type = require('focus').component.types;
/**
 * Page mixin of the advanced search.
 * @type {Object}
 */
let AdvancedSearch = {
    mixins: [ScrollInfoMixin, GroupByMixin, SearchMixin, CartridgeBehaviour],
    /**
     * Display name.
     */
    displayName: 'advanced-search',
    /**
     * Component initialisation
     */
    componentDidMount() {
        this._registerListeners();
        this.search();
    },
    /**
     * Actions before component will unmount.
     * @constructor
     */
    componentWillUnmount() {
        this._unRegisterListeners();
    },
    /** @inheritedDoc
     */
    getDefaultProps() {
        return {
            facetConfig: {},
            idField: 'id', //To remove?
            isSelection: true,
            criteria: {
                scope: undefined,
                searchText: undefined
            },
            exportAction: undefined,
            unselectedScopeAction: undefined
        };
    },
    propTypes: {
        facetConfig: type('object'),
        idField: type('string'),
        isSelection: type('bool'),
        criteria: type('object'),
        exportAction: type(['function', 'object']),
        unselectedScopeAction: type(['function', 'object'])
    },
    /**
     * Init default state.
     * @returns {object} Initialized state.
     */
    getInitialState() {
        return assign({
            facetList: {},
            selectedFacetList: {},
            openedFacetList: this.props.openedFacetList,
            selectionStatus: 'none',
            orderSelected: undefined,
            groupSelectedKey: undefined,
            scope: this.props.scope,
            query: this.props.query
        });
    },
    /**
     * Get the state from store.
     * @returns {object} Dtat to update store.
     */
    _getStateFromStore() {
        if (this.props.store) {
            let data = this.props.store.get();
            return assign({
                facetList: data.facet
            }, this.getScrollState());
        }
    },

    /**
     * Register a listener on the store.
     * @private
     */
    _registerListeners() {
        if (this.props.store) {
            this.props.store.addSearchChangeListener(this._onSearchChange);
        }
    },
    /**
     * Unregister a listener on the store.
     * @private
     */
    _unRegisterListeners() {
        if (this.props.store) {
            this.props.store.removeSearchChangeListener(this._onSearchChange);
        }
    },

    /**
     * Handler when store emit a change event.
     */
    _onSearchChange() {
        this.setState(this._getStateFromStore());
    },
    /**
     * Get the list of facet to print into the top bar.
     * @returns {{}} Facets object : [facet1: 'Label of facet1', facet2: 'Label of facet2'}.
     * @private
     */
    _getFacetListForBar() {
        let facetList = {};
        for (let key in this.state.selectedFacetList) {
            let facet = this.state.selectedFacetList[key];
            facetList[key] = facet.data.label;
        }
        return facetList;
    },
    /**
     * Click on bar facet action handler.
     * @param key [string}  Key of the clicked facet.
     * @private
     */
    _facetBarClick(key) {
        let selectedFacetList = this.state.selectedFacetList;
        delete selectedFacetList[key];

        this.state.selectedFacetList = selectedFacetList;
        this.setState(
            assign(
                {selectedFacetList: selectedFacetList},
                this.getNoFetchState())
            , this.search);
    },
    /**
     * Group action click handler.
     * @param {string} key Name of the column to group (if null => ungroup action).
     * @private
     */
    _groupClick(key) {
        console.log('Group by : ' + key);
        this.setState(
            assign(
                {groupSelectedKey: key},
                this.getNoFetchState()
            ), this.search);
    },
    /**
     * Order action click handler.
     * @param {string} key Column to order.
     * @param {string} order Order  asc/desc
     * @private
     */
    _orderClick(key, order) {
        console.log('Order : ' + key + ' - ' + order);
        this.setState(
            assign(
                {orderSelected: {key: key, order: order}},
                this.getNoFetchState()
            ), this.search);
    },
    /**
     * Selection action handler.
     * @param selectionStatus Current selection status.
     * @private
     */
    _selectionGroupLineClick(selectionStatus) {
        console.log('Selection status : ' + selectionStatus);
        this.setState({
            selectionStatus
        });
    },
    /**
     * Handler called when facet is selected.
     * @param facetComponentData Data of facet.
     */
    _facetSelectionClick(facetComponentData, isDisableGroup) {
        console.warn('Facet selection ');
        console.log(facetComponentData.selectedFacetList);

        let newState = {
            selectedFacetList: facetComponentData.selectedFacetList,
            openedFacetList: facetComponentData.openedFacetList
        };
        if (isDisableGroup) {
            newState.groupSelectedKey = undefined;
        }

        this.setState(assign(newState, this.getNoFetchState()), this.search);
    },
    /**
     * Line selection handler.
     * @param item Line checked/unchecked.
     */
    _selectItem(item) {
        this.setState({selectionStatus: 'partial'});
    },
    /**
     * Export action handler.
     */
    _exportHandler() {
        this.props.exportAction();
    },
    /**
     * Click on scope action handler.
     */
    _scopeClick() {
        this.props.unselectedScopeAction();
    },
    /**
     * Action on line click.
     * @param {object} item  the item clicked
     */
    _lineClick(item) {
        if (this.props.onLineClick) {
            this.props.onLineClick(item);
        }
    },
    /**
     * Render the show all button  seect the group corresponding facet.
     * @param groupKey Group key.
     * @returns {Function} Function to select the facet.
     */
    showAllGroupListHandler(groupKey) {
        return (event)=> {
            let selectedFacetList = this.state.selectedFacetList;

            let facet = this.props.store.getFacet();
            selectedFacetList[this.state.groupSelectedKey] = {
                data: facet[this.state.groupSelectedKey][groupKey],
                key: groupKey
            };
            this._facetSelectionClick({
                selectedFacetList: selectedFacetList,
                facetComponentData: this.state.openedFacetList
            }, true);
        };
    },

    /**
     * Render the facet box.
     * @returns {XML} Render the facetBox.
     */
    getFacetBoxComponent() {
        return (
            <FacetBox
                data-focus='advanced-search-facet-box'
                facetList={this.state.facetList}
                selectedFacetList={this.state.selectedFacetList}
                openedFacetList={this.state.openedFacetList}
                config={this.props.facetConfig}
                dataSelectionHandler={this._facetSelectionClick}
                />
        );
    },
    /**
     * Render the list summary component.
     * @returns {XML} Htm code.
     */
    getListSummaryComponent() {
        let scopeList = {scope: this.props.criteria.scope};
        return (
            <ListSummary
                data-focus='advanced-search-list-summary'
                nb={this.state.totalRecords}
                queryText={this.props.criteria.searchText}
                scopeList={scopeList}
                scopeClickAction={this._scopeClick}
                exportAction={this._exportHandler}/>
        );
    },
    /**
     * Render the action bar.
     * @returns {XML} Rendering of the action bar.
     */
    getActionBarComponent() {
        let groupableColumnList = Object.keys(this.state.facetList).reduce((result, facetKey) => {
            result[facetKey] = facetKey;
            return result;
        }, {});
        return (
            <ListActionBar data-focus='advanced-search-action-bar'
                           selectionStatus={this.state.selectionStatus}
                           selectionAction={this._selectionGroupLineClick}
                           orderableColumnList={this.props.orderableColumnList}
                           orderAction={this._orderClick}
                           orderSelected={this.state.orderSelected}
                           groupableColumnList={groupableColumnList}
                           groupAction={this._groupClick}
                           groupSelectedKey={this.state.groupSelectedKey}
                           facetList={this._getFacetListForBar()}
                           facetClickAction={this._facetBarClick}
                           operationList={this.props.lineOperationList}/>
        );
    },
    render: function render() {
        return (

                    {this.getFacetBoxComponent()}

                    {this.getListSummaryComponent()}
                    {this.getActionBarComponent()}
                    {this.getResultListComponent()}

        );
    }
};

module.exports = builder(AdvancedSearch);
<commit_msg>[advanced-search] Add componentWillReceiveProps<commit_after>// Dependencies

let builder = require('focus').component.builder;
let React = require('react');
let assign = require('object-assign');
let reduce = require('lodash/collection/reduce');
let includes = require('lodash/collection/includes');

// Components

let FacetBox = require('../../../search/facet-box').component;
let ListActionBar = require('../../../list/action-bar/index').component;
let ListSummary = require('../../../list/summary/index').component;

// Store

let SearchStore = require('focus').store.SearchStore;

// Mixins

let ScrollInfoMixin = require('../common/scroll-info-mixin').mixin;
let GroupByMixin = require('../common/group-by-mixin').mixin;
let SearchMixin = require('../common/search-mixin').mixin;
let CartridgeBehaviour = require('../../mixin/cartridge-behaviour');
let type = require('focus').component.types;
/**
 * Page mixin of the advanced search.
 * @type {Object}
 */
let AdvancedSearch = {
    mixins: [ScrollInfoMixin, GroupByMixin, SearchMixin, CartridgeBehaviour],
    /**
     * Display name.
     */
    displayName: 'advanced-search',
    /**
     * Component initialisation
     */
    componentDidMount() {
        this._registerListeners();
        this.search();
    },
    /**
     * Actions before component will unmount.
     * @constructor
     */
    componentWillUnmount() {
        this._unRegisterListeners();
    },
    /** @inheritedDoc
     */
    getDefaultProps() {
        return {
            facetConfig: {},
            idField: 'id', //To remove?
            isSelection: true,
            criteria: {
                scope: undefined,
                searchText: undefined
            },
            exportAction: undefined,
            unselectedScopeAction: undefined
        };
    },
    propTypes: {
        facetConfig: type('object'),
        idField: type('string'),
        isSelection: type('bool'),
        criteria: type('object'),
        exportAction: type(['function', 'object']),
        unselectedScopeAction: type(['function', 'object'])
    },
    /**
     * Init default state.
     * @returns {object} Initialized state.
     */
    getInitialState() {
        return assign({
            facetList: {},
            selectedFacetList: {},
            openedFacetList: this.props.openedFacetList,
            selectionStatus: 'none',
            orderSelected: undefined,
            groupSelectedKey: undefined,
            scope: this.props.scope,
            query: this.props.query
        });
    },
    componentWillReceiveProps(nextProps) {
        this.setState(reduce(nextProps, (newState, key, value) => {
            if (includes(['scope', 'query'], key)) {
                newState[key] = value;
            }
            return newState;
        }, {}));
    },
    /**
     * Get the state from store.
     * @returns {object} Dtat to update store.
     */
    _getStateFromStore() {
        if (this.props.store) {
            let data = this.props.store.get();
            return assign({
                facetList: data.facet
            }, this.getScrollState());
        }
    },

    /**
     * Register a listener on the store.
     * @private
     */
    _registerListeners() {
        if (this.props.store) {
            this.props.store.addSearchChangeListener(this._onSearchChange);
        }
    },
    /**
     * Unregister a listener on the store.
     * @private
     */
    _unRegisterListeners() {
        if (this.props.store) {
            this.props.store.removeSearchChangeListener(this._onSearchChange);
        }
    },

    /**
     * Handler when store emit a change event.
     */
    _onSearchChange() {
        this.setState(this._getStateFromStore());
    },
    /**
     * Get the list of facet to print into the top bar.
     * @returns {{}} Facets object : [facet1: 'Label of facet1', facet2: 'Label of facet2'}.
     * @private
     */
    _getFacetListForBar() {
        let facetList = {};
        for (let key in this.state.selectedFacetList) {
            let facet = this.state.selectedFacetList[key];
            facetList[key] = facet.data.label;
        }
        return facetList;
    },
    /**
     * Click on bar facet action handler.
     * @param key [string}  Key of the clicked facet.
     * @private
     */
    _facetBarClick(key) {
        let selectedFacetList = this.state.selectedFacetList;
        delete selectedFacetList[key];

        this.state.selectedFacetList = selectedFacetList;
        this.setState(
            assign(
                {selectedFacetList: selectedFacetList},
                this.getNoFetchState())
            , this.search);
    },
    /**
     * Group action click handler.
     * @param {string} key Name of the column to group (if null => ungroup action).
     * @private
     */
    _groupClick(key) {
        console.log('Group by : ' + key);
        this.setState(
            assign(
                {groupSelectedKey: key},
                this.getNoFetchState()
            ), this.search);
    },
    /**
     * Order action click handler.
     * @param {string} key Column to order.
     * @param {string} order Order  asc/desc
     * @private
     */
    _orderClick(key, order) {
        console.log('Order : ' + key + ' - ' + order);
        this.setState(
            assign(
                {orderSelected: {key: key, order: order}},
                this.getNoFetchState()
            ), this.search);
    },
    /**
     * Selection action handler.
     * @param selectionStatus Current selection status.
     * @private
     */
    _selectionGroupLineClick(selectionStatus) {
        console.log('Selection status : ' + selectionStatus);
        this.setState({
            selectionStatus
        });
    },
    /**
     * Handler called when facet is selected.
     * @param facetComponentData Data of facet.
     */
    _facetSelectionClick(facetComponentData, isDisableGroup) {
        console.warn('Facet selection ');
        console.log(facetComponentData.selectedFacetList);

        let newState = {
            selectedFacetList: facetComponentData.selectedFacetList,
            openedFacetList: facetComponentData.openedFacetList
        };
        if (isDisableGroup) {
            newState.groupSelectedKey = undefined;
        }

        this.setState(assign(newState, this.getNoFetchState()), this.search);
    },
    /**
     * Line selection handler.
     * @param item Line checked/unchecked.
     */
    _selectItem(item) {
        this.setState({selectionStatus: 'partial'});
    },
    /**
     * Export action handler.
     */
    _exportHandler() {
        this.props.exportAction();
    },
    /**
     * Click on scope action handler.
     */
    _scopeClick() {
        this.props.unselectedScopeAction();
    },
    /**
     * Action on line click.
     * @param {object} item  the item clicked
     */
    _lineClick(item) {
        if (this.props.onLineClick) {
            this.props.onLineClick(item);
        }
    },
    /**
     * Render the show all button  seect the group corresponding facet.
     * @param groupKey Group key.
     * @returns {Function} Function to select the facet.
     */
    showAllGroupListHandler(groupKey) {
        return (event)=> {
            let selectedFacetList = this.state.selectedFacetList;

            let facet = this.props.store.getFacet();
            selectedFacetList[this.state.groupSelectedKey] = {
                data: facet[this.state.groupSelectedKey][groupKey],
                key: groupKey
            };
            this._facetSelectionClick({
                selectedFacetList: selectedFacetList,
                facetComponentData: this.state.openedFacetList
            }, true);
        };
    },

    /**
     * Render the facet box.
     * @returns {XML} Render the facetBox.
     */
    getFacetBoxComponent() {
        return (
            <FacetBox
                data-focus='advanced-search-facet-box'
                facetList={this.state.facetList}
                selectedFacetList={this.state.selectedFacetList}
                openedFacetList={this.state.openedFacetList}
                config={this.props.facetConfig}
                dataSelectionHandler={this._facetSelectionClick}
                />
        );
    },
    /**
     * Render the list summary component.
     * @returns {XML} Htm code.
     */
    getListSummaryComponent() {
        let scopeList = {scope: this.props.criteria.scope};
        return (
            <ListSummary
                data-focus='advanced-search-list-summary'
                nb={this.state.totalRecords}
                queryText={this.props.criteria.searchText}
                scopeList={scopeList}
                scopeClickAction={this._scopeClick}
                exportAction={this._exportHandler}/>
        );
    },
    /**
     * Render the action bar.
     * @returns {XML} Rendering of the action bar.
     */
    getActionBarComponent() {
        let groupableColumnList = Object.keys(this.state.facetList).reduce((result, facetKey) => {
            result[facetKey] = facetKey;
            return result;
        }, {});
        return (
            <ListActionBar data-focus='advanced-search-action-bar'
                           selectionStatus={this.state.selectionStatus}
                           selectionAction={this._selectionGroupLineClick}
                           orderableColumnList={this.props.orderableColumnList}
                           orderAction={this._orderClick}
                           orderSelected={this.state.orderSelected}
                           groupableColumnList={groupableColumnList}
                           groupAction={this._groupClick}
                           groupSelectedKey={this.state.groupSelectedKey}
                           facetList={this._getFacetListForBar()}
                           facetClickAction={this._facetBarClick}
                           operationList={this.props.lineOperationList}/>
        );
    },
    render: function render() {
        return (

                    {this.getFacetBoxComponent()}

                    {this.getListSummaryComponent()}
                    {this.getActionBarComponent()}
                    {this.getResultListComponent()}

        );
    }
};

module.exports = builder(AdvancedSearch);
<|endoftext|>


<commit_before>'use strict';

const request = require('request');
const fs = require('fs');
const jsdom = require('jsdom').jsdom;

const waitForUrlToChangeTo = function (newUrl, timeout) {
  let currentUrl;
  return browser.driver.getCurrentUrl().then(function(url) {
    currentUrl = url;
  }).then(function() {
      return browser.driver.wait(function() {
        return browser.driver.getCurrentUrl().then(function(url) {
          return newUrl === url;
        });
      }, timeout);
    }
  );
};

const waitForBackendReady = function(timeout) {
  return browser.driver.wait(function() {
    let deferred = protractor.promise.defer();
    browser.driver.executeAsyncScript(function() {
      var callback = arguments[arguments.length - 1];
      var xmlHttp = new XMLHttpRequest();
      xmlHttp.onreadystatechange = function() {
        if (xmlHttp.readyState == 4 && xmlHttp.status == 200){
          callback(true);
        }else{
          setTimeout(function(){
            callback(false);
          }, 500);
        }
      };
      xmlHttp.open('GET', 'http://localhost:8008/api/info', true); // true for asynchronous
      xmlHttp.timeout = 500; // time in milliseconds
      xmlHttp.ontimeout = function() {
        xmlHttp.abort();
        callback(false);
      };
      xmlHttp.send(null);
    }).then(function(success){
      if (success)
        deferred.fulfill(true);
      else
        deferred.fulfill(false);
    });
    return deferred.promise;
  }, timeout);
};

const waitForVisualReviewReady = (function() {
  let count = 0;

  return function(max, timeout, next) {
    request('http://localhost:7000', function (error, response) {
      if (error || response.statusCode !== 200) {
        if (count++ < max) {
          return setTimeout(function() {
            waitForVisualReviewReady(max, timeout, next);
          }, timeout);
        } else {
          return next(false);
        }
      }
      next(true);
    });
  };
})();

const readSentEmail = function(emailDirectory, emailFileName){
  var htmlFileContent;
  try {
    htmlFileContent = fs.readFileSync(emailDirectory + '/' + emailFileName, 'utf-8');
  } catch (e) {
    htmlFileContent = fs.readFileSync(emailDirectory + '/' + emailFileName, 'utf-8');
  }
  var domContent = jsdom(htmlFileContent);
  return domContent;
};

exports.waitForUrlToChangeTo = waitForUrlToChangeTo;
exports.waitForBackendReady = waitForBackendReady;
exports.waitForVisualReviewReady = waitForVisualReviewReady;
exports.readSentEmail = readSentEmail;

// CONSTANTS

// Animations
exports.MENU_ANIMATION_SPEED = 200;
exports.SWIPER_ANIMATION_SPEED = 300;
exports.MODAL_REPEAT_ANIMATION_SPEED = 140;

// XPath searches
exports.XPATH_FOCUS_TASKS_TODAY_SLIDE = '//div[@swiper-slide="focus/tasks/left"]';
exports.XPATH_FOCUS_TASKS_NO_DATE_SLIDE = '//div[@swiper-slide="focus/tasks/rightDuplicate"]';
exports.XPATH_FOCUS_TASKS_TOMORROW_SLIDE = '//div[@swiper-slide="focus/tasks/middle"]';
exports.XPATH_FOCUS_TASKS_SLIDE = '//div[@swiper-slide="focus/tasks"]';
exports.XPATH_FOCUS_NOTES_SLIDE = '//div[@swiper-slide="focus/notes"]';
exports.XPATH_TASKS_ALL_SLIDE = '//div[@swiper-slide="tasks/all"]';
exports.XPATH_TASKS_CONTEXTS_SLIDE = '//div[@swiper-slide="tasks/contexts"]';
exports.XPATH_TASKS_CONTEXT_SLIDE = '//div[@swiper-slide="tasks/context"]';
exports.XPATH_MENU_BUTTON = '//button[@ng-click="toggleMenu()"]';
exports.XPATH_TOOLBAR_HEADING_BUTTON = '//div[@ng-click="clickToolbarHeading()"]';
exports.XPATH_EDITOR_HIGHLIGHTED_LINK = '//div[contains(@class, "container-editor")]//a[contains(@class, "highlighted") and ./span/text()="${linkText}"]';
exports.XPATH_EDITOR_HEADER_NAVIGATION_LINK = '//div[contains(@class, "container-editor")]//header//a[contains(@class, "link-navigation") and ./span/text()="${linkText}"]';
exports.XPATH_EDITOR_FOOTER_NAVIGATION_LINK = '//div[contains(@class, "container-editor")]//footer//a[contains(@class, "link-navigation") and ./span/text()="${linkText}"]';
exports.XPATH_EDITOR_FOOTER_LINK = '//div[contains(@class, "container-editor")]//footer//a[./span/text()="${linkText}"]';
exports.XPATH_FOOTER_NAVIGATION_LINK = '//footer//a[contains(@class, "link-navigation") and ./span/text()="${linkText}"]';
exports.XPATH_LINK_LIST_ITEM = '//a[contains(@class, "link-list-item") and ./span/text()="${linkText}"]';
exports.XPATH_LINK_SEARCH_RESULT = '//a[contains(@class, "link-search-result") and ./span/text()="${linkText}"]';
exports.XPATH_TASK_EDITOR_BASIC_SLIDE = '//div[@swiper-slide="taskEditor/basic"]';
exports.XPATH_TASK_EDITOR_ADVANCED_SLIDE = '//div[@swiper-slide="taskEditor/advanced"]';
exports.XPATH_LINK_SWIPER_CLICK = '//a[@swiper-click="${clickMethod}"]';
exports.XPATH_LINK_CLICK = '//a[@ng-click="${clickMethod}"]';
exports.XPATH_BUTTON_CLICK = '//button[@ng-click="${clickMethod}"]';
exports.XPATH_EDITOR_CLOSE = '//div[contains(@class, "container-editor")]//div[contains(@class, "container-titlebar--inner")]//a[@swiper-click="end${ItemType}Edit()"]';
exports.XPATH_EDITOR_DELETE = '//div[contains(@class, "container-editor")]//div[contains(@class, "container-titlebar--inner")]//a[@swiper-click="delete${ItemType}InEdit()"]';
exports.XPATH_MENU_LINK = '//div[contains(@class, "container-menu")]//div[contains(@class, "link-menu") and ./span/text()="${linkText}"]';
exports.XPATH_MENU_LINK_SMALL = '//div[contains(@class, "container-menu")]//div[contains(@class, "link-menu-small") and ./span/text()="${linkText}"]';
exports.XPATH_USER_HOME_SLIDE = '//div[@swiper-slide="user/home"]';
exports.XPATH_USER_DETAILS_SLIDE = '//div[@swiper-slide="user/details"]';
exports.XPATH_USER_EDITOR = '//div[@ng-switch-when="user"]';
exports.XPATH_LINK_ITEM = '//a[contains(@class, "link") and ./span/text()="${linkText}"]';
exports.XPATH_INBOX = '//div[@id="inbox"]';
exports.XPATH_INBOX_CONTENT = '//section[@ng-show="isContentVisible(\'inbox\')"]';
exports.XPATH_NOTES_ALL = '//div[@id="notes-all"]';
exports.XPATH_NOTES_CONTENT = '//div[@ng-show="isContentVisible(\'notes\')"]';
exports.XPATH_SETTINGS_CONTENT = '//section[@ng-show="isContentVisible(\'settings\')"]';
exports.XPATH_NOTE_EDITOR_BASIC_SLIDE = '//div[@swiper-slide="noteEditor/basic"]';
exports.XPATH_NOTE_EDITOR_ADVANCED_SLIDE = '//div[@swiper-slide="noteEditor/advanced"]';
exports.XPATH_LISTS_ACTIVE_SLIDE = '//div[@swiper-slide="lists/active"]';
exports.XPATH_LISTS_ARCHIVED_SLIDE = '//div[@swiper-slide="lists/archived"]';
exports.XPATH_LIST_TASKS_SLIDE = '//div[@swiper-slide="list/tasks"]';
exports.XPATH_LIST_EDITOR_BASIC_SLIDE = '//div[@swiper-slide="listEditor/basic"]';
exports.XPATH_LIST_EDITOR_ADVANCED_SLIDE = '//div[@swiper-slide="listEditor/advanced"]';
exports.XPATH_HEADING = '//h2[contains(@class, "group-heading") and text()="${headingText}"]';
exports.XPATH_OMNIBAR_BUTTON = '//button[@ng-click="openEditor(\'omnibar\')"]';
exports.XPATH_OMNIBAR_EDITOR = '//div[@ng-controller="OmnibarEditorController"]';
exports.XPATH_KEYWORD_LINK = '//a[contains(@class, "keyword") and ./span/text()="${linkText}"]';
exports.XPATH_FOOTER_BUTTON = '//footer//button[@ng-click="${clickMethod}"]';
exports.XPATH_ENTRY_MAIN_SLIDE = '//div[@swiper-slide="entry/main"]';
exports.XPATH_ENTRY_DETAILS_SLIDE = '//div[@swiper-slide="entry/details"]';
exports.XPATH_TAG_EDITOR_HIGHLIGHTED_LINK = '//div[@ng-switch-when="tag"]//a[contains(@class, "highlighted") and ./span/text()="${linkText}"]';
exports.XPATH_ITEM_EDITOR = '//div[@ng-switch-when="item"]';
exports.XPATH_ADD_ITEM = '//div[@swiper-click="addItem()"]';
exports.XPATH_LEFT_ACTION = '//a[@swiper-click="leftAction()"]';
exports.XPATH_RECURRING_NEXT = '//div[@ng-switch-when="recurring"]//a[@swiper-click="saveItemAndGotoNextItem(iterableItem)" and ./span/text()="next"]';
exports.XPATH_RECURRING_FINISH = '//div[@ng-switch-when="recurring"]//a[@swiper-click="saveItemAndGotoNextItem(iterableItem)" and ./span/text()="finish"]';
exports.XPATH_RECURRING_UNDO = '//div[@ng-switch-when="recurring"]//a[@swiper-click="undoSorting(iterableItem)"]';
exports.XPATH_RECURRING_SKIP = '//div[@ng-switch-when="recurring"]//a[@swiper-click="saveItemAndGotoNextItem(iterableItem)" and ./span/text()="skip"]';
exports.XPATH_PRECEDING_COMPLETE = '/preceding-sibling::div[./input]';
exports.XPATH_TEXT_LINK_CONTAINER = '//div[./span[contains(@class, "text-link") and text()="${linkText}"]]';
exports.XPATH_TEXT_DETAILS_CONTAINER = '//div[./span[contains(@class, "text-details") and text()="${linkText}"]]';
exports.XPATH_TOGGLE_COMPLETE_BUTTON = '//button[@ng-click="toggleShowCompletedTasks()"]';
exports.XPATH_COMPLETED_TASK = '//div[contains(@class, "checkbox-checked")]//a[contains(@class, "link-list-item") and ./span/text()="${linkText}"]';
exports.XPATH_UNCOMPLETED_TASK = '//div[not(contains(@class, "checkbox-checked"))]//a[contains(@class, "link-list-item") and ./span/text()="${linkText}"]';
exports.XPATH_TOASTER_TEXT = '//div[contains(@class, "container-toaster")]//span[contains(@class, "textgroup-toaster") and text() = "${toasterText}"]';
exports.XPATH_TOASTER_INLINE_LINK = '//div[contains(@class, "container-toaster")]//a[contains(@class, "link-inline")]';
exports.XPATH_TOASTER_CLOSE_LINK = '//div[contains(@class, "container-toaster")]//a[@swiper-click="closeNotificationAndCall(notification)"]';
exports.XPATH_MODAL_BUTTON = '//div[contains(@class, "container-modal")]//button[./span/text()="${linkText}"]';
exports.XPATH_MODAL_CLOSE_BUTTON = '//div[contains(@class, "container-modal")]//a[@ng-click="close()"]';
exports.XPATH_MODAL_HEADING = '//div[contains(@class, "container-modal")]//h2[contains(@class, "group-heading") and ./span/text()="${headingText}"]';
exports.XPATH_TEXTGROUP_INGRESS = '//div[contains(@class, "textgroup-ingress") and text()="${ingressText}"]';<commit_msg>Fixed info path in test<commit_after>'use strict';

const request = require('request');
const fs = require('fs');
const jsdom = require('jsdom').jsdom;

const waitForUrlToChangeTo = function (newUrl, timeout) {
  let currentUrl;
  return browser.driver.getCurrentUrl().then(function(url) {
    currentUrl = url;
  }).then(function() {
      return browser.driver.wait(function() {
        return browser.driver.getCurrentUrl().then(function(url) {
          return newUrl === url;
        });
      }, timeout);
    }
  );
};

const waitForBackendReady = function(timeout) {
  return browser.driver.wait(function() {
    let deferred = protractor.promise.defer();
    browser.driver.executeAsyncScript(function() {
      var callback = arguments[arguments.length - 1];
      var xmlHttp = new XMLHttpRequest();
      xmlHttp.onreadystatechange = function() {
        if (xmlHttp.readyState == 4 && xmlHttp.status == 200){
          callback(true);
        }else{
          setTimeout(function(){
            callback(false);
          }, 500);
        }
      };
      xmlHttp.open('GET', 'http://localhost:8008/api/v2/info', true); // true for asynchronous
      xmlHttp.timeout = 500; // time in milliseconds
      xmlHttp.ontimeout = function() {
        xmlHttp.abort();
        callback(false);
      };
      xmlHttp.send(null);
    }).then(function(success){
      if (success)
        deferred.fulfill(true);
      else
        deferred.fulfill(false);
    });
    return deferred.promise;
  }, timeout);
};

const waitForVisualReviewReady = (function() {
  let count = 0;

  return function(max, timeout, next) {
    request('http://localhost:7000', function (error, response) {
      if (error || response.statusCode !== 200) {
        if (count++ < max) {
          return setTimeout(function() {
            waitForVisualReviewReady(max, timeout, next);
          }, timeout);
        } else {
          return next(false);
        }
      }
      next(true);
    });
  };
})();

const readSentEmail = function(emailDirectory, emailFileName){
  var htmlFileContent;
  try {
    htmlFileContent = fs.readFileSync(emailDirectory + '/' + emailFileName, 'utf-8');
  } catch (e) {
    htmlFileContent = fs.readFileSync(emailDirectory + '/' + emailFileName, 'utf-8');
  }
  var domContent = jsdom(htmlFileContent);
  return domContent;
};

exports.waitForUrlToChangeTo = waitForUrlToChangeTo;
exports.waitForBackendReady = waitForBackendReady;
exports.waitForVisualReviewReady = waitForVisualReviewReady;
exports.readSentEmail = readSentEmail;

// CONSTANTS

// Animations
exports.MENU_ANIMATION_SPEED = 200;
exports.SWIPER_ANIMATION_SPEED = 300;
exports.MODAL_REPEAT_ANIMATION_SPEED = 140;

// XPath searches
exports.XPATH_FOCUS_TASKS_TODAY_SLIDE = '//div[@swiper-slide="focus/tasks/left"]';
exports.XPATH_FOCUS_TASKS_NO_DATE_SLIDE = '//div[@swiper-slide="focus/tasks/rightDuplicate"]';
exports.XPATH_FOCUS_TASKS_TOMORROW_SLIDE = '//div[@swiper-slide="focus/tasks/middle"]';
exports.XPATH_FOCUS_TASKS_SLIDE = '//div[@swiper-slide="focus/tasks"]';
exports.XPATH_FOCUS_NOTES_SLIDE = '//div[@swiper-slide="focus/notes"]';
exports.XPATH_TASKS_ALL_SLIDE = '//div[@swiper-slide="tasks/all"]';
exports.XPATH_TASKS_CONTEXTS_SLIDE = '//div[@swiper-slide="tasks/contexts"]';
exports.XPATH_TASKS_CONTEXT_SLIDE = '//div[@swiper-slide="tasks/context"]';
exports.XPATH_MENU_BUTTON = '//button[@ng-click="toggleMenu()"]';
exports.XPATH_TOOLBAR_HEADING_BUTTON = '//div[@ng-click="clickToolbarHeading()"]';
exports.XPATH_EDITOR_HIGHLIGHTED_LINK = '//div[contains(@class, "container-editor")]//a[contains(@class, "highlighted") and ./span/text()="${linkText}"]';
exports.XPATH_EDITOR_HEADER_NAVIGATION_LINK = '//div[contains(@class, "container-editor")]//header//a[contains(@class, "link-navigation") and ./span/text()="${linkText}"]';
exports.XPATH_EDITOR_FOOTER_NAVIGATION_LINK = '//div[contains(@class, "container-editor")]//footer//a[contains(@class, "link-navigation") and ./span/text()="${linkText}"]';
exports.XPATH_EDITOR_FOOTER_LINK = '//div[contains(@class, "container-editor")]//footer//a[./span/text()="${linkText}"]';
exports.XPATH_FOOTER_NAVIGATION_LINK = '//footer//a[contains(@class, "link-navigation") and ./span/text()="${linkText}"]';
exports.XPATH_LINK_LIST_ITEM = '//a[contains(@class, "link-list-item") and ./span/text()="${linkText}"]';
exports.XPATH_LINK_SEARCH_RESULT = '//a[contains(@class, "link-search-result") and ./span/text()="${linkText}"]';
exports.XPATH_TASK_EDITOR_BASIC_SLIDE = '//div[@swiper-slide="taskEditor/basic"]';
exports.XPATH_TASK_EDITOR_ADVANCED_SLIDE = '//div[@swiper-slide="taskEditor/advanced"]';
exports.XPATH_LINK_SWIPER_CLICK = '//a[@swiper-click="${clickMethod}"]';
exports.XPATH_LINK_CLICK = '//a[@ng-click="${clickMethod}"]';
exports.XPATH_BUTTON_CLICK = '//button[@ng-click="${clickMethod}"]';
exports.XPATH_EDITOR_CLOSE = '//div[contains(@class, "container-editor")]//div[contains(@class, "container-titlebar--inner")]//a[@swiper-click="end${ItemType}Edit()"]';
exports.XPATH_EDITOR_DELETE = '//div[contains(@class, "container-editor")]//div[contains(@class, "container-titlebar--inner")]//a[@swiper-click="delete${ItemType}InEdit()"]';
exports.XPATH_MENU_LINK = '//div[contains(@class, "container-menu")]//div[contains(@class, "link-menu") and ./span/text()="${linkText}"]';
exports.XPATH_MENU_LINK_SMALL = '//div[contains(@class, "container-menu")]//div[contains(@class, "link-menu-small") and ./span/text()="${linkText}"]';
exports.XPATH_USER_HOME_SLIDE = '//div[@swiper-slide="user/home"]';
exports.XPATH_USER_DETAILS_SLIDE = '//div[@swiper-slide="user/details"]';
exports.XPATH_USER_EDITOR = '//div[@ng-switch-when="user"]';
exports.XPATH_LINK_ITEM = '//a[contains(@class, "link") and ./span/text()="${linkText}"]';
exports.XPATH_INBOX = '//div[@id="inbox"]';
exports.XPATH_INBOX_CONTENT = '//section[@ng-show="isContentVisible(\'inbox\')"]';
exports.XPATH_NOTES_ALL = '//div[@id="notes-all"]';
exports.XPATH_NOTES_CONTENT = '//div[@ng-show="isContentVisible(\'notes\')"]';
exports.XPATH_SETTINGS_CONTENT = '//section[@ng-show="isContentVisible(\'settings\')"]';
exports.XPATH_NOTE_EDITOR_BASIC_SLIDE = '//div[@swiper-slide="noteEditor/basic"]';
exports.XPATH_NOTE_EDITOR_ADVANCED_SLIDE = '//div[@swiper-slide="noteEditor/advanced"]';
exports.XPATH_LISTS_ACTIVE_SLIDE = '//div[@swiper-slide="lists/active"]';
exports.XPATH_LISTS_ARCHIVED_SLIDE = '//div[@swiper-slide="lists/archived"]';
exports.XPATH_LIST_TASKS_SLIDE = '//div[@swiper-slide="list/tasks"]';
exports.XPATH_LIST_EDITOR_BASIC_SLIDE = '//div[@swiper-slide="listEditor/basic"]';
exports.XPATH_LIST_EDITOR_ADVANCED_SLIDE = '//div[@swiper-slide="listEditor/advanced"]';
exports.XPATH_HEADING = '//h2[contains(@class, "group-heading") and text()="${headingText}"]';
exports.XPATH_OMNIBAR_BUTTON = '//button[@ng-click="openEditor(\'omnibar\')"]';
exports.XPATH_OMNIBAR_EDITOR = '//div[@ng-controller="OmnibarEditorController"]';
exports.XPATH_KEYWORD_LINK = '//a[contains(@class, "keyword") and ./span/text()="${linkText}"]';
exports.XPATH_FOOTER_BUTTON = '//footer//button[@ng-click="${clickMethod}"]';
exports.XPATH_ENTRY_MAIN_SLIDE = '//div[@swiper-slide="entry/main"]';
exports.XPATH_ENTRY_DETAILS_SLIDE = '//div[@swiper-slide="entry/details"]';
exports.XPATH_TAG_EDITOR_HIGHLIGHTED_LINK = '//div[@ng-switch-when="tag"]//a[contains(@class, "highlighted") and ./span/text()="${linkText}"]';
exports.XPATH_ITEM_EDITOR = '//div[@ng-switch-when="item"]';
exports.XPATH_ADD_ITEM = '//div[@swiper-click="addItem()"]';
exports.XPATH_LEFT_ACTION = '//a[@swiper-click="leftAction()"]';
exports.XPATH_RECURRING_NEXT = '//div[@ng-switch-when="recurring"]//a[@swiper-click="saveItemAndGotoNextItem(iterableItem)" and ./span/text()="next"]';
exports.XPATH_RECURRING_FINISH = '//div[@ng-switch-when="recurring"]//a[@swiper-click="saveItemAndGotoNextItem(iterableItem)" and ./span/text()="finish"]';
exports.XPATH_RECURRING_UNDO = '//div[@ng-switch-when="recurring"]//a[@swiper-click="undoSorting(iterableItem)"]';
exports.XPATH_RECURRING_SKIP = '//div[@ng-switch-when="recurring"]//a[@swiper-click="saveItemAndGotoNextItem(iterableItem)" and ./span/text()="skip"]';
exports.XPATH_PRECEDING_COMPLETE = '/preceding-sibling::div[./input]';
exports.XPATH_TEXT_LINK_CONTAINER = '//div[./span[contains(@class, "text-link") and text()="${linkText}"]]';
exports.XPATH_TEXT_DETAILS_CONTAINER = '//div[./span[contains(@class, "text-details") and text()="${linkText}"]]';
exports.XPATH_TOGGLE_COMPLETE_BUTTON = '//button[@ng-click="toggleShowCompletedTasks()"]';
exports.XPATH_COMPLETED_TASK = '//div[contains(@class, "checkbox-checked")]//a[contains(@class, "link-list-item") and ./span/text()="${linkText}"]';
exports.XPATH_UNCOMPLETED_TASK = '//div[not(contains(@class, "checkbox-checked"))]//a[contains(@class, "link-list-item") and ./span/text()="${linkText}"]';
exports.XPATH_TOASTER_TEXT = '//div[contains(@class, "container-toaster")]//span[contains(@class, "textgroup-toaster") and text() = "${toasterText}"]';
exports.XPATH_TOASTER_INLINE_LINK = '//div[contains(@class, "container-toaster")]//a[contains(@class, "link-inline")]';
exports.XPATH_TOASTER_CLOSE_LINK = '//div[contains(@class, "container-toaster")]//a[@swiper-click="closeNotificationAndCall(notification)"]';
exports.XPATH_MODAL_BUTTON = '//div[contains(@class, "container-modal")]//button[./span/text()="${linkText}"]';
exports.XPATH_MODAL_CLOSE_BUTTON = '//div[contains(@class, "container-modal")]//a[@ng-click="close()"]';
exports.XPATH_MODAL_HEADING = '//div[contains(@class, "container-modal")]//h2[contains(@class, "group-heading") and ./span/text()="${headingText}"]';
exports.XPATH_TEXTGROUP_INGRESS = '//div[contains(@class, "textgroup-ingress") and text()="${ingressText}"]';
<|endoftext|>


<commit_before>'use strict'

var app;

app = angular.module('cloudStorm.resourceFilter', [])

app.factory('csResourceFilter', function(){

  this.filter = function(array){
    return array.reverse()
  }

  return this
})
<commit_msg>Sort now works with underscore js<commit_after>'use strict'

var app;

app = angular.module('cloudStorm.resourceFilter', [])

app.factory('csResourceFilter', function(){

  this.filter = function(array, column){
    var fieldValue;
    return _.sortBy(array, (function(item){
      fieldValue = this.fieldValue(item, column)
      if(fieldValue)
        fieldValue = fieldValue.toLowerCase()
      console.log(fieldValue)
      return fieldValue
    }).bind(this))
  }

  this.fieldValue = function(item, field) {

    var associations, display_date, display_time, enum_value, item_data, names, ref, relationship;
    if (field.resource) {
      if (field.cardinality === 'many') {
        associations = item.$association(field);
        names = _.map(associations, function(assoc) {
          return assoc.$display_name();
        });
        return names.join(", ");
      } else {
        if (!(item.relationships && item.relationships[field.relationship])) {
          return item.attributes[field.attribute];
        }
        item_data = item.relationships[field.relationship].data;
        relationship = item.$relationship(item_data);
        if (!relationship) {
          return item.attributes[field.attribute];
        }
        return relationship.$display_name();
      }
    } else if (field["enum"]) {
      enum_value = _.find(field["enum"], {
        value: item.attributes[field.attribute]
      });
      if (enum_value) {
        return enum_value.name;
      }
      return item.attributes[field.attribute];
    } else if (field.type === 'boolean') {
      return ((ref = $scope.i18n) != null ? ref.t(item.attributes[field.attribute]) : void 0) || item.attributes[field.attribute];
    } else if (field.type === 'time') {
      display_time = new Date(item.attributes[field.attribute]);
      return $filter('date')(display_time, 'HH:mm');
    } else if (field.type === 'datetime') {
      display_date = new Date(item.attributes[field.attribute]);
      return $filter('date')(display_date, 'EEEE, MMMM d, y HH:mm');
    } else {
      return item.attributes[field.attribute];
    }
  };

  return this
})
<|endoftext|>


<commit_before>var _ = require('underscore');
var BackboneAbortSync = require('../../util/backbone-abort-sync');
var Model = require('../../core/model');
var helper = require('../helpers/histogram-helper');

var DEFAULT_MAX_BUCKETS = 366;

var deleteMe = 'This is only to be able to create the PR. Should not pass Code Review.';

/**
 *  This model is used for getting the total amount of data
 *  from the histogram widget (without any filter).
 */

module.exports = Model.extend({
  defaults: {
    url: '',
    data: [],
    localTimezone: false,
    localOffset: 0
  },

  url: function () {
    var params = [];
    var offset = this._getCurrentOffset();

    if (this.get('column_type') === 'number' && this.get('bins')) {
      params.push('bins=' + this.get('bins'));
    } else if (this.get('column_type') === 'date') {
      params.push('aggregation=' + (this.get('aggregation') || 'auto'));
      if (_.isFinite(offset)) {
        params.push('offset=' + offset);
      }
    }

    if (this.get('apiKey')) {
      params.push('api_key=' + this.get('apiKey'));
    } else if (this.get('authToken')) {
      var authToken = this.get('authToken');
      if (authToken instanceof Array) {
        _.each(authToken, function (token) {
          params.push('auth_token[]=' + token);
        });
      } else {
        params.push('auth_token=' + authToken);
      }
    }
    return this.get('url') + '?' + params.join('&');
  },

  initialize: function () {
    this.sync = BackboneAbortSync.bind(this);
    this._initBinds();
  },

  _initBinds: function () {
    this.on('change:url', function () {
      this.fetch();
    }, this);

    this.on('change:aggregation change:offset', function () {
      if (this.get('column_type') === 'date' && this.get('aggregation')) {
        this.fetch();
      }
    }, this);

    this.on('change:bins', function () {
      if (this.get('column_type') === 'number' && _.isUndefined(this.get('aggregation'))) {
        this.fetch();
      }
    }, this);

    this.on('change:localTimezone', function () {
      this.fetch();
    }, this);
  },

  setUrl: function (url) {
    if (!url) {
      throw new Error('url not specified');
    }
    this.set('url', url);
  },

  setBins: function (bins) {
    this.set('bins', bins, { silent: bins === void 0 });
  },

  getData: function () {
    return this.get('data');
  },

  parse: function (data) {
    var aggregation = data.aggregation;
    var numberOfBins = data.bins_count;
    var width = data.bin_width;
    var start = this.get('column_type') === 'date' ? helper.calculateStart(data.bins, data.bins_start, aggregation) : data.bins_start;

    var parsedData = {
      aggregation: aggregation,
      bins: numberOfBins,
      data: new Array(numberOfBins)
    };

    _.each(data.bins, function (bin) {
      parsedData.data[bin.bin] = bin;
    });

    this.set('aggregation', aggregation, { silent: true });

    if (numberOfBins > DEFAULT_MAX_BUCKETS) {
      parsedData.error = 'Max bins limit reached';
      return parsedData;
    }

    parsedData.error = undefined;
    if (this.get('column_type') === 'date') {
      helper.fillTimestampBuckets(parsedData.data, start, aggregation, numberOfBins, this._getCurrentOffset());
    } else {
      helper.fillNumericBuckets(parsedData.data, start, width, numberOfBins);
    }

    if (parsedData.data.length > 0) {
      parsedData.start = parsedData.data[0].start;
      parsedData.end = parsedData.data[parsedData.data.length - 1].end;
    }

    return parsedData;
  },

  _getCurrentOffset: function () {
    return this.get('localTimezone')
      ? this.get('localOffset')
      : this.get('offset');
  }
});
<commit_msg>Revert unnecessary line<commit_after>var _ = require('underscore');
var BackboneAbortSync = require('../../util/backbone-abort-sync');
var Model = require('../../core/model');
var helper = require('../helpers/histogram-helper');

var DEFAULT_MAX_BUCKETS = 366;

/**
 *  This model is used for getting the total amount of data
 *  from the histogram widget (without any filter).
 */

module.exports = Model.extend({
  defaults: {
    url: '',
    data: [],
    localTimezone: false,
    localOffset: 0
  },

  url: function () {
    var params = [];
    var offset = this._getCurrentOffset();

    if (this.get('column_type') === 'number' && this.get('bins')) {
      params.push('bins=' + this.get('bins'));
    } else if (this.get('column_type') === 'date') {
      params.push('aggregation=' + (this.get('aggregation') || 'auto'));
      if (_.isFinite(offset)) {
        params.push('offset=' + offset);
      }
    }

    if (this.get('apiKey')) {
      params.push('api_key=' + this.get('apiKey'));
    } else if (this.get('authToken')) {
      var authToken = this.get('authToken');
      if (authToken instanceof Array) {
        _.each(authToken, function (token) {
          params.push('auth_token[]=' + token);
        });
      } else {
        params.push('auth_token=' + authToken);
      }
    }
    return this.get('url') + '?' + params.join('&');
  },

  initialize: function () {
    this.sync = BackboneAbortSync.bind(this);
    this._initBinds();
  },

  _initBinds: function () {
    this.on('change:url', function () {
      this.fetch();
    }, this);

    this.on('change:aggregation change:offset', function () {
      if (this.get('column_type') === 'date' && this.get('aggregation')) {
        this.fetch();
      }
    }, this);

    this.on('change:bins', function () {
      if (this.get('column_type') === 'number' && _.isUndefined(this.get('aggregation'))) {
        this.fetch();
      }
    }, this);

    this.on('change:localTimezone', function () {
      this.fetch();
    }, this);
  },

  setUrl: function (url) {
    if (!url) {
      throw new Error('url not specified');
    }
    this.set('url', url);
  },

  setBins: function (bins) {
    this.set('bins', bins, { silent: bins === void 0 });
  },

  getData: function () {
    return this.get('data');
  },

  parse: function (data) {
    var aggregation = data.aggregation;
    var numberOfBins = data.bins_count;
    var width = data.bin_width;
    var start = this.get('column_type') === 'date' ? helper.calculateStart(data.bins, data.bins_start, aggregation) : data.bins_start;

    var parsedData = {
      aggregation: aggregation,
      bins: numberOfBins,
      data: new Array(numberOfBins)
    };

    _.each(data.bins, function (bin) {
      parsedData.data[bin.bin] = bin;
    });

    this.set('aggregation', aggregation, { silent: true });

    if (numberOfBins > DEFAULT_MAX_BUCKETS) {
      parsedData.error = 'Max bins limit reached';
      return parsedData;
    }

    parsedData.error = undefined;
    if (this.get('column_type') === 'date') {
      helper.fillTimestampBuckets(parsedData.data, start, aggregation, numberOfBins, this._getCurrentOffset());
    } else {
      helper.fillNumericBuckets(parsedData.data, start, width, numberOfBins);
    }

    if (parsedData.data.length > 0) {
      parsedData.start = parsedData.data[0].start;
      parsedData.end = parsedData.data[parsedData.data.length - 1].end;
    }

    return parsedData;
  },

  _getCurrentOffset: function () {
    return this.get('localTimezone')
      ? this.get('localOffset')
      : this.get('offset');
  }
});
<|endoftext|>


<commit_before>const moment              = require('moment');
const BinaryPjax          = require('../../../../base/binary_pjax');
const Client              = require('../../../../base/client');
const Header              = require('../../../../base/header');
const BinarySocket        = require('../../../../base/socket');
const Dialog              = require('../../../../common/attach_dom/dialog');
const jpClient            = require('../../../../common/country_base').jpClient;
const Currency            = require('../../../../common/currency');
const FormManager         = require('../../../../common/form_manager');
const DatePicker          = require('../../../../components/date_picker');
const TimePicker          = require('../../../../components/time_picker');
const dateValueChanged    = require('../../../../../_common/common_functions').dateValueChanged;
const localize            = require('../../../../../_common/localize').localize;
const scrollToHashSection = require('../../../../../_common/scroll').scrollToHashSection;

const SelfExclusion = (() => {
    let $form,
        fields,
        self_exclusion_data,
        set_30day_turnover,
        currency;

    const form_id               = '#frm_self_exclusion';
    const timeout_date_id       = '#timeout_until_date';
    const timeout_time_id       = '#timeout_until_time';
    const exclude_until_id      = '#exclude_until';
    const max_30day_turnover_id = '#max_30day_turnover';
    const error_class           = 'errorfield';
    const TURNOVER_LIMIT        = 99999999999999999999; // 20 digits

    const onLoad = () => {
        $form = $(form_id);

        fields = {};
        $form.find('input').each(function () {
            fields[this.name] = '';
        });

        currency = Client.get('currency');

        $('.prepend_currency').parent().prepend(Currency.formatCurrency(currency));

        initDatePicker();
        getData(true);
    };

    const getData = (scroll) => {
        BinarySocket.send({ get_self_exclusion: 1 }).then((response) => {
            if (response.error) {
                if (response.error.code === 'ClientSelfExclusion') {
                    Client.sendLogoutRequest();
                }
                if (response.error.message) {
                    $('#msg_error').html(response.error.message).setVisibility(1);
                    $form.setVisibility(0);
                }
                return;
            }
            BinarySocket.send({ get_account_status: 1 }).then((data) => {
                const has_to_set_30day_turnover = /ukrts_max_turnover_limit_not_set/.test(data.get_account_status.status);
                if (typeof set_30day_turnover === 'undefined') {
                    set_30day_turnover = has_to_set_30day_turnover;
                }
                $('#frm_self_exclusion').find('fieldset > div.form-row:not(.max_30day_turnover)').setVisibility(!has_to_set_30day_turnover);
                $('#description_max_30day_turnover').setVisibility(has_to_set_30day_turnover);
                $('#description').setVisibility(!has_to_set_30day_turnover);
                $('#loading').setVisibility(0);
                $form.setVisibility(1);
                self_exclusion_data = response.get_self_exclusion;
                $.each(self_exclusion_data, (key, value) => {
                    fields[key] = value.toString();
                    if (key === 'timeout_until') {
                        const timeout = moment.unix(value);
                        const date = timeout.format('DD MMM, YYYY');
                        const time = timeout.format('HH:mm');
                        $form.find(timeout_date_id).val(date);
                        $form.find(timeout_time_id).val(time);
                        return;
                    }

                    if (key === 'max_30day_turnover') {
                        $('#chk_no_limit').prop('checked', true);
                        setMax30DayTurnoverLimit(true);
                    }

                    $form.find(`#${key}`).val(value);
                });

                $('#chk_no_limit').on('change', function() {
                    setMax30DayTurnoverLimit($(this).is(':checked'));
                });

                bindValidation();
                if (scroll) scrollToHashSection();
            });
        });
    };

    const setMax30DayTurnoverLimit = (is_checked) => {
        const current_max = self_exclusion_data.max_30day_turnover || TURNOVER_LIMIT;
        let max = TURNOVER_LIMIT;
        if (current_max < max) {
            max = current_max;
        }
        $(max_30day_turnover_id)
            .attr('disabled', is_checked)
            .css('color', is_checked ? 'transparent' : 'inherit')
            .val(is_checked ? max : '');
    };

    const bindValidation = () => {
        const validations    = [{ request_field: 'set_self_exclusion', value: 1 }];
        const decimal_places = Currency.getDecimalPlaces(currency);

        $form.find('input[type="text"]').each(function () {
            const id = $(this).attr('id');

            if (/timeout_until|exclude_until/.test(id)) return;

            const checks  = [];
            const options = { min: 0 };
            if (id in self_exclusion_data) {
                checks.push('req');
                options.max = self_exclusion_data[id];
            } else {
                options.allow_empty = true;
            }
            if (!/session_duration_limit|max_open_bets/.test(id)) {
                options.type     = 'float';
                options.decimals = decimal_places;
            }
            checks.push(['number', options]);

            if (id === 'session_duration_limit') {
                checks.push(['custom', { func: validSessionDuration, message: 'Session duration limit cannot be more than 6 weeks.' }]);
            }

            validations.push({
                selector        : `#${id}`,
                validations     : checks,
                exclude_if_empty: 1,
            });
        });

        validations.push(
            {
                selector        : timeout_date_id,
                request_field   : 'timeout_until',
                re_check_field  : timeout_time_id,
                exclude_if_empty: 1,
                value           : getTimeout,
                validations     : [
                    ['custom', { func: () => ($(timeout_time_id).val() ? $(timeout_date_id).val().length : true),                         message: 'This field is required.' }],
                    ['custom', { func: value => !value.length || getMoment(timeout_date_id).isAfter(moment().subtract(1, 'days'), 'day'), message: 'Time out must be after today.' }],
                    ['custom', { func: value => !value.length || getMoment(timeout_date_id).isBefore(moment().add(6, 'weeks')),           message: 'Time out cannot be more than 6 weeks.' }],
                ],
            },
            {
                selector       : timeout_time_id,
                exclude_request: 1,
                re_check_field : timeout_date_id,
                validations    : [
                    ['custom', { func: () => ($(timeout_date_id).val() && getMoment(timeout_date_id).isSame(moment(), 'day') ? $(timeout_time_id).val().length : true), message: 'This field is required.' }],
                    ['custom', { func: value => !value.length || !$(timeout_date_id).attr('data-value') || (getTimeout() > moment().valueOf() / 1000), message: 'Time out cannot be in the past.' }],
                    ['custom', { func: validTime, message: 'Please select a valid time.' }],
                ],
            },
            {
                selector        : exclude_until_id,
                exclude_if_empty: 1,
                value           : () => getDate(exclude_until_id),
                validations     : [
                    ['custom', { func: value => !value.length || getMoment(exclude_until_id).isAfter(moment().add(6, 'months')), message: 'Exclude time cannot be less than 6 months.' }],
                    ['custom', { func: value => !value.length || getMoment(exclude_until_id).isBefore(moment().add(5, 'years')), message: 'Exclude time cannot be for more than 5 years.' }],
                ],
            });

        FormManager.init(form_id, validations);
        FormManager.handleSubmit({
            form_selector       : form_id,
            fnc_response_handler: setExclusionResponse,
            fnc_additional_check: additionalCheck,
            enable_button       : true,
        });
    };

    const validSessionDuration = value => (+value <= moment.duration(6, 'weeks').as('minutes'));
    const validTime            = value => !value.length || moment(value, 'HH:mm', true).isValid();

    const getDate    = (elm_id) => {
        const $elm = $(elm_id);
        return $elm.attr('data-value') || (!isNaN(new Date($elm.val()).getTime()) ? $elm.val() : '');
    };
    const getMoment  = elm_id => moment(new Date(getDate(elm_id)));
    const getTimeout = () => (getDate(timeout_date_id) ? (moment(new Date(`${getDate(timeout_date_id)}T${$(timeout_time_id).val()}`)).valueOf() / 1000).toFixed(0) : '');

    const initDatePicker = () => {
        // timeout_until
        TimePicker.init({ selector: timeout_time_id });
        DatePicker.init({
            selector: timeout_date_id,
            minDate : 0,
            maxDate : 6 * 7, // 6 weeks
        });

        // exclude_until
        DatePicker.init({
            selector: exclude_until_id,
            minDate : moment().add(6, 'months').add(1, 'day').toDate(),
            maxDate : 5 * 365, // 5 years
        });

        $(`${timeout_date_id}, ${exclude_until_id}`).change(function () {
            dateValueChanged(this, 'date');
        });
    };

    const additionalCheck = data => (
        new Promise((resolve) => {
            const is_changed = Object.keys(data).some(key => ( // using != in next line since response types is inconsistent
                key !== 'set_self_exclusion' && (!(key in self_exclusion_data) || self_exclusion_data[key] != data[key]) // eslint-disable-line eqeqeq
            ));
            if (!is_changed) {
                showFormMessage('You did not change anything.', false);
                resolve(false);
            }

            if ('timeout_until' in data || 'exclude_until' in data) {
                Dialog.confirm({
                    id     : 'timeout_until_dialog',
                    message: 'When you click "OK" you will be excluded from trading on the site until the selected date.',
                }).then((response) => resolve(response));
            } else {
                resolve(true);
            }
        })
    );

    const setExclusionResponse = (response) => {
        if (response.error) {
            const error_msg = response.error.message;
            let error_fld   = response.error.field;
            if (error_fld) {
                error_fld = /^timeout_until$/.test(error_fld) ? 'timeout_until_date' : error_fld;
                const $error_fld = $(`#${error_fld}`);
                $error_fld.siblings('.error-msg').setVisibility(1).html(error_msg);
                $.scrollTo($error_fld, 500, { offset: -10 });
            } else {
                showFormMessage(localize(error_msg), false);
            }
            return;
        }
        showFormMessage('Your changes have been updated.', true);
        Client.set('session_start', moment().unix()); // used to handle session duration limit
        const {exclude_until, timeout_until} = response.echo_req;
        if (exclude_until || timeout_until) {
            Client.set('excluded_until',
                exclude_until ? moment(exclude_until).unix()
                : timeout_until
            );
        }
        BinarySocket.send({ get_account_status: 1 }).then(() => {
            Header.displayAccountStatus();
            if (set_30day_turnover) {
                BinaryPjax.loadPreviousUrl();
            } else {
                getData();
                if (jpClient()) {
                    // need to update daily_loss_limit value inside jp_settings object
                    BinarySocket.send({ get_settings: 1 }, { forced: true });
                }
            }
        });
    };

    const showFormMessage = (msg, is_success) => {
        $('#msg_form')
            .attr('class', is_success ? 'success-msg' : error_class)
            .html(is_success ? $('<ul/>', { class: 'checked' }).append($('<li/>', { text: localize(msg) })) : localize(msg))
            .css('display', 'block')
            .delay(5000)
            .fadeOut(1000);
    };

    return {
        onLoad,
    };
})();

module.exports = SelfExclusion;
<commit_msg>Use max integer value possible<commit_after>const moment              = require('moment');
const BinaryPjax          = require('../../../../base/binary_pjax');
const Client              = require('../../../../base/client');
const Header              = require('../../../../base/header');
const BinarySocket        = require('../../../../base/socket');
const Dialog              = require('../../../../common/attach_dom/dialog');
const jpClient            = require('../../../../common/country_base').jpClient;
const Currency            = require('../../../../common/currency');
const FormManager         = require('../../../../common/form_manager');
const DatePicker          = require('../../../../components/date_picker');
const TimePicker          = require('../../../../components/time_picker');
const dateValueChanged    = require('../../../../../_common/common_functions').dateValueChanged;
const localize            = require('../../../../../_common/localize').localize;
const scrollToHashSection = require('../../../../../_common/scroll').scrollToHashSection;

const SelfExclusion = (() => {
    let $form,
        fields,
        self_exclusion_data,
        set_30day_turnover,
        currency;

    const form_id               = '#frm_self_exclusion';
    const timeout_date_id       = '#timeout_until_date';
    const timeout_time_id       = '#timeout_until_time';
    const exclude_until_id      = '#exclude_until';
    const max_30day_turnover_id = '#max_30day_turnover';
    const error_class           = 'errorfield';
    const TURNOVER_LIMIT        = Number.MAX_SAFE_INTEGER;

    const onLoad = () => {
        $form = $(form_id);

        fields = {};
        $form.find('input').each(function () {
            fields[this.name] = '';
        });

        currency = Client.get('currency');

        $('.prepend_currency').parent().prepend(Currency.formatCurrency(currency));

        initDatePicker();
        getData(true);
    };

    const getData = (scroll) => {
        BinarySocket.send({ get_self_exclusion: 1 }).then((response) => {
            if (response.error) {
                if (response.error.code === 'ClientSelfExclusion') {
                    Client.sendLogoutRequest();
                }
                if (response.error.message) {
                    $('#msg_error').html(response.error.message).setVisibility(1);
                    $form.setVisibility(0);
                }
                return;
            }
            BinarySocket.send({ get_account_status: 1 }).then((data) => {
                const has_to_set_30day_turnover = /ukrts_max_turnover_limit_not_set/.test(data.get_account_status.status);
                if (typeof set_30day_turnover === 'undefined') {
                    set_30day_turnover = has_to_set_30day_turnover;
                }
                $('#frm_self_exclusion').find('fieldset > div.form-row:not(.max_30day_turnover)').setVisibility(!has_to_set_30day_turnover);
                $('#description_max_30day_turnover').setVisibility(has_to_set_30day_turnover);
                $('#description').setVisibility(!has_to_set_30day_turnover);
                $('#loading').setVisibility(0);
                $form.setVisibility(1);
                self_exclusion_data = response.get_self_exclusion;
                $.each(self_exclusion_data, (key, value) => {
                    fields[key] = value.toString();
                    if (key === 'timeout_until') {
                        const timeout = moment.unix(value);
                        const date = timeout.format('DD MMM, YYYY');
                        const time = timeout.format('HH:mm');
                        $form.find(timeout_date_id).val(date);
                        $form.find(timeout_time_id).val(time);
                        return;
                    }

                    if (key === 'max_30day_turnover') {
                        $('#chk_no_limit').prop('checked', true);
                        setMax30DayTurnoverLimit(true);
                    }

                    $form.find(`#${key}`).val(value);
                });

                $('#chk_no_limit').on('change', function() {
                    setMax30DayTurnoverLimit($(this).is(':checked'));
                });

                bindValidation();
                if (scroll) scrollToHashSection();
            });
        });
    };

    const setMax30DayTurnoverLimit = (is_checked) => {
        const current_max = self_exclusion_data.max_30day_turnover || TURNOVER_LIMIT;
        let max = TURNOVER_LIMIT;
        if (current_max < max) {
            max = current_max;
        }
        $(max_30day_turnover_id)
            .attr('disabled', is_checked)
            .css('color', is_checked ? 'transparent' : 'inherit')
            .val(is_checked ? max : '');
    };

    const bindValidation = () => {
        const validations    = [{ request_field: 'set_self_exclusion', value: 1 }];
        const decimal_places = Currency.getDecimalPlaces(currency);

        $form.find('input[type="text"]').each(function () {
            const id = $(this).attr('id');

            if (/timeout_until|exclude_until/.test(id)) return;

            const checks  = [];
            const options = { min: 0 };
            if (id in self_exclusion_data) {
                checks.push('req');
                options.max = self_exclusion_data[id];
            } else {
                options.allow_empty = true;
            }
            if (!/session_duration_limit|max_open_bets/.test(id)) {
                options.type     = 'float';
                options.decimals = decimal_places;
            }
            checks.push(['number', options]);

            if (id === 'session_duration_limit') {
                checks.push(['custom', { func: validSessionDuration, message: 'Session duration limit cannot be more than 6 weeks.' }]);
            }

            validations.push({
                selector        : `#${id}`,
                validations     : checks,
                exclude_if_empty: 1,
            });
        });

        validations.push(
            {
                selector        : timeout_date_id,
                request_field   : 'timeout_until',
                re_check_field  : timeout_time_id,
                exclude_if_empty: 1,
                value           : getTimeout,
                validations     : [
                    ['custom', { func: () => ($(timeout_time_id).val() ? $(timeout_date_id).val().length : true),                         message: 'This field is required.' }],
                    ['custom', { func: value => !value.length || getMoment(timeout_date_id).isAfter(moment().subtract(1, 'days'), 'day'), message: 'Time out must be after today.' }],
                    ['custom', { func: value => !value.length || getMoment(timeout_date_id).isBefore(moment().add(6, 'weeks')),           message: 'Time out cannot be more than 6 weeks.' }],
                ],
            },
            {
                selector       : timeout_time_id,
                exclude_request: 1,
                re_check_field : timeout_date_id,
                validations    : [
                    ['custom', { func: () => ($(timeout_date_id).val() && getMoment(timeout_date_id).isSame(moment(), 'day') ? $(timeout_time_id).val().length : true), message: 'This field is required.' }],
                    ['custom', { func: value => !value.length || !$(timeout_date_id).attr('data-value') || (getTimeout() > moment().valueOf() / 1000), message: 'Time out cannot be in the past.' }],
                    ['custom', { func: validTime, message: 'Please select a valid time.' }],
                ],
            },
            {
                selector        : exclude_until_id,
                exclude_if_empty: 1,
                value           : () => getDate(exclude_until_id),
                validations     : [
                    ['custom', { func: value => !value.length || getMoment(exclude_until_id).isAfter(moment().add(6, 'months')), message: 'Exclude time cannot be less than 6 months.' }],
                    ['custom', { func: value => !value.length || getMoment(exclude_until_id).isBefore(moment().add(5, 'years')), message: 'Exclude time cannot be for more than 5 years.' }],
                ],
            });

        FormManager.init(form_id, validations);
        FormManager.handleSubmit({
            form_selector       : form_id,
            fnc_response_handler: setExclusionResponse,
            fnc_additional_check: additionalCheck,
            enable_button       : true,
        });
    };

    const validSessionDuration = value => (+value <= moment.duration(6, 'weeks').as('minutes'));
    const validTime            = value => !value.length || moment(value, 'HH:mm', true).isValid();

    const getDate    = (elm_id) => {
        const $elm = $(elm_id);
        return $elm.attr('data-value') || (!isNaN(new Date($elm.val()).getTime()) ? $elm.val() : '');
    };
    const getMoment  = elm_id => moment(new Date(getDate(elm_id)));
    const getTimeout = () => (getDate(timeout_date_id) ? (moment(new Date(`${getDate(timeout_date_id)}T${$(timeout_time_id).val()}`)).valueOf() / 1000).toFixed(0) : '');

    const initDatePicker = () => {
        // timeout_until
        TimePicker.init({ selector: timeout_time_id });
        DatePicker.init({
            selector: timeout_date_id,
            minDate : 0,
            maxDate : 6 * 7, // 6 weeks
        });

        // exclude_until
        DatePicker.init({
            selector: exclude_until_id,
            minDate : moment().add(6, 'months').add(1, 'day').toDate(),
            maxDate : 5 * 365, // 5 years
        });

        $(`${timeout_date_id}, ${exclude_until_id}`).change(function () {
            dateValueChanged(this, 'date');
        });
    };

    const additionalCheck = data => (
        new Promise((resolve) => {
            const is_changed = Object.keys(data).some(key => ( // using != in next line since response types is inconsistent
                key !== 'set_self_exclusion' && (!(key in self_exclusion_data) || self_exclusion_data[key] != data[key]) // eslint-disable-line eqeqeq
            ));
            if (!is_changed) {
                showFormMessage('You did not change anything.', false);
                resolve(false);
            }

            if ('timeout_until' in data || 'exclude_until' in data) {
                Dialog.confirm({
                    id     : 'timeout_until_dialog',
                    message: 'When you click "OK" you will be excluded from trading on the site until the selected date.',
                }).then((response) => resolve(response));
            } else {
                resolve(true);
            }
        })
    );

    const setExclusionResponse = (response) => {
        if (response.error) {
            const error_msg = response.error.message;
            let error_fld   = response.error.field;
            if (error_fld) {
                error_fld = /^timeout_until$/.test(error_fld) ? 'timeout_until_date' : error_fld;
                const $error_fld = $(`#${error_fld}`);
                $error_fld.siblings('.error-msg').setVisibility(1).html(error_msg);
                $.scrollTo($error_fld, 500, { offset: -10 });
            } else {
                showFormMessage(localize(error_msg), false);
            }
            return;
        }
        showFormMessage('Your changes have been updated.', true);
        Client.set('session_start', moment().unix()); // used to handle session duration limit
        const {exclude_until, timeout_until} = response.echo_req;
        if (exclude_until || timeout_until) {
            Client.set('excluded_until',
                exclude_until ? moment(exclude_until).unix()
                : timeout_until
            );
        }
        BinarySocket.send({ get_account_status: 1 }).then(() => {
            Header.displayAccountStatus();
            if (set_30day_turnover) {
                BinaryPjax.loadPreviousUrl();
            } else {
                getData();
                if (jpClient()) {
                    // need to update daily_loss_limit value inside jp_settings object
                    BinarySocket.send({ get_settings: 1 }, { forced: true });
                }
            }
        });
    };

    const showFormMessage = (msg, is_success) => {
        $('#msg_form')
            .attr('class', is_success ? 'success-msg' : error_class)
            .html(is_success ? $('<ul/>', { class: 'checked' }).append($('<li/>', { text: localize(msg) })) : localize(msg))
            .css('display', 'block')
            .delay(5000)
            .fadeOut(1000);
    };

    return {
        onLoad,
    };
})();

module.exports = SelfExclusion;
<|endoftext|>


<commit_before>'use strict';

import React from 'react';
import PropTypes from 'prop-types';
import _ from 'underscore';
import { compiler } from 'markdown-to-jsx';
import { CSVMatrixView, TableOfContents, MarkdownHeading, placeholders, HeaderWithLink } from './components';
import * as globals from './../globals';
import { layout, console, object, isServerSide } from './../util';

/**
 * Converts context.content into different format if necessary and returns copy of context with updated 'content'.
 * Currently only converts Markdown content (if a context.content[] item has 'filetype' === 'md'). Others may follow.
 *
 * @param {Object} context - Context provided from back-end, including all properties.
 */
export function parseSectionsContent(context = this.props.context){

    function parse(section){
        if (section.filetype === 'md'){ // If Markdown, we convert 'section.content' to JSX elements.
            var content = compiler(section.content, {
                'overrides' : _.object(_.map(['h1','h2','h3','h4', 'h5', 'h6'], function(type){
                    return [type, {
                        'component' : MarkdownHeading,
                        'props'     : { 'type' : type }
                    }];
                }))
            });
            section =  _.extend({}, section, { 'content' : content });
        }
        // TODO: other parsing stuff based on other filetypes.
        // Else: return the plaintext representation.
        return section;
    }

    if (!Array.isArray(context.content)) throw new Error('context.content is not an array.');

    return _.extend({}, context, { 'content' : _.map(context.content, parse) });
}

/**
 * Converts links to other files into links to sections from a React element and its children (recursively).
 *
 * @param {JSX.Element} elem - A high-level React element representation of some content which might have relative links.
 * @param {{ 'content' : { 'name' : string } }} context - Backend-provided data.
 * @param {number} depth - Current depth.
 * @returns {JSX.Element} - Copy of original 'elem' param with corrected links.
 */
export function correctRelativeLinks(elem, context, depth = 0){
    if (typeof elem !== 'object' || !elem) return elem; // Could be a string, or null.
    if (elem.type === 'a'){
        var href = elem.props.href;
        if (
            typeof href === 'string' &&
            href.charAt(0) !== '#' &&
            href.charAt(0) !== '/' &&
            href.slice(0,4) !== 'http' &&
            href.slice(0,7) !== 'mailto:'
        ){ // We have a relative href link.
            if (href.indexOf('#') > -1){ // It references a title on some other page or section. Likely, this is section is on same page, so we can just use that.
                var parts = href.split('#');
                if (parts.length > 1){
                    href = '#' + parts[1];
                }
            } else { // Check if is name of a section, and if so, correct.
                var filenameWithoutExtension = href.split('.').slice(0, -1).join('.');
                if (typeof _.find(context.content, { 'name' : filenameWithoutExtension }) !== 'undefined'){
                    href = '#' + filenameWithoutExtension;
                }
            }
        }

        if (href !== elem.props.href || href.charAt(0) === '#'){
            return React.cloneElement(
                elem,
                _.extend(_.omit(elem.props, 'children'), {
                    'href' : href,
                    'onClick' : href.charAt(0) !== '#' ? null : function(e){
                        e.preventDefault();
                        layout.animateScrollTo(href.slice(1));
                    }
                }),
                elem.props.children || null
            );
        } else return elem;
    } else if (elem.props.children && typeof elem.type === 'string') {
        return React.cloneElement(
            elem,
            _.omit(elem.props, 'children'),
            React.Children.map(elem.props.children, function(child){
                return correctRelativeLinks(child, context, depth + 1);
            })
        );
    } else return elem;
}

class Wrapper extends React.Component {

    static defaultProps = {
        'contentColSize' : 12,
        'tableOfContents' : false,
        'tocListStyles' : ['decimal', 'lower-alpha', 'lower-roman']
    };

    contentColSize(){
        return Math.min(
            this.props.tableOfContents ? 9 : 12,
            Math.max(6, this.props.contentColSize) // Min 6.
        );
    }

    renderToC(){
        if (!this.props.tableOfContents || this.props.tableOfContents.enabled === false) return null;
        var contentColSize = this.contentColSize();
        var context = this.props.context;
        var toc = context['table-of-contents'] || (this.props.tableOfContents && typeof this.props.tableOfContents === 'object' ? this.props.tableOfContents : {});
        var title = this.props.title || (context && context.title) || null;
        return (

                <TableOfContents
                    context={context}
                    pageTitle={title}
                    fixedGridWidth={12 - contentColSize}
                    navigate={this.props.navigate}
                    href={this.props.href}
                    //skipDepth={1}
                    maxHeaderDepth={toc['header-depth'] || 6}
                    //includeTop={toc['include-top-link']}
                    //listStyleTypes={['none'].concat((toc && toc['list-styles']) || this.props.tocListStyles)}
                />

        );
    }

    render(){

        var title = this.props.title || (this.props.context && this.props.context.title) || null;
        var contentColSize = this.contentColSize();
        var mainColClassName = "col-xs-12 col-sm-12 col-lg-" + contentColSize;

        return (

                { this.renderToC() }

        );
    }
}

export class StaticEntry extends React.Component {

    static defaultProps = {
        'section'   : null,
        'content'   : null,
        'entryType' : 'help',
        'className' : null
    }

    constructor(props){
        super(props);
        this.replacePlaceholder = this.replacePlaceholder.bind(this);
        this.renderEntryContent = this.renderEntryContent.bind(this);
        this.render = this.render.bind(this);
    }

    replacePlaceholder(placeholderString){
        if (placeholderString === '<SlideCarousel/>'){
            return (<placeholders.SlideCarousel />);
        }
        return placeholderString;
    }

    renderEntryContent(baseClassName){
        var content  = (this.props.content && this.props.content.content)  || null;
        if (!content) return null;

        var filetype = this.props.content.filetype || null;
        var placeholder = false;

        if (typeof content === 'string' && content.slice(0,12) === 'placeholder:'){
            placeholder = true;
            content = this.replacePlaceholder(content.slice(12).trim().replace(/\s/g,'')); // Remove all whitespace to help reduce any typo errors.
        }

        var className ="section-content" + (baseClassName? ' ' + baseClassName : '');

        if (filetype === 'csv'){
            return <CSVMatrixView csv={content} options={this.props.content.options} />;
        } else if (placeholder || (filetype === 'md')){
            content = correctRelativeLinks(content, this.props.context);
            //console.log(this.props.section, content, this.props.context);
            return
{ content }
;
        } else {
            return

;
        }
    }

    render(){
        var { content, entryType, sectionName, className } = this.props;
        var id = TableOfContents.elementIDFromSectionName(sectionName);
        return (

                { content && content.title ?
                    <HeaderWithLink className="section-title" link={id} context={this.props.context}>{ content.title }</HeaderWithLink>
                : null }
                { this.renderEntryContent(className) }

        );
    }

}

export default class StaticPage extends React.Component {

    static Entry = StaticEntry

    static Wrapper = Wrapper

    static renderSections(renderMethod, parsedContent){
        if (!parsedContent || !parsedContent.content || !Array.isArray(parsedContent.content)){
            console.error('No content defined for page', parsedContent);
            return null;
        }
        return _.map(parsedContent.content, function(section){ return renderMethod(section.id || section.name, section, parsedContent); });
    }

    static defaultProps = {
        "context" : {
            "title" : "Page Title",
            "content" : {
                "sectionNameID1" : {
                    "order"      : 0,
                    "title"      : "Section Title 1",
                    "content"    : "
Hello
",
                    "filetype"   : "html"
                },
                "sectionNameID2" : {
                    "order"      : 1,
                    "title"      : "Section Title 2",
                    "content"    : "
World
",
                    "filetype"   : "html"
                }
            }
        },
        'entryRenderFxn' : function(sectionName, content, context){
            return <StaticEntry key={sectionName} sectionName={sectionName} content={content} context={context} />;
        }
    };

    static propTypes = {
        'context' : PropTypes.shape({
            "title" : PropTypes.string,
            "content" : PropTypes.any.isRequired,
            "table-of-contents" : PropTypes.object
        }).isRequired,
        'entryRenderFxn' : PropTypes.func
    }

    constructor(props){
        super(props);
        this.render = this.render.bind(this);
        this.entryRenderFxn = typeof this.entryRenderFxn === 'function' ? this.entryRenderFxn.bind(this) : this.props.entryRenderFxn;
    }

    render(){
        var parsedContent = parseSectionsContent(this.props.context);
        var tableOfContents = (parsedContent && parsedContent['table-of-contents'] && parsedContent['table-of-contents'].enabled) ? parsedContent['table-of-contents'] : false;
        return (
            <Wrapper
                key="page-wrapper"
                title={parsedContent.title}
                tableOfContents={tableOfContents}
                context={parsedContent}
                navigate={this.props.navigate}
                href={this.props.href}
                children={StaticPage.renderSections(this.entryRenderFxn, parsedContent)}
            />
        );
    }
}

globals.content_views.register(StaticPage, 'StaticPage');
<commit_msg>Filter out non-published/non-visible sections<commit_after>'use strict';

import React from 'react';
import PropTypes from 'prop-types';
import _ from 'underscore';
import { compiler } from 'markdown-to-jsx';
import { CSVMatrixView, TableOfContents, MarkdownHeading, placeholders, HeaderWithLink } from './components';
import * as globals from './../globals';
import { layout, console, object, isServerSide } from './../util';

/**
 * Converts context.content into different format if necessary and returns copy of context with updated 'content'.
 * Currently only converts Markdown content (if a context.content[] item has 'filetype' === 'md'). Others may follow.
 *
 * @param {Object} context - Context provided from back-end, including all properties.
 */
export function parseSectionsContent(context = this.props.context){

    function parse(section){
        if (section.filetype === 'md'){ // If Markdown, we convert 'section.content' to JSX elements.
            var content = compiler(section.content, {
                'overrides' : _.object(_.map(['h1','h2','h3','h4', 'h5', 'h6'], function(type){
                    return [type, {
                        'component' : MarkdownHeading,
                        'props'     : { 'type' : type }
                    }];
                }))
            });
            section =  _.extend({}, section, { 'content' : content });
        }
        // TODO: other parsing stuff based on other filetypes.
        // Else: return the plaintext representation.
        return section;
    }

    if (!Array.isArray(context.content)) throw new Error('context.content is not an array.');

    return _.extend(
        {}, context, {
            'content' : _.map(
                _.filter(context.content || [], function(section){ return section && section.content && !section.error; }),
                parse
            )
        });
}

/**
 * Converts links to other files into links to sections from a React element and its children (recursively).
 *
 * @param {JSX.Element} elem - A high-level React element representation of some content which might have relative links.
 * @param {{ 'content' : { 'name' : string } }} context - Backend-provided data.
 * @param {number} depth - Current depth.
 * @returns {JSX.Element} - Copy of original 'elem' param with corrected links.
 */
export function correctRelativeLinks(elem, context, depth = 0){
    if (typeof elem !== 'object' || !elem) return elem; // Could be a string, or null.
    if (elem.type === 'a'){
        var href = elem.props.href;
        if (
            typeof href === 'string' &&
            href.charAt(0) !== '#' &&
            href.charAt(0) !== '/' &&
            href.slice(0,4) !== 'http' &&
            href.slice(0,7) !== 'mailto:'
        ){ // We have a relative href link.
            if (href.indexOf('#') > -1){ // It references a title on some other page or section. Likely, this is section is on same page, so we can just use that.
                var parts = href.split('#');
                if (parts.length > 1){
                    href = '#' + parts[1];
                }
            } else { // Check if is name of a section, and if so, correct.
                var filenameWithoutExtension = href.split('.').slice(0, -1).join('.');
                if (typeof _.find(context.content, { 'name' : filenameWithoutExtension }) !== 'undefined'){
                    href = '#' + filenameWithoutExtension;
                }
            }
        }

        if (href !== elem.props.href || href.charAt(0) === '#'){
            return React.cloneElement(
                elem,
                _.extend(_.omit(elem.props, 'children'), {
                    'href' : href,
                    'onClick' : href.charAt(0) !== '#' ? null : function(e){
                        e.preventDefault();
                        layout.animateScrollTo(href.slice(1));
                    }
                }),
                elem.props.children || null
            );
        } else return elem;
    } else if (elem.props.children && typeof elem.type === 'string') {
        return React.cloneElement(
            elem,
            _.omit(elem.props, 'children'),
            React.Children.map(elem.props.children, function(child){
                return correctRelativeLinks(child, context, depth + 1);
            })
        );
    } else return elem;
}

class Wrapper extends React.Component {

    static defaultProps = {
        'contentColSize' : 12,
        'tableOfContents' : false,
        'tocListStyles' : ['decimal', 'lower-alpha', 'lower-roman']
    };

    contentColSize(){
        return Math.min(
            this.props.tableOfContents ? 9 : 12,
            Math.max(6, this.props.contentColSize) // Min 6.
        );
    }

    renderToC(){
        if (!this.props.tableOfContents || this.props.tableOfContents.enabled === false) return null;
        var contentColSize = this.contentColSize();
        var context = this.props.context;
        var toc = context['table-of-contents'] || (this.props.tableOfContents && typeof this.props.tableOfContents === 'object' ? this.props.tableOfContents : {});
        var title = this.props.title || (context && context.title) || null;
        return (

                <TableOfContents
                    context={context}
                    pageTitle={title}
                    fixedGridWidth={12 - contentColSize}
                    navigate={this.props.navigate}
                    href={this.props.href}
                    //skipDepth={1}
                    maxHeaderDepth={toc['header-depth'] || 6}
                    //includeTop={toc['include-top-link']}
                    //listStyleTypes={['none'].concat((toc && toc['list-styles']) || this.props.tocListStyles)}
                />

        );
    }

    render(){

        var title = this.props.title || (this.props.context && this.props.context.title) || null;
        var contentColSize = this.contentColSize();
        var mainColClassName = "col-xs-12 col-sm-12 col-lg-" + contentColSize;

        return (

                { this.renderToC() }

        );
    }
}

export class StaticEntry extends React.Component {

    static defaultProps = {
        'section'   : null,
        'content'   : null,
        'entryType' : 'help',
        'className' : null
    }

    constructor(props){
        super(props);
        this.replacePlaceholder = this.replacePlaceholder.bind(this);
        this.renderEntryContent = this.renderEntryContent.bind(this);
        this.render = this.render.bind(this);
    }

    replacePlaceholder(placeholderString){
        if (placeholderString === '<SlideCarousel/>'){
            return (<placeholders.SlideCarousel />);
        }
        return placeholderString;
    }

    renderEntryContent(baseClassName){
        var content  = (this.props.content && this.props.content.content)  || null;
        if (!content) return null;

        var filetype = this.props.content.filetype || null;
        var placeholder = false;

        if (typeof content === 'string' && content.slice(0,12) === 'placeholder:'){
            placeholder = true;
            content = this.replacePlaceholder(content.slice(12).trim().replace(/\s/g,'')); // Remove all whitespace to help reduce any typo errors.
        }

        var className ="section-content" + (baseClassName? ' ' + baseClassName : '');

        if (filetype === 'csv'){
            return <CSVMatrixView csv={content} options={this.props.content.options} />;
        } else if (placeholder || (filetype === 'md')){
            content = correctRelativeLinks(content, this.props.context);
            //console.log(this.props.section, content, this.props.context);
            return
{ content }
;
        } else {
            return

;
        }
    }

    render(){
        var { content, entryType, sectionName, className } = this.props;
        var id = TableOfContents.elementIDFromSectionName(sectionName);
        return (

                { content && content.title ?
                    <HeaderWithLink className="section-title" link={id} context={this.props.context}>{ content.title }</HeaderWithLink>
                : null }
                { this.renderEntryContent(className) }

        );
    }

}

export default class StaticPage extends React.Component {

    static Entry = StaticEntry

    static Wrapper = Wrapper

    static renderSections(renderMethod, parsedContent){
        if (!parsedContent || !parsedContent.content || !Array.isArray(parsedContent.content)){
            console.error('No content defined for page', parsedContent);
            return null;
        }
        return _.map(parsedContent.content, function(section){ return renderMethod(section.id || section.name, section, parsedContent); });
    }

    static defaultProps = {
        "context" : {
            "title" : "Page Title",
            "content" : {
                "sectionNameID1" : {
                    "order"      : 0,
                    "title"      : "Section Title 1",
                    "content"    : "
Hello
",
                    "filetype"   : "html"
                },
                "sectionNameID2" : {
                    "order"      : 1,
                    "title"      : "Section Title 2",
                    "content"    : "
World
",
                    "filetype"   : "html"
                }
            }
        },
        'entryRenderFxn' : function(sectionName, content, context){
            return <StaticEntry key={sectionName} sectionName={sectionName} content={content} context={context} />;
        }
    };

    static propTypes = {
        'context' : PropTypes.shape({
            "title" : PropTypes.string,
            "content" : PropTypes.any.isRequired,
            "table-of-contents" : PropTypes.object
        }).isRequired,
        'entryRenderFxn' : PropTypes.func
    }

    constructor(props){
        super(props);
        this.render = this.render.bind(this);
        this.entryRenderFxn = typeof this.entryRenderFxn === 'function' ? this.entryRenderFxn.bind(this) : this.props.entryRenderFxn;
    }

    render(){
        var parsedContent = parseSectionsContent(this.props.context);
        var tableOfContents = (parsedContent && parsedContent['table-of-contents'] && parsedContent['table-of-contents'].enabled) ? parsedContent['table-of-contents'] : false;
        return (
            <Wrapper
                key="page-wrapper"
                title={parsedContent.title}
                tableOfContents={tableOfContents}
                context={parsedContent}
                navigate={this.props.navigate}
                href={this.props.href}
                children={StaticPage.renderSections(this.entryRenderFxn, parsedContent)}
            />
        );
    }
}

globals.content_views.register(StaticPage, 'StaticPage');
<|endoftext|>