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

  // Store a local reference to jQuery and Underscorea.
  var $ = window.jQuery;
  var _ = window._;

  var keyDirMap = {'38': 'prev', '40': 'next'};
  var ctrlKeyDirMap = {'80': 'prev', '78': 'next'};

  // Delay a callback `n` ms or invoke immediately if 0.
  var delay = function (cb, n) { return !n ? cb() : _.delay(cb, n); };

  // Direct event bindings to `$el`.
  var directEvents = {
    mouseover: function () { this._hover = true; },
    mouseout: function () {
      this._hover = false;
      _.defer(_.bind(function () {
        if (!this._focus && !this._hover) this.hide();
      }, this));
    }
  };

  // Delegated event bindings to children of `$el`.
  var delegatedEvents = {

    // Delegated events for the input area.
    '.js-rec-input': {

      // Update the `_focus` flag and show the latest query results.
      focus: function (ev) {
        this._focus = true;
        this.query($(ev.currentTarget).val()).show();
      },

      // Check for the up/down keys and enter key, and finally submit a new
      // query.
      keydown: function (ev) {
        var key = ev.which;
        var dir = keyDirMap[key] || (ev.ctrlKey && ctrlKeyDirMap[key]);
        if (dir) {
          this[dir]();
          return ev.preventDefault();
        }
        if (key === 13) {
          var $selected = this.$el.find('.js-rec-selected');
          if ($selected.length) {
            $selected[0].click();
            return ev.preventDefault();
          }
        }
        var $input = $(ev.currentTarget);
        if (key === 27) {
          $input.blur();
          this.hide();
          return ev.preventDefault();
        }
        _.defer(_.bind(function () { this.query($input.val()); }, this));
      },

      // Hide the results when focus is lost.
      blur: function () {
        this._focus = false;
        if (!this._hover) this.hide();
      }
    },

    // Delegated events for invidiual results.
    '.js-rec-result': {

      // Select the result that is being hovered over.
      mouseover: function (ev) { this.select($(ev.currentTarget)); },

      // Optionally clear and hide the input and results on result click.
      click: function () {
        var $input = this.$el.find('.js-rec-input');
        if (this.clearOnClick) $input.val('').focus();
        if (this.hideOnClick) {
          $input.blur();
          this.hide();
        }
      }
    }
  };

  // Create the `Rec` constructor.
  //
  // ```js
  // var rec = new Rec('#my-rec-container', {
  //   url: '/search',
  //   template: jst['search-result']
  // });
  // ```
  var Rec = window.Rec = function (el, options) {

    // Define direct and delegated event hashes unique to this instance. This
    // will allow a clean unbinding of these events without worry about any
    // event collisions.
    this._directEvents = {};
    _.each(directEvents, function (cb, key) {
      this._directEvents[key] = _.bind(cb, this);
    }, this);
    this._delegatedEvents = {};
    _.each(delegatedEvents, function (evs, selector) {
      var bound = this._delegatedEvents[selector] = {};
      _.each(evs, function (cb, name) {
        bound[name] = _.bind(cb, this);
      }, this);
    }, this);

    // Create a cache object to store results.
    this._cached = {};

    // Track concurrent fetches for showing/hiding the loading spinner.
    this._fetchQueue = 0;

    // Set the container element.
    this.setElement(el);

    // Extend the instance with its options.
    for (var name in options) this[name] = options[name];
  };

  // Define `prototype` properties and methods for `Olay`.
  var proto = {

    // How long since the last keystroke should rec wait to call `fetch`?
    delay: 250,

    // How many results should be displayed? `0` means as many as are returned
    // by `fetch`.
    limit: 0,

    // Should results be cached? Any cross-session caching will need to be
    // handled by your `fetch` method, but in-memory caching is free with rec.
    cache: true,

    // Should the first result automatically be selected? In other words, will
    // hitting return fire the first recommended result or the broad result?
    selectFirst: true,

    // Should the results be hidden and focus blurred when a result is clicked?
    hideOnClick: false,

    // Should the input be cleared when a result is clicked?
    clearOnClick: true,

    // The query string key that will be used with the default `fetch` method.
    queryKey: 'q',

    // Should rec attempt to fetch query results for an empty string?
    fetchEmptyQuery: false,

    // Override this template functions to return your labels and results the
    // way you want them. If you are not using the `groupBy` option,
    // `labelTemplate` will be ignored.
    labelTemplate: function (label) { return '
' + label + '
'; },
    resultTemplate: function (result) { return '
' + result + '
'; },

    // Parse the query before sending it to `fetch`. This allows you to clean up
    // bad characters, extra spaces, etc...
    parse: function (q) {
      return q.replace(/\s+/g, ' ').replace(/^\s+|\s+$/g, '').toLowerCase();
    },

    // `filter` works alongside `cache` to intelligently predict possible
    // results while waiting for a server request to come back. Override this
    // function with logic that best matches your server's filter logic.
    filter: function (q, result) {
      var str = JSON.stringify(result).toLowerCase();
      return _.every(q.split(' '), function (word) {
        return ~str.indexOf(word);
      });
    },

    // The default `fetch` is designed for simple, JSON AJAX requests. Override
    // thid function to suite your application's needs.
    fetch: function (q, cb) {
      var options = this.fetchOptions || {};
      (options.data || (options.data = {}))[this.queryKey] = q;
      $.ajax(_.extend({
        type: 'get',
        dataType: 'json',
        success: function (results) { cb(null, results); },
        error: function (xhr) { cb(xhr.responseText); }
      }, options));
      return this;
    },

    query: function (q) {

      // Parse the raw query string.
      q = this.parse(q);

      // Compare to the last query and store so we don't waste any time.
      if (q === this._lastQ) return this;
      this._lastQ = q;

      // Clear the old `fetch` request if one was about to fire.
      if (this._timeout) {
        clearTimeout(this._timeout);
        this._timeout = null;
        if (!--this._fetchQueue) this.$el.removeClass('js-rec-loading');
      }

      // Looked for a cache result before trying to load.
      if (!this._getCached(q) && (q || this.fetchEmptyQuery)) {

        // Add the loading class.
        ++this._fetchQueue;
        this.$el.addClass('js-rec-loading');

        // Start a timeout to `fetch`
        var self = this;
        this._timeout = delay(function () {
          self._timeout = null;
          self.fetch(q, function (er, results) {
            if (!--self._fetchQueue) self.$el.removeClass('js-rec-loading');
            if (!er) self._setCached(q, results);
            self._render();
          });
        }, this.delay);
      }
      return this._render();
    },

    // Set the input element the rec will watch.
    setElement: function (el) {
      if (this.$el) this._unbind();
      this.$el = el instanceof $ ? el : $(el);
      return this._bind();
    },

    show: function () {
      this.$el.addClass('js-rec-active');
      this.select();
      return this;
    },

    hide: function () {
      this.$el.addClass('js-rec-active');
      return this;
    },

    select: function ($el) {
      this.$el.find('.js-rec-selected').removeClass('js-rec-selected');
      if (!$el && this.selectFirst) {
        $el = this.$el.find('.js-rec-selectable').first();
      }
      if ($el) $el.addClass('js-rec-selected');
      return this;
    },

    prev: function () {
      return this.select(
        this.$el.find('.js-rec-selected').prevAll('.js-rec-selectable').first()
      );
    },

    next: function () {
      var $selected = this.$el.find('.js-rec-selected');
      var $next = $selected.nextAll('.js-rec-selectable').first();
      if (!$selected.length) {
        $next = this.$el.find('.js-rec-selectable').first();
      } else if (!$next.length) {
        return this;
      }
      return this.select($next);
    },

    _getCached: function (q) {
      return this.cache && this._cached[q];
    },

    _setCached: function (q, results) {
      this._cached[q] = results instanceof Array ? results : [];
      return this;
    },

    _getFiltered: function (q) {
      if (!this.filter) return null;
      var cached;
      for (var i = q.length - 1; i > 0; --i) {
        if (cached = this._getCached(q.slice(0, i))) break;
      }
      if (!cached) return null;
      var filter = _.bind(this.filter, this, q);
      var matches = _.filter(cached, filter);
      return _.size(matches) ? matches : null;
    },

    // Bind (or unbind) delegated events from a multi-level object.
    _bind: function (unbind) {
      var $el = this.$el;
      var method = unbind ? 'off': 'on';
      $el[method](this._directEvents);
      _.each(this._delegatedEvents, function (evs, selector) {
        _.each(evs, function (cb, name) { $el[method](name, selector, cb); });
      });
      return this;
    },

    // Inverse `_bind`.
    _unbind: function () { return this._bind(true); },

    // Build the elements for the most recent query.
    _render: function () {
      var q = this._lastQ;
      var results = this._getCached(q) || this._getFiltered(q);
      this.$el.removeClass('js-rec-no-results');
      this.$el.find('.js-rec-result, .js-rec-label').remove();
      if (!results) return this;
      var $results = this.$el.find('.js-rec-results');
      if (results.length) {
        results = this.limit ? _.first(results, this.limit) : results;
        var limit = results.length;
        var count = 0;
        results = _.groupBy(results, this.groupBy || 'undefined');
        _.each(results, function (results, label) {
          if (count === limit) return;
          if (label !== 'undefined') $results.append(this._renderLabel(label));
          _.each(results, function (result) {
            if (count === limit) return;
            $results.append(this._renderResult(result));
            ++count;
          }, this);
        }, this);
        this.select();
      } else {
        this.$el.addClass('js-rec-no-results');
      }
      return this;
    },

    _renderLabel: function (label) {
      var el = this.labelTemplate(label);
      return (el instanceof $ ? el : $(el)).addClass('js-rec-label');
    },

    _renderResult: function (result) {
      var el = this.resultTemplate(result);
      return (el instanceof $ ? el : $(el))
        .addClass('js-rec-result js-rec-selectable');
    }
  };

  // Extend `Rec.prototype`.
  for (var name in proto) Rec.prototype[name] = proto[name];
})();
<commit_msg>Remove `js-rec-active` on `hide`<commit_after>(function () {
  'use strict';

  // Store a local reference to jQuery and Underscorea.
  var $ = window.jQuery;
  var _ = window._;

  var keyDirMap = {'38': 'prev', '40': 'next'};
  var ctrlKeyDirMap = {'80': 'prev', '78': 'next'};

  // Delay a callback `n` ms or invoke immediately if 0.
  var delay = function (cb, n) { return !n ? cb() : _.delay(cb, n); };

  // Direct event bindings to `$el`.
  var directEvents = {
    mouseover: function () { this._hover = true; },
    mouseout: function () {
      this._hover = false;
      _.defer(_.bind(function () {
        if (!this._focus && !this._hover) this.hide();
      }, this));
    }
  };

  // Delegated event bindings to children of `$el`.
  var delegatedEvents = {

    // Delegated events for the input area.
    '.js-rec-input': {

      // Update the `_focus` flag and show the latest query results.
      focus: function (ev) {
        this._focus = true;
        this.query($(ev.currentTarget).val()).show();
      },

      // Check for the up/down keys and enter key, and finally submit a new
      // query.
      keydown: function (ev) {
        var key = ev.which;
        var dir = keyDirMap[key] || (ev.ctrlKey && ctrlKeyDirMap[key]);
        if (dir) {
          this[dir]();
          return ev.preventDefault();
        }
        if (key === 13) {
          var $selected = this.$el.find('.js-rec-selected');
          if ($selected.length) {
            $selected[0].click();
            return ev.preventDefault();
          }
        }
        var $input = $(ev.currentTarget);
        if (key === 27) {
          $input.blur();
          this.hide();
          return ev.preventDefault();
        }
        _.defer(_.bind(function () { this.query($input.val()); }, this));
      },

      // Hide the results when focus is lost.
      blur: function () {
        this._focus = false;
        if (!this._hover) this.hide();
      }
    },

    // Delegated events for invidiual results.
    '.js-rec-result': {

      // Select the result that is being hovered over.
      mouseover: function (ev) { this.select($(ev.currentTarget)); },

      // Optionally clear and hide the input and results on result click.
      click: function () {
        var $input = this.$el.find('.js-rec-input');
        if (this.clearOnClick) $input.val('').focus();
        if (this.hideOnClick) {
          $input.blur();
          this.hide();
        }
      }
    }
  };

  // Create the `Rec` constructor.
  //
  // ```js
  // var rec = new Rec('#my-rec-container', {
  //   url: '/search',
  //   template: jst['search-result']
  // });
  // ```
  var Rec = window.Rec = function (el, options) {

    // Define direct and delegated event hashes unique to this instance. This
    // will allow a clean unbinding of these events without worry about any
    // event collisions.
    this._directEvents = {};
    _.each(directEvents, function (cb, key) {
      this._directEvents[key] = _.bind(cb, this);
    }, this);
    this._delegatedEvents = {};
    _.each(delegatedEvents, function (evs, selector) {
      var bound = this._delegatedEvents[selector] = {};
      _.each(evs, function (cb, name) {
        bound[name] = _.bind(cb, this);
      }, this);
    }, this);

    // Create a cache object to store results.
    this._cached = {};

    // Track concurrent fetches for showing/hiding the loading spinner.
    this._fetchQueue = 0;

    // Set the container element.
    this.setElement(el);

    // Extend the instance with its options.
    for (var name in options) this[name] = options[name];
  };

  // Define `prototype` properties and methods for `Olay`.
  var proto = {

    // How long since the last keystroke should rec wait to call `fetch`?
    delay: 250,

    // How many results should be displayed? `0` means as many as are returned
    // by `fetch`.
    limit: 0,

    // Should results be cached? Any cross-session caching will need to be
    // handled by your `fetch` method, but in-memory caching is free with rec.
    cache: true,

    // Should the first result automatically be selected? In other words, will
    // hitting return fire the first recommended result or the broad result?
    selectFirst: true,

    // Should the results be hidden and focus blurred when a result is clicked?
    hideOnClick: false,

    // Should the input be cleared when a result is clicked?
    clearOnClick: true,

    // The query string key that will be used with the default `fetch` method.
    queryKey: 'q',

    // Should rec attempt to fetch query results for an empty string?
    fetchEmptyQuery: false,

    // Override this template functions to return your labels and results the
    // way you want them. If you are not using the `groupBy` option,
    // `labelTemplate` will be ignored.
    labelTemplate: function (label) { return '
' + label + '
'; },
    resultTemplate: function (result) { return '
' + result + '
'; },

    // Parse the query before sending it to `fetch`. This allows you to clean up
    // bad characters, extra spaces, etc...
    parse: function (q) {
      return q.replace(/\s+/g, ' ').replace(/^\s+|\s+$/g, '').toLowerCase();
    },

    // `filter` works alongside `cache` to intelligently predict possible
    // results while waiting for a server request to come back. Override this
    // function with logic that best matches your server's filter logic.
    filter: function (q, result) {
      var str = JSON.stringify(result).toLowerCase();
      return _.every(q.split(' '), function (word) {
        return ~str.indexOf(word);
      });
    },

    // The default `fetch` is designed for simple, JSON AJAX requests. Override
    // thid function to suite your application's needs.
    fetch: function (q, cb) {
      var options = this.fetchOptions || {};
      (options.data || (options.data = {}))[this.queryKey] = q;
      $.ajax(_.extend({
        type: 'get',
        dataType: 'json',
        success: function (results) { cb(null, results); },
        error: function (xhr) { cb(xhr.responseText); }
      }, options));
      return this;
    },

    query: function (q) {

      // Parse the raw query string.
      q = this.parse(q);

      // Compare to the last query and store so we don't waste any time.
      if (q === this._lastQ) return this;
      this._lastQ = q;

      // Clear the old `fetch` request if one was about to fire.
      if (this._timeout) {
        clearTimeout(this._timeout);
        this._timeout = null;
        if (!--this._fetchQueue) this.$el.removeClass('js-rec-loading');
      }

      // Looked for a cache result before trying to load.
      if (!this._getCached(q) && (q || this.fetchEmptyQuery)) {

        // Add the loading class.
        ++this._fetchQueue;
        this.$el.addClass('js-rec-loading');

        // Start a timeout to `fetch`
        var self = this;
        this._timeout = delay(function () {
          self._timeout = null;
          self.fetch(q, function (er, results) {
            if (!--self._fetchQueue) self.$el.removeClass('js-rec-loading');
            if (!er) self._setCached(q, results);
            self._render();
          });
        }, this.delay);
      }
      return this._render();
    },

    // Set the input element the rec will watch.
    setElement: function (el) {
      if (this.$el) this._unbind();
      this.$el = el instanceof $ ? el : $(el);
      return this._bind();
    },

    show: function () {
      this.$el.addClass('js-rec-active');
      this.select();
      return this;
    },

    hide: function () {
      this.$el.removeClass('js-rec-active');
      return this;
    },

    select: function ($el) {
      this.$el.find('.js-rec-selected').removeClass('js-rec-selected');
      if (!$el && this.selectFirst) {
        $el = this.$el.find('.js-rec-selectable').first();
      }
      if ($el) $el.addClass('js-rec-selected');
      return this;
    },

    prev: function () {
      return this.select(
        this.$el.find('.js-rec-selected').prevAll('.js-rec-selectable').first()
      );
    },

    next: function () {
      var $selected = this.$el.find('.js-rec-selected');
      var $next = $selected.nextAll('.js-rec-selectable').first();
      if (!$selected.length) {
        $next = this.$el.find('.js-rec-selectable').first();
      } else if (!$next.length) {
        return this;
      }
      return this.select($next);
    },

    _getCached: function (q) {
      return this.cache && this._cached[q];
    },

    _setCached: function (q, results) {
      this._cached[q] = results instanceof Array ? results : [];
      return this;
    },

    _getFiltered: function (q) {
      if (!this.filter) return null;
      var cached;
      for (var i = q.length - 1; i > 0; --i) {
        if (cached = this._getCached(q.slice(0, i))) break;
      }
      if (!cached) return null;
      var filter = _.bind(this.filter, this, q);
      var matches = _.filter(cached, filter);
      return _.size(matches) ? matches : null;
    },

    // Bind (or unbind) delegated events from a multi-level object.
    _bind: function (unbind) {
      var $el = this.$el;
      var method = unbind ? 'off': 'on';
      $el[method](this._directEvents);
      _.each(this._delegatedEvents, function (evs, selector) {
        _.each(evs, function (cb, name) { $el[method](name, selector, cb); });
      });
      return this;
    },

    // Inverse `_bind`.
    _unbind: function () { return this._bind(true); },

    // Build the elements for the most recent query.
    _render: function () {
      var q = this._lastQ;
      var results = this._getCached(q) || this._getFiltered(q);
      this.$el.removeClass('js-rec-no-results');
      this.$el.find('.js-rec-result, .js-rec-label').remove();
      if (!results) return this;
      var $results = this.$el.find('.js-rec-results');
      if (results.length) {
        results = this.limit ? _.first(results, this.limit) : results;
        var limit = results.length;
        var count = 0;
        results = _.groupBy(results, this.groupBy || 'undefined');
        _.each(results, function (results, label) {
          if (count === limit) return;
          if (label !== 'undefined') $results.append(this._renderLabel(label));
          _.each(results, function (result) {
            if (count === limit) return;
            $results.append(this._renderResult(result));
            ++count;
          }, this);
        }, this);
        this.select();
      } else {
        this.$el.addClass('js-rec-no-results');
      }
      return this;
    },

    _renderLabel: function (label) {
      var el = this.labelTemplate(label);
      return (el instanceof $ ? el : $(el)).addClass('js-rec-label');
    },

    _renderResult: function (result) {
      var el = this.resultTemplate(result);
      return (el instanceof $ ? el : $(el))
        .addClass('js-rec-result js-rec-selectable');
    }
  };

  // Extend `Rec.prototype`.
  for (var name in proto) Rec.prototype[name] = proto[name];
})();
<|endoftext|>


<commit_before>import React, {Component, PropTypes} from 'react';
import Immutable, {List, Map} from 'immutable';
import classNames from 'classnames';
import selectn from 'selectn';

import t from 'tcomb-form';

import fields from 'models/schemas/filter-fields';
import options from 'models/schemas/filter-options';

import ProviderHelpers from 'common/ProviderHelpers';
import StringHelpers from 'common/StringHelpers';
import NavigationHelpers from 'common/NavigationHelpers';

import {RaisedButton, FlatButton, Popover} from 'material-ui';
import IntlService from 'views/services/intl';

const intl = IntlService.instance;
const confirmationButtonsStyle = {padding: '0', marginLeft: '5px', minWidth: 'auto', border: '1px solid gray'};

export default function withForm(ComposedFilter, publishWarningEnabled = false) {
    class ViewWithForm extends Component {

        static propTypes = {
            routeParams: PropTypes.object,
            initialValues: PropTypes.object,
            fields: PropTypes.object.isRequired,
            options: PropTypes.object.isRequired,
            type: PropTypes.string.isRequired,
            itemId: PropTypes.string.isRequired,
            saveMethod: PropTypes.func.isRequired,
            cancelMethod: PropTypes.func,
            currentPath: PropTypes.array,
            navigationMethod: PropTypes.func,
            computeEntities: PropTypes.instanceOf(List).isRequired
        };

        static defaultProps = {
            cancelMethod: () => {
            }
        }

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

            this.state = {
                formValue: null,
                showCancelWarning: false,
                saved: false
            };

            ['_onRequestSaveForm', '_onRequestCancelForm'].forEach((method => this[method] = this[method].bind(this)));
        }

        _getComputeItem(props) {

            const {computeEntities, itemId} = props;

            const item = computeEntities.find((value, key) =>
                value.get('id') === itemId
            );

            return ProviderHelpers.getEntry(item.get('entity'), itemId);
        }

        _onRequestSaveForm(portal, e) {

            // Prevent default behaviour
            e.preventDefault();

            let formValue = this.refs["form_" + this.props.type].getValue();

            // Passed validation
            if (formValue) {
                this.props.saveMethod(portal, formValue);

                this.setState({
                    saved: true,
                    formValue: formValue
                });
            } else {
                window.scrollTo(0, 0);
            }
        }

        _onRequestCancelForm(e, force = false) {

            if (force) {
                if (this.props.cancelMethod) {
                    this.props.cancelMethod();
                }
                else {
                    NavigationHelpers.navigateUp(this.props.currentPath, this.props.navigationMethod);
                }
            } else {
                e.preventDefault();
            }

            this.setState({
                cancelButtonEl: e.currentTarget,
                showCancelWarning: true
            })
        }

        componentWillReceiveProps(nextProps) {

            if (this.state.saved) {
                let currentWord = this._getComputeItem(this.props);
                let nextWord = this._getComputeItem(nextProps);

                let currentWordWasUpdated = selectn('wasUpdated', currentWord);
                let currentWordWasCreated = selectn('wasCreated', currentWord);

                let nextWordWasUpdated = selectn('wasUpdated', nextWord);
                let nextWordWasCreated = selectn('wasCreated', nextWord);

                // 'Redirect' on update or creation success
                if ((nextWordWasUpdated && currentWordWasUpdated != nextWordWasUpdated) || (nextWordWasCreated && currentWordWasCreated != nextWordWasCreated)) {

                    if (nextWordWasUpdated) {
                        NavigationHelpers.navigateUp(this.props.currentPath, this.props.navigationMethod);
                    } else if (nextWordWasCreated) {
                        //navigateForwardReplace
                        //nextProps.replaceWindowPath('/' + nextProps.routeParams.theme + selectn('response.path', nextWord).replace('Dictionary', 'learn/words'));
                    }
                }
            }
        }

        render() {

            const {initialValues, fields, options, type} = this.props;
            const computeItem = this._getComputeItem(this.props);

            return (

                        <ComposedFilter renderOnError={true} {...this.props} {...this.state}>

                                <form onSubmit={this._onRequestSaveForm.bind(this, computeItem)}>
                                    <t.form.Form
                                        ref={"form_" + type}
                                        type={t.struct(selectn(type, fields))}
                                        context={initialValues}
                                        value={this.state.formValue || selectn("response.properties", computeItem)}
                                        options={selectn(type, options)}/>

                                        <FlatButton onTouchTap={this._onRequestCancelForm} style={{marginRight: '10px'}}
                                                    label={intl.trans('cancel', 'Cancel', 'first')}/>
                                        <RaisedButton onTouchTap={this._onRequestSaveForm.bind(this, computeItem)}
                                                      primary={true} label={intl.trans('save', 'Save', 'first')}/>

                                        <Popover
                                            open={this.state.showCancelWarning}
                                            anchorEl={this.state.cancelButtonEl}
                                            anchorOrigin={{horizontal: 'left', vertical: 'center'}}
                                            targetOrigin={{horizontal: 'right', vertical: 'center'}}
                                            onRequestClose={() => this.setState({showCancelWarning: false})}>

                                                <span dangerouslySetInnerHTML={{__html: intl.trans('views.hoc.view.discard_warning', 'Are you sure you want to discard your changes?', 'first')}}>
                                                <FlatButton style={confirmationButtonsStyle}
                                                            onTouchTap={this._onRequestCancelForm.bind(this, true)}
                                                            label={intl.trans('yes', 'Yes', 'first') + '!'}/>
                                                <FlatButton style={confirmationButtonsStyle}
                                                            onTouchTap={() => this.setState({showCancelWarning: false})}
                                                            label={intl.trans('no', 'No', 'first') + '!'}/>

                                        </Popover>

                                </form>

                        </ComposedFilter>

Metadata

                                    {intl.trans('last_modified', 'Last Modified', 'first')}

                                    {selectn("response.lastModified", computeItem)}

                                    {intl.trans('last_contributor', 'Last Contributor', 'first')}

                                    {selectn("response.properties.dc:lastContributor", computeItem)}

                                    {intl.trans('date_created', 'Date Created', 'first')}

                                    {selectn("response.properties.dc:created", computeItem)}

                                    {intl.trans('contributors', 'Contributors', 'first')}

                                    {(selectn("response.properties.dc:contributors", computeItem) || []).join(',')}

                                    {intl.trans('version', 'Version', 'first')}

                                    {selectn("response.properties.uid:major_version", computeItem)}.{selectn("response.properties.uid:minor_version", computeItem)}

);
        }
    }

    return ViewWithForm;
}<commit_msg>TRELLO-131 - Add save button to top of form<commit_after>import React, {Component, PropTypes} from 'react';
import Immutable, {List, Map} from 'immutable';
import classNames from 'classnames';
import selectn from 'selectn';

import t from 'tcomb-form';

import fields from 'models/schemas/filter-fields';
import options from 'models/schemas/filter-options';

import ProviderHelpers from 'common/ProviderHelpers';
import StringHelpers from 'common/StringHelpers';
import NavigationHelpers from 'common/NavigationHelpers';

import {RaisedButton, FlatButton, Popover} from 'material-ui';
import IntlService from 'views/services/intl';

const intl = IntlService.instance;
const confirmationButtonsStyle = {padding: '0', marginLeft: '5px', minWidth: 'auto', border: '1px solid gray'};

export default function withForm(ComposedFilter, publishWarningEnabled = false) {
    class ViewWithForm extends Component {

        static propTypes = {
            routeParams: PropTypes.object,
            initialValues: PropTypes.object,
            fields: PropTypes.object.isRequired,
            options: PropTypes.object.isRequired,
            type: PropTypes.string.isRequired,
            itemId: PropTypes.string.isRequired,
            saveMethod: PropTypes.func.isRequired,
            cancelMethod: PropTypes.func,
            currentPath: PropTypes.array,
            navigationMethod: PropTypes.func,
            computeEntities: PropTypes.instanceOf(List).isRequired
        };

        static defaultProps = {
            cancelMethod: () => {
            }
        }

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

            this.state = {
                formValue: null,
                showCancelWarning: false,
                saved: false
            };

            ['_onRequestSaveForm', '_onRequestCancelForm'].forEach((method => this[method] = this[method].bind(this)));
        }

        _getComputeItem(props) {

            const {computeEntities, itemId} = props;

            const item = computeEntities.find((value, key) =>
                value.get('id') === itemId
            );

            return ProviderHelpers.getEntry(item.get('entity'), itemId);
        }

        _onRequestSaveForm(portal, e) {

            // Prevent default behaviour
            e.preventDefault();

            let formValue = this.refs["form_" + this.props.type].getValue();

            // Passed validation
            if (formValue) {
                this.props.saveMethod(portal, formValue);

                this.setState({
                    saved: true,
                    formValue: formValue
                });
            } else {
                window.scrollTo(0, 0);
            }
        }

        _onRequestCancelForm(e, force = false) {

            if (force) {
                if (this.props.cancelMethod) {
                    this.props.cancelMethod();
                }
                else {
                    NavigationHelpers.navigateUp(this.props.currentPath, this.props.navigationMethod);
                }
            } else {
                e.preventDefault();
            }

            this.setState({
                cancelButtonEl: e.currentTarget,
                showCancelWarning: true
            })
        }

        componentWillReceiveProps(nextProps) {

            if (this.state.saved) {
                let currentWord = this._getComputeItem(this.props);
                let nextWord = this._getComputeItem(nextProps);

                let currentWordWasUpdated = selectn('wasUpdated', currentWord);
                let currentWordWasCreated = selectn('wasCreated', currentWord);

                let nextWordWasUpdated = selectn('wasUpdated', nextWord);
                let nextWordWasCreated = selectn('wasCreated', nextWord);

                // 'Redirect' on update or creation success
                if ((nextWordWasUpdated && currentWordWasUpdated != nextWordWasUpdated) || (nextWordWasCreated && currentWordWasCreated != nextWordWasCreated)) {

                    if (nextWordWasUpdated) {
                        NavigationHelpers.navigateUp(this.props.currentPath, this.props.navigationMethod);
                    } else if (nextWordWasCreated) {
                        //navigateForwardReplace
                        //nextProps.replaceWindowPath('/' + nextProps.routeParams.theme + selectn('response.path', nextWord).replace('Dictionary', 'learn/words'));
                    }
                }
            }
        }

        render() {

            const {initialValues, fields, options, type} = this.props;
            const computeItem = this._getComputeItem(this.props);

            return (

                        <ComposedFilter renderOnError={true} {...this.props} {...this.state}>

                                <form onSubmit={this._onRequestSaveForm.bind(this, computeItem)}>

                                        <FlatButton onTouchTap={this._onRequestCancelForm} style={{marginRight: '10px'}}
                                                label={intl.trans('cancel', 'Cancel', 'first')}/>
                                        <RaisedButton onTouchTap={this._onRequestSaveForm.bind(this, computeItem)}
                                                primary={true} label={intl.trans('save', 'Save', 'first')}/>

                                    <t.form.Form
                                        ref={"form_" + type}
                                        type={t.struct(selectn(type, fields))}
                                        context={initialValues}
                                        value={this.state.formValue || selectn("response.properties", computeItem)}
                                        options={selectn(type, options)}/>

                                        <FlatButton onTouchTap={this._onRequestCancelForm} style={{marginRight: '10px'}}
                                                    label={intl.trans('cancel', 'Cancel', 'first')}/>
                                        <RaisedButton onTouchTap={this._onRequestSaveForm.bind(this, computeItem)}
                                                      primary={true} label={intl.trans('save', 'Save', 'first')}/>

                                        <Popover
                                            open={this.state.showCancelWarning}
                                            anchorEl={this.state.cancelButtonEl}
                                            anchorOrigin={{horizontal: 'left', vertical: 'center'}}
                                            targetOrigin={{horizontal: 'right', vertical: 'center'}}
                                            onRequestClose={() => this.setState({showCancelWarning: false})}>

                                                <span dangerouslySetInnerHTML={{__html: intl.trans('views.hoc.view.discard_warning', 'Are you sure you want to discard your changes?', 'first')}}>
                                                <FlatButton style={confirmationButtonsStyle}
                                                            onTouchTap={this._onRequestCancelForm.bind(this, true)}
                                                            label={intl.trans('yes', 'Yes', 'first') + '!'}/>
                                                <FlatButton style={confirmationButtonsStyle}
                                                            onTouchTap={() => this.setState({showCancelWarning: false})}
                                                            label={intl.trans('no', 'No', 'first') + '!'}/>

                                        </Popover>

                                </form>

                        </ComposedFilter>

Metadata

                                    {intl.trans('last_modified', 'Last Modified', 'first')}

                                    {selectn("response.lastModified", computeItem)}

                                    {intl.trans('last_contributor', 'Last Contributor', 'first')}

                                    {selectn("response.properties.dc:lastContributor", computeItem)}

                                    {intl.trans('date_created', 'Date Created', 'first')}

                                    {selectn("response.properties.dc:created", computeItem)}

                                    {intl.trans('contributors', 'Contributors', 'first')}

                                    {(selectn("response.properties.dc:contributors", computeItem) || []).join(',')}

                                    {intl.trans('version', 'Version', 'first')}

                                    {selectn("response.properties.uid:major_version", computeItem)}.{selectn("response.properties.uid:minor_version", computeItem)}

);
        }
    }

    return ViewWithForm;
}<|endoftext|>


<commit_before><commit_msg>Check for currency symbol in first or second position since negative numbers start with open paren<commit_after><|endoftext|>


<commit_before>/* jshint browser: true, devel: true, indent: 2, curly: true, eqeqeq: true, futurehostile: true, latedef: true, undef: true, unused: true */
/* global $, jQuery, document, Modernizr */

function l(data) {
  'use strict';
  console.log(data);
}

// VARS

var retina = Modernizr.highresdisplay,
  largeImageThreshold = 800,
  largestImageThreshold = 1400,

  margin = 35,

  windowHeight = $(window).innerHeight(),

  captionHeight = $('#single-slider-text').outerHeight(),

  caption,
  activeIndex,
  initSlide,
  State = History.getState(),
  hashState = State.data.state;

// FUNCTIONS

  // LAZY IMAGES

function lazyLoadImages(selector) {
  $(selector).each(function() {
    var $this = $(this);
    var data = $this.data();
    var windowWidth = $(window).width();

    if (retina) {
      if (windowWidth > (largeImageThreshold*1.5)) {
        $this.attr('src', data.largest);
      } else {
        $this.attr('src', data.large);
      }
    } else if (windowWidth > largestImageThreshold) {
      $this.attr('src', data.largest);
    } else if (windowWidth > largeImageThreshold) {
      $this.attr('src', data.large);
    } else {
      $this.attr('src', data.basic);
    }
  });
}

  // LAYOUT

$(window).resize(function() {
  windowHeight = $(window).innerHeight();
});

function singleLayout() {
  $('#single-slider').css({
    'padding-top': margin,
    'height': (windowHeight - captionHeight)
  });
}

// OBJECTS

  // HOME SPREADS

var Spreads = {
  containerHeight: 0,
  containerMargin: 0,
  init: function() {
    var _this = this;
    _this.containerLayoutFix();
    _this.resizeImages();

    $('.spread-image').on('click', function() {
      _this.nextSpread();
    });

    $(window).resize(function() {
      _this.containerLayoutFix();
      _this.resizeImages();
    });
  },

  containerLayoutFix: function() {
    var _this = this;
    _this.containerMargin = parseInt($('#header').css('margin-top'), 10);
    _this.containerHeight = windowHeight - $('#header').outerHeight(true) - _this.containerMargin;

    $('#spread-container').height( _this.containerHeight );
  },

  resizeImages: function () {
    var _this = this;

    $('.home-spread.home-spread-active').children('.spread-image-wrapper').each(function() {

      var $this = $(this),
      imageWrapHeight = $this.height(),
      position = $this.position(),
      top = position.top,
      scale = ($this.attr('data-scale'))*0.01,
      imageCaptionHeight = $this.find('.spread-image-caption').outerHeight();

      if ((imageWrapHeight + top) > _this.containerHeight) {

        $this.css({
          'height' : (_this.containerHeight - top) + 'px'
        });

      }

      if (scale > 1) {
        scale = 1;
        l(scale);
      }
      imageWrapHeight = $this.height();
      var imageMaxHeight = (imageWrapHeight*scale) - imageCaptionHeight;

      $this.find('.spread-image').css( 'max-height', imageMaxHeight );

    });
  },

  nextSpread: function() {
    var activeSpread = $('.home-spread.home-spread-active');
    var nextSpread = activeSpread.next();

    activeSpread.removeClass('home-spread-active');

    if (nextSpread.length) {
      nextSpread.addClass('home-spread-active');
    } else {
      $('.home-spread').first().addClass('home-spread-active');
    }

    this.resizeImages();

  }
};

  // SLICK

var Slick = {
  init: function() {
    var _this = this;
    $('.js-slick-container').on({
      init: function(event, slick){
        var currentSlideIndex = $('.slick-active').attr('data-slick-index');
        // set caption
        _this.replaceCaption(currentSlideIndex);

        // set length for n of * in captions
        var slidesLength = $('.js-slick-item').length;
        if (slidesLength === 1) {
          $('#slick-length').html(1);
          $('#slide-nav').remove();
        } else {
          $('#slick-length').html(slidesLength);
        }

        // lazy load images for screen resolution
        lazyLoadImages('.slider-img');

        // fix images for window height
        _this.resizeImages();

        // fade in when ready
        $('#single-slider').css( 'opacity' , 1 );
        $('#single-slider-text').css( 'opacity' , 1 );
      },
      afterChange: function(event, slick, currentSlide){
        // set caption
        _this.replaceCaption(currentSlide);

        // set active index in human readable form
        activeIndex = $('[data-slick-index="'+currentSlide+'"]').attr('data-number');
        $('#slick-current-index').html(activeIndex);

        caption = $('[data-slick-index="'+currentSlide+'"]').attr('data-caption');

        //_this.pushSlideState(activeIndex,caption);
        history.pushState({state: activeIndex}, caption, "#"+activeIndex);
      },
    })
    .slick({
      fade: true,
      speed: 500,
      prevArrow: '#slick-prev',
      nextArrow: '#slick-next',
    });

    if (hashState > 1) {
      initSlide = $('[data-number="' + hashState + '"]').attr('data-slick-index');
      $('.js-slick-container').slick('slickGoTo',initSlide);
    }

    $('.js-slick-item').on('click', function() {
      $('.js-slick-container').slick('slickNext');
    });

    $(window).on('resize', function() {
      _this.resizeImages();
    });
  },

  pushSlideState: function(activeIndex, caption) {
    History.pushState({state:activeIndex}, null, "#"+activeIndex);
  },

  replaceCaption: function(currentSlide) {
    var caption = $('[data-slick-index="' + currentSlide + '"]').data('caption');
    if (! caption || caption === undefined || caption === null) {
      $('#slick-caption').html(' ');
    } else {
      $('#slick-caption').html(caption);
    }
  },

  resizeImages: function() {
    $('.js-slick-item img').css( 'max-height' , ( windowHeight - captionHeight - margin ) );
  }
};

jQuery(document).ready(function () {
  'use strict';
  l('Hola Globie');

  // CONDITIONAL INITS

  if ( $('.js-packery-container').length ) {
    $('.js-packery-container').imagesLoaded( function() {
      $('.js-packery-container').packery({
        itemSelector: '.js-packery-item',
        transitionDuration: '0s',
        percentPosition: true
      }).css({
        'opacity': 1
      });
    });
  }

  if ($('body').hasClass('single')) {
    singleLayout();
  }

  if ($('body').hasClass('home')) {
    Spreads.init();
  }

  if ( $('.js-slick-item').length ) {
    Slick.init();
  }

});<commit_msg>fix hashState var.  pushState use data-id attr from slide / img id.  remove pushSlideState thing.  fix spread image wrapper scale<commit_after>/* jshint browser: true, devel: true, indent: 2, curly: true, eqeqeq: true, futurehostile: true, latedef: true, undef: true, unused: true */
/* global $, jQuery, document, Modernizr */

function l(data) {
  'use strict';
  console.log(data);
}

// VARS

var retina = Modernizr.highresdisplay,
  largeImageThreshold = 800,
  largestImageThreshold = 1400,

  margin = 35,

  windowHeight = $(window).innerHeight(),

  captionHeight = $('#single-slider-text').outerHeight(),

  caption,
  activeIndex,
  initSlide,
  State = History.getState(),
  hashState = State.hash.slice(-1);

// FUNCTIONS

  // LAZY IMAGES

function lazyLoadImages(selector) {
  $(selector).each(function() {
    var $this = $(this);
    var data = $this.data();
    var windowWidth = $(window).width();

    if (retina) {
      if (windowWidth > (largeImageThreshold*1.5)) {
        $this.attr('src', data.largest);
      } else {
        $this.attr('src', data.large);
      }
    } else if (windowWidth > largestImageThreshold) {
      $this.attr('src', data.largest);
    } else if (windowWidth > largeImageThreshold) {
      $this.attr('src', data.large);
    } else {
      $this.attr('src', data.basic);
    }
  });
}

  // LAYOUT

$(window).resize(function() {
  windowHeight = $(window).innerHeight();
});

function singleLayout() {
  $('#single-slider').css({
    'padding-top': margin,
    'height': (windowHeight - captionHeight)
  });
}

// OBJECTS

  // HOME SPREADS

var Spreads = {
  containerHeight: 0,
  containerMargin: 0,
  init: function() {
    var _this = this;
    _this.containerLayoutFix();
    _this.resizeImages();

    $('.spread-image').on('click', function() {
      _this.nextSpread();
    });

    $(window).resize(function() {
      _this.containerLayoutFix();
      _this.resizeImages();
    });
  },

  containerLayoutFix: function() {
    var _this = this;
    _this.containerMargin = parseInt($('#header').css('margin-top'), 10);
    _this.containerHeight = windowHeight - $('#header').outerHeight(true) - _this.containerMargin;

    $('#spread-container').height( _this.containerHeight );
  },

  resizeImages: function () {
    var _this = this;

    $('.home-spread.home-spread-active').children('.spread-image-wrapper').each(function() {

      var $this = $(this),
      imageWrapHeight = $this.height(),
      position = $this.position(),
      top = position.top,
      scale = ($this.attr('data-scale'))*0.01,
      imageCaptionHeight = $this.find('.spread-image-caption').outerHeight();

      if ((imageWrapHeight + top) > _this.containerHeight) {

        $this.css({
          'height' : (_this.containerHeight - top) + 'px'
        });

      }

      imageWrapHeight = $this.height();

      if (scale > 0) {
        if (scale > 1) {
          scale = 1;
        }
        var imageMaxHeight = (imageWrapHeight*scale) - imageCaptionHeight;
      } else {
        var imageMaxHeight = imageWrapHeight - imageCaptionHeight;
      }

      $this.find('img.spread-image').css( 'max-height', imageMaxHeight );

    });
  },

  nextSpread: function() {
    var activeSpread = $('.home-spread.home-spread-active');
    var nextSpread = activeSpread.next();

    activeSpread.removeClass('home-spread-active');

    if (nextSpread.length) {
      nextSpread.addClass('home-spread-active');
    } else {
      $('.home-spread').first().addClass('home-spread-active');
    }

    this.resizeImages();

  }
};

  // SLICK

var Slick = {
  init: function() {
    var _this = this;
    $('.js-slick-container').on({
      init: function(event, slick){
        var currentSlideIndex = $('.slick-active').attr('data-slick-index');
        // set caption
        _this.replaceCaption(currentSlideIndex);

        // set length for n of * in captions
        var slidesLength = $('.js-slick-item').length;
        if (slidesLength === 1) {
          $('#slick-length').html(1);
          $('#slide-nav').remove();
        } else {
          $('#slick-length').html(slidesLength);
        }

        // lazy load images for screen resolution
        lazyLoadImages('.slider-img');

        // fix images for window height
        _this.resizeImages();

        // fade in when ready
        $('#single-slider').css( 'opacity' , 1 );
        $('#single-slider-text').css( 'opacity' , 1 );
      },
      afterChange: function(event, slick, currentSlide){
        // set caption
        _this.replaceCaption(currentSlide);

        // set active index in human readable form
        activeIndex = $('[data-slick-index="'+currentSlide+'"]').attr('data-number');
        $('#slick-current-index').html(activeIndex);

        caption = $('[data-slick-index="'+currentSlide+'"]').attr('data-caption');

        var activeId = $('[data-number="' + activeIndex + '"]').attr('data-id');
        history.pushState({state: activeId}, caption, "#"+activeId);
      },
    })
    .slick({
      fade: true,
      speed: 500,
      prevArrow: '#slick-prev',
      nextArrow: '#slick-next',
    });

    if (hashState > 1) {
      initSlide = $('[data-id="' + hashState + '"]').attr('data-slick-index');
      $('.js-slick-container').slick('slickGoTo',initSlide);
    }

    $('.js-slick-item').on('click', function() {
      $('.js-slick-container').slick('slickNext');
    });

    $(window).on('resize', function() {
      _this.resizeImages();
    });
  },

  replaceCaption: function(currentSlide) {
    var caption = $('[data-slick-index="' + currentSlide + '"]').data('caption');
    if (! caption || caption === undefined || caption === null) {
      $('#slick-caption').html(' ');
    } else {
      $('#slick-caption').html(caption);
    }
  },

  resizeImages: function() {
    $('.js-slick-item img').css( 'max-height' , ( windowHeight - captionHeight - margin ) );
  }
};

jQuery(document).ready(function () {
  'use strict';
  l('Hola Globie');

  // CONDITIONAL INITS

  if ( $('.js-packery-container').length ) {
    $('.js-packery-container').imagesLoaded( function() {
      $('.js-packery-container').packery({
        itemSelector: '.js-packery-item',
        transitionDuration: '0s',
        percentPosition: true
      }).css({
        'opacity': 1
      });
    });
  }

  if ($('body').hasClass('single')) {
    singleLayout();
  }

  if ($('body').hasClass('home')) {
    Spreads.init();
  }

  if ( $('.js-slick-item').length ) {
    Slick.init();
  }

});<|endoftext|>


<commit_before>/* global Vue, gl */
/* eslint-disable no-param-reassign, no-plusplus */

//= require ./param_helper.js.es6

((gl) => {
  const PAGINATION_UI_BUTTON_LIMIT = 4;
  const SPREAD = '...';
  const PREV = 'Prev';
  const NEXT = 'Next';
  const FIRST = '<< First';
  const LAST = 'Last >>';

  gl.VueGlPagination = Vue.extend({
    props: [
      'change',
      'pageInfo',
    ],
    methods: {
      changePage(e) {
        let pageNum = this.pageInfo.page;
        let apiScope = gl.getParameterByName('scope');

        if (!apiScope) apiScope = 'all';

        const text = e.target.innerText;
        const { totalPages, nextPage, previousPage } = this.pageInfo;

        if (text === SPREAD) {
          return;
        } else if (text === LAST) {
          pageNum = totalPages;
        } else if (text === NEXT) {
          pageNum = nextPage;
        } else if (text === PREV) {
          pageNum = previousPage;
        } else if (text === FIRST) {
          pageNum = 1;
        } else {
          pageNum = +text;
        }

        this.change(pageNum, apiScope);
      },
    },
    computed: {
      prev() {
        return this.pageInfo.previousPage;
      },
      next() {
        return this.pageInfo.nextPage;
      },
      getItems() {
        const total = this.pageInfo.totalPages;
        const page = this.pageInfo.page;
        const items = [];

        if (page > 1) items.push({ title: FIRST });

        if (page > 1) {
          items.push({ title: PREV });
        } else {
          items.push({ title: PREV, disabled: true });
        }

        if (page > 6) items.push({ title: SPREAD, separator: true });

        const start = Math.max(page - PAGINATION_UI_BUTTON_LIMIT, 1);
        const end = Math.min(page + PAGINATION_UI_BUTTON_LIMIT, total);

        for (let i = start; i <= end; i++) {
          const isActive = i === page;
          items.push({ title: i, active: isActive });
        }

        if (total - page > PAGINATION_UI_BUTTON_LIMIT) {
          items.push({ title: SPREAD, separator: true });
        }

        if (page === total) {
          items.push({ title: NEXT, disabled: true });
        } else if (total - page >= 1) {
          items.push({ title: NEXT });
        }

        if (total - page >= 1) items.push({ title: LAST });

        return items;
      },
    },
    template: `

              {{item.title}}

    `,
  });
})(window.gl || (window.gl = {}));
<commit_msg>strict props for easier re-use<commit_after>/* global Vue, gl */
/* eslint-disable no-param-reassign, no-plusplus */

//= require ./param_helper.js.es6

((gl) => {
  const PAGINATION_UI_BUTTON_LIMIT = 4;
  const SPREAD = '...';
  const PREV = 'Prev';
  const NEXT = 'Next';
  const FIRST = '<< First';
  const LAST = 'Last >>';

  gl.VueGlPagination = Vue.extend({
    props: {
      change: {
        type: Function,
        required: true,
      },
      pageInfo: {
        type: Object,
        required: true,
      },
    },
    methods: {
      changePage(e) {
        let pageNum = this.pageInfo.page;
        let apiScope = gl.getParameterByName('scope');

        if (!apiScope) apiScope = 'all';

        const text = e.target.innerText;
        const { totalPages, nextPage, previousPage } = this.pageInfo;

        if (text === SPREAD) {
          return;
        } else if (text === LAST) {
          pageNum = totalPages;
        } else if (text === NEXT) {
          pageNum = nextPage;
        } else if (text === PREV) {
          pageNum = previousPage;
        } else if (text === FIRST) {
          pageNum = 1;
        } else {
          pageNum = +text;
        }

        this.change(pageNum, apiScope);
      },
    },
    computed: {
      prev() {
        return this.pageInfo.previousPage;
      },
      next() {
        return this.pageInfo.nextPage;
      },
      getItems() {
        const total = this.pageInfo.totalPages;
        const page = this.pageInfo.page;
        const items = [];

        if (page > 1) items.push({ title: FIRST });

        if (page > 1) {
          items.push({ title: PREV });
        } else {
          items.push({ title: PREV, disabled: true });
        }

        if (page > 6) items.push({ title: SPREAD, separator: true });

        const start = Math.max(page - PAGINATION_UI_BUTTON_LIMIT, 1);
        const end = Math.min(page + PAGINATION_UI_BUTTON_LIMIT, total);

        for (let i = start; i <= end; i++) {
          const isActive = i === page;
          items.push({ title: i, active: isActive });
        }

        if (total - page > PAGINATION_UI_BUTTON_LIMIT) {
          items.push({ title: SPREAD, separator: true });
        }

        if (page === total) {
          items.push({ title: NEXT, disabled: true });
        } else if (total - page >= 1) {
          items.push({ title: NEXT });
        }

        if (total - page >= 1) items.push({ title: LAST });

        return items;
      },
    },
    template: `

              {{item.title}}

    `,
  });
})(window.gl || (window.gl = {}));
<|endoftext|>


<commit_before>/**
 * This is the mock binary which allows to 'start' fake APIs by serving the
 * data in the procKr database.
 */

'use strict';

var argv        = require('minimist')(process.argv.slice(2)),
    _           = require('lodash'),
    _s          = require('underscore.string'),
    express     = require('express'),
    bodyParser  = require('body-parser'),
    router      = express.Router(),
    db          = require('../lib/db'),
    targetId    = argv.targetId;

/**
 * Write log on stdout.
 */
var log = function (message) {
  console.log('[mock-out] ' + message);
};

var logError = function (message) {
  console.log('[mock-error] ' + message);
};

var logResponse = function (message) {
  console.log('[mock-response] ' + message);
};

/**
 * Express app with an in-memory database which will be used to mock data.
 */
var runApp = function (target, db) {
  var mockData = (function () {
    return function (req, res) {
      // search a response for the current query
      db.model('Response').find({
        method: req.method,
        url: req.url,
        parameters: JSON.stringify(req.body),
        targetId: target.id
      }, function (err, responses) {
        if (err) {
          log('An error has occurred when fetching data. ' + err);
          res.status(500).send(err);
        } else {
          var response = _.first(responses);

          if (!response) {
            res.status(404).send('No response has been found.');

            logResponse(_s.sprintf('%s %s %s on localhost:%s',
              '404', req.method, req.url, target.port));
          } else {
            logResponse(_s.sprintf('%s %s %s on localhost:%s',
              response.status, response.method, response.url, target.port));

            // set headers
            _.forEach(response.resHeaders, function (value, key) {
              res.setHeader(key, value);
            });

            res.setHeader('X-procKr-rowuuid', response.uuid);

            res
              .status(response.status || 500)
              .send(response.body || 'empty body');
          }
        }
      });
    };
  })();

  router.route('/*').all(mockData);

  var app = express();

  app
    .set('port', target.port)
    // to support JSON-encoded bodies()
    .use(bodyParser.json())
    // to support URL-encoded bodies
    .use(bodyParser.urlencoded({
      extended: true
    }))
    .use(router)
    .listen(app.get('port'), function () {
      var message = _s.sprintf('%s %d %s %s',
        'Mock listening on port',
        app.get('port'),
        'and mocking the target ID:',
        targetId
      );

      log(message);
    });
};

// search Target in DB
db.whenReady().then(function () {
  if (!targetId) {
    log('The target has not been set.');
  }

  db.model('Target').get(targetId, function (err, target_) {
    if (err) {
      logError('An error has occurred when fetching data. ' +  err);
      process.exit(1);
    }

    // dump the database in memory, used for this process
    db.toMemory(function (err, db_) {
      if (err) {
        logError('err ' + err);
        process.exit(1);
      }

      // run the Express app with the target row and the in-memory database
      runApp(target_, db_);
    });
  });
});
<commit_msg>Use process.std***.write instead of console.log.<commit_after>/**
 * This is the mock binary which allows to 'start' fake APIs by serving the
 * data in the procKr database.
 */

'use strict';

var argv        = require('minimist')(process.argv.slice(2)),
    _           = require('lodash'),
    _s          = require('underscore.string'),
    express     = require('express'),
    bodyParser  = require('body-parser'),
    router      = express.Router(),
    db          = require('../lib/db'),
    targetId    = argv.targetId;

/**
 * Write log on stdout/stderr.
 */
var log = function (message) {
  process.stdout.write('[mock-out] ' + message + '\n');
};

var logError = function (message) {
  process.stderr.write('[mock-error] ' + message + '\n');
};

var logResponse = function (message) {
  process.stdout.write('[mock-response] ' + message + '\n');
};

/**
 * Express app with an in-memory database which will be used to mock data.
 */
var runApp = function (target, db) {
  var mockData = (function () {
    return function (req, res) {
      // search a response for the current query
      db.model('Response').find({
        method: req.method,
        url: req.url,
        parameters: JSON.stringify(req.body),
        targetId: target.id
      }, function (err, responses) {
        if (err) {
          log('An error has occurred when fetching data. ' + err);
          res.status(500).send(err);
        } else {
          var response = _.first(responses);

          if (!response) {
            res.status(404).send('No response has been found.');

            logResponse(_s.sprintf('%s %s %s on localhost:%s',
              '404', req.method, req.url, target.port));
          } else {
            logResponse(_s.sprintf('%s %s %s on localhost:%s',
              response.status, response.method, response.url, target.port));

            // set headers
            _.forEach(response.resHeaders, function (value, key) {
              res.setHeader(key, value);
            });

            res.setHeader('X-procKr-rowuuid', response.uuid);

            res
              .status(response.status || 500)
              .send(response.body || 'empty body');
          }
        }
      });
    };
  })();

  router.route('/*').all(mockData);

  var app = express();

  app
    .set('port', target.port)
    // to support JSON-encoded bodies()
    .use(bodyParser.json())
    // to support URL-encoded bodies
    .use(bodyParser.urlencoded({
      extended: true
    }))
    .use(router)
    .listen(app.get('port'), function () {
      var message = _s.sprintf('%s %d %s %s',
        'Mock listening on port',
        app.get('port'),
        'and mocking the target ID:',
        targetId
      );

      log(message);
    });
};

// search Target in DB
db.whenReady().then(function () {
  if (!targetId) {
    log('The target has not been set.');
  }

  db.model('Target').get(targetId, function (err, target_) {
    if (err) {
      logError('An error has occurred when fetching data. ' +  err);
      process.exit(1);
    }

    // dump the database in memory, used for this process
    db.toMemory(function (err, db_) {
      if (err) {
        logError('err ' + err);
        process.exit(1);
      }

      // run the Express app with the target row and the in-memory database
      runApp(target_, db_);
    });
  });
});
<|endoftext|>


<commit_before>// Typed.js about-me
document.addEventListener('DOMContentLoaded', function () {
    Typed.new('.welcome-typed', {
        stringsElement: document.getElementById('typed-strings'),
        loop: true,
        typeSpeed: 50
    });

    /* particlesJS.load(@dom-id, @path-json, @callback (optional)); */
    /* ---- particles.js config ---- */
    particlesJS("particles-js", {
        "particles": {
            "number": {
                "value": 100,
                "density": {
                    "enable": true,
                    "value_area": 800
                }
            },
            "color": {
                "value": "#2f2f2f"
            },
            "shape": {
                "type": "circle",
                "stroke": {
                    "width": 0,
                    "color": "#000000"
                },
                "polygon": {
                    "nb_sides": 5
                }
            },
            "opacity": {
                "value": 1,
                "random": true,
                "anim": {
                    "enable": true,
                    "speed": 1,
                    "opacity_min": 0,
                    "sync": false
                }
            },
            "size": {
                "value": 3,
                "random": true,
                "anim": {
                    "enable": false,
                    "speed": 4,
                    "size_min": 0.3,
                    "sync": false
                }
            },
            "line_linked": {
                "enable": false,
                "distance": 150,
                "color": "#ffffff",
                "opacity": 0.4,
                "width": 1
            },
            "move": {
                "enable": true,
                "speed": 1,
                "direction": "none",
                "random": true,
                "straight": false,
                "out_mode": "out",
                "bounce": false,
                "attract": {
                    "enable": false,
                    "rotateX": 600,
                    "rotateY": 600
                }
            }
        },
        "interactivity": {
            "detect_on": "canvas",
            "events": {
                "onhover": {
                    "enable": true,
                    "mode": "bubble"
                },
                "onclick": {
                    "enable": true,
                    "mode": "repulse"
                },
                "resize": true
            },
            "modes": {
                "grab": {
                    "distance": 400,
                    "line_linked": {
                        "opacity": 1
                    }
                },
                "bubble": {
                    "distance": 250,
                    "size": 0,
                    "duration": 2,
                    "opacity": 0,
                    "speed": 3
                },
                "repulse": {
                    "distance": 400,
                    "duration": 0.4
                },
                "push": {
                    "particles_nb": 4
                },
                "remove": {
                    "particles_nb": 2
                }
            }
        },
        "retina_detect": true
    });
});
<commit_msg>Added G Analytics script<commit_after>document.addEventListener('DOMContentLoaded', function () {

    /* Google Analytics */
    var links = document.getElementsByTagName("a");
    for (var i = 0; i < links.length; i++) {
        links[i].onclick = function () {
            var url = this.href;
            ga('send', 'event', 'outbound', 'click', url, {
                'transport': 'beacon',
                'hitCallback': function () {}
            });
        }
    }

    /* Typed Setup */
    Typed.new('.welcome-typed', {
        stringsElement: document.getElementById('typed-strings'),
        loop: true,
        typeSpeed: 50
    });

    /* particlesJS.load(@dom-id, @path-json, @callback (optional)); */
    /* ---- particles.js config ---- */
    particlesJS("particles-js", {
        "particles": {
            "number": {
                "value": 100,
                "density": {
                    "enable": true,
                    "value_area": 800
                }
            },
            "color": {
                "value": "#2f2f2f"
            },
            "shape": {
                "type": "circle",
                "stroke": {
                    "width": 0,
                    "color": "#000000"
                },
                "polygon": {
                    "nb_sides": 5
                }
            },
            "opacity": {
                "value": 1,
                "random": true,
                "anim": {
                    "enable": true,
                    "speed": 1,
                    "opacity_min": 0,
                    "sync": false
                }
            },
            "size": {
                "value": 3,
                "random": true,
                "anim": {
                    "enable": false,
                    "speed": 4,
                    "size_min": 0.3,
                    "sync": false
                }
            },
            "line_linked": {
                "enable": false,
                "distance": 150,
                "color": "#ffffff",
                "opacity": 0.4,
                "width": 1
            },
            "move": {
                "enable": true,
                "speed": 1,
                "direction": "none",
                "random": true,
                "straight": false,
                "out_mode": "out",
                "bounce": false,
                "attract": {
                    "enable": false,
                    "rotateX": 600,
                    "rotateY": 600
                }
            }
        },
        "interactivity": {
            "detect_on": "canvas",
            "events": {
                "onhover": {
                    "enable": true,
                    "mode": "bubble"
                },
                "onclick": {
                    "enable": true,
                    "mode": "repulse"
                },
                "resize": true
            },
            "modes": {
                "grab": {
                    "distance": 400,
                    "line_linked": {
                        "opacity": 1
                    }
                },
                "bubble": {
                    "distance": 250,
                    "size": 0,
                    "duration": 2,
                    "opacity": 0,
                    "speed": 3
                },
                "repulse": {
                    "distance": 400,
                    "duration": 0.4
                },
                "push": {
                    "particles_nb": 4
                },
                "remove": {
                    "particles_nb": 2
                }
            }
        },
        "retina_detect": true
    });
});
<|endoftext|>


<commit_before>'use strict';

var L = require('leaflet');
var geo = require('./geolocation.js');
var itinerary = require('./itCalculation.js');
var getToilets = require('./getToilets.js');
var addInfos = require('./addInfos.js');
var U = require('./utilities.js');

// set map options
var BORDEAUX_COORDS = [44.84, -0.57];
var map = L.map('map', {
	center: BORDEAUX_COORDS,
	zoom: 12,
	minZoom: 12 // minZoom is set b/c there is no sense to zoom out of Bordeaux
});

map.setMaxBounds(map.getBounds()); // MaxBounds are set because there is no sense to pan out of Bordeaux

L.tileLayer('http://api.tiles.mapbox.com/v3/ourson.k0i572pc/{z}/{x}/{y}.png', {
    attribution: '© OpenStreetMap contributors'
}).addTo(map);

var typologieToCSSClass = {
	"Urinoir": "urinoir",
	"Sanitaire automatique": "sanitaire",
	"Sanitaire automatique avec urinoir": "sanitaire",
	"Chalet de nécessité": "sanitaire",
};

var iconMap = {
	"urinoir" : "male",
	"sanitaire": "female",
	"handicap": "wheelchair",
	"chalet": "umbrella"
};

var drawables = {
	singleGroup: L.layerGroup(),
	closestGroup: L.layerGroup(),
	urinoirGroup: L.layerGroup(),
	sanitaireGroup: L.layerGroup(),
	handiGroup: L.layerGroup(),
	toiletGroup: L.layerGroup()
};

var filteredToiletsP = [];

// Get user position
function updatePosition(position){
 	var latitude  = position.coords.latitude;
	var longitude = position.coords.longitude;

	var icon = L.divIcon({
	        className: "user",
	        iconSize: new L.Point(72, 72),
		    iconAnchor: new L.Point(36, 36),
	        html: ''
	    });

	var marker;

	if(marker)
		map.removeLayer(marker);

	marker = L.marker([latitude, longitude], {icon: icon});
	drawables.user = marker;

	// Add click event on user position
	marker.addEventListener('click', function(){
	    drawables.singleGroup.clearLayers();
	    drawables.closestGroup.addTo(map);
	});

	map.addLayer(marker);

	return {
		lat: latitude,
		lng: longitude
	};
}

function setMarker(toilet){
	// Add icons from FontAwesome
	var myHtml = '';
	var groups = [drawables.toiletGroup];

	if (toilet.class == 'sanitaire') {
		myHtml += '\n';
		groups.push(drawables.sanitaireGroup);
	}
	else {
		myHtml += '\n';
		groups.push(drawables.urinoirGroup);
	}

	if (toilet.handicap == true){
		myHtml += '

\n';
		groups.push(drawables.handiGroup);
	}

	var icon = L.divIcon({
        className: "icon",
        iconSize: new L.Point(46, 46),
	    iconAnchor: new L.Point(23, 23),
        html: myHtml
    });

    toilet.marker = L.marker([toilet.lat, toilet.lng], {icon: icon});
    return groups;
}

// Get toilets position
var toilettesP = getToilets('data/toilettes.json')
    .then(function(data){
        console.log('raw', data);

        return data["d"].map(function(t){
        	var test = typologieToCSSClass[t["typologie"]];
        	var option = t["options"] ? true: false;
        	if (!test)
        		console.error(t);
        	else {
        		return {
	                lng: parseFloat(t["x_long"]),
	                lat: parseFloat(t["y_lat"]),
	                nom: t["nom"],
	                // typologie: t["typologie"],
	                class: typologieToCSSClass[t["typologie"]],
	                handicap: option,
	                marker: undefined
            	};
        	}
        });
    });

// render points on map regardless of geolocation
toilettesP
	.then(function(toilettes){
	    toilettes.forEach(function(element){
			var groups = setMarker(element);

			groups.forEach(function(group){
				element.marker.addTo(group);
			});

		});

	    drawables.toiletGroup.addTo(map);
	});

// var closestUrinoirs = undefined;
// var closestSanitaires = undefined;
// var closestHandis = undefined;

////////////////////// FONCTION GENERALE
var position = geo(updatePosition);

// When user and toilet positions are available:
Promise.all([toilettesP, position])
	.then(function(values){
		// set toiletFiltered(filter)
		var toilettes = values[0],
			position = values[1];

		console.log('verif ', toilettes);

		findClosest(toilettes, position);
	})
	.catch(function(err){console.error(err)})

//////////////////////////

function findClosest(list, position){

	list.forEach(function(toilette){
        // Calculate rough distance b/w user and toilet
        toilette.d = Math.hypot(toilette.lat - position.lat, toilette.lng - position.lng);

        // Add click event on toilet
	    toilette.marker.addEventListener('click', function(){
	    	map.removeLayer(drawables.closestGroup);
	    	drawables.singleGroup.clearLayers();

			itinerary(position, toilette)
				.then(function(result){
					var infos = addInfos(result, 1);

					infos.polyline.addTo(drawables.singleGroup);
					infos.marker.addTo(drawables.singleGroup);

					drawables.singleGroup.addTo(map);

				}).catch(function(err){console.error(err)})
		});
    });

	list.sort(function (a, b) {
		return (a.d - b.d);
	});

    var closestToilettes = list.slice(0, 3);

	var closestLats = closestToilettes.map(function(t){return t.lat;}),
		closestLngs = closestToilettes.map(function(t){return t.lng;});

	var itinerariesPs = closestToilettes.map(function(t){ return itinerary(position, t); });

	// Fits the map so all shortest routes are displayed
	var north = U.getMaxOfArray(closestLats),
		south = U.getMinOfArray(closestLats),
		east = U.getMaxOfArray(closestLngs),
		west = U.getMinOfArray(closestLngs);

	var southWest = L.latLng(south, west),
    	northEast = L.latLng(north, east);

    var bounds = L.latLngBounds(southWest, northEast);
    map.fitBounds(bounds);

    // When all itineraries are computed
    Promise.all(itinerariesPs).then(function(toilets){

		toilets.sort(function (a, b) {
			return (a.routes[0].legs[0].distance.value - b.routes[0].legs[0].distance.value);
		});

		// Calculate itineraries for 3 closest toilets
		toilets.forEach(function(toilet, i){

			console.log('toilet ', toilet);

			var infos = addInfos(toilet, i);
			infos.polyline.addTo(drawables.closestGroup);
			infos.marker.addTo(drawables.closestGroup);

        });

		// Draw infos on closest toilets
		drawables.closestGroup.addTo(map);

	}).catch(function(err){console.error(err)})
}

<commit_msg>some refactoring<commit_after>'use strict';

var L = require('leaflet');
var geo = require('./geolocation.js');
var itinerary = require('./itCalculation.js');
var getToilets = require('./getToilets.js');
var addInfos = require('./addInfos.js');
var U = require('./utilities.js');

// set map options
var BORDEAUX_COORDS = [44.84, -0.57];
var map = L.map('map', {
	center: BORDEAUX_COORDS,
	zoom: 12,
	minZoom: 12 // minZoom is set b/c there is no sense to zoom out of Bordeaux
});

map.setMaxBounds(map.getBounds()); // MaxBounds are set because there is no sense to pan out of Bordeaux

L.tileLayer('http://api.tiles.mapbox.com/v3/ourson.k0i572pc/{z}/{x}/{y}.png', {
    attribution: '© OpenStreetMap contributors'
}).addTo(map);

var typologieToCSSClass = {
	"Urinoir": "urinoir",
	"Sanitaire automatique": "sanitaire",
	"Sanitaire automatique avec urinoir": "sanitaire",
	"Chalet de nécessité": "sanitaire",
};

var iconMap = {
	"urinoir" : "male",
	"sanitaire": "female",
	"handicap": "wheelchair",
	"chalet": "umbrella"
};

var drawables = {
	user: undefined,
	singleGroup: L.layerGroup(),
	closestGroup: L.layerGroup(),
	urinoirGroup: L.layerGroup(),
	sanitaireGroup: L.layerGroup(),
	handiGroup: L.layerGroup(),
	toiletGroup: L.layerGroup()
};

var filteredToiletsP = [];

// Get user position
function updatePosition(position){
 	var latitude  = position.coords.latitude;
	var longitude = position.coords.longitude;

	var icon = L.divIcon({
	        className: "user",
	        iconSize: new L.Point(72, 72),
		    iconAnchor: new L.Point(36, 36),
	        html: ''
	    });

	var marker;

	if(marker)
		map.removeLayer(marker);

	marker = L.marker([latitude, longitude], {icon: icon});
	drawables.user = marker;

	// Add click event on user position
	marker.addEventListener('click', function(){
	    drawables.singleGroup.clearLayers();
	    updateMap(drawables.closestGroup);
	});

	updateMap(marker);

	return {
		lat: latitude,
		lng: longitude
	};
}

function updateMap(items, boundaries){
	if (boundaries){
		map.fitBounds(boundaries);
	}

	items.addTo(map);
}

function setMarker(toilet){
	// Add icons from FontAwesome
	var myHtml = '';
	var groups = [drawables.toiletGroup];

	if (toilet.class == 'sanitaire') {
		myHtml += '\n';
		groups.push(drawables.sanitaireGroup);
	}
	else {
		myHtml += '\n';
		groups.push(drawables.urinoirGroup);
	}

	if (toilet.handicap == true){
		myHtml += '

\n';
		groups.push(drawables.handiGroup);
	}

	var icon = L.divIcon({
        className: "icon",
        iconSize: new L.Point(46, 46),
	    iconAnchor: new L.Point(23, 23),
        html: myHtml
    });

    toilet.marker = L.marker([toilet.lat, toilet.lng], {icon: icon});
    return groups;
}

function addClicBehaviour(list, position){
	list.forEach(function(toilette){

        // Add click event on toilet
	    toilette.marker.addEventListener('click', function(){
	    	map.removeLayer(drawables.closestGroup);
	    	drawables.singleGroup.clearLayers();

			itinerary(position, toilette)
				.then(function(result){
					var infos = addInfos(result, 1);

					infos.polyline.addTo(drawables.singleGroup);
					infos.marker.addTo(drawables.singleGroup);

					updateMap(drawables.singleGroup);
					// drawables.singleGroup.addTo(map);

				}).catch(function(err){console.error(err)})
		});
    });
}

function findClosest(list, position){

	list.forEach(function(toilette){
        // Calculate rough distance b/w user and toilet
        toilette.d = Math.hypot(toilette.lat - position.lat, toilette.lng - position.lng);
    });

	list.sort(function (a, b) {
		return (a.d - b.d);
	});

    var closestToilettes = list.slice(0, 3);

	var closestLats = closestToilettes.map(function(t){return t.lat;}),
		closestLngs = closestToilettes.map(function(t){return t.lng;});

	var itinerariesPs = closestToilettes.map(function(t){ return itinerary(position, t); });

	// Fits the map so all shortest routes are displayed
	var north = U.getMaxOfArray(closestLats),
		south = U.getMinOfArray(closestLats),
		east = U.getMaxOfArray(closestLngs),
		west = U.getMinOfArray(closestLngs);

	var southWest = L.latLng(south, west),
    	northEast = L.latLng(north, east);

    var bounds = L.latLngBounds(southWest, northEast);

    // When all itineraries are computed
    Promise.all(itinerariesPs).then(function(toilets){

		toilets.sort(function (a, b) {
			return (a.routes[0].legs[0].distance.value - b.routes[0].legs[0].distance.value);
		});

		// Calculate itineraries for 3 closest toilets
		toilets.forEach(function(toilet, i){

			console.log('toilet ', toilet);

			var infos = addInfos(toilet, i);
			infos.polyline.addTo(drawables.closestGroup);
			infos.marker.addTo(drawables.closestGroup);

        });

		// Draw infos on closest toilets
		updateMap(drawables.closestGroup, bounds);

	}).catch(function(err){console.error(err)})
}

/// MAIN CODE

// Get toilets position
var toilettesP = getToilets('data/toilettes.json')
    .then(function(data){
        console.log('raw', data);

        return data["d"].map(function(t){
        	var test = typologieToCSSClass[t["typologie"]];
        	var option = t["options"] ? true: false;
        	if (!test)
        		console.error(t);
        	else {
        		return {
	                lng: parseFloat(t["x_long"]),
	                lat: parseFloat(t["y_lat"]),
	                nom: t["nom"],
	                // typologie: t["typologie"],
	                class: typologieToCSSClass[t["typologie"]],
	                handicap: option,
	                marker: undefined
            	};
        	}
        });
    });

// render points on map regardless of geolocation
toilettesP
	.then(function(toilettes){
	    toilettes.forEach(function(element){
			var groups = setMarker(element);

			groups.forEach(function(group){
				element.marker.addTo(group);
			});

		});

	    updateMap(drawables.toiletGroup);
	});

// var closestUrinoirs = undefined;
// var closestSanitaires = undefined;
// var closestHandis = undefined;

////////////////////// FONCTION GENERALE
var position = geo(updatePosition);

// When user and toilet positions are available:
Promise.all([toilettesP, position])
	.then(function(values){
		// set toiletFiltered(filter)
		var toilettes = values[0],
			position = values[1];

		console.log('verif ', toilettes);

		addClicBehaviour(toilettes, position);

		findClosest(toilettes, position);
	})
	.catch(function(err){console.error(err)})

//////////////////////////

<|endoftext|>


<commit_before>define([
	'jquery',
	'underscore',
	'backbone',
	'd3',

	'appconfig',
	'backendServiceM',
	'DistributionChartV',
	'LocatingChartV',
	'VisualizationsPaneV',
	'WaterfallChartV',
	'hbs!templates/chartsTabs'
	],
	function ($, _, Backbone, d3, appconfig, backendServiceM, DistributionChartV, LocatingChartV, VisualizationsPaneV,
		WaterfallChartV, chartsTabsT) {
		'use strict';

		var enableLocatingChart = !! appconfig.enabledModules.locatingChart;
		var viewClassByChartName = {
			distribution: DistributionChartV,
			visualizations: VisualizationsPaneV,
			waterfall: WaterfallChartV
		};
		if (enableLocatingChart) {
			viewClassByChartName.locating = LocatingChartV;
		}

		var AppV = Backbone.View.extend({
			currentChildView: null,
			el: '#chart-wrapper',
			events: {
				'click a[data-toggle="tab"]': 'onTabClicked',
				'shown.bs.tab a[data-toggle="tab"]': 'onTabShown'
			},
			initialize: function () {
				this.$el.html(chartsTabsT({enableLocatingChart: enableLocatingChart}));
				this.$overlay = this.$el.find('.overlay');
				this.listenTo(backendServiceM, 'change:simulationInProgress', this.updateOverlay);
				this.updateOverlay();
			},
			onTabClicked: function(evt) {
				evt.preventDefault();
			},
			onTabShown: function(evt) {
				window.location.hash = $(evt.target).attr('href');
			},
			render: function (chartName) {
				if (this.$el.find('.nav .active').length === 0) {
					this.$el.find('.nav a[href="#' + chartName + '"]').tab('show');
				}
				this.$el.find('.tab-pane.active').empty();
				this.currentChildView = new viewClassByChartName[chartName]({el: this.$el.find('#' + chartName)});
				return this;
			},
			updateOverlay: function() {
				var simulationInProgress = backendServiceM.get('simulationInProgress');
				var $svg = this.$el.find('svg');
				if (simulationInProgress) {
					$svg.css('opacity', 0.1);
					this.$overlay.show();
				} else {
					$svg.css('opacity', 1);
					this.$overlay.hide();
				}
			}
		});

		var appV = new AppV();
		return appV;

	}
);
<commit_msg>Use show event for tabs.<commit_after>define([
	'jquery',
	'underscore',
	'backbone',
	'd3',

	'appconfig',
	'backendServiceM',
	'DistributionChartV',
	'LocatingChartV',
	'VisualizationsPaneV',
	'WaterfallChartV',
	'hbs!templates/chartsTabs'
	],
	function ($, _, Backbone, d3, appconfig, backendServiceM, DistributionChartV, LocatingChartV, VisualizationsPaneV,
		WaterfallChartV, chartsTabsT) {
		'use strict';

		var enableLocatingChart = !! appconfig.enabledModules.locatingChart;
		var viewClassByChartName = {
			distribution: DistributionChartV,
			visualizations: VisualizationsPaneV,
			waterfall: WaterfallChartV
		};
		if (enableLocatingChart) {
			viewClassByChartName.locating = LocatingChartV;
		}

		var AppV = Backbone.View.extend({
			currentChildView: null,
			el: '#chart-wrapper',
			events: {
				'show.bs.tab a[data-toggle="tab"]': 'onTabShow'
			},
			initialize: function () {
				this.$el.html(chartsTabsT({enableLocatingChart: enableLocatingChart}));
				this.$overlay = this.$el.find('.overlay');
				this.listenTo(backendServiceM, 'change:simulationInProgress', this.updateOverlay);
				this.updateOverlay();
			},
			onTabShow: function(evt) {
				window.location.hash = $(evt.target).attr('href');
			},
			render: function (chartName) {
				if (this.$el.find('.nav .active').length === 0) {
					this.$el.find('.nav a[href="#' + chartName + '"]').tab('show');
				}
				this.$el.find('.tab-pane.active').empty();
				this.currentChildView = new viewClassByChartName[chartName]({el: this.$el.find('#' + chartName)});
				return this;
			},
			updateOverlay: function() {
				var simulationInProgress = backendServiceM.get('simulationInProgress');
				var $svg = this.$el.find('svg');
				if (simulationInProgress) {
					$svg.css('opacity', 0.1);
					this.$overlay.show();
				} else {
					$svg.css('opacity', 1);
					this.$overlay.hide();
				}
			}
		});

		var appV = new AppV();
		return appV;

	}
);
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import { Checkbox } from '@blueprintjs/core';
import { getAllSounds } from '../../utils/sounds.util';
import { Phases } from '../../enums';

const TickSoundOption = ({
  label,
  selectedSound,
  sounds,
  onChange,
}) => (

    {label}:

      <select value={selectedSound} onChange={onChange}>
        {sounds.map((sound, i) => {
          const { title } = sound;
          return <option key={`${label}-${title}`} value={i}>{title}</option>;
        })}
      </select>

);

TickSoundOption.propTypes = {
  label: PropTypes.string.isRequired,
  selectedSound: PropTypes.number.isRequired,
  sounds: PropTypes.arrayOf(PropTypes.any),
  onChange: PropTypes.func.isRequired,
};

const SoundsPanel = ({
  audioPhaseDisabled,
  audioTickDisabled,
  sounds,
  soundFocusPhase,
  soundShortBreakPhase,
  soundLongBreakPhase,
  soundPhaseEnded,
  onSettingsChange,
  toggleAudioPhase,
  toggleAudioTick,
  setAudio
}) => (

      <TickSoundOption
        label="Focus Tick"
        selectedSound={soundFocusPhase}
        sounds={sounds}
        onChange={e => {
          onSettingsChange(
            'sounds.focusPhase',
            +e.target.value,
            setAudio,
            Phases.FOCUS
          );
        }}
      />
      <TickSoundOption
        label="Short Break Tick"
        selectedSound={soundShortBreakPhase}
        sounds={sounds}
        onChange={e => {
          onSettingsChange(
            'sounds.shortBreakPhase',
            +e.target.value,
            setAudio,
            Phases.SHORT_BREAK
          );
        }}
      />
      <TickSoundOption
        label="Long Break Tick"
        selectedSound={soundLongBreakPhase}
        sounds={sounds}
        onChange={e => {
          onSettingsChange(
            'sounds.longBreakPhase',
            +e.target.value,
            setAudio,
            Phases.LONG_BREAK
          );
        }}
      />
      <TickSoundOption
        label="Phase Ended Tick"
        selectedSound={soundPhaseEnded}
        sounds={sounds}
        onChange={e => {
          onSettingsChange(
            'sounds.phaseEnded',
            +e.target.value,
            setAudio
          );
        }}
      />

      Volume Control
      <Checkbox
        label="Play ticking sound"
        checked={!audioTickDisabled}
        onChange={e =>
          onSettingsChange(
            'sounds.audioTickDisabled',
            !e.target.checked,
            toggleAudioTick
          )
        }
      />
      <Checkbox
        label="Play sound when phase ends"
        checked={!audioPhaseDisabled}
        onChange={e =>
          onSettingsChange(
            'sounds.audioPhaseDisabled',
            !e.target.checked,
            toggleAudioPhase
          )
        }
      />

);

SoundsPanel.defaultProps = {
  sounds: Array.from(getAllSounds())
};

SoundsPanel.propTypes = {
  audioPhaseDisabled: PropTypes.bool.isRequired,
  audioTickDisabled: PropTypes.bool.isRequired,
  soundFocusPhase: PropTypes.number.isRequired,
  soundShortBreakPhase: PropTypes.number.isRequired,
  soundLongBreakPhase: PropTypes.number.isRequired,
  soundPhaseEnded: PropTypes.number.isRequired,
  sounds: PropTypes.arrayOf(PropTypes.any),
  onSettingsChange: PropTypes.func.isRequired,
  toggleAudioPhase: PropTypes.func.isRequired,
  toggleAudioTick: PropTypes.func.isRequired,
  setAudio: PropTypes.func.isRequired
};

export default SoundsPanel;
<commit_msg>style: Show tabs for sound type selection<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import { Checkbox, Tab2, Tabs2 } from '@blueprintjs/core';
import { getAllSounds } from '../../utils/sounds.util';
import { Phases } from '../../enums';

const SoundOption = ({
  label,
  selectedSound,
  sounds,
  onChange,
}) => (

    {label}:

      <select value={selectedSound} onChange={onChange}>
        {sounds.map((sound, i) => {
          const { title } = sound;
          return <option key={`${label}-${title}`} value={i}>{title}</option>;
        })}
      </select>

);

SoundOption.propTypes = {
  label: PropTypes.string.isRequired,
  selectedSound: PropTypes.number.isRequired,
  sounds: PropTypes.arrayOf(PropTypes.any),
  onChange: PropTypes.func.isRequired,
};

const TickPanel = ({
  sounds,
  soundFocusPhase,
  soundShortBreakPhase,
  soundLongBreakPhase,
  soundPhaseEnded,
  onSettingsChange,
  setAudio,
}) => (

    <SoundOption
      label="Focus Tick"
      selectedSound={soundFocusPhase}
      sounds={sounds}
      onChange={e => {
        onSettingsChange(
          'sounds.focusPhase',
          +e.target.value,
          setAudio,
          Phases.FOCUS
        );
      }}
    />
    <SoundOption
      label="Short Break Tick"
      selectedSound={soundShortBreakPhase}
      sounds={sounds}
      onChange={e => {
        onSettingsChange(
          'sounds.shortBreakPhase',
          +e.target.value,
          setAudio,
          Phases.SHORT_BREAK
        );
      }}
    />
    <SoundOption
      label="Long Break Tick"
      selectedSound={soundLongBreakPhase}
      sounds={sounds}
      onChange={e => {
        onSettingsChange(
          'sounds.longBreakPhase',
          +e.target.value,
          setAudio,
          Phases.LONG_BREAK
        );
      }}
    />
    <SoundOption
      label="Phase Ended Tick"
      selectedSound={soundPhaseEnded}
      sounds={sounds}
      onChange={e => {
        onSettingsChange(
          'sounds.phaseEnded',
          +e.target.value,
          setAudio
        );
      }}
    />

);

TickPanel.propTypes = {
  soundFocusPhase: PropTypes.number.isRequired,
  soundShortBreakPhase: PropTypes.number.isRequired,
  soundLongBreakPhase: PropTypes.number.isRequired,
  soundPhaseEnded: PropTypes.number.isRequired,
  sounds: PropTypes.arrayOf(PropTypes.any),
  onSettingsChange: PropTypes.func.isRequired,
  setAudio: PropTypes.func.isRequired,
};

const VolumnControl = ({
  audioPhaseDisabled,
  audioTickDisabled,
  onSettingsChange,
  toggleAudioPhase,
  toggleAudioTick,
  className
}) => (

    Volume Control
    <Checkbox
      label="Play ticking sound"
      checked={!audioTickDisabled}
      onChange={e =>
        onSettingsChange(
          'sounds.audioTickDisabled',
          !e.target.checked,
          toggleAudioTick
        )
      }
    />
    <Checkbox
      label="Play sound when phase ends"
      checked={!audioPhaseDisabled}
      onChange={e =>
        onSettingsChange(
          'sounds.audioPhaseDisabled',
          !e.target.checked,
          toggleAudioPhase
        )
      }
    />

);

VolumnControl.propTypes = {
  audioPhaseDisabled: PropTypes.bool.isRequired,
  audioTickDisabled: PropTypes.bool.isRequired,
  onSettingsChange: PropTypes.func.isRequired,
  toggleAudioPhase: PropTypes.func.isRequired,
  toggleAudioTick: PropTypes.func.isRequired,
  className: PropTypes.string
};

const SoundsPanel = (props) => (

    <Tabs2 id="SoundType" animate={false}>
      <Tab2
        id="soundTypeTick"
        title="Tick"
        panel={<TickPanel {...props} />}
      />
    </Tabs2>
    <VolumnControl className="mt-4" {...props} />

);

SoundsPanel.defaultProps = {
  sounds: Array.from(getAllSounds())
};

export default SoundsPanel;
<|endoftext|>