<commit_before>var CoreView = require('backbone/core-view');
var $ = require('jquery');
var _ = require('underscore');
var Clipboard = require('clipboard');
var browserDetect = require('../../../helpers/browser-detect');
var TableBodyRowView = require('./table-body-row-view');
var ContextMenuView = require('../../context-menu/context-menu-view');
var CustomListCollection = require('../../custom-list/custom-list-collection');
var addTableRowOperation = require('../operations/table-add-row');
var removeTableRowOperation = require('../operations/table-remove-row');
var editCellOperation = require('../operations/table-edit-cell');
var ConfirmationModalView = require('../../modals/confirmation/modal-confirmation-view');
var TablePaginatorView = require('../paginator/table-paginator-view');
var tableBodyTemplate = require('./table-body.tpl');
var renderLoading = require('../../loading/render-loading');
var ErrorView = require('../../error/error-view');
var tableNoRowsTemplate = require('./table-no-rows.tpl');
var EditorsServiceModel = require('../editors/editors-service-model');
var EditorsModel = require('../editors/types/editor-model');
var errorParser = require('../../../helpers/error-parser');

var EDITORS_MAP = {
  'string': require('../editors/types/editor-string-view'),
  'number': require('../editors/types/editor-base-view'),
  'boolean': require('../editors/types/editor-boolean-view'),
  'date': require('../editors/types/editor-date-view'),
  'default': require('../editors/types/editor-string-view')
};

/*
 *  Table body view
 */

module.exports = CoreView.extend({

  className: 'Table-body',
  tagName: 'div',

  events: {
    'click .js-options': '_showContextMenu',
    'dblclick .js-value': '_onDblClickValue'
  },

  initialize: function (opts) {
    if (!opts.queryGeometryModel) throw new Error('queryGeometryModel is required');
    if (!opts.querySchemaModel) throw new Error('querySchemaModel is required');
    if (!opts.rowsCollection) throw new Error('rowsCollection is required');
    if (!opts.columnsCollection) throw new Error('columnsCollection is required');
    if (!opts.tableViewModel) throw new Error('tableViewModel');
    if (!opts.modals) throw new Error('modals is required');

    this._modals = opts.modals;
    this._tableViewModel = opts.tableViewModel;
    this._queryGeometryModel = opts.queryGeometryModel;
    this._querySchemaModel = opts.querySchemaModel;
    this._columnsCollection = opts.columnsCollection;
    this._rowsCollection = opts.rowsCollection;
    this._editors = new EditorsServiceModel();

    this._closeEditor = this._closeEditor.bind(this);
    this._hideContextMenu = this._hideContextMenu.bind(this);

    this._initBinds();
  },

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

    // Render results when we have the schema and goemetry is not being fetched
    if (this._querySchemaModel.isFetched() && !this._queryGeometryModel.isFetching() && !this._rowsCollection.isFetching()) {
      if (!this._rowsCollection.size()) {
        this._renderNoRows();
      } else {
        this.$el.html(tableBodyTemplate());
        this._rowsCollection.each(this._renderBodyRow, this);
        this._initPaginator();
      }
    } else {
      this._renderQueryState();
    }

    this.$el.toggleClass('Table-body--relative', !!this._tableViewModel.get('relativePositionated'));

    return this;
  },

  _initBinds: function () {
    this._queryGeometryModel.bind('change:status', this.render, this);
    this._querySchemaModel.bind('change:status', this.render, this);
    this._rowsCollection.bind('reset', _.debounce(this.render.bind(this), 20), this);
    this._rowsCollection.bind('add', function (model) {
      if (this._rowsCollection.size() === 1) {
        this.render();
      } else {
        this._renderBodyRow(model);
        this._scrollToBottom();
      }
    }, this);
    this._rowsCollection.bind('remove', this._onRemoveRow, this);
    this._rowsCollection.bind('fail', function (mdl, response) {
      this._renderError(errorParser(response));
    }, this);
    this.add_related_model(this._queryGeometryModel);
    this.add_related_model(this._querySchemaModel);
    this.add_related_model(this._rowsCollection);
  },

  _renderQueryState: function () {
    var querySchemaStatus = this._querySchemaModel.get('status');
    var nodeReady = this._querySchemaModel.get('ready');
    var geometryStatus = this._queryGeometryModel.get('status');

    if (nodeReady) {
      if (querySchemaStatus === 'unavailable' && geometryStatus === 'unavailable') {
        this._renderError(this._querySchemaModel.get('query_errors'));
      } else {
        this._renderLoading();
      }
    } else {
      this._renderLoading();
    }
  },

  _renderLoading: function () {
    this.$el.html(
      renderLoading({
        title: _t('components.table.rows.loading.title')
      })
    );
  },

  _renderError: function (desc) {
    var view = new ErrorView({
      title: _t('components.table.rows.error.title'),
      desc: desc || _t('components.table.rows.error.desc')
    });
    this.addView(view);
    this.$el.html(view.render().el);
  },

  _renderNoRows: function () {
    this.$el.html(
      tableNoRowsTemplate({
        page: this._tableViewModel.get('page'),
        customQuery: this._tableViewModel.isCustomQueryApplied()
      })
    );
  },

  _initPaginator: function () {
    var paginatorView = new TablePaginatorView({
      rowsCollection: this._rowsCollection,
      tableViewModel: this._tableViewModel
    });

    // Bug in Chrome with position:fixed :(, so we have to choose body as
    // parent
    var $el = $('body');
    // But if we have chosen relativePositionated, we should add close
    // to the table view
    if (this._tableViewModel.get('relativePositionated')) {
      $el = this.$el.closest('.Table').parent();
    }

    $el.append(paginatorView.render().el);
    this.addView(paginatorView);
  },

  _initScrollBinding: function () {
    $('.Table').scroll(this._hideContextMenu);
    this.$('.js-tbody').scroll(this._hideContextMenu);
  },

  _destroyScrollBinding: function () {
    $('.Table').off('scroll', this._hideContextMenu);
    this.$('.js-tbody').off('scroll', this._hideContextMenu);
  },

  _renderBodyRow: function (mdl) {
    var view = new TableBodyRowView({
      model: mdl,
      columnsCollection: this._columnsCollection,
      simpleGeometry: this._queryGeometryModel.get('simple_geom'),
      tableViewModel: this._tableViewModel
    });
    this.addView(view);
    this.$('.js-tbody').append(view.render().el);
  },

  _onRemoveRow: function () {
    if (!this._rowsCollection.size()) {
      var page = this._tableViewModel.get('page');
      if (page > 0) {
        this._tableViewModel.set('page', page - 1);
      } else {
        this.render();
      }
    }
  },

  _hasContextMenu: function () {
    return this._menuView;
  },

  _hideContextMenu: function () {
    this._unhighlightCell();
    this._destroyScrollBinding();
    this._menuView.collection.unbind(null, null, this);
    this._menuView.remove();
    this.removeView(this._menuView);
    delete this._menuView;
  },

  _highlightCell: function ($tableCellItem, $tableRow) {
    $tableCellItem.addClass('is-highlighted');
    $tableRow.addClass('is-highlighted');
  },

  _unhighlightCell: function () {
    this.$('.Table-cellItem.is-highlighted, .Table-row.is-highlighted').removeClass('is-highlighted');
  },

  _showContextMenu: function (ev) {
    var self = this;
    var position = { x: ev.clientX, y: ev.clientY };
    var $tableRow = $(ev.target).closest('.Table-row');
    var $tableCellItem = $(ev.target).closest('.Table-cellItem');
    var modelCID = $tableRow.data('model');
    var attribute = $tableCellItem.data('attribute');
    var rowModel = self._rowsCollection.get({ cid: modelCID });
    var browser = browserDetect();
    var menuItems = [];

    if (browser.name !== 'Safari') {
      menuItems.push({
        label: _t('components.table.rows.options.copy'),
        val: 'copy',
        action: function () {
          self._copyValue($tableCellItem);
        }
      });
    }

    if (!this._tableViewModel.isDisabled()) {
      menuItems = [
        {
          label: _t('components.table.rows.options.edit'),
          val: 'edit',
          action: function () {
            self._editCell(rowModel, attribute);
          }
        }, {
          label: _t('components.table.rows.options.create'),
          val: 'create',
          action: function () {
            self._addRow();
          }
        }
      ].concat(menuItems);

      menuItems.push({
        label: _t('components.table.rows.options.delete'),
        val: 'delete',
        destructive: true,
        action: function () {
          self._removeRow(rowModel);
        }
      });
    }

    // No options?, don't open anything
    if (!menuItems.length) {
      return false;
    }

    var collection = new CustomListCollection(menuItems);

    this._menuView = new ContextMenuView({
      className: 'Table-rowMenu ' + ContextMenuView.prototype.className,
      collection: collection,
      triggerElementID: modelCID,
      position: position
    });

    // Show options up
    if ($tableRow.index() > 36) {
      this._menuView.$el.css({
        'margin-top': -((collection.size() * 40) + 35) + 'px'
      });
    }

    collection.bind('change:selected', function (menuItem) {
      var action = menuItem.get('action');
      action && action();
    }, this);

    this._menuView.model.bind('change:visible', function (model, isContextMenuVisible) {
      if (this._hasContextMenu() && !isContextMenuVisible) {
        this._hideContextMenu();
      }
    }, this);

    this._menuView.show();
    this.addView(this._menuView);

    this._highlightCell($tableCellItem, $tableRow);
    this._initScrollBinding();
  },

  _onDblClickValue: function (ev) {
    var $tableCellItem = $(ev.target).closest('.Table-cellItem');
    var $tableRow = $tableCellItem.closest('.Table-row');
    var modelCID = $tableRow.data('model');
    var attribute = $tableCellItem.data('attribute');
    var rowModel = this._rowsCollection.get({ cid: modelCID });

    if (!this._tableViewModel.isDisabled() && rowModel && attribute && attribute !== 'cartodb_id') {
      this._editCell(rowModel, attribute);
    }
  },

  _copyValue: function ($el) {
    // Work-around for Clipboard \o/
    this._clipboard = new Clipboard($el.get(0));
    $el.click();
    this._clipboard.destroy();
  },

  _addRow: function () {
    addTableRowOperation({
      rowsCollection: this._rowsCollection
    });
  },

  _initEditorScrollBinding: function () {
    $('.Table').scroll(this._closeEditor);
    this.$('.js-tbody').scroll(this._closeEditor);
  },

  _destroyEditorScrollBinding: function () {
    $('.Table').unbind('scroll', this._closeEditor);
    this.$('.js-tbody').unbind('scroll', this._closeEditor);
  },

  _closeEditor: function () {
    this._unhighlightCell();
    this._destroyEditorScrollBinding();
    this._editors.unbind(null, null, this);
    this._editors.destroy();
  },

  _saveValue: function (rowModel, attribute, newValue) {
    if (rowModel.get(attribute) !== newValue) {
      editCellOperation({
        rowModel: rowModel,
        attribute: attribute,
        newValue: newValue
      });
    }
  },

  _doCellEdition: function (rowModel, attribute) {
    var $tableRow = this.$('[data-model="' + rowModel.cid + '"]');
    var $tableCell = $tableRow.find('[data-attribute="' + attribute + '"]');
    var $options = $tableCell.find('.js-options');
    var columnModel = _.first(this._columnsCollection.where({ name: attribute }));
    var type = columnModel.get('type');

    this._highlightCell($tableCell, $tableRow);
    this._initEditorScrollBinding();

    var model = new EditorsModel({
      type: type,
      value: rowModel.get(attribute)
    });

    this._editors.bind('destroyedEditor', this._closeEditor, this);
    this._editors.bind('confirmedEditor', function () {
      if (model.isValid()) {
        this._saveValue(
          rowModel,
          attribute,
          model.get('value')
        );
      }
    }, this);

    var position = $options.offset();

    if ($tableCell.index() > 1) {
      position.right = window.innerWidth - position.left;
      delete position.left;
    }

    if (this._rowsCollection.size() > 4 && ($tableRow.index() + 2) >= (this._rowsCollection.size() - 1)) {
      position.bottom = window.innerHeight - position.top;
      delete position.top;
    } else {
      position.top = position.top + 20;
    }

    var View = EDITORS_MAP[type];

    if (!View) {
      View = EDITORS_MAP['default'];
    }

    this._editors.create(
      function (editorModel) {
        return new View({
          editorModel: editorModel,
          model: model
        });
      },
      position
    );
  },

  _editCell: function (rowModel, attribute) {
    var callback = this._doCellEdition.bind(this, rowModel, attribute);
    rowModel.fetchRowIfGeomIsNotLoaded(callback);
  },

  _removeRow: function (rowModel) {
    var self = this;

    this._modals.create(
      function (modalModel) {
        return new ConfirmationModalView({
          modalModel: modalModel,
          template: require('./modals-templates/remove-table-row.tpl'),
          renderOpts: {
            cartodb_id: rowModel.get('cartodb_id')
          },
          loadingTitle: _t('components.table.rows.destroy.loading', {
            cartodb_id: rowModel.get('cartodb_id')
          }),
          runAction: function () {
            removeTableRowOperation({
              tableViewModel: self._tableViewModel,
              rowModel: rowModel,
              onSuccess: function () {
                modalModel.destroy();
              },
              onError: function (e) {
                modalModel.destroy();
              }
            });
          }
        });
      }
    );
  },

  _scrollToBottom: function () {
    var tbodyHeight = this.$('.js-tbody').get(0).scrollHeight;
    this.$('.js-tbody').animate({
      scrollTop: tbodyHeight
    }, 'slow');
  },

  clean: function () {
    this._destroyScrollBinding();
    CoreView.prototype.clean.apply(this);
  }

});
<commit_msg>Only show error when it is not aborted<commit_after>var CoreView = require('backbone/core-view');
var $ = require('jquery');
var _ = require('underscore');
var Clipboard = require('clipboard');
var browserDetect = require('../../../helpers/browser-detect');
var TableBodyRowView = require('./table-body-row-view');
var ContextMenuView = require('../../context-menu/context-menu-view');
var CustomListCollection = require('../../custom-list/custom-list-collection');
var addTableRowOperation = require('../operations/table-add-row');
var removeTableRowOperation = require('../operations/table-remove-row');
var editCellOperation = require('../operations/table-edit-cell');
var ConfirmationModalView = require('../../modals/confirmation/modal-confirmation-view');
var TablePaginatorView = require('../paginator/table-paginator-view');
var tableBodyTemplate = require('./table-body.tpl');
var renderLoading = require('../../loading/render-loading');
var ErrorView = require('../../error/error-view');
var tableNoRowsTemplate = require('./table-no-rows.tpl');
var EditorsServiceModel = require('../editors/editors-service-model');
var EditorsModel = require('../editors/types/editor-model');
var errorParser = require('../../../helpers/error-parser');

var EDITORS_MAP = {
  'string': require('../editors/types/editor-string-view'),
  'number': require('../editors/types/editor-base-view'),
  'boolean': require('../editors/types/editor-boolean-view'),
  'date': require('../editors/types/editor-date-view'),
  'default': require('../editors/types/editor-string-view')
};

/*
 *  Table body view
 */

module.exports = CoreView.extend({

  className: 'Table-body',
  tagName: 'div',

  events: {
    'click .js-options': '_showContextMenu',
    'dblclick .js-value': '_onDblClickValue'
  },

  initialize: function (opts) {
    if (!opts.queryGeometryModel) throw new Error('queryGeometryModel is required');
    if (!opts.querySchemaModel) throw new Error('querySchemaModel is required');
    if (!opts.rowsCollection) throw new Error('rowsCollection is required');
    if (!opts.columnsCollection) throw new Error('columnsCollection is required');
    if (!opts.tableViewModel) throw new Error('tableViewModel');
    if (!opts.modals) throw new Error('modals is required');

    this._modals = opts.modals;
    this._tableViewModel = opts.tableViewModel;
    this._queryGeometryModel = opts.queryGeometryModel;
    this._querySchemaModel = opts.querySchemaModel;
    this._columnsCollection = opts.columnsCollection;
    this._rowsCollection = opts.rowsCollection;
    this._editors = new EditorsServiceModel();

    this._closeEditor = this._closeEditor.bind(this);
    this._hideContextMenu = this._hideContextMenu.bind(this);

    this._initBinds();
  },

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

    // Render results when we have the schema and goemetry is not being fetched
    if (this._querySchemaModel.isFetched() && !this._queryGeometryModel.isFetching() && !this._rowsCollection.isFetching()) {
      if (!this._rowsCollection.size()) {
        this._renderNoRows();
      } else {
        this.$el.html(tableBodyTemplate());
        this._rowsCollection.each(this._renderBodyRow, this);
        this._initPaginator();
      }
    } else {
      this._renderQueryState();
    }

    this.$el.toggleClass('Table-body--relative', !!this._tableViewModel.get('relativePositionated'));

    return this;
  },

  _initBinds: function () {
    this._queryGeometryModel.bind('change:status', this.render, this);
    this._querySchemaModel.bind('change:status', this.render, this);
    this._rowsCollection.bind('reset', _.debounce(this.render.bind(this), 20), this);
    this._rowsCollection.bind('add', function (model) {
      if (this._rowsCollection.size() === 1) {
        this.render();
      } else {
        this._renderBodyRow(model);
        this._scrollToBottom();
      }
    }, this);
    this._rowsCollection.bind('remove', this._onRemoveRow, this);
    this._rowsCollection.bind('fail', function (mdl, response) {
      if (!response || (response && response.statusText !== 'abort')) {
        this._renderError(errorParser(response));
      }
    }, this);
    this.add_related_model(this._queryGeometryModel);
    this.add_related_model(this._querySchemaModel);
    this.add_related_model(this._rowsCollection);
  },

  _renderQueryState: function () {
    var querySchemaStatus = this._querySchemaModel.get('status');
    var nodeReady = this._querySchemaModel.get('ready');
    var geometryStatus = this._queryGeometryModel.get('status');

    if (nodeReady) {
      if (querySchemaStatus === 'unavailable' && geometryStatus === 'unavailable') {
        this._renderError(this._querySchemaModel.get('query_errors'));
      } else {
        this._renderLoading();
      }
    } else {
      this._renderLoading();
    }
  },

  _renderLoading: function () {
    this.$el.html(
      renderLoading({
        title: _t('components.table.rows.loading.title')
      })
    );
  },

  _renderError: function (desc) {
    var view = new ErrorView({
      title: _t('components.table.rows.error.title'),
      desc: desc || _t('components.table.rows.error.desc')
    });
    this.addView(view);
    this.$el.html(view.render().el);
  },

  _renderNoRows: function () {
    this.$el.html(
      tableNoRowsTemplate({
        page: this._tableViewModel.get('page'),
        customQuery: this._tableViewModel.isCustomQueryApplied()
      })
    );
  },

  _initPaginator: function () {
    var paginatorView = new TablePaginatorView({
      rowsCollection: this._rowsCollection,
      tableViewModel: this._tableViewModel
    });

    // Bug in Chrome with position:fixed :(, so we have to choose body as
    // parent
    var $el = $('body');
    // But if we have chosen relativePositionated, we should add close
    // to the table view
    if (this._tableViewModel.get('relativePositionated')) {
      $el = this.$el.closest('.Table').parent();
    }

    $el.append(paginatorView.render().el);
    this.addView(paginatorView);
  },

  _initScrollBinding: function () {
    $('.Table').scroll(this._hideContextMenu);
    this.$('.js-tbody').scroll(this._hideContextMenu);
  },

  _destroyScrollBinding: function () {
    $('.Table').off('scroll', this._hideContextMenu);
    this.$('.js-tbody').off('scroll', this._hideContextMenu);
  },

  _renderBodyRow: function (mdl) {
    var view = new TableBodyRowView({
      model: mdl,
      columnsCollection: this._columnsCollection,
      simpleGeometry: this._queryGeometryModel.get('simple_geom'),
      tableViewModel: this._tableViewModel
    });
    this.addView(view);
    this.$('.js-tbody').append(view.render().el);
  },

  _onRemoveRow: function () {
    if (!this._rowsCollection.size()) {
      var page = this._tableViewModel.get('page');
      if (page > 0) {
        this._tableViewModel.set('page', page - 1);
      } else {
        this.render();
      }
    }
  },

  _hasContextMenu: function () {
    return this._menuView;
  },

  _hideContextMenu: function () {
    this._unhighlightCell();
    this._destroyScrollBinding();
    this._menuView.collection.unbind(null, null, this);
    this._menuView.remove();
    this.removeView(this._menuView);
    delete this._menuView;
  },

  _highlightCell: function ($tableCellItem, $tableRow) {
    $tableCellItem.addClass('is-highlighted');
    $tableRow.addClass('is-highlighted');
  },

  _unhighlightCell: function () {
    this.$('.Table-cellItem.is-highlighted, .Table-row.is-highlighted').removeClass('is-highlighted');
  },

  _showContextMenu: function (ev) {
    var self = this;
    var position = { x: ev.clientX, y: ev.clientY };
    var $tableRow = $(ev.target).closest('.Table-row');
    var $tableCellItem = $(ev.target).closest('.Table-cellItem');
    var modelCID = $tableRow.data('model');
    var attribute = $tableCellItem.data('attribute');
    var rowModel = self._rowsCollection.get({ cid: modelCID });
    var browser = browserDetect();
    var menuItems = [];

    if (browser.name !== 'Safari') {
      menuItems.push({
        label: _t('components.table.rows.options.copy'),
        val: 'copy',
        action: function () {
          self._copyValue($tableCellItem);
        }
      });
    }

    if (!this._tableViewModel.isDisabled()) {
      menuItems = [
        {
          label: _t('components.table.rows.options.edit'),
          val: 'edit',
          action: function () {
            self._editCell(rowModel, attribute);
          }
        }, {
          label: _t('components.table.rows.options.create'),
          val: 'create',
          action: function () {
            self._addRow();
          }
        }
      ].concat(menuItems);

      menuItems.push({
        label: _t('components.table.rows.options.delete'),
        val: 'delete',
        destructive: true,
        action: function () {
          self._removeRow(rowModel);
        }
      });
    }

    // No options?, don't open anything
    if (!menuItems.length) {
      return false;
    }

    var collection = new CustomListCollection(menuItems);

    this._menuView = new ContextMenuView({
      className: 'Table-rowMenu ' + ContextMenuView.prototype.className,
      collection: collection,
      triggerElementID: modelCID,
      position: position
    });

    // Show options up
    if ($tableRow.index() > 36) {
      this._menuView.$el.css({
        'margin-top': -((collection.size() * 40) + 35) + 'px'
      });
    }

    collection.bind('change:selected', function (menuItem) {
      var action = menuItem.get('action');
      action && action();
    }, this);

    this._menuView.model.bind('change:visible', function (model, isContextMenuVisible) {
      if (this._hasContextMenu() && !isContextMenuVisible) {
        this._hideContextMenu();
      }
    }, this);

    this._menuView.show();
    this.addView(this._menuView);

    this._highlightCell($tableCellItem, $tableRow);
    this._initScrollBinding();
  },

  _onDblClickValue: function (ev) {
    var $tableCellItem = $(ev.target).closest('.Table-cellItem');
    var $tableRow = $tableCellItem.closest('.Table-row');
    var modelCID = $tableRow.data('model');
    var attribute = $tableCellItem.data('attribute');
    var rowModel = this._rowsCollection.get({ cid: modelCID });

    if (!this._tableViewModel.isDisabled() && rowModel && attribute && attribute !== 'cartodb_id') {
      this._editCell(rowModel, attribute);
    }
  },

  _copyValue: function ($el) {
    // Work-around for Clipboard \o/
    this._clipboard = new Clipboard($el.get(0));
    $el.click();
    this._clipboard.destroy();
  },

  _addRow: function () {
    addTableRowOperation({
      rowsCollection: this._rowsCollection
    });
  },

  _initEditorScrollBinding: function () {
    $('.Table').scroll(this._closeEditor);
    this.$('.js-tbody').scroll(this._closeEditor);
  },

  _destroyEditorScrollBinding: function () {
    $('.Table').unbind('scroll', this._closeEditor);
    this.$('.js-tbody').unbind('scroll', this._closeEditor);
  },

  _closeEditor: function () {
    this._unhighlightCell();
    this._destroyEditorScrollBinding();
    this._editors.unbind(null, null, this);
    this._editors.destroy();
  },

  _saveValue: function (rowModel, attribute, newValue) {
    if (rowModel.get(attribute) !== newValue) {
      editCellOperation({
        rowModel: rowModel,
        attribute: attribute,
        newValue: newValue
      });
    }
  },

  _doCellEdition: function (rowModel, attribute) {
    var $tableRow = this.$('[data-model="' + rowModel.cid + '"]');
    var $tableCell = $tableRow.find('[data-attribute="' + attribute + '"]');
    var $options = $tableCell.find('.js-options');
    var columnModel = _.first(this._columnsCollection.where({ name: attribute }));
    var type = columnModel.get('type');

    this._highlightCell($tableCell, $tableRow);
    this._initEditorScrollBinding();

    var model = new EditorsModel({
      type: type,
      value: rowModel.get(attribute)
    });

    this._editors.bind('destroyedEditor', this._closeEditor, this);
    this._editors.bind('confirmedEditor', function () {
      if (model.isValid()) {
        this._saveValue(
          rowModel,
          attribute,
          model.get('value')
        );
      }
    }, this);

    var position = $options.offset();

    if ($tableCell.index() > 1) {
      position.right = window.innerWidth - position.left;
      delete position.left;
    }

    if (this._rowsCollection.size() > 4 && ($tableRow.index() + 2) >= (this._rowsCollection.size() - 1)) {
      position.bottom = window.innerHeight - position.top;
      delete position.top;
    } else {
      position.top = position.top + 20;
    }

    var View = EDITORS_MAP[type];

    if (!View) {
      View = EDITORS_MAP['default'];
    }

    this._editors.create(
      function (editorModel) {
        return new View({
          editorModel: editorModel,
          model: model
        });
      },
      position
    );
  },

  _editCell: function (rowModel, attribute) {
    var callback = this._doCellEdition.bind(this, rowModel, attribute);
    rowModel.fetchRowIfGeomIsNotLoaded(callback);
  },

  _removeRow: function (rowModel) {
    var self = this;

    this._modals.create(
      function (modalModel) {
        return new ConfirmationModalView({
          modalModel: modalModel,
          template: require('./modals-templates/remove-table-row.tpl'),
          renderOpts: {
            cartodb_id: rowModel.get('cartodb_id')
          },
          loadingTitle: _t('components.table.rows.destroy.loading', {
            cartodb_id: rowModel.get('cartodb_id')
          }),
          runAction: function () {
            removeTableRowOperation({
              tableViewModel: self._tableViewModel,
              rowModel: rowModel,
              onSuccess: function () {
                modalModel.destroy();
              },
              onError: function (e) {
                modalModel.destroy();
              }
            });
          }
        });
      }
    );
  },

  _scrollToBottom: function () {
    var tbodyHeight = this.$('.js-tbody').get(0).scrollHeight;
    this.$('.js-tbody').animate({
      scrollTop: tbodyHeight
    }, 'slow');
  },

  clean: function () {
    this._destroyScrollBinding();
    CoreView.prototype.clean.apply(this);
  }

});
<|endoftext|>


<commit_before>// ==UserScript==
// @name		FBHack
// @version		2015.05.16
// @description		FB Hackathon
// @author		victorjtfranco
// @namespace		https://github.com/victorjtfranco
// @icon		http://s3.amazonaws.com/uso_ss/icon/87011/large.png
// @include		http://facebook.com*
// @include		http://www.facebook.com*
// @include		https://facebook.com*
// @include		https://www.facebook.com*
// @grant		GM_xmlhttpRequest
// @grant		GM_setValue
// @grant		GM_getValue
// @require     http://popcornjs.org/code/dist/popcorn-complete.min.js
// ==/UserScript==

(function() {

  var userscript = 'FBHack';
  console.debug("FBHack extension");

  try{
    if (window.top != window.self) return;
    if (!window.location.href.match('facebook.com/(video.php|.*/videos/)')) return;

    var flashvars = {};
    var hackvideo = {};

    function replace_video(){
      console.debug("FBHack replace_video");
      flashvars.s = document.querySelectorAll(".stageWrapper embed").item(0).getAttribute("flashvars");
      flashvars.params = JSON.parse(decodeURIComponent(flashvars.s.split("&")[0].slice(7)));
      flashvars.other = flashvars.s.split("&").slice(1).map(function(x){return x.split("=")}).reduce(function(p,c,i,a){p[c[0]]=c[1]; return p;},{});

      hackvideo.sd_src = flashvars.params.video_data[0].sd_src;
      hackvideo.hd_src = flashvars.params.video_data[0].hd_src;
      if(hackvideo.sd_src != null) hackvideo.video_src = hackvideo.sd_src;
      if(hackvideo.hd_src != null) hackvideo.video_src = hackvideo.hd_src;

      hackvideo.old_videoel = document.querySelectorAll("#"+flashvars.other.div_id)[0];
      hackvideo.new_videoel_html =
        '<video id="hackvideo" '+
          'style="position: absolute; top: 0px; left: 0px; height: '+flashvars.other.height+'px; width: '+flashvars.other.width+'px"'+
          'controls'+
        '>'+
          '<source src="'+hackvideo.video_src+'" type=\'video/mp4; codecs="avc1.42E01E, mp4a.40.2"\' />'+
        '</video>';
      hackvideo.old_videoel.insertAdjacentHTML('beforebegin',hackvideo.new_videoel_html);
      hackvideo.old_videoel.hidden = true;

      hackvideo.header = document.querySelectorAll("#fbPhotoPageHeader>div:nth-child(2)");
      if(hackvideo.header.length !== 0) {
        hackvideo.header = hackvideo.header[0];
        hackvideo.header_newhtml =
        '
'+
          //'Download'+
          //'

'+
          'Watch in sync'+
        '
';
        hackvideo.header.insertAdjacentHTML('beforeend',hackvideo.header_newhtml);
        console.debug("FBHack header");

        document.querySelectorAll("#sync_watch_button")[0].addEventListener('click',
          function(){
            // Helper function to get elements
            var ready01 = false,
                ready02 = false,
                url = "http://hackvideo.herokuapp.com";

            function element(id) {
              return document.getElementById(id);
            }

              var paused = true,
                  right = false,
                  from_play = false;
                  from_ended = false;
                  c_id = 0,
                  user = 3;
                  popcorn = Popcorn("#hackvideo");

              popcorn = popcorn
               .on('timeupdate', function() {
                 console.log("timeupdate");
               })
               .on('ended', function() {
                 console.log("ended");
                 from_ended = true;
               })

              .on('play', function() {
                console.debug("play");
                var request = new XMLHttpRequest();

                request.open("POST", url+'/play', true);

                console.log(request.readyState + " & " + request.status);

                request.onreadystatechange = function() {
                  console.log(request.readyState + " & " + request.status);
                  if (request.readyState == 0) {
                    element('status').innerHTML = "0";
                  }
                  else if (request.readyState == 1) {
                    element('status').innerHTML = "1";
                  }
                  else if (request.readyState == 2) {
                    element('status').innerHTML = "2";
                  }
                  else if (request.readyState == 3) {
                    element('status').innerHTML = "3";
                  }
                  else if (request.readyState == 4 && request.status == 200) {
                      element('status').innerHTML = "Ok";
                  }
                }

                request.onerror = function() {
                  element('status').innerHTML = "An error occurred.\nConnection timed out.";
                }

                request.setRequestHeader("Content-Type", "application/json");
                request.send(JSON.stringify({'c_id':c_id,'user':user}));

                if (!right) {
                  from_play = true;
                  popcorn.pause();
                }
              })
              .on('pause', function() {
                if(from_play){
                    from_play = false;
                    return;
                }
                var request = new XMLHttpRequest();

                request.open("POST", url+'/stop', true);

                console.log(request.readyState + " & " + request.status);

                request.onreadystatechange = function() {
                  console.log(request.readyState + " & " + request.status);
                  if (request.readyState == 0) {
                    element('status').innerHTML = "0";
                  }
                  else if (request.readyState == 1) {
                    element('status').innerHTML = "1";
                  }
                  else if (request.readyState == 2) {
                    element('status').innerHTML = "2";
                  }
                  else if (request.readyState == 3) {
                    element('status').innerHTML = "3";
                  }
                  else if (request.readyState == 4 && request.status == 200) {
                      element('status').innerHTML = "Ok";
                  }
                }

                request.onerror = function() {
                  element('status').innerHTML = "An error occurred.\nConnection timed out.";
                }

                request.setRequestHeader("Content-Type", "application/json");
                request.send(JSON.stringify({'c_id':c_id,'user':user}));

              });

              function keepAlive() {
                var request = new XMLHttpRequest();
                request.open("POST", url + "/keepalive/", true);
                request.onload = function() {
                  if (request.status == 200 && from_ended != true) {
                    right = true;
                    popcorn.play();
                  } else {
                    right = false;
                    if (from_ended) {
                      popcorn.pause(0.0);
                    }
                    else {
                      popcorn.pause();
                    }

                    from_ended = false;

                    popcorn.pause();
                  }
                }
                request.setRequestHeader("Content-Type", "application/json");
                request.send(JSON.stringify({'c_id':c_id,'user':user, 'time':popcorn.currentTime}));
                setTimeout(keepAlive,500);
              };
              keepAlive();
          }
        );

      }

      var player_el = document.createElement('script');
      player_el.setAttribute("type","text/javascript");
      player_el.innerHTML =
        "fbhack = {};\n"+
        "fbhack.load_player_js = function(){\n"+
          "console.debug('fbhack.load_player_js')\n"+
          "var player_el = document.createElement('script');\n"+
          "player_el.setAttribute(\"type\",\"text/javascript\");\n"+
          "player_el.setAttribute(\"src\", \"//hackvideo.herokuapp.com/assets/player.js\");\n"+
          "document.getElementsByTagName(\"head\")[0].appendChild(player_el);\n"+
        "}";
      document.getElementsByTagName("head")[0].appendChild(player_el);

    }

    var timeout_counter = 0;
    console.debug("FBHack extension 1");
    function timeout(){
      console.debug("FBHack extension timeout");
      setTimeout(function() {
        if(timeout_counter > 25) return;
        timeout_counter++;
        if(document.querySelectorAll(".stageWrapper embed").length !== 0){
          replace_video();
          return;
        }
        timeout();
      }, 200);
    }
    timeout();

  } catch(e) { console.log(e); }

})();
<commit_msg>Atom.io whitespaces<commit_after>// ==UserScript==
// @name		FBHack
// @version		2015.05.16
// @description		FB Hackathon
// @author		victorjtfranco
// @namespace		https://github.com/victorjtfranco
// @icon		http://s3.amazonaws.com/uso_ss/icon/87011/large.png
// @include		http://facebook.com*
// @include		http://www.facebook.com*
// @include		https://facebook.com*
// @include		https://www.facebook.com*
// @grant		GM_xmlhttpRequest
// @grant		GM_setValue
// @grant		GM_getValue
// @require     http://popcornjs.org/code/dist/popcorn-complete.min.js
// ==/UserScript==

(function() {

  var userscript = 'FBHack';
  console.debug("FBHack extension");

  try{
    if (window.top != window.self) return;
    if (!window.location.href.match('facebook.com/(video.php|.*/videos/)')) return;

    var flashvars = {};
    var hackvideo = {};

    function replace_video(){
      console.debug("FBHack replace_video");
      flashvars.s = document.querySelectorAll(".stageWrapper embed").item(0).getAttribute("flashvars");
      flashvars.params = JSON.parse(decodeURIComponent(flashvars.s.split("&")[0].slice(7)));
      flashvars.other = flashvars.s.split("&").slice(1).map(function(x){return x.split("=")}).reduce(function(p,c,i,a){p[c[0]]=c[1]; return p;},{});

      hackvideo.sd_src = flashvars.params.video_data[0].sd_src;
      hackvideo.hd_src = flashvars.params.video_data[0].hd_src;
      if(hackvideo.sd_src != null) hackvideo.video_src = hackvideo.sd_src;
      if(hackvideo.hd_src != null) hackvideo.video_src = hackvideo.hd_src;

      hackvideo.old_videoel = document.querySelectorAll("#"+flashvars.other.div_id)[0];
      hackvideo.new_videoel_html =
        '<video id="hackvideo" '+
          'style="position: absolute; top: 0px; left: 0px; height: '+flashvars.other.height+'px; width: '+flashvars.other.width+'px"'+
          'controls'+
        '>'+
          '<source src="'+hackvideo.video_src+'" type=\'video/mp4; codecs="avc1.42E01E, mp4a.40.2"\' />'+
        '</video>';
      hackvideo.old_videoel.insertAdjacentHTML('beforebegin',hackvideo.new_videoel_html);
      hackvideo.old_videoel.hidden = true;

      hackvideo.header = document.querySelectorAll("#fbPhotoPageHeader>div:nth-child(2)");
      if(hackvideo.header.length !== 0) {
        hackvideo.header = hackvideo.header[0];
        hackvideo.header_newhtml =
        '
'+
          //'Download'+
          //'

'+
          'Watch in sync'+
        '
';
        hackvideo.header.insertAdjacentHTML('beforeend',hackvideo.header_newhtml);
        console.debug("FBHack header");

        document.querySelectorAll("#sync_watch_button")[0].addEventListener('click',
          function(){
            // Helper function to get elements
            var ready01 = false,
                ready02 = false,
                url = "http://hackvideo.herokuapp.com";

            function element(id) {
              return document.getElementById(id);
            }

              var paused = true,
                  right = false,
                  from_play = false;
                  from_ended = false;
                  c_id = 0,
                  user = 3;
                  popcorn = Popcorn("#hackvideo");

              popcorn = popcorn
               .on('timeupdate', function() {
                 console.log("timeupdate");
               })
               .on('ended', function() {
                 console.log("ended");
                 from_ended = true;
               })

              .on('play', function() {
                console.debug("play");
                var request = new XMLHttpRequest();

                request.open("POST", url+'/play', true);

                console.log(request.readyState + " & " + request.status);

                request.onreadystatechange = function() {
                  console.log(request.readyState + " & " + request.status);
                  if (request.readyState == 0) {
                    element('status').innerHTML = "0";
                  }
                  else if (request.readyState == 1) {
                    element('status').innerHTML = "1";
                  }
                  else if (request.readyState == 2) {
                    element('status').innerHTML = "2";
                  }
                  else if (request.readyState == 3) {
                    element('status').innerHTML = "3";
                  }
                  else if (request.readyState == 4 && request.status == 200) {
                      element('status').innerHTML = "Ok";
                  }
                }

                request.onerror = function() {
                  element('status').innerHTML = "An error occurred.\nConnection timed out.";
                }

                request.setRequestHeader("Content-Type", "application/json");
                request.send(JSON.stringify({'c_id':c_id,'user':user}));

                if (!right) {
                  from_play = true;
                  popcorn.pause();
                }
              })
              .on('pause', function() {
                if(from_play){
                    from_play = false;
                    return;
                }
                var request = new XMLHttpRequest();

                request.open("POST", url+'/stop', true);

                console.log(request.readyState + " & " + request.status);

                request.onreadystatechange = function() {
                  console.log(request.readyState + " & " + request.status);
                  if (request.readyState == 0) {
                    element('status').innerHTML = "0";
                  }
                  else if (request.readyState == 1) {
                    element('status').innerHTML = "1";
                  }
                  else if (request.readyState == 2) {
                    element('status').innerHTML = "2";
                  }
                  else if (request.readyState == 3) {
                    element('status').innerHTML = "3";
                  }
                  else if (request.readyState == 4 && request.status == 200) {
                      element('status').innerHTML = "Ok";
                  }
                }

                request.onerror = function() {
                  element('status').innerHTML = "An error occurred.\nConnection timed out.";
                }

                request.setRequestHeader("Content-Type", "application/json");
                request.send(JSON.stringify({'c_id':c_id,'user':user}));

              });

              function keepAlive() {
                var request = new XMLHttpRequest();
                request.open("POST", url + "/keepalive/", true);
                request.onload = function() {
                  if (request.status == 200 && from_ended != true) {
                    right = true;
                    popcorn.play();
                  } else {
                    right = false;
                    if (from_ended) {
                      popcorn.pause(0.0);
                    }
                    else {
                      popcorn.pause();
                    }

                    from_ended = false;

                    popcorn.pause();
                  }
                }
                request.setRequestHeader("Content-Type", "application/json");
                request.send(JSON.stringify({'c_id':c_id,'user':user, 'time':popcorn.currentTime}));
                setTimeout(keepAlive,500);
              };
              keepAlive();
          }
        );

      }

      var player_el = document.createElement('script');
      player_el.setAttribute("type","text/javascript");
      player_el.innerHTML =
        "fbhack = {};\n"+
        "fbhack.load_player_js = function(){\n"+
          "console.debug('fbhack.load_player_js')\n"+
          "var player_el = document.createElement('script');\n"+
          "player_el.setAttribute(\"type\",\"text/javascript\");\n"+
          "player_el.setAttribute(\"src\", \"//hackvideo.herokuapp.com/assets/player.js\");\n"+
          "document.getElementsByTagName(\"head\")[0].appendChild(player_el);\n"+
        "}";
      document.getElementsByTagName("head")[0].appendChild(player_el);

    }

    var timeout_counter = 0;
    console.debug("FBHack extension 1");
    function timeout(){
      console.debug("FBHack extension timeout");
      setTimeout(function() {
        if(timeout_counter > 25) return;
        timeout_counter++;
        if(document.querySelectorAll(".stageWrapper embed").length !== 0){
          replace_video();
          return;
        }
        timeout();
      }, 200);
    }
    timeout();

  } catch(e) { console.log(e); }

})();
<|endoftext|>


<commit_before>'use strict'

describe('thresholds service', function () {
  var $rootScope
  var thresholdsService
  var angularNavDataMock // eslint-disable-line
  var testMod // eslint-disable-line

  beforeEach(function () {
    angularNavDataMock = angular.module('angularNavData', [])
      .service('lgasService', function ($q) {
        this.list = function () {
          var lga = angular.merge({ _id: 'zone:nc:state:kogi:lga:adavi' }, location)
          return $q.when([lga])
        }
      })
      .service('statesService', function ($q) {
        this.list = function () {
          var state = angular.merge({ _id: 'zone:nc:state:kogi' }, location)
          return $q.when([state])
        }
      })
    testMod = angular.module('testMod', ['angularNavData', 'angularNavThresholds'])
  })

  beforeEach(module('testMod'))

  beforeEach(inject(function (_$rootScope_, _thresholdsService_) {
    thresholdsService = _thresholdsService_
    $rootScope = _$rootScope_
  }))

  var location = {
    allocations: [
      { version: 1,
        weeklyLevels: {
          'product:a': 50,
          'product:b': 100
        }
      },
      {
        version: 2,
        weeklyLevels: {
          'product:a': 100,
          'product:b': 200
        }
      }
    ],
    targetPopulation: {
      'product:a': 1000,
      'product:b': 2000
    },
    plans: [
      {
        version: 1,
        weeksOfStock: {
          max: 5,
          reOrder: 2,
          min: 1
        }
      }
    ]
  }

  var stockCount = {
    allocations: { version: 2 },
    plans: { version: 1 }
  }

  var stockCounts = [
    // { location: { zone: 'nc' }, allocations: { version: 2 }, plans: { version: 1 } },
    { location: { zone: 'nc', state: 'kogi' }, allocations: { version: 2 }, plans: { version: 1 } },
    { location: { zone: 'nc', state: 'kogi', lga: 'adavi' } }
  ]

  describe('calculateThresholds', function () {
    it('takes a location and a stockCount and returns the min, reOrder, max thresholds', function () {
      var expected = {
        'product:a': {
          min: 100,
          reOrder: 200,
          max: 500,
          targetPopulation: 1000
        },
        'product:b': {
          min: 200,
          reOrder: 400,
          max: 1000,
          targetPopulation: 2000
        }
      }
      var actual = thresholdsService.calculateThresholds(location, stockCount)
      expect(expected).toEqual(actual)
    })
  })

  describe('getThresholdsFor', function () {
    it('takes an array of objects with location, allocations and plans fields and returns an object of location thresholds', function (done) {
      var expected = {
        // 'zone:nc': {
          // thresholds: {
            // 'product:a': { min: 100, reOrder: 200, max: 500 },
            // 'product:b': { min: 200, reOrder: 400, max: 1000 }
          // }
        // },
        'zone:nc:state:kogi': {
          allocations: { version: 2 },
          plans: { version: 1 },
          thresholds: {
            'product:a': { min: 100, reOrder: 200, max: 500, targetPopulation: 1000 },
            'product:b': { min: 200, reOrder: 400, max: 1000, targetPopulation: 2000 }
          }
        },
        'zone:nc:state:kogi:lga:adavi': {
          allocations: { version: 1 },
          plans: { version: 1 },
          thresholds: {
            'product:a': { min: 50, reOrder: 100, max: 250, targetPopulation: 1000 },
            'product:b': { min: 100, reOrder: 200, max: 500, targetPopulation: 2000 }
          }
        }
      }

      thresholdsService.getThresholdsFor(stockCounts)
        .then(function (thresholds) {
          expect(expected).toEqual(thresholds)
        })
      $rootScope.$digest()
      done()
    })
  })
})
<commit_msg>test: calculate right thresholds for zones<commit_after>'use strict'

describe('thresholds service', function () {
  var $rootScope
  var thresholdsService
  var angularNavDataMock // eslint-disable-line
  var testMod // eslint-disable-line

  beforeEach(function () {
    angularNavDataMock = angular.module('angularNavData', [])
      .service('lgasService', function ($q) {
        this.list = function () {
          var lga = angular.merge({ _id: 'zone:nc:state:kogi:lga:adavi' }, location)
          return $q.when([lga])
        }
      })
      .service('statesService', function ($q) {
        this.list = function () {
          var state = angular.merge({ _id: 'zone:nc:state:kogi' }, location)
          return $q.when([state])
        }
      })
    testMod = angular.module('testMod', ['angularNavData', 'angularNavThresholds'])
  })

  beforeEach(module('testMod'))

  beforeEach(inject(function (_$rootScope_, _thresholdsService_) {
    thresholdsService = _thresholdsService_
    $rootScope = _$rootScope_
  }))

  var location = {
    level: 'lga',
    allocations: [
      { version: 1,
        weeklyLevels: {
          'product:a': 50,
          'product:b': 100
        }
      },
      {
        version: 2,
        weeklyLevels: {
          'product:a': 100,
          'product:b': 200
        }
      }
    ],
    targetPopulation: {
      'product:a': 1000,
      'product:b': 2000
    },
    plans: [
      {
        version: 1,
        weeksOfStock: {
          max: 5,
          reOrder: 2,
          min: 1
        }
      }
    ]
  }

  var stockCount = {
    allocations: { version: 2 },
    plans: { version: 1 }
  }

  var stockCounts = [
    // { location: { zone: 'nc' }, allocations: { version: 2 }, plans: { version: 1 } },
    { location: { zone: 'nc', state: 'kogi' }, allocations: { version: 2 }, plans: { version: 1 } },
    { location: { zone: 'nc', state: 'kogi', lga: 'adavi' } }
  ]

  describe('calculateThresholds', function () {
    it('takes a location and a stockCount and returns the min, reOrder, max thresholds', function () {
      var expected = {
        'product:a': {
          min: 100,
          reOrder: 200,
          max: 500,
          targetPopulation: 1000
        },
        'product:b': {
          min: 200,
          reOrder: 400,
          max: 1000,
          targetPopulation: 2000
        }
      }
      var actual = thresholdsService.calculateThresholds(location, stockCount)
      expect(actual).toEqual(expected)
    })
    it('also works with zones where the thresholds are based on required allocation for zone state stores', function () {
      var zone = angular.extend({}, location, { level: 'zone' })
      // Note: `plans.weeksOfStock` is ignored (at least for now) in the case of zone stores
      var requiredStatesStoresAllocation = { 'product:a': 20, 'product:b': 50 }
      var expected = {
        'product:a': {
          min: 20,
          reOrder: 320,
          max: 620,
          targetPopulation: 1000
        },
        'product:b': {
          min: 50,
          reOrder: 650,
          max: 1250,
          targetPopulation: 2000
        }
      }
      var actual = thresholdsService.calculateThresholds(zone, stockCount, requiredStatesStoresAllocation)
      expect(actual).toEqual(expected)
    })
  })

  describe('getThresholdsFor', function () {
    it('takes an array of objects with location, allocations and plans fields and returns an object of location thresholds', function (done) {
      var expected = {
        // 'zone:nc': {
          // thresholds: {
            // 'product:a': { min: 100, reOrder: 200, max: 500 },
            // 'product:b': { min: 200, reOrder: 400, max: 1000 }
          // }
        // },
        'zone:nc:state:kogi': {
          allocations: { version: 2 },
          plans: { version: 1 },
          thresholds: {
            'product:a': { min: 100, reOrder: 200, max: 500, targetPopulation: 1000 },
            'product:b': { min: 200, reOrder: 400, max: 1000, targetPopulation: 2000 }
          }
        },
        'zone:nc:state:kogi:lga:adavi': {
          allocations: { version: 1 },
          plans: { version: 1 },
          thresholds: {
            'product:a': { min: 50, reOrder: 100, max: 250, targetPopulation: 1000 },
            'product:b': { min: 100, reOrder: 200, max: 500, targetPopulation: 2000 }
          }
        }
      }

      thresholdsService.getThresholdsFor(stockCounts)
        .then(function (thresholds) {
          expect(thresholds).toEqual(expected)
        })
      $rootScope.$digest()
      done()
    })
  })
})
<|endoftext|>


<commit_before>// MAPS STYLE CONFIG
window.MAPS_CONFIG = {
  standard: [],
  custom: [],
  // GRAY STYLE
  gray: [
    // BASIC
    {
      'stylers': [
        {hue: '#B9B9B9'},
        {saturation: -100}
      ]
    },
    // Lanscape
    {
      'featureType': 'landscape',
      'stylers': [
        {
          'color': '#E5E5E5'
        }
      ]
    },
    // Water
    {
      'featureType': 'water',
      'stylers': [
        {
          'visibility': 'on'
        },
        {
          'color': '#DCDCDC'
        }
      ]
    },
    // Transit
    {
      'featureType': 'transit',
      'elementType': 'geometry',
      'stylers': [
        {
          'color': '#B9B9B9'
        }
      ]
    },
    // Road hight way
    {
      'featureType': 'road.highway',
      'elementType': 'geometry',
      'stylers': [
        {
          'color': '#E2E2E2'
        }
      ]
    },
    // Road hight way control access
    {
      'featureType': 'road.highway.controlled_access',
      'elementType': 'geometry',
      'stylers': [
        {
          'color': '#ACACAC'
        }
      ]
    },
    // Road arterial
    {
      'featureType': 'road.arterial',
      'elementType': 'geometry',
      'stylers': [
        {
          'color': '#ffffff'
        }
      ]
    },
    // Road local
    {
      'featureType': 'road.local',
      'elementType': 'geometry',
      'stylers': [
        {
          'color': '#F6F6F6'
        }
      ]
    },
    // Point global
    {
      'featureType': 'poi',
      'elementType': 'geometry',
      'stylers': [
        {
          'color': '#DDDDDD'
        }
      ]
    },
    {
      'featureType': 'poi.park',
      'elementType': 'geometry',
      'stylers': [
        {
          'color': '#D3D3D3'
        }
      ]
    },
    {
      'featureType': 'poi.attraction',
      'elementType': 'geometry',
      'stylers': [
        {
          'color': '#DBDBDB'
        }
      ]
    },
    {
      'featureType': 'poi.business',
      'elementType': 'geometry',
      'stylers': [
        {
          'color': '#DBDBDB'
        }
      ]
    },
    {
      'featureType': 'poi.place_of_worship',
      'elementType': 'geometry',
      'stylers': [
        {
          'color': '#DBDBDB'
        }
      ]
    }
  ],
  // Blue STYLE
  blue: [
    // BASIC
    {
      'stylers': [
        {hue: '#4C95B2'},
        {saturation: -100}
      ]
    },
    // Lanscape
    {
      'featureType': 'landscape',
      'stylers': [
        {
          'color': '#DEE7EB'
        }
      ]
    },
    // Water
    {
      'featureType': 'water',
      'stylers': [
        {
          'visibility': 'on'
        },
        {
          'color': '#BBE7F9'
        }
      ]
    },
    // Transit
    {
      'featureType': 'transit',
      'elementType': 'geometry',
      'stylers': [
        {
          'color': '#86CEEB'
        }
      ]
    },
    // Road hight way
    {
      'featureType': 'road.highway',
      'elementType': 'geometry',
      'stylers': [
        {
          'color': '#C2EDFF'
        }
      ]
    },
    // Road hight way control access
    {
      'featureType': 'road.highway.controlled_access',
      'elementType': 'geometry',
      'stylers': [
        {
          'color': '#4DCDFF'
        }
      ]
    },
    // Road arterial
    {
      'featureType': 'road.arterial',
      'elementType': 'geometry',
      'stylers': [
        {
          'color': '#ffffff'
        }
      ]
    },
    // Road local
    {
      'featureType': 'road.local',
      'elementType': 'geometry',
      'stylers': [
        {
          'color': '#F3F7F9'
        }
      ]
    },
    // Point global
    {
      'featureType': 'poi',
      'elementType': 'geometry',
      'stylers': [
        {
          'color': '#C6E5F1'
        }
      ]
    },
    {
      'featureType': 'poi.park',
      'elementType': 'geometry',
      'stylers': [
        {
          'color': '#B7DDEC'
        }
      ]
    },
    {
      'featureType': 'poi.attraction',
      'elementType': 'geometry',
      'stylers': [
        {
          'color': '#D5DDE0'
        }
      ]
    },
    {
      'featureType': 'poi.business',
      'elementType': 'geometry',
      'stylers': [
        {
          'color': '#D5DDE0'
        }
      ]
    },
    {
      'featureType': 'poi.place_of_worship',
      'elementType': 'geometry',
      'stylers': [
        {
          'color': '#D5DDE0'
        }
      ]
    }
  ]
}
<commit_msg>Remove quotes for object keys<commit_after>// MAPS STYLE CONFIG
window.MAPS_CONFIG = {
  standard: [],
  custom: [],
  // GRAY STYLE
  gray: [
    // BASIC
    {
      stylers: [
        {hue: '#B9B9B9'},
        {saturation: -100}
      ]
    },
    // Lanscape
    {
      featureType: 'landscape',
      stylers: [
        {
          color: '#E5E5E5'
        }
      ]
    },
    // Water
    {
      featureType: 'water',
      stylers: [
        {
          'visibility': 'on'
        },
        {
          color: '#DCDCDC'
        }
      ]
    },
    // Transit
    {
      featureType: 'transit',
      elementType: 'geometry',
      stylers: [
        {
          color: '#B9B9B9'
        }
      ]
    },
    // Road hight way
    {
      featureType: 'road.highway',
      elementType: 'geometry',
      stylers: [
        {
          color: '#E2E2E2'
        }
      ]
    },
    // Road hight way control access
    {
      featureType: 'road.highway.controlled_access',
      elementType: 'geometry',
      stylers: [
        {
          color: '#ACACAC'
        }
      ]
    },
    // Road arterial
    {
      featureType: 'road.arterial',
      elementType: 'geometry',
      stylers: [
        {
          color: '#ffffff'
        }
      ]
    },
    // Road local
    {
      featureType: 'road.local',
      elementType: 'geometry',
      stylers: [
        {
          color: '#F6F6F6'
        }
      ]
    },
    // Point global
    {
      featureType: 'poi',
      elementType: 'geometry',
      stylers: [
        {
          color: '#DDDDDD'
        }
      ]
    },
    {
      featureType: 'poi.park',
      elementType: 'geometry',
      stylers: [
        {
          color: '#D3D3D3'
        }
      ]
    },
    {
      featureType: 'poi.attraction',
      elementType: 'geometry',
      stylers: [
        {
          color: '#DBDBDB'
        }
      ]
    },
    {
      featureType: 'poi.business',
      elementType: 'geometry',
      stylers: [
        {
          color: '#DBDBDB'
        }
      ]
    },
    {
      featureType: 'poi.place_of_worship',
      elementType: 'geometry',
      stylers: [
        {
          color: '#DBDBDB'
        }
      ]
    }
  ],
  // Blue STYLE
  blue: [
    // BASIC
    {
      stylers: [
        {hue: '#4C95B2'},
        {saturation: -100}
      ]
    },
    // Lanscape
    {
      featureType: 'landscape',
      stylers: [
        {
          color: '#DEE7EB'
        }
      ]
    },
    // Water
    {
      featureType: 'water',
      stylers: [
        {
          'visibility': 'on'
        },
        {
          color: '#BBE7F9'
        }
      ]
    },
    // Transit
    {
      featureType: 'transit',
      elementType: 'geometry',
      stylers: [
        {
          color: '#86CEEB'
        }
      ]
    },
    // Road hight way
    {
      featureType: 'road.highway',
      elementType: 'geometry',
      stylers: [
        {
          color: '#C2EDFF'
        }
      ]
    },
    // Road hight way control access
    {
      featureType: 'road.highway.controlled_access',
      elementType: 'geometry',
      stylers: [
        {
          color: '#4DCDFF'
        }
      ]
    },
    // Road arterial
    {
      featureType: 'road.arterial',
      elementType: 'geometry',
      stylers: [
        {
          color: '#ffffff'
        }
      ]
    },
    // Road local
    {
      featureType: 'road.local',
      elementType: 'geometry',
      stylers: [
        {
          color: '#F3F7F9'
        }
      ]
    },
    // Point global
    {
      featureType: 'poi',
      elementType: 'geometry',
      stylers: [
        {
          color: '#C6E5F1'
        }
      ]
    },
    {
      featureType: 'poi.park',
      elementType: 'geometry',
      stylers: [
        {
          color: '#B7DDEC'
        }
      ]
    },
    {
      featureType: 'poi.attraction',
      elementType: 'geometry',
      stylers: [
        {
          color: '#D5DDE0'
        }
      ]
    },
    {
      featureType: 'poi.business',
      elementType: 'geometry',
      stylers: [
        {
          color: '#D5DDE0'
        }
      ]
    },
    {
      featureType: 'poi.place_of_worship',
      elementType: 'geometry',
      stylers: [
        {
          color: '#D5DDE0'
        }
      ]
    }
  ]
}
<|endoftext|>


<commit_before>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import { UIDatabase } from '../../../database/index';
import { MODAL_KEYS } from '../../../utilities';
import { ACTIONS } from './constants';
import { pageObjectSelector, pageStateSelector } from '../selectors';

/**
 * Refreshes the underlying data array by slicing backingData.
 * BackingData is a live realm collection which side effects i.e.
 * finalising can make out of sync with the data array used for display.
 *
 * NOTE: This is a duplicate action to avoid a dependency cycle with
 *       tableActions.js
 */
const refreshData = route => ({ type: ACTIONS.REFRESH_DATA, payload: { route } });

/**
 * Edits the name field in the current store.
 * use case: Stocktake naming.
 *
 * @param {String} value New name value to set.
 */
export const editName = (value, route) => ({ type: ACTIONS.EDIT_NAME, payload: { value, route } });

/**
 * Closes a modal by unsetting the modalKey aand modalValue (if used).
 */
export const closeModal = route => ({ type: ACTIONS.CLOSE_MODAL, payload: { route } });

/**
 * Splitter action creator, assumes the current modalValue is an instance
 * of some rowData - extracting the key and refreshing the row before
 * closing the modal.
 * use case: opening a stocktake batch and refreshing the stocktake edit page row.
 */
export const closeAndRefresh = route => (dispatch, getState) => {
  const { modalValue, keyExtractor } = pageStateSelector(route, getState());

  const rowKey = keyExtractor(modalValue);

  // Avoiding refresh row dependency cycle.
  dispatch({ type: ACTIONS.REFRESH_ROW, payload: { rowKey, route } });
  dispatch(closeModal(route));
};

/**
 * Opens a modal given a modal key. Can pass an optional value
 * to set as `modalValue`
 *
 * @param {String} modalKey Key for the modal to open
 * @param {Any}    value     Optional value to set as `modalValue` in state.
 */
export const openModal = (modalKey, value, route) => {
  switch (modalKey) {
    case MODAL_KEYS.STOCKTAKE_REASON:
    case MODAL_KEYS.EDIT_STOCKTAKE_BATCH:
    case MODAL_KEYS.ENFORCE_STOCKTAKE_REASON:
      return { type: ACTIONS.OPEN_MODAL, payload: { modalKey, rowKey: value, route } };

    case MODAL_KEYS.MONTHS_SELECT:
    case MODAL_KEYS.PROGRAM_REQUISITION:
    case MODAL_KEYS.PROGRAM_STOCKTAKE:
    case MODAL_KEYS.VIEW_REGIMEN_DATA:
    case MODAL_KEYS.SELECT_ITEM:
    case MODAL_KEYS.SELECT_MONTHS:
    case MODAL_KEYS.SELECT_CUSTOMER:
    case MODAL_KEYS.SELECT_EXTERNAL_SUPPLIER:
    case MODAL_KEYS.SELECT_INTERNAL_SUPPLIER:
    case MODAL_KEYS.STOCKTAKE_OUTDATED_ITEM:
    case MODAL_KEYS.STOCKTAKE_COMMENT_EDIT:
    case MODAL_KEYS.TRANSACTION_COMMENT_EDIT:
    case MODAL_KEYS.REQUISITION_COMMENT_EDIT:
    case MODAL_KEYS.THEIR_REF_EDIT:
    default:
      return { type: ACTIONS.OPEN_MODAL, payload: { modalKey, route: route || value } };
  }
};

/**
 * Edits the `name` field of a pageObject.
 *
 * @param {String} value          New name value.
 * @param {String} pageObjectType PageObject type to edit i.e. Transaction.
 */
export const editPageObjectName = (value, pageObjectType, route) => (dispatch, getState) => {
  const pageObject = pageObjectSelector(getState());

  const { name } = pageObject;

  if (name !== value) {
    UIDatabase.write(() => {
      UIDatabase.update(pageObjectType, {
        ...pageObject,
        name: value,
      });
    });
  }

  dispatch(closeModal(route));
};

/**
 * Edits the `theirRef` field of a pageObject.
 *
 * @param {String} value          New theifRef value.
 * @param {String} pageObjectType PageObject type to edit i.e. Transaction.
 */
export const editTheirRef = (value, pageObjectType, route) => (dispatch, getState) => {
  const pageObject = pageObjectSelector(getState());

  const { theirRef } = pageObject;

  if (theirRef !== value) {
    UIDatabase.write(() => {
      UIDatabase.update(pageObjectType, { ...pageObject, theirRef: value });
    });
  }

  dispatch(closeModal(route));
};

/**
 * Edits the `comment` field of a `pageObject`.
 *
 * @param {String} value          New comment value.
 * @param {String} pageObjectType Type of the pageObject to edit i.e. Transaction.
 */
export const editComment = (value, pageObjectType, route) => (dispatch, getState) => {
  const pageObject = pageObjectSelector(getState());

  const { comment } = pageObject;

  if (comment !== value) {
    UIDatabase.write(() => {
      UIDatabase.update(pageObjectType, { ...pageObject, comment: value });
    });
  }

  dispatch(closeModal(route));
};

/**
 * Sets a requisitions `monthsToSupply` field. Refreshing data afterwards
 * for new suggested quantities to be calculated.
 *
 * @param {Number} value  New months of supply value.
 */
export const editMonthsToSupply = (value, route) => (dispatch, getState) => {
  const pageObject = pageObjectSelector(getState());

  const { monthsToSupply } = pageObject;

  if (monthsToSupply !== value) {
    UIDatabase.write(() => {
      pageObject.monthsToSupply = value;
      UIDatabase.save('Requisition', pageObject);
    });

    // Update suggested quantities when the monthsToSupply changes.
    dispatch(refreshData(route));
  }

  dispatch(closeModal(route));
};

/**
 * Resets a Stocktake - refreshing all snapshot quantities.
 *
 * use case: Snapshot quantities need to be refreshed if they are
 *           outdated when revisiting an un-finalised Stocktake.
 */
export const resetStocktake = route => (dispatch, getState) => {
  const pageObject = pageObjectSelector(getState());

  pageObject.resetStocktake(UIDatabase);

  dispatch(refreshData(route));
  dispatch(closeModal(route));
};

export const PageActionsLookup = {
  editName,
  closeModal,
  openModal,
  editTheirRef,
  editComment,
  editMonthsToSupply,
  resetStocktake,
  closeAndRefresh,
  editPageObjectName,
};
<commit_msg>Add update pageActions<commit_after>/**
 * mSupply Mobile
 * Sustainable Solutions (NZ) Ltd. 2019
 */

import { UIDatabase } from '../../../database/index';
import { MODAL_KEYS } from '../../../utilities';
import { ACTIONS } from './constants';
import { pageObjectSelector, pageStateSelector } from '../selectors';

/**
 * Refreshes the underlying data array by slicing backingData.
 * BackingData is a live realm collection which side effects i.e.
 * finalising can make out of sync with the data array used for display.
 *
 * NOTE: This is a duplicate action to avoid a dependency cycle with
 *       tableActions.js
 */
const refreshData = route => ({ type: ACTIONS.REFRESH_DATA, payload: { route } });

/**
 * Edits the name field in the current store.
 * use case: Stocktake naming.
 *
 * @param {String} value New name value to set.
 */
export const editName = (value, route) => ({ type: ACTIONS.EDIT_NAME, payload: { value, route } });

/**
 * Closes a modal by unsetting the modalKey aand modalValue (if used).
 */
export const closeModal = route => ({ type: ACTIONS.CLOSE_MODAL, payload: { route } });

/**
 * Splitter action creator, assumes the current modalValue is an instance
 * of some rowData - extracting the key and refreshing the row before
 * closing the modal.
 * use case: opening a stocktake batch and refreshing the stocktake edit page row.
 */
export const closeAndRefresh = route => (dispatch, getState) => {
  const { modalValue, keyExtractor } = pageStateSelector(getState());

  const rowKey = keyExtractor(modalValue);

  // Avoiding refresh row dependency cycle.
  dispatch({ type: ACTIONS.REFRESH_ROW, payload: { rowKey, route } });
  dispatch(closeModal(route));
};

/**
 * Opens a modal given a modal key. Can pass an optional value
 * to set as `modalValue`
 *
 * @param {String} modalKey Key for the modal to open
 * @param {Any}    value     Optional value to set as `modalValue` in state.
 */
export const openModal = (modalKey, value, route) => {
  switch (modalKey) {
    case MODAL_KEYS.STOCKTAKE_REASON:
    case MODAL_KEYS.EDIT_STOCKTAKE_BATCH:
    case MODAL_KEYS.ENFORCE_STOCKTAKE_REASON:
      return { type: ACTIONS.OPEN_MODAL, payload: { modalKey, rowKey: value, route } };

    case MODAL_KEYS.MONTHS_SELECT:
    case MODAL_KEYS.PROGRAM_REQUISITION:
    case MODAL_KEYS.PROGRAM_STOCKTAKE:
    case MODAL_KEYS.VIEW_REGIMEN_DATA:
    case MODAL_KEYS.SELECT_ITEM:
    case MODAL_KEYS.SELECT_MONTHS:
    case MODAL_KEYS.SELECT_CUSTOMER:
    case MODAL_KEYS.SELECT_EXTERNAL_SUPPLIER:
    case MODAL_KEYS.SELECT_INTERNAL_SUPPLIER:
    case MODAL_KEYS.STOCKTAKE_OUTDATED_ITEM:
    case MODAL_KEYS.STOCKTAKE_COMMENT_EDIT:
    case MODAL_KEYS.TRANSACTION_COMMENT_EDIT:
    case MODAL_KEYS.REQUISITION_COMMENT_EDIT:
    case MODAL_KEYS.THEIR_REF_EDIT:
    default:
      return { type: ACTIONS.OPEN_MODAL, payload: { modalKey, route: route || value } };
  }
};

/**
 * Edits the `name` field of a pageObject.
 *
 * @param {String} value          New name value.
 * @param {String} pageObjectType PageObject type to edit i.e. Transaction.
 */
export const editPageObjectName = (value, pageObjectType, route) => (dispatch, getState) => {
  const pageObject = pageObjectSelector(getState());

  const { name } = pageObject;

  if (name !== value) {
    UIDatabase.write(() => {
      UIDatabase.update(pageObjectType, {
        ...pageObject,
        name: value,
      });
    });
  }

  dispatch(closeModal(route));
};

/**
 * Edits the `theirRef` field of a pageObject.
 *
 * @param {String} value          New theifRef value.
 * @param {String} pageObjectType PageObject type to edit i.e. Transaction.
 */
export const editTheirRef = (value, pageObjectType, route) => (dispatch, getState) => {
  const pageObject = pageObjectSelector(getState());

  const { theirRef } = pageObject;

  if (theirRef !== value) {
    UIDatabase.write(() => {
      UIDatabase.update(pageObjectType, { ...pageObject, theirRef: value });
    });
  }

  dispatch(closeModal(route));
};

/**
 * Edits the `comment` field of a `pageObject`.
 *
 * @param {String} value          New comment value.
 * @param {String} pageObjectType Type of the pageObject to edit i.e. Transaction.
 */
export const editComment = (value, pageObjectType, route) => (dispatch, getState) => {
  const pageObject = pageObjectSelector(getState());

  const { comment } = pageObject;

  if (comment !== value) {
    UIDatabase.write(() => {
      UIDatabase.update(pageObjectType, { ...pageObject, comment: value });
    });
  }

  dispatch(closeModal(route));
};

/**
 * Sets a requisitions `monthsToSupply` field. Refreshing data afterwards
 * for new suggested quantities to be calculated.
 *
 * @param {Number} value  New months of supply value.
 */
export const editMonthsToSupply = (value, route) => (dispatch, getState) => {
  const pageObject = pageObjectSelector(getState());

  const { monthsToSupply } = pageObject;

  if (monthsToSupply !== value) {
    UIDatabase.write(() => {
      pageObject.monthsToSupply = value;
      UIDatabase.save('Requisition', pageObject);
    });

    // Update suggested quantities when the monthsToSupply changes.
    dispatch(refreshData(route));
  }

  dispatch(closeModal(route));
};

/**
 * Resets a Stocktake - refreshing all snapshot quantities.
 *
 * use case: Snapshot quantities need to be refreshed if they are
 *           outdated when revisiting an un-finalised Stocktake.
 */
export const resetStocktake = route => (dispatch, getState) => {
  const pageObject = pageObjectSelector(getState());

  pageObject.resetStocktake(UIDatabase);

  dispatch(refreshData(route));
  dispatch(closeModal(route));
};

export const PageActionsLookup = {
  editName,
  closeModal,
  openModal,
  editTheirRef,
  editComment,
  editMonthsToSupply,
  resetStocktake,
  closeAndRefresh,
  editPageObjectName,
};
<|endoftext|>


<commit_before>Ember.Application.initializer({
    name: 'registerTracker',
    before: 'injectTracker',

    initialize: function(container, application) {
        application.register("controller:tracker", App.TrackerController);
    }
});

Ember.Application.initializer({
    name: 'injectTracker',
    before: 'currentUser',

    initialize: function(container, application) {
        // Calling the lookup function seems to have the side-effect of instantiating the tracker controller.
        // Perhaps the call to lookup also registers the controller with the container so that it may be injected
        container.lookup('controller:tracker');

        // Without the previous lookup the injection fails. We inject on the application object, rather than the container.
        // See: http://balinterdi.com/2014/05/01/dependency-injection-in-ember-dot-js.html
        application.inject("route", "tracker", "controller:tracker");
        application.inject("controller", "tracker", "controller:tracker");

  }
});

/*
    Specialized controller for tracking (still coupled to Mixpanel at the moment)
 */
App.TrackerController = Em.ObjectController.extend({
   needs: "currentUser",

   init: function(){
       this._super();
       if (MIXPANEL_KEY && mixpanel) {
           this.set('_tracker', mixpanel);
       }

   }.observes('window'),

   trackEvent: function(name, properties) {
        if (Em.typeOf(properties) == 'undefined') properties = {};

        if (Em.typeOf(name) == 'string' && Em.typeOf(properties) == 'object') {
            this.get('_tracker').track(name, properties);
        }
    },

    identify: function(id){
        if ( Em.typeOf(id) == 'number') {
            this.get('_tracker').identify(id);
        }
    },

    alias: function(id) {
        if (Em.typeOf(id) == 'number') {
            this.get('_tracker').alias(id);
        }
    }},

    peopleSet: function(properties) {
        if (Em.typeOf(properties) == 'undefined') properties = {};

        if (Em.typeOf(properties) == 'object') {
            this.get('_tracker').people.set(properties);
        }
    },

    peopleIncrement: function(key, value) {
        if (Em.typeOf(name) == 'string' && Em.typeOf(value) == 'number') {
             this.get('_tracker').people.increment(key, value);
        }

        if (Em.typeOf(name) == 'string' && Em.typeOf(value) == 'undefined') {
             this.get('_tracker').people.increment(key);
        }

    },

    setUserDetails: function(){
        if (this.get('controllers.currentUser.isAuthenticated')) {
            var user = this.get('controllers.currentUser');
            this.get('_tracker').register({
                "email": user.get('email'),
                "name": user.get('full_name')
            });
        }
    }.observes('controllers.currentUser.isAuthenticated')

});

<commit_msg>colon fix<commit_after>Ember.Application.initializer({
    name: 'registerTracker',
    before: 'injectTracker',

    initialize: function(container, application) {
        application.register("controller:tracker", App.TrackerController);
    }
});

Ember.Application.initializer({
    name: 'injectTracker',
    before: 'currentUser',

    initialize: function(container, application) {
        // Calling the lookup function seems to have the side-effect of instantiating the tracker controller.
        // Perhaps the call to lookup also registers the controller with the container so that it may be injected
        container.lookup('controller:tracker');

        // Without the previous lookup the injection fails. We inject on the application object, rather than the container.
        // See: http://balinterdi.com/2014/05/01/dependency-injection-in-ember-dot-js.html
        application.inject("route", "tracker", "controller:tracker");
        application.inject("controller", "tracker", "controller:tracker");

  }
});

/*
    Specialized controller for tracking (still coupled to Mixpanel at the moment)
 */
App.TrackerController = Em.ObjectController.extend({
   needs: "currentUser",

   init: function(){
       this._super();
       if (MIXPANEL_KEY && mixpanel) {
           this.set('_tracker', mixpanel);
       }

   }.observes('window'),

   trackEvent: function(name, properties) {
        if (Em.typeOf(properties) == 'undefined') properties = {};

        if (Em.typeOf(name) == 'string' && Em.typeOf(properties) == 'object') {
            this.get('_tracker').track(name, properties);
        }
    },

    identify: function(id){
        if ( Em.typeOf(id) == 'number') {
            this.get('_tracker').identify(id);
        }
    },

    alias: function(id) {
        if (Em.typeOf(id) == 'number') {
            this.get('_tracker').alias(id);
        }
    },

    peopleSet: function(properties) {
        if (Em.typeOf(properties) == 'undefined') properties = {};

        if (Em.typeOf(properties) == 'object') {
            this.get('_tracker').people.set(properties);
        }
    },

    peopleIncrement: function(key, value) {
        if (Em.typeOf(name) == 'string' && Em.typeOf(value) == 'number') {
             this.get('_tracker').people.increment(key, value);
        }

        if (Em.typeOf(name) == 'string' && Em.typeOf(value) == 'undefined') {
             this.get('_tracker').people.increment(key);
        }

    },

    setUserDetails: function(){
        if (this.get('controllers.currentUser.isAuthenticated')) {
            var user = this.get('controllers.currentUser');
            this.get('_tracker').register({
                "email": user.get('email'),
                "name": user.get('full_name')
            });
        }
    }.observes('controllers.currentUser.isAuthenticated')

});

<|endoftext|>


<commit_before>angular.module('vdui.widget.modalWebcam', [])

.directive('vduiEscapeKeyDown', function() {
  return function(scope, element, attrs) {
    element.bind("keydown keypress", function(event) {
      if(event.which === 27) {
        scope.$apply(function() {
          scope.$eval(attrs.vduiEscapeKeyDown, {'event': event});
        });
        event.preventDefault();
      }
    });
  };
})

.directive('vduiModalWebcam', function() {
  return {
    restrict: 'E',
    scope: {
      visible: '=',
      disabled: '=?'
    },
    templateUrl: '/' + module.getPath(OPENMRS_CONTEXT_PATH) + '/templates/modalWebcam.page',
    controller: function($scope, $rootScope, $window) {
      $scope.imgWidth = 640;
      $scope.imgHeight = 480;

      $scope.setWebcam = function() {
        Webcam.set({
          width: $scope.imgWidth,
          height: $scope.imgHeight,
          dest_width: $scope.imgWidth,
          dest_height: $scope.imgHeight,
          force_flash: $window.chrome && $window.location.protocol != "https:"
        });
        Webcam.attach('#vdui_webcam-id');
      }

      $scope.$watch("visible", function() {
        if ($scope.disabled === true)
          return;

        if ($scope.visible) {
          $scope.setWebcam();
        }
        else {
          Webcam.reset();
        }
      });

      $scope.close = function(){
        $scope.visible = false;
        $scope.dataUri = null;
      }

      $scope.snap = function() {
        Webcam.snap( function(dataUri) {
          $scope.dataUri = dataUri;
        });
      }

      $scope.repeat = function() {
        $scope.dataUri = null;
      }

      $scope.finalise = function() {
        var file = module.dataUritoBlob($scope.dataUri);
        file.name = "wc_pic_" + moment(new Date()).format("YYYYMMDD_HHmmss") + ".png";
        $rootScope.$emit(module.webcamCaptureForUpload, file);
        $scope.close();
      }
    }
  };
});<commit_msg>MRSDOC-43: Disabling Flash for 'localhost' on Chrome. Also improved snapshot rendering when using Flash with a $scope.$apply().   modified:   omod/src/main/webapp/resources/scripts/directives/modalWebcam.js<commit_after>angular.module('vdui.widget.modalWebcam', [])

.directive('vduiEscapeKeyDown', function() {
  return function(scope, element, attrs) {
    element.bind("keydown keypress", function(event) {
      if(event.which === 27) {
        scope.$apply(function() {
          scope.$eval(attrs.vduiEscapeKeyDown, {'event': event});
        });
        event.preventDefault();
      }
    });
  };
})

.directive('vduiModalWebcam', function() {
  return {
    restrict: 'E',
    scope: {
      visible: '=',
      disabled: '=?'
    },
    templateUrl: '/' + module.getPath(OPENMRS_CONTEXT_PATH) + '/templates/modalWebcam.page',
    controller: function($scope, $rootScope, $window) {
      $scope.imgWidth = 640;
      $scope.imgHeight = 480;
      var forceFlash = $window.chrome && $window.location.protocol != "https:";
      forceFlash = !($window.chrome && $window.location.hostname === "localhost");  // When debugging with Chrome on localhost Flash can be avoided

      $scope.setWebcam = function() {
        Webcam.set({
          width: $scope.imgWidth,
          height: $scope.imgHeight,
          dest_width: $scope.imgWidth,
          dest_height: $scope.imgHeight,
          force_flash: forceFlash
        });
        Webcam.attach('#vdui_webcam-id');
      }

      $scope.$watch("visible", function() {
        if ($scope.disabled === true)
          return;

        if ($scope.visible) {
          $scope.setWebcam();
        }
        else {
          Webcam.reset();
        }
      });

      $scope.close = function(){
        $scope.visible = false;
        $scope.dataUri = null;
      }

      $scope.snap = function() {
        Webcam.snap( function(dataUri) {
          $scope.dataUri = dataUri;
          if (forceFlash === true)
            $scope.$apply();  // This is needed when Flash is being used... To be investigated.
        });
      }

      $scope.repeat = function() {
        $scope.dataUri = null;
      }

      $scope.finalise = function() {
        var file = module.dataUritoBlob($scope.dataUri);
        file.name = "wc_pic_" + moment(new Date()).format("YYYYMMDD_HHmmss") + ".png";
        $rootScope.$emit(module.webcamCaptureForUpload, file);
        $scope.close();
      }
    }
  };
});<|endoftext|>