<commit_before>(function () {
  'use strict';
  angular.module('civic.add')
    .config(CommunityMainConfig)
    .controller('CommunityMainController', CommunityMainController);

  // @ngInject
  function CommunityMainConfig($stateProvider,
                               $resourceProvider) {
    // TODO: toggle trailing-slash trim after civic-server configured to accept either
    $resourceProvider.defaults.stripTrailingSlashes = false;

    $stateProvider
      .state('community.main', {
        url: '/main',
        templateUrl: 'app/views/community/main/communityMain.tpl.html',
        resolve: {
          Users: 'Users',
          Community: 'Community'
        },
        controller: 'CommunityMainController',
        controllerAs: 'vm',
        data: {
          navMode: 'sub',
          title: 'Community'
        }
      });

  }

  // @ngInject
  function CommunityMainController($scope,
                                   _,
                                   Security,
                                   Community,
                                   Users) {
    var vm = this;

    vm.isEditor = Security.isEditor();
    vm.isAdmin = Security.isAdmin();
    vm.isAuthenticated = Security.isAuthenticated();
    vm.leaderboards = {};
    vm.users = [];

    Community.getLeaderboards()
      .then(function(response) {
        angular.copy(response, vm.leaderboards);
      });

    // setup initial paging var and data update
    vm.page = 1;
    vm.count = 24;
    vm.totalItems = Number();
    vm.totalPages = Number();

    $scope.$watch(function(){ return vm.totalItems; }, function() {
      vm.totalPages = Math.ceil(vm.totalItems / vm.count);
    });

    vm.model = {};

    var updateData = _.debounce(function () {
      var filters = [{
        field: 'display_name',
        term: vm.model.filter
      }];

      var sorting = [{
        field: vm.model.sort_by,
        direction: vm.model.sort_order
      }];

      var limit = vm.model.limit;

      fetchUsers(vm.count, vm.page, sorting, filters, limit, vm.model.role, vm.model.area_of_expertise)
        .then(function(data){
          angular.copy(data.result, vm.users);
          vm.totalItems = data.total;
        });
    }, 250);

    vm.formFields = [
      {
        key: 'filter',
        type: 'input',
        className: 'col-xs-2',
        templateOptions: {
          label: 'Find User',
          required: false
        },
        watcher: {
          listener: function() {
            updateData();
          }
        }
      },
      {
        key: 'role',
        type: 'select',
        className: 'col-xs-2',
        defaultValue: undefined,
        templateOptions: {
          label: 'Role',
          required: false,
          options: [
            { value: undefined, name: '--' },
            { value: 'curator', name: 'Curator'},
            { value: 'editor', name: 'Editor' },
            { value: 'admin', name: 'Administrator' }
          ]
        },
        watcher: {
          listener: function() {
            updateData();
          }
        }
      },
      {
        key: 'area_of_expertise',
        type: 'select',
        className: 'col-xs-2',
        defaultValue: undefined,
        templateOptions: {
          label: 'Area of Expertise',
          required: false,
          options: [
            { value: undefined, name: '--' },
            { value: 'Patient Advocate', name: 'Patient Advocate'},
            { value: 'Clinical Scientist', name: 'Clinical Scientist' },
            { value: 'Research Scientist', name: 'Research Scientist' }
          ]
        },
        watcher: {
          listener: function() {
            updateData();
          }
        }
      },
      {
        key: 'sort_by',
        type: 'select',
        className: 'col-xs-2',
        defaultValue: 'most_active',
        templateOptions: {
          label: 'Sort By',
          required: false,
          options: [
            // last_seen, recent_activity, join_date, most_active
            {name: 'Last seen', value: 'last_seen', sort_order: 'desc'},
            {name: 'Recent activity', value: 'recent_activity', sort_order: 'desc'},
            {name: 'Join date', value: 'join_date', sort_order: 'asc'},
            {name: 'Most active', value: 'most_active', sort_order: 'desc'}
          ]
        },
        watcher: {
          listener: function(field, newValue) {
            if(!_.isUndefined(newValue)) {
              vm.model.sort_order = _.find(field.templateOptions.options, {value: newValue}).sort_order;
            }
            updateData();
          }
        }
      },
      {
        key: 'sort_order',
        type: 'select',
        className: 'col-xs-2',
        defaultValue: 'desc',
        templateOptions: {
          label: 'Sort Order',
          required: false,
          options: [
            { name: 'Ascending', value: 'asc' },
            { name: 'Descending', value: 'desc' }
          ]
        },
        watcher: {
          listener: function() {
            updateData();
          }
        }
      },
      {
        key: 'limit',
        type: 'select',
        className: 'col-xs-2',
        defaultValue: 'all_time',
        templateOptions: {
          label: 'Limit To',
          required: false,
          options: [
            // this_week, this_month, this_year, all_time
            {name: 'All time', value: 'all_time'},
            {name: 'This week', value: 'this_week'},
            {name: 'This month', value: 'this_month'},
            {name: 'This year', value: 'this_year'}
          ]
        },
        watcher: {
          listener: function() {
            updateData();
          }
        }
      },
    ];

    updateData();

    vm.pageChanged = function() {
      updateData();
    };

    function fetchUsers(count, page, sorting, filters, limit, role, area_of_expertise) {
      var request;

      request = {
        count: count,
        page: page
      };

      if(role) {
        request['filter[role]'] = role;
      }
      if(area_of_expertise) {
        request['filter[area_of_expertise]'] = area_of_expertise;
      }

      request['limit[' + sorting[0].field + ']'] = limit;

      if (filters.length > 0) {
        _.each(filters, function(filter) {
          request['filter[' + filter.field + ']'] = filter.term;
        });
      }

      if (sorting.length > 0) {
        _.each(sorting, function(sort) {
          request['sorting[' + sort.field + ']'] = sort.direction;
        });
      }
      return Users.query(request);
    }
  }

})();
<commit_msg>community main page now defaults to sorting by most active, limited to current month<commit_after>(function () {
  'use strict';
  angular.module('civic.add')
    .config(CommunityMainConfig)
    .controller('CommunityMainController', CommunityMainController);

  // @ngInject
  function CommunityMainConfig($stateProvider,
                               $resourceProvider) {
    // TODO: toggle trailing-slash trim after civic-server configured to accept either
    $resourceProvider.defaults.stripTrailingSlashes = false;

    $stateProvider
      .state('community.main', {
        url: '/main',
        templateUrl: 'app/views/community/main/communityMain.tpl.html',
        resolve: {
          Users: 'Users',
          Community: 'Community'
        },
        controller: 'CommunityMainController',
        controllerAs: 'vm',
        data: {
          navMode: 'sub',
          title: 'Community'
        }
      });

  }

  // @ngInject
  function CommunityMainController($scope,
                                   _,
                                   Security,
                                   Community,
                                   Users) {
    var vm = this;

    vm.isEditor = Security.isEditor();
    vm.isAdmin = Security.isAdmin();
    vm.isAuthenticated = Security.isAuthenticated();
    vm.leaderboards = {};
    vm.users = [];

    Community.getLeaderboards()
      .then(function(response) {
        angular.copy(response, vm.leaderboards);
      });

    // setup initial paging var and data update
    vm.page = 1;
    vm.count = 24;
    vm.totalItems = Number();
    vm.totalPages = Number();

    $scope.$watch(function(){ return vm.totalItems; }, function() {
      vm.totalPages = Math.ceil(vm.totalItems / vm.count);
    });

    vm.model = {};

    var updateData = _.debounce(function () {
      var filters = [{
        field: 'display_name',
        term: vm.model.filter
      }];

      var sorting = [{
        field: vm.model.sort_by,
        direction: vm.model.sort_order
      }];

      var limit = vm.model.limit;

      fetchUsers(vm.count, vm.page, sorting, filters, limit, vm.model.role, vm.model.area_of_expertise)
        .then(function(data){
          angular.copy(data.result, vm.users);
          vm.totalItems = data.total;
        });
    }, 250);

    vm.formFields = [
      {
        key: 'filter',
        type: 'input',
        className: 'col-xs-2',
        templateOptions: {
          label: 'Find User',
          required: false
        },
        watcher: {
          listener: function() {
            updateData();
          }
        }
      },
      {
        key: 'role',
        type: 'select',
        className: 'col-xs-2',
        defaultValue: undefined,
        templateOptions: {
          label: 'Role',
          required: false,
          options: [
            { value: undefined, name: '--' },
            { value: 'curator', name: 'Curator'},
            { value: 'editor', name: 'Editor' },
            { value: 'admin', name: 'Administrator' }
          ]
        },
        watcher: {
          listener: function() {
            updateData();
          }
        }
      },
      {
        key: 'area_of_expertise',
        type: 'select',
        className: 'col-xs-2',
        defaultValue: undefined,
        templateOptions: {
          label: 'Area of Expertise',
          required: false,
          options: [
            { value: undefined, name: '--' },
            { value: 'Patient Advocate', name: 'Patient Advocate'},
            { value: 'Clinical Scientist', name: 'Clinical Scientist' },
            { value: 'Research Scientist', name: 'Research Scientist' }
          ]
        },
        watcher: {
          listener: function() {
            updateData();
          }
        }
      },
      {
        key: 'sort_by',

        type: 'select',
        className: 'col-xs-2',
        defaultValue: 'most_active',
        templateOptions: {
          label: 'Sort By',
          required: false,
          options: [
            // last_seen, recent_activity, join_date, most_active
            {name: 'Last seen', value: 'last_seen', sort_order: 'desc'},
            {name: 'Recent activity', value: 'recent_activity', sort_order: 'desc'},
            {name: 'Join date', value: 'join_date', sort_order: 'asc'},
            {name: 'Most active', value: 'most_active', sort_order: 'desc'}
          ]
        },
        watcher: {
          listener: function(field, newValue) {
            if(!_.isUndefined(newValue)) {
              vm.model.sort_order = _.find(field.templateOptions.options, {value: newValue}).sort_order;
            }
            updateData();
          }
        }
      },
      {
        key: 'sort_order',
        type: 'select',
        className: 'col-xs-2',
        defaultValue: 'desc',
        templateOptions: {
          label: 'Sort Order',
          required: false,
          options: [
            { name: 'Ascending', value: 'asc' },
            { name: 'Descending', value: 'desc' }
          ]
        },
        watcher: {
          listener: function() {
            updateData();
          }
        }
      },
      {
        key: 'limit',
        type: 'select',
        className: 'col-xs-2',
        defaultValue: 'this_month',
        templateOptions: {
          label: 'Limit To',
          required: false,
          options: [
            // this_week, this_month, this_year, all_time
            {name: 'All time', value: 'all_time'},
            {name: 'This week', value: 'this_week'},
            {name: 'This month', value: 'this_month'},
            {name: 'This year', value: 'this_year'}
          ]
        },
        watcher: {
          listener: function() {
            updateData();
          }
        }
      },
    ];

    updateData();

    vm.pageChanged = function() {
      updateData();
    };

    function fetchUsers(count, page, sorting, filters, limit, role, area_of_expertise) {
      var request;

      request = {
        count: count,
        page: page
      };

      if(role) {
        request['filter[role]'] = role;
      }
      if(area_of_expertise) {
        request['filter[area_of_expertise]'] = area_of_expertise;
      }

      request['limit[' + sorting[0].field + ']'] = limit;

      if (filters.length > 0) {
        _.each(filters, function(filter) {
          request['filter[' + filter.field + ']'] = filter.term;
        });
      }

      if (sorting.length > 0) {
        _.each(sorting, function(sort) {
          request['sorting[' + sort.field + ']'] = sort.direction;
        });
      }
      return Users.query(request);
    }
  }

})();
<|endoftext|>


<commit_before>/**
 * The ContextPanel Utility
 *
 * @module aui-context-panel
 */

var L = A.Lang,
	isBoolean = L.isBoolean,
	isString = L.isString,
	isObject = L.isObject,

	ALIGN = 'align',
	ANIM = 'anim',
	ARROW = 'arrow',
	BACKGROUND_COLOR = 'backgroundColor',
	BLANK = '',
	BOUNDING_BOX = 'boundingBox',
	CLICK = 'click',
	CONTENT_BOX = 'contentBox',
	CONTEXTPANEL = 'contextpanel',
	DEFAULT = 'default',
	DOT = '.',
	END = 'end',
	HIDDEN = 'hidden',
	INNER = 'inner',
	OPACITY = 'opacity',
	POINTER = 'pointer',
	SHOW_ARROW = 'showArrow',
	STATE = 'state',
	STYLE = 'style',
	VISIBLE = 'visible',

	BC = 'bc',
	BL = 'bl',
	BR = 'br',
	CC = 'cc',
	LB = 'lb',
	LC = 'lc',
	LT = 'lt',
	RB = 'rb',
	RC = 'rc',
	RL = 'rl',

	getCN = A.ClassNameManager.getClassName,

	CSS_CONTEXTPANEL = getCN(CONTEXTPANEL),
	CSS_CONTEXTPANEL_ARROW = getCN(CONTEXTPANEL, ARROW, BLANK),
	CSS_CONTEXTPANEL_HIDDEN = getCN(CONTEXTPANEL, HIDDEN),
	CSS_CONTEXTPANEL_POINTER = getCN(CONTEXTPANEL, POINTER),
	CSS_CONTEXTPANEL_POINTER_INNER = getCN(CONTEXTPANEL, POINTER, INNER),
	CSS_STATE_DEFAULT = getCN(STATE, DEFAULT),

	TPL_POINTER = '

',
	TPL_POINTER_INNER = '

';

/**
 *

 *
 * A base class for ContextPanel, providing:
 *

 *
Widget Lifecycle (initializer, renderUI, bindUI, syncUI, destructor)

 *
Customizable arrow

 *
Optional animation when show or hide

 *

 *
 * Quick Example:

 *
 *
var instance = new A.ContextPanel({
 *  bodyContent: 'Here s a sample ContextPanel.',
 *  boundingBox: '#contextpanel',
 *  trigger: '#triggerButton',
 *  cancellableHide: true,
 *  hideDelay: 200,
 *  hideOnDocumentClick: false,
 *  anim: true
 * }).render();
 *

 *
 * Check the list of Configuration Attributes available for
 * ContextPanel.
 *
 * @param config {Object} Object literal specifying widget configuration properties.
 *
 * @class ContextPanel
 * @constructor
 * @extends ContextOverlay
 */
function ContextPanel(config) {
 	ContextPanel.superclass.constructor.apply(this, arguments);
}

A.mix(ContextPanel, {
	/**
	 * Static property provides a string to identify the class.
	 *
	 * @property ContextPanel.NAME
	 * @type String
	 * @static
	 */
	NAME: CONTEXTPANEL,

	/**
	 * Static property used to define the default attribute
	 * configuration for the ContextPanel.
	 *
	 * @property ContextPanel.ATTRS
	 * @type Object
	 * @static
	 */
	ATTRS: {
		/**
		 * Enable or disable the animation for hide and show. Used as the
         * Anim configuration attributes.
		 *
		 *
anim: {
		 *  show: {
		 *  	duration: .9
		 *  },
		 *  hide: {
		 *  	duration: .2
		 *  }
		 * }
		 *

		 *
		 * @attribute anim
		 * @default { show: false }
		 * @type Object
		 */
		anim: {
			lazyAdd: false,
			value: {
				show: false
			},
			setter: function(v) {
				return this._setAnim(v);
			}
		},

		/**
		 * Position where the arrow will be placed. See
         * showArrow. If it's
         * not set, it will get the value set on the
         * align point. Here is a
         * list of valid arrows 'bc', 'bl', 'br', 'cc', 'lb', 'lc', 'lt', 'rb',
         * 'rc', 'rl'.
		 *
		 * @attribute arrow
		 * @default null
		 * @type String
		 */
		arrow: {
			value: null,
			validator: isString
		},

		/**
		 * See hideOn.
		 *
		 * @attribute hideOn
		 * @default click
		 * @type String
		 */
		hideOn: {
			value: CLICK
		},

		/**
		 * See showOn.
		 *
		 * @attribute showOn
		 * @default click
		 * @type String
		 */
		showOn: {
			value: CLICK
		},

		/**
		 * If true the ContextPanel will show an arrow positioned on the
         * href="ContextPanel.html#config_arrow">arrow point.
		 *
		 * @attribute showArrow
		 * @default true
		 * @type boolean
		 */
		showArrow: {
			lazyAdd: false,
			value: true,
			validator: isBoolean
		},

		/**
		 * Gives stacking habilities to the ContextPanel.
		 *
		 * @attribute stack
		 * @default true
		 * @type boolean
		 */
		stack: {
			lazyAdd: false,
			value: true,
			setter: function(v) {
				return this._setStack(v);
			},
			validator: isBoolean
		}
	}
});

A.extend(ContextPanel, A.ContextOverlay, {
	/**
	 * Bind the events on the ContextPanel UI. Lifecycle.
	 *
	 * @method bindUI
	 * @protected
	 */
	bindUI: function() {
		var instance = this;

		instance.after('showArrowChange', instance._afterShowArrowChange);

		instance.before('show', instance._beforeShow);

		ContextPanel.superclass.bindUI.apply(instance, arguments);
	},

	/**
	 * Create the DOM structure for the ContextPanel. Lifecycle.
	 *
	 * @method renderUI
	 * @protected
	 */
	renderUI: function() {
		var instance = this;

		instance._renderElements();
	},

	/**
	 * Sync the ContextPanel UI. Lifecycle.
	 *
	 * @method syncUI
	 * @protected
	 */
	syncUI: function() {
		var instance = this;

		instance._syncElements();
	},

	/**
	 * Aligns the ContextPanel to the provided node (or viewport) using the
     * provided points. Inherited from
     * Overlay.
	 *
	 * @method align
	 * @param {Node | String | null} node A reference (or selector string) for
     * the Node which with the ContextPanel is to be aligned.
	 * @param {Array[2]} points A two element array, specifying the points on
     * the ContextPanel and node/viewport which need to be aligned.
	 */
	align: function (node, points) {
		var instance = this;

		ContextPanel.superclass.align.apply(this, arguments);

		instance._syncElements();
	},

	/**
	 * ContextPanel uses a imageless arrow, which involves some CSS technics.
     * This method is meant to fix the color of the borders of the arrow.
	 *
	 * @method fixPointerColor
	 */
	fixPointerColor: function() {
		var instance = this;
		var contentBox = instance.get(CONTENT_BOX);
		var pointer = contentBox.query(DOT+CSS_CONTEXTPANEL_POINTER_INNER);

		pointer.removeAttribute(STYLE);

		var bColor = contentBox.getStyle(BACKGROUND_COLOR);
		var border = 'borderBottomColor';

		var right = [
			DOT+CSS_CONTEXTPANEL_ARROW+RB,
				DOT+CSS_CONTEXTPANEL_ARROW+RC,
					DOT+CSS_CONTEXTPANEL_ARROW+RL
		]
		.join(',');

		var bottom = [
			DOT+CSS_CONTEXTPANEL_ARROW+BR,
				DOT+CSS_CONTEXTPANEL_ARROW+BC,
					DOT+CSS_CONTEXTPANEL_ARROW+BL
		]
		.join(',');

		var left = [
			DOT+CSS_CONTEXTPANEL_ARROW+LB,
				DOT+CSS_CONTEXTPANEL_ARROW+LC,
					DOT+CSS_CONTEXTPANEL_ARROW+LT
		]
		.join(',');

		if (contentBox.test(right)) {
			border = 'borderLeftColor';
		}
		else if (contentBox.test(bottom)) {
			border = 'borderTopColor';
		}
		else if (contentBox.test(left)) {
			border = 'borderRightColor';
		}

		pointer.setStyle(border, bColor);
	},

	/**
	 * Normalize the align point value. The align point 'cc' is not a valid
     * position for the arrow and then it's normalized to the 'bc' point.
	 *
	 * @method getAlignPoint
	 * @return {String}
	 */
	getAlignPoint: function() {
		var instance = this;
		var overlayPoint = instance.get(ALIGN).points[0];

		if (overlayPoint == CC) {
			// CC is not a valid position for the arrow
			overlayPoint = BC;
		}

		return instance.get(ARROW) || overlayPoint;
	},

	/**
	 * Hides the ContextPanel.
	 *
	 * @method hide
	 * @param {EventFacade} event
	 */
	hide: function(event) {
		var instance = this;
		var boundingBox = instance.get(BOUNDING_BOX);

		if(instance._hideAnim) {
			var visible = instance.get(VISIBLE);

			if (visible) {
				instance._hideAnim.run();

				instance._hideAnim.on(END, function() {
					ContextPanel.superclass.hide.apply(instance, arguments);
				});
			}
		}
		else {
			ContextPanel.superclass.hide.apply(instance, arguments);
		}
	},

	/**
	 * Render DOM elements for the ContextPanel.
	 *
	 * @method _renderElements
	 * @protected
	 */
	_renderElements: function() {
		var instance = this;
		var contentBox = instance.get(CONTENT_BOX);
		var align = instance.get(ALIGN);
		var overlayPoint = align.points[0];

		contentBox.addClass(CSS_STATE_DEFAULT);

		instance._pointerNode = A.Node.create(TPL_POINTER).append(TPL_POINTER_INNER);

		contentBox.append(
			instance._pointerNode
		);
	},

	/**
	 * Sync the UI of the ContextPanel elements.
	 *
	 * @method _syncElements
	 * @protected
	 */
	_syncElements: function() {
		var instance = this;
		var contentBox = instance.get(CONTENT_BOX);
		var pointerNode = instance._pointerNode;
		var overlayPoint = instance.getAlignPoint();

		if (instance.get(SHOW_ARROW)) {
			pointerNode.removeClass(CSS_CONTEXTPANEL_HIDDEN);
			contentBox.removeClass(CSS_CONTEXTPANEL_ARROW + instance._lastOverlayPoint);
			contentBox.addClass(CSS_CONTEXTPANEL_ARROW + overlayPoint);

			instance.fixPointerColor();
		}
		else {
			pointerNode.addClass(CSS_CONTEXTPANEL_HIDDEN);
		}

		instance._lastOverlayPoint = overlayPoint;
	},

	/**
	 * Setter for the
     * stack attribute.
	 *
	 * @method _setStack
	 * @param {boolean} value
	 * @protected
	 * @return {boolean}
	 */
	_setStack: function(value) {
		var instance = this;

		if (value) {
			A.ContextPanelManager.register(instance);
		}
		else {
			A.ContextPanelManager.remove(instance);
		}

		return value;
	},

	/**
	 * Setter for the
     * anim attribute.
	 *
	 * @method _setAnim
	 * @param {Object} value
	 * @protected
	 * @return {Object}
	 */
	_setAnim: function(value) {
		var instance = this;
		var boundingBox = instance.get(BOUNDING_BOX);

		if (value) {
			var defaults = {
				node: boundingBox,
				duration: 0.1
			};

			var showOptions = A.merge(defaults, {
	    		from: { opacity: 0 },
	    		to: { opacity: 1 }
			});

			var hideOptions = A.merge(defaults, {
				from: { opacity: 1 },
	    		to: { opacity: 0 }
			});

			if (isObject(value)) {
				// loading user settings for animation
				showOptions = A.merge(showOptions, value.show);
				hideOptions = A.merge(hideOptions, value.hide);
			}

			instance._showAnim = new A.Anim(showOptions);
			instance._hideAnim = new A.Anim(hideOptions);

			// if anim.show or anim.hide === false, cancel respective animation
			if (isObject(value)) {
				if (value.show === false) {
					instance._showAnim = null;
				}

				if (value.hide === false) {
					instance._hideAnim = null;
				}
			}
		}

		return value;
	},

	/**
	 * Fires before show the ContextPanel.
	 *
	 * @method _beforeShow
	 * @param {EventFacade} event
	 * @protected
	 */
	_beforeShow: function(event) {
		var instance = this;
		var boundingBox = instance.get(BOUNDING_BOX);
		var visible = instance.get(VISIBLE);

		if(!visible && instance._showAnim) {
			boundingBox.setStyle(OPACITY, 0);

			instance._showAnim.run();
		}
		else {
			boundingBox.setStyle(OPACITY, 1);
		}
	},

	/**
	 * Fires after showArrow attribute changes.
	 *
	 * @method _afterShowArrowChange
	 * @param {EventFacade} event
	 * @protected
	 */
	_afterShowArrowChange: function() {
		var instance = this;

		instance._syncElements();
	}
});

A.ContextPanel = ContextPanel;

/**
 * A base class for ContextPanelManager:
 *
 * @param config {Object} Object literal specifying widget configuration properties.
 *
 * @class ContextPanelManager
 * @constructor
 * @extends OverlayManager
 * @static
 */
A.ContextPanelManager = new A.OverlayManager({
	zIndexBase: 1000
});<commit_msg>Document clean up<commit_after>/**
 * The ContextPanel Utility
 *
 * @module aui-context-panel
 */

var L = A.Lang,
	isBoolean = L.isBoolean,
	isString = L.isString,
	isObject = L.isObject,

	ALIGN = 'align',
	ANIM = 'anim',
	ARROW = 'arrow',
	BACKGROUND_COLOR = 'backgroundColor',
	BLANK = '',
	BOUNDING_BOX = 'boundingBox',
	CLICK = 'click',
	CONTENT_BOX = 'contentBox',
	CONTEXTPANEL = 'contextpanel',
	DEFAULT = 'default',
	DOT = '.',
	END = 'end',
	HIDDEN = 'hidden',
	INNER = 'inner',
	OPACITY = 'opacity',
	POINTER = 'pointer',
	SHOW_ARROW = 'showArrow',
	STATE = 'state',
	STYLE = 'style',
	VISIBLE = 'visible',

	BC = 'bc',
	BL = 'bl',
	BR = 'br',
	CC = 'cc',
	LB = 'lb',
	LC = 'lc',
	LT = 'lt',
	RB = 'rb',
	RC = 'rc',
	RL = 'rl',

	getCN = A.ClassNameManager.getClassName,

	CSS_CONTEXTPANEL = getCN(CONTEXTPANEL),
	CSS_CONTEXTPANEL_ARROW = getCN(CONTEXTPANEL, ARROW, BLANK),
	CSS_CONTEXTPANEL_HIDDEN = getCN(CONTEXTPANEL, HIDDEN),
	CSS_CONTEXTPANEL_POINTER = getCN(CONTEXTPANEL, POINTER),
	CSS_CONTEXTPANEL_POINTER_INNER = getCN(CONTEXTPANEL, POINTER, INNER),
	CSS_STATE_DEFAULT = getCN(STATE, DEFAULT),

	TPL_POINTER = '

',
	TPL_POINTER_INNER = '

';

/**
 *

 *
 * A base class for ContextPanel, providing:
 *

 *
Widget Lifecycle (initializer, renderUI, bindUI, syncUI, destructor)

 *
Customizable arrow

 *
Optional animation when show or hide

 *

 *
 * Quick Example:

 *
 *
var instance = new A.ContextPanel({
 *  bodyContent: 'Here s a sample ContextPanel.',
 *  boundingBox: '#contextpanel',
 *  trigger: '#triggerButton',
 *  cancellableHide: true,
 *  hideDelay: 200,
 *  hideOnDocumentClick: false,
 *  anim: true
 * }).render();
 *

 *
 * Check the list of Configuration Attributes available for
 * ContextPanel.
 *
 * @param config {Object} Object literal specifying widget configuration properties.
 *
 * @class ContextPanel
 * @constructor
 * @extends ContextOverlay
 */
function ContextPanel(config) {
 	ContextPanel.superclass.constructor.apply(this, arguments);
}

A.mix(ContextPanel, {
	/**
	 * Static property provides a string to identify the class.
	 *
	 * @property ContextPanel.NAME
	 * @type String
	 * @static
	 */
	NAME: CONTEXTPANEL,

	/**
	 * Static property used to define the default attribute
	 * configuration for the ContextPanel.
	 *
	 * @property ContextPanel.ATTRS
	 * @type Object
	 * @static
	 */
	ATTRS: {
		/**
		 * Enable or disable the animation for hide and show. Used as the
         * Anim configuration attributes.
		 *
		 *
anim: {
		 *  show: {
		 *  	duration: .9
		 *  },
		 *  hide: {
		 *  	duration: .2
		 *  }
		 * }
		 *

		 *
		 * @attribute anim
		 * @default { show: false }
		 * @type Object
		 */
		anim: {
			lazyAdd: false,
			value: {
				show: false
			},
			setter: function(v) {
				return this._setAnim(v);
			}
		},

		/**
		 * Position where the arrow will be placed. See
         * showArrow. If it's
         * not set, it will get the value set on the
         * align point. Here is a
         * list of valid arrows 'bc', 'bl', 'br', 'cc', 'lb', 'lc', 'lt', 'rb',
         * 'rc', 'rl'.
		 *
		 * @attribute arrow
		 * @default null
		 * @type String
		 */
		arrow: {
			value: null,
			validator: isString
		},

		/**
		 * See hideOn.
		 *
		 * @attribute hideOn
		 * @default click
		 * @type String
		 */
		hideOn: {
			value: CLICK
		},

		/**
		 * See showOn.
		 *
		 * @attribute showOn
		 * @default click
		 * @type String
		 */
		showOn: {
			value: CLICK
		},

		/**
		 * If true the ContextPanel will show an arrow positioned on the
         * arrow point.
		 *
		 * @attribute showArrow
		 * @default true
		 * @type boolean
		 */
		showArrow: {
			lazyAdd: false,
			value: true,
			validator: isBoolean
		},

		/**
		 * Gives stacking habilities to the ContextPanel.
		 *
		 * @attribute stack
		 * @default true
		 * @type boolean
		 */
		stack: {
			lazyAdd: false,
			value: true,
			setter: function(v) {
				return this._setStack(v);
			},
			validator: isBoolean
		}
	}
});

A.extend(ContextPanel, A.ContextOverlay, {
	/**
	 * Bind the events on the ContextPanel UI. Lifecycle.
	 *
	 * @method bindUI
	 * @protected
	 */
	bindUI: function() {
		var instance = this;

		instance.after('showArrowChange', instance._afterShowArrowChange);

		instance.before('show', instance._beforeShow);

		ContextPanel.superclass.bindUI.apply(instance, arguments);
	},

	/**
	 * Create the DOM structure for the ContextPanel. Lifecycle.
	 *
	 * @method renderUI
	 * @protected
	 */
	renderUI: function() {
		var instance = this;

		instance._renderElements();
	},

	/**
	 * Sync the ContextPanel UI. Lifecycle.
	 *
	 * @method syncUI
	 * @protected
	 */
	syncUI: function() {
		var instance = this;

		instance._syncElements();
	},

	/**
	 * Aligns the ContextPanel to the provided node (or viewport) using the
     * provided points. Inherited from
     * Overlay.
	 *
	 * @method align
	 * @param {Node | String | null} node A reference (or selector string) for
     * the Node which with the ContextPanel is to be aligned.
	 * @param {Array[2]} points A two element array, specifying the points on
     * the ContextPanel and node/viewport which need to be aligned.
	 */
	align: function (node, points) {
		var instance = this;

		ContextPanel.superclass.align.apply(this, arguments);

		instance._syncElements();
	},

	/**
	 * ContextPanel uses a imageless arrow, which involves some CSS technics.
     * This method is meant to fix the color of the borders of the arrow.
	 *
	 * @method fixPointerColor
	 */
	fixPointerColor: function() {
		var instance = this;
		var contentBox = instance.get(CONTENT_BOX);
		var pointer = contentBox.query(DOT+CSS_CONTEXTPANEL_POINTER_INNER);

		pointer.removeAttribute(STYLE);

		var bColor = contentBox.getStyle(BACKGROUND_COLOR);
		var border = 'borderBottomColor';

		var right = [
			DOT+CSS_CONTEXTPANEL_ARROW+RB,
				DOT+CSS_CONTEXTPANEL_ARROW+RC,
					DOT+CSS_CONTEXTPANEL_ARROW+RL
		]
		.join(',');

		var bottom = [
			DOT+CSS_CONTEXTPANEL_ARROW+BR,
				DOT+CSS_CONTEXTPANEL_ARROW+BC,
					DOT+CSS_CONTEXTPANEL_ARROW+BL
		]
		.join(',');

		var left = [
			DOT+CSS_CONTEXTPANEL_ARROW+LB,
				DOT+CSS_CONTEXTPANEL_ARROW+LC,
					DOT+CSS_CONTEXTPANEL_ARROW+LT
		]
		.join(',');

		if (contentBox.test(right)) {
			border = 'borderLeftColor';
		}
		else if (contentBox.test(bottom)) {
			border = 'borderTopColor';
		}
		else if (contentBox.test(left)) {
			border = 'borderRightColor';
		}

		pointer.setStyle(border, bColor);
	},

	/**
	 * Normalize the align point value. The align point 'cc' is not a valid
     * position for the arrow and then it's normalized to the 'bc' point.
	 *
	 * @method getAlignPoint
	 * @return {String}
	 */
	getAlignPoint: function() {
		var instance = this;
		var overlayPoint = instance.get(ALIGN).points[0];

		if (overlayPoint == CC) {
			// CC is not a valid position for the arrow
			overlayPoint = BC;
		}

		return instance.get(ARROW) || overlayPoint;
	},

	/**
	 * Hides the ContextPanel.
	 *
	 * @method hide
	 * @param {EventFacade} event
	 */
	hide: function(event) {
		var instance = this;
		var boundingBox = instance.get(BOUNDING_BOX);

		if(instance._hideAnim) {
			var visible = instance.get(VISIBLE);

			if (visible) {
				instance._hideAnim.run();

				instance._hideAnim.on(END, function() {
					ContextPanel.superclass.hide.apply(instance, arguments);
				});
			}
		}
		else {
			ContextPanel.superclass.hide.apply(instance, arguments);
		}
	},

	/**
	 * Render DOM elements for the ContextPanel.
	 *
	 * @method _renderElements
	 * @protected
	 */
	_renderElements: function() {
		var instance = this;
		var contentBox = instance.get(CONTENT_BOX);
		var align = instance.get(ALIGN);
		var overlayPoint = align.points[0];

		contentBox.addClass(CSS_STATE_DEFAULT);

		instance._pointerNode = A.Node.create(TPL_POINTER).append(TPL_POINTER_INNER);

		contentBox.append(
			instance._pointerNode
		);
	},

	/**
	 * Sync the UI of the ContextPanel elements.
	 *
	 * @method _syncElements
	 * @protected
	 */
	_syncElements: function() {
		var instance = this;
		var contentBox = instance.get(CONTENT_BOX);
		var pointerNode = instance._pointerNode;
		var overlayPoint = instance.getAlignPoint();

		if (instance.get(SHOW_ARROW)) {
			pointerNode.removeClass(CSS_CONTEXTPANEL_HIDDEN);
			contentBox.removeClass(CSS_CONTEXTPANEL_ARROW + instance._lastOverlayPoint);
			contentBox.addClass(CSS_CONTEXTPANEL_ARROW + overlayPoint);

			instance.fixPointerColor();
		}
		else {
			pointerNode.addClass(CSS_CONTEXTPANEL_HIDDEN);
		}

		instance._lastOverlayPoint = overlayPoint;
	},

	/**
	 * Setter for the
     * stack attribute.
	 *
	 * @method _setStack
	 * @param {boolean} value
	 * @protected
	 * @return {boolean}
	 */
	_setStack: function(value) {
		var instance = this;

		if (value) {
			A.ContextPanelManager.register(instance);
		}
		else {
			A.ContextPanelManager.remove(instance);
		}

		return value;
	},

	/**
	 * Setter for the
     * anim attribute.
	 *
	 * @method _setAnim
	 * @param {Object} value
	 * @protected
	 * @return {Object}
	 */
	_setAnim: function(value) {
		var instance = this;
		var boundingBox = instance.get(BOUNDING_BOX);

		if (value) {
			var defaults = {
				node: boundingBox,
				duration: 0.1
			};

			var showOptions = A.merge(defaults, {
	    		from: { opacity: 0 },
	    		to: { opacity: 1 }
			});

			var hideOptions = A.merge(defaults, {
				from: { opacity: 1 },
	    		to: { opacity: 0 }
			});

			if (isObject(value)) {
				// loading user settings for animation
				showOptions = A.merge(showOptions, value.show);
				hideOptions = A.merge(hideOptions, value.hide);
			}

			instance._showAnim = new A.Anim(showOptions);
			instance._hideAnim = new A.Anim(hideOptions);

			// if anim.show or anim.hide === false, cancel respective animation
			if (isObject(value)) {
				if (value.show === false) {
					instance._showAnim = null;
				}

				if (value.hide === false) {
					instance._hideAnim = null;
				}
			}
		}

		return value;
	},

	/**
	 * Fires before show the ContextPanel.
	 *
	 * @method _beforeShow
	 * @param {EventFacade} event
	 * @protected
	 */
	_beforeShow: function(event) {
		var instance = this;
		var boundingBox = instance.get(BOUNDING_BOX);
		var visible = instance.get(VISIBLE);

		if(!visible && instance._showAnim) {
			boundingBox.setStyle(OPACITY, 0);

			instance._showAnim.run();
		}
		else {
			boundingBox.setStyle(OPACITY, 1);
		}
	},

	/**
	 * Fires after showArrow attribute changes.
	 *
	 * @method _afterShowArrowChange
	 * @param {EventFacade} event
	 * @protected
	 */
	_afterShowArrowChange: function() {
		var instance = this;

		instance._syncElements();
	}
});

A.ContextPanel = ContextPanel;

/**
 * A base class for ContextPanelManager:
 *
 * @param config {Object} Object literal specifying widget configuration properties.
 *
 * @class ContextPanelManager
 * @constructor
 * @extends OverlayManager
 * @static
 */
A.ContextPanelManager = new A.OverlayManager({
	zIndexBase: 1000
});<|endoftext|>


<commit_before>import store from 'APP/app/store'
import { toggleChatBox } from 'APP/app/reducers/chat'

import EntityPrefab from './EntityPrefab'
import HealthBar from './HealthBar.js'
import Weapon from './Weapon'
import HUD from './HUD'

import armorProperties from '../properties/armorProperties.json'
import playerProperties from '../properties/playerProperties.json'
import itemProperties from '../properties/itemProperties.json'

import { socket, GameGroups } from '../sockets'

/* global StackQuest, Phaser */

// client side class for Playable Characters
export default class Player extends EntityPrefab {
  constructor(game, name, player) {
    super(game, name, { x: player.x, y: player.y }, player.class)

    GameGroups.players.add(this)

    this.player = player
    this.anchor.set(0.5, 0.2)
    this.orientation = 'down'

    this.lastSpecialAttack = Date.now()

    this.absorbProperties(playerProperties[player.class])

    this.checkStats(player)

    this.stats.hp = player.hp
    this.setAnimationFrames(this)
    this.killCount = 0
    this.lootCount = 0
    this.loadControls = this.loadControls.bind(this)
    this.loadControls()
    this.chatToggleTime = Date.now()

    this.movePlayer = this.movePlayer.bind(this)
    this.moveOther = this.moveOther.bind(this)

    this.bulletPool = this.game.add.group()

    this.equipSpecial = this.equipSpecial.bind(this)
    this.equipSpecial(this.specialKey)
    this.specialAttack = this.specialAttack.bind(this)

    this.equipWeapon = this.equipWeapon.bind(this)
    this.equipWeapon(this.weaponKey)
    this.attack = this.attack.bind(this)

    this.equipArmor = this.equipArmor.bind(this)
    this.equipArmor(this.armorKey)

    this.takeDamage = this.takeDamage.bind(this)
    this.respawn = this.respawn.bind(this)
    this.playerHealthBar = new HealthBar(game, { x: player.x, y: player.y - 10 })
    this.computeLifeBar()
    this.recoverHp = this.recoverHp.bind(this)

    this.pickUpItem = this.pickUpItem.bind(this)
    this.savePlayer = this.savePlayer.bind(this)
  }

  checkStats(player) {
    if (player.stats) this.stats = player.stats
  }

  equipSpecial(specialKey) {
    this.special = new Weapon(this.game, this, specialKey)
    this.special.name = specialKey
    return true
  }
  equipWeapon(weaponKey) {
    this.weapon = new Weapon(this.game, this, weaponKey)
    this.weapon.name = weaponKey
    return true
  }

  equipArmor(armorKey) {
    this.armor = armorProperties[armorKey]
    this.armor.name = armorKey
    return true
  }

  loadControls() {
    this.cursors = {}
    this.cursors.up = this.game.input.keyboard.addKey(Phaser.Keyboard.W)
    this.cursors.down = this.game.input.keyboard.addKey(Phaser.Keyboard.S)
    this.cursors.right = this.game.input.keyboard.addKey(Phaser.Keyboard.D)
    this.cursors.left = this.game.input.keyboard.addKey(Phaser.Keyboard.A)
    this.cursors.space = this.game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR)
    this.cursors.chat = this.game.input.keyboard.addKey(Phaser.Keyboard.TAB)
    this.cursors.board = this.game.input.keyboard.addKey(Phaser.Keyboard.B)
    this.cursors.click = this.game.input.activePointer

    this.cursors.board.onDown.add(this.toggleHUDBoards, this)
  }

  chat() {
    if (this.cursors.chat.isDown) {
      if (Date.now() - this.chatToggleTime > 150) {
        store.dispatch(toggleChatBox())
        this.chatToggleTime = Date.now()
      }
      if (store.getState().chat.showChat) {
        this.game.input.keyboard.removeKey(Phaser.Keyboard.W)
        this.game.input.keyboard.removeKey(Phaser.Keyboard.S)
        this.game.input.keyboard.removeKey(Phaser.Keyboard.D)
        this.game.input.keyboard.removeKey(Phaser.Keyboard.A)
        this.game.input.keyboard.removeKey(Phaser.Keyboard.SPACEBAR)
        this.game.input.keyboard.removeKey(Phaser.Keyboard.B)
      } else {
        this.loadControls()
      }
    }
  }

  moveOther(targetX, targetY) {
    const xDirection = this.position.x - targetX
    const yDirection = this.position.y - targetY
    const absDirection = Math.abs(xDirection) * 2 - Math.abs(yDirection)

    if (yDirection > 0) {
      this.orientation = 'up'
    } else if (yDirection < 0) {
      this.orientation = 'down'
    }
    if (xDirection > 0) {
      this.orientation = 'left'
    } else if (xDirection < 0) {
      this.orientation = 'right'
    }

    this.animations.play(`walk_${this.orientation}`, null, true)
    if (this.game) {
      this.moveTween = this.game.add.tween(this.position).to({ x: targetX, y: targetY }, 33)
      this.moveTween.onComplete.add(this.completeMovement, this)
      this.moveTween.start()
    }
    this.playerHealthBar.setPosition(targetX, targetY - 10)
  }

  completeMovement() {
    this.animations.stop()
  }

  takeDamage(damage) {
    const damageTaken = damage - (this.stats.defense + this.armor.defense)
    if (damageTaken > 0) {
      this.stats.hp -= damageTaken
      const damageText = StackQuest.game.add.text(this.x + Math.random() * 20, this.y + Math.random() * 20, damageTaken, { font: '20px Press Start 2P', fill: '#ffa500' })
      setTimeout(() => damageText.destroy(), 500)

      if (this.HUD) {
        this.HUD.updateHealth()
      }

      console.log(this.game.plugins.screenShake)
      this.game.plugins.screenShake.shake(3, this)
      socket.emit('updateStats', this.stats)

      this.computeLifeBar()
      //  check if dead
      if (this.stats.hp <= 0) this.respawn()
    }
  }

  respawn() {
    //  make them move to set location
    this.position.x = 500
    this.position.y = 500

    // Revive
    setTimeout(() => {
      this.recoverHp()
      this.savePlayer()
    }, 100)
    socket.emit('updatePlayer', { playerPos: this.position, lootCount: 0, killCount: this.killCount })

    if (this.HUD) {
      this.HUD.updateFeed('You Died')
    }
  }

  recoverHp() {
    this.stats.hp = this.stats.maxHp
    if (this.HUD) {
      this.HUD.updateHealth()
    }
    this.computeLifeBar()
    socket.emit('updateStats', this.stats)
  }

  movePlayer() {
    this.body.velocity.x = 0
    this.body.velocity.y = 0

    this.playerHealthBar.setPosition(this.position.x, this.position.y - 10)

    if (this.cursors.up.isDown) {
      this.body.velocity.y = -this.stats.speed
      this.orientation = 'up'
      socket.emit('updatePlayer', { playerPos: this.position, lootCount: this.lootCount, killCount: this.killCount })
    } else if (this.cursors.down.isDown) {
      this.body.velocity.y = this.stats.speed
      this.orientation = 'down'
      socket.emit('updatePlayer', { playerPos: this.position, lootCount: this.lootCount, killCount: this.killCount })
    }
    if (this.cursors.left.isDown) {
      this.body.velocity.x = -this.stats.speed
      this.orientation = 'left'
      socket.emit('updatePlayer', { playerPos: this.position, lootCount: this.lootCount, killCount: this.killCount })
    } else if (this.cursors.right.isDown) {
      this.body.velocity.x = this.stats.speed
      this.orientation = 'right'
      socket.emit('updatePlayer', { playerPos: this.position, lootCount: this.lootCount, killCount: this.killCount })
    }

    if (this.body.velocity.x + this.body.velocity.y !== 0) {
      this.animations.play(`walk_${this.orientation}`)
    }
  }

  specialAttack() {
    if (this.cursors.space.isDown && this.cursors.click.isDown) {
      if (Date.now() - this.lastSpecialAttack > 5000) {
        this.lastSpecialAttack = Date.now()
        const targetX = this.game.input.worldX
        const targetY = this.game.input.worldY
        this.special.fire(null, targetX, targetY)
        socket.emit('fireSpecial', targetX, targetY)
      }
    }
  }

  attack() {
    if (this.cursors.click.isDown && !this.cursors.space.isDown) {
      const targetX = this.game.input.worldX
      const targetY = this.game.input.worldY
      this.weapon.fire(null, targetX, targetY)
      socket.emit('fireProjectile', targetX, targetY)
    }
  }

  computeLifeBar() {
    if (this.stats.hp < 0) this.stats.hp = 0
    const percent = Math.floor((this.stats.hp / this.stats.maxHp) * 100)
    this.playerHealthBar.setPercent(percent)
  }

  pickUpItem(item) {
    const itemProperty = itemProperties[item]

    switch (itemProperty.type) {
    case 'attack':
      this.stats.attack += itemProperty.buff
      break

    case 'defense':
      this.stats.defense += itemProperty.buff
      break

    case 'loot':
      this.lootCount += itemProperty.buff
      break

    case 'hp':
      this.stats.hp += itemProperty.buff
      if (this.stats.hp > 100) this.stats.hp = 100
      break
    }

    if (this.HUD) {
      let text = `Acquired ${item}`

      if (itemProperty.type === 'attack' || itemProperty.type === 'defense') {
        this.HUD.updateStats()
        text += `, ${itemProperty.type}+${itemProperty.buff} `
      } else if (itemProperty.type === 'hp') {
        this.computeLifeBar()
        this.HUD.updateHealth()
        text += `, ${itemProperty.type}+${itemProperty.buff} `
      }

      this.HUD.updateFeed(text)
    }

    socket.emit('updateStats', this.stats)
    socket.emit('updatePlayer', { playerPos: this.position, lootCount: this.lootCount, killCount: this.killCount })
  }

  savePlayer() {
    this.player.x = this.x
    this.player.y = this.y
    this.player.hp = this.stats.hp

    socket.emit('savePlayer', this.player)
  }

  toggleHUDBoards() {
    if (this.HUD && this.cursors.board.isDown) {
      this.HUD.toggleBoards()
    }
  }

  update() {
    if (this.HUD) {
      this.HUD.updateScoreboard()
      this.HUD.updateKillboard()
    }
  }
}
<commit_msg>minor fix<commit_after>import store from 'APP/app/store'
import { toggleChatBox } from 'APP/app/reducers/chat'

import EntityPrefab from './EntityPrefab'
import HealthBar from './HealthBar.js'
import Weapon from './Weapon'
import HUD from './HUD'

import armorProperties from '../properties/armorProperties.json'
import playerProperties from '../properties/playerProperties.json'
import itemProperties from '../properties/itemProperties.json'

import { socket, GameGroups } from '../sockets'

/* global StackQuest, Phaser */

// client side class for Playable Characters
export default class Player extends EntityPrefab {
  constructor(game, name, player) {
    super(game, name, { x: player.x, y: player.y }, player.class)

    GameGroups.players.add(this)

    this.player = player
    this.anchor.set(0.5, 0.2)
    this.orientation = 'down'

    this.lastSpecialAttack = Date.now()

    this.absorbProperties(playerProperties[player.class])

    this.checkStats(player)

    this.stats.hp = player.hp
    this.setAnimationFrames(this)
    this.killCount = 0
    this.lootCount = 0
    this.loadControls = this.loadControls.bind(this)
    this.loadControls()
    this.chatToggleTime = Date.now()

    this.movePlayer = this.movePlayer.bind(this)
    this.moveOther = this.moveOther.bind(this)

    this.bulletPool = this.game.add.group()

    this.equipSpecial = this.equipSpecial.bind(this)
    this.equipSpecial(this.specialKey)
    this.specialAttack = this.specialAttack.bind(this)

    this.equipWeapon = this.equipWeapon.bind(this)
    this.equipWeapon(this.weaponKey)
    this.attack = this.attack.bind(this)

    this.equipArmor = this.equipArmor.bind(this)
    this.equipArmor(this.armorKey)

    this.takeDamage = this.takeDamage.bind(this)
    this.respawn = this.respawn.bind(this)
    this.playerHealthBar = new HealthBar(game, { x: player.x, y: player.y - 10 })
    this.computeLifeBar()
    this.recoverHp = this.recoverHp.bind(this)

    this.pickUpItem = this.pickUpItem.bind(this)
    this.savePlayer = this.savePlayer.bind(this)
  }

  checkStats(player) {
    if (player.stats) this.stats = player.stats
  }

  equipSpecial(specialKey) {
    this.special = new Weapon(this.game, this, specialKey)
    this.special.name = specialKey
    return true
  }
  equipWeapon(weaponKey) {
    this.weapon = new Weapon(this.game, this, weaponKey)
    this.weapon.name = weaponKey
    return true
  }

  equipArmor(armorKey) {
    this.armor = armorProperties[armorKey]
    this.armor.name = armorKey
    return true
  }

  loadControls() {
    this.cursors = {}
    this.cursors.up = this.game.input.keyboard.addKey(Phaser.Keyboard.W)
    this.cursors.down = this.game.input.keyboard.addKey(Phaser.Keyboard.S)
    this.cursors.right = this.game.input.keyboard.addKey(Phaser.Keyboard.D)
    this.cursors.left = this.game.input.keyboard.addKey(Phaser.Keyboard.A)
    this.cursors.space = this.game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR)
    this.cursors.chat = this.game.input.keyboard.addKey(Phaser.Keyboard.TAB)
    this.cursors.board = this.game.input.keyboard.addKey(Phaser.Keyboard.B)
    this.cursors.click = this.game.input.activePointer

    this.cursors.board.onDown.add(this.toggleHUDBoards, this)
  }

  chat() {
    if (this.cursors.chat.isDown) {
      if (Date.now() - this.chatToggleTime > 150) {
        store.dispatch(toggleChatBox())
        this.chatToggleTime = Date.now()
      }
      if (store.getState().chat.showChat) {
        this.game.input.keyboard.removeKey(Phaser.Keyboard.W)
        this.game.input.keyboard.removeKey(Phaser.Keyboard.S)
        this.game.input.keyboard.removeKey(Phaser.Keyboard.D)
        this.game.input.keyboard.removeKey(Phaser.Keyboard.A)
        this.game.input.keyboard.removeKey(Phaser.Keyboard.SPACEBAR)
        this.game.input.keyboard.removeKey(Phaser.Keyboard.B)
      } else {
        this.loadControls()
      }
    }
  }

  moveOther(targetX, targetY) {
    const xDirection = this.position.x - targetX
    const yDirection = this.position.y - targetY
    const absDirection = Math.abs(xDirection) * 2 - Math.abs(yDirection)

    if (yDirection > 0) {
      this.orientation = 'up'
    } else if (yDirection < 0) {
      this.orientation = 'down'
    }
    if (xDirection > 0) {
      this.orientation = 'left'
    } else if (xDirection < 0) {
      this.orientation = 'right'
    }

    this.animations.play(`walk_${this.orientation}`, null, true)
    if (this.game) {
      this.moveTween = this.game.add.tween(this.position).to({ x: targetX, y: targetY }, 33)
      this.moveTween.onComplete.add(this.completeMovement, this)
      this.moveTween.start()
    }
    this.playerHealthBar.setPosition(targetX, targetY - 10)
  }

  completeMovement() {
    this.animations.stop()
  }

  takeDamage(damage) {
    const damageTaken = damage - (this.stats.defense + this.armor.defense)
    if (damageTaken > 0) {
      this.stats.hp -= damageTaken
      const damageText = StackQuest.game.add.text(this.x + Math.random() * 20, this.y + Math.random() * 20, damageTaken, { font: '20px Press Start 2P', fill: '#ffa500' })
      setTimeout(() => damageText.destroy(), 500)

      if (this.HUD) {
        this.HUD.updateHealth()
        this.game.plugins.screenShake.shake(3, this)
      }
      socket.emit('updateStats', this.stats)

      this.computeLifeBar()
      //  check if dead
      if (this.stats.hp <= 0) this.respawn()
    }
  }

  respawn() {
    //  make them move to set location
    this.position.x = 500
    this.position.y = 500

    // Revive
    setTimeout(() => {
      this.recoverHp()
      this.savePlayer()
    }, 100)
    socket.emit('updatePlayer', { playerPos: this.position, lootCount: 0, killCount: this.killCount })

    if (this.HUD) {
      this.HUD.updateFeed('You Died')
    }
  }

  recoverHp() {
    this.stats.hp = this.stats.maxHp
    if (this.HUD) {
      this.HUD.updateHealth()
    }
    this.computeLifeBar()
    socket.emit('updateStats', this.stats)
  }

  movePlayer() {
    this.body.velocity.x = 0
    this.body.velocity.y = 0

    this.playerHealthBar.setPosition(this.position.x, this.position.y - 10)

    if (this.cursors.up.isDown) {
      this.body.velocity.y = -this.stats.speed
      this.orientation = 'up'
      socket.emit('updatePlayer', { playerPos: this.position, lootCount: this.lootCount, killCount: this.killCount })
    } else if (this.cursors.down.isDown) {
      this.body.velocity.y = this.stats.speed
      this.orientation = 'down'
      socket.emit('updatePlayer', { playerPos: this.position, lootCount: this.lootCount, killCount: this.killCount })
    }
    if (this.cursors.left.isDown) {
      this.body.velocity.x = -this.stats.speed
      this.orientation = 'left'
      socket.emit('updatePlayer', { playerPos: this.position, lootCount: this.lootCount, killCount: this.killCount })
    } else if (this.cursors.right.isDown) {
      this.body.velocity.x = this.stats.speed
      this.orientation = 'right'
      socket.emit('updatePlayer', { playerPos: this.position, lootCount: this.lootCount, killCount: this.killCount })
    }

    if (this.body.velocity.x + this.body.velocity.y !== 0) {
      this.animations.play(`walk_${this.orientation}`)
    }
  }

  specialAttack() {
    if (this.cursors.space.isDown && this.cursors.click.isDown) {
      if (Date.now() - this.lastSpecialAttack > 5000) {
        this.lastSpecialAttack = Date.now()
        const targetX = this.game.input.worldX
        const targetY = this.game.input.worldY
        this.special.fire(null, targetX, targetY)
        socket.emit('fireSpecial', targetX, targetY)
      }
    }
  }

  attack() {
    if (this.cursors.click.isDown && !this.cursors.space.isDown) {
      const targetX = this.game.input.worldX
      const targetY = this.game.input.worldY
      this.weapon.fire(null, targetX, targetY)
      socket.emit('fireProjectile', targetX, targetY)
    }
  }

  computeLifeBar() {
    if (this.stats.hp < 0) this.stats.hp = 0
    const percent = Math.floor((this.stats.hp / this.stats.maxHp) * 100)
    this.playerHealthBar.setPercent(percent)
  }

  pickUpItem(item) {
    const itemProperty = itemProperties[item]

    switch (itemProperty.type) {
    case 'attack':
      this.stats.attack += itemProperty.buff
      break

    case 'defense':
      this.stats.defense += itemProperty.buff
      break

    case 'loot':
      this.lootCount += itemProperty.buff
      break

    case 'hp':
      this.stats.hp += itemProperty.buff
      if (this.stats.hp > 100) this.stats.hp = 100
      break
    }

    if (this.HUD) {
      let text = `Acquired ${item}`

      if (itemProperty.type === 'attack' || itemProperty.type === 'defense') {
        this.HUD.updateStats()
        text += `, ${itemProperty.type}+${itemProperty.buff} `
      } else if (itemProperty.type === 'hp') {
        this.computeLifeBar()
        this.HUD.updateHealth()
        text += `, ${itemProperty.type}+${itemProperty.buff} `
      }

      this.HUD.updateFeed(text)
    }

    socket.emit('updateStats', this.stats)
    socket.emit('updatePlayer', { playerPos: this.position, lootCount: this.lootCount, killCount: this.killCount })
  }

  savePlayer() {
    this.player.x = this.x
    this.player.y = this.y
    this.player.hp = this.stats.hp

    socket.emit('savePlayer', this.player)
  }

  toggleHUDBoards() {
    if (this.HUD && this.cursors.board.isDown) {
      this.HUD.toggleBoards()
    }
  }

  update() {
    if (this.HUD) {
      this.HUD.updateScoreboard()
      this.HUD.updateKillboard()
    }
  }
}
<|endoftext|>


<commit_before>var Lang = A.Lang,
	isArray = Lang.isArray,
	isString = Lang.isString,

	getClassName = A.ClassNameManager.getClassName,

	NAME = 'creoleplugin',
	CREOLE_PLUGIN = 'creole',

	TPL_HTML_ELEMENTS = '(<[a-z][a-z0-9]*[^>]*>|</[a-z][a-z0-9]*>)',

	REGEX_HTML_ELEMENTS = new RegExp(TPL_HTML_ELEMENTS, 'gi');

if (!YUI.AUI.defaults.EditorToolbar) {
	YUI.AUI.defaults.EditorToolbar = {
		STRINGS: {}
	};
}

var EditorCreoleCode = A.Component.create(
	{
		NAME: NAME,

		NS: CREOLE_PLUGIN,

		EXTENDS: A.Plugin.Base,

		ATTRS: {
			interwiki: {
				value: {
					WikiCreole: 'http://www.wikicreole.org/wiki/',
					Wikipedia: 'http://en.wikipedia.org/wiki/'
				}
			},

			host: {
				value: false
			},

			linkFormat: {
				value: ''
			},

			strict: {
				value: false
			}
		},

		prototype: {
			html2creole: null,

			initializer: function() {
				var instance = this;

				this._creole = new A.CreoleParser({
					forIE: A.UA.os === 'windows',
					interwiki: this.get('interwiki'),
					linkFormat: this.get('linkFormat'),
					strict: this.get('strict')
				});

				var host = instance.get('host');

				instance.afterHostMethod('getContent', instance.getCreoleCode, instance);
				host.on('contentChange', instance._contentChange, instance);
			},

			_convertHTML2Creole: function(data) {
				var instance = this;

				if (!instance.html2creole) {
					instance.html2creole = new A.HTML2CreoleConvertor({
						data: data
					});
				} else {
					instance.html2creole.set(data);
				}

				return instance.html2creole.convert();
			},

			getCreoleCode: function() {
				var instance = this;

				var content = A.Do.originalRetVal;

				content = instance._convertHTML2Creole(content);

				content = content.replace(REGEX_HTML_ELEMENTS, '');

				return new A.Do.AlterReturn(null, content);
			},

			getContentAsHtml: function() {
				var instance = this;

				var host = instance.get('host');

				return host.constructor.prototype.getContent.apply(host, arguments);
			},

			setContentAsCreoleCode: function(creoleCode) {
				var instance = this;

				var host = instance.get('host');

				host.set('content', creoleCode);
			},

			_contentChange: function(event) {
				var instance = this;

				event.newVal = instance._parseCreoleCode(event.newVal);

				event.stopImmediatePropagation();
			},

			_parseCreoleCode: function(creoleCode) {
				var instance = this;

				var div = A.config.doc.createElement('div');

				instance._creole.parse(div, creoleCode);

				var data = div.innerHTML;

				return data;
			}
		}
	}
);

A.namespace('Plugin').EditorCreoleCode = EditorCreoleCode;
<commit_msg>AUI-291 - Fixed typo in Creole plugin<commit_after>var Lang = A.Lang,
	isArray = Lang.isArray,
	isString = Lang.isString,

	getClassName = A.ClassNameManager.getClassName,

	NAME = 'creoleplugin',
	CREOLE_PLUGIN = 'creole',

	TPL_HTML_ELEMENTS = '(<[a-z][a-z0-9]*[^>]*>|</[a-z][a-z0-9]*>)',

	REGEX_HTML_ELEMENTS = new RegExp(TPL_HTML_ELEMENTS, 'gi');

if (!YUI.AUI.defaults.EditorToolbar) {
	YUI.AUI.defaults.EditorToolbar = {
		STRINGS: {}
	};
}

var EditorCreoleCode = A.Component.create(
	{
		NAME: NAME,

		NS: CREOLE_PLUGIN,

		EXTENDS: A.Plugin.Base,

		ATTRS: {
			interwiki: {
				value: {
					WikiCreole: 'http://www.wikicreole.org/wiki/',
					Wikipedia: 'http://en.wikipedia.org/wiki/'
				}
			},

			host: {
				value: false
			},

			linkFormat: {
				value: ''
			},

			strict: {
				value: false
			}
		},

		prototype: {
			html2creole: null,

			initializer: function() {
				var instance = this;

				this._creole = new A.CreoleParser({
					forIE: A.UA.os === 'windows',
					interwiki: this.get('interwiki'),
					linkFormat: this.get('linkFormat'),
					strict: this.get('strict')
				});

				var host = instance.get('host');

				instance.afterHostMethod('getContent', instance.getCreoleCode, instance);
				host.on('contentChange', instance._contentChange, instance);
			},

			_convertHTML2Creole: function(data) {
				var instance = this;

				if (!instance.html2creole) {
					instance.html2creole = new A.HTML2CreoleConvertor({
						data: data
					});
				} else {
					instance.html2creole.set('data', data);
				}

				return instance.html2creole.convert();
			},

			getCreoleCode: function() {
				var instance = this;

				var content = A.Do.originalRetVal;

				content = instance._convertHTML2Creole(content);

				content = content.replace(REGEX_HTML_ELEMENTS, '');

				return new A.Do.AlterReturn(null, content);
			},

			getContentAsHtml: function() {
				var instance = this;

				var host = instance.get('host');

				return host.constructor.prototype.getContent.apply(host, arguments);
			},

			setContentAsCreoleCode: function(creoleCode) {
				var instance = this;

				var host = instance.get('host');

				host.set('content', creoleCode);
			},

			_contentChange: function(event) {
				var instance = this;

				event.newVal = instance._parseCreoleCode(event.newVal);

				event.stopImmediatePropagation();
			},

			_parseCreoleCode: function(creoleCode) {
				var instance = this;

				var div = A.config.doc.createElement('div');

				instance._creole.parse(div, creoleCode);

				var data = div.innerHTML;

				return data;
			}
		}
	}
);

A.namespace('Plugin').EditorCreoleCode = EditorCreoleCode;
<|endoftext|>


<commit_before>({
    /**
     * @description: clears all info on user-selected open donation
     */
    clearDonationSelectionOptions: function(component) {
        component.set('v.selectedDonation', null);
        component.set('v.openOpportunities', null);
        component.set('v.unpaidPayments', null);
    },

    /**
     * @description: sets the value of the selected donation
     */
    setDonation: function(component, selectedDonation) {
        component.set('v.selectedDonation', selectedDonation);
    },

    /**
     * @description: handle apex errors on the page by displaying a toast with the returned information
     */
    handleApexErrors: function(component, errors) {
        let message = $A.get('$Label.c.stgUnknownError');
        if (errors) {
            if (errors[0] && errors[0].message) {
                message = errors[0].message;
            }
        }
        this.sendMessage(component, 'onError', {title: $A.get('$Label.c.PageMessagesError'), errorMessage: message});
    },

    /**
     * @description: adds hidden and non-lightning:inputfield fields to the Data Import record before submitting.
     * @return: Object rowFields with hidden fields added
     */
    getRowWithHiddenFields: function (component, event) {
        let rowFields = event.getParam('fields');
        const labels = component.get('v.labels');

        const recId = component.get('v.recordId');
        rowFields[labels.batchIdField] = recId;

        // add donor type hidden fields
        const donorType = component.get('v.donorType');
        rowFields[labels.donationDonor] = donorType;

        // add any picklist fields manually, because they use lightning:select
        const dynamicInputFields = component.find('dynamicInputFields');
        const dataImportFields = component.get('v.dataImportFields');

        //dataImportFields and dynamicInputFields have the same order, so can loop both to get the value
        for (let i=0; i<dataImportFields.length; i++) {
            if (dataImportFields[i].options && dataImportFields[i].options.length > 0) {
                var fieldValue = dynamicInputFields[i].get('v.value');
                var fieldName = dataImportFields[i].name;
                rowFields[fieldName] = fieldValue;
            }
        }

        // assign opportunity/payment lookup and import status
        const selectedDonation = component.get('v.selectedDonation');
        const userSelectedMatch = $A.get('$Label.c.bdiMatchedByUser');
        const userSelectedNewOpp = $A.get('$Label.c.bdiMatchedByUserNewOpp');
        const applyNewPayment = $A.get('$Label.c.bdiMatchedApplyNewPayment');

        //set status fields to prevent dry run overwrite of lookup fields
        //else status fields are left null to allow for dry run in grid
        if (selectedDonation) {
            if (selectedDonation.attributes.type === 'Opportunity') {
                rowFields[labels.opportunityImportedLookupField] = selectedDonation.Id;
                if (selectedDonation.applyPayment) {
                    // Apply New Payment
                    rowFields[labels.opportunityImportedStatusField] = applyNewPayment;
                } else {
                    // Update Opportunity
                    rowFields[labels.opportunityImportedStatusField] = userSelectedMatch;
                }
            } else {
                // Update Payment
                rowFields[labels.paymentImportedLookupField] = selectedDonation.Id;
                rowFields[labels.paymentImportedStatusField] = userSelectedMatch;
                rowFields[labels.opportunityImportedLookupField] = selectedDonation.npe01__Opportunity__c;
                rowFields[labels.opportunityImportedStatusField] = userSelectedMatch;
            }
        } else if (selectedDonation === '') {
            //create new opportunity if selectedDonation is set as empty string
            rowFields[labels.opportunityImportedStatusField] = userSelectedNewOpp;
        }

        return rowFields;
    },

    /**
     * @description: Gets open donations without any Payments and unpaid Payments
     * for use in the Donation Selector component.
     * @return: void
     */
    getOpportunitiesWithOppPayments: function(component, recordId) {
        const donorType = component.get('v.donorType');

        let action = component.get('c.getOpportunitiesWithOppPayments');
        action.setParams({donorId: recordId, donorType: donorType});
        action.setCallback(this, function (response) {
            const state = response.getState();
            if (state === 'SUCCESS') {
                const opportunitiesWithPayments = JSON.parse(response.getReturnValue());
                component.set('v.donations', opportunitiesWithPayments);
                let openOpportunitiesWithoutAnyPayments = [];
                let unpaidPayments = [];
                opportunitiesWithPayments.forEach(function (opportunityWrapper) {
                        if (opportunityWrapper.hasPayments === false) {
                            openOpportunitiesWithoutAnyPayments.push(opportunityWrapper.opportunity);
                        }
                        opportunityWrapper.unpaidPayments.forEach(function (payment) {
                            unpaidPayments.push(payment);
                        });
                    }
                );
                component.set('v.unpaidPayments', unpaidPayments);
                component.set('v.openOpportunities', openOpportunitiesWithoutAnyPayments);
            } else {
                this.handleApexErrors(component, response.getError());
            }
            this.sendMessage(component, 'hideFormSpinner', '');
        });
        $A.enqueueAction(action);
    },

    /**
     * @description: sends error toast to parent component notifying user of missing fields
     * @param missingFields: Array of missing fields
     */
    sendErrorToast: function(component, missingFields) {
        let channel = 'onError';
        let error = $A.get('$Label.c.exceptionRequiredField') + ' ' + missingFields.join(', ') + '.';
        let message = {title: $A.get('$Label.c.PageMessagesError'), errorMessage: error};
        this.sendMessage(component, channel, message);
    },

    /**
     * @description: send a message to other components
     */
    sendMessage: function (component, channel, info) {
        let message = {
            info: info,
            batchId: component.get('v.recordId')
        };
        let sendMessage = $A.get('e.ltng:sendMessage');
        sendMessage.setParams({
            'channel': channel,
            'message': message
        });
        sendMessage.fire();
    },

    /**
     * @description: checks for Donor and other required fields and gathers error messages before submitting
     * @return: validity Object with Boolean for validity and an array of any missing fields to display
     */
    validateFields: function(component, rowFields) {
        let validity = {isValid: true, missingFields: []};

        const hasDonor = this.verifyRowHasDonor(component, rowFields);
        if (!hasDonor) {
            const labels = component.get("v.labels");
            const missingDonor = (component.get("v.donorType") === 'Contact1') ? labels.contactObject : labels.accountObject;
            validity.missingFields.push(missingDonor);
        }

        let missingRequiredFields = this.verifyRequiredFields(component, rowFields);
        if (missingRequiredFields.length != 0) {
            validity.missingFields = validity.missingFields.concat(missingRequiredFields);
        }

        if (validity.missingFields.length !== 0) {
            validity.isValid = false;
        }

        return validity;
    },

    /**
     * @description: checks for presence of fields that user has marked as required
     * @param rowFields: Object rowFields with updated hidden values
     * @return missingFields: list of any fields by label that are missing
     */
    verifyRequiredFields: function(component, rowFields) {
        let missingFields = [];
        const dataImportFields = component.get('v.dataImportFields');
        let dynamicInputFields = component.find('dynamicInputFields');

        if (! Array.isArray(dynamicInputFields)) {
            dynamicInputFields = [dynamicInputFields];
        }

        //dataImportFields and dynamicInputFields have the same order, so can loop both to check validity
        for (let i=0; i<dataImportFields.length; i++) {
            if (dataImportFields[i].required) {
                const fieldValue = dynamicInputFields[i].get('v.value');
                if (fieldValue === '' || fieldValue === null || (Array.isArray(fieldValue) && !fieldValue.length) ) {
                    missingFields.push(dataImportFields[i].label);
                }
            }
        }

        return missingFields;
    },

    /**
     * @description: checks for presence of a donor, which is always required
     * @param rowFields: Object rowFields with updated hidden values
     * @return hasDonor: Boolean to indicate if row has a donor
     */
    verifyRowHasDonor: function(component, rowFields) {
        let hasDonor = true;
        let lookupValue;

        if (component.get("v.donorType") === 'Contact1') {
            lookupValue = rowFields[component.get("v.labels.contactLookup")];
        } else if (component.get("v.donorType") === 'Account1') {
            lookupValue = rowFields[component.get("v.labels.accountLookup")];
        }

        if (!lookupValue || lookupValue.length !== 18) {
            hasDonor = false;
        }

        return hasDonor;
    }

})<commit_msg>remove unneeded attribute reference<commit_after>({
    /**
     * @description: clears all info on user-selected open donation
     */
    clearDonationSelectionOptions: function(component) {
        component.set('v.selectedDonation', null);
        component.set('v.openOpportunities', null);
        component.set('v.unpaidPayments', null);
    },

    /**
     * @description: sets the value of the selected donation
     */
    setDonation: function(component, selectedDonation) {
        component.set('v.selectedDonation', selectedDonation);
    },

    /**
     * @description: handle apex errors on the page by displaying a toast with the returned information
     */
    handleApexErrors: function(component, errors) {
        let message = $A.get('$Label.c.stgUnknownError');
        if (errors) {
            if (errors[0] && errors[0].message) {
                message = errors[0].message;
            }
        }
        this.sendMessage(component, 'onError', {title: $A.get('$Label.c.PageMessagesError'), errorMessage: message});
    },

    /**
     * @description: adds hidden and non-lightning:inputfield fields to the Data Import record before submitting.
     * @return: Object rowFields with hidden fields added
     */
    getRowWithHiddenFields: function (component, event) {
        let rowFields = event.getParam('fields');
        const labels = component.get('v.labels');

        const recId = component.get('v.recordId');
        rowFields[labels.batchIdField] = recId;

        // add donor type hidden fields
        const donorType = component.get('v.donorType');
        rowFields[labels.donationDonor] = donorType;

        // add any picklist fields manually, because they use lightning:select
        const dynamicInputFields = component.find('dynamicInputFields');
        const dataImportFields = component.get('v.dataImportFields');

        //dataImportFields and dynamicInputFields have the same order, so can loop both to get the value
        for (let i=0; i<dataImportFields.length; i++) {
            if (dataImportFields[i].options && dataImportFields[i].options.length > 0) {
                var fieldValue = dynamicInputFields[i].get('v.value');
                var fieldName = dataImportFields[i].name;
                rowFields[fieldName] = fieldValue;
            }
        }

        // assign opportunity/payment lookup and import status
        const selectedDonation = component.get('v.selectedDonation');
        const userSelectedMatch = $A.get('$Label.c.bdiMatchedByUser');
        const userSelectedNewOpp = $A.get('$Label.c.bdiMatchedByUserNewOpp');
        const applyNewPayment = $A.get('$Label.c.bdiMatchedApplyNewPayment');

        //set status fields to prevent dry run overwrite of lookup fields
        //else status fields are left null to allow for dry run in grid
        if (selectedDonation) {
            if (selectedDonation.attributes.type === 'Opportunity') {
                rowFields[labels.opportunityImportedLookupField] = selectedDonation.Id;
                if (selectedDonation.applyPayment) {
                    // Apply New Payment
                    rowFields[labels.opportunityImportedStatusField] = applyNewPayment;
                } else {
                    // Update Opportunity
                    rowFields[labels.opportunityImportedStatusField] = userSelectedMatch;
                }
            } else {
                // Update Payment
                rowFields[labels.paymentImportedLookupField] = selectedDonation.Id;
                rowFields[labels.paymentImportedStatusField] = userSelectedMatch;
                rowFields[labels.opportunityImportedLookupField] = selectedDonation.npe01__Opportunity__c;
                rowFields[labels.opportunityImportedStatusField] = userSelectedMatch;
            }
        } else if (selectedDonation === '') {
            //create new opportunity if selectedDonation is set as empty string
            rowFields[labels.opportunityImportedStatusField] = userSelectedNewOpp;
        }

        return rowFields;
    },

    /**
     * @description: Gets open donations without any Payments and unpaid Payments
     * for use in the Donation Selector component.
     * @return: void
     */
    getOpportunitiesWithOppPayments: function(component, recordId) {
        const donorType = component.get('v.donorType');

        let action = component.get('c.getOpportunitiesWithOppPayments');
        action.setParams({donorId: recordId, donorType: donorType});
        action.setCallback(this, function (response) {
            const state = response.getState();
            if (state === 'SUCCESS') {
                const opportunitiesWithPayments = JSON.parse(response.getReturnValue());
                let openOpportunitiesWithoutAnyPayments = [];
                let unpaidPayments = [];
                opportunitiesWithPayments.forEach(function (opportunityWrapper) {
                        if (opportunityWrapper.hasPayments === false) {
                            openOpportunitiesWithoutAnyPayments.push(opportunityWrapper.opportunity);
                        }
                        opportunityWrapper.unpaidPayments.forEach(function (payment) {
                            unpaidPayments.push(payment);
                        });
                    }
                );
                component.set('v.unpaidPayments', unpaidPayments);
                component.set('v.openOpportunities', openOpportunitiesWithoutAnyPayments);
            } else {
                this.handleApexErrors(component, response.getError());
            }
            this.sendMessage(component, 'hideFormSpinner', '');
        });
        $A.enqueueAction(action);
    },

    /**
     * @description: sends error toast to parent component notifying user of missing fields
     * @param missingFields: Array of missing fields
     */
    sendErrorToast: function(component, missingFields) {
        let channel = 'onError';
        let error = $A.get('$Label.c.exceptionRequiredField') + ' ' + missingFields.join(', ') + '.';
        let message = {title: $A.get('$Label.c.PageMessagesError'), errorMessage: error};
        this.sendMessage(component, channel, message);
    },

    /**
     * @description: send a message to other components
     */
    sendMessage: function (component, channel, info) {
        let message = {
            info: info,
            batchId: component.get('v.recordId')
        };
        let sendMessage = $A.get('e.ltng:sendMessage');
        sendMessage.setParams({
            'channel': channel,
            'message': message
        });
        sendMessage.fire();
    },

    /**
     * @description: checks for Donor and other required fields and gathers error messages before submitting
     * @return: validity Object with Boolean for validity and an array of any missing fields to display
     */
    validateFields: function(component, rowFields) {
        let validity = {isValid: true, missingFields: []};

        const hasDonor = this.verifyRowHasDonor(component, rowFields);
        if (!hasDonor) {
            const labels = component.get("v.labels");
            const missingDonor = (component.get("v.donorType") === 'Contact1') ? labels.contactObject : labels.accountObject;
            validity.missingFields.push(missingDonor);
        }

        let missingRequiredFields = this.verifyRequiredFields(component, rowFields);
        if (missingRequiredFields.length != 0) {
            validity.missingFields = validity.missingFields.concat(missingRequiredFields);
        }

        if (validity.missingFields.length !== 0) {
            validity.isValid = false;
        }

        return validity;
    },

    /**
     * @description: checks for presence of fields that user has marked as required
     * @param rowFields: Object rowFields with updated hidden values
     * @return missingFields: list of any fields by label that are missing
     */
    verifyRequiredFields: function(component, rowFields) {
        let missingFields = [];
        const dataImportFields = component.get('v.dataImportFields');
        let dynamicInputFields = component.find('dynamicInputFields');

        if (! Array.isArray(dynamicInputFields)) {
            dynamicInputFields = [dynamicInputFields];
        }

        //dataImportFields and dynamicInputFields have the same order, so can loop both to check validity
        for (let i=0; i<dataImportFields.length; i++) {
            if (dataImportFields[i].required) {
                const fieldValue = dynamicInputFields[i].get('v.value');
                if (fieldValue === '' || fieldValue === null || (Array.isArray(fieldValue) && !fieldValue.length) ) {
                    missingFields.push(dataImportFields[i].label);
                }
            }
        }

        return missingFields;
    },

    /**
     * @description: checks for presence of a donor, which is always required
     * @param rowFields: Object rowFields with updated hidden values
     * @return hasDonor: Boolean to indicate if row has a donor
     */
    verifyRowHasDonor: function(component, rowFields) {
        let hasDonor = true;
        let lookupValue;

        if (component.get("v.donorType") === 'Contact1') {
            lookupValue = rowFields[component.get("v.labels.contactLookup")];
        } else if (component.get("v.donorType") === 'Account1') {
            lookupValue = rowFields[component.get("v.labels.accountLookup")];
        }

        if (!lookupValue || lookupValue.length !== 18) {
            hasDonor = false;
        }

        return hasDonor;
    }

})<|endoftext|>


<commit_before>import React, { Component } from 'react'

export default class App extends Component {
  render() {
    return(

Hello World 3

    )
  }
}
<commit_msg>Starting to extend<commit_after>import React, { Component } from 'react'

class Row extends Component {
  render() {
    return (

{this.props.label}

    );
  }
}

const ROWS = [
  {label: "Item 1"},
  {label: "Item 2"},
  {label: "Item 3"},
  {label: "Item 4"},
  {label: "Item 5"}
];

export default class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      rows: []
    };
  }
  update() {
    this.setState({rows: ROWS});
  }
  render() {
    const rows = [];
    this.state.rows.forEach(function(row) {
      rows.push(<Row label={row.label} />);
    });
    return(

Hello World 3

<button type="button" onClick={this.update.bind(this)}>Update</button>

{rows}

    );
  }
}
<|endoftext|>


<commit_before>/*global Mousetrap*/
import _ from 'lodash';
import React from 'react';
import fetch from 'isomorphic-fetch';
import GraphiQL from 'graphiql/dist';
import Modal from 'react-modal/lib/index';

Modal.setAppElement(document.getElementById('react-root'));

import HTTPHeaderEditor from './HTTPHeaderEditor';

export default class App extends React.Component {
  constructor() {
    super();

    const storage = window.localStorage;

    this.state = {
      headerEditOpen: false,
      currentTabKey: storage.getItem('currentTab') ? parseInt(storage.getItem('currentTab')) : 0,
      tabs: storage.getItem('tabs') ? JSON.parse(storage.getItem('tabs')) : [
        {
          name: null,
          headers: {},
          endpoint: '',
          method: 'post'
        }
      ]
    };
  }

  componentDidMount() {
    Mousetrap.bindGlobal('command+shift+]', (e) => {
      e.preventDefault();
      this.gotoNextTab();
      return false;
    });

    Mousetrap.bindGlobal('command+shift+[', (e) => {
      e.preventDefault();
      this.gotoPreviousTab();
      return false;
    });
  }

  handleElectronMenuOption(option) {
    switch (option) {
      case 'NEW_TAB':
        this.createNewTab();
        break;
      case 'CLOSE_TAB':
        this.closeCurrentTab();
        break;
      case 'NEXT_TAB':
        this.gotoNextTab();
        break;
      case 'PREVIOUS_TAB':
        this.gotoPreviousTab();
        break;
    }
  }

  createNewTab() {
    const currentTab = this.getCurrentTab();
    const newTabKey = this.state.tabs.length;

    this.setState({
      tabs: [...this.state.tabs, {
        headers: currentTab.headers,
        endpoint: currentTab.endpoint,
        method: currentTab.method
      }],
      currentTabKey: newTabKey
    }, () => {
      this.updateLocalStorage();
    });
  }

  closeCurrentTab() {
    const currentTabKey = this.state.currentTabKey;
    let gotoTabKey = currentTabKey - 1;
    if (currentTabKey === 0) {
      gotoTabKey = 0;
    }

    let newTabs = [
      ...this.state.tabs
    ];

    newTabs.splice(currentTabKey, 1);

    if (newTabs.length === 0) {
      newTabs = [
        {
          name: null,
          headers: {},
          endpoint: '',
          method: 'post'
        }
      ];
    }

    this.setState({
      tabs: newTabs,
      currentTabKey: gotoTabKey
    }, () => {
      this.updateLocalStorage();
    });
  }

  gotoNextTab() {
    let nextTab = this.state.currentTabKey + 1;
    if (nextTab >= this.state.tabs.length) {
      nextTab = 0;
    }
    this.setState({
      currentTabKey: nextTab
    }, () => {
      this.updateLocalStorage();
    });
  }

  gotoPreviousTab() {
    let prevTab = this.state.currentTabKey - 1;
    if (prevTab < 0) {
      prevTab = this.state.tabs.length - 1;
    }
    this.setState({
      currentTabKey: prevTab
    }, () => {
      this.updateLocalStorage();
    });
  }

  getCurrentTab() {
    return this.state.tabs[this.state.currentTabKey];
  }

  updateLocalStorage() {
    window.localStorage.setItem('tabs', JSON.stringify(this.state.tabs));
    window.localStorage.setItem('currentTab', this.state.currentTabKey);
  }

  graphQLFetcher = (graphQLParams) => {
    const defaultHeaders = {
      'Content-Type': 'application/json'
    };

    const { endpoint, method, headers } = this.getCurrentTab();

    if (method == "get") {
      return fetch(endpoint + "?query=" + encodeURIComponent(graphQLParams['query']) + "&variables=" + encodeURIComponent(graphQLParams['variables']), {
        method: method,
        headers: Object.assign({}, defaultHeaders, headers),
        body: null
      }).then(response => response.json());
    }
    return fetch(endpoint, {
      method: method,
      headers: Object.assign({}, defaultHeaders, headers),
      body: JSON.stringify(graphQLParams)
    }).then(response => response.json());
  }

  handleChange(field, eOrKey, e) {
    if (typeof eOrKey === 'number') {
      this.updateFieldForTab(eOrKey, field, e.target.value);
    } else {
      this.updateFieldForTab(this.state.currentTabKey, field, eOrKey.target.value);
    }
  }

  updateFieldForTab(tabKey, field, value) {
    const { tabs } = this.state;

    const newTabs = [...tabs];

    newTabs[tabKey] = {
      ...tabs[tabKey],
      [field]: value
    };

    this.setState({
      tabs: newTabs
    }, () => {
      this.updateLocalStorage();
    });
  }

  handleTabClick = (tabKey) => {
    if (tabKey !== this.state.editingTab) {
      this.setState({
        currentTabKey: tabKey,
        editingTab: null
      });
    }
  }

  handleTabDoubleClick = (tabKey) => {
    this.setState({
      editingTab: tabKey
    }, () => {
      React.findDOMNode(this.refs.editingTabNameInput).focus();
    });
  }

  handleEditTabKeyUp = (e) => {
    if(e.keyCode === 13) {
      this.setState({
        editingTab: null
      });
    }
  }

  openHeaderEdit = () => {
    this.setState({
      headerEditOpen: true
    });
  }

  closeModal = () => {
    this.setState({
      headerEditOpen: false
    });
  }

  getHeadersFromModal = (headers) => {
    this.updateFieldForTab(this.state.currentTabKey, 'headers', headers);
  }

  render() {
    const currentTab = this.getCurrentTab();

    const { currentTabKey } = this.state;
    const tabEl = (

            GraphQL Endpoint
            <input type="text" name="endpoint"
              value={currentTab.endpoint} onChange={this.handleChange.bind(this, 'endpoint')} />

            Method
            <select name="method" value={currentTab.method} onChange={this.handleChange.bind(this, 'method')}>
              <option value="get">GET</option>
              <option value="post">POST</option>
            </select>

            Edit HTTP Headers

          {
            // THIS IS THE GROSSEST THING I'VE EVER DONE AND I HATE IT. FIXME ASAP
          }
          <GraphiQL
            key={currentTabKey + currentTab.endpoint + JSON.stringify(currentTab.headers)}
            storage={getStorage(`graphiql:${currentTabKey}`)}
            fetcher={this.graphQLFetcher} />

    );

    return (

            {_.map(this.state.tabs, (tab, tabKey) => {
              return (

                    { this.state.editingTab === tabKey ?
                      <input ref="editingTabNameInput"
                        type="text"
                        value={tab.name}
                        onKeyUp={this.handleEditTabKeyUp}
                        onChange={this.handleChange.bind(this, 'name', tabKey)} />
                      : tab.name || `Untitled Query ${tabKey + 1}` }

              );
            })}

          {tabEl}

        <Modal isOpen={this.state.headerEditOpen} onRequestClose={this.closeModal}>
          <HTTPHeaderEditor
            headers={_.map(this.state.tabs[this.state.currentTabKey].headers, (value, key) => ({ key, value }))}
            onCreateHeaders={this.getHeadersFromModal}
            closeModal={this.closeModal} />
        </Modal>

    );
  }
}

const _storages = {};

function _makeStorage(storageKey) {
  return {
    setItem: (key, val) => window.localStorage.setItem(`${storageKey}${key}`, val),
    getItem: (key) => window.localStorage.getItem(`${storageKey}${key}`)
  };
}

function getStorage(storageKey) {
  if (!_storages[storageKey]) {
    _storages[storageKey] = _makeStorage(storageKey);
  }
  return _storages[storageKey];
}
<commit_msg>Fix issue for introspection query where variables was undefined. Replaced with empty object. Fix issue that prevented having an endpoint with query string parameters.<commit_after>/*global Mousetrap*/
import _ from 'lodash';
import React from 'react';
import fetch from 'isomorphic-fetch';
import GraphiQL from 'graphiql/dist';
import Modal from 'react-modal/lib/index';

Modal.setAppElement(document.getElementById('react-root'));

import HTTPHeaderEditor from './HTTPHeaderEditor';

export default class App extends React.Component {
  constructor() {
    super();

    const storage = window.localStorage;

    this.state = {
      headerEditOpen: false,
      currentTabKey: storage.getItem('currentTab') ? parseInt(storage.getItem('currentTab')) : 0,
      tabs: storage.getItem('tabs') ? JSON.parse(storage.getItem('tabs')) : [
        {
          name: null,
          headers: {},
          endpoint: '',
          method: 'post'
        }
      ]
    };
  }

  componentDidMount() {
    Mousetrap.bindGlobal('command+shift+]', (e) => {
      e.preventDefault();
      this.gotoNextTab();
      return false;
    });

    Mousetrap.bindGlobal('command+shift+[', (e) => {
      e.preventDefault();
      this.gotoPreviousTab();
      return false;
    });
  }

  handleElectronMenuOption(option) {
    switch (option) {
      case 'NEW_TAB':
        this.createNewTab();
        break;
      case 'CLOSE_TAB':
        this.closeCurrentTab();
        break;
      case 'NEXT_TAB':
        this.gotoNextTab();
        break;
      case 'PREVIOUS_TAB':
        this.gotoPreviousTab();
        break;
    }
  }

  createNewTab() {
    const currentTab = this.getCurrentTab();
    const newTabKey = this.state.tabs.length;

    this.setState({
      tabs: [...this.state.tabs, {
        headers: currentTab.headers,
        endpoint: currentTab.endpoint,
        method: currentTab.method
      }],
      currentTabKey: newTabKey
    }, () => {
      this.updateLocalStorage();
    });
  }

  closeCurrentTab() {
    const currentTabKey = this.state.currentTabKey;
    let gotoTabKey = currentTabKey - 1;
    if (currentTabKey === 0) {
      gotoTabKey = 0;
    }

    let newTabs = [
      ...this.state.tabs
    ];

    newTabs.splice(currentTabKey, 1);

    if (newTabs.length === 0) {
      newTabs = [
        {
          name: null,
          headers: {},
          endpoint: '',
          method: 'post'
        }
      ];
    }

    this.setState({
      tabs: newTabs,
      currentTabKey: gotoTabKey
    }, () => {
      this.updateLocalStorage();
    });
  }

  gotoNextTab() {
    let nextTab = this.state.currentTabKey + 1;
    if (nextTab >= this.state.tabs.length) {
      nextTab = 0;
    }
    this.setState({
      currentTabKey: nextTab
    }, () => {
      this.updateLocalStorage();
    });
  }

  gotoPreviousTab() {
    let prevTab = this.state.currentTabKey - 1;
    if (prevTab < 0) {
      prevTab = this.state.tabs.length - 1;
    }
    this.setState({
      currentTabKey: prevTab
    }, () => {
      this.updateLocalStorage();
    });
  }

  getCurrentTab() {
    return this.state.tabs[this.state.currentTabKey];
  }

  updateLocalStorage() {
    window.localStorage.setItem('tabs', JSON.stringify(this.state.tabs));
    window.localStorage.setItem('currentTab', this.state.currentTabKey);
  }

  graphQLFetcher = (graphQLParams) => {
    const defaultHeaders = {
      'Content-Type': 'application/json'
    };

    const { endpoint, method, headers } = this.getCurrentTab();

    if (method == "get") {
      var url = endpoint;
      if (typeof graphQLParams['variables'] === "undefined"){
        graphQLParams['variables'] = "{}";
      }

      url += url.indexOf('?') == -1 ? "?" : "&";

      return fetch(url + "query=" + encodeURIComponent(graphQLParams['query']) + "&variables=" + encodeURIComponent(graphQLParams['variables']), {
        method: method,
        headers: Object.assign({}, defaultHeaders, headers),
        body: null
      }).then(response => response.json());
    }
    return fetch(endpoint, {
      method: method,
      headers: Object.assign({}, defaultHeaders, headers),
      body: JSON.stringify(graphQLParams)
    }).then(response => response.json());
  }

  handleChange(field, eOrKey, e) {
    if (typeof eOrKey === 'number') {
      this.updateFieldForTab(eOrKey, field, e.target.value);
    } else {
      this.updateFieldForTab(this.state.currentTabKey, field, eOrKey.target.value);
    }
  }

  updateFieldForTab(tabKey, field, value) {
    const { tabs } = this.state;

    const newTabs = [...tabs];

    newTabs[tabKey] = {
      ...tabs[tabKey],
      [field]: value
    };

    this.setState({
      tabs: newTabs
    }, () => {
      this.updateLocalStorage();
    });
  }

  handleTabClick = (tabKey) => {
    if (tabKey !== this.state.editingTab) {
      this.setState({
        currentTabKey: tabKey,
        editingTab: null
      });
    }
  }

  handleTabDoubleClick = (tabKey) => {
    this.setState({
      editingTab: tabKey
    }, () => {
      React.findDOMNode(this.refs.editingTabNameInput).focus();
    });
  }

  handleEditTabKeyUp = (e) => {
    if(e.keyCode === 13) {
      this.setState({
        editingTab: null
      });
    }
  }

  openHeaderEdit = () => {
    this.setState({
      headerEditOpen: true
    });
  }

  closeModal = () => {
    this.setState({
      headerEditOpen: false
    });
  }

  getHeadersFromModal = (headers) => {
    this.updateFieldForTab(this.state.currentTabKey, 'headers', headers);
  }

  render() {
    const currentTab = this.getCurrentTab();

    const { currentTabKey } = this.state;
    const tabEl = (

            GraphQL Endpoint
            <input type="text" name="endpoint"
              value={currentTab.endpoint} onChange={this.handleChange.bind(this, 'endpoint')} />

            Method
            <select name="method" value={currentTab.method} onChange={this.handleChange.bind(this, 'method')}>
              <option value="get">GET</option>
              <option value="post">POST</option>
            </select>

            Edit HTTP Headers

          {
            // THIS IS THE GROSSEST THING I'VE EVER DONE AND I HATE IT. FIXME ASAP
          }
          <GraphiQL
            key={currentTabKey + currentTab.endpoint + JSON.stringify(currentTab.headers)}
            storage={getStorage(`graphiql:${currentTabKey}`)}
            fetcher={this.graphQLFetcher} />

    );

    return (

            {_.map(this.state.tabs, (tab, tabKey) => {
              return (

                    { this.state.editingTab === tabKey ?
                      <input ref="editingTabNameInput"
                        type="text"
                        value={tab.name}
                        onKeyUp={this.handleEditTabKeyUp}
                        onChange={this.handleChange.bind(this, 'name', tabKey)} />
                      : tab.name || `Untitled Query ${tabKey + 1}` }

              );
            })}

          {tabEl}

        <Modal isOpen={this.state.headerEditOpen} onRequestClose={this.closeModal}>
          <HTTPHeaderEditor
            headers={_.map(this.state.tabs[this.state.currentTabKey].headers, (value, key) => ({ key, value }))}
            onCreateHeaders={this.getHeadersFromModal}
            closeModal={this.closeModal} />
        </Modal>

    );
  }
}

const _storages = {};

function _makeStorage(storageKey) {
  return {
    setItem: (key, val) => window.localStorage.setItem(`${storageKey}${key}`, val),
    getItem: (key) => window.localStorage.getItem(`${storageKey}${key}`)
  };
}

function getStorage(storageKey) {
  if (!_storages[storageKey]) {
    _storages[storageKey] = _makeStorage(storageKey);
  }
  return _storages[storageKey];
}
<|endoftext|>


<commit_before>import React from 'react';
import { connect } from 'react-redux';

import { doAuthWithPassword, parseAuthToken, getProfile } from 'lib/actions/auth';
import Layout from 'components/layout';
import LogInBox from 'components/log-in-box';

const App = React.createClass( {
  propTypes: {
    auth: React.PropTypes.object.isRequired,
  },

  componentWillMount() {
    if ( ! this.props.auth.token ) {
      this.props.parseAuthToken();
    }
  },

  componentDidMount() {
    this.getUserInfo();
  },

  componentDidUpdate() {
    this.getUserInfo();
  },

  getUserInfo() {
    if ( this.props.auth.token && ! this.props.auth.user ) {
      this.props.getProfile();
    }
  },

  showAuthWithPassword() {
    this.props.doAuthWithPassword();
  },

  render() {
    if ( this.props.auth.token ) {
      return ( <Layout children={ this.props.children } /> );
    }
    return ( <LogInBox showAuth={ this.showAuthWithPassword } /> );
  }
} );

function mapStateToProps( state ) {
  const { auth } = state;
  return { auth };
}

export default connect( mapStateToProps, { doAuthWithPassword, getProfile, parseAuthToken } )( App );
<commit_msg>Remove createClass from App<commit_after>import React from 'react';
import { connect } from 'react-redux';

import { doAuthWithPassword, parseAuthToken, getProfile } from 'lib/actions/auth';
import Layout from 'components/layout';
import LogInBox from 'components/log-in-box';

class App extends React.Component {
  componentWillMount() {
    if ( ! this.props.auth.token ) {
      this.props.parseAuthToken();
    }
  }

  componentDidMount() {
    this.getUserInfo( this.props );
  }

  componentDidUpdate() {
    this.getUserInfo( this.props );
  }

  getUserInfo( props ) {
    if ( props.auth.token && ! props.auth.user ) {
      props.getProfile();
    }
  }

  render() {
    if ( this.props.auth.token ) {
      return ( <Layout children={ this.props.children } /> );
    }
    return ( <LogInBox showAuth={ this.props.doAuthWithPassword } /> );
  }
}

App.propTypes = {
  auth: React.PropTypes.object.isRequired,
};

function mapStateToProps( state ) {
  const { auth } = state;
  return { auth };
}

export default connect( mapStateToProps, { doAuthWithPassword, getProfile, parseAuthToken } )( App );
<|endoftext|>