<commit_before>// NEW-QUESTION-TYPE: add to questionFunctions dictionary below

var binHexOctDec = require("./binHexOctDec");
var changeOfBase = require("./changeOfBase");
var cppAppropriateVariables = require("./cppAppropriateVariables");

module.exports.questionTypes = {
    "binHexOctDec":             {"f": binHexOctDec.binHexOctDecQuestion,        title: "Bin Hex Oct Dec"},
    "changeOfBase":             {"f": changeOfBase.changeOfBaseQuestion,        title: "Change of Base"},
    "cppAppropriateVariables":  {"f": cppAppropriateVariablesQuestion,          title: "C++ Variable Types"},
};

// To QA a new question type:
//   * Write instructor documentation for question type
//   * Move the js file from questionsDev to questions
//   * Add unit tests for all the code in file
//   * Refactor code to be understandable
//   * Add integration and regression tests for all code in the file
//   * Get code review of question type
//   * Do pull request for question type

/*
    "cppAppropriateVariables":  {"f": cppAppropriateVariablesQuestion, title: "C++ Variable Types"},
    "cppArgcArgv":              {"f": CppArgcArgvQuestion,         title: "C++ Command Line Arguments"},
    "cppBooleanEval":           {"f": CppBooleanEvalQuestion,      title: "C++ Boolean Evaluation"},
    "cppFunctionOverloading":   {"f": CppFunctionOverloadingQuestion, title: "C++ Function Overloading"},
    "cppFunctionParameters":    {"f": cppFunctionParametersQuestion, title: "C++ Function Parameters"},
    "cppPointerAssignment":     {"f": cppPointerAssignmentQuestion, title: "C++ Pointer Assignment"}
*/

// "changeOfBase":          {"f": changeOfBaseQuestion,       title: "Change of Base"},
// "orderOfOperations":     {"f": orderOfOperationsQuestion,   title: "Order of Operations"},
// "operandsAndOperators":  {"f": operandsAndOperatorsQuestion,title: "Operands and Operators"},
// "pythonProgramOutput":   {"f": pythonProgramOutputQuestion, title: "Python Program Output"},
// "pythonStringSlice":     {"f": pythonStringSliceQuestion,   title: "Python String Slice"},
// "symbolicLogic":         {"f": symbolicLogicQuestion,       title: "Symbolic Logic"},
// "CvariableType":         {"f": CvariableTypeQuestion,       title: "C Variable Type"},
// "cStrings":              {"f": cStringsQuestion,            title: "C Strings"},
// "pyStrings":             {"f": pyStringsQuestion,           title: "Python Strings"},

module.exports.addOptionForEachQuestionType = function(e) {
    $.each(module.exports.questionTypes, function(key, val) {
	    e.append($('<option></option>').val(key).html(val.title));
	});
};
<commit_msg>Fixed require in questions index.js.<commit_after>// NEW-QUESTION-TYPE: add to questionFunctions dictionary below

var binHexOctDec = require("./binHexOctDec");
var changeOfBase = require("./changeOfBase");
var cppAppropriateVariables = require("./cppAppropriateVariables");

module.exports.questionTypes = {
    "binHexOctDec":             {"f": binHexOctDec.binHexOctDecQuestion,        title: "Bin Hex Oct Dec"},
    "changeOfBase":             {"f": changeOfBase.changeOfBaseQuestion,        title: "Change of Base"},
    "cppAppropriateVariables":  {"f": cppAppropriateVariables.cppAppropriateVariablesQuestion,          title: "C++ Variable Types"},
};

// To QA a new question type:
//   * Write instructor documentation for question type
//   * Move the js file from questionsDev to questions
//   * Add unit tests for all the code in file
//   * Refactor code to be understandable
//   * Add integration and regression tests for all code in the file
//   * Get code review of question type
//   * Do pull request for question type

/*
    "cppAppropriateVariables":  {"f": cppAppropriateVariablesQuestion, title: "C++ Variable Types"},
    "cppArgcArgv":              {"f": CppArgcArgvQuestion,         title: "C++ Command Line Arguments"},
    "cppBooleanEval":           {"f": CppBooleanEvalQuestion,      title: "C++ Boolean Evaluation"},
    "cppFunctionOverloading":   {"f": CppFunctionOverloadingQuestion, title: "C++ Function Overloading"},
    "cppFunctionParameters":    {"f": cppFunctionParametersQuestion, title: "C++ Function Parameters"},
    "cppPointerAssignment":     {"f": cppPointerAssignmentQuestion, title: "C++ Pointer Assignment"}
*/

// "changeOfBase":          {"f": changeOfBaseQuestion,       title: "Change of Base"},
// "orderOfOperations":     {"f": orderOfOperationsQuestion,   title: "Order of Operations"},
// "operandsAndOperators":  {"f": operandsAndOperatorsQuestion,title: "Operands and Operators"},
// "pythonProgramOutput":   {"f": pythonProgramOutputQuestion, title: "Python Program Output"},
// "pythonStringSlice":     {"f": pythonStringSliceQuestion,   title: "Python String Slice"},
// "symbolicLogic":         {"f": symbolicLogicQuestion,       title: "Symbolic Logic"},
// "CvariableType":         {"f": CvariableTypeQuestion,       title: "C Variable Type"},
// "cStrings":              {"f": cStringsQuestion,            title: "C Strings"},
// "pyStrings":             {"f": pyStringsQuestion,           title: "Python Strings"},

module.exports.addOptionForEachQuestionType = function(e) {
    $.each(module.exports.questionTypes, function(key, val) {
	    e.append($('<option></option>').val(key).html(val.title));
	});
};
<|endoftext|>


<commit_before>define([
    'underscore',
    'jquery',
    'oroui/js/app/components/base/component',
    'routing',
    'oroui/js/mediator'
], function(_, $, BaseComponent, routing, mediator) {
    'use strict';

    return BaseComponent.extend({

        MODE_CREATE: 'create',
        MODE_VIEW: 'view',
        MODE_EDIT: 'edit',

        requiredOptions: [
            'modeSelector',
            'newEntitySelector',
            'existingEntitySelector',
            'existingEntityInputSelector',
            '_sourceElement'
        ],

        $el: null,
        $mode: null,
        $newEntity: null,
        $existingEntity: null,
        $existingEntityInput: null,
        $dialog: null,
        editable: false,

        /**
         * @param options
         */
        initialize: function(options) {
            if (this.editable = options.editable === true) {
                this.requiredOptions.push('editRoute');
            }
            var missingProperties = _.filter(this.requiredOptions, _.negate(_.bind(options.hasOwnProperty, options)));
            if (missingProperties.length) {
                throw new Error(
                    'Following properties are required but weren\'t passed: ' +
                    missingProperties.join(', ') +
                    '.'
                );
            }

            this.$el = options._sourceElement;
            this.$mode = this.$el.find(options.modeSelector);
            this.$newEntity = this.$el.find(options.newEntitySelector);
            this.newEntityFormName = options.newEntityFormName;
            this.$existingEntity = this.$el.find(options.existingEntitySelector);
            this.$existingEntityInput = $(options.existingEntityInputSelector);
            this.$dialog = this.$el.closest('.ui-dialog');
            this.$dialog.css('top', 0);
            this.editRoute = options.editRoute;

            this.$existingEntity.on('change', _.bind(this._onEntityChange, this));
            this.$existingEntity.on('change', _.bind(this._retrieveEntityData, this));
            this.$existingEntity.on('change', _.bind(this._cleanEntityData, this));
            this.$mode.on('change', _.bind(this._updateNewEntityVisibility, this));

            this._onEntityChange({val: this.$existingEntityInput.val()});
        },

        /**
         * Processes change in selected entity field.
         *
         * @param e
         * @private
         */
        _onEntityChange: function(e) {
            var mode = this.MODE_CREATE;
            if (e.val) {
                mode = this.editable ? this.MODE_EDIT : this.MODE_VIEW;
            }
            this._setMode(mode);
        },

        /**
         * Updates entity form visibility according to mode.
         *
         * @private
         */
        _updateNewEntityVisibility: function() {
            if (this._isInMode(this.MODE_CREATE) || this._isInMode(this.MODE_EDIT)) {
                this.$newEntity.show();
            } else {
                this.$newEntity.hide();
            }
        },

        /**
         * @param mode
         * @private
         */
        _setMode: function(mode) {
            if (this.$mode.val() === mode) {
                return;
            }

            this.$mode.val(mode).change();
        },

        /**
         * Checks if is in provided mode.
         *
         * @param mode
         * @returns Boolean
         * @private
         */
        _isInMode: function(mode) {
            return this.$mode.val() === mode;
        },

        /**
         * Retrieves entity data using set route. Provided id is used as query parameter for request.
         *
         * @param e
         * @private
         */
        _retrieveEntityData: function(e) {
            if (!this._isInMode(this.MODE_EDIT) || !e.val) {
                return;
            }

            var route = routing.generate(this.editRoute, {id: e.val});

            this._setLoading(true);
            $.get(route)
                .done(_.bind(this._setNewEntityForm, this))
                .fail(_.bind(this._handleDataRequestError, this))
                .always(_.bind(function() {
                    this._setLoading(false);
                }, this));
        },

        /**
         * Cleans entity data when selecting default choice.
         *
         * @param e
         * @private
         */
        _cleanEntityData: function(e) {
            if (!this._isInMode(this.MODE_CREATE) || e.val || !this.editable) {
                return;
            }

            this.$newEntity.find(':input').not(':checkbox').not(':radio').val(null);
        },

        /**
         * @param data HTML of edit form.
         * @private
         */
        _setNewEntityForm: function(data) {
            var $data = $(data);
            /*
             * For each input element in added form, create new name to match naming scheme.
             */
            $data.find(':input').each(_.bind(function(index, element) {
                var $element = $(element);
                var inputName = $element.attr('name');
                inputName = inputName.substr(inputName.indexOf('['));

                var $modifiedField = this.$newEntity.find('[name$="' + inputName + '"]');

                if ($element.is(':checkbox') || $element.is(':radio')) {
                    $modifiedField = this.$newEntity.find(
                        '[name$="' + inputName + '"][value="' + $element.val() + '"]'
                    );
                    $modifiedField.prop('checked', $element.is(':checked')).change();
                } else {
                    $modifiedField.val($element.val()).change();
                }
            }, this));
        },

        /**
         * @param jqXHR
         * @param textStatus
         * @param error
         * @private
         */
        _handleDataRequestError: function(jqXHR, textStatus, error) {
            console.error(textStatus); // Display error
        },

        /**
         * @param enabled
         * @private
         */
        _setLoading: function(enabled) {
            if (enabled) {
                mediator.execute('showLoading');
            } else {
                mediator.execute('hideLoading');
            }
        }
    });
});
<commit_msg>CRM-3998: Can't edit Subject and Content fields for Autoresponse on edit page  - JSCS fix<commit_after>define([
    'underscore',
    'jquery',
    'oroui/js/app/components/base/component',
    'routing',
    'oroui/js/mediator'
], function(_, $, BaseComponent, routing, mediator) {
    'use strict';

    return BaseComponent.extend({

        MODE_CREATE: 'create',
        MODE_VIEW: 'view',
        MODE_EDIT: 'edit',

        requiredOptions: [
            'modeSelector',
            'newEntitySelector',
            'existingEntitySelector',
            'existingEntityInputSelector',
            '_sourceElement'
        ],

        $el: null,
        $mode: null,
        $newEntity: null,
        $existingEntity: null,
        $existingEntityInput: null,
        $dialog: null,
        editable: false,

        /**
         * @param options
         */
        initialize: function(options) {
            this.editable = (options.editable === true);
            if (this.editable) {
                this.requiredOptions.push('editRoute');
            }
            var missingProperties = _.filter(this.requiredOptions, _.negate(_.bind(options.hasOwnProperty, options)));
            if (missingProperties.length) {
                throw new Error(
                    'Following properties are required but weren\'t passed: ' +
                    missingProperties.join(', ') +
                    '.'
                );
            }

            this.$el = options._sourceElement;
            this.$mode = this.$el.find(options.modeSelector);
            this.$newEntity = this.$el.find(options.newEntitySelector);
            this.newEntityFormName = options.newEntityFormName;
            this.$existingEntity = this.$el.find(options.existingEntitySelector);
            this.$existingEntityInput = $(options.existingEntityInputSelector);
            this.$dialog = this.$el.closest('.ui-dialog');
            this.$dialog.css('top', 0);
            this.editRoute = options.editRoute;

            this.$existingEntity.on('change', _.bind(this._onEntityChange, this));
            this.$existingEntity.on('change', _.bind(this._retrieveEntityData, this));
            this.$existingEntity.on('change', _.bind(this._cleanEntityData, this));
            this.$mode.on('change', _.bind(this._updateNewEntityVisibility, this));

            this._onEntityChange({val: this.$existingEntityInput.val()});
        },

        /**
         * Processes change in selected entity field.
         *
         * @param e
         * @private
         */
        _onEntityChange: function(e) {
            var mode = this.MODE_CREATE;
            if (e.val) {
                mode = this.editable ? this.MODE_EDIT : this.MODE_VIEW;
            }
            this._setMode(mode);
        },

        /**
         * Updates entity form visibility according to mode.
         *
         * @private
         */
        _updateNewEntityVisibility: function() {
            if (this._isInMode(this.MODE_CREATE) || this._isInMode(this.MODE_EDIT)) {
                this.$newEntity.show();
            } else {
                this.$newEntity.hide();
            }
        },

        /**
         * @param mode
         * @private
         */
        _setMode: function(mode) {
            if (this.$mode.val() === mode) {
                return;
            }

            this.$mode.val(mode).change();
        },

        /**
         * Checks if is in provided mode.
         *
         * @param mode
         * @returns Boolean
         * @private
         */
        _isInMode: function(mode) {
            return this.$mode.val() === mode;
        },

        /**
         * Retrieves entity data using set route. Provided id is used as query parameter for request.
         *
         * @param e
         * @private
         */
        _retrieveEntityData: function(e) {
            if (!this._isInMode(this.MODE_EDIT) || !e.val) {
                return;
            }

            var route = routing.generate(this.editRoute, {id: e.val});

            this._setLoading(true);
            $.get(route)
                .done(_.bind(this._setNewEntityForm, this))
                .fail(_.bind(this._handleDataRequestError, this))
                .always(_.bind(function() {
                    this._setLoading(false);
                }, this));
        },

        /**
         * Cleans entity data when selecting default choice.
         *
         * @param e
         * @private
         */
        _cleanEntityData: function(e) {
            if (!this._isInMode(this.MODE_CREATE) || e.val || !this.editable) {
                return;
            }

            this.$newEntity.find(':input').not(':checkbox').not(':radio').val(null);
        },

        /**
         * @param data HTML of edit form.
         * @private
         */
        _setNewEntityForm: function(data) {
            var $data = $(data);
            /*
             * For each input element in added form, create new name to match naming scheme.
             */
            $data.find(':input').each(_.bind(function(index, element) {
                var $element = $(element);
                var inputName = $element.attr('name');
                inputName = inputName.substr(inputName.indexOf('['));

                var $modifiedField = this.$newEntity.find('[name$="' + inputName + '"]');

                if ($element.is(':checkbox') || $element.is(':radio')) {
                    $modifiedField = this.$newEntity.find(
                        '[name$="' + inputName + '"][value="' + $element.val() + '"]'
                    );
                    $modifiedField.prop('checked', $element.is(':checked')).change();
                } else {
                    $modifiedField.val($element.val()).change();
                }
            }, this));
        },

        /**
         * @param jqXHR
         * @param textStatus
         * @param error
         * @private
         */
        _handleDataRequestError: function(jqXHR, textStatus, error) {
            throw new Error(textStatus); // Throw error
        },

        /**
         * @param enabled
         * @private
         */
        _setLoading: function(enabled) {
            if (enabled) {
                mediator.execute('showLoading');
            } else {
                mediator.execute('hideLoading');
            }
        }
    });
});
<|endoftext|>


<commit_before>var _ = require('underscore');
var CDB = require('cartodb.js');
var Backbone = require('backbone');
var CoreView = require('backbone/core-view');
var template = require('./input-color-categories.tpl');
var StackLayoutView = require('../../../../../../components/stack-layout/stack-layout-view');
var InputColorPickerView = require('../input-color-picker/input-color-picker-view');
var CategoriesListView = require('./input-color-categories-list-view');
var rampList = require('cartocolor');
var AssetPickerView = require('../assets-picker/input-asset-picker-view');
var MAX_VALUES = 10;
var DEFAULT_COLORS = _.clone(rampList.Prism[MAX_VALUES + 1]); // max values + "others" color
var queryTemplate = _.template('SELECT <%= column %>, count(<%= column %>) FROM (<%= sql %>) _table_sql GROUP BY <%= column %> ORDER BY count DESC, <%= column %> ASC LIMIT <%= max_values %>');
var checkAndBuildOpts = require('../../../../../../helpers/required-opts');
var REQUIRED_OPTS = [
  'columns',
  'configModel',
  'modals',
  'query',
  'userModel'
];

function _quote (c) {
  if (c && c !== true) {
    return '"' + c.toString().replace(/"/g, '\\"').replace(/\n/g, '\\n') + '"';
  }
  return c;
}

module.exports = CoreView.extend({
  events: {
    'click .js-back': '_onClickBack',
    'click .js-quantification': '_onClickQuantification'
  },

  initialize: function (opts) {
    checkAndBuildOpts(opts, REQUIRED_OPTS, this);
    this._imageEnabled = opts.imageEnabled;
    this._initBinds();
  },

  render: function () {
    this.clearSubViews();
    this.$el.empty();

    this._generateStackLayoutView();

    var column = this._getColumn();
    var columnType = column && column.type;

    this.$el.append(template({
      columnType: columnType,
      showBackButton: this._stackLayoutView.getCurrentPosition() === 0,
      attribute: this.model.get('attribute'),
      quantification: this.model.get('quantification') || 'category'
    }));

    if (this._stackLayoutView) {
      this.$('.js-content').append(this._stackLayoutView.render().$el);
    }

    return this;
  },

  _initBinds: function () {
    this.model.on('change:attribute', this._fetchColumns, this);
  },

  _generateStackLayoutView: function () {
    var self = this;

    var stackViewCollection = new Backbone.Collection([{
      createStackView: function (stackLayoutModel, opts) {
        return self._createRangeListView(stackLayoutModel, opts).bind(self);
      }
    }, {
      createStackView: function (stackLayoutModel, opts) {
        return self._createColorPickerView(stackLayoutModel, opts).bind(self);
      }
    }]);

    if (this._iconStylingEnabled()) {
      stackViewCollection.add({
        createStackView: function (stackLayoutModel, opts) {
          return self._createImagePickerView(stackLayoutModel, opts).bind(self);
        }
      });
    }

    this._stackLayoutView = new StackLayoutView({ collection: stackViewCollection });
    this._stackLayoutView.bind('positionChanged', this.render, this);
    this.addView(this._stackLayoutView);
  },

  _iconStylingEnabled: function () {
    return this._imageEnabled;
  },

  _getColumn: function () {
    return _.find(this._columns, function (column) {
      return column.label === this.model.get('attribute');
    }, this);
  },

  _fetchColumns: function () {
    if (!this.model.get('attribute')) {
      return;
    }

    if (this._query) {
      this._startLoader();

      var sql = new CDB.SQL({
        user: this._configModel.get('user_name'),
        sql_api_template: this._configModel.get('sql_api_template'),
        api_key: this._configModel.get('api_key')
      });

      sql.execute(
        queryTemplate({
          sql: this._query,
          column: this.model.get('attribute'),
          max_values: MAX_VALUES + 1
        }),
        null,
        {
          success: this._onQueryDone.bind(this),
          error: function () {
            // TODO: what happens if fails?
          }
        }
      );
    } else {
      this._onQueryDone();
    }
  },

  _onQueryDone: function (data) {
    data = data || {};
    this._stopLoader();
    this._updateRangeAndDomain(data.rows);
    this.render();
  },

  _updateRangeAndDomain: function (rows) {
    rows = rows || [];
    var categoryNames = _.pluck(rows, this.model.get('attribute'));

    var domain = _.map(categoryNames, function (name, i) {
      return name;
    }).slice(0, MAX_VALUES);

    if (this.model.get('attribute_type') !== 'number') {
      domain = domain.filter(function (item, pos, self) {
        return self.indexOf(item) === pos;
      }).map(_quote);
    }

    var range = _.map(categoryNames, function (name, i) {
      return (i < MAX_VALUES) ? DEFAULT_COLORS[i] : DEFAULT_COLORS[MAX_VALUES + 1];
    });

    if (this._iconStylingEnabled()) {
      var images = _.map(categoryNames, function () {
        return '';
      });

      this.model.attributes.images = images;
    }

    this.model.set({
      range: range,
      domain: domain
    });
  },

  _startLoader: function () {
    this.$('.js-loader').removeClass('is-hidden');
    this.$('.js-content').addClass('is-hidden');
  },

  _stopLoader: function () {
    this.$('.js-loader').addClass('is-hidden');
    this.$('.js-content').removeClass('is-hidden');
  },

  _getRange: function () {
    return _.map(this.model.get('range'), function (color, i) {
      var range = {
        val: color,
        color: color,
        title: this.model.get('domain')[i]
      };

      if (this._iconStylingEnabled()) {
        range.image = this.model.get('images')[i];
      }

      return range;
    }, this);
  },

  _updateRange: function (categories) {
    var range = _.clone(this.model.get('range'));
    range[this._index] = categories[this._index].color;
    this.model.set('range', range);
  },

  _createColorPickerView: function (stackLayoutModel, opts) {
    var range = this._getRange();

    var opacity = typeof this.model.get('opacity') !== 'undefined' ? this.model.get('opacity') : 1;

    var view = new InputColorPickerView({
      index: this._index,
      ramp: range,
      opacity: opacity,
      imageEnabled: this._iconStylingEnabled()
    });

    view.bind('back', function (value) {
      stackLayoutModel.prevStep();
    }, this);

    view.bind('goToAssetPicker', function () {
      stackLayoutModel.nextStep();
    }, this);

    view.bind('change', this._updateRange, this);
    view.bind('changeIndex', function (index) {
      this._index = index;
    }, this);
    view.bind('change:opacity', function (opacity) {
      this.model.set('opacity', opacity);
    }, this);

    return view;
  },

  _createRangeListView: function (stackLayoutModel, opts) {
    var view = new CategoriesListView({
      maxValues: MAX_VALUES,
      model: this.model,
      imageEnabled: this._iconStylingEnabled()
    });

    view.bind('back', function (value) {
      stackLayoutModel.prevStep();
    }, this);

    view.bind('selectItem', function (item) {
      this._index = item.index;

      if (item.target === 'asset') {
        stackLayoutModel.goToStep(2);
      } else {
        stackLayoutModel.goToStep(1);
      }
    }, this);

    return view;
  },

  _createImagePickerView: function (stackLayoutModel, opts) {
    var range = this._getRange();

    var view = new AssetPickerView({
      userModel: this._userModel,
      configModel: this._configModel,
      index: this._index,
      ramp: range,
      modals: this._modals,
      imageEnabled: this._iconStylingEnabled()
    });

    view.bind('back', function (value) {
      stackLayoutModel.goToStep(0);
    }, this);

    view.bind('goToColorPicker', function () {
      stackLayoutModel.prevStep();
    }, this);

    if (this._iconStylingEnabled()) {
      view.bind('change:image', this._onImageChanged, this);
    }

    return view;
  },

  _onImageChanged: function (data) {
    var images = _.clone(this.model.get('images'));
    images[this._index] = data.url;
    this.model.set('images', images);
  },

  _onClickQuantification: function (e) {
    this.killEvent(e);
    this.trigger('selectQuantification', this);
  },

  _onClickBack: function (e) {
    this.killEvent(e);
    this.trigger('back', this);
  }
}, {
  MAX_VALUES: MAX_VALUES
});
<commit_msg>Moved the only bind to initialize and using listenTo<commit_after>var _ = require('underscore');
var CDB = require('cartodb.js');
var Backbone = require('backbone');
var CoreView = require('backbone/core-view');
var template = require('./input-color-categories.tpl');
var StackLayoutView = require('../../../../../../components/stack-layout/stack-layout-view');
var InputColorPickerView = require('../input-color-picker/input-color-picker-view');
var CategoriesListView = require('./input-color-categories-list-view');
var rampList = require('cartocolor');
var AssetPickerView = require('../assets-picker/input-asset-picker-view');
var MAX_VALUES = 10;
var DEFAULT_COLORS = _.clone(rampList.Prism[MAX_VALUES + 1]); // max values + "others" color
var queryTemplate = _.template('SELECT <%= column %>, count(<%= column %>) FROM (<%= sql %>) _table_sql GROUP BY <%= column %> ORDER BY count DESC, <%= column %> ASC LIMIT <%= max_values %>');
var checkAndBuildOpts = require('../../../../../../helpers/required-opts');
var REQUIRED_OPTS = [
  'columns',
  'configModel',
  'modals',
  'query',
  'userModel'
];

function _quote (c) {
  if (c && c !== true) {
    return '"' + c.toString().replace(/"/g, '\\"').replace(/\n/g, '\\n') + '"';
  }
  return c;
}

module.exports = CoreView.extend({
  events: {
    'click .js-back': '_onClickBack',
    'click .js-quantification': '_onClickQuantification'
  },

  initialize: function (opts) {
    checkAndBuildOpts(opts, REQUIRED_OPTS, this);
    this._imageEnabled = opts.imageEnabled;
    this.listenTo(this.model, 'change:attribute', this._fetchColumns);
  },

  render: function () {
    this.clearSubViews();
    this.$el.empty();

    this._generateStackLayoutView();

    var column = this._getColumn();
    var columnType = column && column.type;

    this.$el.append(template({
      columnType: columnType,
      showBackButton: this._stackLayoutView.getCurrentPosition() === 0,
      attribute: this.model.get('attribute'),
      quantification: this.model.get('quantification') || 'category'
    }));

    if (this._stackLayoutView) {
      this.$('.js-content').append(this._stackLayoutView.render().$el);
    }

    return this;
  },

  _generateStackLayoutView: function () {
    var self = this;

    var stackViewCollection = new Backbone.Collection([{
      createStackView: function (stackLayoutModel, opts) {
        return self._createRangeListView(stackLayoutModel, opts).bind(self);
      }
    }, {
      createStackView: function (stackLayoutModel, opts) {
        return self._createColorPickerView(stackLayoutModel, opts).bind(self);
      }
    }]);

    if (this._iconStylingEnabled()) {
      stackViewCollection.add({
        createStackView: function (stackLayoutModel, opts) {
          return self._createImagePickerView(stackLayoutModel, opts).bind(self);
        }
      });
    }

    this._stackLayoutView = new StackLayoutView({ collection: stackViewCollection });
    this._stackLayoutView.bind('positionChanged', this.render, this);
    this.addView(this._stackLayoutView);
  },

  _iconStylingEnabled: function () {
    return this._imageEnabled;
  },

  _getColumn: function () {
    return _.find(this._columns, function (column) {
      return column.label === this.model.get('attribute');
    }, this);
  },

  _fetchColumns: function () {
    if (!this.model.get('attribute')) {
      return;
    }

    if (this._query) {
      this._startLoader();

      var sql = new CDB.SQL({
        user: this._configModel.get('user_name'),
        sql_api_template: this._configModel.get('sql_api_template'),
        api_key: this._configModel.get('api_key')
      });

      sql.execute(
        queryTemplate({
          sql: this._query,
          column: this.model.get('attribute'),
          max_values: MAX_VALUES + 1
        }),
        null,
        {
          success: this._onQueryDone.bind(this),
          error: function () {
            // TODO: what happens if fails?
          }
        }
      );
    } else {
      this._onQueryDone();
    }
  },

  _onQueryDone: function (data) {
    data = data || {};
    this._stopLoader();
    this._updateRangeAndDomain(data.rows);
    this.render();
  },

  _updateRangeAndDomain: function (rows) {
    rows = rows || [];
    var categoryNames = _.pluck(rows, this.model.get('attribute'));

    var domain = _.map(categoryNames, function (name, i) {
      return name;
    }).slice(0, MAX_VALUES);

    if (this.model.get('attribute_type') !== 'number') {
      domain = domain.filter(function (item, pos, self) {
        return self.indexOf(item) === pos;
      }).map(_quote);
    }

    var range = _.map(categoryNames, function (name, i) {
      return (i < MAX_VALUES) ? DEFAULT_COLORS[i] : DEFAULT_COLORS[MAX_VALUES + 1];
    });

    if (this._iconStylingEnabled()) {
      var images = _.map(categoryNames, function () {
        return '';
      });

      this.model.attributes.images = images;
    }

    this.model.set({
      range: range,
      domain: domain
    });
  },

  _startLoader: function () {
    this.$('.js-loader').removeClass('is-hidden');
    this.$('.js-content').addClass('is-hidden');
  },

  _stopLoader: function () {
    this.$('.js-loader').addClass('is-hidden');
    this.$('.js-content').removeClass('is-hidden');
  },

  _getRange: function () {
    return _.map(this.model.get('range'), function (color, i) {
      var range = {
        val: color,
        color: color,
        title: this.model.get('domain')[i]
      };

      if (this._iconStylingEnabled()) {
        range.image = this.model.get('images')[i];
      }

      return range;
    }, this);
  },

  _updateRange: function (categories) {
    var range = _.clone(this.model.get('range'));
    range[this._index] = categories[this._index].color;
    this.model.set('range', range);
  },

  _createColorPickerView: function (stackLayoutModel, opts) {
    var range = this._getRange();

    var opacity = typeof this.model.get('opacity') !== 'undefined' ? this.model.get('opacity') : 1;

    var view = new InputColorPickerView({
      index: this._index,
      ramp: range,
      opacity: opacity,
      imageEnabled: this._iconStylingEnabled()
    });

    view.bind('back', function (value) {
      stackLayoutModel.prevStep();
    }, this);

    view.bind('goToAssetPicker', function () {
      stackLayoutModel.nextStep();
    }, this);

    view.bind('change', this._updateRange, this);
    view.bind('changeIndex', function (index) {
      this._index = index;
    }, this);
    view.bind('change:opacity', function (opacity) {
      this.model.set('opacity', opacity);
    }, this);

    return view;
  },

  _createRangeListView: function (stackLayoutModel, opts) {
    var view = new CategoriesListView({
      maxValues: MAX_VALUES,
      model: this.model,
      imageEnabled: this._iconStylingEnabled()
    });

    view.bind('back', function (value) {
      stackLayoutModel.prevStep();
    }, this);

    view.bind('selectItem', function (item) {
      this._index = item.index;

      if (item.target === 'asset') {
        stackLayoutModel.goToStep(2);
      } else {
        stackLayoutModel.goToStep(1);
      }
    }, this);

    return view;
  },

  _createImagePickerView: function (stackLayoutModel, opts) {
    var range = this._getRange();

    var view = new AssetPickerView({
      userModel: this._userModel,
      configModel: this._configModel,
      index: this._index,
      ramp: range,
      modals: this._modals,
      imageEnabled: this._iconStylingEnabled()
    });

    view.bind('back', function (value) {
      stackLayoutModel.goToStep(0);
    }, this);

    view.bind('goToColorPicker', function () {
      stackLayoutModel.prevStep();
    }, this);

    if (this._iconStylingEnabled()) {
      view.bind('change:image', this._onImageChanged, this);
    }

    return view;
  },

  _onImageChanged: function (data) {
    var images = _.clone(this.model.get('images'));
    images[this._index] = data.url;
    this.model.set('images', images);
  },

  _onClickQuantification: function (e) {
    this.killEvent(e);
    this.trigger('selectQuantification', this);
  },

  _onClickBack: function (e) {
    this.killEvent(e);
    this.trigger('back', this);
  }
}, {
  MAX_VALUES: MAX_VALUES
});
<|endoftext|>


<commit_before>import won from "../../won-es6.js";
import Immutable from "immutable";
import { parseConnection } from "./parse-connection.js";
import { markUriAsRead, markConnUriAsClosed } from "../../won-localstorage.js";

import { markNeedAsRead } from "./reduce-needs.js";

export function storeConnectionsData(state, connectionsToStore) {
  if (connectionsToStore && connectionsToStore.size > 0) {
    connectionsToStore.forEach(connection => {
      state = addConnectionFull(state, connection);
    });
  }
  return state;
}

/**
 * Add's the connection to the needs connections.
 *
 * @param state
 * @param connection
 * @param newConnection
 * @return {*}
 */
export function addConnectionFull(state, connection) {
  let parsedConnection = parseConnection(connection);

  if (parsedConnection) {
    // console.log("parsedConnection: ", parsedConnection.toJS(), "immutable
    // ", parsedConnection);

    const needUri = parsedConnection.get("belongsToUri");
    let connections = state.getIn([needUri, "connections"]);

    if (connections) {
      const connectionUri = parsedConnection.getIn(["data", "uri"]);

      if (parsedConnection.getIn(["data", "unread"])) {
        //If there is a new message for the connection we will set the connection to newConnection
        state = state.setIn(
          [needUri, "lastUpdateDate"],
          parsedConnection.getIn(["data", "lastUpdateDate"])
        );
        state = state.setIn([needUri, "unread"], true);
      }

      if (parsedConnection.getIn(["data", "state"]) === won.WON.Closed) {
        markConnUriAsClosed(connectionUri);
      }

      return state.mergeDeepIn(
        [needUri, "connections", connectionUri],
        parsedConnection.get("data")
      );
    } else {
      console.error(
        "Couldnt add valid connection - missing need data in state",
        needUri,
        "parsedConnection: ",
        parsedConnection.toJS()
      );
    }
  } else {
    // console.log("No connection parsed, add no connection to this state:
    // ", state);
  }
  return state;
}

export function markConnectionAsRead(state, connectionUri, needUri) {
  const need = state.get(needUri);
  const connection = need && need.getIn(["connections", connectionUri]);

  markUriAsRead(connectionUri);

  if (!connection) {
    console.error(
      "no connection with connectionUri: <",
      connectionUri,
      "> found within needUri: <",
      needUri,
      ">"
    );
    return state;
  }

  state = state.setIn([needUri, "connections", connectionUri, "unread"], false);

  if (
    state.getIn([needUri, "connections"]).filter(conn => conn.get("unread"))
      .size == 0
  ) {
    state = markNeedAsRead(state, needUri);
  }

  return state;
}

export function setConnectionLoadingMessages(
  state,
  connectionUri,
  isLoadingMessages
) {
  const need = connectionUri && selectNeedByConnectionUri(state, connectionUri);
  const needUri = need && need.get("uri");
  const connection = need && need.getIn(["connections", connectionUri]);

  if (!connection) {
    console.error(
      "no connection with connectionUri: <",
      connectionUri,
      "> found within needUri: <",
      needUri,
      ">"
    );
    return state;
  }

  return state.setIn(
    [needUri, "connections", connectionUri, "isLoadingMessages"],
    isLoadingMessages
  );
}

export function setConnectionLoadingAgreementData(
  state,
  connectionUri,
  isLoadingAgreementData
) {
  const need = connectionUri && selectNeedByConnectionUri(state, connectionUri);
  const needUri = need && need.get("uri");
  const connection = need && need.getIn(["connections", connectionUri]);

  if (!connection) {
    console.error(
      "no connection with connectionUri: <",
      connectionUri,
      "> found within needUri: <",
      needUri,
      ">"
    );
    return state;
  }

  return state.setIn(
    [needUri, "connections", connectionUri, "isLoadingAgreementData"],
    isLoadingAgreementData
  );
}

export function markConnectionAsRated(state, connectionUri) {
  let need = connectionUri && selectNeedByConnectionUri(state, connectionUri);
  let connection = need && need.getIn(["connections", connectionUri]);

  if (!connection) {
    console.error(
      "no connection with connectionUri: <",
      connectionUri,
      "> found within needUri: <",
      need && need.get("uri"),
      ">"
    );
    return state;
  }

  return state.setIn(
    [need.get("uri"), "connections", connectionUri, "isRated"],
    true
  );
}

/**
 * Get the need for a given connectionUri
 *
 * @param state
 *            to retrieve data from
 * @param connectionUri
 *            to find corresponding need for
 */
export function selectNeedByConnectionUri(allNeedsInState, connectionUri) {
  return allNeedsInState
    .filter(need => need.getIn(["connections", connectionUri]))
    .first();
}

export function changeConnectionState(state, connectionUri, newState) {
  const need = selectNeedByConnectionUri(state, connectionUri);

  if (!need) {
    console.error("no need found for connectionUri", connectionUri);
    return state;
  }

  const needUri = need.get("uri");

  if (newState === won.WON.Closed) {
    markConnUriAsClosed(connectionUri);
  }

  return state
    .setIn([needUri, "connections", connectionUri, "state"], newState)
    .setIn([needUri, "connections", connectionUri, "unread"], true);
}

export function changeConnectionStateByFun(state, connectionUri, fun) {
  const need = selectNeedByConnectionUri(state, connectionUri);

  if (!need) {
    console.error("no need found for connectionUri", connectionUri);
    return state;
  }

  const needUri = need.get("uri");
  const connectionState = state.getIn([
    needUri,
    "connections",
    connectionUri,
    "state",
  ]);

  return changeConnectionState(state, connectionUri, fun(connectionState));
}

export function updateAgreementStateData(state, connectionUri, agreementData) {
  const need = selectNeedByConnectionUri(state, connectionUri);

  if (!need || !agreementData) {
    console.error("no need found for connectionUri", connectionUri);
    return state;
  }

  const needUri = need.get("uri");

  return state
    .setIn(
      [needUri, "connections", connectionUri, "agreementData"],
      agreementData
    )
    .setIn(
      [needUri, "connections", connectionUri, "agreementData", "isLoaded"],
      true
    )
    .setIn(
      [needUri, "connections", connectionUri, "isLoadingAgreementData"],
      false
    );
}

export function setShowAgreementData(state, connectionUri, showAgreementData) {
  const need = selectNeedByConnectionUri(state, connectionUri);

  if (!need) {
    console.error("no need found for connectionUri", connectionUri);
    return state;
  }

  const needUri = need.get("uri");

  return state.setIn(
    [needUri, "connections", connectionUri, "showAgreementData"],
    showAgreementData
  );
}

export function setShowMultiSelect(state, connectionUri, showMultiSelect) {
  const need = selectNeedByConnectionUri(state, connectionUri);

  if (!need) {
    console.error("no need found for connectionUri", connectionUri);
    return state;
  }

  const needUri = need.get("uri");

  //TODO: RESET ALL THE SELECTIONS IF THE SHOWMULTISELECT IS SET TO FALSE
  return state.setIn(
    [needUri, "connections", connectionUri, "showMultiSelect"],
    showMultiSelect
  );
}

export function addActiveConnectionsToNeedInLoading(state, needUri, connUris) {
  needUri &&
    connUris &&
    connUris.size > 0 &&
    console.log("addActiveConnectionsToNeedInLoading: ", connUris);
  let newState = state;
  needUri &&
    connUris &&
    connUris.forEach(connUri => {
      newState = addActiveConnectionToNeed(newState, needUri, connUri);
    });
  return newState;
}

export function addActiveConnectionToNeed(state, needUri, connUri) {
  const storedNeed = state.get(needUri);
  const isConnectionPresent =
    storedNeed && !!storedNeed.getIn(["connections", connUri]);

  if (storedNeed && !isConnectionPresent) {
    const connection = Immutable.fromJS({
      uri: connUri,
      state: undefined,
      messages: Immutable.Map(),
      agreementData: undefined,
      remoteNeedUri: undefined,
      remoteConnectionUri: undefined,
      creationDate: undefined,
      lastUpdateDate: undefined,
      unread: undefined,
      isRated: false,
      isLoadingMessages: false,
      isLoading: true,
      showAgreementData: false,
    });

    return state.mergeDeepIn([needUri, "connections", connUri], connection);
  }

  return state;
}
<commit_msg>de select all selected messages when closing the multiselect option<commit_after>import won from "../../won-es6.js";
import Immutable from "immutable";
import { parseConnection } from "./parse-connection.js";
import { markUriAsRead, markConnUriAsClosed } from "../../won-localstorage.js";

import { markNeedAsRead } from "./reduce-needs.js";

export function storeConnectionsData(state, connectionsToStore) {
  if (connectionsToStore && connectionsToStore.size > 0) {
    connectionsToStore.forEach(connection => {
      state = addConnectionFull(state, connection);
    });
  }
  return state;
}

/**
 * Add's the connection to the needs connections.
 *
 * @param state
 * @param connection
 * @param newConnection
 * @return {*}
 */
export function addConnectionFull(state, connection) {
  let parsedConnection = parseConnection(connection);

  if (parsedConnection) {
    // console.log("parsedConnection: ", parsedConnection.toJS(), "immutable
    // ", parsedConnection);

    const needUri = parsedConnection.get("belongsToUri");
    let connections = state.getIn([needUri, "connections"]);

    if (connections) {
      const connectionUri = parsedConnection.getIn(["data", "uri"]);

      if (parsedConnection.getIn(["data", "unread"])) {
        //If there is a new message for the connection we will set the connection to newConnection
        state = state.setIn(
          [needUri, "lastUpdateDate"],
          parsedConnection.getIn(["data", "lastUpdateDate"])
        );
        state = state.setIn([needUri, "unread"], true);
      }

      if (parsedConnection.getIn(["data", "state"]) === won.WON.Closed) {
        markConnUriAsClosed(connectionUri);
      }

      return state.mergeDeepIn(
        [needUri, "connections", connectionUri],
        parsedConnection.get("data")
      );
    } else {
      console.error(
        "Couldnt add valid connection - missing need data in state",
        needUri,
        "parsedConnection: ",
        parsedConnection.toJS()
      );
    }
  } else {
    // console.log("No connection parsed, add no connection to this state:
    // ", state);
  }
  return state;
}

export function markConnectionAsRead(state, connectionUri, needUri) {
  const need = state.get(needUri);
  const connection = need && need.getIn(["connections", connectionUri]);

  markUriAsRead(connectionUri);

  if (!connection) {
    console.error(
      "no connection with connectionUri: <",
      connectionUri,
      "> found within needUri: <",
      needUri,
      ">"
    );
    return state;
  }

  state = state.setIn([needUri, "connections", connectionUri, "unread"], false);

  if (
    state.getIn([needUri, "connections"]).filter(conn => conn.get("unread"))
      .size == 0
  ) {
    state = markNeedAsRead(state, needUri);
  }

  return state;
}

export function setConnectionLoadingMessages(
  state,
  connectionUri,
  isLoadingMessages
) {
  const need = connectionUri && selectNeedByConnectionUri(state, connectionUri);
  const needUri = need && need.get("uri");
  const connection = need && need.getIn(["connections", connectionUri]);

  if (!connection) {
    console.error(
      "no connection with connectionUri: <",
      connectionUri,
      "> found within needUri: <",
      needUri,
      ">"
    );
    return state;
  }

  return state.setIn(
    [needUri, "connections", connectionUri, "isLoadingMessages"],
    isLoadingMessages
  );
}

export function setConnectionLoadingAgreementData(
  state,
  connectionUri,
  isLoadingAgreementData
) {
  const need = connectionUri && selectNeedByConnectionUri(state, connectionUri);
  const needUri = need && need.get("uri");
  const connection = need && need.getIn(["connections", connectionUri]);

  if (!connection) {
    console.error(
      "no connection with connectionUri: <",
      connectionUri,
      "> found within needUri: <",
      needUri,
      ">"
    );
    return state;
  }

  return state.setIn(
    [needUri, "connections", connectionUri, "isLoadingAgreementData"],
    isLoadingAgreementData
  );
}

export function markConnectionAsRated(state, connectionUri) {
  let need = connectionUri && selectNeedByConnectionUri(state, connectionUri);
  let connection = need && need.getIn(["connections", connectionUri]);

  if (!connection) {
    console.error(
      "no connection with connectionUri: <",
      connectionUri,
      "> found within needUri: <",
      need && need.get("uri"),
      ">"
    );
    return state;
  }

  return state.setIn(
    [need.get("uri"), "connections", connectionUri, "isRated"],
    true
  );
}

/**
 * Get the need for a given connectionUri
 *
 * @param state
 *            to retrieve data from
 * @param connectionUri
 *            to find corresponding need for
 */
export function selectNeedByConnectionUri(allNeedsInState, connectionUri) {
  return allNeedsInState
    .filter(need => need.getIn(["connections", connectionUri]))
    .first();
}

export function changeConnectionState(state, connectionUri, newState) {
  const need = selectNeedByConnectionUri(state, connectionUri);

  if (!need) {
    console.error("no need found for connectionUri", connectionUri);
    return state;
  }

  const needUri = need.get("uri");

  if (newState === won.WON.Closed) {
    markConnUriAsClosed(connectionUri);
  }

  return state
    .setIn([needUri, "connections", connectionUri, "state"], newState)
    .setIn([needUri, "connections", connectionUri, "unread"], true);
}

export function changeConnectionStateByFun(state, connectionUri, fun) {
  const need = selectNeedByConnectionUri(state, connectionUri);

  if (!need) {
    console.error("no need found for connectionUri", connectionUri);
    return state;
  }

  const needUri = need.get("uri");
  const connectionState = state.getIn([
    needUri,
    "connections",
    connectionUri,
    "state",
  ]);

  return changeConnectionState(state, connectionUri, fun(connectionState));
}

export function updateAgreementStateData(state, connectionUri, agreementData) {
  const need = selectNeedByConnectionUri(state, connectionUri);

  if (!need || !agreementData) {
    console.error("no need found for connectionUri", connectionUri);
    return state;
  }

  const needUri = need.get("uri");

  return state
    .setIn(
      [needUri, "connections", connectionUri, "agreementData"],
      agreementData
    )
    .setIn(
      [needUri, "connections", connectionUri, "agreementData", "isLoaded"],
      true
    )
    .setIn(
      [needUri, "connections", connectionUri, "isLoadingAgreementData"],
      false
    );
}

export function setShowAgreementData(state, connectionUri, showAgreementData) {
  const need = selectNeedByConnectionUri(state, connectionUri);

  if (!need) {
    console.error("no need found for connectionUri", connectionUri);
    return state;
  }

  const needUri = need.get("uri");

  return state.setIn(
    [needUri, "connections", connectionUri, "showAgreementData"],
    showAgreementData
  );
}

export function setShowMultiSelect(state, connectionUri, showMultiSelect) {
  const need = selectNeedByConnectionUri(state, connectionUri);

  if (!need) {
    console.error("no need found for connectionUri", connectionUri);
    return state;
  }

  const needUri = need.get("uri");

  let messages = state.getIn([
    needUri,
    "connections",
    connectionUri,
    "messages",
  ]);

  if (!showMultiSelect) {
    messages = messages.map(msg => {
      msg = msg.set("isSelected", false);
      return msg;
    });
  }

  return state
    .setIn([needUri, "connections", connectionUri, "messages"], messages)
    .setIn(
      [needUri, "connections", connectionUri, "showMultiSelect"],
      showMultiSelect
    );
}

export function addActiveConnectionsToNeedInLoading(state, needUri, connUris) {
  needUri &&
    connUris &&
    connUris.size > 0 &&
    console.log("addActiveConnectionsToNeedInLoading: ", connUris);
  let newState = state;
  needUri &&
    connUris &&
    connUris.forEach(connUri => {
      newState = addActiveConnectionToNeed(newState, needUri, connUri);
    });
  return newState;
}

export function addActiveConnectionToNeed(state, needUri, connUri) {
  const storedNeed = state.get(needUri);
  const isConnectionPresent =
    storedNeed && !!storedNeed.getIn(["connections", connUri]);

  if (storedNeed && !isConnectionPresent) {
    const connection = Immutable.fromJS({
      uri: connUri,
      state: undefined,
      messages: Immutable.Map(),
      agreementData: undefined,
      remoteNeedUri: undefined,
      remoteConnectionUri: undefined,
      creationDate: undefined,
      lastUpdateDate: undefined,
      unread: undefined,
      isRated: false,
      isLoadingMessages: false,
      isLoading: true,
      showAgreementData: false,
    });

    return state.mergeDeepIn([needUri, "connections", connUri], connection);
  }

  return state;
}
<|endoftext|>


<commit_before>/**
 * Mixin for basenode controllers that need to set the breadcrumb.
 */
Ext.define('devilry_subjectadmin.utils.BasenodeBreadcrumbMixin', {
    requires: ['devilry_subjectadmin.utils.UrlLookup'],

    _getBreadcrumbPrefix: function(basenodeRecord) {
        var userInfoRecord = this.application.userInfoRecord;
        if(userInfoRecord.isSubjectPeriodOrAssignmentAdmin()) {
            return [{
                text: interpolate(gettext("All my %(subjects_term)s"), {
                    subjects_term: gettext('subjects')
                }, true),
                url: '#/'
            }];
        }
        return [];
    },

    _addBasenodeBreadcrumbToBreadcrumb: function(breadcrumb, basenodeRecord, skipLast) {
        var breadcrumbList = basenodeRecord.get('breadcrumb');
        Ext.each(breadcrumbList, function(item, index) {
            var isLast = index === breadcrumbList.length-1;
            if(isLast && skipLast) {
                return false; // break;
            }
            var url = devilry_subjectadmin.utils.UrlLookup.overviewByType(item.type, item.id);
            breadcrumb.push({
                text: item.text,
                url: url
            });
        }, this);
    },

    setBreadcrumb: function(basenodeRecord) {
        var breadcrumb = this._getBreadcrumbPrefix(basenodeRecord);
        this._addBasenodeBreadcrumbToBreadcrumb(breadcrumb, basenodeRecord, true);
        var breadcrumbList = basenodeRecord.get('breadcrumb');
        var lastBreadcrumb = breadcrumbList[breadcrumbList.length-1];
        this.application.breadcrumbs.set(breadcrumb, lastBreadcrumb.text);
    },

    /** For children of basenodes */
    setSubviewBreadcrumb: function(basenodeRecord, basenodeType, extra, current) {
        var breadcrumb = this._getBreadcrumbPrefix(basenodeRecord);
        this._addBasenodeBreadcrumbToBreadcrumb(breadcrumb, basenodeRecord);
        breadcrumb = breadcrumb.concat(extra);
        this.application.breadcrumbs.set(breadcrumb, current);
    },

    setLoadingBreadcrumb: function() {
        this.application.breadcrumbs.set([], gettext('Loading') + ' ...');
    },

    getPathFromBreadcrumb: function(basenodeRecord) {
        var path = [];
        Ext.Array.each(basenodeRecord.get('breadcrumb'), function(item) {
            if(item.type === 'Node') {
                return false; // break
            }
            path.push(item.text);
        }, this);
        return path.join('.');
    }
});
<commit_msg>devilry_subjectadmin...BasenodeBreadcrumbMixin: Fixed getPathFromBreadcrumb().<commit_after>/**
 * Mixin for basenode controllers that need to set the breadcrumb.
 */
Ext.define('devilry_subjectadmin.utils.BasenodeBreadcrumbMixin', {
    requires: ['devilry_subjectadmin.utils.UrlLookup'],

    _getBreadcrumbPrefix: function(basenodeRecord) {
        var userInfoRecord = this.application.userInfoRecord;
        if(userInfoRecord.isSubjectPeriodOrAssignmentAdmin()) {
            return [{
                text: interpolate(gettext("All my %(subjects_term)s"), {
                    subjects_term: gettext('subjects')
                }, true),
                url: '#/'
            }];
        }
        return [];
    },

    _addBasenodeBreadcrumbToBreadcrumb: function(breadcrumb, basenodeRecord, skipLast) {
        var breadcrumbList = basenodeRecord.get('breadcrumb');
        Ext.each(breadcrumbList, function(item, index) {
            var isLast = index === breadcrumbList.length-1;
            if(isLast && skipLast) {
                return false; // break;
            }
            var url = devilry_subjectadmin.utils.UrlLookup.overviewByType(item.type, item.id);
            breadcrumb.push({
                text: item.text,
                url: url
            });
        }, this);
    },

    setBreadcrumb: function(basenodeRecord) {
        var breadcrumb = this._getBreadcrumbPrefix(basenodeRecord);
        this._addBasenodeBreadcrumbToBreadcrumb(breadcrumb, basenodeRecord, true);
        var breadcrumbList = basenodeRecord.get('breadcrumb');
        var lastBreadcrumb = breadcrumbList[breadcrumbList.length-1];
        this.application.breadcrumbs.set(breadcrumb, lastBreadcrumb.text);
    },

    /** For children of basenodes */
    setSubviewBreadcrumb: function(basenodeRecord, basenodeType, extra, current) {
        var breadcrumb = this._getBreadcrumbPrefix(basenodeRecord);
        this._addBasenodeBreadcrumbToBreadcrumb(breadcrumb, basenodeRecord);
        breadcrumb = breadcrumb.concat(extra);
        this.application.breadcrumbs.set(breadcrumb, current);
    },

    setLoadingBreadcrumb: function() {
        this.application.breadcrumbs.set([], gettext('Loading') + ' ...');
    },

    getPathFromBreadcrumb: function(basenodeRecord) {
        var path = [];
        Ext.Array.each(basenodeRecord.get('breadcrumb'), function(item) {
            if(item.type === 'Node') {
                return false; // break
            }
            path.push(item.text);
        }, this, true);
        path.reverse();
        return path.join('.');
    }
});
<|endoftext|>


<commit_before>import * as utils from 'base/utils';
import Component from 'base/component';
import DynamicBackground from 'helpers/d3.dynamicBackground';
import axisWithLabelPicker from 'helpers/d3.axisWithLabelPicker';

/*!
 * VIZABI POP BY AGE Component
 */

//POP BY AGE CHART COMPONENT
var BarRankChart = Component.extend({

  values: {}, // values for all the hooks

  /**
   * Initializes the component (Bar Chart).
   * Executed once before any template is rendered.
   * @param {Object} config The options passed to the component
   * @param {Object} context The component's parent
   */
  init: function(config, context) {

    this.name = 'barrankchart';
    this.template = 'barrank.html';

    //define expected models for this component
    this.model_expects = [{
      name: "time",
      type: "time"
    }, {
      name: "entities",
      type: "entities"
    }, {
      name: "marker",
      type: "model"
    }, {
      name: "language",
      type: "language"
    }, {
      name: "ui",
      type: "model"
    }];

    var _this = this;
    this.model_binds = {
      "change:time:value": function(evt) {
        _this.onTimeChange();
      },
      "change:entities:select": function(evt) {
        _this.selectBars();
      }
    };

    //contructor is the same as any component
    this._super(config, context);

    // set up the scales
    this.xScale = null;
    this.cScale = d3.scale.category10();

    // set up the axes
    this.xAxis = axisWithLabelPicker();
  },

  onTimeChange: function() {
    this.year.setText(this.timeFormatter(this.model.time.value));

    this.loadData();
    this.draw();
  },

  /**
   * DOM and model are ready
   */
  readyOnce: function() {
    this.element = d3.select(this.element);

    // reference elements
    this.graph = this.element.select('.vzb-br-graph');
    this.yearEl = this.graph.select('.vzb-br-year');
    this.year = new DynamicBackground(this.yearEl);
    this.barContainer = this.graph.select('.vzb-br-bars');
    this.xAxisEl = this.graph.select('.vzb-br-axis-x');

    // set up formatters
    this.timeFormatter = d3.time.format(this.model.time.formatOutput);
    this.xAxis.tickFormat(this.model.marker.axis_x.tickFormatter);

    // set up scales and axes
    this.xScale = this.model.marker.axis_x.getScale(false);
    this.cScale = this.model.marker.color.getScale();

  },

  /*
   * Both model and DOM are ready
   */
  ready: function() {

    this.loadData();
    this.draw();
  },

  resize: function() {
    this.draw();
  },

  loadData: function() {

    // get data, for the active year. Nest them using the entity of the graph
    var filter = {};
    filter[this.model.time.dim] = this.model.time.value;
    this.values = this.model.marker.getValues(filter, [this.model.entities.dim]);

    // sort the data
    this.sortedEntities = this.sortByIndicator(this.values.axis_x);
  },

  draw: function() {
    this.drawAxes();
    this.drawData();
  },

  /*
  * draw the chart/stage
  */
  drawAxes: function() {

    // these should go in some style-config
    this.barHeight = 20;
    var margin = {top: 20, bottom: 40, left: 90, right: 20};

    // draw the stage - copied from popbyage, should figure out what it exactly does and what is necessary.
    this.height = parseInt(this.element.style("height"), 10) - margin.top - margin.bottom;
    this.width = parseInt(this.element.style("width"), 10) - margin.left - margin.right;

    this.graph
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    if(this.model.marker.axis_x.scaleType !== "ordinal") {
      this.xScale.range([0, this.width]);
    } else {
      this.xScale.rangePoints([0, this.width]).range();
    }

    // redraw the limits
    var limits = this.model.marker.axis_x.getLimits(this.model.marker.axis_x.which);
    this.xScale = this.xScale.domain([limits.min, limits.max]);

    this.xAxis.scale(this.xScale)
      .orient("bottom")
      .tickSize(6, 0)
      .tickSizeMinor(3, 0)
      .labelerOptions({
        scaleType: this.model.marker.axis_x.scaleType,
        toolMargin: margin,
        limitMaxTickNumber: 6
      });

    this.xAxisEl.attr("transform", "translate(0," + this.height + ")")
      .call(this.xAxis);

    this.xAxisEl.call(this.xAxis);

  },

  drawData: function() {

    var _this = this;
    var bar_margin = 2; // should go in some config
    var duration = (this.model.time.playing) ? this.model.time.delayAnimations : 0;

    // apply the current data to the bars (including ordering)
    var updatedBars = this.barContainer
      .selectAll('.vzb-br-bar')
      .data(this.sortedEntities, getDataKey)
      .order();

    // update the shown bars for new data-set
    this.createAndDeleteBars(updatedBars);

    this.barContainer
      .selectAll('.vzb-br-bar')
      .data(this.sortedEntities, getDataKey)
      .order()
      .transition().duration(duration).ease("linear")
      .attr("transform", getBarPosition)
      .each(function (d, i) {
        var bar = d3.select(this);

        // set width of the bars
        bar.selectAll('rect')
          .transition().duration(duration).ease("linear")
          .attr("width", function() {
            var width = _this.xScale(d.value);
            return width > 0 ? width : 0;
          })

        // set positions of the bar-values
        bar.selectAll('.vzb-br-value')
          .text(function() {
            return _this.model.marker.axis_x.tickFormatter(d.value);
          })

        // set title (tooltip)
        bar.selectAll('title')
          .text(function() {
            return _this.values.label[d.entity] + ' (' + _this.model.marker.axis_x.tickFormatter(d.value) + ')';
          });

      });

      // helper functions
      function getBarPosition(d, i) {
          return 'translate(0,'+ (_this.barHeight+bar_margin)*i + ')';
      }
      function getDataKey(d) {
        return d.entity;
      }

  },

  createAndDeleteBars: function(updatedBars) {

    var _this = this;

    // remove groups for entities that are gone
    updatedBars.exit().remove();

    // make the groups for the entities which were not drawn yet (.data.enter() does this)
    var newGroups = updatedBars.enter().append("g")
        .attr("class", 'vzb-br-bar')
        .attr("id", function(d) {
          return "vzb-br-bar-" + d.entity;
        })
        .on("mousemove", function(bar) { _this.setHover(bar, true)  })
        .on("mouseout",  function(bar) { _this.setHover(bar, false) })
        .on("click", function(d) {

          utils.forEach(_this.model.marker.space, function(entity) {
            if (_this.model[entity].getDimension() !== 'time')
              _this.model[entity].selectEntity(d); // this will trigger a change in the model, which the tool listens to
          });

        });

    // draw new bars per group
    newGroups.append('rect')
        .attr("x", 0)
        .attr("rx", this.barHeight/4)
        .attr("ry", this.barHeight/4)
        .attr("stroke", "white")
        .attr("stroke-opacity", 0)
        .attr("stroke-width", 2)
        .attr("height", this.barHeight)
        .attr("fill", function(d) {
          var color = _this.cScale(_this.values.color[d.entity]);
          return d3.rgb(color);
        });

    // draw new labels per group
    newGroups.append('text')
        .attr("class", "vzb-br-label")
        .attr("x", -5)
        .attr("y", this.barHeight/2)
        .attr("text-anchor", "end")
        .attr("alignment-baseline", "middle")
        .text(function(d, i) {
          var label = _this.values.label[d.entity];
          return label.length < 12 ? label : label.substring(0, 9) + '...';
        })
        .style("fill", function(d) {
          var color = _this.cScale(_this.values.color[d.entity]);
          return d3.rgb(color).darker(2);
        });

    // draw new values on each bar
    newGroups.append('text')
        .attr("class", "vzb-br-value")
        .attr("x", 5)
        .attr("y", this.barHeight/2)
        .attr("alignment-baseline", "middle")
        .style("fill", function(d) {
          var color = _this.cScale(_this.values.color[d.entity]);
          return d3.rgb(color).darker(2);
        });

    newGroups.append('title');
  },

  /**
  * DATA HELPER FUNCTIONS
  */

  sortByIndicator: function(values) {
    var _this = this;
    var data_array = [];
    // first put the data in an array (objects aren't sortable)
    utils.forEach(values, function(indicator_value, entity) {
      var row = { entity: entity, value: indicator_value };
      row[_this.model.entities.dim] = entity;
      data_array.push(row);
      //data_array.push({ entity: entity, value: indicator_value });
    });
    data_array.sort(function(a, b) {
      // if a is bigger, a comes first, i.e. descending sort
      return b.value - a.value;
    });
    return data_array;
  },

  /**
  * UI METHODS
  */

  /**
   * setting hover
   */
  setHover: function(bar, hover) {
    this.barContainer.classed('vzb-dimmed', hover);
    this.barContainer.select("#vzb-br-bar-" + bar.entity).classed('vzb-hovered', hover);
  },

  /**
   * Select Entities
   */
  selectBars: function() {
    var _this = this;
    var entityDim = this.model.entities.dim;
    var selected = this.model.entities.select;

    // unselect all bars
    this.barContainer.classed('vzb-dimmed-selected', false);
    this.barContainer.selectAll('.vzb-br-bar.vzb-selected').classed('vzb-selected', false);

    // select the selected ones
    if(selected.length) {
      this.barContainer.classed('vzb-dimmed-selected', true);
      utils.forEach(selected, function(selectedBar) {
        _this.barContainer.select("#vzb-br-bar-" + selectedBar[entityDim]).classed('vzb-selected', true);
      });
    }

  },

});

export default BarRankChart;<commit_msg>Moved tooltip to label only and simplified code some more<commit_after>import * as utils from 'base/utils';
import Component from 'base/component';
import DynamicBackground from 'helpers/d3.dynamicBackground';
import axisWithLabelPicker from 'helpers/d3.axisWithLabelPicker';

/*!
 * VIZABI POP BY AGE Component
 */

//POP BY AGE CHART COMPONENT
var BarRankChart = Component.extend({

  values: {}, // values for all the hooks

  /**
   * Initializes the component (Bar Chart).
   * Executed once before any template is rendered.
   * @param {Object} config The options passed to the component
   * @param {Object} context The component's parent
   */
  init: function(config, context) {

    this.name = 'barrankchart';
    this.template = 'barrank.html';

    //define expected models for this component
    this.model_expects = [{
      name: "time",
      type: "time"
    }, {
      name: "entities",
      type: "entities"
    }, {
      name: "marker",
      type: "model"
    }, {
      name: "language",
      type: "language"
    }, {
      name: "ui",
      type: "model"
    }];

    var _this = this;
    this.model_binds = {
      "change:time:value": function(evt) {
        _this.onTimeChange();
      },
      "change:entities:select": function(evt) {
        _this.selectBars();
      }
    };

    //contructor is the same as any component
    this._super(config, context);

    // set up the scales
    this.xScale = null;
    this.cScale = d3.scale.category10();

    // set up the axes
    this.xAxis = axisWithLabelPicker();
  },

  onTimeChange: function() {
    this.year.setText(this.timeFormatter(this.model.time.value));

    this.loadData();
    this.draw();
  },

  /**
   * DOM and model are ready
   */
  readyOnce: function() {
    this.element = d3.select(this.element);

    // reference elements
    this.graph = this.element.select('.vzb-br-graph');
    this.yearEl = this.graph.select('.vzb-br-year');
    this.year = new DynamicBackground(this.yearEl);
    this.barContainer = this.graph.select('.vzb-br-bars');
    this.xAxisEl = this.graph.select('.vzb-br-axis-x');

    // set up formatters
    this.timeFormatter = d3.time.format(this.model.time.formatOutput);
    this.xAxis.tickFormat(this.model.marker.axis_x.tickFormatter);

    // set up scales and axes
    this.xScale = this.model.marker.axis_x.getScale(false);
    this.cScale = this.model.marker.color.getScale();

  },

  /*
   * Both model and DOM are ready
   */
  ready: function() {

    this.loadData();
    this.draw();
  },

  resize: function() {
    this.draw();
  },

  loadData: function() {

    // get data, for the active year. Nest them using the entity of the graph
    var filter = {};
    filter[this.model.time.dim] = this.model.time.value;
    this.values = this.model.marker.getValues(filter, [this.model.entities.dim]);

    // sort the data
    this.sortedEntities = this.sortByIndicator(this.values.axis_x);
  },

  draw: function() {
    this.drawAxes();
    this.drawData();
  },

  /*
  * draw the chart/stage
  */
  drawAxes: function() {

    // these should go in some style-config
    this.barHeight = 20;
    var margin = {top: 20, bottom: 40, left: 90, right: 20};

    // draw the stage - copied from popbyage, should figure out what it exactly does and what is necessary.
    this.height = parseInt(this.element.style("height"), 10) - margin.top - margin.bottom;
    this.width = parseInt(this.element.style("width"), 10) - margin.left - margin.right;

    this.graph
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    if(this.model.marker.axis_x.scaleType !== "ordinal") {
      this.xScale.range([0, this.width]);
    } else {
      this.xScale.rangePoints([0, this.width]).range();
    }

    // redraw the limits
    var limits = this.model.marker.axis_x.getLimits(this.model.marker.axis_x.which);
    this.xScale = this.xScale.domain([limits.min, limits.max]);

    this.xAxis.scale(this.xScale)
      .orient("bottom")
      .tickSize(6, 0)
      .tickSizeMinor(3, 0)
      .labelerOptions({
        scaleType: this.model.marker.axis_x.scaleType,
        toolMargin: margin,
        limitMaxTickNumber: 6
      });

    this.xAxisEl.attr("transform", "translate(0," + this.height + ")")
      .call(this.xAxis);

    this.xAxisEl.call(this.xAxis);

  },

  drawData: function() {

    var _this = this;
    var bar_margin = 2; // should go in some config
    var duration = (this.model.time.playing) ? this.model.time.delayAnimations : 0;

    // apply the current data to the bars (including ordering)
    var updatedBars = this.barContainer
      .selectAll('.vzb-br-bar')
      .data(this.sortedEntities, getDataKey)
      .order();

    // update the shown bars for new data-set
    this.createAndDeleteBars(updatedBars);

    this.barContainer
      .selectAll('.vzb-br-bar')
      .data(this.sortedEntities, getDataKey)
      .order()
      .transition().duration(duration).ease("linear")
      .attr("transform", getBarPosition)
      .each(function (d, i) {

        var bar = d3.select(this);
        var barWidth = _this.xScale(d.value);
        var xValue = _this.model.marker.axis_x.tickFormatter(d.value);

        // set width of the bars
        bar.selectAll('rect')
          .transition().duration(duration).ease("linear")
          .attr("width", (barWidth > 0) ? barWidth : 0)

        // set positions of the bar-values
        bar.selectAll('.vzb-br-value')
          .text(xValue)

        // set title (tooltip)
        bar.selectAll('title')
          .text(_this.values.label[d.entity] + ' (' + xValue + ')');

      });

      // helper functions
      function getBarPosition(d, i) {
          var barY = (_this.barHeight+bar_margin)*i;
          return 'translate(0, '+ barY + ')';
      }
      function getDataKey(d) {
        return d.entity;
      }

  },

  createAndDeleteBars: function(updatedBars) {

    var _this = this;

    // remove groups for entities that are gone
    updatedBars.exit().remove();

    // make the groups for the entities which were not drawn yet (.data.enter() does this)
    var newGroups = updatedBars.enter().append("g")
        .attr("class", 'vzb-br-bar')
        .attr("id", function(d) {
          return "vzb-br-bar-" + d.entity;
        })
        .on("mousemove", function(bar) { _this.setHover(bar, true)  })
        .on("mouseout",  function(bar) { _this.setHover(bar, false) })
        .on("click", function(d) {

          utils.forEach(_this.model.marker.space, function(entity) {
            if (_this.model[entity].getDimension() !== 'time')
              _this.model[entity].selectEntity(d); // this will trigger a change in the model, which the tool listens to
          });

        });

    // draw new bars per group
    newGroups.append('rect')
        .attr("x", 0)
        .attr("rx", this.barHeight/4)
        .attr("ry", this.barHeight/4)
        .attr("stroke", "white")
        .attr("stroke-opacity", 0)
        .attr("stroke-width", 2)
        .attr("height", this.barHeight)
        .attr("fill", function(d) {
          var color = _this.cScale(_this.values.color[d.entity]);
          return d3.rgb(color);
        });

    // draw new labels per group
    newGroups.append('text')
        .attr("class", "vzb-br-label")
        .attr("x", -5)
        .attr("y", this.barHeight/2)
        .attr("text-anchor", "end")
        .attr("alignment-baseline", "middle")
        .text(function(d, i) {
          var label = _this.values.label[d.entity];
          return label.length < 12 ? label : label.substring(0, 9) + '...';
        })
        .style("fill", function(d) {
          var color = _this.cScale(_this.values.color[d.entity]);
          return d3.rgb(color).darker(2);
        })
        .append('title'); // watch out: might be overwritten if changing the labeltext later on

    // draw new values on each bar
    newGroups.append('text')
        .attr("class", "vzb-br-value")
        .attr("x", 5)
        .attr("y", this.barHeight/2)
        .attr("alignment-baseline", "middle")
        .style("fill", function(d) {
          var color = _this.cScale(_this.values.color[d.entity]);
          return d3.rgb(color).darker(2);
        });
  },

  /**
  * DATA HELPER FUNCTIONS
  */

  sortByIndicator: function(values) {
    var _this = this;
    var data_array = [];
    // first put the data in an array (objects aren't sortable)
    utils.forEach(values, function(indicator_value, entity) {
      var row = { entity: entity, value: indicator_value };
      row[_this.model.entities.dim] = entity;
      data_array.push(row);
      //data_array.push({ entity: entity, value: indicator_value });
    });
    data_array.sort(function(a, b) {
      // if a is bigger, a comes first, i.e. descending sort
      return b.value - a.value;
    });
    return data_array;
  },

  /**
  * UI METHODS
  */

  /**
   * setting hover
   */
  setHover: function(bar, hover) {
    this.barContainer.classed('vzb-dimmed', hover);
    this.barContainer.select("#vzb-br-bar-" + bar.entity).classed('vzb-hovered', hover);
  },

  /**
   * Select Entities
   */
  selectBars: function() {
    var _this = this;
    var entityDim = this.model.entities.dim;
    var selected = this.model.entities.select;

    // unselect all bars
    this.barContainer.classed('vzb-dimmed-selected', false);
    this.barContainer.selectAll('.vzb-br-bar.vzb-selected').classed('vzb-selected', false);

    // select the selected ones
    if(selected.length) {
      this.barContainer.classed('vzb-dimmed-selected', true);
      utils.forEach(selected, function(selectedBar) {
        _this.barContainer.select("#vzb-br-bar-" + selectedBar[entityDim]).classed('vzb-selected', true);
      });
    }

  },

});

export default BarRankChart;<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc controller
 * @name officePageModule:OfficePageController
 *
 * @description
 *
 *
 * @requires $log, $state, officePageFactory
 * */

function OfficePageController($interpolate, $log, $state, officePageFactory) {
  // var uiRouterParams = localeStateDataStore.getStateData();
  // var officePageData = officePageFactory.getOfficePageData();
  var officePageData = {};

  var office = this;
  office.state = $state;
  office.officeTypeId = $state.params.electionTypeId;

  office.candidatesList = [];
  // office.officeData = officePageData;

  activate();
  function activate() {
    return officePageFactory.getListOfCandidatesForOffice(office.officeTypeId)
      .then(function(data) {
        officePageData = data;
        office.pageData = officePageData;
        $log.info('OFFICE PAGE DATA! = ', officePageData);
        createCandidatesList(office.pageData.candidates);
      });
  }

  function createCandidatesList(candidatesArray) {
    angular.forEach(candidatesArray, function(candidate) {
      var candidateListItem = {};
      candidateListItem = createCandidateListItem(candidate);
      office.candidatesList.push(candidateListItem);
    });
  }

  function createCandidateListItem(candidateObject) {
    var item = {};
    item.linkTitle = candidateObject.name;
    item.subTitle = 'amount collected';
    item.avatarUrl = candidateObject.photo_url;
    item.dollarAmount = null;   //TODO: add in total dollar amounts for candidates

    item.toState = $interpolate('appMain.localePage.candidate({electionYear: "{{ electionYear }}", candidateId: "{{ candidateId }}" })')({
      electionYear: $state.params.electionYear,
      candidateId: candidateObject.id
    });
    return item;
  }

}

OfficePageController.$inject = ['$interpolate', '$log', '$state', 'officePageFactory'];
module.exports = OfficePageController;
<commit_msg>Use candidate_photo filter to show placeholder url<commit_after>'use strict';

/**
 * @ngdoc controller
 * @name officePageModule:OfficePageController
 *
 * @description
 *
 *
 * @requires $log, $state, officePageFactory
 * */

function OfficePageController($filter, $interpolate, $log, $state, officePageFactory) {
  // var uiRouterParams = localeStateDataStore.getStateData();
  // var officePageData = officePageFactory.getOfficePageData();
  var officePageData = {};

  var office = this;
  office.state = $state;
  office.officeTypeId = $state.params.electionTypeId;

  office.candidatesList = [];
  // office.officeData = officePageData;

  activate();
  function activate() {
    return officePageFactory.getListOfCandidatesForOffice(office.officeTypeId)
      .then(function(data) {
        officePageData = data;
        office.pageData = officePageData;
        $log.info('OFFICE PAGE DATA! = ', officePageData);
        createCandidatesList(office.pageData.candidates);
      });
  }

  function createCandidatesList(candidatesArray) {
    angular.forEach(candidatesArray, function(candidate) {
      var candidateListItem = {};
      candidateListItem = createCandidateListItem(candidate);
      office.candidatesList.push(candidateListItem);
    });
  }

  function createCandidateListItem(candidateObject) {
    var item = {};
    item.linkTitle = candidateObject.name;
    item.subTitle = 'amount collected';
    item.avatarUrl = $filter('candidate_photo')(candidateObject);
    item.dollarAmount = null;   //TODO: add in total dollar amounts for candidates

    item.toState = $interpolate('appMain.localePage.candidate({electionYear: "{{ electionYear }}", candidateId: "{{ candidateId }}" })')({
      electionYear: $state.params.electionYear,
      candidateId: candidateObject.id
    });
    return item;
  }

}

OfficePageController.$inject = ['$filter', '$interpolate', '$log', '$state', 'officePageFactory'];
module.exports = OfficePageController;
<|endoftext|>


<commit_before>/* @flow */
import type { LaunchpadDevice } from '../../'

import type { ChannelControl, ControlMessage } from '@mixxx-launchpad/mixxx'
import Bpm from '../../App/Bpm'

import { modes } from '../ModifierSidebar'
import type { Modifier } from '../ModifierSidebar'

export default (gridPosition: [number, number]) => (deck: ChannelControl) => (modifier: Modifier) => (device: LaunchpadDevice) => {
  const tempoBpm = new Bpm()
  tempoBpm.on('tap', (avg) => {
    deck.bpm.setValue(avg)
  })
  return {
    bindings: {
      tap: {
        type: 'button',
        target: gridPosition,
        attack: () => {
          modes(modifier.getState(),
            () => {
              tempoBpm.tap()
            },
            undefined,
            () => {
              deck.beats_translate_curpos.setValue(1)
            },
            () => {
              deck.beats_translate_match_alignment.setValue(1)
            }
          )
        }
      },
      beat: {
        type: 'control',
        target: deck.beat_active,
        update: ({ value }: ControlMessage, { bindings }: Object) => {
          if (value) {
            bindings.tap.button.sendColor(device.colors.hi_red)
          } else {
            bindings.tap.button.sendColor(device.colors.black)
          }
        }
      }
    }
  }
}
<commit_msg>and bpm tap<commit_after>/* @flow */
import type { LaunchpadDevice } from '../../'

import type { ChannelControl, ControlMessage } from '@mixxx-launchpad/mixxx'
import Bpm from '../../App/Bpm'

import { modes } from '../ModifierSidebar'
import type { Modifier } from '../ModifierSidebar'

export default (gridPosition: [number, number]) => (deck: ChannelControl) => (modifier: Modifier) => (device: LaunchpadDevice) => {
  const tempoBpm = new Bpm()
  tempoBpm.on('tap', (avg) => {
    deck.bpm.setValue(avg)
  })
  return {
    bindings: {
      tap: {
        type: 'button',
        target: gridPosition,
        attack: () => {
          modes(modifier.getState(),
            () => {
              tempoBpm.tap()
            },
            () => {
              deck.bpm_tap.setValue(1)
            },
            () => {
              deck.beats_translate_curpos.setValue(1)
            },
            () => {
              deck.beats_translate_match_alignment.setValue(1)
            }
          )
        }
      },
      beat: {
        type: 'control',
        target: deck.beat_active,
        update: ({ value }: ControlMessage, { bindings }: Object) => {
          if (value) {
            bindings.tap.button.sendColor(device.colors.hi_red)
          } else {
            bindings.tap.button.sendColor(device.colors.black)
          }
        }
      }
    }
  }
}
<|endoftext|>


<commit_before>import { string } from "lively.lang";
import { Color, pr } from "lively.graphics";
import { Morph, show } from "./index.js";
import { FontMetric } from "./rendering/renderer.js";

export class Text extends Morph {

  static makeLabel(string, props) {
    return new Text({
      textString: string,
      fontFamily: "Helvetica Neue, Arial",
      fontColor: Color.black,
      fontSize: 11,
      readOnly: true,
      ...props
    });
  }

  constructor(props) {
    super({
      readOnly: false,
      clipMode: "hidden",
      textString: "",
      fixedWidth: false, fixedHeight: false,
      draggable: false,
      _selection: { start: 0, end: 0 },
      ...props
    });
    this.fit();
    this._needsFit = false;
    this._needsSelect = false;
  }

  get fontMetric() { return FontMetric.default(); }

  get isText() { return true }

  get _nodeType() { return 'textarea'; }

  get textString() { return this.getProperty("textString") }
  set textString(value) {
    this.recordChange({prop: "textString", value: String(value)});
    this._needsFit = true;
  }

  get readOnly() { return this.getProperty("readOnly"); }
  set readOnly(value) {
    this.nativeCursor = value ? "default" : "auto";
    this.recordChange({prop: "readOnly", value});
  }

  get fixedWidth() { return this.getProperty("fixedWidth") }
  set fixedWidth(value) {
    this.recordChange({prop: "fixedWidth", value});
    this._needsFit = true;
  }

  get fixedHeight() { return this.getProperty("fixedHeight") }
  set fixedHeight(value) {
    this.recordChange({prop: "fixedHeight", value});
    this._needsFit = true;
  }

  get fontFamily() { return this.getProperty("fontFamily") }
  set fontFamily(value) {
    this.recordChange({prop: "fontFamily", value});
    this._needsFit = true;
  }

  get fontSize() { return this.getProperty("fontSize") }
  set fontSize(value) {
    this.recordChange({prop: "fontSize", value});
    this._needsFit = true;
  }

  get fontColor() { return this.getProperty("fontColor") }
  set fontColor(value) {
    this.recordChange({prop: "fontColor", value});
  }

  get placeholder() { return this.getProperty("placeholder") }
  set placeholder(value) {
    this.recordChange({prop: "placeholder", value});
    this._needsFit = true;
  }

  get _selection() { return this.getProperty("_selection") }
  set _selection(value) { this.recordChange({prop: "_selection", value}); }

  get selection() { return new TextSelection(this) }

  selectionOrLineString() {
    var sel = this.selection;
    if (sel.text) return sel.text;
    var line = string.lineIndexComputer(this.textString)(sel.start),
        [start, end] = string.lineNumberToIndexesComputer(this.textString)(line);
    return this.textString.slice(start, end).trim();
  }

  aboutToRender() {
    super.aboutToRender();
    this.fitIfNeeded();
  }

  render(renderer) {
    var tree = super.render(renderer),
        domNode = renderer.getNodeForMorph(this);
    this.selectIfNeeded(renderer);
    return tree;
  }

  shape() {
    return {
      value: this.textString,
      readOnly: this.readOnly,
      placeholder: this.placeholder,
      style: {
        resize: "none", border: 0,
       "white-space": "nowrap", padding: "0px",
       "font-family": this.fontFamily,
       "font-size": this.fontSize + "px",
       "color": String(this.fontColor)
     }
    }
  }

  fit() {
    var {fixedHeight, fixedWidth} = this;
    if (fixedHeight && fixedWidth) return;

    var {fontMetric, fontFamily, fontSize, placeholder, textString} = this,
        {height: placeholderHeight, width: placeholderWidth} = fontMetric.sizeForStr(fontFamily, fontSize, placeholder || " "),
        {height, width} = fontMetric.sizeForStr(fontFamily, fontSize, textString);
    if (!fixedHeight)
      this.height = Math.max(placeholderHeight, height);
    if (!fixedWidth)
      this.width = Math.max(placeholderWidth, width);
  }

  fitIfNeeded() {
    if (this._needsFit) { this.fit(); this._needsFit = false; }
  }

  select(renderer) {
    var domNode = renderer.getNodeForMorph(this);
    domNode && ({ start: domNode.selectionStart, end: domNode.selectionEnd } = this._selection);
  }

  selectIfNeeded(renderer) {
    if (this._needsSelect) {
      this.select(renderer);
      this._needsSelect = false;
    }
  }

  onInput(evt) {
    this.textString = evt.domEvt.target.value;
  }

  onMouseUp(evt) { this.onSelect(evt); }

  onMouseDown(evt) { this.onSelect(evt); }

  onDeselect(evt) { this.onSelect(evt) }

  onSelect(evt) {
    var { selectionStart: start, selectionEnd: end } = evt.domEvt.target;
    this._selection = { start: start, end: end };
  }

  onDeselect(evt) {
    this._selection = { start: 0, end: 0 };
  }

  onKeyUp(evt) {
    switch (evt.keyString()) {
      case 'Command-D': case 'Command-P': evt.stop(); break;
    }
  }

  async onKeyDown(evt) {
    switch (evt.keyString()) {
      case 'Command-D':
        evt.stop();
        var result = await lively.vm.runEval(this.selectionOrLineString(), {System, targetModule: "lively://test-text/1"});
        this.world()[result.isError ? "logError" : "setStatusMessage"](result.value);
        break;

      case 'Command-P':
        var sel = this.selection;
        evt.stop();
        var result = await lively.vm.runEval(this.selectionOrLineString(), {System, targetModule: "lively://test-text/1"});
        this.textString = this.textString.slice(0, sel.end) + result.value + this.textString.slice(sel.end);
        break;
    }
  }

}

class TextSelection {

  constructor(textMorph) {
    this.textMorph = textMorph;
  }

  get start() { return this.textMorph._selection.start }
  set start(val) {
    let morph = this.textMorph;
    morph._selection = { start: val, end: this.end };
    morph._needsSelect = true;
  }

  get end() { return this.textMorph._selection.end }
  set end(val) {
    let morph = this.textMorph;
    morph._selection = { start: this.start, end: val };
    morph._needsSelect = true;
  }

  get text() { return this.textMorph.textString.substring(this.start, this.end) }
  set text(val) {
    var oldText = this.textMorph.textString,
        newText = oldText.substr(0, this.start) + val + oldText.substr(this.end);
    this.textMorph.textString = newText;
  }
}
<commit_msg>Text: default font family/size, clean up render()<commit_after>import { string } from "lively.lang";
import { Color, pr } from "lively.graphics";
import { Morph, show } from "./index.js";
import { FontMetric } from "./rendering/renderer.js";

export class Text extends Morph {

  static makeLabel(string, props) {
    return new Text({
      textString: string,
      fontFamily: "Helvetica Neue, Arial",
      fontColor: Color.black,
      fontSize: 11,
      readOnly: true,
      ...props
    });
  }

  constructor(props) {
    super({
      readOnly: false,
      clipMode: "hidden",
      textString: "",
      fixedWidth: false, fixedHeight: false,
      draggable: false,
      _selection: { start: 0, end: 0 },
      fontFamily: "Sans-Serif",
      fontSize: 12,
      ...props
    });
    this.fit();
    this._needsFit = false;
    this._needsSelect = false;
  }

  get fontMetric() { return FontMetric.default(); }

  get isText() { return true }

  get _nodeType() { return 'textarea'; }

  get textString() { return this.getProperty("textString") }
  set textString(value) {
    this.recordChange({prop: "textString", value: String(value)});
    this._needsFit = true;
  }

  get readOnly() { return this.getProperty("readOnly"); }
  set readOnly(value) {
    this.nativeCursor = value ? "default" : "auto";
    this.recordChange({prop: "readOnly", value});
  }

  get fixedWidth() { return this.getProperty("fixedWidth") }
  set fixedWidth(value) {
    this.recordChange({prop: "fixedWidth", value});
    this._needsFit = true;
  }

  get fixedHeight() { return this.getProperty("fixedHeight") }
  set fixedHeight(value) {
    this.recordChange({prop: "fixedHeight", value});
    this._needsFit = true;
  }

  get fontFamily() { return this.getProperty("fontFamily") }
  set fontFamily(value) {
    this.recordChange({prop: "fontFamily", value});
    this._needsFit = true;
  }

  get fontSize() { return this.getProperty("fontSize") }
  set fontSize(value) {
    this.recordChange({prop: "fontSize", value});
    this._needsFit = true;
  }

  get fontColor() { return this.getProperty("fontColor") }
  set fontColor(value) {
    this.recordChange({prop: "fontColor", value});
  }

  get placeholder() { return this.getProperty("placeholder") }
  set placeholder(value) {
    this.recordChange({prop: "placeholder", value});
    this._needsFit = true;
  }

  get _selection() { return this.getProperty("_selection") }
  set _selection(value) { this.recordChange({prop: "_selection", value}); }

  get selection() { return new TextSelection(this) }

  selectionOrLineString() {
    var sel = this.selection;
    if (sel.text) return sel.text;
    var line = string.lineIndexComputer(this.textString)(sel.start),
        [start, end] = string.lineNumberToIndexesComputer(this.textString)(line);
    return this.textString.slice(start, end).trim();
  }

  aboutToRender() {
    super.aboutToRender();
    this.fitIfNeeded();
  }

  render(renderer) {
    this.selectIfNeeded(renderer);
    return super.render(renderer);
  }

  shape() {
    return {
      value: this.textString,
      readOnly: this.readOnly,
      placeholder: this.placeholder,
      style: {
        resize: "none", border: 0,
       "white-space": "nowrap", padding: "0px",
       "font-family": this.fontFamily,
       "font-size": this.fontSize + "px",
       "color": String(this.fontColor)
     }
    }
  }

  fit() {
    var {fixedHeight, fixedWidth} = this;
    if (fixedHeight && fixedWidth) return;

    var {fontMetric, fontFamily, fontSize, placeholder, textString} = this,
        {height: placeholderHeight, width: placeholderWidth} = fontMetric.sizeForStr(fontFamily, fontSize, placeholder || " "),
        {height, width} = fontMetric.sizeForStr(fontFamily, fontSize, textString);
    if (!fixedHeight)
      this.height = Math.max(placeholderHeight, height);
    if (!fixedWidth)
      this.width = Math.max(placeholderWidth, width);
  }

  fitIfNeeded() {
    if (this._needsFit) { this.fit(); this._needsFit = false; }
  }

  select(renderer) {
    var domNode = renderer.getNodeForMorph(this);
    domNode && ({ start: domNode.selectionStart, end: domNode.selectionEnd } = this._selection);
  }

  selectIfNeeded(renderer) {
    if (this._needsSelect) {
      this.select(renderer);
      this._needsSelect = false;
    }
  }

  onInput(evt) {
    this.textString = evt.domEvt.target.value;
  }

  onMouseUp(evt) { this.onSelect(evt); }

  onMouseDown(evt) { this.onSelect(evt); }

  onDeselect(evt) { this.onSelect(evt) }

  onSelect(evt) {
    var { selectionStart: start, selectionEnd: end } = evt.domEvt.target;
    this._selection = { start: start, end: end };
  }

  onDeselect(evt) {
    this._selection = { start: 0, end: 0 };
  }

  onKeyUp(evt) {
    switch (evt.keyString()) {
      case 'Command-D': case 'Command-P': evt.stop(); break;
    }
  }

  async onKeyDown(evt) {
    switch (evt.keyString()) {
      case 'Command-D':
        evt.stop();
        var result = await lively.vm.runEval(this.selectionOrLineString(), {System, targetModule: "lively://test-text/1"});
        this.world()[result.isError ? "logError" : "setStatusMessage"](result.value);
        break;

      case 'Command-P':
        var sel = this.selection;
        evt.stop();
        var result = await lively.vm.runEval(this.selectionOrLineString(), {System, targetModule: "lively://test-text/1"});
        this.textString = this.textString.slice(0, sel.end) + result.value + this.textString.slice(sel.end);
        break;
    }
  }

}

class TextSelection {

  constructor(textMorph) {
    this.textMorph = textMorph;
  }

  get start() { return this.textMorph._selection.start }
  set start(val) {
    let morph = this.textMorph;
    morph._selection = { start: val, end: this.end };
    morph._needsSelect = true;
  }

  get end() { return this.textMorph._selection.end }
  set end(val) {
    let morph = this.textMorph;
    morph._selection = { start: this.start, end: val };
    morph._needsSelect = true;
  }

  get text() { return this.textMorph.textString.substring(this.start, this.end) }
  set text(val) {
    var oldText = this.textMorph.textString,
        newText = oldText.substr(0, this.start) + val + oldText.substr(this.end);
    this.textMorph.textString = newText;
  }
}
<|endoftext|>