<commit_before>'use strict';

/*global nodeca, _*/

// 3rd-party
var Store = require('nlib').Settings.Store;
var async = require('nlib').Vendor.Async;

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

// Helper to fetch usergroups by IDs
//
function fetchForumSettings(id, callback) {
  nodeca.models.forum.Section.findOne({ _id: id })
    .select('settings')
    .exec(callback);
}

// Memoized version of fetchUserGroups helper
//
var fetchForumSesstingsCached = nodeca.components.memoizee(fetchForumSettings, {
  // momoizee options. revalidate cache after 30 sec
  async:  true,
  maxAge: 30000
});

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

var ForumUserGroupStore = new Store({
  get: function (key, params, options, callback) {
    var func = options.skipCache ? fetchForumSettings : fetchForumSesstingsCached;

    func(params.forum_id, function (err, forum) {
      if (err) {
        callback(err);
        return;
      }

      var settings  = forum.settings || {};
      var values    = [];

      params.usergroup_ids.forEach(function (id) {
        if (settings[key + ':usergroup:' + id]) {
          values.push(settings[key + ':usergroup:' + id]);
        }
      });

      // push default value
      values.push({ value: ForumUserGroupStore.getDefaultValue(key) });

      var result;

      try {
        result = Store.mergeValues(values);
      } catch (err) {
        callback(err);
        return;
      }

      callback(null, result);
    });
  },
  set: function (values, params, callback) {
    fetchForumSettings(params.forum_id, function (err, forum) {
      if (err) {
        callback(err);
        return;
      }

      // leave only those params, that we know about
      values = _.pick(values || {}, ForumUserGroupStore.keys);

      forum.settings = forum.settings || {};

      params.usergroup_ids.forEach(function (id) {
        _.each(values, function (opts, key) {
          forum.settings[key + ':usergroup:' + id] = {
            value: opts.value,
            force: !!opts.value
          };
        });
      });

      forum.markModified('settings');
      forum.save(callback);
    });
  },
  params: {
    usergroup_ids: {
      type: 'array',
      required: true,
      minItems: 1
    },
    forum_id: {
      type: 'string',
      required: true
    }
  }
});

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

module.exports = ForumUserGroupStore;
<commit_msg>Update FirumUserGroupStore signture<commit_after>'use strict';

/*global nodeca, _*/

// 3rd-party
var Store = require('nlib').Settings.Store;
var async = require('nlib').Vendor.Async;

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

// Helper to fetch usergroups by IDs
//
function fetchForumSettings(id, callback) {
  nodeca.models.forum.Section.findOne({ _id: id })
    .select('settings')
    .exec(callback);
}

// Memoized version of fetchUserGroups helper
//
var fetchForumSesstingsCached = nodeca.components.memoizee(fetchForumSettings, {
  // momoizee options. revalidate cache after 30 sec
  async:  true,
  maxAge: 30000
});

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

module.exports = new Store({
  get: function (keys, params, options, callback) {
    var self = this;
    var func = options.skipCache ? fetchForumSettings : fetchForumSesstingsCached;

    func(params.forum_id, function (err, forum) {
      if (err) {
        callback(err);
        return;
      }

      var settings  = forum.settings || {};
      var results   = {};

      try {
        keys.forEach(function (k) {
          var values = [];

          params.usergroup_ids.forEach(function (id) {
            if (settings[k + ':usergroup:' + id]) {
              values.push(settings[k + ':usergroup:' + id]);
            }
          });

          // push default value
          values.push({ value: self.getDefaultValue(k) });

          results[k] = Store.mergeValues(values);
        });
      } catch (err) {
        callback(err);
        return;
      }

      callback(null, results);
    });
  },
  set: function (values, params, callback) {
    var self = this;

    fetchForumSettings(params.forum_id, function (err, forum) {
      if (err) {
        callback(err);
        return;
      }

      // leave only those params, that we know about
      values = _.pick(values || {}, self.keys);

      forum.settings = forum.settings || {};

      params.usergroup_ids.forEach(function (id) {
        _.each(values, function (opts, key) {
          forum.settings[key + ':usergroup:' + id] = {
            value: opts.value,
            force: !!opts.value
          };
        });
      });

      forum.markModified('settings');
      forum.save(callback);
    });
  }
});
<|endoftext|>


<commit_before>(function() {
  'use strict';
  /**
   *  @ngdoc overview
   *  @name ui.grid.columnsFilters
   *
   *  @description
   *
   *  #ui.grid.columnsFilters
   *
   *
Beta This feature is ready for testing, but it either hasn't seen a lot of use or has some known bugs.

   *
   *  This module provides column filter in popup from the filter header cells.
   */
  var module = angular.module('ui.grid.columnsFilters', ['ui.grid']);

  /**
   *  @ngdoc object
   *  @name ui.grid.columnsFilters.constant:uiGridColumnsFiltersConstants
   *
   *  @description constants available in columnsFilters module.
   *
   *  @property {string} featureName               - The name of the feature.
   *  @property {object} filterType                - {
      STRING: 'string',
      NUMBER: 'number',
      DATE: 'date',
      SELECT: 'select'
    }
    * @property {object} dateType                - {
      DATE: 'date',
      TIME: 'time',
      DATETIME: 'datetime-local',
      DATETIMELOCALE: 'datetime-locale'
    }
   *  @property {object} numberOperators                - {
      8: 'Exact',
      512: 'Not Equal',
      128: 'Less than',
      256: 'Less than or equal',
      32: 'More than',
      64: 'More than or equal'
    }
   * @property {object} dateOperators                - {
      8: 'Exact',
      512: 'Not Equal',
      128: 'Before',
      256: 'Before or equal',
      32: 'Later',
      64: 'Later or equal'
    }
   * @property {object} stringOperators                - {
      16: 'Contains',
      4: 'Ends With',
      8: 'Exact',
      512: 'Not Equal',
      2: 'Starts With'
    }
   * @property {object} selectOperators                - {
      16: 'Contains',
      4: 'Ends With',
      8: 'Exact',
      512: 'Not Equal',
      2: 'Starts With'
    }
   * @property {object} logics                - {
      "OR": 'Or',
      "AND": 'And'
    }
   *
   */

  module.constant('uiGridColumnsFiltersConstants', {
    featureName: "columnsFilters",
    filterType: {
      STRING: 'string',
      NUMBER: 'number',
      DATE: 'date',
      SELECT: 'select'
    },
    dateTypes: {
      DATE: 'date',
      TIME: 'time',
      DATETIME: 'datetime-locale',
      DATETIMELOCALE: 'datetime-locale'
    },
    numberOperators: {
      8: 'Exact',
      512: 'Not Equal',
      128: 'Less than',
      256: 'Less than or equal',
      32: 'More than',
      64: 'More than or equal'
    },
    dateOperators: {
      8: 'Exact',
      512: 'Not Equal',
      128: 'Before',
      256: 'Before or equal',
      32: 'Later',
      64: 'Later or equal'
    },
    stringOperators: {
      16: 'Contains',
      4: 'Ends With',
      8: 'Exact',
      512: 'Not Equal',
      2: 'Starts With'
    },
    selectOperators: {
      16: 'Contains',
      4: 'Ends With',
      8: 'Exact',
      512: 'Not Equal',
      2: 'Starts With'
    },
    logics: {
      "OR": 'Or',
      "AND": 'And'
    }
  });

  /**
   *  @ngdoc service
   *  @name ui.grid.columnsFilters.service:uiGridColumnsFiltersService
   *
   *  @description Services for columnsFilters feature
   */
  /**
   *  @ngdoc object
   *  @name ui.grid.columnsFilters.api:ColumnDef
   *
   *  @description ColumnDef for column filter feature, these are available to be
   *  set using the ui-grid {@link ui.grid.class:GridOptions.columnDef gridOptions.columnDefs}
   *
   * @property {object} columnFilter - Specific column columnsFilters definitions
   * @property {string} columnFilter.type  - can be: 'date', 'select', 'string', 'number'
   * @property {string} columnFilter.type  - can be: 'date', 'select', 'string', 'number'
   * @property {boolean} columnFilter.multiple   - Boolean stating is a select filter would show as multiple or singular choice
   * @property {object} columnFilter.selectOptions   - states the values of a select option (if needed)
   * @property {object} columnFilter.terms   - holds the search terms for every filter form
   * @property {object} columnFilter.logics  - holds the logics (and/or) for every filter form
   * @property {object} columnFilter.operators   - holds the operators (bigger than, smaller than etc.) for every filter form
   */

  module.service('uiGridColumnsFiltersService', ['$q', 'uiGridColumnsFiltersConstants', 'rowSearcher', 'GridRow', 'gridClassFactory', 'i18nService', 'uiGridConstants', 'rowSorter', '$templateCache',
    function ($q, uiGridColumnsFiltersConstants, rowSearcher, GridRow, gridClassFactory, i18nService, uiGridConstants, rowSorter, $templateCache) {

       var runColumnFilter = rowSearcher.runColumnFilter;

       function columnFilter(searchTerm, cellValue, row, column){
          var conditions = column.colDef.columnFilter.operators;
          var logics = column.colDef.columnFilter.logics;
          var filterPass = true;

          for (var i = 0; i < searchTerm.length; i++){
              var term = searchTerm[i];
              var newFilter = rowSearcher.setupFilters([{
                term: term,
                condition: Number( conditions[i] ? conditions[i] : conditions[0] ),
                flags: {
                  caseSensitive: false
                }
              }])[0];
              // if we are on the second run check for "OR"
              if (i){
                  if ( angular.isDefined(logics) && logics[i-1] === 'OR' ){
                    return filterPass || runColumnFilter(row.grid, row, column, newFilter);
                  }
                  else {
                    return filterPass && runColumnFilter(row.grid, row, column, newFilter);
                  }
              }
              // TODO::check for the "select" condition in order to make sure we check for mulitple select
              filterPass = rowSearcher.runColumnFilter(row.grid, row, column, newFilter);
          }

          return filterPass;
       }

      var service = {
        initializeGrid: function (grid, $scope) {
         //add feature namespace and any properties to grid for needed
          /**
           *  @ngdoc object
           *  @name columnsFilters
           *  @name ui.grid.columnsFilters.api:Grid
           *
           *  @description Grid properties and functions added for columnsFilters
           *
           *  @property {object} columnsFilters - object that holds global definitions
           */
          grid.columnsFilters = {
            currentColumn: undefined
          };

          angular.forEach(grid.options.columnDefs, function(colDef){
            if (colDef.enableFiltering !== false){
              var columnFilter = {
                  terms: [],
                  operators: [],
                  logics: []
              };

              if (angular.isUndefined(colDef.columnFilter)){
                colDef.columnFilter = columnFilter;
              }
              else {
                colDef.columnFilter = angular.merge({}, columnFilter, colDef.columnFilter);
              }
              colDef.filterHeaderTemplate = $templateCache.get('ui-grid/filterButton');
            }
            else {
              colDef.filterHeaderTemplate = '';
            }
          });
        },
        /**
         * @ngdoc method
         * @methodOf ui.grid.columnsFilters.service:uiGridColumnsFiltersService
         * @name filterPopupStyle
         * @description Calculates the column filter's popup absolute position
         * @param {event} $event the event from the click event
         * @returns {object} and object with top and left styling expressions
         */
        filterPopupStyle: function($event){
          var rect = $event.target.parentElement.getClientRects()[0];
          return {
            top: document.body.scrollTop + (rect.height + rect.top) + 'px',
            left: rect.left + 'px'
          };
        },
        /**
         * @ngdoc method
         * @methodOf ui.grid.columnsFilters.service:uiGridColumnsFiltersService
         * @name filter
         * @description Sets the filter parameters of the column
         * @param {column} col - the column that is now being filtered
         */
        filter: function(col){
          var terms = col.colDef.columnFilter.terms;

          var logics = col.colDef.columnFilter.logics;

          // add the data into the filter object of the column
          // the terms array is the "term"
          col.filters[0].term = terms;

          // set condition as our filter function
          col.filters[0].condition = columnFilter;

          // logic is new, so we will add it, and handle it in our override function
          col.filters[0].logic = logics;
          col.grid.api.core.notifyDataChange( uiGridConstants.dataChange.COLUMN );
        },
        /**
         * @ngdoc method
         * @methodOf ui.grid.columnsFilters.service:uiGridColumnsFiltersService
         * @name clear
         * @description Clears the filter parameters of the column
         * @param {column} col -  the column that is now being filtered
         */
        clear: function(col){
          col.filters[0].term.length = 0;
          col.filters[0].condition = undefined;
          col.grid.api.core.notifyDataChange( uiGridConstants.dataChange.COLUMN );
        }
      };

      return service;
  }]);

  module.directive('uiGridColumnsFiltersDirective', ['$compile', 'gridUtil', 'uiGridColumnsFiltersService', 'uiGridColumnsFiltersConstants', '$templateCache', '$document',
    function ($compile, gridUtil, uiGridColumnsFiltersService, uiGridColumnsFiltersConstants, $templateCache, $document) {
    return {
      require: 'uiGrid',
      scope: false,
      link: function ($scope, $elm, $attrs, uiGridCtrl) {
        uiGridColumnsFiltersService.initializeGrid(uiGridCtrl.grid, $scope);
      }
    };

    /**
    *  @ngdoc directive
    *  @name ui.grid.columnsFilters.directive:uiGridColumnFiltersDirective
    *
    *  @description directive for columnsFilters button in column header
    */

  }]);

  /**
    *  @ngdoc directive
    *  @name ui.grid.columnsFilters.directive.api:uiGridFilter
    *
    *  @description Extanding the uiGridFilter directive to prepare the column filter
    */
  module.directive('uiGridFilter', ['uiGridColumnsFiltersService', 'uiGridColumnsFiltersConstants', '$templateCache', '$compile',
      function(uiGridColumnsFiltersService, uiGridColumnsFiltersConstants, $templateCache, $compile){
      return {
        priority: 500,
        scope: false,
        link: function($scope, $elm, $attrs, uiGridCtrl) {
          //TODO::need to decide if we work with the filter API when it is sufficient and only expand it...
          var currentColumn = $scope.col; // cache current column

          // if we're not supposed to filter this column, no need to activate filter for it...
          if (angular.isDefined(currentColumn.colDef.enableFiltering) && !currentColumn.colDef.enableFiltering) {
            return;
          }

          // get the filter type (default is string)
          var filterType = 'string';
          if (angular.isDefined(currentColumn.colDef.columnFilter) && angular.isDefined(currentColumn.colDef.columnFilter.type)){
            filterType = currentColumn.colDef.columnFilter.type;
          }
          else if (angular.isDefined(currentColumn.colDef.filter) && angular.isDefined(currentColumn.colDef.filter.type)){
            filterType = currentColumn.colDef.filter.type;
          }

          // get the filter popup template
          var thisFilterTemplate = 'ui-grid/filters/%%^^ColumnFilter'.replace('%%^^', filterType); // get the filter type template name
          var formElementsTemplate = $templateCache.get(thisFilterTemplate);
          var popupTemplate = $templateCache.get('ui-grid/filterPopup').replace('', formElementsTemplate); // get the full popup template

          // get the selection options if needed
          if (filterType === 'select'){
            currentColumn.colDef.columnFilter.logics = ["OR"];
            if (angular.isDefined(currentColumn.colDef.columnFilter) && angular.isDefined(currentColumn.colDef.columnFilter.selectOptions)){
              $scope.selectOptions = currentColumn.colDef.columnFilter.selectOptions;
            }
            else if (angular.isDefined(currentColumn.colDef.filter) && angular.isDefined(currentColumn.colDef.filter.selectOptions)){
              $scope.selectOptions = currentColumn.colDef.filter.selectOptions;
            }

            // remove multiple selection if needed - can be defined only in th e columnFilter right now
            if (angular.isDefined(currentColumn.colDef.columnFilter) && !currentColumn.colDef.columnFilter.multiple){
              popupTemplate = popupTemplate.replace('multiple', '');
              popupTemplate = popupTemplate.replace('.terms', '.terms[0]');
            }
          }

          $scope.filter = uiGridColumnsFiltersService.filter; // set the filtering function in the scope
          $scope.clear = uiGridColumnsFiltersService.clear; // set the clear filter function in the scope
          $scope.operators = uiGridColumnsFiltersConstants[filterType + 'Operators']; // set the operators in the scope
          $scope.logics = uiGridColumnsFiltersConstants.logics; // set the logics in the scope

          // toggle filter popup
          $scope.toggleFilter = function(){
            event.stopPropagation();
            event.preventDefault();

            if (currentColumn.grid.columnsFilters.currentColumn){
              // if we have an open filter
              angular.element(document.getElementById('uiGridFilterPopup')).remove(); //remove it
              if (angular.equals(currentColumn.grid.columnsFilters.currentColumn, currentColumn)){
                // if the same column that its filter shown is clicked, close it
                currentColumn.grid.columnsFilters.currentColumn = undefined; //clear the current open column popup
                return;
              }
            }

            // open a popup
            currentColumn.grid.columnsFilters.currentColumn = currentColumn; // set the current opened columnFilter
            $scope.filterPopupStyle = uiGridColumnsFiltersService.filterPopupStyle(event); //set the style in the scope
            var popupElement = $compile(popupTemplate)($scope); // compile it
            angular.element(document.body).append(popupElement); // append to body

            angular.element(document.body).on('click', $scope.toggleFilter); // make sure the popup closes when clicking outside

            // make sure popup is not closing when clicking inside
            popupElement.on('click', function(){
              event.preventDefault();
              event.stopPropagation();
            });

            // remove the click events on destroy
            popupElement.on('$destroy', function(){
              popupElement.off('click');
              angular.element(document.body).off('click', $scope.toggleFilter);
            });
          };
        }
      };
    }]);
})();
<commit_msg>Fixed a bug with service.clear<commit_after>(function () {
  'use strict';
  /**
   *  @ngdoc overview
   *  @name ui.grid.columnsFilters
   *
   *  @description
   *
   *  #ui.grid.columnsFilters
   *
   *
Beta This feature is ready for testing, but it either hasn't seen a lot of use or has some known bugs.

   *
   *  This module provides column filter in popup from the filter header cells.
   */
  var module = angular.module('ui.grid.columnsFilters', ['ui.grid']);

  /**
   *  @ngdoc object
   *  @name ui.grid.columnsFilters.constant:uiGridColumnsFiltersConstants
   *
   *  @description constants available in columnsFilters module.
   *
   *  @property {string} featureName               - The name of the feature.
   *  @property {object} filterType                - {
      STRING: 'string',
      NUMBER: 'number',
      DATE: 'date',
      SELECT: 'select'
    }
   * @property {object} dateType                - {
      DATE: 'date',
      TIME: 'time',
      DATETIME: 'datetime-local',
      DATETIMELOCALE: 'datetime-locale'
    }
   *  @property {object} numberOperators                - {
      8: 'Exact',
      512: 'Not Equal',
      128: 'Less than',
      256: 'Less than or equal',
      32: 'More than',
      64: 'More than or equal'
    }
   * @property {object} dateOperators                - {
      8: 'Exact',
      512: 'Not Equal',
      128: 'Before',
      256: 'Before or equal',
      32: 'Later',
      64: 'Later or equal'
    }
   * @property {object} stringOperators                - {
      16: 'Contains',
      4: 'Ends With',
      8: 'Exact',
      512: 'Not Equal',
      2: 'Starts With'
    }
   * @property {object} selectOperators                - {
      16: 'Contains',
      4: 'Ends With',
      8: 'Exact',
      512: 'Not Equal',
      2: 'Starts With'
    }
   * @property {object} logics                - {
      "OR": 'Or',
      "AND": 'And'
    }
   *
   */

  module.constant('uiGridColumnsFiltersConstants', {
    featureName: "columnsFilters",
    filterType: {
      STRING: 'string',
      NUMBER: 'number',
      DATE: 'date',
      SELECT: 'select'
    },
    dateTypes: {
      DATE: 'date',
      TIME: 'time',
      DATETIME: 'datetime-locale',
      DATETIMELOCALE: 'datetime-locale'
    },
    numberOperators: {
      8: 'Exact',
      512: 'Not Equal',
      128: 'Less than',
      256: 'Less than or equal',
      32: 'More than',
      64: 'More than or equal'
    },
    dateOperators: {
      8: 'Exact',
      512: 'Not Equal',
      128: 'Before',
      256: 'Before or equal',
      32: 'Later',
      64: 'Later or equal'
    },
    stringOperators: {
      16: 'Contains',
      4: 'Ends With',
      8: 'Exact',
      512: 'Not Equal',
      2: 'Starts With'
    },
    selectOperators: {
      16: 'Contains',
      4: 'Ends With',
      8: 'Exact',
      512: 'Not Equal',
      2: 'Starts With'
    },
    logics: {
      "OR": 'Or',
      "AND": 'And'
    }
  });

  /**
   *  @ngdoc service
   *  @name ui.grid.columnsFilters.service:uiGridColumnsFiltersService
   *
   *  @description Services for columnsFilters feature
   */
  /**
   *  @ngdoc object
   *  @name ui.grid.columnsFilters.api:ColumnDef
   *
   *  @description ColumnDef for column filter feature, these are available to be
   *  set using the ui-grid {@link ui.grid.class:GridOptions.columnDef gridOptions.columnDefs}
   *
   * @property {object} columnFilter - Specific column columnsFilters definitions
   * @property {string} columnFilter.type  - can be: 'date', 'select', 'string', 'number'
   * @property {string} columnFilter.type  - can be: 'date', 'select', 'string', 'number'
   * @property {boolean} columnFilter.multiple   - Boolean stating is a select filter would show as multiple or singular choice
   * @property {object} columnFilter.selectOptions   - states the values of a select option (if needed)
   * @property {object} columnFilter.terms   - holds the search terms for every filter form
   * @property {object} columnFilter.logics  - holds the logics (and/or) for every filter form
   * @property {object} columnFilter.operators   - holds the operators (bigger than, smaller than etc.) for every filter form
   */

  module.service('uiGridColumnsFiltersService', ['$q', 'uiGridColumnsFiltersConstants', 'rowSearcher', 'GridRow', 'gridClassFactory', 'i18nService', 'uiGridConstants', 'rowSorter', '$templateCache',
    function ($q, uiGridColumnsFiltersConstants, rowSearcher, GridRow, gridClassFactory, i18nService, uiGridConstants, rowSorter, $templateCache) {

      var runColumnFilter = rowSearcher.runColumnFilter;

      function columnFilter(searchTerm, cellValue, row, column) {
        var conditions = column.colDef.columnFilter.operators;
        var logics = column.colDef.columnFilter.logics;
        var filterPass = true;

        for (var i = 0; i < searchTerm.length; i++) {
          var term = searchTerm[i];
          var newFilter = rowSearcher.setupFilters([{
            term: term,
            condition: Number(conditions[i] ? conditions[i] : conditions[0]),
            flags: {
              caseSensitive: false
            }
          }])[0];
          // if we are on the second run check for "OR"
          if (i) {
            if (angular.isDefined(logics) && logics[i - 1] === 'OR') {
              return filterPass || runColumnFilter(row.grid, row, column, newFilter);
            }
            else {
              return filterPass && runColumnFilter(row.grid, row, column, newFilter);
            }
          }
          // TODO::check for the "select" condition in order to make sure we check for mulitple select
          filterPass = rowSearcher.runColumnFilter(row.grid, row, column, newFilter);
        }

        return filterPass;
      }

      var service = {
        initializeGrid: function (grid, $scope) {
          //add feature namespace and any properties to grid for needed
          /**
           *  @ngdoc object
           *  @name columnsFilters
           *  @name ui.grid.columnsFilters.api:Grid
           *
           *  @description Grid properties and functions added for columnsFilters
           *
           *  @property {object} columnsFilters - object that holds global definitions
           */
          grid.columnsFilters = {
            currentColumn: undefined
          };

          angular.forEach(grid.options.columnDefs, function (colDef) {
            if (colDef.enableFiltering !== false) {
              var columnFilter = {
                terms: [],
                operators: [],
                logics: []
              };

              if (angular.isUndefined(colDef.columnFilter)) {
                colDef.columnFilter = columnFilter;
              }
              else {
                colDef.columnFilter = angular.merge({}, columnFilter, colDef.columnFilter);
              }
              colDef.filterHeaderTemplate = $templateCache.get('ui-grid/filterButton');
            }
            else {
              colDef.filterHeaderTemplate = '';
            }
          });
        },
        /**
         * @ngdoc method
         * @methodOf ui.grid.columnsFilters.service:uiGridColumnsFiltersService
         * @name filterPopupStyle
         * @description Calculates the column filter's popup absolute position
         * @param {event} $event the event from the click event
         * @returns {object} and object with top and left styling expressions
         */
        filterPopupStyle: function ($event) {
          var rect = $event.target.parentElement.getClientRects()[0];
          return {
            top: document.body.scrollTop + (rect.height + rect.top) + 'px',
            left: rect.left + 'px'
          };
        },
        /**
         * @ngdoc method
         * @methodOf ui.grid.columnsFilters.service:uiGridColumnsFiltersService
         * @name filter
         * @description Sets the filter parameters of the column
         * @param {column} col - the column that is now being filtered
         */
        filter: function (col) {
          var terms = col.colDef.columnFilter.terms;

          var logics = col.colDef.columnFilter.logics;

          // add the data into the filter object of the column
          // the terms array is the "term"
          col.filters[0].term = terms;

          // set condition as our filter function
          col.filters[0].condition = columnFilter;

          // logic is new, so we will add it, and handle it in our override function
          col.filters[0].logic = logics;
          col.grid.api.core.notifyDataChange(uiGridConstants.dataChange.COLUMN);
        },
        /**
         * @ngdoc method
         * @methodOf ui.grid.columnsFilters.service:uiGridColumnsFiltersService
         * @name clear
         * @description Clears the filter parameters of the column
         * @param {column} col -  the column that is now being filtered
         */
        clear: function (col) {
          if (angular.isUndefined(col.filters[0].term) || !angular.isArray(col.filters[0].term)) {
            return;
          }

          col.filters[0].term.length = 0;
          col.filters[0].condition = undefined;
          col.grid.api.core.notifyDataChange(uiGridConstants.dataChange.COLUMN);
        }
      };

      return service;
    }]);

  module.directive('uiGridColumnsFiltersDirective', ['$compile', 'gridUtil', 'uiGridColumnsFiltersService', 'uiGridColumnsFiltersConstants', '$templateCache', '$document',
    function ($compile, gridUtil, uiGridColumnsFiltersService, uiGridColumnsFiltersConstants, $templateCache, $document) {
      return {
        require: 'uiGrid',
        scope: false,
        link: function ($scope, $elm, $attrs, uiGridCtrl) {
          uiGridColumnsFiltersService.initializeGrid(uiGridCtrl.grid, $scope);
        }
      };

      /**
       *  @ngdoc directive
       *  @name ui.grid.columnsFilters.directive:uiGridColumnFiltersDirective
       *
       *  @description directive for columnsFilters button in column header
       */

    }]);

  /**
   *  @ngdoc directive
   *  @name ui.grid.columnsFilters.directive.api:uiGridFilter
   *
   *  @description Extanding the uiGridFilter directive to prepare the column filter
   */
  module.directive('uiGridFilter', ['uiGridColumnsFiltersService', 'uiGridColumnsFiltersConstants', '$templateCache', '$compile',
    function (uiGridColumnsFiltersService, uiGridColumnsFiltersConstants, $templateCache, $compile) {
      return {
        priority: 500,
        scope: false,
        link: function ($scope, $elm, $attrs, uiGridCtrl) {
          //TODO::need to decide if we work with the filter API when it is sufficient and only expand it...
          var currentColumn = $scope.col; // cache current column

          // if we're not supposed to filter this column, no need to activate filter for it...
          if (angular.isDefined(currentColumn.colDef.enableFiltering) && !currentColumn.colDef.enableFiltering) {
            return;
          }

          // get the filter type (default is string)
          var filterType = 'string';
          if (angular.isDefined(currentColumn.colDef.columnFilter) && angular.isDefined(currentColumn.colDef.columnFilter.type)) {
            filterType = currentColumn.colDef.columnFilter.type;
          }
          else if (angular.isDefined(currentColumn.colDef.filter) && angular.isDefined(currentColumn.colDef.filter.type)) {
            filterType = currentColumn.colDef.filter.type;
          }

          // get the filter popup template
          var thisFilterTemplate = 'ui-grid/filters/%%^^ColumnFilter'.replace('%%^^', filterType); // get the filter type template name
          var formElementsTemplate = $templateCache.get(thisFilterTemplate);
          var popupTemplate = $templateCache.get('ui-grid/filterPopup').replace('', formElementsTemplate); // get the full popup template

          // get the selection options if needed
          if (filterType === 'select') {
            currentColumn.colDef.columnFilter.logics = ["OR"];
            if (angular.isDefined(currentColumn.colDef.columnFilter) && angular.isDefined(currentColumn.colDef.columnFilter.selectOptions)) {
              $scope.selectOptions = currentColumn.colDef.columnFilter.selectOptions;
            }
            else if (angular.isDefined(currentColumn.colDef.filter) && angular.isDefined(currentColumn.colDef.filter.selectOptions)) {
              $scope.selectOptions = currentColumn.colDef.filter.selectOptions;
            }

            // remove multiple selection if needed - can be defined only in th e columnFilter right now
            if (angular.isDefined(currentColumn.colDef.columnFilter) && !currentColumn.colDef.columnFilter.multiple) {
              popupTemplate = popupTemplate.replace('multiple', '');
              popupTemplate = popupTemplate.replace('.terms', '.terms[0]');
            }

            if (angular.isUndefined($scope.selectOptions)) {
              // if we have select options, it means we have definitions set and we use the static def
              $scope.$watchCollection('col.grid.options.data', function (newValue, oldValue) {
                // watch for changes in the data
                if (angular.isUndefined(newValue) || !newValue.length) {
                  return;
                }

                // now wait for the rows to be updated with the new data
                var watchForRows = $scope.$watch('col.grid.rows.length', function (newRowsLength) {
                  // make sure we have updated...
                  if (newRowsLength !== newValue.length) {
                    return;
                  }
                  // set the options
                  $scope.selectOptions = $scope.setSelectOptions($scope.selectOptions, currentColumn);
                  // remove the listener
                  watchForRows();
                });
              });
            }

            $scope.setSelectOptions = function (items, col) {
              // if we have static definitions, do nothing
              if (angular.isDefined(col.colDef.filter) && angular.isDefined(col.colDef.filter.selectOptions) ||
                angular.isDefined(col.colDef.columnFilter) && angular.isDefined(col.colDef.columnFilter.selectOptions)) {
                return items;
              }

              // if we don't create a dynamic selectOptions array
              var filteredItems = [];
              var tmpIDs = [];
              var tmpItem = {};
              var rows = col.grid.rows;

              // for every row in the grid
              for (var i = 0; i < rows.length; i++) {
                // get the label and the value
                tmpItem.label = col.grid.getCellDisplayValue(rows[i], col);
                tmpItem.value = col.grid.getCellValue(rows[i], col);

                // make sure we take only unique values
                if (tmpIDs.indexOf(tmpItem.value) === -1) {
                  tmpIDs.push(tmpItem.value);
                  filteredItems.push(angular.copy(tmpItem));
                }

              }

              // insert the items into the selectOptions array
              items = filteredItems;
              return items;
            };
          }

          $scope.filter = uiGridColumnsFiltersService.filter; // set the filtering function in the scope
          $scope.clear = uiGridColumnsFiltersService.clear; // set the clear filter function in the scope
          $scope.operators = uiGridColumnsFiltersConstants[filterType + 'Operators']; // set the operators in the scope
          $scope.logics = uiGridColumnsFiltersConstants.logics; // set the logics in the scope

          // toggle filter popup
          $scope.toggleFilter = function () {
            event.stopPropagation();
            event.preventDefault();

            if (currentColumn.grid.columnsFilters.currentColumn) {
              // if we have an open filter
              angular.element(document.getElementById('uiGridFilterPopup')).remove(); //remove it
              if (angular.equals(currentColumn.grid.columnsFilters.currentColumn, currentColumn)) {
                // if the same column that its filter shown is clicked, close it
                currentColumn.grid.columnsFilters.currentColumn = undefined; //clear the current open column popup
                return;
              }
            }

            // open a popup
            currentColumn.grid.columnsFilters.currentColumn = currentColumn; // set the current opened columnFilter
            $scope.filterPopupStyle = uiGridColumnsFiltersService.filterPopupStyle(event); //set the style in the scope
            var popupElement = $compile(popupTemplate)($scope); // compile it
            angular.element(document.body).append(popupElement); // append to body

            angular.element(document.body).on('click', $scope.toggleFilter); // make sure the popup closes when clicking outside

            // make sure popup is not closing when clicking inside
            popupElement.on('click', function () {
              event.preventDefault();
              event.stopPropagation();
            });

            // remove the click events on destroy
            popupElement.on('$destroy', function () {
              popupElement.off('click');
              angular.element(document.body).off('click', $scope.toggleFilter);
            });
          };
        }
      };
    }]);

  module.filter('filterSelectValues', function () {
    return function (items, col) {

    };
  });

})();
<|endoftext|>


<commit_before>var NB = require('../lib/nodebrainz');

NB.prototype.request = function(config, callback){

  var options = {
    host: config.host,
    path: config.path,
    headers: {
      'user-agent': config.userAgent
    },
    json: true
  };

  callback(null,{response:'faked'}) ;
};

var assert = require("assert");

describe('nb', function(){

  // Describe the constructor
  describe('Constructor', function(){

    describe('Defaults', function(){
      it('limit', function(){
        var nb = new NB();
        assert.equal(nb.limit, 25);
      });

      it('host', function(){
        var nb = new NB();
        assert.equal(nb.host, 'musicbrainz.org');
      });

      it('path', function(){
        var nb = new NB();
        assert.equal(nb.basePath, '/ws/2/');
      });
    });

    it('Can set User Agent', function(){
      var nb = new NB({userAgent:'my-app/0.0.1 ( http://myapp.com )'});
      assert.equal(nb.userAgent, 'my-app/0.0.1 ( http://myapp.com )');

    });

    it('Can set Host', function(){
      var nb = new NB({host:'localhost'});
      assert.equal(nb.host, 'localhost');

    });

    it('Can set Path', function(){
      var nb = new NB({basePath:'/path/to/data/'});
      assert.equal(nb.basePath, '/path/to/data/');
    });

    it('Can set Default limit', function(){
      var nb = new NB({defaultLimit:50});
      assert.equal(nb.limit, 50);
    });

    it('Can set all at the same time', function(){
      var nb = new NB({host:'localhost',basePath:'/path/to/data/',userAgent:'my-app/0.0.1 ( http://myapp.com )'});
      assert.equal(nb.userAgent, 'my-app/0.0.1 ( http://myapp.com )');
      assert.equal(nb.host, 'localhost');
      assert.equal(nb.basePath, '/path/to/data/');
    });

  });

  // Describe lookup for...
  describe('Lookup', function(){

    // Artists
    describe('Artist', function(){

      it('By Artist ID', function(){
        var nb = new NB();

        nb.artist('e0140a67-e4d1-4f13-8a01-364355bee46e', {} , function(err, response){
          assert.equal(err, null);
          assert.equal(nb.path, '/ws/2/artist/e0140a67-e4d1-4f13-8a01-364355bee46e?fmt=json');
        });
      });

      it('Only two arguments', function(){
        var nb = new NB();

        nb.artist('e0140a67-e4d1-4f13-8a01-364355bee46e', function(err, response){
          assert.equal(err, null);
          assert.equal(nb.path, '/ws/2/artist/e0140a67-e4d1-4f13-8a01-364355bee46e?fmt=json');
        });
      });

      it('With subquery', function(){
        var nb = new NB();

        nb.artist('e0140a67-e4d1-4f13-8a01-364355bee46e', {inc:'aliases+release-groups'} , function(err, response){
          assert.equal(err, null);
          assert.equal(nb.path, '/ws/2/artist/e0140a67-e4d1-4f13-8a01-364355bee46e?fmt=json&inc=aliases+release-groups');
        });
      });
    });

    // Release
    describe('Release', function(){

      it('By Release ID', function(){
        var nb = new NB();

        nb.release('6bfba6d5-71fc-454b-b3a0-63632a1459fa', function(err, response){
          assert.equal(err, null);
          assert.equal(nb.path, '/ws/2/release/6bfba6d5-71fc-454b-b3a0-63632a1459fa?fmt=json');
        });
      });
    });

    // Release Group
    describe('Release Group', function(){

      it('By Release Group ID', function(){
        var nb = new NB();

        nb.releaseGroup('6bfba6d5-71fc-454b-b3a0-63632a1459fa', function(err, response){
          assert.equal(err, null);
          assert.equal(nb.path, '/ws/2/release-group/6bfba6d5-71fc-454b-b3a0-63632a1459fa?fmt=json');
        });
      });
    });

    // Recording
    describe('Recording', function(){

      it('By Recording ID', function(){
        var nb = new NB();

        nb.recording('811cfc83-0c1a-44d6-b644-3740ac313016', function(err, response){
          assert.equal(err, null);
          assert.equal(nb.path, '/ws/2/recording/811cfc83-0c1a-44d6-b644-3740ac313016?fmt=json');
        });
      });
    });

    // Work
    describe('Work', function(){

      it('By Work ID', function(){
        var nb = new NB();

        nb.work('282c08bb-9bd8-37be-9e36-f816d16f9a48', function(err, response){
          assert.equal(err, null);
          assert.equal(nb.path, '/ws/2/work/282c08bb-9bd8-37be-9e36-f816d16f9a48?fmt=json');
        });
      });
    });

    // Label
    describe('Label', function(){

      it('By Label ID', function(){
        var nb = new NB();

        nb.label('dfd92cd3-4888-46d2-b968-328b1feb2642', function(err, response){
          assert.equal(err, null);
          assert.equal(nb.path, '/ws/2/label/dfd92cd3-4888-46d2-b968-328b1feb2642?fmt=json');
        });
      });
    });

    // URL
    describe('URL', function(){

      it('By URL ID', function(){
        var nb = new NB();

        nb.url('13a37218-94c4-4844-8f6e-f843fe88e444', function(err, response){
          assert.equal(err, null);
          assert.equal(nb.path, '/ws/2/url/13a37218-94c4-4844-8f6e-f843fe88e444?fmt=json');
        });
      });
    });

    // Area
    describe('Area', function(){

      it('By Area ID', function(){
        var nb = new NB();

        nb.area('db3634e7-5414-41dd-be0b-68ae71798dcd', function(err, response){
          assert.equal(err, null);
          assert.equal(nb.path, '/ws/2/area/db3634e7-5414-41dd-be0b-68ae71798dcd?fmt=json');
        });
      });
    });
  });

  // Describe search
  describe('Search', function(){

    // A query must be set
    it('Must set query', function(){
      var nb = new NB();

      nb.search('artist', {} , function(err, response){
        assert.notEqual(err, null);
      });
    });

    // Test search
    it('Basic functionality', function(){
      var nb = new NB();

      nb.search('artist', {artist:'tool', country:'US'} , function(err, response){
        assert.equal(err, null);
        assert.equal(nb.path, '/ws/2/artist/?query=artist:\"tool\"%20AND%20country:\"US\"&limit=25&offset=0&fmt=json');
      });
    });

    // Test search
    it('Limit and offset', function(){
      var nb = new NB();

      nb.search('release', {artist:'pink floyd', limit:20, offset:5}, function(err, response){
        assert.equal(err, null);
        assert.equal(nb.path, '/ws/2/release/?query=artist:"pink%20floyd"&limit=20&offset=5&fmt=json');
      });
    });

  });

  // Describe Lucene search
  describe('Lucene Search', function(){

    // A query must be set
    it('Must set query', function(){
      var nb = new NB();

      nb.luceneSearch('artist', {} , function(err, response){
        assert.notEqual(err, null);
      });

      nb.luceneSearch('artist', {limit:5} , function(err, response){
        assert.notEqual(err, null);
      });

      nb.luceneSearch('artist', {query:'artist:t??l AND -artist:"Jethro Tull"'} , function(err, response){
        assert.equal(err, null);
      });
    });

    // Test search
    it('Basic functionality', function(){
      var nb = new NB();

      nb.luceneSearch('artist', {query:'artist:t??l AND -artist:"Jethro Tull"'} , function(err, response){
        assert.equal(err, null);
        assert.equal(nb.path, '/ws/2/artist/?query=artist%3At%3F%3Fl%20AND%20-artist%3A%22Jethro%20Tull%22&limit=25&offset=0&fmt=json');
      });
    });

    // Limit and offset
    it('Limit and offset', function(){
      var nb = new NB();

      nb.luceneSearch('artist', {query:'artist:t??l AND -artist:"Jethro Tull"', limit:5, offset:10} , function(err, response){
        assert.equal(err, null);
        assert.equal(nb.path, '/ws/2/artist/?query=artist%3At%3F%3Fl%20AND%20-artist%3A%22Jethro%20Tull%22&limit=5&offset=10&fmt=json');
      });
    });
  });

  // Describe Browse
  describe('Browse', function(){

    // A query must be set
    it('Basic functionality', function(){
      var nb = new NB();

      nb.browse('release-group', {artist:'e0140a67-e4d1-4f13-8a01-364355bee46e'}, function(err, response){
          assert.equal(nb.path, '/ws/2/release-group?fmt=json&artist=e0140a67-e4d1-4f13-8a01-364355bee46e');
      });
    });

    // Limit and offset
    it('Limit and offset', function(){
      var nb = new NB();

      nb.browse('release-group', {artist:'e0140a67-e4d1-4f13-8a01-364355bee46e', limit:2, offset:1}, function(err, response){
          assert.equal(nb.path, '/ws/2/release-group?fmt=json&artist=e0140a67-e4d1-4f13-8a01-364355bee46e&limit=2&offset=1');
      });

    });

    // A little more functionality
    it('Advance functionality', function(){
      var nb = new NB();

      nb.browse('release-group', {artist:'e0140a67-e4d1-4f13-8a01-364355bee46e', type:'album', limit:2, offset:1, inc: 'artist-credits'}, function(err, response){
          assert.equal(nb.path, '/ws/2/release-group?fmt=json&artist=e0140a67-e4d1-4f13-8a01-364355bee46e&type=album&limit=2&offset=1&inc=artist-credits');
      });

    });

  });
});
<commit_msg>Fake commit<commit_after>var NB = require('../lib/nodebrainz');

// We don't actually want to make real http requests, this fakes it for us.
NB.prototype.request = function(config, callback){

  var options = {
    host: config.host,
    path: config.path,
    headers: {
      'user-agent': config.userAgent
    },
    json: true
  };

  callback(null,{response:'faked'}) ;
};

var assert = require("assert");

describe('nb', function(){

  // Describe the constructor
  describe('Constructor', function(){

    describe('Defaults', function(){
      it('limit', function(){
        var nb = new NB();
        assert.equal(nb.limit, 25);
      });

      it('host', function(){
        var nb = new NB();
        assert.equal(nb.host, 'musicbrainz.org');
      });

      it('path', function(){
        var nb = new NB();
        assert.equal(nb.basePath, '/ws/2/');
      });
    });

    it('Can set User Agent', function(){
      var nb = new NB({userAgent:'my-app/0.0.1 ( http://myapp.com )'});
      assert.equal(nb.userAgent, 'my-app/0.0.1 ( http://myapp.com )');

    });

    it('Can set Host', function(){
      var nb = new NB({host:'localhost'});
      assert.equal(nb.host, 'localhost');

    });

    it('Can set Path', function(){
      var nb = new NB({basePath:'/path/to/data/'});
      assert.equal(nb.basePath, '/path/to/data/');
    });

    it('Can set Default limit', function(){
      var nb = new NB({defaultLimit:50});
      assert.equal(nb.limit, 50);
    });

    it('Can set all at the same time', function(){
      var nb = new NB({host:'localhost',basePath:'/path/to/data/',userAgent:'my-app/0.0.1 ( http://myapp.com )'});
      assert.equal(nb.userAgent, 'my-app/0.0.1 ( http://myapp.com )');
      assert.equal(nb.host, 'localhost');
      assert.equal(nb.basePath, '/path/to/data/');
    });

  });

  // Describe lookup for...
  describe('Lookup', function(){

    // Artists
    describe('Artist', function(){

      it('By Artist ID', function(){
        var nb = new NB();

        nb.artist('e0140a67-e4d1-4f13-8a01-364355bee46e', {} , function(err, response){
          assert.equal(err, null);
          assert.equal(nb.path, '/ws/2/artist/e0140a67-e4d1-4f13-8a01-364355bee46e?fmt=json');
        });
      });

      it('Only two arguments', function(){
        var nb = new NB();

        nb.artist('e0140a67-e4d1-4f13-8a01-364355bee46e', function(err, response){
          assert.equal(err, null);
          assert.equal(nb.path, '/ws/2/artist/e0140a67-e4d1-4f13-8a01-364355bee46e?fmt=json');
        });
      });

      it('With subquery', function(){
        var nb = new NB();

        nb.artist('e0140a67-e4d1-4f13-8a01-364355bee46e', {inc:'aliases+release-groups'} , function(err, response){
          assert.equal(err, null);
          assert.equal(nb.path, '/ws/2/artist/e0140a67-e4d1-4f13-8a01-364355bee46e?fmt=json&inc=aliases+release-groups');
        });
      });
    });

    // Release
    describe('Release', function(){

      it('By Release ID', function(){
        var nb = new NB();

        nb.release('6bfba6d5-71fc-454b-b3a0-63632a1459fa', function(err, response){
          assert.equal(err, null);
          assert.equal(nb.path, '/ws/2/release/6bfba6d5-71fc-454b-b3a0-63632a1459fa?fmt=json');
        });
      });
    });

    // Release Group
    describe('Release Group', function(){

      it('By Release Group ID', function(){
        var nb = new NB();

        nb.releaseGroup('6bfba6d5-71fc-454b-b3a0-63632a1459fa', function(err, response){
          assert.equal(err, null);
          assert.equal(nb.path, '/ws/2/release-group/6bfba6d5-71fc-454b-b3a0-63632a1459fa?fmt=json');
        });
      });
    });

    // Recording
    describe('Recording', function(){

      it('By Recording ID', function(){
        var nb = new NB();

        nb.recording('811cfc83-0c1a-44d6-b644-3740ac313016', function(err, response){
          assert.equal(err, null);
          assert.equal(nb.path, '/ws/2/recording/811cfc83-0c1a-44d6-b644-3740ac313016?fmt=json');
        });
      });
    });

    // Work
    describe('Work', function(){

      it('By Work ID', function(){
        var nb = new NB();

        nb.work('282c08bb-9bd8-37be-9e36-f816d16f9a48', function(err, response){
          assert.equal(err, null);
          assert.equal(nb.path, '/ws/2/work/282c08bb-9bd8-37be-9e36-f816d16f9a48?fmt=json');
        });
      });
    });

    // Label
    describe('Label', function(){

      it('By Label ID', function(){
        var nb = new NB();

        nb.label('dfd92cd3-4888-46d2-b968-328b1feb2642', function(err, response){
          assert.equal(err, null);
          assert.equal(nb.path, '/ws/2/label/dfd92cd3-4888-46d2-b968-328b1feb2642?fmt=json');
        });
      });
    });

    // URL
    describe('URL', function(){

      it('By URL ID', function(){
        var nb = new NB();

        nb.url('13a37218-94c4-4844-8f6e-f843fe88e444', function(err, response){
          assert.equal(err, null);
          assert.equal(nb.path, '/ws/2/url/13a37218-94c4-4844-8f6e-f843fe88e444?fmt=json');
        });
      });
    });

    // Area
    describe('Area', function(){

      it('By Area ID', function(){
        var nb = new NB();

        nb.area('db3634e7-5414-41dd-be0b-68ae71798dcd', function(err, response){
          assert.equal(err, null);
          assert.equal(nb.path, '/ws/2/area/db3634e7-5414-41dd-be0b-68ae71798dcd?fmt=json');
        });
      });
    });
  });

  // Describe search
  describe('Search', function(){

    // A query must be set
    it('Must set query', function(){
      var nb = new NB();

      nb.search('artist', {} , function(err, response){
        assert.notEqual(err, null);
      });
    });

    // Test search
    it('Basic functionality', function(){
      var nb = new NB();

      nb.search('artist', {artist:'tool', country:'US'} , function(err, response){
        assert.equal(err, null);
        assert.equal(nb.path, '/ws/2/artist/?query=artist:\"tool\"%20AND%20country:\"US\"&limit=25&offset=0&fmt=json');
      });
    });

    // Test search
    it('Limit and offset', function(){
      var nb = new NB();

      nb.search('release', {artist:'pink floyd', limit:20, offset:5}, function(err, response){
        assert.equal(err, null);
        assert.equal(nb.path, '/ws/2/release/?query=artist:"pink%20floyd"&limit=20&offset=5&fmt=json');
      });
    });

  });

  // Describe Lucene search
  describe('Lucene Search', function(){

    // A query must be set
    it('Must set query', function(){
      var nb = new NB();

      nb.luceneSearch('artist', {} , function(err, response){
        assert.notEqual(err, null);
      });

      nb.luceneSearch('artist', {limit:5} , function(err, response){
        assert.notEqual(err, null);
      });

      nb.luceneSearch('artist', {query:'artist:t??l AND -artist:"Jethro Tull"'} , function(err, response){
        assert.equal(err, null);
      });
    });

    // Test search
    it('Basic functionality', function(){
      var nb = new NB();

      nb.luceneSearch('artist', {query:'artist:t??l AND -artist:"Jethro Tull"'} , function(err, response){
        assert.equal(err, null);
        assert.equal(nb.path, '/ws/2/artist/?query=artist%3At%3F%3Fl%20AND%20-artist%3A%22Jethro%20Tull%22&limit=25&offset=0&fmt=json');
      });
    });

    // Limit and offset
    it('Limit and offset', function(){
      var nb = new NB();

      nb.luceneSearch('artist', {query:'artist:t??l AND -artist:"Jethro Tull"', limit:5, offset:10} , function(err, response){
        assert.equal(err, null);
        assert.equal(nb.path, '/ws/2/artist/?query=artist%3At%3F%3Fl%20AND%20-artist%3A%22Jethro%20Tull%22&limit=5&offset=10&fmt=json');
      });
    });
  });

  // Describe Browse
  describe('Browse', function(){

    // A query must be set
    it('Basic functionality', function(){
      var nb = new NB();

      nb.browse('release-group', {artist:'e0140a67-e4d1-4f13-8a01-364355bee46e'}, function(err, response){
          assert.equal(nb.path, '/ws/2/release-group?fmt=json&artist=e0140a67-e4d1-4f13-8a01-364355bee46e');
      });
    });

    // Limit and offset
    it('Limit and offset', function(){
      var nb = new NB();

      nb.browse('release-group', {artist:'e0140a67-e4d1-4f13-8a01-364355bee46e', limit:2, offset:1}, function(err, response){
          assert.equal(nb.path, '/ws/2/release-group?fmt=json&artist=e0140a67-e4d1-4f13-8a01-364355bee46e&limit=2&offset=1');
      });

    });

    // A little more functionality
    it('Advance functionality', function(){
      var nb = new NB();

      nb.browse('release-group', {artist:'e0140a67-e4d1-4f13-8a01-364355bee46e', type:'album', limit:2, offset:1, inc: 'artist-credits'}, function(err, response){
          assert.equal(nb.path, '/ws/2/release-group?fmt=json&artist=e0140a67-e4d1-4f13-8a01-364355bee46e&type=album&limit=2&offset=1&inc=artist-credits');
      });
    });

  });
});
<|endoftext|>


<commit_before>import { hashHistory } from 'react-router';
import api from '../../common/api';

export const GET_CATEGORIES = 'GET_CATEGORIES';
export const POST_CHALLENGE = 'POST_CHALLENGE';

function receiveCategories(categories) {
    return {
        type: GET_CATEGORIES,
        categories
    };
}

function receiveChallenge(challenge) {
    return {
        type: POST_CHALLENGE,
        challenge
    };
}

export function fetchCategories() {
    return function innerFetchCategories(dispatch) {
        api('/api/categories')
            .then((categories) => {
                dispatch(receiveCategories(categories));
            });
    };
}

export function postChallenge(formData) {
    return function innerPostCategories(dispatch) {
        api('/api/challenges/', {
            method: 'POST',
            body: JSON.stringify(formData) })
            .then((response) => {
                dispatch(receiveChallenge(response));
                hashHistory.goBack();
            });
    };
}

<commit_msg>Integretion with Image Hosting<commit_after>import { hashHistory } from 'react-router';
import api from '../../common/api';

export const GET_CATEGORIES = 'GET_CATEGORIES';
export const POST_CHALLENGE = 'POST_CHALLENGE';

function receiveCategories(categories) {
    return {
        type: GET_CATEGORIES,
        categories
    };
}

function receiveChallenge(challenge) {
    return {
        type: POST_CHALLENGE,
        challenge
    };
}

export function fetchCategories() {
    return function innerFetchCategories(dispatch) {
        api('/api/categories')
            .then((categories) => {
                dispatch(receiveCategories(categories));
            });
    };
}

export function postChallenge(formData) {
    return function innerPostChallenge(dispatch) {
        const baseUrl = 'http://ec2-52-35-85-119.us-west-2.compute.amazonaws.com:8080/';
        const hostingRequest = {
            file: formData.image
        };
        const headers = new Headers({
            "Content-Type": "multipart/form-data"
        });
        fetch(`${baseUrl}picture`,  {
            method: 'POST',
            headers,
            body: JSON.stringify(hostingRequest) })
            .then(response => response.json())
            .then(response => `${baseUrl}${response.path}`)
            .then(image => {
                api('/api/challenges/', {
                    method: 'POST',
                    body: JSON.stringify({...formData, image}) })
                    .then((response) => {
                        dispatch(receiveChallenge(response));
                        hashHistory.goBack();
                    });
            });
    };
}

<|endoftext|>


<commit_before>// mozilla chrome compat layer -- very similar to adapter.js
function setupRTC() {
    var RTC = null;
    if (navigator.mozGetUserMedia && mozRTCPeerConnection) {
        console.log('This appears to be Firefox');
        var version = parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1]);
        if (version >= 22) {
            RTC = {
                peerconnection: mozRTCPeerConnection,
                browser: 'firefox',
                getUserMedia: navigator.mozGetUserMedia.bind(navigator),
                attachMediaStream: function(element, stream) {
                    element[0].mozSrcObject = stream;
                    element[0].play();
                },
                pc_constraints: {}
            };
            if (!MediaStream.prototype.getVideoTracks)
                MediaStream.prototype.getVideoTracks = function() { return []; };
            if (!MediaStream.prototype.getAudioTracks)
                MediaStream.prototype.getAudioTracks = function() { return []; };
            RTCSessionDescription = mozRTCSessionDescription;
            RTCIceCandidate = mozRTCIceCandidate;
        }
    } else if (navigator.webkitGetUserMedia) {
        console.log('This appears to be Chrome');
        RTC = {
            peerconnection: webkitRTCPeerConnection,
            browser: 'chrome',
            getUserMedia: navigator.webkitGetUserMedia.bind(navigator),
            attachMediaStream: function(element, stream) {
                element.attr('src', webkitURL.createObjectURL(stream));
            },
//            pc_constraints: {} // FIVE-182
            pc_constraints: {'optional': [{'DtlsSrtpKeyAgreement': 'true'}]} // enable dtls support in canary
        };
        if (navigator.userAgent.indexOf('Android') != -1) {
            RTC.pc_constraints = {}; // disable DTLS on Android
        }
        if (!webkitMediaStream.prototype.getVideoTracks) {
            webkitMediaStream.prototype.getVideoTracks = function()
            { return this.videoTracks; };
        }
        if (!webkitMediaStream.prototype.getAudioTracks) {
            webkitMediaStream.prototype.getAudioTracks = function()
            { return this.audioTracks; };
        }
    }
    if (RTC == null) {
        try { console.log('Browser does not appear to be WebRTC-capable'); } catch (e) { }
    }
    return RTC;
}

function getUserMediaWithConstraints(um, resolution, bandwidth, fps) {

    var constraints = {audio: false, video: false};

    if ($.inArray('video', um) >= 0) {
        constraints.video = true;
    }
    if ($.inArray('audio', um) >= 0) {
        constraints.audio = true;
    }
    if ($.inArray('screen', um) >= 0) {
        constraints.video = {
            "mandatory": {
                "chromeMediaSource": "screen"
            }
        }
    }

    // see https://code.google.com/p/chromium/issues/detail?id=143631#c9 for list of supported resolutions
    switch (resolution) {
        // 16:9 first
        case '720':
        case 'hd':
            constraints.video = {mandatory: {minWidth: 1280, minHeight: 720, minAspectRatio: 1.77}};
            break;
        case '360':
            constraints.video = {mandatory: {minWidth: 640, minHeight: 360, minAspectRatio: 1.77}};
            break;
        case '180':
            constraints.video = {mandatory: {minWidth: 320, minHeight: 180, minAspectRatio: 1.77}};
            break;
            // 4:3
        case '960':
            constraints.video = {mandatory: {minWidth: 960, minHeight: 720}};
            break;
        case '640':
        case 'vga':
            constraints.video = {mandatory: {maxWidth: 640, maxHeight: 480}};
            break;
        case '320':
            constraints.video = {mandatory: {maxWidth: 320, maxHeight: 240}};
            break;
        default:
            if (navigator.userAgent.indexOf('Android') != -1) {
                constraints.video = {mandatory: {maxWidth: 320, maxHeight: 240, maxFrameRate: 15}};
            }
            break;
    }

    if (bandwidth) { // doesn't work currently, see webrtc issue 1846
        constraints.video.optional = [{bandwidth: bandwidth}];
    }
    if (fps) { // for some cameras it might be necessary to request 30fps
        // so they choose 30fps mjpg over 10fps yuy2
        constraints.video.mandatory['minFrameRate'] = fps;
    }

    try {
        RTC.getUserMedia(constraints,
                function(stream) {
                    console.log('onUserMediaSuccess');
                    $(document).trigger('mediaready.jingle', [stream]);
                },
                function(error) {
                    console.warn('Failed to get access to local media. Error ', error);
                    $(document).trigger('mediafailure.jingle');
                });
    } catch (e) {
        console.error('GUM failed: ', e);
        $(document).trigger('mediafailure.jingle');
    }
}
<commit_msg>fix improper mandatory constraints handling<commit_after>// mozilla chrome compat layer -- very similar to adapter.js
function setupRTC() {
    var RTC = null;
    if (navigator.mozGetUserMedia && mozRTCPeerConnection) {
        console.log('This appears to be Firefox');
        var version = parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1]);
        if (version >= 22) {
            RTC = {
                peerconnection: mozRTCPeerConnection,
                browser: 'firefox',
                getUserMedia: navigator.mozGetUserMedia.bind(navigator),
                attachMediaStream: function(element, stream) {
                    element[0].mozSrcObject = stream;
                    element[0].play();
                },
                pc_constraints: {}
            };
            if (!MediaStream.prototype.getVideoTracks)
                MediaStream.prototype.getVideoTracks = function() { return []; };
            if (!MediaStream.prototype.getAudioTracks)
                MediaStream.prototype.getAudioTracks = function() { return []; };
            RTCSessionDescription = mozRTCSessionDescription;
            RTCIceCandidate = mozRTCIceCandidate;
        }
    } else if (navigator.webkitGetUserMedia) {
        console.log('This appears to be Chrome');
        RTC = {
            peerconnection: webkitRTCPeerConnection,
            browser: 'chrome',
            getUserMedia: navigator.webkitGetUserMedia.bind(navigator),
            attachMediaStream: function(element, stream) {
                element.attr('src', webkitURL.createObjectURL(stream));
            },
//            pc_constraints: {} // FIVE-182
            pc_constraints: {'optional': [{'DtlsSrtpKeyAgreement': 'true'}]} // enable dtls support in canary
        };
        if (navigator.userAgent.indexOf('Android') != -1) {
            RTC.pc_constraints = {}; // disable DTLS on Android
        }
        if (!webkitMediaStream.prototype.getVideoTracks) {
            webkitMediaStream.prototype.getVideoTracks = function()
            { return this.videoTracks; };
        }
        if (!webkitMediaStream.prototype.getAudioTracks) {
            webkitMediaStream.prototype.getAudioTracks = function()
            { return this.audioTracks; };
        }
    }
    if (RTC == null) {
        try { console.log('Browser does not appear to be WebRTC-capable'); } catch (e) { }
    }
    return RTC;
}

function getUserMediaWithConstraints(um, resolution, bandwidth, fps) {
    var constraints = {audio: false, video: false};

    if ($.inArray('video', um) >= 0) {
        constraints.video = {mandatory:{}};// same behaviour as true
    }
    if ($.inArray('audio', um) >= 0) {
        constraints.audio = {};// same behaviour as true
    }
    if ($.inArray('screen', um) >= 0) {
        constraints.video = {
            "mandatory": {
                "chromeMediaSource": "screen"
            }
        }
    }

    if (resolution && !constraints.video) {
        constraints.video = {mandatory:{}};// same behaviour as true
    }
    // see https://code.google.com/p/chromium/issues/detail?id=143631#c9 for list of supported resolutions
    switch (resolution) {
        // 16:9 first
        case '1080':
        case 'fullhd':
            constraints.video.mandatory.minWidth = 1920;
            constraints.video.mandatory.minHeight = 1080;
            constraints.video.mandatory.minAspectRatio = 1.77;
            break;
        case '720':
        case 'hd':
            constraints.video.mandatory.minWidth = 1280;
            constraints.video.mandatory.minHeight = 720;
            constraints.video.mandatory.minAspectRatio = 1.77;
            break;
        case '360':
            constraints.video.mandatory.minWidth = 640;
            constraints.video.mandatory.minHeight = 360;
            constraints.video.mandatory.minAspectRatio = 1.77;
            break;
        case '180':
            constraints.video.mandatory.minWidth = 320;
            constraints.video.mandatory.minHeight = 180;
            constraints.video.mandatory.minAspectRatio = 1.77;
            break;
            // 4:3
        case '960':
            constraints.video.mandatory.minWidth = 960;
            constraints.video.mandatory.minHeight = 720;
            break;
        case '640':
        case 'vga':
            constraints.video.mandatory.minWidth = 640;
            constraints.video.mandatory.minHeight = 480;
            break;
        case '320':
            constraints.video.mandatory.minWidth = 320;
            constraints.video.mandatory.minHeight = 240;
            break;
        default:
            if (navigator.userAgent.indexOf('Android') != -1) {
                constraints.video.mandatory.minWidth = 320;
                constraints.video.mandatory.minHeight = 240;
                constraints.video.mandatory.maxFrameRate = 15;
            }
            break;
    }

    if (bandwidth) { // doesn't work currently, see webrtc issue 1846
        if (!constraints.video) constraints.video = {mandatory:{}};//same behaviour as true
        constraints.video.optional = [{bandwidth: bandwidth}];
    }
    if (fps) { // for some cameras it might be necessary to request 30fps
        // so they choose 30fps mjpg over 10fps yuy2
        if (!constraints.video) constraints.video = {mandatory:{}};// same behaviour as tru;
        constraints.video.mandatory['minFrameRate'] = fps;
    }

    try {
        RTC.getUserMedia(constraints,
                function(stream) {
                    console.log('onUserMediaSuccess');
                    $(document).trigger('mediaready.jingle', [stream]);
                },
                function(error) {
                    console.warn('Failed to get access to local media. Error ', error);
                    $(document).trigger('mediafailure.jingle');
                });
    } catch (e) {
        console.error('GUM failed: ', e);
        $(document).trigger('mediafailure.jingle');
    }
}
<|endoftext|>


<commit_before>/* jshint -W117 */
function TraceablePeerConnection(ice_config, constraints) {
    var self = this;
    var RTCPeerconnection = navigator.mozGetUserMedia ? mozRTCPeerConnection : webkitRTCPeerConnection;
    this.peerconnection = new RTCPeerconnection(ice_config, constraints);
    this.updateLog = [];
    this.stats = {};
    this.statsinterval = null;
    this.maxstats = 300; // limit to 300 values, i.e. 5 minutes; set to 0 to disable

    // override as desired
    this.trace = function(what, info) {
        //console.warn('WTRACE', what, info);
        self.updateLog.push({
            time: new Date(),
            type: what,
            value: info || ""
        });
    };
    this.onicecandidate = null;
    this.peerconnection.onicecandidate = function (event) {
        self.trace('onicecandidate', JSON.stringify(event.candidate, null, ' '));
        if (self.onicecandidate !== null) {
            self.onicecandidate(event);
        }
    };
    this.onaddstream = null;
    this.peerconnection.onaddstream = function (event) {
        self.trace('onaddstream', event.stream.id);
        if (self.onaddstream !== null) {
            self.onaddstream(event);
        }
    };
    this.onremovestream = null;
    this.peerconnection.onremovestream = function (event) {
        self.trace('onremovestream', event.stream.id);
        if (self.onremovestream !== null) {
            self.onremovestream(event);
        }
    };
    this.onsignalingstatechange = null;
    this.peerconnection.onsignalingstatechange = function (event) {
        self.trace('onsignalingstatechange', event.srcElement.signalingState);
        if (self.onsignalingstatechange !== null) {
            self.onsignalingstatechange(event);
        }
    };
    this.oniceconnectionstatechange = null;
    this.peerconnection.oniceconnectionstatechange = function (event) {
        self.trace('oniceconnectionstatechange', event.srcElement.iceConnectionState);
        if (self.oniceconnectionstatechange !== null) {
            self.oniceconnectionstatechange(event);
        }
    };
    this.onnegotiationneeded = null;
    this.peerconnection.onnegotiationneeded = function (event) {
        self.trace('onnegotiationneeded');
        if (self.onnegotiationneeded !== null) {
            self.onnegotiationneeded(event);
        }
    };
    self.ondatachannel = null;
    this.peerconnection.ondatachannel = function (event) {
        self.trace('ondatachannel', event);
        if (self.ondatachannel !== null) {
            self.ondatachannel(event);
        }
    }
    if (!navigator.mozGetUserMedia) {
        this.statsinterval = window.setInterval(function() {
            self.peerconnection.getStats(function(stats) {
                var results = stats.result();
                for (var i = 0; i < results.length; ++i) {
                    //console.log(results[i].type, results[i].id, results[i].names())
                    var now = new Date();
                    results[i].names().forEach(function (name) {
                        var id = results[i].id + '-' + name;
                        if (!self.stats[id]) {
                            self.stats[id] = {
                                startTime: now,
                                endTime: now,
                                values: [],
                                times: []
                            };
                        }
                        self.stats[id].values.push(results[i].stat(name));
                        self.stats[id].times.push(now.getTime());
                        if (self.stats[id].values.length > self.maxstats) {
                            self.stats[id].values.shift();
                            self.stats[id].times.shift();
                        }
                        self.stats[id].endTime = now;
                    });
                }
            });

        }, 1000);
    }
};

dumpSDP = function(description) {
    return 'type: ' + description.type + '\r\n' + description.sdp;
}

if (TraceablePeerConnection.prototype.__defineGetter__ !== undefined) {
    TraceablePeerConnection.prototype.__defineGetter__('signalingState', function() { return this.peerconnection.signalingState; });
    TraceablePeerConnection.prototype.__defineGetter__('iceConnectionState', function() { return this.peerconnection.iceConnectionState; });
    TraceablePeerConnection.prototype.__defineGetter__('localDescription', function() { return this.peerconnection.localDescription; });
    TraceablePeerConnection.prototype.__defineGetter__('remoteDescription', function() { return this.peerconnection.remoteDescription; });
}

TraceablePeerConnection.prototype.addStream = function (stream) {
    this.trace('addStream', stream.id);
    this.peerconnection.addStream(stream);
};

TraceablePeerConnection.prototype.removeStream = function (stream) {
    this.trace('removeStream', stream.id);
    this.peerconnection.removeStream(stream);
};

TraceablePeerConnection.prototype.createDataChannel = function (label, opts) {
    this.trace('createDataChannel', label, opts);
    this.peerconnection.createDataChannel(label, opts);
}

TraceablePeerConnection.prototype.setLocalDescription = function (description, successCallback, failureCallback) {
    var self = this;
    this.trace('setLocalDescription', dumpSDP(description));
    this.peerconnection.setLocalDescription(description,
        function () {
            self.trace('setLocalDescriptionOnSuccess');
            successCallback();
        },
        function (err) {
            self.trace('setLocalDescriptionOnFailure', err);
            failureCallback(err);
        }
    );
    /*
    if (this.statsinterval === null && this.maxstats > 0) {
        // start gathering stats
    }
    */
};

TraceablePeerConnection.prototype.setRemoteDescription = function (description, successCallback, failureCallback) {
    var self = this;
    this.trace('setRemoteDescription', dumpSDP(description));
    this.peerconnection.setRemoteDescription(description,
        function () {
            self.trace('setRemoteDescriptionOnSuccess');
            successCallback();
        },
        function (err) {
            self.trace('setRemoteDescriptionOnFailure', err);
            failureCallback(err);
        }
    );
    /*
    if (this.statsinterval === null && this.maxstats > 0) {
        // start gathering stats
    }
    */
};

TraceablePeerConnection.prototype.close = function () {
    this.trace('stop');
    if (this.statsinterval !== null) {
        window.clearInterval(this.statsinterval);
        this.statsinterval = null;
    }
    this.peerconnection.close();
};

TraceablePeerConnection.prototype.createOffer = function (successCallback, failureCallback, constraints) {
    var self = this;
    this.trace('createOffer', JSON.stringify(constraints, null, ' '));
    this.peerconnection.createOffer(
        function (offer) {
            self.trace('createOfferOnSuccess', dumpSDP(offer));
            successCallback(offer);
        },
        function(err) {
            self.trace('createOfferOnFailure', err);
            failureCallback(err);
        },
        constraints
    );
};

TraceablePeerConnection.prototype.createAnswer = function (successCallback, failureCallback, constraints) {
    var self = this;
    this.trace('createAnswer', JSON.stringify(constraints, null, ' '));
    this.peerconnection.createAnswer(
        function (answer) {
            self.trace('createAnswerOnSuccess', dumpSDP(answer));
            successCallback(answer);
        },
        function(err) {
            self.trace('createAnswerOnFailure', err);
            failureCallback(err);
        },
        constraints
    );
};

TraceablePeerConnection.prototype.addIceCandidate = function (candidate, successCallback, failureCallback) {
    var self = this;
    this.trace('addIceCandidate', JSON.stringify(candidate, null, ' '));
    this.peerconnection.addIceCandidate(candidate);
    /* maybe later
    this.peerconnection.addIceCandidate(candidate,
        function () {
            self.trace('addIceCandidateOnSuccess');
            successCallback();
        },
        function (err) {
            self.trace('addIceCandidateOnFailure', err);
            failureCallback(err);
        }
    );
    */
};

TraceablePeerConnection.prototype.getStats = function(callback, errback) {
    if (navigator.mozGetUserMedia) {
        // ignore for now...
    } else {
        this.peerconnection.getStats(callback);
    }
};

// mozilla chrome compat layer -- very similar to adapter.js
function setupRTC() {
    var RTC = null;
    if (navigator.mozGetUserMedia) {
        console.log('This appears to be Firefox');
        var version = parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
        if (version >= 22) {
            RTC = {
                peerconnection: mozRTCPeerConnection,
                browser: 'firefox',
                getUserMedia: navigator.mozGetUserMedia.bind(navigator),
                attachMediaStream: function (element, stream) {
                    element[0].mozSrcObject = stream;
                    element[0].play();
                },
                pc_constraints: {}
            };
            if (!MediaStream.prototype.getVideoTracks)
                MediaStream.prototype.getVideoTracks = function () { return []; };
            if (!MediaStream.prototype.getAudioTracks)
                MediaStream.prototype.getAudioTracks = function () { return []; };
            RTCSessionDescription = mozRTCSessionDescription;
            RTCIceCandidate = mozRTCIceCandidate;
        }
    } else if (navigator.webkitGetUserMedia) {
        console.log('This appears to be Chrome');
        RTC = {
            peerconnection: webkitRTCPeerConnection,
            browser: 'chrome',
            getUserMedia: navigator.webkitGetUserMedia.bind(navigator),
            attachMediaStream: function (element, stream) {
                element.attr('src', webkitURL.createObjectURL(stream));
            },
            // DTLS should now be enabled by default but..
            pc_constraints: {'optional': [{'DtlsSrtpKeyAgreement': 'true'}]}
        };
        if (navigator.userAgent.indexOf('Android') != -1) {
            RTC.pc_constraints = {}; // disable DTLS on Android
        }
        if (!webkitMediaStream.prototype.getVideoTracks) {
            webkitMediaStream.prototype.getVideoTracks = function () {
                return this.videoTracks;
            };
        }
        if (!webkitMediaStream.prototype.getAudioTracks) {
            webkitMediaStream.prototype.getAudioTracks = function () {
                return this.audioTracks;
            };
        }
    }
    if (RTC === null) {
        try { console.log('Browser does not appear to be WebRTC-capable'); } catch (e) { }
    }
    return RTC;
}

function getUserMediaWithConstraints(um, resolution, bandwidth, fps) {
    var constraints = {audio: false, video: false};

    if (um.indexOf('video') >= 0) {
        constraints.video = {mandatory: {}};// same behaviour as true
    }
    if (um.indexOf('audio') >= 0) {
        constraints.audio = {};// same behaviour as true
    }
    if (um.indexOf('screen') >= 0) {
        constraints.video = {
            "mandatory": {
                "chromeMediaSource": "screen"
            }
        };
    }

    if (resolution && !constraints.video) {
        constraints.video = {mandatory: {}};// same behaviour as true
    }
    // see https://code.google.com/p/chromium/issues/detail?id=143631#c9 for list of supported resolutions
    switch (resolution) {
    // 16:9 first
    case '1080':
    case 'fullhd':
        constraints.video.mandatory.minWidth = 1920;
        constraints.video.mandatory.minHeight = 1080;
        constraints.video.mandatory.minAspectRatio = 1.77;
        break;
    case '720':
    case 'hd':
        constraints.video.mandatory.minWidth = 1280;
        constraints.video.mandatory.minHeight = 720;
        constraints.video.mandatory.minAspectRatio = 1.77;
        break;
    case '360':
        constraints.video.mandatory.minWidth = 640;
        constraints.video.mandatory.minHeight = 360;
        constraints.video.mandatory.minAspectRatio = 1.77;
        break;
    case '180':
        constraints.video.mandatory.minWidth = 320;
        constraints.video.mandatory.minHeight = 180;
        constraints.video.mandatory.minAspectRatio = 1.77;
        break;
        // 4:3
    case '960':
        constraints.video.mandatory.minWidth = 960;
        constraints.video.mandatory.minHeight = 720;
        break;
    case '640':
    case 'vga':
        constraints.video.mandatory.minWidth = 640;
        constraints.video.mandatory.minHeight = 480;
        break;
    case '320':
        constraints.video.mandatory.minWidth = 320;
        constraints.video.mandatory.minHeight = 240;
        break;
    default:
        if (navigator.userAgent.indexOf('Android') != -1) {
            constraints.video.mandatory.minWidth = 320;
            constraints.video.mandatory.minHeight = 240;
            constraints.video.mandatory.maxFrameRate = 15;
        }
        break;
    }

    if (bandwidth) { // doesn't work currently, see webrtc issue 1846
        if (!constraints.video) constraints.video = {mandatory: {}};//same behaviour as true
        constraints.video.optional = [{bandwidth: bandwidth}];
    }
    if (fps) { // for some cameras it might be necessary to request 30fps
        // so they choose 30fps mjpg over 10fps yuy2
        if (!constraints.video) constraints.video = {mandatory: {}};// same behaviour as tru;
        constraints.video.mandatory.minFrameRate = fps;
    }

    try {
        RTC.getUserMedia(constraints,
                function (stream) {
                    console.log('onUserMediaSuccess');
                    $(document).trigger('mediaready.jingle', [stream]);
                },
                function (error) {
                    console.warn('Failed to get access to local media. Error ', error);
                    $(document).trigger('mediafailure.jingle');
                });
    } catch (e) {
        console.error('GUM failed: ', e);
        $(document).trigger('mediafailure.jingle');
    }
}
<commit_msg>bugfix from fippo<commit_after>/* jshint -W117 */
function TraceablePeerConnection(ice_config, constraints) {
    var self = this;
    var RTCPeerconnection = navigator.mozGetUserMedia ? mozRTCPeerConnection : webkitRTCPeerConnection;
    this.peerconnection = new RTCPeerconnection(ice_config, constraints);
    this.updateLog = [];
    this.stats = {};
    this.statsinterval = null;
    this.maxstats = 300; // limit to 300 values, i.e. 5 minutes; set to 0 to disable

    // override as desired
    this.trace = function(what, info) {
        //console.warn('WTRACE', what, info);
        self.updateLog.push({
            time: new Date(),
            type: what,
            value: info || ""
        });
    };
    this.onicecandidate = null;
    this.peerconnection.onicecandidate = function (event) {
        self.trace('onicecandidate', JSON.stringify(event.candidate, null, ' '));
        if (self.onicecandidate !== null) {
            self.onicecandidate(event);
        }
    };
    this.onaddstream = null;
    this.peerconnection.onaddstream = function (event) {
        self.trace('onaddstream', event.stream.id);
        if (self.onaddstream !== null) {
            self.onaddstream(event);
        }
    };
    this.onremovestream = null;
    this.peerconnection.onremovestream = function (event) {
        self.trace('onremovestream', event.stream.id);
        if (self.onremovestream !== null) {
            self.onremovestream(event);
        }
    };
    this.onsignalingstatechange = null;
    this.peerconnection.onsignalingstatechange = function (event) {
        self.trace('onsignalingstatechange', self.signalingState);
        if (self.onsignalingstatechange !== null) {
            self.onsignalingstatechange(event);
        }
    };
    this.oniceconnectionstatechange = null;
    this.peerconnection.oniceconnectionstatechange = function (event) {
        self.trace('oniceconnectionstatechange', self.iceConnectionState);
        if (self.oniceconnectionstatechange !== null) {
            self.oniceconnectionstatechange(event);
        }
    };
    this.onnegotiationneeded = null;
    this.peerconnection.onnegotiationneeded = function (event) {
        self.trace('onnegotiationneeded');
        if (self.onnegotiationneeded !== null) {
            self.onnegotiationneeded(event);
        }
    };
    self.ondatachannel = null;
    this.peerconnection.ondatachannel = function (event) {
        self.trace('ondatachannel', event);
        if (self.ondatachannel !== null) {
            self.ondatachannel(event);
        }
    }
    if (!navigator.mozGetUserMedia) {
        this.statsinterval = window.setInterval(function() {
            self.peerconnection.getStats(function(stats) {
                var results = stats.result();
                for (var i = 0; i < results.length; ++i) {
                    //console.log(results[i].type, results[i].id, results[i].names())
                    var now = new Date();
                    results[i].names().forEach(function (name) {
                        var id = results[i].id + '-' + name;
                        if (!self.stats[id]) {
                            self.stats[id] = {
                                startTime: now,
                                endTime: now,
                                values: [],
                                times: []
                            };
                        }
                        self.stats[id].values.push(results[i].stat(name));
                        self.stats[id].times.push(now.getTime());
                        if (self.stats[id].values.length > self.maxstats) {
                            self.stats[id].values.shift();
                            self.stats[id].times.shift();
                        }
                        self.stats[id].endTime = now;
                    });
                }
            });

        }, 1000);
    }
};

dumpSDP = function(description) {
    return 'type: ' + description.type + '\r\n' + description.sdp;
}

if (TraceablePeerConnection.prototype.__defineGetter__ !== undefined) {
    TraceablePeerConnection.prototype.__defineGetter__('signalingState', function() { return this.peerconnection.signalingState; });
    TraceablePeerConnection.prototype.__defineGetter__('iceConnectionState', function() { return this.peerconnection.iceConnectionState; });
    TraceablePeerConnection.prototype.__defineGetter__('localDescription', function() { return this.peerconnection.localDescription; });
    TraceablePeerConnection.prototype.__defineGetter__('remoteDescription', function() { return this.peerconnection.remoteDescription; });
}

TraceablePeerConnection.prototype.addStream = function (stream) {
    this.trace('addStream', stream.id);
    this.peerconnection.addStream(stream);
};

TraceablePeerConnection.prototype.removeStream = function (stream) {
    this.trace('removeStream', stream.id);
    this.peerconnection.removeStream(stream);
};

TraceablePeerConnection.prototype.createDataChannel = function (label, opts) {
    this.trace('createDataChannel', label, opts);
    this.peerconnection.createDataChannel(label, opts);
}

TraceablePeerConnection.prototype.setLocalDescription = function (description, successCallback, failureCallback) {
    var self = this;
    this.trace('setLocalDescription', dumpSDP(description));
    this.peerconnection.setLocalDescription(description,
        function () {
            self.trace('setLocalDescriptionOnSuccess');
            successCallback();
        },
        function (err) {
            self.trace('setLocalDescriptionOnFailure', err);
            failureCallback(err);
        }
    );
    /*
    if (this.statsinterval === null && this.maxstats > 0) {
        // start gathering stats
    }
    */
};

TraceablePeerConnection.prototype.setRemoteDescription = function (description, successCallback, failureCallback) {
    var self = this;
    this.trace('setRemoteDescription', dumpSDP(description));
    this.peerconnection.setRemoteDescription(description,
        function () {
            self.trace('setRemoteDescriptionOnSuccess');
            successCallback();
        },
        function (err) {
            self.trace('setRemoteDescriptionOnFailure', err);
            failureCallback(err);
        }
    );
    /*
    if (this.statsinterval === null && this.maxstats > 0) {
        // start gathering stats
    }
    */
};

TraceablePeerConnection.prototype.close = function () {
    this.trace('stop');
    if (this.statsinterval !== null) {
        window.clearInterval(this.statsinterval);
        this.statsinterval = null;
    }
    this.peerconnection.close();
};

TraceablePeerConnection.prototype.createOffer = function (successCallback, failureCallback, constraints) {
    var self = this;
    this.trace('createOffer', JSON.stringify(constraints, null, ' '));
    this.peerconnection.createOffer(
        function (offer) {
            self.trace('createOfferOnSuccess', dumpSDP(offer));
            successCallback(offer);
        },
        function(err) {
            self.trace('createOfferOnFailure', err);
            failureCallback(err);
        },
        constraints
    );
};

TraceablePeerConnection.prototype.createAnswer = function (successCallback, failureCallback, constraints) {
    var self = this;
    this.trace('createAnswer', JSON.stringify(constraints, null, ' '));
    this.peerconnection.createAnswer(
        function (answer) {
            self.trace('createAnswerOnSuccess', dumpSDP(answer));
            successCallback(answer);
        },
        function(err) {
            self.trace('createAnswerOnFailure', err);
            failureCallback(err);
        },
        constraints
    );
};

TraceablePeerConnection.prototype.addIceCandidate = function (candidate, successCallback, failureCallback) {
    var self = this;
    this.trace('addIceCandidate', JSON.stringify(candidate, null, ' '));
    this.peerconnection.addIceCandidate(candidate);
    /* maybe later
    this.peerconnection.addIceCandidate(candidate,
        function () {
            self.trace('addIceCandidateOnSuccess');
            successCallback();
        },
        function (err) {
            self.trace('addIceCandidateOnFailure', err);
            failureCallback(err);
        }
    );
    */
};

TraceablePeerConnection.prototype.getStats = function(callback, errback) {
    if (navigator.mozGetUserMedia) {
        // ignore for now...
    } else {
        this.peerconnection.getStats(callback);
    }
};

// mozilla chrome compat layer -- very similar to adapter.js
function setupRTC() {
    var RTC = null;
    if (navigator.mozGetUserMedia) {
        console.log('This appears to be Firefox');
        var version = parseInt(navigator.userAgent.match(/Firefox\/([0-9]+)\./)[1], 10);
        if (version >= 22) {
            RTC = {
                peerconnection: mozRTCPeerConnection,
                browser: 'firefox',
                getUserMedia: navigator.mozGetUserMedia.bind(navigator),
                attachMediaStream: function (element, stream) {
                    element[0].mozSrcObject = stream;
                    element[0].play();
                },
                pc_constraints: {}
            };
            if (!MediaStream.prototype.getVideoTracks)
                MediaStream.prototype.getVideoTracks = function () { return []; };
            if (!MediaStream.prototype.getAudioTracks)
                MediaStream.prototype.getAudioTracks = function () { return []; };
            RTCSessionDescription = mozRTCSessionDescription;
            RTCIceCandidate = mozRTCIceCandidate;
        }
    } else if (navigator.webkitGetUserMedia) {
        console.log('This appears to be Chrome');
        RTC = {
            peerconnection: webkitRTCPeerConnection,
            browser: 'chrome',
            getUserMedia: navigator.webkitGetUserMedia.bind(navigator),
            attachMediaStream: function (element, stream) {
                element.attr('src', webkitURL.createObjectURL(stream));
            },
            // DTLS should now be enabled by default but..
            pc_constraints: {'optional': [{'DtlsSrtpKeyAgreement': 'true'}]}
        };
        if (navigator.userAgent.indexOf('Android') != -1) {
            RTC.pc_constraints = {}; // disable DTLS on Android
        }
        if (!webkitMediaStream.prototype.getVideoTracks) {
            webkitMediaStream.prototype.getVideoTracks = function () {
                return this.videoTracks;
            };
        }
        if (!webkitMediaStream.prototype.getAudioTracks) {
            webkitMediaStream.prototype.getAudioTracks = function () {
                return this.audioTracks;
            };
        }
    }
    if (RTC === null) {
        try { console.log('Browser does not appear to be WebRTC-capable'); } catch (e) { }
    }
    return RTC;
}

function getUserMediaWithConstraints(um, resolution, bandwidth, fps) {
    var constraints = {audio: false, video: false};

    if (um.indexOf('video') >= 0) {
        constraints.video = {mandatory: {}};// same behaviour as true
    }
    if (um.indexOf('audio') >= 0) {
        constraints.audio = {};// same behaviour as true
    }
    if (um.indexOf('screen') >= 0) {
        constraints.video = {
            "mandatory": {
                "chromeMediaSource": "screen"
            }
        };
    }

    if (resolution && !constraints.video) {
        constraints.video = {mandatory: {}};// same behaviour as true
    }
    // see https://code.google.com/p/chromium/issues/detail?id=143631#c9 for list of supported resolutions
    switch (resolution) {
    // 16:9 first
    case '1080':
    case 'fullhd':
        constraints.video.mandatory.minWidth = 1920;
        constraints.video.mandatory.minHeight = 1080;
        constraints.video.mandatory.minAspectRatio = 1.77;
        break;
    case '720':
    case 'hd':
        constraints.video.mandatory.minWidth = 1280;
        constraints.video.mandatory.minHeight = 720;
        constraints.video.mandatory.minAspectRatio = 1.77;
        break;
    case '360':
        constraints.video.mandatory.minWidth = 640;
        constraints.video.mandatory.minHeight = 360;
        constraints.video.mandatory.minAspectRatio = 1.77;
        break;
    case '180':
        constraints.video.mandatory.minWidth = 320;
        constraints.video.mandatory.minHeight = 180;
        constraints.video.mandatory.minAspectRatio = 1.77;
        break;
        // 4:3
    case '960':
        constraints.video.mandatory.minWidth = 960;
        constraints.video.mandatory.minHeight = 720;
        break;
    case '640':
    case 'vga':
        constraints.video.mandatory.minWidth = 640;
        constraints.video.mandatory.minHeight = 480;
        break;
    case '320':
        constraints.video.mandatory.minWidth = 320;
        constraints.video.mandatory.minHeight = 240;
        break;
    default:
        if (navigator.userAgent.indexOf('Android') != -1) {
            constraints.video.mandatory.minWidth = 320;
            constraints.video.mandatory.minHeight = 240;
            constraints.video.mandatory.maxFrameRate = 15;
        }
        break;
    }

    if (bandwidth) { // doesn't work currently, see webrtc issue 1846
        if (!constraints.video) constraints.video = {mandatory: {}};//same behaviour as true
        constraints.video.optional = [{bandwidth: bandwidth}];
    }
    if (fps) { // for some cameras it might be necessary to request 30fps
        // so they choose 30fps mjpg over 10fps yuy2
        if (!constraints.video) constraints.video = {mandatory: {}};// same behaviour as tru;
        constraints.video.mandatory.minFrameRate = fps;
    }

    try {
        RTC.getUserMedia(constraints,
                function (stream) {
                    console.log('onUserMediaSuccess');
                    $(document).trigger('mediaready.jingle', [stream]);
                },
                function (error) {
                    console.warn('Failed to get access to local media. Error ', error);
                    $(document).trigger('mediafailure.jingle');
                });
    } catch (e) {
        console.error('GUM failed: ', e);
        $(document).trigger('mediafailure.jingle');
    }
}
<|endoftext|>


<commit_before>var logme = require('logme');
var express = require('express');

var config = require('../config');

process.on('uncaughtException', function(err) {
    debugger;
    logme.error('Caught Error ' + err);
    if (err.stack) {
        logme.error('Stack ' + err.stack);
    }
});

// create the express web application server
var app = express();
app.configure(function() {
    app.use(express.bodyParser());
    app.use(express.methodOverride());
    app.use(app.router);

    /**
     *  Default error handler when things go wrong
     **/
    app.use(function(err, req, res, next){
        logme.error(err.stack);
        res.json({error:'Internal Server Error'},500);
        next();
    });

    // Define the webhook endpoints
    require('../lib/routes/slack')(app);
    require('../lib/routes/twilio')(app);

    // Default root route for server
    app.get('/', function(req,res,next) {
        res.send('Success! Stuart is running.');
    });

});

// start the web application server
var port = config.port;
app.listen(port);
logme.info('Stuart started on port ' + port);
module.exports = app;
<commit_msg>added express logging for all front-end requests<commit_after>var logme = require('logme');
var express = require('express');

var config = require('../config');

process.on('uncaughtException', function(err) {
    debugger;
    logme.error('Caught Error ' + err);
    if (err.stack) {
        logme.error('Stack ' + err.stack);
    }
});

// create the express web application server
var app = express();
app.configure(function() {
    app.use(express.bodyParser());
    app.use(express.methodOverride());

    // simple logger
    app.use(function(req, res, next){
        logme.info(req.method + '  ' + req.url);
        next();
    });

    /**
     *  Default error handler when things go wrong
     **/
    app.use(function(err, req, res, next){
        logme.error(err.stack);
        res.json({error:'Internal Server Error'},500);
        next();
    });

    app.use(app.router);

    // Define the webhook endpoints
    require('../lib/routes/slack')(app);
    require('../lib/routes/twilio')(app);

    // Default root route for server
    app.get('/', function(req,res,next) {
        res.send('Success! Stuart is running.');
    });

});

// start the web application server
var port = config.port;
app.listen(port);
logme.info('Stuart started on port ' + port);
module.exports = app;
<|endoftext|>