<commit_before>mini.Module(
	"engine/rendering/animation"
)
.requires(
	"engine/rendering/animationsheet"
)
.defines(function(AnimationSheet) {
	var Animation = mini.Class.subclass({
		initialize: function(sheet, frameTime, order) {
			this.sheet = sheet;
			this.frameTime = frameTime;
			this.currentTime = 0;

			this.order = order;
			this.orderIndex = 0;
			this.tileNumber = this.order[this.orderIndex];
		},

		update: function() {
			this.currentTime += env.time.timePassed;

			while(this.currentTime >= this.frameTime) {
				this.currentTime -= this.frameTime;
				this.orderIndex = (this.orderIndex + 1) % this.order.length;
				this.tileNumber = this.order[this.orderIndex];
			};
		},

		draw: function(targetAABB) {
			this.sheet.draw(targetAABB, this.tileNumber);
		}
	});

	Animation.prototype.toJson = function() {
		var json = {
			sheet: this.sheet.toJson(),
			frameTime: this.frameTime,
			order: this.order
		};

		return json;
	};

	return Animation;
});
<commit_msg>added fromJson to Animation<commit_after>mini.Module(
	"engine/rendering/animation"
)
.requires(
	"engine/rendering/animationsheet"
)
.defines(function(AnimationSheet) {
	var Animation = mini.Class.subclass({
		initialize: function(sheet, frameTime, order) {
			this.sheet = sheet;
			this.frameTime = frameTime;
			this.currentTime = 0;

			this.order = order;
			this.orderIndex = 0;
			this.tileNumber = this.order[this.orderIndex];
		},

		update: function() {
			this.currentTime += env.time.timePassed;

			while(this.currentTime >= this.frameTime) {
				this.currentTime -= this.frameTime;
				this.orderIndex = (this.orderIndex + 1) % this.order.length;
				this.tileNumber = this.order[this.orderIndex];
			};
		},

		draw: function(targetAABB) {
			this.sheet.draw(targetAABB, this.tileNumber);
		}
	});

	Animation.prototype.toJson = function() {
		var json = {
			sheet: this.sheet.toJson(),
			frameTime: this.frameTime,
			order: this.order
		};

		return json;
	};

	Animation.fromJson = function(json) {
		var animation = new Animation(AnimationSheet.fromJson(json.sheet), json.frameTime, json.order);

		return animation;
	};

	return Animation;
});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
CLASS({
  package: 'foam.u2.md',
  name: 'Select',
  extends: 'foam.u2.View',
  traits: ['foam.u2.ChoiceViewTrait'],

  requires: [
    'foam.u2.md.PopupMenu',
  ],
  imports: [
    'document',
  ],

  listeners: [
    {
      name: 'onClick',
      isFramed: true,
      code: function() {
        if ( this.mode !== 'rw' ) return;
        // Blur any active input element.
        var active = this.document.activeElement;
        if (active) active.blur();

        var self = this;
        var popup = this.PopupMenu.create({
          data$: this.data$,
          choices: this.choices,
          autoSetData: this.autoSetData
        });
        popup.open(this.index, this.el());
      }
    },
  ],

  templates: [
    function CSS() {/*
      ^ {
        align-items: flex-end;
        border-bottom: 1px solid #e0e0e0;
        cursor: pointer;
        display: flex;
        flex-direction: row;
        justify-content: space-between;
        margin: 24px 16px 8px;
        padding: 8px 0;
        position: relative;
      }

      ^label {
        color: #999;
        flex-grow: 1;
        font-size: 85%;
        font-weight: 500;
        position: absolute;
        top: -8px;
        z-index: 0;
      }

      ^value {
        flex-grow: 1;
      }

      ^down-arrow {
        fill: #999;
        height: 12px;
        margin: 8px 8px 2px 16px;
        width: 12px;
      }
    */},
    function initE() {/*#U2

        {{this.label$}}

{{this.text$}}

        <svg class="^down-arrow" width="12px" height="12px" viewBox="0 0 48 48">
          <g><path d="M0 16 l24 24 24 -24 z"></path></g>
        </svg>

    */},
  ]
});
<commit_msg>U2 MD Select: Add showLabel and inline properties<commit_after>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
CLASS({
  package: 'foam.u2.md',
  name: 'Select',
  extends: 'foam.u2.View',
  traits: ['foam.u2.ChoiceViewTrait'],

  requires: [
    'foam.u2.md.PopupMenu',
  ],
  imports: [
    'document',
  ],

  properties: [
    {
      type: 'Boolean',
      name: 'inline',
      defaultValue: false,
      attribute: true,
    },
    {
      type: 'Boolean',
      name: 'showLabel',
      attribute: true,
      defaultValueFn: function() {
        return !this.inline;
      }
    },
  ],

  listeners: [
    {
      name: 'onClick',
      isFramed: true,
      code: function() {
        if ( this.mode !== 'rw' ) return;
        // Blur any active input element.
        var active = this.document.activeElement;
        if (active) active.blur();

        var self = this;
        var popup = this.PopupMenu.create({
          data$: this.data$,
          choices: this.choices,
          autoSetData: this.autoSetData
        });
        popup.open(this.index, this.el());
      }
    },
  ],

  templates: [
    function CSS() {/*
      ^ {
        align-items: flex-end;
        border-bottom: 1px solid #e0e0e0;
        cursor: pointer;
        display: flex;
        flex-direction: row;
        justify-content: space-between;
        margin: 8px 16px;
        padding: 8px 0;
        position: relative;
      }
      ^.show-label {
        margin-top: 24px;
      }

      ^label {
        color: #999;
        display: none;
        flex-grow: 1;
        font-size: 85%;
        font-weight: 500;
        position: absolute;
        top: -8px;
        z-index: 0;
      }
      ^.show-label ^label {
        display: block;
      }

      ^value {
        flex-grow: 1;
      }

      ^down-arrow {
        fill: #999;
        height: 12px;
        margin: 8px 8px 2px 16px;
        width: 12px;
      }
    */},
    function initE() {/*#U2

        {{this.label$}}

{{this.text$}}

        <svg class="^down-arrow" width="12px" height="12px" viewBox="0 0 48 48">
          <g><path d="M0 16 l24 24 24 -24 z"></path></g>
        </svg>

    */},
  ]
});
<|endoftext|>


<commit_before>/*
 * This program is part of the OpenLMIS logistics management information system platform software.
 * Copyright © 2017 VillageReach
 *
 * This program is free software: you can redistribute it and/or modify it under the terms
 * of the GNU Affero General Public License as published by the Free Software Foundation, either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details. You should have received a copy of
 * the GNU Affero General Public License along with this program. If not, see
 * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.
 */

(function() {

	'use strict';

	/**
     * @ngdoc service
     * @name requisition-initiate.periodService
     *
     * @description
     * Responsible for retrieving periods from server.
     */
	angular
		.module('requisition-initiate')
	    .service('periodService', service);

    service.$inject = ['$resource', 'requisitionUrlFactory', 'dateUtils'];

    function service($resource, requisitionUrlFactory, dateUtils) {

        var resource = $resource(requisitionUrlFactory('/api/requisitions/periodsForInitiate'), {}, {
            periodsForInitiate: {
                method: 'GET',
                isArray: true,
                transformResponse: transformResponse
            }
        });

        this.getPeriodsForInitiate = getPeriodsForInitiate;

		/**
         * @ngdoc method
         * @methodOf requisition-initiate.periodService
         * @name getPeriodsForInitiate
         *
         * @description
         * Retrieves periods for initiate from server.
         *
         * @param  {String}  programId  program UUID
         * @param  {String}  facilityId facility UUID
         * @param  {Boolean} emergency  if searching for emergency periods
         * @return {Promise}            promise with periods array
         */
        function getPeriodsForInitiate(programId, facilityId, emergency) {
            return resource.periodsForInitiate({
                programId: programId,
                facilityId: facilityId,
                emergency: emergency
            }).$promise;
        }

        function transformResponse(data, headers, status) {
            if (status === 200) {
                var periods = angular.fromJson(data);
                periods.forEach(function(period) {
                    period.startDate = dateUtils.toDate(period.startDate);
                    period.endDate = dateUtils.toDate(period.endDate);
                })
                return periods;
            }
            return data;
        }
    }
})();
<commit_msg>OLMIS-2695: refactored period service to be a decorator<commit_after>/*
 * This program is part of the OpenLMIS logistics management information system platform software.
 * Copyright © 2017 VillageReach
 *
 * This program is free software: you can redistribute it and/or modify it under the terms
 * of the GNU Affero General Public License as published by the Free Software Foundation, either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details. You should have received a copy of
 * the GNU Affero General Public License along with this program. If not, see
 * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.
 */

(function() {

	'use strict';

	/**
     * @ngdoc service
     * @name requisition-initiate.periodService
     *
     * @description
     * Responsible for retrieving periods from server.
     */
	angular
		.module('requisition-initiate')
        .config(function($provide) {
            $provide.decorator('periodService', decorator);
        });

    decorator.$inject = ['$delegate', '$resource', 'requisitionUrlFactory', 'dateUtils'];

    function decorator($delegate, $resource, requisitionUrlFactory, dateUtils) {
        var resource = $resource(requisitionUrlFactory('/api/requisitions/periodsForInitiate'), {}, {
                periodsForInitiate: {
                    method: 'GET',
                    isArray: true,
                    transformResponse: transformResponse
                }
            }),
            periodService = $delegate;

        periodService.getPeriodsForInitiate = getPeriodsForInitiate;

        return periodService;

		/**
         * @ngdoc method
         * @methodOf requisition-initiate.periodService
         * @name getPeriodsForInitiate
         *
         * @description
         * Retrieves periods for initiate from server.
         *
         * @param  {String}  programId  program UUID
         * @param  {String}  facilityId facility UUID
         * @param  {Boolean} emergency  if searching for emergency periods
         * @return {Promise}            promise with periods array
         */
        function getPeriodsForInitiate(programId, facilityId, emergency) {
            return resource.periodsForInitiate({
                programId: programId,
                facilityId: facilityId,
                emergency: emergency
            }).$promise;
        }

        function transformResponse(data, headers, status) {
            if (status === 200) {
                var periods = angular.fromJson(data);
                periods.forEach(function(period) {
                    period.startDate = dateUtils.toDate(period.startDate);
                    period.endDate = dateUtils.toDate(period.endDate);
                })
                return periods;
            }
            return data;
        }
    }
})();
<|endoftext|>


<commit_before>var sys = require('sys'),
  http = require('http'),
  url = require('url'),
  chainGang = require('chain-gang'),
  request = require('request');

var currentTime = function() { return new Date(); };

var crawlerChain = chainGang.create({workers: 1});
crawlerChain.on('add', function(url) {
  console.log('[CRAWLER]', currentTime(), 'added:', url)
});
crawlerChain.on('finished', function(url, error) {
  if (error) {
    console.error('[CRAWLER]', currentTime(), url, error)
  } else {
    console.log('[CRAWLER]', currentTime(), url ,'finished.')
  }
});

var parse = function(body) {
  console.log("Parser called! ", body)
};

var crawl = function(worker, stationId) {
  var error;
  try {
    var requestUrl = 'http://www.vbb-fahrinfo.de/hafas/stboard.exe/dox?ld=&input='
      + stationId + '&boardType=dep&productsFilter=1100000&start=yes&maxJourneys=15';
    request({url: requestUrl}, function (error, response, body) {
        if (!error && response.statusCode == 200)
          parse(body);
      }
    );
  } catch(e) { error = e; }
  worker.finish(error);
};

http.createServer(function(request, response) {
  process.addListener('uncaughtException', function(error) {
    sys.puts('Caught exception: ' + error);
    response.writeHead(500, 'text/plain');
    response.end('error!');
  });

  crawlerChain.add(
    function(worker) { crawl(worker, 9120004); },
    request.url
  );
  response.writeHead(200, {'Content-Type': 'text/plain'});
  response.end('Added to queue!');
}).listen(8221, "127.0.0.1");
sys.puts('Crawler running at http://127.0.0.1:8221/');<commit_msg>implemented crawling<commit_after>var sys = require('sys'),
  http = require('http'),
  url = require('url'),
  chainGang = require('chain-gang'),
  request = require('request'),
  $ = require('jquery');

var currentTime = function() { return new Date(); };

var crawlerChain = chainGang.create({workers: 1});
crawlerChain.on('add', function(url) {
  console.log('[CRAWLER]', currentTime(), 'added:', url)
});
crawlerChain.on('finished', function(url, error) {
  if (error) {
    console.error('[CRAWLER]', currentTime(), url, error)
  } else {
    console.log('[CRAWLER]', currentTime(), url ,'finished.')
  }
});

var parse = function(body, cb) {
  //console.log("Parser called! ", body);
  var journeys = [];
  $(body).find('.sq').each(function(index, journey) {
    var data = $(journey).text().split('\n');
    var route = data[2],
        dest = data[5],
        depTime = data[7];
        journeys.push({
          'route': route,
          'dest': dest,
          'depTime': depTime
        });
  });
  if (cb) cb(journeys);
};

var crawl = function(worker, stationId, cb) {
  var error;
  try {
    var requestUrl = 'http://www.vbb-fahrinfo.de/hafas/stboard.exe/dox?ld=&input='
      + stationId + '&boardType=dep&productsFilter=1100000&start=yes&maxJourneys=15';
    request({url: requestUrl}, function (error, response, body) {
        if (!error && response.statusCode == 200)
          parse(body, cb);
      }
    );
  } catch(e) { error = e; }
  worker.finish(error);
};

http.createServer(function(request, response) {
  process.addListener('uncaughtException', function(error) {
    sys.puts('Caught exception: ' + error);
    response.writeHead(500, 'text/plain');
    response.end('error!');
  });

  var workerFunction = function(worker) {
    var stationId = 9120004;
    crawl(worker, stationId, function(journeys) {
      var jsonResp = {
        'station': stationId,
        'journeys': journeys
      };
      response.end(JSON.stringify(jsonResp));
    });
  };

  crawlerChain.add(workerFunction, request.url);
  response.writeHead(200, {'Content-Type': 'application/json'});
}).listen(8221, "127.0.0.1");
sys.puts('Crawler running at http://127.0.0.1:8221/');<|endoftext|>


<commit_before>/*!
  Copyright (C) 2016 Google Inc.
  Licensed under http://www.apache.org/licenses/LICENSE-2.0 <see LICENSE file>
*/

'use strict';

describe('GGRC utils allowed_to_map() method', function () {
  var allowedToMap;
  var fakeOptions;
  var fakeProgram;
  var fakeRequest;
  var fakeAudit;

  beforeAll(function () {
    allowedToMap = GGRC.Utils.allowed_to_map;
  });

  beforeEach(function () {
    fakeOptions = {};
  });

  describe('given an Audit and Program pair', function () {
    beforeEach(function () {
      fakeProgram = new CMS.Models.Program({type: 'Program'});
      fakeAudit = new CMS.Models.Audit({type: 'Audit'});

      spyOn(GGRC.Mappings, 'get_canonical_mapping_name')
        .and.returnValue('audits');

      spyOn(Permission, 'is_allowed_for').and.returnValue(true);
    });

    it('returns false for Audit as source and Program as target', function () {
      var result = allowedToMap(fakeAudit, fakeProgram, fakeOptions);
      expect(result).toBe(false);
    });

    it('returns false for Program as source and Audit as target', function () {
      var result = allowedToMap(fakeProgram, fakeAudit, fakeOptions);
      expect(result).toBe(false);
    });
  });

  describe('given an Audit and Request pair', function () {
    beforeEach(function () {
      fakeRequest = new CMS.Models.Request({type: 'Request'});
      fakeAudit = new CMS.Models.Audit({type: 'Audit'});

      spyOn(GGRC.Mappings, 'get_canonical_mapping_name')
        .and.returnValue('audits');

      spyOn(Permission, 'is_allowed_for').and.returnValue(true);
    });

    it('returns false for Audit as source and Request as target', function () {
      var result = allowedToMap(fakeAudit, fakeRequest, fakeOptions);
      expect(result).toBe(false);
    });

    it('returns false for Request as source and Audit as target', function () {
      var result = allowedToMap(fakeRequest, fakeAudit, fakeOptions);
      expect(result).toBe(false);
    });
  });

  describe('given a Person instance', function () {
    var origShortName;
    var otherInstance;
    var person;

    beforeAll(function () {
      origShortName = can.Model.shortName;
      can.Model.shortName = 'cacheable';
    });

    afterAll(function () {
      can.Model.shortName = origShortName;
    });

    beforeEach(function () {
      person = new CMS.Models.Person({type: 'Person'});
      otherInstance = new can.Model({type: 'Foo'});
    });

    it('returns false for any object', function () {
      var result = allowedToMap(otherInstance, person);
      expect(result).toBe(false);
    });
  });
});

describe('GGRC utils isEmptyCA() method', function () {
  var isEmptyCA;

  beforeAll(function () {
    isEmptyCA = GGRC.Utils.isEmptyCA;
  });

  describe('check Rich Text value', function () {
    it('returns true for empty div', function () {
      var result = isEmptyCA('

', 'Rich Text');
      expect(result).toBe(true);
    });

    it('returns true for div with a line break', function () {
      var result = isEmptyCA('

', 'Rich Text');
      expect(result).toBe(true);
    });

    it('returns true for div with a empty list', function () {
      var result = isEmptyCA('

', 'Rich Text');
      expect(result).toBe(true);
    });

    it('returns true for div with a empty paragraph', function () {
      var result = isEmptyCA('

', 'Rich Text');
      expect(result).toBe(true);
    });

    it('returns false for div with the text', function () {
      var result = isEmptyCA('
Very important text!
', 'Rich Text');
      expect(result).toBe(false);
    });

    it('returns false for not empty list', function () {
      var result = isEmptyCA('

One

Two

',
        'Rich Text');
      expect(result).toBe(false);
    });
  });

  describe('check Checkbox value', function () {
    it('returns false for unchecked', function () {
      var result = isEmptyCA('0', 'Checkbox');
      expect(result).toBe(true);
    });

    it('returns true for checked', function () {
      var result = isEmptyCA('1', 'Checkbox');
      expect(result).toBe(false);
    });
  });

  describe('check Text value', function () {
    it('returns false for not empty', function () {
      var result = isEmptyCA('some text', 'Text');
      expect(result).toBe(false);
    });

    it('returns true for empty', function () {
      var result = isEmptyCA('', 'Text');
      expect(result).toBe(true);
    });
  });

  describe('check Map:Person type', function () {
    it('returns false for selected', function () {
      var result = isEmptyCA('Person', 'Map:Person');
      expect(result).toBe(false);
    });

    it('returns true for not selected', function () {
      var result = isEmptyCA('', 'Map:Person');
      expect(result).toBe(true);
    });
  });

  describe('check Date type', function () {
    it('returns false for selected', function () {
      var result = isEmptyCA('01/01/2016', 'Date');
      expect(result).toBe(false);
    });

    it('returns true for not selected', function () {
      var result = isEmptyCA('', 'Date');
      expect(result).toBe(true);
    });
  });

  describe('check Dropdown type', function () {
    it('returns false for selected', function () {
      var result = isEmptyCA('value', 'Dropdown');
      expect(result).toBe(false);
    });

    it('returns true for not selected', function () {
      var result = isEmptyCA('', 'Dropdown');
      expect(result).toBe(true);
    });
  });

  describe('check invalid type', function () {
    it('returns false for invalid type', function () {
      var result = isEmptyCA('some value', 'Invalid');
      expect(result).toBe(false);
    });
  });
});

describe('GGRC utils getRelatedObjects() method', function () {
  it('returns related object only for second tier', function () {
    var result = GGRC.Utils.getRelatedObjects(1);
    expect(result).toEqual(jasmine.any(Object));
    expect(result.mapping).toEqual('related_objects');
    expect(result.draw_children).toBeFalsy();
    expect(result.child_options).toEqual([{}]);
  });

  it('returns related objects for second and third tier', function () {
    var result = GGRC.Utils.getRelatedObjects(2);
    expect(result).toEqual(jasmine.any(Object));
    expect(result.mapping).toEqual('related_objects');
    expect(result.draw_children).toBeTruthy();
    expect(result.child_options).toEqual(jasmine.any(Object));
    expect(result.child_options[0].draw_children).toBeFalsy();
    expect(result.child_options[0].mapping).toEqual('related_objects');
  });
});

describe('GGRC utils getMappableTypes() method', function () {
  var mapper;

  beforeAll(function () {
    var canonicalMappings = {};
    var OBJECT_TYPES = [
      'DataAsset', 'Facility', 'Market', 'OrgGroup', 'Vendor', 'Process',
      'Product', 'Project', 'System', 'Regulation', 'Policy', 'Contract',
      'Standard', 'Program', 'Issue', 'Control', 'Section', 'Clause',
      'Objective', 'Audit', 'Assessment', 'AccessGroup', 'Request',
      'Document', 'Risk', 'Threat'
    ];
    mapper = GGRC.Utils.getMappableTypes;
    OBJECT_TYPES.forEach(function (item) {
      canonicalMappings[item] = {};
    });
    spyOn(GGRC.Mappings, 'get_canonical_mappings_for')
      .and.returnValue(canonicalMappings);
  });

  it('excludes the References type from the result', function () {
    var result = mapper('Reference');
    expect(_.contains(result, 'Reference')).toBe(false);
  });
  it('returns no results for Person', function () {
    var result = mapper('Person');
    expect(result.length).toBe(0);
  });
  it('returns no results for AssessmentTemplate', function () {
    var result = mapper('AssessmentTemplate');
    expect(result.length).toBe(0);
  });
  it('always returns whitelisted items', function () {
    var whitelisted = ['Hello', 'World'];
    var result = mapper('AssessmentTemplate', {
      whitelist: whitelisted
    });
    expect(_.intersection(result, whitelisted)).toEqual(whitelisted);
  });
  it('always remove forbidden items', function () {
    var forbidden = ['Policy', 'Process', 'Product', 'Program'];
    var list = mapper('DataAsset');
    var result = mapper('DataAsset', {
      forbidden: forbidden
    });
    expect(_.difference(list, result).sort()).toEqual(forbidden.sort());
  });
  it('always leave whitelisted and remove forbidden items', function () {
    var forbidden = ['Policy', 'Process', 'Product', 'Program'];
    var whitelisted = ['Hello', 'World'];
    var list = mapper('DataAsset');
    var result = mapper('DataAsset', {
      forbidden: forbidden,
      whitelist: whitelisted
    });
    var input = _.difference(list, result).concat(_.difference(result, list));
    var output = forbidden.concat(whitelisted);

    expect(input.sort()).toEqual(output.sort());
  });
});

describe('GGRC utils isMappableType() method', function () {
  it('returns false for Person and any type', function () {
    var result = GGRC.Utils.isMappableType('Person', 'Program');
    expect(result).toBe(false);
  });
  it('returns false for AssessmentTemplate and  any type', function () {
    var result = GGRC.Utils.isMappableType('AssessmentTemplate', 'Program');
    expect(result).toBe(false);
  });
  it('returns true for Program and Control', function () {
    var result = GGRC.Utils.isMappableType('Program', 'Control');
    expect(result).toBe(true);
  });
});
<commit_msg>Update default test config<commit_after>/*!
  Copyright (C) 2016 Google Inc.
  Licensed under http://www.apache.org/licenses/LICENSE-2.0 <see LICENSE file>
*/

'use strict';

describe('GGRC utils allowed_to_map() method', function () {
  var allowedToMap;
  var fakeOptions;
  var fakeProgram;
  var fakeRequest;
  var fakeAudit;

  GGRC.config.snapshotable_parents = ['Audit'];
  beforeAll(function () {
    allowedToMap = GGRC.Utils.allowed_to_map;
  });

  beforeEach(function () {
    fakeOptions = {};
  });

  describe('given an Audit and Program pair', function () {
    beforeEach(function () {
      fakeProgram = new CMS.Models.Program({type: 'Program'});
      fakeAudit = new CMS.Models.Audit({type: 'Audit'});

      spyOn(GGRC.Mappings, 'get_canonical_mapping_name')
        .and.returnValue('audits');

      spyOn(Permission, 'is_allowed_for').and.returnValue(true);
    });

    it('returns false for Audit as source and Program as target', function () {
      var result = allowedToMap(fakeAudit, fakeProgram, fakeOptions);
      expect(result).toBe(false);
    });

    it('returns false for Program as source and Audit as target', function () {
      var result = allowedToMap(fakeProgram, fakeAudit, fakeOptions);
      expect(result).toBe(false);
    });
  });

  describe('given an Audit and Request pair', function () {
    beforeEach(function () {
      fakeRequest = new CMS.Models.Request({type: 'Request'});
      fakeAudit = new CMS.Models.Audit({type: 'Audit'});

      spyOn(GGRC.Mappings, 'get_canonical_mapping_name')
        .and.returnValue('audits');

      spyOn(Permission, 'is_allowed_for').and.returnValue(true);
    });

    it('returns false for Audit as source and Request as target', function () {
      var result = allowedToMap(fakeAudit, fakeRequest, fakeOptions);
      expect(result).toBe(false);
    });

    it('returns false for Request as source and Audit as target', function () {
      var result = allowedToMap(fakeRequest, fakeAudit, fakeOptions);
      expect(result).toBe(false);
    });
  });

  describe('given a Person instance', function () {
    var origShortName;
    var otherInstance;
    var person;

    beforeAll(function () {
      origShortName = can.Model.shortName;
      can.Model.shortName = 'cacheable';
    });

    afterAll(function () {
      can.Model.shortName = origShortName;
    });

    beforeEach(function () {
      person = new CMS.Models.Person({type: 'Person'});
      otherInstance = new can.Model({type: 'Foo'});
    });

    it('returns false for any object', function () {
      var result = allowedToMap(otherInstance, person);
      expect(result).toBe(false);
    });
  });
});

describe('GGRC utils isEmptyCA() method', function () {
  var isEmptyCA;

  beforeAll(function () {
    isEmptyCA = GGRC.Utils.isEmptyCA;
  });

  describe('check Rich Text value', function () {
    it('returns true for empty div', function () {
      var result = isEmptyCA('

', 'Rich Text');
      expect(result).toBe(true);
    });

    it('returns true for div with a line break', function () {
      var result = isEmptyCA('

', 'Rich Text');
      expect(result).toBe(true);
    });

    it('returns true for div with a empty list', function () {
      var result = isEmptyCA('

', 'Rich Text');
      expect(result).toBe(true);
    });

    it('returns true for div with a empty paragraph', function () {
      var result = isEmptyCA('

', 'Rich Text');
      expect(result).toBe(true);
    });

    it('returns false for div with the text', function () {
      var result = isEmptyCA('
Very important text!
', 'Rich Text');
      expect(result).toBe(false);
    });

    it('returns false for not empty list', function () {
      var result = isEmptyCA('

One

Two

',
        'Rich Text');
      expect(result).toBe(false);
    });
  });

  describe('check Checkbox value', function () {
    it('returns false for unchecked', function () {
      var result = isEmptyCA('0', 'Checkbox');
      expect(result).toBe(true);
    });

    it('returns true for checked', function () {
      var result = isEmptyCA('1', 'Checkbox');
      expect(result).toBe(false);
    });
  });

  describe('check Text value', function () {
    it('returns false for not empty', function () {
      var result = isEmptyCA('some text', 'Text');
      expect(result).toBe(false);
    });

    it('returns true for empty', function () {
      var result = isEmptyCA('', 'Text');
      expect(result).toBe(true);
    });
  });

  describe('check Map:Person type', function () {
    it('returns false for selected', function () {
      var result = isEmptyCA('Person', 'Map:Person');
      expect(result).toBe(false);
    });

    it('returns true for not selected', function () {
      var result = isEmptyCA('', 'Map:Person');
      expect(result).toBe(true);
    });
  });

  describe('check Date type', function () {
    it('returns false for selected', function () {
      var result = isEmptyCA('01/01/2016', 'Date');
      expect(result).toBe(false);
    });

    it('returns true for not selected', function () {
      var result = isEmptyCA('', 'Date');
      expect(result).toBe(true);
    });
  });

  describe('check Dropdown type', function () {
    it('returns false for selected', function () {
      var result = isEmptyCA('value', 'Dropdown');
      expect(result).toBe(false);
    });

    it('returns true for not selected', function () {
      var result = isEmptyCA('', 'Dropdown');
      expect(result).toBe(true);
    });
  });

  describe('check invalid type', function () {
    it('returns false for invalid type', function () {
      var result = isEmptyCA('some value', 'Invalid');
      expect(result).toBe(false);
    });
  });
});

describe('GGRC utils getRelatedObjects() method', function () {
  it('returns related object only for second tier', function () {
    var result = GGRC.Utils.getRelatedObjects(1);
    expect(result).toEqual(jasmine.any(Object));
    expect(result.mapping).toEqual('related_objects');
    expect(result.draw_children).toBeFalsy();
    expect(result.child_options).toEqual([{}]);
  });

  it('returns related objects for second and third tier', function () {
    var result = GGRC.Utils.getRelatedObjects(2);
    expect(result).toEqual(jasmine.any(Object));
    expect(result.mapping).toEqual('related_objects');
    expect(result.draw_children).toBeTruthy();
    expect(result.child_options).toEqual(jasmine.any(Object));
    expect(result.child_options[0].draw_children).toBeFalsy();
    expect(result.child_options[0].mapping).toEqual('related_objects');
  });
});

describe('GGRC utils getMappableTypes() method', function () {
  var mapper;

  beforeAll(function () {
    var canonicalMappings = {};
    var OBJECT_TYPES = [
      'DataAsset', 'Facility', 'Market', 'OrgGroup', 'Vendor', 'Process',
      'Product', 'Project', 'System', 'Regulation', 'Policy', 'Contract',
      'Standard', 'Program', 'Issue', 'Control', 'Section', 'Clause',
      'Objective', 'Audit', 'Assessment', 'AccessGroup', 'Request',
      'Document', 'Risk', 'Threat'
    ];
    mapper = GGRC.Utils.getMappableTypes;
    OBJECT_TYPES.forEach(function (item) {
      canonicalMappings[item] = {};
    });
    spyOn(GGRC.Mappings, 'get_canonical_mappings_for')
      .and.returnValue(canonicalMappings);
  });

  it('excludes the References type from the result', function () {
    var result = mapper('Reference');
    expect(_.contains(result, 'Reference')).toBe(false);
  });
  it('returns no results for Person', function () {
    var result = mapper('Person');
    expect(result.length).toBe(0);
  });
  it('returns no results for AssessmentTemplate', function () {
    var result = mapper('AssessmentTemplate');
    expect(result.length).toBe(0);
  });
  it('always returns whitelisted items', function () {
    var whitelisted = ['Hello', 'World'];
    var result = mapper('AssessmentTemplate', {
      whitelist: whitelisted
    });
    expect(_.intersection(result, whitelisted)).toEqual(whitelisted);
  });
  it('always remove forbidden items', function () {
    var forbidden = ['Policy', 'Process', 'Product', 'Program'];
    var list = mapper('DataAsset');
    var result = mapper('DataAsset', {
      forbidden: forbidden
    });
    expect(_.difference(list, result).sort()).toEqual(forbidden.sort());
  });
  it('always leave whitelisted and remove forbidden items', function () {
    var forbidden = ['Policy', 'Process', 'Product', 'Program'];
    var whitelisted = ['Hello', 'World'];
    var list = mapper('DataAsset');
    var result = mapper('DataAsset', {
      forbidden: forbidden,
      whitelist: whitelisted
    });
    var input = _.difference(list, result).concat(_.difference(result, list));
    var output = forbidden.concat(whitelisted);

    expect(input.sort()).toEqual(output.sort());
  });
});

describe('GGRC utils isMappableType() method', function () {
  it('returns false for Person and any type', function () {
    var result = GGRC.Utils.isMappableType('Person', 'Program');
    expect(result).toBe(false);
  });
  it('returns false for AssessmentTemplate and  any type', function () {
    var result = GGRC.Utils.isMappableType('AssessmentTemplate', 'Program');
    expect(result).toBe(false);
  });
  it('returns true for Program and Control', function () {
    var result = GGRC.Utils.isMappableType('Program', 'Control');
    expect(result).toBe(true);
  });
});
<|endoftext|>


<commit_before>$(function() {
    var osm = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
        attribution: '© OpenStreetMap contributors'
    });

    var wsUri = 'ws://busdrone.com:28737/';

    var templates = {
        table: '

{body}

',
        row:'
{key}	{value}
'
    };

    var map = L.map('map', {
        center: [47.6210, -122.3328],
        zoom: 13,
        layers: [osm]
    });

    L.control.locate().addTo(map);

    function debug(val) {
      console.log(val);
    }

    function wsConnect() {
      debug('Connecting to ' + wsUri);
      websocket = new WebSocket(wsUri);
      websocket.onopen = function(evt) { onWsOpen(evt) };
      websocket.onmessage = function(evt) { onWsMessage(evt) };
      websocket.onerror = function(evt) { onWsError(evt) };
      websocket.onclose = function(evt) { onWsClose(evt) };
    }

    function onWsOpen(evt) {
      debug('Connected');
    }

    function onWsClose(evt) {
      debug('Disconnected');
      window.setTimeout(wsConnect, Math.random() * 1000 * 25 + 5);
    }

    function onWsMessage(evt) {
      var data = JSON.parse(evt.data);
      if (data.type == 'update_vehicle') {
      } else if (data.type == 'init') {
        data.vehicles.forEach(function(vehicle) {
            var body = '';
            jQuery.each(vehicle, function(key, value){
                body += L.Util.template(templates.row, {key: key, value: value});
            });
            var popupContent = L.Util.template(templates.table, {body: body});
            L.marker([vehicle.lat, vehicle.lon])
                //.bindPopup(popupContent)
                .on('click', function(e) {
                    websocket.send(JSON.stringify({type: "trip_polyline", trip_uid: vehicle.dataProvider + "/" + vehicle.tripId}))
            }).addTo(map);
        });
      } else if (data.type == 'remove_vehicle') {
        debug('remove');
      } else if (data.type == 'trip_polyline') {
        debug(data);
        L.Polyline.fromEncoded(data.polyline).addTo(map);
      } else {
        debug(data);
      }
    }

    wsConnect();

    var hash = new L.Hash(map);
});

<commit_msg>Use style from mapbox<commit_after>$(function() {
    var wsUri = 'ws://busdrone.com:28737/';

    var templates = {
        table: '

{body}

',
        row:'
{key}	{value}
'
    };

    var map = L.map('map', {
        center: [47.6210, -122.3328],
        zoom: 13
    });

    var mapboxTiles = L.tileLayer('https://{s}.tiles.mapbox.com/v3/domoritz.h6ibh733/{z}/{x}/{y}.png', {
        attribution: 'Terms & Feedback'
    }).addTo(map);

    L.control.locate().addTo(map);

    function debug(val) {
      console.log(val);
    }

    function wsConnect() {
      debug('Connecting to ' + wsUri);
      websocket = new WebSocket(wsUri);
      websocket.onopen = function(evt) { onWsOpen(evt) };
      websocket.onmessage = function(evt) { onWsMessage(evt) };
      websocket.onerror = function(evt) { onWsError(evt) };
      websocket.onclose = function(evt) { onWsClose(evt) };
    }

    function onWsOpen(evt) {
      debug('Connected');
    }

    function onWsClose(evt) {
      debug('Disconnected');
      window.setTimeout(wsConnect, Math.random() * 1000 * 25 + 5);
    }

    function onWsMessage(evt) {
      var data = JSON.parse(evt.data);
      if (data.type == 'update_vehicle') {
      } else if (data.type == 'init') {
        data.vehicles.forEach(function(vehicle) {
            var body = '';
            jQuery.each(vehicle, function(key, value){
                body += L.Util.template(templates.row, {key: key, value: value});
            });
            var popupContent = L.Util.template(templates.table, {body: body});
            L.marker([vehicle.lat, vehicle.lon])
                //.bindPopup(popupContent)
                .on('click', function(e) {
                    websocket.send(JSON.stringify({type: "trip_polyline", trip_uid: vehicle.dataProvider + "/" + vehicle.tripId}))
            }).addTo(map);
        });
      } else if (data.type == 'remove_vehicle') {
        debug('remove');
      } else if (data.type == 'trip_polyline') {
        debug(data);
        L.Polyline.fromEncoded(data.polyline).addTo(map);
      } else {
        debug(data);
      }
    }

    wsConnect();

    var hash = new L.Hash(map);
});

<|endoftext|>


<commit_before>define(['jquery',
    'underscore',
    'arches',
    'knockout',
    'knockout-mapping',
    'viewmodels/function',
    'bindings/chosen'],
function($, _, arches, ko, koMapping, FunctionViewModel, chosen) {
    return ko.components.register('views/components/functions/primary-descriptors', {
        viewModel: function(params) {
            FunctionViewModel.apply(this, arguments);
            var nodegroups = {};
            this.cards = ko.observableArray();
            this.loading = ko.observable(false);
            this.cards.unshift({
                'name': null,
            });

            this.graph.cards.forEach(function(card){
                var found = !!_.find(this.graph.nodegroups, function(nodegroup){
                    return nodegroup.parentnodegroup_id === card.nodegroup_id;
                }, this);
                if(!found && !(card.nodegroup_id in nodegroups)){
                    this.cards.push(card);
                    nodegroups[card.nodegroup_id] = true;
                }
            }, this);

            this.name = params.config.name;

            this.description = params.config.description;
            this.map_popup = params.config.map_popup;

            _.each([this.name, this.description, this.map_popup], function(property){
                if (property.nodegroup_id) {
                    property.nodegroup_id.subscribe(function(nodegroup_id){
                        property.string_template(nodegroup_id);
                        var nodes = _.filter(this.graph.nodes, function(node){
                            return node.nodegroup_id === nodegroup_id;
                        }, this);
                        var templateFragments = [];
                        _.each(nodes, function(node){
                            templateFragments.push('<' + node.name + '>');
                        }, this);

                        var template = templateFragments.join(', ');
                        property.string_template(template);
                    }, this);
                }
            }, this);

            this.reindexdb = function(){
                this.loading(true);
                $.ajax({
                    type: "POST",
                    url: arches.urls.reindex,
                    context: this,
                    data: JSON.stringify({'graphids': [this.graph.graphid]}),
                    error: function() {
                        console.log('error');
                    },
                    complete: function(){
                        this.loading(false);
                    }
                });
            };
            window.setTimeout(function(){$("select[data-bind^=chosen]").trigger("chosen:updated");}, 300);
        },
        template: {
            require: 'text!templates/views/components/functions/primary-descriptors.htm'
        }
    });
});
<commit_msg>allows all cards in descriptor function, re: #4300<commit_after>define(['jquery',
    'underscore',
    'arches',
    'knockout',
    'knockout-mapping',
    'viewmodels/function',
    'bindings/chosen'],
function($, _, arches, ko, koMapping, FunctionViewModel, chosen) {
    return ko.components.register('views/components/functions/primary-descriptors', {
        viewModel: function(params) {
            FunctionViewModel.apply(this, arguments);
            var nodegroups = {};
            this.cards = ko.observableArray();
            this.loading = ko.observable(false);
            this.cards.unshift({
                'name': null,
            });

            this.graph.cards.forEach(function(card){
                this.cards.push(card);
                nodegroups[card.nodegroup_id] = true;
            }, this);

            this.name = params.config.name;

            this.description = params.config.description;
            this.map_popup = params.config.map_popup;

            _.each([this.name, this.description, this.map_popup], function(property){
                if (property.nodegroup_id) {
                    property.nodegroup_id.subscribe(function(nodegroup_id){
                        property.string_template(nodegroup_id);
                        var nodes = _.filter(this.graph.nodes, function(node){
                            return node.nodegroup_id === nodegroup_id;
                        }, this);
                        var templateFragments = [];
                        _.each(nodes, function(node){
                            templateFragments.push('<' + node.name + '>');
                        }, this);

                        var template = templateFragments.join(', ');
                        property.string_template(template);
                    }, this);
                }
            }, this);

            this.reindexdb = function(){
                this.loading(true);
                $.ajax({
                    type: "POST",
                    url: arches.urls.reindex,
                    context: this,
                    data: JSON.stringify({'graphids': [this.graph.graphid]}),
                    error: function() {
                        console.log('error');
                    },
                    complete: function(){
                        this.loading(false);
                    }
                });
            };
            window.setTimeout(function(){$("select[data-bind^=chosen]").trigger("chosen:updated");}, 300);
        },
        template: {
            require: 'text!templates/views/components/functions/primary-descriptors.htm'
        }
    });
});
<|endoftext|>


<commit_before>/*
stat effects from a fraymotif are temporary. wear off after battle.
so, players, player snapshots AND gameEntities will have to have an array of applied fraymotifs.
and their getPower, getHP etc stats must use these.
at start AND end of battle (can't be too careful), wipe applied fraymotifs
*/
function Fraymotif(aspects, name,tier){
    this.aspects = aspects; //expect to be an array
    this.name = name;
    this.tier = tier;
    this.used = false; //when start fight, set to false. set to true when used. once per fight
	this.effects = [];  //each effect is a target, a revive, a statName
	this.baseDamage = 50 * this.tier;
	if(this.tier == 3) this.baseDamage = 1000;

    this.toString  = function(){
      return this.name;
    }

	this.addEffectsForPlayers = function(players){
		for(var i = 0; i<players.length; i++){
			var effect = new FraymotifEffect();
			effect.setEffectForPlayer(players[i]);
			this.effects.push(effect);
		}
	}

	this.useFraymotif = function(users, enemies){
		console.log("TODO: calculate  damage by all statName values for all involved users - all involved enemies ")
		/*
			base damage * 1, 2, or 3 for each stat.

			for each stat, sum the values of the stat for allies, and subtract the values for the stat for the enemies.
			if final value < baseDamage, damage = baseDaamge.  if final > base < 2Base, damage = 2base;  if final > 2base, damage = 3base;

			STATNAME is always used, btw.  Either it is directly the thing being buffed or debuffed, or if damage it is what is used for damage calc.
			This DOES mean that buffing hp is the same thing as damage/healing. whatever.

		*/

		console.log("TODO: also don't forget that some fraymotifs should have triggers (but think most shouldn't now). don't try to heal or revive if it's not needed, dunkass.")
	}
}

//no global functions any more. bad pastJR.
function FraymotifCreator(session){
  this.session = session;

  this.getRandomBreathName = function(){
      var names = ["Gale", "Feather", "Breathless","Jetstream", "Hurricane", "Tornado"," Kansas", "Breath", "Breeze", "Twister", "Storm", "Wild", "Inhale", "Windy", "Skylark", "Fugue", "Pneumatic", "Wheeze", "Forward", "Vertical", "Whirlwind", "Jetstream"];
      return getRandomElementFromArray(names)
  }

  this.getRandomRageName = function(){
      var names = ["Rage", "Barbaric", "Impossible", "Juggalo","Horrorcore" ,"Madness", "Carnival", "Mirthful", "Screaming", "Berserk", "MoThErFuCkInG", "War", "Haze", "Murder", "Furioso", "Aggressive", "ATBasher", "Violent", "Unbound", "Purple", "Unholy", "Hateful", "Fearful", "Inconceivable", "Impossible"];
      return getRandomElementFromArray(names)
  }

  this.getRandomLifeName = function(){
      var names = ["Life" ,"Pastoral", "Green", "Relief", "Healing", "Plant", "Vitality", "Growing", "Garden", "Multiplying", "Rising", "Survival", "Phoenix", "Respawn", "Mangrit", "Animato", "Gaia", "Increasing", "Overgrowth", "Jungle", "Harvest", "Lazarus"];
      return getRandomElementFromArray(names)
  }

  this.getRandomHopeName = function(){
      var names = ["Hope","Fake", "Belief", "Bullshit", "Determination", "Burn", "Stubborn", "Religion", "Will", "Hero", "Undying", "Dream", "Sepulchritude", "Prophet", "Apocryphal"];
      return getRandomElementFromArray(names)
  }

  this.getRandomVoidName = function(){
      var randBonus = "" + getRandomElementFromArray(interests) +  ""
      var names = ["Void","Disappearification","Pumpkin", "Nothing", "Emptiness", "Invisible", "Dark", "Hole", "Solo", "Silent", "Alone", "Night", "Null", "[Censored]", "[???]", "Vacuous", "Abyss", "Noir", "Blank", "Tenebrous", "Antithesis", "404"];
      return getRandomElementFromArray(names)+ randBonus;
  }

  this.getRandomLightName = function(){
      var names = ["Lucky", "Light", "Knowledge", "Blinding", "Brilliant", "Break", "Blazing", "Glow", "Flare", "Gamble", "Omnifold", "Apollo", "Helios", "Scintillating", "Horseshoe", "Leggiero", "Star", "Kindle", "Gambit", "Blaze"];
      return getRandomElementFromArray(names)
  }

  this.getRandomMindName = function(){
      var names = ["Mind", "Modulation", "Shock", "Awe", "Coin", "Judgement", "Mind", "Decision", "Spark", "Path", "Trial", "Variations", "Thunder", "Logical", "Telekinetic", "Brainiac", "Hysteria", "Deciso", "Thesis", "Imagination",  "Psycho", "Control", "Execution", "Bolt"];
      return getRandomElementFromArray(names)
  }

  this.getRandomHeartName = function(){
      var names = ["Heart","Soul", "Jazz", "Blues", "Spirit", "Splintering", "Clone", "Self", "Havoc", "Noble", "Animus", "Astral", "Shatter", "Breakdown", "Ethereal", "Beat", "Pulchritude"];
      return getRandomElementFromArray(names)
  }

  this.getRandomBloodName = function(){
      var names = ["Blood", "Chain", "Flow", "Charge", "Awakening", "Ichorial", "Friendship", "Trusting", "Clotting", "Union", "Bleeding", "Team", "Transfusion", "Pulse", "Sanguine", "Positive", "Negative", "Cruor", "Vim", "Chorus", "Iron", "Ichorial", "Fever", "Heat"];
      return getRandomElementFromArray(names)
  }

  this.getRandomDoomName = function(){
      var names = ["Dark", "Broken", "Meteoric", "Diseased","Fate", "Doomed", "Inevitable", "Doom", "End", "Final", "Dead", "Ruin", "Rot", "Coffin", "Apocalypse", "Morendo", "Smorzando", "~Ath", "Armistyx", "Grave", "Corpse", "Ashen", "Reaper", "Diseased", "Armageddon", "Cursed"];
      return getRandomElementFromArray(names)
  }

  this.getRandomTimeName = function(){
      var names = ["Time","Paradox", "Moment", "Foregone", "Reset", "Endless", "Temporal", "Shenanigans", "Clock", "Tick-Tock", "Spinning", "Repeat", "Rhythm", "Redshift",  "Epoch", "Beatdown", "Slow", "Remix", "Clockwork", "Lock", "Eternal"];
      return getRandomElementFromArray(names)
  }

  this.getRandomSpaceName = function(){
      var names = ["Canon","Space","Frogs", "Location", "Spatial", "Universe", "Infinite", "Spiral", "Physics", "Star", "Galaxy", "Nuclear", "Atomic", "Nucleus", "Horizon", "Event", "CROAK", "Spatium", "Squiddle", "Engine", "Meteor", "Gravity", "Crush"];
      return getRandomElementFromArray(names)
  }

  this.getRandomNameForAspect = function(aspect){
    console.log(aspect);
    var ret = "";
    if(aspect == "Blood") ret = this.getRandomBloodName();
    if(aspect == "Mind") ret = this.getRandomMindName();
    if(aspect == "Rage") ret =  this.getRandomRageName();
    if(aspect == "Time") ret =  this.getRandomTimeName();
    if(aspect == "Void") ret =  this.getRandomVoidName();
    if(aspect == "Heart") ret =  this.getRandomHeartName();
    if(aspect == "Breath") ret =  this.getRandomBreathName();
    if(aspect == "Light") ret =  this.getRandomLightName();
    if(aspect == "Space") ret =  this.getRandomSpaceName();
    if(aspect == "Hope") ret =  this.getRandomHopeName();
    if(aspect == "Life") ret =  this.getRandomLifeName();
    if(aspect == "Doom") ret =  this.getRandomDoomName();
    return getFontColorFromAspect(aspect) + ret + ""
  }

  this.getRandomMusicWord = function(aspect){ //takes in an aspect for color
    var names = ["Fortississimo", "Leitmotif", "Liberetto", "Sarabande", "Serenade", "Anthem", "Crescendo", "Vivace", "Encore", "Vivante", "Allegretto", "Fugue", "Choir", "Nobilmente", "Hymn", "Eroico", "Chant", "Mysterioso", "Diminuendo", "Perdendo", "Staccato", "Allegro", "Caloroso", "Nocturne"];
    names = names.concat(["Cadenza", "Cadence", "Waltz", "Concerto", "Finale", "Requiem", "Coda", "Dirge", "Battaglia", "Leggiadro", "Capriccio", "Presto", "Largo", "Accelerando", "Polytempo", "Overture", "Reprise", "Orchestra"])

    var ret = getRandomElementFromArray(names);
    if(Math.seededRandom() > 0.5){
      return "" + ret.toLowerCase()+"";  //tacked onto existin word
    }else{
      return " " + ret; //extra word
    }
  }

  this.getFraymotifName = function(players, tier){
    var name = "";
    var indexOfMusic = players.length-1;  //used to be random now always at end.
    if(players.length == 1){
      indexOfMusic = getRandomInt(0,tier-1);
      for(var i = 0; i < tier; i++){
        var musicWord = "";
        if(i == indexOfMusic) musicWord = this.getRandomMusicWord(players[i].aspect);
        name += this.getRandomNameForAspect(players[0].aspect) + musicWord +" ";
      }
    }else{

      for(var i = 0; i<players.length; i++){
        var musicWord = "";
        if(i == indexOfMusic) musicWord = this.getRandomMusicWord(players[i].aspect);
        name += this.getRandomNameForAspect(players[i].aspect) + musicWord +  " ";
      }
    }
    return name;
  }

  //classes is between 0 and aspects.length. each aspect is paired with a class.
  //should there be no class to pair with, random effect based on aspect
  //otherwise, effect is based on both class and aspect
  this.makeFraymotif = function(players,tier){
    var name = this.getFraymotifName(players, tier);
	var aspects = [];
	for(var i = 0; i<players.length; i++){
		if(aspects.indexOf(players[i].aspect) == -1) aspects.push(players[i].aspect); //if multiple of the same aspect is included here, just ignore.
	}
    var f= new Fraymotif(aspects, name, tier);
	f.addEffectsForPlayers(players);
	return f;
  }
}

    //effects are frozen at creation, basically.  if this fraymotif is created by a Bard of Breath in a session with a Prince of Time,
    //who then dies, and then a combo session results in an Heir of Time being able to use it with the Bard of Breath, then it'll still have the prince effect.
function FraymotifEffect(statName, target, damageInsteadOfBuff){
	this.statName = statName;  //RELATIONSHIP, hp AND LIFE are all treated differently.  (Life can only be cast on allies), hp is applied to currentHp rather than hp. otherwise same stuff as associatedStats.
	this.target = target; //self, allies or enemy or enemies, 0, 1, 2, 3
	this.damageInsteadOfBuff = damageInsteadOfBuff; // statName can either be applied towards damaging someone or buffing someone.  (damaging self or allies is "healing", buffing enemies is applied in the negative direction.)
	this.s = 0;  //convineience methods cause i don't think js has enums but am too lazy to confirm.
	this.a = 1;
	this.e = 2;
	this.e2 = 3;

	this.setEffectForPlayer = function(player){
		var effect = new FraymotifEffect("",this.e, true); //default to just damaging the enemy.
		if(player.class_name == "Knight") effect = getRandomElementFromArray(this.knightEffects());
		this.target = effect.target;
		this.damageInsteadOfBuff = effect.damageInsteadOfBuff;
		if(this.target == 0 || this.target == 1){
			this.statName = getRandomElementFromArray(player.getOnlyPositiveAspectAssociatedStats()).name;
		}else{
			this.statName = getRandomElementFromArray(player.getOnlyNegativeAspectAssociatedStats()).name;
		}
	}

	//preliminary design detailed here: https://docs.google.com/spreadsheets/d/1kam2FnKJiek6DidDpQdSnR3Wl9-vk1oZBa0pPpxlJk4/edit#gid=0
	//knights protect themselves and hurt the enemy
	this.knightEffects = function(){
		return [new FraymotifEffect("",this.s,true),new FraymotifEffect("",this.e,true),new FraymotifEffect("",this.e2,true),new FraymotifEffect("",this.s,false),new FraymotifEffect("",this.e,false) ];
	}

	this.toString = function(){
		var ret = "";
		if(this.damageInsteadOfBuff && this.target < 2){
			 ret += " Heals"
		}else if (this.damageInsteadOfBuff && this.target >= 2){
			ret += " Damages"
		}else if(!this.damageInsteadOfBuff && this.target < 2){
			ret += " Buffs"
		}else if(!this.damageInsteadOfBuff && this.target >= 2){
			ret += " Debuffs"
		}

		if(this.target == 0){
			ret += " Self"
		}else if(this.target ==1){
			ret += " Allies"
		}else if(this.target ==2){
			ret += " An Enemy"
		}else if(this.target ==3){
			ret += " All Enemies"
		}

		ret += " Based On " + this.statName
		return ret;
	}
}<commit_msg>this is going pretty well<commit_after>/*
stat effects from a fraymotif are temporary. wear off after battle.
so, players, player snapshots AND gameEntities will have to have an array of applied fraymotifs.
and their getPower, getHP etc stats must use these.
at start AND end of battle (can't be too careful), wipe applied fraymotifs
*/
function Fraymotif(aspects, name,tier){
    this.aspects = aspects; //expect to be an array
    this.name = name;
    this.tier = tier;
    this.used = false; //when start fight, set to false. set to true when used. once per fight
	this.effects = [];  //each effect is a target, a revive, a statName
	this.baseDamage = 50 * this.tier;
	if(this.tier == 3) this.baseDamage = 1000;

    this.toString  = function(){
      return this.name;
    }

	this.addEffectsForPlayers = function(players){
		for(var i = 0; i<players.length; i++){
			var effect = new FraymotifEffect();
			effect.setEffectForPlayer(players[i]);
			this.effects.push(effect);
		}
	}

	this.useFraymotif = function(users, enemies){
		console.log("TODO: calculate  damage by all statName values for all involved users - all involved enemies ")
		/*
			base damage * 1, 2, or 3 for each stat.

			for each stat, sum the values of the stat for allies, and subtract the values for the stat for the enemies.
			if final value < baseDamage, damage = baseDaamge.  if final > base < 2Base, damage = 2base;  if final > 2base, damage = 3base;

			STATNAME is always used, btw.  Either it is directly the thing being buffed or debuffed, or if damage it is what is used for damage calc.
			This DOES mean that buffing hp is the same thing as damage/healing. whatever.

		*/

		console.log("TODO: also don't forget that some fraymotifs should have triggers (but think most shouldn't now). don't try to heal or revive if it's not needed, dunkass.")
	}
}

//no global functions any more. bad pastJR.
function FraymotifCreator(session){
  this.session = session;

  this.getRandomBreathName = function(){
      var names = ["Gale", "Feather", "Breathless","Jetstream", "Hurricane", "Tornado"," Kansas", "Breath", "Breeze", "Twister", "Storm", "Wild", "Inhale", "Windy", "Skylark", "Fugue", "Pneumatic", "Wheeze", "Forward", "Vertical", "Whirlwind", "Jetstream"];
      return getRandomElementFromArray(names)
  }

  this.getRandomRageName = function(){
      var names = ["Rage", "Barbaric", "Impossible", "Juggalo","Horrorcore" ,"Madness", "Carnival", "Mirthful", "Screaming", "Berserk", "MoThErFuCkInG", "War", "Haze", "Murder", "Furioso", "Aggressive", "ATBasher", "Violent", "Unbound", "Purple", "Unholy", "Hateful", "Fearful", "Inconceivable", "Impossible"];
      return getRandomElementFromArray(names)
  }

  this.getRandomLifeName = function(){
      var names = ["Life" ,"Pastoral", "Green", "Relief", "Healing", "Plant", "Vitality", "Growing", "Garden", "Multiplying", "Rising", "Survival", "Phoenix", "Respawn", "Mangrit", "Animato", "Gaia", "Increasing", "Overgrowth", "Jungle", "Harvest", "Lazarus"];
      return getRandomElementFromArray(names)
  }

  this.getRandomHopeName = function(){
      var names = ["Hope","Fake", "Belief", "Bullshit", "Determination", "Burn", "Stubborn", "Religion", "Will", "Hero", "Undying", "Dream", "Sepulchritude", "Prophet", "Apocryphal"];
      return getRandomElementFromArray(names)
  }

  this.getRandomVoidName = function(){
      var randBonus = "" + getRandomElementFromArray(interests) +  ""
      var names = ["Void","Disappearification","Pumpkin", "Nothing", "Emptiness", "Invisible", "Dark", "Hole", "Solo", "Silent", "Alone", "Night", "Null", "[Censored]", "[???]", "Vacuous", "Abyss", "Noir", "Blank", "Tenebrous", "Antithesis", "404"];
      return getRandomElementFromArray(names)+ randBonus;
  }

  this.getRandomLightName = function(){
      var names = ["Lucky", "Light", "Knowledge", "Blinding", "Brilliant", "Break", "Blazing", "Glow", "Flare", "Gamble", "Omnifold", "Apollo", "Helios", "Scintillating", "Horseshoe", "Leggiero", "Star", "Kindle", "Gambit", "Blaze"];
      return getRandomElementFromArray(names)
  }

  this.getRandomMindName = function(){
      var names = ["Mind", "Modulation", "Shock", "Awe", "Coin", "Judgement", "Mind", "Decision", "Spark", "Path", "Trial", "Variations", "Thunder", "Logical", "Telekinetic", "Brainiac", "Hysteria", "Deciso", "Thesis", "Imagination",  "Psycho", "Control", "Execution", "Bolt"];
      return getRandomElementFromArray(names)
  }

  this.getRandomHeartName = function(){
      var names = ["Heart","Soul", "Jazz", "Blues", "Spirit", "Splintering", "Clone", "Self", "Havoc", "Noble", "Animus", "Astral", "Shatter", "Breakdown", "Ethereal", "Beat", "Pulchritude"];
      return getRandomElementFromArray(names)
  }

  this.getRandomBloodName = function(){
      var names = ["Blood", "Chain", "Flow", "Charge", "Awakening", "Ichorial", "Friendship", "Trusting", "Clotting", "Union", "Bleeding", "Team", "Transfusion", "Pulse", "Sanguine", "Positive", "Negative", "Cruor", "Vim", "Chorus", "Iron", "Ichorial", "Fever", "Heat"];
      return getRandomElementFromArray(names)
  }

  this.getRandomDoomName = function(){
      var names = ["Dark", "Broken", "Meteoric", "Diseased","Fate", "Doomed", "Inevitable", "Doom", "End", "Final", "Dead", "Ruin", "Rot", "Coffin", "Apocalypse", "Morendo", "Smorzando", "~Ath", "Armistyx", "Grave", "Corpse", "Ashen", "Reaper", "Diseased", "Armageddon", "Cursed"];
      return getRandomElementFromArray(names)
  }

  this.getRandomTimeName = function(){
      var names = ["Time","Paradox", "Moment", "Foregone", "Reset", "Endless", "Temporal", "Shenanigans", "Clock", "Tick-Tock", "Spinning", "Repeat", "Rhythm", "Redshift",  "Epoch", "Beatdown", "Slow", "Remix", "Clockwork", "Lock", "Eternal"];
      return getRandomElementFromArray(names)
  }

  this.getRandomSpaceName = function(){
      var names = ["Canon","Space","Frogs", "Location", "Spatial", "Universe", "Infinite", "Spiral", "Physics", "Star", "Galaxy", "Nuclear", "Atomic", "Nucleus", "Horizon", "Event", "CROAK", "Spatium", "Squiddle", "Engine", "Meteor", "Gravity", "Crush"];
      return getRandomElementFromArray(names)
  }

  this.getRandomNameForAspect = function(aspect){
    console.log(aspect);
    var ret = "";
    if(aspect == "Blood") ret = this.getRandomBloodName();
    if(aspect == "Mind") ret = this.getRandomMindName();
    if(aspect == "Rage") ret =  this.getRandomRageName();
    if(aspect == "Time") ret =  this.getRandomTimeName();
    if(aspect == "Void") ret =  this.getRandomVoidName();
    if(aspect == "Heart") ret =  this.getRandomHeartName();
    if(aspect == "Breath") ret =  this.getRandomBreathName();
    if(aspect == "Light") ret =  this.getRandomLightName();
    if(aspect == "Space") ret =  this.getRandomSpaceName();
    if(aspect == "Hope") ret =  this.getRandomHopeName();
    if(aspect == "Life") ret =  this.getRandomLifeName();
    if(aspect == "Doom") ret =  this.getRandomDoomName();
    return getFontColorFromAspect(aspect) + ret + ""
  }

  this.getRandomMusicWord = function(aspect){ //takes in an aspect for color
    var names = ["Fortississimo", "Leitmotif", "Liberetto", "Sarabande", "Serenade", "Anthem", "Crescendo", "Vivace", "Encore", "Vivante", "Allegretto", "Fugue", "Choir", "Nobilmente", "Hymn", "Eroico", "Chant", "Mysterioso", "Diminuendo", "Perdendo", "Staccato", "Allegro", "Caloroso", "Nocturne"];
    names = names.concat(["Cadenza", "Cadence", "Waltz", "Concerto", "Finale", "Requiem", "Coda", "Dirge", "Battaglia", "Leggiadro", "Capriccio", "Presto", "Largo", "Accelerando", "Polytempo", "Overture", "Reprise", "Orchestra"])

    var ret = getRandomElementFromArray(names);
    if(Math.seededRandom() > 0.5){
      return "" + ret.toLowerCase()+"";  //tacked onto existin word
    }else{
      return " " + ret; //extra word
    }
  }

  this.getFraymotifName = function(players, tier){
    var name = "";
    var indexOfMusic = players.length-1;  //used to be random now always at end.
    if(players.length == 1){
      indexOfMusic = getRandomInt(0,tier-1);
      for(var i = 0; i < tier; i++){
        var musicWord = "";
        if(i == indexOfMusic) musicWord = this.getRandomMusicWord(players[i].aspect);
        name += this.getRandomNameForAspect(players[0].aspect) + musicWord +" ";
      }
    }else{

      for(var i = 0; i<players.length; i++){
        var musicWord = "";
        if(i == indexOfMusic) musicWord = this.getRandomMusicWord(players[i].aspect);
        name += this.getRandomNameForAspect(players[i].aspect) + musicWord +  " ";
      }
    }
    return name;
  }

  //classes is between 0 and aspects.length. each aspect is paired with a class.
  //should there be no class to pair with, random effect based on aspect
  //otherwise, effect is based on both class and aspect
  this.makeFraymotif = function(players,tier){
    var name = this.getFraymotifName(players, tier);
	var aspects = [];
	for(var i = 0; i<players.length; i++){
		if(aspects.indexOf(players[i].aspect) == -1) aspects.push(players[i].aspect); //if multiple of the same aspect is included here, just ignore.
	}
    var f= new Fraymotif(aspects, name, tier);
	f.addEffectsForPlayers(players);
	return f;
  }
}

    //effects are frozen at creation, basically.  if this fraymotif is created by a Bard of Breath in a session with a Prince of Time,
    //who then dies, and then a combo session results in an Heir of Time being able to use it with the Bard of Breath, then it'll still have the prince effect.
function FraymotifEffect(statName, target, damageInsteadOfBuff){
	this.statName = statName;  //RELATIONSHIP, hp AND LIFE are all treated differently.  (Life can only be cast on allies), hp is applied to currentHp rather than hp. otherwise same stuff as associatedStats.
	this.target = target; //self, allies or enemy or enemies, 0, 1, 2, 3
	this.damageInsteadOfBuff = damageInsteadOfBuff; // statName can either be applied towards damaging someone or buffing someone.  (damaging self or allies is "healing", buffing enemies is applied in the negative direction.)
	this.s = 0;  //convineience methods cause i don't think js has enums but am too lazy to confirm.
	this.a = 1;
	this.e = 2;
	this.e2 = 3;

	this.setEffectForPlayer = function(player){
		var effect = new FraymotifEffect("",this.e, true); //default to just damaging the enemy.
		if(player.class_name == "Knight") effect = getRandomElementFromArray(this.knightEffects());
		this.target = effect.target;
		this.damageInsteadOfBuff = effect.damageInsteadOfBuff;
		this.statName = getRandomElementFromArray(player.getOnlyPositiveAspectAssociatedStats()).name;
	}

	//preliminary design detailed here: https://docs.google.com/spreadsheets/d/1kam2FnKJiek6DidDpQdSnR3Wl9-vk1oZBa0pPpxlJk4/edit#gid=0
	//knights protect themselves and hurt the enemy
	this.knightEffects = function(){
		return [new FraymotifEffect("",this.s,true),new FraymotifEffect("",this.e,true),new FraymotifEffect("",this.e2,true),new FraymotifEffect("",this.s,false),new FraymotifEffect("",this.e,false) ];
	}

	this.toString = function(){
		var ret = "";
		if(this.damageInsteadOfBuff && this.target < 2){
			 ret += " Heals"
		}else if (this.damageInsteadOfBuff && this.target >= 2){
			ret += " Damages"
		}else if(!this.damageInsteadOfBuff && this.target < 2){
			ret += " Buffs"
		}else if(!this.damageInsteadOfBuff && this.target >= 2){
			ret += " Debuffs"
		}

		if(this.target == 0){
			ret += " Self"
		}else if(this.target ==1){
			ret += " Allies"
		}else if(this.target ==2){
			ret += " An Enemy"
		}else if(this.target ==3){
			ret += " All Enemies"
		}

		ret += " Based On " + this.statName
		return ret;
	}
}<|endoftext|>


<commit_before>// @flow
import $ from 'jquery';
import React, { Component } from 'react';
import { FormattedMessage, FormattedHTMLMessage } from 'react-intl';
import { connect } from 'react-redux';
import braintreeClient from 'braintree-web/client';
import dataCollector from 'braintree-web/data-collector';
import { isEmpty } from 'lodash';
import ee from '../../shared/pub_sub';

import PayPal from '../Braintree/PayPal';
import BraintreeCardFields from '../Braintree/BraintreeCardFields';
import PaymentTypeSelection from './PaymentTypeSelection';
import WelcomeMember from '../WelcomeMember/WelcomeMember';
import DonateButton from '../DonateButton';
import Checkbox from '../Checkbox/Checkbox';
import ShowIf from '../ShowIf';
import { resetMember } from '../../state/member/reducer';
import {
  changeStep,
  setRecurring,
  setStoreInVault,
  setPaymentType,
} from '../../state/fundraiser/actions';
import ExpressDonation from '../ExpressDonation/ExpressDonation';

import type { Dispatch } from 'redux';
import type { Client } from 'braintree-web';
import type { AppState, Member, Fundraiser, PaymentMethod } from '../../state';
import type { PaymentType } from '../../state/fundraiser/types';

// Styles
import './Payment.css';

const BRAINTREE_TOKEN_URL =
  process.env.BRAINTREE_TOKEN_URL || '/api/payment/braintree/token';

type OwnProps = {
  defaultPaymentType: PaymentType,
  disableSavedPayments: boolean,
  currentPaymentType: PaymentType,
  member: Member,
  fundraiser: Fundraiser,
  paymentMethods: PaymentMethod[],
  page: ChampaignPage,
  hideRecurring: boolean,
  disableFormReveal: boolean,
  formData: { member: any, storeInVault: boolean },
  resetMember: () => void,
  changeStep: (step: number) => void,
  setRecurring: (value: boolean) => void,
  setStoreInVault: (value: boolean) => void,
  setPaymentType: (value: PaymentType) => void,
  setSubmitting: (value: boolean) => void,
  showDirectDebit: boolean,
  extraActionFields: { [string]: string | boolean | number },
};

type OwnState = {
  client: Client,
  deviceData: Object,
  loading: boolean,
  submitting: boolean,
  expressHidden: boolean,
  initializing: {
    gocardless: boolean,
    paypal: boolean,
    card: boolean,
  },
  errors: any[],
  waitingForGoCardless: boolean,
};
export class Payment extends Component {
  props: OwnProps;
  state: OwnState;

  static title = <FormattedMessage id="payment" defaultMessage="payment" />;

  constructor(props: OwnProps) {
    super(props);
    this.state = {
      client: null,
      deviceData: {},
      loading: true,
      submitting: false,
      expressHidden: false,
      initializing: {
        gocardless: false,
        paypal: true,
        card: true,
      },
      errors: [],
      waitingForGoCardless: false,
    };
  }

  componentDidMount() {
    $.get(BRAINTREE_TOKEN_URL)
      .done(data => {
        braintreeClient.create(
          { authorization: data.token },
          (error, client) => {
            // todo: handle err?
            dataCollector.create(
              {
                client,
                kount: true,
                paypal: true,
              },
              (err, collectorInst) => {
                if (err) {
                  return this.setState({ client, loading: false });
                }

                const deviceData = collectorInst.deviceData;
                this.setState({
                  client,
                  deviceData: JSON.parse(deviceData),
                  loading: false,
                });
              }
            );
          }
        );
      })
      .fail(failure => {
        console.warn('could not fetch Braintree token');
      });
  }

  componentDidUpdate() {
    ee.emit('sidebar:height_change');
  }

  selectPaymentType(paymentType: PaymentType) {
    this.props.setPaymentType(paymentType);
  }

  resetMember() {
    this.props.resetMember();
  }

  paymentInitialized(name: string) {
    this.setState({
      initializing: { ...this.state.initializing, [name]: false },
    });
  }

  loading(paymentType: ?string) {
    const loading = this.state.loading;
    if (paymentType) {
      return loading || this.state.initializing[paymentType];
    } else {
      return loading;
    }
  }

  disableSubmit() {
    return (
      this.loading(this.props.currentPaymentType) ||
      !this.props.currentPaymentType ||
      !this.props.fundraiser.donationAmount
    );
  }

  // this should actually be a selector (a fn that returns a slice of state)
  donationData() {
    const {
      fundraiser: {
        donationAmount,
        currency,
        recurring,
        storeInVault,
        form,
        formValues,
      },
      extraActionFields,
    } = this.props;

    return {
      amount: donationAmount,
      currency: currency,
      recurring: recurring,
      store_in_vault: storeInVault,
      user: {
        ...formValues,
        ...form,
      },
      extra_action_fields: extraActionFields,
    };
  }

  delegate() {
    const delegate = this.refs[this.props.currentPaymentType];

    if (delegate && delegate.submit) {
      return delegate;
    } else if (delegate && delegate.getWrappedInstance().submit) {
      return delegate.getWrappedInstance();
    }

    return null;
  }

  submitGoCardless() {
    const payload = {
      ...this.donationData(),
      device_data: this.state.deviceData,
      provider: 'GC',
    };
    const url = `/api/go_cardless/pages/${
      this.props.page.id
    }/start_flow?${$.param(payload)}`;
    window.open(url);

    if (!this.state.waitingForGoCardless) {
      window.addEventListener('message', this.waitForGoCardless.bind(this));
      this.setState({ waitingForGoCardless: true });
    }
  }

  waitForGoCardless(event: any) {
    if (typeof event.data === 'object') {
      if (event.data.event === 'follow_up:loaded') {
        event.source.close();
        ee.emit('direct_debit:donated');
        this.onSuccess({});
      } else if (event.data.event === 'donation:error') {
        const messages = event.data.errors.map(({ message }) => message);
        this.onError(messages);
        event.source.close();
      }
    }
  }

  makePayment() {
    if (this.props.currentPaymentType === 'gocardless') {
      this.submitGoCardless();
      return;
    }
    const delegate = this.delegate();

    this.props.setSubmitting(true);

    if (delegate && delegate.submit) {
      delegate
        .submit()
        .then(success => this.submit(success), reason => this.onError(reason));
    } else {
      this.submit();
    }
  }

  submit(data: any) {
    const payload = {
      ...this.donationData(),
      payment_method_nonce: data.nonce,
      device_data: this.state.deviceData,
    };

    const eventPayload = {
      value: this.props.fundraiser.donationAmount,
      currency: this.props.fundraiser.currency,
      content_category: this.props.currentPaymentType,
      recurring: this.props.fundraiser.recurring,
    };

    if (typeof window.fbq === 'function') {
      window.fbq('track', 'AddPaymentInfo', eventPayload);
    }

    ee.emit(
      'fundraiser:transaction_submitted',
      eventPayload,
      this.props.formData
    );

    $.post(
      `/api/payment/braintree/pages/${this.props.page.id}/transaction`,
      payload
    ).then(this.onSuccess.bind(this), this.onBraintreeError.bind(this));
  }

  onSuccess(data: any) {
    if (typeof window.fbq === 'function') {
      window.fbq('track', 'Purchase', {
        value: this.props.fundraiser.donationAmount,
        currency: this.props.fundraiser.currency,
        content_name: this.props.page.title,
        content_ids: [this.props.page.id],
        content_type: this.props.fundraiser.recurring
          ? 'recurring'
          : 'not_recurring',
      });
    }
    ee.emit('fundraiser:transaction_success', data, this.props.formData);
    this.setState({ errors: [] });
  }

  onError(reason: any) {
    ee.emit('fundraiser:transaction_error', reason, this.props.formData);
    this.props.setSubmitting(false);
  }

  onBraintreeError(response: any) {
    let errors;
    if (
      response.status === 422 &&
      response.responseJSON &&
      response.responseJSON.errors
    ) {
      errors = response.responseJSON.errors.map(function(error) {
        if (error.declined) {
          return <FormattedMessage id="fundraiser.card_declined" />;
        } else {
          return error.message;
        }
      });
    } else {
      errors = [<FormattedMessage id="fundraiser.unknown_error" />];
    }
    this.setState({ errors: errors });
    this.onError(response);
  }

  isExpressHidden() {
    return this.state.expressHidden || this.props.disableSavedPayments;
  }

  render() {
    const {
      member,
      hideRecurring,
      fundraiser: {
        currency,
        donationAmount,
        currentPaymentType,
        recurring,
        storeInVault,
      },
    } = this.props;

    return (

        <ShowIf condition={!isEmpty(this.state.errors)}>

              <FormattedMessage
                id="fundraiser.error_intro"
                defaultMessage="Unable to process donation!"
              />

            {this.state.errors.map((e, i) => {
              return (

                  {e}

              );
            })}

        </ShowIf>

        {!this.props.disableFormReveal && (
          <WelcomeMember
            member={member}
            resetMember={() => this.resetMember()}
          />
        )}

        <ExpressDonation
          setSubmitting={s => this.props.setSubmitting(s)}
          hidden={this.isExpressHidden()}
          onHide={() => this.setState({ expressHidden: true })}
        />

        <ShowIf condition={this.isExpressHidden()}>
          <PaymentTypeSelection
            disabled={this.state.loading}
            currentPaymentType={this.props.currentPaymentType}
            onChange={p => this.selectPaymentType(p)}
          />

          <PayPal
            ref="paypal"
            amount={donationAmount}
            currency={currency}
            client={this.state.client}
            vault={recurring || storeInVault}
            onInit={() => this.paymentInitialized('paypal')}
          />

          <BraintreeCardFields
            ref="card"
            client={this.state.client}
            recurring={recurring}
            isActive={currentPaymentType === 'card'}
            onInit={() => this.paymentInitialized('card')}
          />

          {currentPaymentType === 'paypal' && (

              <FormattedMessage
                id={'fundraiser.payment_methods.ready_for_paypal'}
              />

          )}

          {currentPaymentType === 'gocardless' && (

              <FormattedMessage
                id={'fundraiser.payment_methods.ready_for_gocardless'}
              />

          )}

          {!hideRecurring && (
            <Checkbox
              className="Payment__config"
              disabled={hideRecurring}
              defaultChecked={recurring}
              onChange={e => this.props.setRecurring(e.target.checked)}
            >
              <FormattedMessage
                id="fundraiser.make_recurring"
                defaultMessage="Make my donation monthly"
              />
            </Checkbox>
          )}

          <Checkbox
            className="Payment__config"
            defaultChecked={storeInVault}
            onChange={e => this.props.setStoreInVault(e.target.checked)}
          >
            <FormattedMessage
              id="fundraiser.store_in_vault"
              defaultMessage="Securely store my payment information"
            />
          </Checkbox>

          <DonateButton
            currency={currency}
            amount={donationAmount || 0}
            submitting={this.state.submitting}
            recurring={recurring}
            disabled={this.disableSubmit()}
            onClick={() => this.makePayment()}
          />
        </ShowIf>

          <FormattedHTMLMessage
            className="Payment__fine-print"
            id="fundraiser.fine_print"
            defaultMessage={`
              SumOfUs is a registered 501(c)4 non-profit incorporated in Washington, DC, United
              States. Contributions or gifts to SumOfUs are not tax deductible.
              For further information, please contact info@sumofus.org.
            `}
          />

        {this.props.showDirectDebit && (

        )}

    );
  }
}

const mapStateToProps = (state: AppState) => ({
  disableSavedPayments:
    state.fundraiser.disableSavedPayments || state.paymentMethods.length === 0,
  defaultPaymentType: state.fundraiser.directDebitOnly ? 'gocardless' : 'card',
  showDirectDebit: state.fundraiser.showDirectDebit,
  currentPaymentType: state.fundraiser.directDebitOnly
    ? 'gocardless'
    : state.fundraiser.currentPaymentType,
  fundraiser: state.fundraiser,
  page: state.page,
  paymentMethods: state.paymentMethods,
  member: state.member,
  hideRecurring: state.fundraiser.recurringDefault === 'only_recurring',
  formData: {
    storeInVault: state.fundraiser.storeInVault,
    member: {
      ...state.fundraiser.formValues,
      ...state.fundraiser.form,
    },
  },
  extraActionFields: state.extraActionFields,
});

const mapDispatchToProps = (dispatch: Dispatch<*>) => ({
  resetMember: () => dispatch(resetMember()),
  changeStep: (step: number) => dispatch(changeStep(step)),
  setRecurring: (value: boolean) => dispatch(setRecurring(value)),
  setStoreInVault: (value: boolean) => dispatch(setStoreInVault(value)),
  setPaymentType: (value: PaymentType) => dispatch(setPaymentType(value)),
});

export default connect(mapStateToProps, mapDispatchToProps)(Payment);
<commit_msg>Use checked instead of defaultChecked in sidebar checkboxes<commit_after>// @flow
import $ from 'jquery';
import React, { Component } from 'react';
import { FormattedMessage, FormattedHTMLMessage } from 'react-intl';
import { connect } from 'react-redux';
import braintreeClient from 'braintree-web/client';
import dataCollector from 'braintree-web/data-collector';
import { isEmpty } from 'lodash';
import ee from '../../shared/pub_sub';

import PayPal from '../Braintree/PayPal';
import BraintreeCardFields from '../Braintree/BraintreeCardFields';
import PaymentTypeSelection from './PaymentTypeSelection';
import WelcomeMember from '../WelcomeMember/WelcomeMember';
import DonateButton from '../DonateButton';
import Checkbox from '../Checkbox/Checkbox';
import ShowIf from '../ShowIf';
import { resetMember } from '../../state/member/reducer';
import {
  changeStep,
  setRecurring,
  setStoreInVault,
  setPaymentType,
} from '../../state/fundraiser/actions';
import ExpressDonation from '../ExpressDonation/ExpressDonation';

import type { Dispatch } from 'redux';
import type { Client } from 'braintree-web';
import type { AppState, Member, Fundraiser, PaymentMethod } from '../../state';
import type { PaymentType } from '../../state/fundraiser/types';

// Styles
import './Payment.css';

const BRAINTREE_TOKEN_URL =
  process.env.BRAINTREE_TOKEN_URL || '/api/payment/braintree/token';

type OwnProps = {
  defaultPaymentType: PaymentType,
  disableSavedPayments: boolean,
  currentPaymentType: PaymentType,
  member: Member,
  fundraiser: Fundraiser,
  paymentMethods: PaymentMethod[],
  page: ChampaignPage,
  hideRecurring: boolean,
  disableFormReveal: boolean,
  formData: { member: any, storeInVault: boolean },
  resetMember: () => void,
  changeStep: (step: number) => void,
  setRecurring: (value: boolean) => void,
  setStoreInVault: (value: boolean) => void,
  setPaymentType: (value: PaymentType) => void,
  setSubmitting: (value: boolean) => void,
  showDirectDebit: boolean,
  extraActionFields: { [string]: string | boolean | number },
};

type OwnState = {
  client: Client,
  deviceData: Object,
  loading: boolean,
  submitting: boolean,
  expressHidden: boolean,
  initializing: {
    gocardless: boolean,
    paypal: boolean,
    card: boolean,
  },
  errors: any[],
  waitingForGoCardless: boolean,
};
export class Payment extends Component {
  props: OwnProps;
  state: OwnState;

  static title = <FormattedMessage id="payment" defaultMessage="payment" />;

  constructor(props: OwnProps) {
    super(props);
    this.state = {
      client: null,
      deviceData: {},
      loading: true,
      submitting: false,
      expressHidden: false,
      initializing: {
        gocardless: false,
        paypal: true,
        card: true,
      },
      errors: [],
      waitingForGoCardless: false,
    };
  }

  componentDidMount() {
    $.get(BRAINTREE_TOKEN_URL)
      .done(data => {
        braintreeClient.create(
          { authorization: data.token },
          (error, client) => {
            // todo: handle err?
            dataCollector.create(
              {
                client,
                kount: true,
                paypal: true,
              },
              (err, collectorInst) => {
                if (err) {
                  return this.setState({ client, loading: false });
                }

                const deviceData = collectorInst.deviceData;
                this.setState({
                  client,
                  deviceData: JSON.parse(deviceData),
                  loading: false,
                });
              }
            );
          }
        );
      })
      .fail(failure => {
        console.warn('could not fetch Braintree token');
      });
  }

  componentDidUpdate() {
    ee.emit('sidebar:height_change');
  }

  selectPaymentType(paymentType: PaymentType) {
    this.props.setPaymentType(paymentType);
  }

  resetMember() {
    this.props.resetMember();
  }

  paymentInitialized(name: string) {
    this.setState({
      initializing: { ...this.state.initializing, [name]: false },
    });
  }

  loading(paymentType: ?string) {
    const loading = this.state.loading;
    if (paymentType) {
      return loading || this.state.initializing[paymentType];
    } else {
      return loading;
    }
  }

  disableSubmit() {
    return (
      this.loading(this.props.currentPaymentType) ||
      !this.props.currentPaymentType ||
      !this.props.fundraiser.donationAmount
    );
  }

  // this should actually be a selector (a fn that returns a slice of state)
  donationData() {
    const {
      fundraiser: {
        donationAmount,
        currency,
        recurring,
        storeInVault,
        form,
        formValues,
      },
      extraActionFields,
    } = this.props;

    return {
      amount: donationAmount,
      currency: currency,
      recurring: recurring,
      store_in_vault: storeInVault,
      user: {
        ...formValues,
        ...form,
      },
      extra_action_fields: extraActionFields,
    };
  }

  delegate() {
    const delegate = this.refs[this.props.currentPaymentType];

    if (delegate && delegate.submit) {
      return delegate;
    } else if (delegate && delegate.getWrappedInstance().submit) {
      return delegate.getWrappedInstance();
    }

    return null;
  }

  submitGoCardless() {
    const payload = {
      ...this.donationData(),
      device_data: this.state.deviceData,
      provider: 'GC',
    };
    const url = `/api/go_cardless/pages/${
      this.props.page.id
    }/start_flow?${$.param(payload)}`;
    window.open(url);

    if (!this.state.waitingForGoCardless) {
      window.addEventListener('message', this.waitForGoCardless.bind(this));
      this.setState({ waitingForGoCardless: true });
    }
  }

  waitForGoCardless(event: any) {
    if (typeof event.data === 'object') {
      if (event.data.event === 'follow_up:loaded') {
        event.source.close();
        ee.emit('direct_debit:donated');
        this.onSuccess({});
      } else if (event.data.event === 'donation:error') {
        const messages = event.data.errors.map(({ message }) => message);
        this.onError(messages);
        event.source.close();
      }
    }
  }

  makePayment() {
    if (this.props.currentPaymentType === 'gocardless') {
      this.submitGoCardless();
      return;
    }
    const delegate = this.delegate();

    this.props.setSubmitting(true);

    if (delegate && delegate.submit) {
      delegate
        .submit()
        .then(success => this.submit(success), reason => this.onError(reason));
    } else {
      this.submit();
    }
  }

  submit(data: any) {
    const payload = {
      ...this.donationData(),
      payment_method_nonce: data.nonce,
      device_data: this.state.deviceData,
    };

    const eventPayload = {
      value: this.props.fundraiser.donationAmount,
      currency: this.props.fundraiser.currency,
      content_category: this.props.currentPaymentType,
      recurring: this.props.fundraiser.recurring,
    };

    if (typeof window.fbq === 'function') {
      window.fbq('track', 'AddPaymentInfo', eventPayload);
    }

    ee.emit(
      'fundraiser:transaction_submitted',
      eventPayload,
      this.props.formData
    );

    $.post(
      `/api/payment/braintree/pages/${this.props.page.id}/transaction`,
      payload
    ).then(this.onSuccess.bind(this), this.onBraintreeError.bind(this));
  }

  onSuccess(data: any) {
    if (typeof window.fbq === 'function') {
      window.fbq('track', 'Purchase', {
        value: this.props.fundraiser.donationAmount,
        currency: this.props.fundraiser.currency,
        content_name: this.props.page.title,
        content_ids: [this.props.page.id],
        content_type: this.props.fundraiser.recurring
          ? 'recurring'
          : 'not_recurring',
      });
    }
    ee.emit('fundraiser:transaction_success', data, this.props.formData);
    this.setState({ errors: [] });
  }

  onError(reason: any) {
    ee.emit('fundraiser:transaction_error', reason, this.props.formData);
    this.props.setSubmitting(false);
  }

  onBraintreeError(response: any) {
    let errors;
    if (
      response.status === 422 &&
      response.responseJSON &&
      response.responseJSON.errors
    ) {
      errors = response.responseJSON.errors.map(function(error) {
        if (error.declined) {
          return <FormattedMessage id="fundraiser.card_declined" />;
        } else {
          return error.message;
        }
      });
    } else {
      errors = [<FormattedMessage id="fundraiser.unknown_error" />];
    }
    this.setState({ errors: errors });
    this.onError(response);
  }

  isExpressHidden() {
    return this.state.expressHidden || this.props.disableSavedPayments;
  }

  render() {
    const {
      member,
      hideRecurring,
      fundraiser: {
        currency,
        donationAmount,
        currentPaymentType,
        recurring,
        storeInVault,
      },
    } = this.props;

    return (

        <ShowIf condition={!isEmpty(this.state.errors)}>

              <FormattedMessage
                id="fundraiser.error_intro"
                defaultMessage="Unable to process donation!"
              />

            {this.state.errors.map((e, i) => {
              return (

                  {e}

              );
            })}

        </ShowIf>

        {!this.props.disableFormReveal && (
          <WelcomeMember
            member={member}
            resetMember={() => this.resetMember()}
          />
        )}

        <ExpressDonation
          setSubmitting={s => this.props.setSubmitting(s)}
          hidden={this.isExpressHidden()}
          onHide={() => this.setState({ expressHidden: true })}
        />

        <ShowIf condition={this.isExpressHidden()}>
          <PaymentTypeSelection
            disabled={this.state.loading}
            currentPaymentType={this.props.currentPaymentType}
            onChange={p => this.selectPaymentType(p)}
          />

          <PayPal
            ref="paypal"
            amount={donationAmount}
            currency={currency}
            client={this.state.client}
            vault={recurring || storeInVault}
            onInit={() => this.paymentInitialized('paypal')}
          />

          <BraintreeCardFields
            ref="card"
            client={this.state.client}
            recurring={recurring}
            isActive={currentPaymentType === 'card'}
            onInit={() => this.paymentInitialized('card')}
          />

          {currentPaymentType === 'paypal' && (

              <FormattedMessage
                id={'fundraiser.payment_methods.ready_for_paypal'}
              />

          )}

          {currentPaymentType === 'gocardless' && (

              <FormattedMessage
                id={'fundraiser.payment_methods.ready_for_gocardless'}
              />

          )}

          {!hideRecurring && (
            <Checkbox
              className="Payment__config"
              disabled={hideRecurring}
              checked={recurring}
              onChange={e => this.props.setRecurring(e.target.checked)}
            >
              <FormattedMessage
                id="fundraiser.make_recurring"
                defaultMessage="Make my donation monthly"
              />
            </Checkbox>
          )}

          <Checkbox
            className="Payment__config"
            checked={storeInVault}
            onChange={e => this.props.setStoreInVault(e.target.checked)}
          >
            <FormattedMessage
              id="fundraiser.store_in_vault"
              defaultMessage="Securely store my payment information"
            />
          </Checkbox>

          <DonateButton
            currency={currency}
            amount={donationAmount || 0}
            submitting={this.state.submitting}
            recurring={recurring}
            disabled={this.disableSubmit()}
            onClick={() => this.makePayment()}
          />
        </ShowIf>

          <FormattedHTMLMessage
            className="Payment__fine-print"
            id="fundraiser.fine_print"
            defaultMessage={`
              SumOfUs is a registered 501(c)4 non-profit incorporated in Washington, DC, United
              States. Contributions or gifts to SumOfUs are not tax deductible.
              For further information, please contact info@sumofus.org.
            `}
          />

        {this.props.showDirectDebit && (

        )}

    );
  }
}

const mapStateToProps = (state: AppState) => ({
  disableSavedPayments:
    state.fundraiser.disableSavedPayments || state.paymentMethods.length === 0,
  defaultPaymentType: state.fundraiser.directDebitOnly ? 'gocardless' : 'card',
  showDirectDebit: state.fundraiser.showDirectDebit,
  currentPaymentType: state.fundraiser.directDebitOnly
    ? 'gocardless'
    : state.fundraiser.currentPaymentType,
  fundraiser: state.fundraiser,
  page: state.page,
  paymentMethods: state.paymentMethods,
  member: state.member,
  hideRecurring: state.fundraiser.recurringDefault === 'only_recurring',
  formData: {
    storeInVault: state.fundraiser.storeInVault,
    member: {
      ...state.fundraiser.formValues,
      ...state.fundraiser.form,
    },
  },
  extraActionFields: state.extraActionFields,
});

const mapDispatchToProps = (dispatch: Dispatch<*>) => ({
  resetMember: () => dispatch(resetMember()),
  changeStep: (step: number) => dispatch(changeStep(step)),
  setRecurring: (value: boolean) => dispatch(setRecurring(value)),
  setStoreInVault: (value: boolean) => dispatch(setStoreInVault(value)),
  setPaymentType: (value: PaymentType) => dispatch(setPaymentType(value)),
});

export default connect(mapStateToProps, mapDispatchToProps)(Payment);
<|endoftext|>