<commit_before>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt

frappe.ui.form.on("Supplier", {
	refresh: function(frm) {
		frm.cscript.make_dashboard(frm.doc);

		if(frappe.defaults.get_default("supp_master_name")!="Naming Series") {
			frm.toggle_display("naming_series", false);
		} else {
			erpnext.toggle_naming_series();
		}

		if(frm.doc.__islocal){
	    	hide_field(['address_html','contact_html']);
			erpnext.utils.clear_address_and_contact(frm);
		}
		else {
		  	unhide_field(['address_html','contact_html']);
			erpnext.utils.render_address_and_contact(frm);
		}

		frm.events.add_custom_buttons(frm);
	},
	add_custom_buttons: function(frm) {
		["Supplier Quotation", "Purchase Order", "Purchase Receipt", "Purchase Invoice"].forEach(function(doctype, i) {
			if(frappe.model.can_read(doctype)) {
				frm.add_custom_button(__(doctype), function() {
					frappe.route_options = {"supplier": frm.doc.name};
					frappe.set_route("List", doctype);
				}, __("View"));
			}
			if(frappe.model.can_create(doctype)) {
				frm.add_custom_button(__(doctype), function() {
					frappe.route_options = {"supplier": frm.doc.name};
					new_doc(doctype);
				}, __("Make"));
			}
		});
	},
});

cur_frm.cscript.make_dashboard = function(doc) {
	cur_frm.dashboard.reset();
	if(doc.__islocal)
		return;
	if (in_list(user_roles, "Accounts User") || in_list(user_roles, "Accounts Manager"))
		cur_frm.dashboard.set_headline('' + __('Loading') + '')

	cur_frm.dashboard.add_doctype_badge("Supplier Quotation", "supplier");
	cur_frm.dashboard.add_doctype_badge("Purchase Order", "supplier");
	cur_frm.dashboard.add_doctype_badge("Purchase Receipt", "supplier");
	cur_frm.dashboard.add_doctype_badge("Purchase Invoice", "supplier");

	return frappe.call({
		type: "GET",
		method: "erpnext.buying.doctype.supplier.supplier.get_dashboard_info",
		args: {
			supplier: cur_frm.doc.name
		},
		callback: function(r) {
			if (in_list(user_roles, "Accounts User") || in_list(user_roles, "Accounts Manager")) {
				cur_frm.dashboard.set_headline(
					__("Total billing this year") + ": "
					+ format_currency(r.message.billing_this_year, cur_frm.doc.party_account_currency)
					+ ' / ' + __("Total Unpaid") + ": "
					+ format_currency(r.message.total_unpaid, cur_frm.doc.party_account_currency)
					+ '');
			}
			cur_frm.dashboard.set_badge_count(r.message);
		}
	})
}

cur_frm.fields_dict['default_price_list'].get_query = function(doc, cdt, cdn) {
	return{
		filters:{'buying': 1}
	}
}

cur_frm.fields_dict['accounts'].grid.get_field('account').get_query = function(doc, cdt, cdn) {
	var d  = locals[cdt][cdn];
	return {
		filters: {
			'account_type': 'Payable',
			'company': d.company,
			"is_group": 0
		}
	}
}

cur_frm.cscript.before_load = function(doc, dt, dn, callback) {
	var update_language_select = function(user_language) {
		cur_frm.set_df_property("print_language", "options", frappe.languages || ["", "English"]);
		callback && callback();
	}

	if(!frappe.languages) {
		frappe.call({
			method: "frappe.core.doctype.user.user.get_languages",
			callback: function(r) {
				frappe.languages = r.message.languages;
				update_language_select();
			}
		});
	} else {
		update_language_select();
	}
}
<commit_msg>removed languages filled methods, added this methods into new file<commit_after>// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt

frappe.require("assets/erpnext/js/controllers/load_languages.js")

frappe.ui.form.on("Supplier", {
	refresh: function(frm) {
		frm.cscript.make_dashboard(frm.doc);

		if(frappe.defaults.get_default("supp_master_name")!="Naming Series") {
			frm.toggle_display("naming_series", false);
		} else {
			erpnext.toggle_naming_series();
		}

		if(frm.doc.__islocal){
	    	hide_field(['address_html','contact_html']);
			erpnext.utils.clear_address_and_contact(frm);
		}
		else {
		  	unhide_field(['address_html','contact_html']);
			erpnext.utils.render_address_and_contact(frm);
		}

		frm.events.add_custom_buttons(frm);
	},
	add_custom_buttons: function(frm) {
		["Supplier Quotation", "Purchase Order", "Purchase Receipt", "Purchase Invoice"].forEach(function(doctype, i) {
			if(frappe.model.can_read(doctype)) {
				frm.add_custom_button(__(doctype), function() {
					frappe.route_options = {"supplier": frm.doc.name};
					frappe.set_route("List", doctype);
				}, __("View"));
			}
			if(frappe.model.can_create(doctype)) {
				frm.add_custom_button(__(doctype), function() {
					frappe.route_options = {"supplier": frm.doc.name};
					new_doc(doctype);
				}, __("Make"));
			}
		});
	},
});

cur_frm.cscript.make_dashboard = function(doc) {
	cur_frm.dashboard.reset();
	if(doc.__islocal)
		return;
	if (in_list(user_roles, "Accounts User") || in_list(user_roles, "Accounts Manager"))
		cur_frm.dashboard.set_headline('' + __('Loading') + '')

	cur_frm.dashboard.add_doctype_badge("Supplier Quotation", "supplier");
	cur_frm.dashboard.add_doctype_badge("Purchase Order", "supplier");
	cur_frm.dashboard.add_doctype_badge("Purchase Receipt", "supplier");
	cur_frm.dashboard.add_doctype_badge("Purchase Invoice", "supplier");

	return frappe.call({
		type: "GET",
		method: "erpnext.buying.doctype.supplier.supplier.get_dashboard_info",
		args: {
			supplier: cur_frm.doc.name
		},
		callback: function(r) {
			if (in_list(user_roles, "Accounts User") || in_list(user_roles, "Accounts Manager")) {
				cur_frm.dashboard.set_headline(
					__("Total billing this year") + ": "
					+ format_currency(r.message.billing_this_year, cur_frm.doc.party_account_currency)
					+ ' / ' + __("Total Unpaid") + ": "
					+ format_currency(r.message.total_unpaid, cur_frm.doc.party_account_currency)
					+ '');
			}
			cur_frm.dashboard.set_badge_count(r.message);
		}
	})
}

cur_frm.fields_dict['default_price_list'].get_query = function(doc, cdt, cdn) {
	return{
		filters:{'buying': 1}
	}
}

cur_frm.fields_dict['accounts'].grid.get_field('account').get_query = function(doc, cdt, cdn) {
	var d  = locals[cdt][cdn];
	return {
		filters: {
			'account_type': 'Payable',
			'company': d.company,
			"is_group": 0
		}
	}
}
<|endoftext|>


<commit_before>/* Just copy and paste this snippet into your code */

module.exports = function (event, done) {
  var MYOB = Hoist.connector("<key>");
  return MYOB.authorize("<bouncerToken>").then(function () {
    //only call this once per authorization/bucket
    return MYOB.setUsernameAndPassword('username', 'password');
  }).then(function () {
    return MYOB.get('/Contact')
      .then(function (ContactsResponse) {
        return Hoist.event.raise('CONTACTS:FOUND', {
          count: ContactsResponse.Items.length
        });
      });
  });
};
<commit_msg>explain we're using promises<commit_after>/* Just copy and paste this snippet into your code */

/* either return a promise or call done, here we're returning a promise */
module.exports = function (event, done) {
  var MYOB = Hoist.connector("<key>");
  return MYOB.authorize("<bouncerToken>").then(function () {
    //only call this once per authorization/bucket
    return MYOB.setUsernameAndPassword('username', 'password');
  }).then(function () {
    return MYOB.get('/Contact')
      .then(function (ContactsResponse) {
        return Hoist.event.raise('CONTACTS:FOUND', {
          count: ContactsResponse.Items.length
        });
      });
  });
};
<|endoftext|>


<commit_before>// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt

frappe.ui.form.on('Department', {
	refresh: function(frm) {
		// read-only for root department
		if(!frm.doc.parent_department) {
			cur_frm.set_read_only();
			cur_frm.set_intro(__("This is a root department group and cannot be edited."));
		}
	},
	validate: function(frm) {
		if(frm.doc.name=="All Departments") {
			frappe.throw(__("You cannot edit root node."));
		}
	}
});
<commit_msg>replace department group to department<commit_after>// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt

frappe.ui.form.on('Department', {
	refresh: function(frm) {
		// read-only for root department
		if(!frm.doc.parent_department) {
			cur_frm.set_read_only();
			cur_frm.set_intro(__("This is a root department and cannot be edited."));
		}
	},
	validate: function(frm) {
		if(frm.doc.name=="All Departments") {
			frappe.throw(__("You cannot edit root node."));
		}
	}
});
<|endoftext|>


<commit_before>export { ConcatGather };
export { ConcatGatherPool };

import * as Pool from "../../util/Pool.js";
import * as Recyclable from "../../util/Recyclable.js";
import { ShuffleInfo, ShuffleInfoPool } from "./ChannelShuffler_ShuffleInfo.js";

/**
 * Implement the channel shuffler by tf.concat() and tf.gather().
 *
 * When outputGroupCount is smaller (e.g. 2), this may be faster than concat-reshape-transpose-reshape-split and
 * split-concat because the total operations (and memory access) are smaller.
 *
 * The extra cost is a pre-built channel index look up table (with tensor1d).
 *
 *
 * @member {number[]} concatenatedShape
 *   (Please see ShuffleInfo explanation.)
 *
 * @member {number} outputGroupCount
 *   (Please see ShuffleInfo explanation.)
 *
 * @member {ShuffleInfo} shuffleInfo
 *   The information calculated from concatenatedShape and outputGroupCount.
 *
 * @member {tf.tensor1d[]} shuffledChannelIndicesTensor1dArray
 *   The look up table for tf.gather()'s channel index. This table is composed of tensor1d so should be released
 * by calling disposeResources().
 *
 * @member {number} tensorWeightCountExtracted
 *   The wieght count extracted from inputFloat32Array and used in tensors. Not including Params, because they are not used in
 * tensors. Not including inferenced weights (even if they are used in tensors), because they are not extracted from inputFloat32Array.
 *
 * @member {number} tensorWeightCountTotal
 *   The total wieght count used in tensors. Not including Params, because they are not used in tensors. Including inferenced
 * weights, if they are used in tensors.
 *
 * @member {function} gather
 *   Permute and split the input tensor by gather. It is a function pointer to one of this.gather_XXX().
 *
 * @member {function} concatGather
 *   Concatenate, permute and split the input tensor by concat-gather. It is a function pointer to one of
 * this.concatGather_XXX().
 */
class ConcatGather extends Recyclable.Root {

  /**
   * Used as default ChannelShuffler.ConcatGather provider for conforming to Recyclable interface.
   */
  static Pool = new Pool.Root( "ChannelShuffler.ConcatGatherPool", ConcatGather, ConcatGather.setAsConstructor );

  /**
   *
   * @param {number[]} concatenatedShape  Used to calculate shuffleInfo.
   * @param {number}   outputGroupCount   Used to calculate shuffleInfo.
   * @exception {Object} If failed (e.g. out of GPU memory).
   *
   * @see ShuffleInfo
   */
  constructor( concatenatedShape, outputGroupCount ) {
    super();
    ConcatGather.setAsConstructor_self.call( this, concatenatedShape, outputGroupCount );
  }

  /** @override */
  static setAsConstructor_self( concatenatedShape, outputGroupCount ) {

    this.tensorWeightCountExtracted = 0;
    this.tensorWeightCountTotal = 0;

    this.shuffleInfo = ShuffleInfo.Pool.get_or_create_by( concatenatedShape, outputGroupCount );

    // Build shuffled channel index table (as an array of tf.tensor1d).
    //
    // It can be used by algorithm ConcatGather().
    // They should be integers so that can be used as tf.gather()'s index.
    //
    // Not like SplitConcat, the channel indexes will not be sorted here. According to testing, sorted
    // channel seems slow down memory access when using them as tf.gather()'s index list.
    {
      this.shuffledChannelIndicesTensor1dArray
        = tf.tidy( "ChannelShuffler.ConcatGather.init.shuffledChannelIndicesTensor1dArray", () => {
          let channelIndices = tf.range( 0, this.shuffleInfo.totalChannelCount, 1, "int32" );
          return Pool.All.sessionCall( () => {
            let channelIndicesShuffleInfo = ShuffleInfo.Pool.get_or_create_by( channelIndices.shape, outputGroupCount );
            return channelIndicesShuffleInfo.reshapeTransposeReshapeSplit( channelIndices );
          });
        });

      // Calculate total weight count.
      for ( let i = 0; i < this.shuffledChannelIndicesTensor1dArray.length; ++i ) {
        let shuffledChannelIndicesTensor1d = this.shuffledChannelIndicesTensor1dArray[ i ];
        if ( shuffledChannelIndicesTensor1d ) {
          this.tensorWeightCountTotal += shuffledChannelIndicesTensor1d.size;
        }
      }

    // Exception if failed (e.g. out of (GPU) memory).
    }

    this.gather = this.gather_loop;
    this.concatGather = this.concatGather_dispose_finally_call_loop;
  }

  /** @override */
  static setAsConstructor( concatenatedShape, outputGroupCount ) {
    super.setAsConstructor();
    ConcatGather.setAsConstructor_self.call( this, concatenatedShape, outputGroupCount );
    return this;
  }

  /**
   * Release tf.tensor.
   *
   * Sub-class should override this method (and call super.disposeResources() before return).
   *
   * @override
   */
  disposeResources() {
    this.concatGather = null;
    this.gather = null;

    this.tensorWeightCountTotal = 0;
    this.tensorWeightCountExtracted = 0;

    if ( this.shuffledChannelIndicesTensor1dArray ) {
      for ( let i = 0; i < this.shuffledChannelIndicesTensor1dArray.length; ++i ) {
        tf.dispose( this.shuffledChannelIndicesTensor1dArray[ i ] );
        this.shuffledChannelIndicesTensor1dArray[ i ] = null;
      }

//!!! (2022/06/25 Remarked) Now only Recyclable.Array could be recycled.
//      // Note: This array is generated by tensorflow.js (i.e. not by Pool.Array). Although it could be recycled by Pool.Array,
//      //       however, this could result in more and more array being cumulated (and then out of memory). So do not recycle it.
//      //
//      //Pool.Array.Singleton.recycle( this.shuffledChannelIndicesTensor1dArray );

      this.shuffledChannelIndicesTensor1dArray = null;
    }

    if ( this.shuffleInfo ) {
      this.shuffleInfo.disposeResources_and_recycleToPool();
      this.shuffleInfo = null;
    }

    super.disposeResources();
  }

  get concatenatedShape() {
    return this.shuffleInfo.concatenatedShape;
  }

  get outputGroupCount() {
    return this.shuffleInfo.outputGroupCount;
  }

  /**
   * Permute and split the input tensor by gather.
   *
   * @param {tf.tensor} concatenatedTensor
   *   An single tensor (not array) to be processed. It should conform to this.shuffleInfo.concatenatedShape.
   *
   * @return {tf.tensor[]}
   *   An array of shuffled tensors. Their total channel count is the same as concatenated tensorArray, but their
   * last dimensions are shuffled.
   */
  gather_loop( concatenatedTensor ) {
    let shuffledSplitedTensorArray = new Array( this.shuffledChannelIndicesTensor1dArray.length );
    for ( let i = 0; i < shuffledSplitedTensorArray.length; ++i ) {
      // shuffle and split by gather (one operation achieves two operations).
      shuffledSplitedTensorArray[ i ] = concatenatedTensor.gather( this.shuffledChannelIndicesTensor1dArray[ i ], this.shuffleInfo.lastAxisId );
    }
    return shuffledSplitedTensorArray;
  }

  /**
   * Concatenate, permute and split the input tensor by concat-gather.
   *
   * @param {tf.tensor[]} tensorArray
   *   An array of tensors to be processed. It should conform to this.concatenatedShape.
   *
   * @return {tf.tensor[]}
   *   An array of shuffled tensors. Their total channel count is the same as concatenated tensorArray, but their
   * last dimensions are shuffled.
   */
  concatGather_dispose_finally_call_loop( tensorArray ) {
    let concatenatedTensor = tf.concat( tensorArray, this.shuffleInfo.lastAxisId );

    try {
      return this.gather_loop( concatenatedTensor );

    } finally {
      concatenatedTensor.dispose();
    }
  }
}

<commit_msg>Update ChannelShuffler_ConcatGather.js<commit_after>export { ConcatGather };

import * as Pool from "../../util/Pool.js";
import * as Recyclable from "../../util/Recyclable.js";
import { ShuffleInfo } from "./ChannelShuffler_ShuffleInfo.js";

/**
 * Implement the channel shuffler by tf.concat() and tf.gather().
 *
 * When outputGroupCount is smaller (e.g. 2), this may be faster than concat-reshape-transpose-reshape-split and
 * split-concat because the total operations (and memory access) are smaller.
 *
 * The extra cost is a pre-built channel index look up table (with tensor1d).
 *
 *
 * @member {number[]} concatenatedShape
 *   (Please see ShuffleInfo explanation.)
 *
 * @member {number} outputGroupCount
 *   (Please see ShuffleInfo explanation.)
 *
 * @member {ShuffleInfo} shuffleInfo
 *   The information calculated from concatenatedShape and outputGroupCount.
 *
 * @member {tf.tensor1d[]} shuffledChannelIndicesTensor1dArray
 *   The look up table for tf.gather()'s channel index. This table is composed of tensor1d so should be released
 * by calling disposeResources().
 *
 * @member {number} tensorWeightCountExtracted
 *   The wieght count extracted from inputFloat32Array and used in tensors. Not including Params, because they are not used in
 * tensors. Not including inferenced weights (even if they are used in tensors), because they are not extracted from inputFloat32Array.
 *
 * @member {number} tensorWeightCountTotal
 *   The total wieght count used in tensors. Not including Params, because they are not used in tensors. Including inferenced
 * weights, if they are used in tensors.
 *
 * @member {function} gather
 *   Permute and split the input tensor by gather. It is a function pointer to one of this.gather_XXX().
 *
 * @member {function} concatGather
 *   Concatenate, permute and split the input tensor by concat-gather. It is a function pointer to one of
 * this.concatGather_XXX().
 */
class ConcatGather extends Recyclable.Root {

  /**
   * Used as default ChannelShuffler.ConcatGather provider for conforming to Recyclable interface.
   */
  static Pool = new Pool.Root( "ChannelShuffler.ConcatGatherPool", ConcatGather, ConcatGather.setAsConstructor );

  /**
   *
   * @param {number[]} concatenatedShape  Used to calculate shuffleInfo.
   * @param {number}   outputGroupCount   Used to calculate shuffleInfo.
   * @exception {Object} If failed (e.g. out of GPU memory).
   *
   * @see ShuffleInfo
   */
  constructor( concatenatedShape, outputGroupCount ) {
    super();
    ConcatGather.setAsConstructor_self.call( this, concatenatedShape, outputGroupCount );
  }

  /** @override */
  static setAsConstructor_self( concatenatedShape, outputGroupCount ) {

    this.tensorWeightCountExtracted = 0;
    this.tensorWeightCountTotal = 0;

    this.shuffleInfo = ShuffleInfo.Pool.get_or_create_by( concatenatedShape, outputGroupCount );

    // Build shuffled channel index table (as an array of tf.tensor1d).
    //
    // It can be used by algorithm ConcatGather().
    // They should be integers so that can be used as tf.gather()'s index.
    //
    // Not like SplitConcat, the channel indexes will not be sorted here. According to testing, sorted
    // channel seems slow down memory access when using them as tf.gather()'s index list.
    {
      this.shuffledChannelIndicesTensor1dArray
        = tf.tidy( "ChannelShuffler.ConcatGather.init.shuffledChannelIndicesTensor1dArray", () => {
          let channelIndices = tf.range( 0, this.shuffleInfo.totalChannelCount, 1, "int32" );
          return Pool.All.sessionCall( () => {
            let channelIndicesShuffleInfo = ShuffleInfo.Pool.get_or_create_by( channelIndices.shape, outputGroupCount );
            return channelIndicesShuffleInfo.reshapeTransposeReshapeSplit( channelIndices );
          });
        });

      // Calculate total weight count.
      for ( let i = 0; i < this.shuffledChannelIndicesTensor1dArray.length; ++i ) {
        let shuffledChannelIndicesTensor1d = this.shuffledChannelIndicesTensor1dArray[ i ];
        if ( shuffledChannelIndicesTensor1d ) {
          this.tensorWeightCountTotal += shuffledChannelIndicesTensor1d.size;
        }
      }

    // Exception if failed (e.g. out of (GPU) memory).
    }

    this.gather = this.gather_loop;
    this.concatGather = this.concatGather_dispose_finally_call_loop;
  }

  /** @override */
  static setAsConstructor( concatenatedShape, outputGroupCount ) {
    super.setAsConstructor();
    ConcatGather.setAsConstructor_self.call( this, concatenatedShape, outputGroupCount );
    return this;
  }

  /**
   * Release tf.tensor.
   *
   * Sub-class should override this method (and call super.disposeResources() before return).
   *
   * @override
   */
  disposeResources() {
    this.concatGather = null;
    this.gather = null;

    this.tensorWeightCountTotal = 0;
    this.tensorWeightCountExtracted = 0;

    if ( this.shuffledChannelIndicesTensor1dArray ) {
      for ( let i = 0; i < this.shuffledChannelIndicesTensor1dArray.length; ++i ) {
        tf.dispose( this.shuffledChannelIndicesTensor1dArray[ i ] );
        this.shuffledChannelIndicesTensor1dArray[ i ] = null;
      }

//!!! (2022/06/25 Remarked) Now only Recyclable.Array could be recycled.
//      // Note: This array is generated by tensorflow.js (i.e. not by Pool.Array). Although it could be recycled by Pool.Array,
//      //       however, this could result in more and more array being cumulated (and then out of memory). So do not recycle it.
//      //
//      //Pool.Array.Singleton.recycle( this.shuffledChannelIndicesTensor1dArray );

      this.shuffledChannelIndicesTensor1dArray = null;
    }

    if ( this.shuffleInfo ) {
      this.shuffleInfo.disposeResources_and_recycleToPool();
      this.shuffleInfo = null;
    }

    super.disposeResources();
  }

  get concatenatedShape() {
    return this.shuffleInfo.concatenatedShape;
  }

  get outputGroupCount() {
    return this.shuffleInfo.outputGroupCount;
  }

  /**
   * Permute and split the input tensor by gather.
   *
   * @param {tf.tensor} concatenatedTensor
   *   An single tensor (not array) to be processed. It should conform to this.shuffleInfo.concatenatedShape.
   *
   * @return {tf.tensor[]}
   *   An array of shuffled tensors. Their total channel count is the same as concatenated tensorArray, but their
   * last dimensions are shuffled.
   */
  gather_loop( concatenatedTensor ) {
    let shuffledSplitedTensorArray = new Array( this.shuffledChannelIndicesTensor1dArray.length );
    for ( let i = 0; i < shuffledSplitedTensorArray.length; ++i ) {
      // shuffle and split by gather (one operation achieves two operations).
      shuffledSplitedTensorArray[ i ] = concatenatedTensor.gather( this.shuffledChannelIndicesTensor1dArray[ i ], this.shuffleInfo.lastAxisId );
    }
    return shuffledSplitedTensorArray;
  }

  /**
   * Concatenate, permute and split the input tensor by concat-gather.
   *
   * @param {tf.tensor[]} tensorArray
   *   An array of tensors to be processed. It should conform to this.concatenatedShape.
   *
   * @return {tf.tensor[]}
   *   An array of shuffled tensors. Their total channel count is the same as concatenated tensorArray, but their
   * last dimensions are shuffled.
   */
  concatGather_dispose_finally_call_loop( tensorArray ) {
    let concatenatedTensor = tf.concat( tensorArray, this.shuffleInfo.lastAxisId );

    try {
      return this.gather_loop( concatenatedTensor );

    } finally {
      concatenatedTensor.dispose();
    }
  }
}

<|endoftext|>


<commit_before>import React from 'react'
import { Field, FieldArray, reduxForm } from 'redux-form'
import validate from './validate'

const renderField = ({ input, label, type, meta: { touched, error } }) => (

    {label}

      <input {...input} type={type} placeholder={label}/>
      {touched && error && {error}}

)

const renderMembers = ({ fields, meta: { touched, error, submitFailed } }) => (

      <button type="button" onClick={() => fields.push({})}>Add Member</button>
      {(touched || submitFailed) && error && {error}}

    {fields.map((member, index) =>

        <button
          type="button"
          title="Remove Member"
          onClick={() => fields.remove(index)}/>

Member #{index + 1}

        <Field
          name={`${member}.firstName`}
          type="text"
          component={renderField}
          label="First Name"/>
        <Field
          name={`${member}.lastName`}
          type="text"
          component={renderField}
          label="Last Name"/>
        <FieldArray name={`${member}.hobbies`} component={renderHobbies}/>

    )}

)

const renderHobbies = ({ fields, meta: { error } }) => (

      <button type="button" onClick={() => fields.push()}>Add Hobby</button>

    {fields.map((hobby, index) =>

        <button
          type="button"
          title="Remove Hobby"
          onClick={() => fields.remove(index)}/>
        <Field
          name={hobby}
          type="text"
          component={renderField}
          label={`Hobby #${index + 1}`}/>

    )}
    {error &&
{error}
}

)

const FieldArraysForm = (props) => {
  const { handleSubmit, pristine, reset, submitting } = props
  return (
    <form onSubmit={handleSubmit}>
      <Field name="clubName" type="text" component={renderField} label="Club Name"/>
      <FieldArray name="members" component={renderMembers}/>

        <button type="submit" disabled={submitting}>Submit</button>
        <button type="button" disabled={pristine || submitting} onClick={reset}>Clear Values</button>

    </form>
  )
}

export default reduxForm({
  form: 'fieldArrays',     // a unique identifier for this form
  validate
})(FieldArraysForm)
<commit_msg>Fix 'renderHobbies' was used before it was defined eslint error (#2750)<commit_after>import React from 'react'
import { Field, FieldArray, reduxForm } from 'redux-form'
import validate from './validate'

const renderField = ({ input, label, type, meta: { touched, error } }) => (

    {label}

      <input {...input} type={type} placeholder={label}/>
      {touched && error && {error}}

)

const renderHobbies = ({ fields, meta: { error } }) => (

      <button type="button" onClick={() => fields.push()}>Add Hobby</button>

    {fields.map((hobby, index) =>

        <button
          type="button"
          title="Remove Hobby"
          onClick={() => fields.remove(index)}/>
        <Field
          name={hobby}
          type="text"
          component={renderField}
          label={`Hobby #${index + 1}`}/>

    )}
    {error &&
{error}
}

)

const renderMembers = ({ fields, meta: { touched, error, submitFailed } }) => (

      <button type="button" onClick={() => fields.push({})}>Add Member</button>
      {(touched || submitFailed) && error && {error}}

    {fields.map((member, index) =>

        <button
          type="button"
          title="Remove Member"
          onClick={() => fields.remove(index)}/>

Member #{index + 1}

        <Field
          name={`${member}.firstName`}
          type="text"
          component={renderField}
          label="First Name"/>
        <Field
          name={`${member}.lastName`}
          type="text"
          component={renderField}
          label="Last Name"/>
        <FieldArray name={`${member}.hobbies`} component={renderHobbies}/>

    )}

)

const FieldArraysForm = (props) => {
  const { handleSubmit, pristine, reset, submitting } = props
  return (
    <form onSubmit={handleSubmit}>
      <Field name="clubName" type="text" component={renderField} label="Club Name"/>
      <FieldArray name="members" component={renderMembers}/>

        <button type="submit" disabled={submitting}>Submit</button>
        <button type="button" disabled={pristine || submitting} onClick={reset}>Clear Values</button>

    </form>
  )
}

export default reduxForm({
  form: 'fieldArrays',     // a unique identifier for this form
  validate
})(FieldArraysForm)
<|endoftext|>


<commit_before>export { PromiseResolveReject };
export { processingId_PromiseResolveRejectArray_Map };

//!!! (2022/09/10 Remarked) replaced by AsyncIterator.
//export { ProcessRelayPromises };

/**
 * Hold a processing's id, promise, resolve (fulfilling function object), reject
 * (rejecting function object).
 *
 * @member {number} processingId
 *   The id of the processing.
 *
 * @member {boolean} bPending
 *   If true, the promise is still pending. If false, the promise is fulfilled.
 *
 * @member {Promise} promise
 *   The pending promise for the processing.
 *
 */
 class PromiseResolveReject {

  constructor( processingId ) {
    this.processingId = processingId;
    this.bPending = true;

    this.promise = new Promise( ( resolve, reject ) => {
      this.resolve_internal = resolve;
      this.reject_internal = reject;
    });
  }

  /** Resolve the pending promise for the processing. */
  resolve( value ) {
    if ( !this.bPending )
      return; // A fulfilled promise can not be changed again.
    this.resolve_internal( value );
    this.bPending = fasle;
  }

  /** Reject the pending promise for the processing. */
  reject( value ) {
    if ( !this.bPending )
      return; // A fulfilled promise can not be changed again.
    this.reject_internal( value );
    this.bPending = fasle;
  }

}

/**
 *
 *
 * @member {PromiseResolveReject[]} array
 *   All promises for resulter to yield/return.
 *
 * @member {AsyncGenerator} resulter
 *   The result sync generator for passing result of WorkerBody to WorkerProxy's caller.
 */
class PromiseResolveRejectArray {

  /** */
  constructor( resulter ) {
    this.resulter = resulter;
    this.array = new Array();
  }

}

/**
 * A collection PromiseResolveReject[] by processingId as key.
 */
class processingId_PromiseResolveRejectArray_Map {

  /** */
  constructor() {
    this.map = new Map();
  }

  /**
   * Create a new PromiseResolveRejectArray and Resulter. Record them in this map
   * by processingId as key.
   *
   * This method is the initializer the processingId's PromiseResolveRejectArray.
   *
   * @param {number} processingId
   *   The numeric identifier for the processing.
   *
   * @return {AsyncGenerator}
   *   Return the created Resulter() object.
   */
  resulter_PromiseResolveRejectArray_create_by_processingId( processingId ) {
    let resulter = processingId_PromiseResolveRejectArray_Map.Resulter( processingId );

    thePromiseResolveRejectArray = new PromiseResolveRejectArray( resulter );
    this.map.set( processingId, thePromiseResolveRejectArray );

    let thePromiseResolveReject = new PromiseResolveReject( processingId );
    thePromiseResolveRejectArray.array.push( thePromiseResolveReject );

    return resulter;
  }

//!!! (2022/09/12 Remarked) should be done by Resulter.
//   /**
//    * Remove the processingId's result array.
//    *
//    * @param {number} processingId
//    *   The numeric identifier for the removing from this Map.
//    *
//    * @return {boolean}
//    *   Return true, if processingId existed and has been removed. Return false,
//    * if processingId does not exist.
//    */
//   PromiseResolveRejectArray_remove_by_processingId( processingId ) {
//     return this.map.delete( processingId );
//   }

  /**
   * Suppose every method function of WorkerProxy is an async generator.
   *
   * When WorkerProxy receives { done, value } object from WorkerBody, please call
   * this method. It will resolve the corresponding pending promise by
   * PromiseResolveReject.resolve( value ). And the,
   *
   *   - if ( done == false ), create a new PromiseResolveReject placed at the
   *       same position (i.e. replace old one) for waiting future result from
   *       web worker.
   *
   *   - if ( done == true ), remove the PromiseResolveReject of processingId from
   *       this map because the processing will have no more result coming from
   *       web worker in the future.
   *
   *
   * This method is the producer the processingId's PromiseResolveRejectArray.
   *
   */
  resolve_by_processingId_done_value( processingId, done, value ) {
    let thePromiseResolveRejectArray = this.map.get( processingId );
    if ( !thePromiseResolveRejectArray )
      return; // The pending promise does not exist.

    if ( thePromiseResolveRejectArray.array.length <= 0 )
      return; // The pending promise does not exist.

    // Always resolve the last promise. (Assume it is pending.)
    let thePromiseResolveReject = thePromiseResolveRejectArray.array[
      thePromiseResolveRejectArray.array.length - 1 ];

    // 1. Resolve the pending promise to the specified value.
    thePromiseResolveReject.resolve( value );

    // 2. Prepare new pending promise.

    // 2.1 The web worker says the processing is not yet completed, create a new
    //     pending promise for the same processing for waiting future result from
    //     web worker.
    if ( !done ) {
      let thePromiseResolveRejectNext = new PromiseResolveReject( processingId );
      thePromiseResolveRejectArray.push( thePromiseResolveRejectNext );

    // 2.2 Since web worker says the processing is done, do not create any more
    //     pending promise because the processing will have no more result coming
    //     from web worker in the future.
    } else {

//!!! ...unfinished... (2022/09/12)
// Perhaps, should call AsyncGenerator.return() to end the result?
// Problem: What if the resulter still has some promise not yet yield?

    }

//!!! (2022/09/12 Remarked) Old Codes. The delete should be done by resulter.
//     // 2. Remove or create new pending promise.
//
//     // 2.1 Since web worker says the processing is done, remove the pending promise
//     //     because the processing will have no more result coming from web worker
//     //     in the future.
//     if ( done ) {
//       this.map.delete( processingId );
//
//     // 2.2 The web worker says the processing is not yet completed, create a new
//     //     pending promise for the same processing for waiting future result from
//     //     web worker.
//     } else {
//       this.set_new_by_processingId( processingId );
//     }
  }

  /**
   * This async generator could be used as the processing method's result.
   *
   * This async generator is the consumer of the processingId's
   * PromiseResolveRejectArray.
   *
   * @return {AsyncGenerator}
   *   An async generator. Its .next() returns a promise resolved to { done, value }.
   * The value will be:
   *
   *   - The resolved value of the processingId's PromiseResolveReject.promise, if
   *       the processingId's PromiseResolveReject exists.
   *       (i.e. { done: false, value: resolved_PromiseResolveReject.promise } )
   *
   *   - undefined, if the corresponding PromiseResolveReject does not exist.
   *       (i.e. { done: true, value: undefined } )
   *
   */
  async *Resulter( processingId ) {

    try {

      let thePromiseResolveRejectArray = this.map.get( processingId );
      if ( !thePromiseResolveRejectArray )
        return; // No pending promise for the processing.

      if ( thePromiseResolveRejectArray.length <= 0 ) {
        return; // No more pending promise for the processing.
      }

//!!! ...unfinished... (2022/09/12)
      while ( ??? ) {

        // Always yield the first promise.
        let thePromiseResolveReject = thePromiseResolveRejectArray[ 0 ];

        // If the promise has been fulfilled, remove it so that it will not be
        // yielded again in the future.
        if ( !thePromiseResolveReject.bPending ) {
          thePromiseResolveRejectArray.shift();

        // Otherwise, the first pending promise will be yielded again and again
        // (until it has been fulfilled).
        }

        yield thePromiseResolveReject.promise;
      }

    } finally {
      // When this resulter return, it means no more pending promise for the
      // processing. So remove the PromiseResolveRejectArray of the processing.
      this.map.delete( processingId );
    }

  }

}

//!!! (2022/09/10 Remarked) replaced by AsyncIterator.
// /**
//  * Hold two PromiseResolveReject.
//  *
//  * @member {PromiseResolveReject} process
//  *   The promise for reporting worker's function execution done.
//  *
//  * @member {PromiseResolveReject} relay
//  *   The promise for reporting scaledSourceImageData received from this web worker.
//  */
// class ProcessRelayPromises {
//
//   constructor( processingId, workerId ) {
//     this.process = new PromiseResolveReject( processingId, workerId );
//     this.relay = new PromiseResolveReject( processingId, workerId );
//   }
//
// }

<commit_msg>Improve comments.<commit_after>export { PromiseResolveReject };
export { processingId_PromiseResolveRejectArray_Map };

//!!! (2022/09/10 Remarked) replaced by AsyncIterator.
//export { ProcessRelayPromises };

/**
 * Hold a processing's id, promise, resolve (fulfilling function object), reject
 * (rejecting function object).
 *
 * @member {number} processingId
 *   The id of the processing.
 *
 * @member {boolean} bPending
 *   If true, the promise is still pending. If false, the promise is fulfilled.
 *
 * @member {Promise} promise
 *   The pending promise for the processing.
 *
 */
 class PromiseResolveReject {

  constructor( processingId ) {
    this.processingId = processingId;
    this.bPending = true;

    this.promise = new Promise( ( resolve, reject ) => {
      this.resolve_internal = resolve;
      this.reject_internal = reject;
    });
  }

  /** Resolve the pending promise for the processing. */
  resolve( value ) {
    if ( !this.bPending )
      return; // A fulfilled promise can not be changed again.
    this.resolve_internal( value );
    this.bPending = fasle;
  }

  /** Reject the pending promise for the processing. */
  reject( value ) {
    if ( !this.bPending )
      return; // A fulfilled promise can not be changed again.
    this.reject_internal( value );
    this.bPending = fasle;
  }

}

/**
 *
 *
 * @member {PromiseResolveReject[]} array
 *   All promises for resulter to yield/return.
 *
 * @member {AsyncGenerator} resulter
 *   The result sync generator for passing result of WorkerBody to WorkerProxy's caller.
 */
class PromiseResolveRejectArray {

  /** */
  constructor( resulter ) {
    this.resulter = resulter;
    this.array = new Array();
  }

}

/**
 * A collection PromiseResolveReject[] by processingId as key.
 */
class processingId_PromiseResolveRejectArray_Map {

  /** */
  constructor() {
    this.map = new Map();
  }

  /**
   * Create a new PromiseResolveRejectArray and Resulter. Record them in this map
   * by processingId as key.
   *
   * This method is the initializer the processingId's PromiseResolveRejectArray.
   *
   * @param {number} processingId
   *   The numeric identifier for the processing.
   *
   * @return {AsyncGenerator}
   *   Return the created Resulter() object.
   */
  resulter_PromiseResolveRejectArray_create_by_processingId( processingId ) {
    let resulter = processingId_PromiseResolveRejectArray_Map.Resulter( processingId );

    thePromiseResolveRejectArray = new PromiseResolveRejectArray( resulter );
    this.map.set( processingId, thePromiseResolveRejectArray );

    let thePromiseResolveReject = new PromiseResolveReject( processingId );
    thePromiseResolveRejectArray.array.push( thePromiseResolveReject );

    return resulter;
  }

//!!! (2022/09/12 Remarked) should be done by Resulter.
//   /**
//    * Remove the processingId's result array.
//    *
//    * @param {number} processingId
//    *   The numeric identifier for the removing from this Map.
//    *
//    * @return {boolean}
//    *   Return true, if processingId existed and has been removed. Return false,
//    * if processingId does not exist.
//    */
//   PromiseResolveRejectArray_remove_by_processingId( processingId ) {
//     return this.map.delete( processingId );
//   }

  /**
   * Suppose every method function of WorkerProxy is an async generator.
   *
   * When WorkerProxy receives { done, value } object from WorkerBody, please call
   * this method. It will resolve the corresponding pending promise by
   * PromiseResolveReject.resolve( value ). And the,
   *
   *   - if ( done == false ), create a new PromiseResolveReject placed at the
   *       same position (i.e. replace old one) for waiting future result from
   *       web worker.
   *
   *   - if ( done == true ), remove the PromiseResolveReject of processingId from
   *       this map because the processing will have no more result coming from
   *       web worker in the future.
   *
   *
   * This method is the producer the processingId's PromiseResolveRejectArray.
   *
   */
  resolve_by_processingId_done_value( processingId, done, value ) {
    let thePromiseResolveRejectArray = this.map.get( processingId );
    if ( !thePromiseResolveRejectArray )
      return; // The pending promise does not exist.

    if ( thePromiseResolveRejectArray.array.length <= 0 )
      return; // The pending promise does not exist.

    // Always resolve the last promise. (Assume it is pending.)
    let thePromiseResolveReject = thePromiseResolveRejectArray.array[
      thePromiseResolveRejectArray.array.length - 1 ];

    // 1. Resolve the pending promise to the specified value.
    thePromiseResolveReject.resolve( value );

    // 2. Prepare new pending promise.

    // 2.1 The web worker says the processing is not yet completed, create a new
    //     pending promise for the same processing for waiting future result from
    //     web worker.
    if ( !done ) {
      let thePromiseResolveRejectNext = new PromiseResolveReject( processingId );
      thePromiseResolveRejectArray.push( thePromiseResolveRejectNext );

    // 2.2 Since web worker says the processing is done, do not create any more
    //     pending promise because the processing will have no more result coming
    //     from web worker in the future.
    } else {

//!!! ...unfinished... (2022/09/12)
// Perhaps, should call AsyncGenerator.return() to end the resulter?
// Problem: What if the resulter still has some promise not yet yield?
//
// If the resulter has yielded the last pending promise, call AsyncGenerator.return()
// to end the resulter. Otherwise, place the resolved promised at the end so
// the resulter could return it.
//
// Problem: How to know resulter has yielded which promise?
// Problem: The resulter how to know a promise is the final promise?

    }

//!!! (2022/09/12 Remarked) Old Codes. The delete should be done by resulter.
//     // 2. Remove or create new pending promise.
//
//     // 2.1 Since web worker says the processing is done, remove the pending promise
//     //     because the processing will have no more result coming from web worker
//     //     in the future.
//     if ( done ) {
//       this.map.delete( processingId );
//
//     // 2.2 The web worker says the processing is not yet completed, create a new
//     //     pending promise for the same processing for waiting future result from
//     //     web worker.
//     } else {
//       this.set_new_by_processingId( processingId );
//     }
  }

  /**
   * This async generator could be used as the processing method's result.
   *
   * This async generator is the consumer of the processingId's
   * PromiseResolveRejectArray.
   *
   * @return {AsyncGenerator}
   *   An async generator. Its .next() returns a promise resolved to { done, value }.
   * The value will be:
   *
   *   - The resolved value of the processingId's PromiseResolveReject.promise, if
   *       the processingId's PromiseResolveReject exists.
   *       (i.e. { done: false, value: resolved_PromiseResolveReject.promise } )
   *
   *   - undefined, if the corresponding PromiseResolveReject does not exist.
   *       (i.e. { done: true, value: undefined } )
   *
   */
  async *Resulter( processingId ) {

    try {

      let thePromiseResolveRejectArray = this.map.get( processingId );
      if ( !thePromiseResolveRejectArray )
        return; // No pending promise for the processing.

      if ( thePromiseResolveRejectArray.length <= 0 ) {
        return; // No more pending promise for the processing.
      }

//!!! ...unfinished... (2022/09/12)
      while ( ??? ) {

        // Always yield the first promise.
        let thePromiseResolveReject = thePromiseResolveRejectArray[ 0 ];

        // If the promise has been fulfilled, remove it so that it will not be
        // yielded again in the future.
        if ( !thePromiseResolveReject.bPending ) {
          thePromiseResolveRejectArray.shift();

        // Otherwise, the first pending promise will be yielded again and again
        // (until it has been fulfilled).
        }

        yield thePromiseResolveReject.promise;
      }

    } finally {
      // When this resulter return, it means no more pending promise for the
      // processing. So remove the PromiseResolveRejectArray of the processing.
      this.map.delete( processingId );
    }

  }

}

//!!! (2022/09/10 Remarked) replaced by AsyncIterator.
// /**
//  * Hold two PromiseResolveReject.
//  *
//  * @member {PromiseResolveReject} process
//  *   The promise for reporting worker's function execution done.
//  *
//  * @member {PromiseResolveReject} relay
//  *   The promise for reporting scaledSourceImageData received from this web worker.
//  */
// class ProcessRelayPromises {
//
//   constructor( processingId, workerId ) {
//     this.process = new PromiseResolveReject( processingId, workerId );
//     this.relay = new PromiseResolveReject( processingId, workerId );
//   }
//
// }

<|endoftext|>


<commit_before>crossPlatformShapes.pathCalculator.triangle = function(data){
  'use strict';

  var x = data.x,
    y = data.y,
    width = data.width,
    height = data.height;

  // TODO: the commands below are just a copy of the commands for rectangle.
  // We need to convert the commented out pathData below to the HTML5 Canvas path command format.
  var pathData = [{command: 'moveTo', points: [x, y]},
    {command: 'lineTo', points: [(x + width), y]},
    {command: 'lineTo', points: [(x + width), (y + height)]},
    {command: 'lineTo', points: [(x), (y + height)]},
    {command: 'closePath', points: []}];

    /*
  var pathData =  'M' + x + ',' + y +
        'L' + (x + width) + ',' + (y + height/2) +
        'L' + x + ',' + (y + height) +
        'z';
  //*/
  return pathData;
};
<commit_msg>adds commands for triangle<commit_after>crossPlatformShapes.pathCalculator.triangle = function(data){
  'use strict';

  var x = data.x,
    y = data.y,
    width = data.width,
    height = data.height;

  // TODO: the commands below are just a copy of the commands for rectangle.
  // We need to convert the commented out pathData below to the HTML5 Canvas path command format.

/*/
  var pathData = [{command: 'moveTo', points: [x, y]},
    {command: 'lineTo', points: [(x + width), y]},
    {command: 'lineTo', points: [(x + width), (y + height)]},
    {command: 'lineTo', points: [(x), (y + height)]},
    {command: 'closePath', points: []}];
//*/

  var pathData = [{command: 'moveTo', points: [x, y]},
        {command: 'lineTo', points: [(x + width), (y + height/2)]},
        {command: 'lineTo', points: [x, (y + height)]},
        {command: 'closePath', points: []}];

  return pathData;
};
<|endoftext|>


<commit_before>/**
 *
 * jspsych-nAFC-circle
 * Judy Fan
 *
 * displays a target image at center surrounded by several unique images
 * positioned equidistant from the target.
 * participant's goal is to click on the surround image that best matches the target.
 *
 *
 * requires Snap.svg library (snapsvg.io)
 *
 * documentation: docs.jspsych.org || TBD
 *
 **/

socket = io.connect();

var start_time;

jsPsych.plugins["nAFC-circle"] = (function() {

  var plugin = {};

  plugin.info = {
    name: 'nAFC-circle',
    parameters: {
    }
  }

  plugin.trial = function(display_element, trial) {

    // screen information
    var screenw = $(display_element).width();
    var screenh = $(display_element).height();
    var centerx = screenw / 2;
    var centery = screenh / 2;

    // // initialize start_time timestamp
    // var start_time = Date.now();

    // grid params
    var num_cells = 6;
    var paper_size = trial.grid_size;
    var cell_size = Math.floor(trial.grid_size / num_cells);

    // stimuli width, height
    var stimh = trial.object_size[0];
    var stimw = trial.object_size[1];
    var hstimh = stimh / 2;
    var hstimw = stimw / 2;

    // sketch location
    var fix_loc = [Math.floor(paper_size / 2 - trial.sketch_size[0] / 4), Math.floor(paper_size / 2 - trial.sketch_size[1] / 4)];

    // possible stimulus locations on the circle
    var display_locs = [];

    function get_grid_coords(xrange,yrange) {
      var coords = [];
      for (var i = 0; i < xrange.length; i++) {
        for (var j = 0; j < yrange.length; j++) {
          coords.push([xrange[i],yrange[j]]);
        }
      }
      return coords;
    }

    function remove_one_coord(coords,excluded) {
      var tmp = [];
      for (var i = 0; i < coords.length; i++) {
        if (!(coords[i][0] == excluded[0] && coords[i][1] == excluded[1])) {
          tmp.push(coords[i]);
        }
      }
      return tmp;
    }

    // top left group
    xrange = _.map(_.range(0,cell_size*3,cell_size), function(x) {return x + hstimw});
    yrange = _.map(_.range(0,cell_size*3,cell_size), function(x) {return x + hstimw});
    excluded = [Math.max.apply(null,xrange),Math.max.apply(null,yrange)];
    coords = get_grid_coords(xrange,yrange);
    coordsTL = remove_one_coord(coords,excluded);

    // top right
    xrange = _.map(_.range(cell_size*3,cell_size*6,cell_size), function(x) {return x + hstimw});
    yrange = _.map(_.range(0,cell_size*3,cell_size), function(x) {return x + hstimw});
    excluded = [Math.min.apply(null,xrange),Math.max.apply(null,yrange)];
    coords = get_grid_coords(xrange,yrange);
    coordsTR = remove_one_coord(coords,excluded);

    // bottom left
    xrange = _.map(_.range(0,cell_size*3,cell_size), function(x) {return x + hstimw});
    yrange = _.map(_.range(cell_size*3,cell_size*6,cell_size), function(x) {return x + hstimw});
    excluded = [Math.max.apply(null,xrange),Math.min.apply(null,yrange)];
    coords = get_grid_coords(xrange,yrange);
    coordsBL = remove_one_coord(coords,excluded);

    // bottom right
    xrange = _.map(_.range(cell_size*3,cell_size*6,cell_size), function(x) {return x + hstimw});
    yrange = _.map(_.range(cell_size*3,cell_size*6,cell_size), function(x) {return x + hstimw});
    excluded = [Math.min.apply(null,xrange),Math.min.apply(null,yrange)];
    coords = get_grid_coords(xrange,yrange);
    coordsBR = remove_one_coord(coords,excluded);

    var display_locs = coordsTL.concat(coordsTR).concat(coordsBL).concat(coordsBR);

    // get target to draw on
    var bound = parseFloat(paper_size) + 80;
    display_element.innerHTML = '<svg id="jspsych-nAFC-circle-svg" width=' + bound + ' height=' + bound + '></svg> ';
    var paper = Snap('#jspsych-nAFC-circle-svg');

    // wait
    setTimeout(function() {show_object_array(); }, trial.timing_objects);

    function show_sketch() {
      // show sketch
      var sketch = paper.image(trial.sketch, fix_loc[0], fix_loc[1], trial.sketch_size[0], trial.sketch_size[1]);
      start_time = Date.now();
    }

    function show_object_array() {
      var object_array_images = [];
    	img = new Array;
    	for (var i = 0; i < display_locs.length; i++) {
          var img = paper.image(trial.options[i], display_locs[i][0], display_locs[i][1],
				trial.object_size[0], trial.object_size[1]);
        object_array_images.push(img);
      }
      var trial_over = false;

      // group object images and add hover animation
      images = paper.g(paper.selectAll('image'));
      images.selectAll('image').forEach( function( el, index ) {
         el.hover( function() { el.animate({ transform: 's1.5,1.5' }, 100, mina.easein); },
                   function() { el.animate({ transform: 's1,1' }, 100 , mina.easein); }
          )
      } );

      // add click listener to the objects
      var a = document.getElementsByTagName('g')[0];
      imgs = a.children;

      for (var i = 0; i < display_locs.length; i++) {
        imgs[i].addEventListener('click', function (e) {
          var choice = e.currentTarget.getAttribute('href'); // don't use dataset for jsdom compatibility
          after_response(choice);
         })
      }

      var after_response = function(choice) {
        trial_over = true;
        // measure rt
        var end_time = Date.now();
        var rt = end_time - start_time;
        bare_choice = choice.split('/')[2].split('.')[0];
        console.log('choice',bare_choice);
        console.log('target',trial.target);
        var correct = 0;
        if (bare_choice == trial.target) {
          correct = 1;
        }
        clear_display();
        end_trial(rt, correct, choice);
      }

      function clear_display() {
        paper.clear();
      }

      // wait
      setTimeout(function() {
        // after wait is over
        show_sketch();
      }, trial.timing_sketch);

    }

    function end_trial(rt, correct, choice) {

      var turkInfo = jsPsych.turk.turkInfo();

      // workerID
      var wID = turkInfo.workerId;
      // hitID
      var hitID = turkInfo.hitId;
      // assignmentID
      var aID = turkInfo.assignmentId;

      // data saving
      var current_data = {
        dbname: '3dObjects',
        colname: 'sketchpad_basic_recog',
        trial_num: trial.trial_num,
        rt: rt,
        correct: correct,
        choice: choice.split('/')[2].split('.')[0],
        locations: JSON.stringify(display_locs),
        sketch: trial.sketch,
        target: trial.target,
        category: trial.category,
        distractor1: trial.distractor1,
        distractor2: trial.distractor2,
        distractor3: trial.distractor3,
        context: trial.context,
        draw_duration: trial.draw_duration,
        num_strokes: trial.num_strokes,
        viewer_correct_in_context: trial.viewer_correct_in_context,
        viewer_response_in_context: trial.viewer_response_in_context,
        viewer_RT_in_context: trial.viewer_RT_in_context,
        gameID: trial.gameID,
        original_gameID: trial.original_gameID,
        original_trialNum: trial.original_trialNum,
        wID: wID,
        hitID: hitID,
        aID: aID,
        object_size: trial.object_size,
        sketch_size: trial.sketch_size,
        grid_size: trial.grid_size
      };

      console.log(current_data);

      // this line merges together the trial_data object and the generic
      // data object (trial.data), and then stores them.
      jsPsych.data.write(current_data);

      // send data to server to write to database
      socket.emit('current_data', current_data);

      // go to next trial
      jsPsych.finishTrial();

    }
  };

  // helper function for determining stimulus locations

  function cosd(num) {
    return Math.cos(num / 180 * Math.PI);
  }

  function sind(num) {
    return Math.sin(num / 180 * Math.PI);
  }

  return plugin;
})();
<commit_msg>Add auto-scroll, remove some margins<commit_after>/**
 *
 * jspsych-nAFC-circle
 * Judy Fan
 *
 * displays a target image at center surrounded by several unique images
 * positioned equidistant from the target.
 * participant's goal is to click on the surround image that best matches the target.
 *
 *
 * requires Snap.svg library (snapsvg.io)
 *
 * documentation: docs.jspsych.org || TBD
 *
 **/

socket = io.connect();

var start_time;

jsPsych.plugins["nAFC-circle"] = (function() {

  var plugin = {};

  plugin.info = {
    name: 'nAFC-circle',
    parameters: {
    }
  }

  plugin.trial = function(display_element, trial) {

    // screen information
    var screenw = $(display_element).width();
    var screenh = $(display_element).height();
    var centerx = screenw / 2;
    var centery = screenh / 2;

    // // initialize start_time timestamp
    // var start_time = Date.now();

    // grid params
    var num_cells = 6;
    var paper_size = trial.grid_size;
    var cell_size = Math.floor(trial.grid_size / num_cells);

    // stimuli width, height
    var stimh = trial.object_size[0];
    var stimw = trial.object_size[1];
    var hstimh = stimh / 2;
    var hstimw = stimw / 2;

    // sketch location
    var fix_loc = [Math.floor(paper_size / 2 - trial.sketch_size[0] / 4), Math.floor(paper_size / 2 - trial.sketch_size[1] / 4)];

    // possible stimulus locations on the circle
    var display_locs = [];

    function get_grid_coords(xrange,yrange) {
      var coords = [];
      for (var i = 0; i < xrange.length; i++) {
        for (var j = 0; j < yrange.length; j++) {
          coords.push([xrange[i],yrange[j]]);
        }
      }
      return coords;
    }

    function remove_one_coord(coords,excluded) {
      var tmp = [];
      for (var i = 0; i < coords.length; i++) {
        if (!(coords[i][0] == excluded[0] && coords[i][1] == excluded[1])) {
          tmp.push(coords[i]);
        }
      }
      return tmp;
    }

    // top left group
    xrange = _.map(_.range(0,cell_size*3,cell_size), function(x) {return x + hstimw});
    yrange = _.map(_.range(0,cell_size*3,cell_size), function(x) {return x + hstimw});
    excluded = [Math.max.apply(null,xrange),Math.max.apply(null,yrange)];
    coords = get_grid_coords(xrange,yrange);
    coordsTL = remove_one_coord(coords,excluded);

    // top right
    xrange = _.map(_.range(cell_size*3,cell_size*6,cell_size), function(x) {return x + hstimw});
    yrange = _.map(_.range(0,cell_size*3,cell_size), function(x) {return x + hstimw});
    excluded = [Math.min.apply(null,xrange),Math.max.apply(null,yrange)];
    coords = get_grid_coords(xrange,yrange);
    coordsTR = remove_one_coord(coords,excluded);

    // bottom left
    xrange = _.map(_.range(0,cell_size*3,cell_size), function(x) {return x + hstimw});
    yrange = _.map(_.range(cell_size*3,cell_size*6,cell_size), function(x) {return x + hstimw});
    excluded = [Math.max.apply(null,xrange),Math.min.apply(null,yrange)];
    coords = get_grid_coords(xrange,yrange);
    coordsBL = remove_one_coord(coords,excluded);

    // bottom right
    xrange = _.map(_.range(cell_size*3,cell_size*6,cell_size), function(x) {return x + hstimw});
    yrange = _.map(_.range(cell_size*3,cell_size*6,cell_size), function(x) {return x + hstimw});
    excluded = [Math.min.apply(null,xrange),Math.min.apply(null,yrange)];
    coords = get_grid_coords(xrange,yrange);
    coordsBR = remove_one_coord(coords,excluded);

    var display_locs = coordsTL.concat(coordsTR).concat(coordsBL).concat(coordsBR);

    // get target to draw on
    var upperBound = parseFloat(paper_size) + 50;
    var sideBound = parseFloat(paper_size) + 80;
    display_element.innerHTML = '<svg id="jspsych-nAFC-circle-svg" width=' + sideBound + ' height=' + upperBound + '></svg> ';
    var paper = Snap('#jspsych-nAFC-circle-svg');
    var element = document.getElementById("jspsych-nAFC-circle-svg");
    element.scrollIntoView(false);

    // wait
    setTimeout(function() {show_object_array(); }, trial.timing_objects);

    function show_sketch() {
      // show sketch
      var sketch = paper.image(trial.sketch, fix_loc[0], fix_loc[1], trial.sketch_size[0], trial.sketch_size[1]);
      start_time = Date.now();
    }

    function show_object_array() {
      var object_array_images = [];
    	img = new Array;
    	for (var i = 0; i < display_locs.length; i++) {
          var img = paper.image(trial.options[i], display_locs[i][0], display_locs[i][1],
				trial.object_size[0], trial.object_size[1]);
        object_array_images.push(img);
      }
      var trial_over = false;

      // group object images and add hover animation
      images = paper.g(paper.selectAll('image'));
      images.selectAll('image').forEach( function( el, index ) {
         el.hover( function() { el.animate({ transform: 's1.5,1.5' }, 100, mina.easein); },
                   function() { el.animate({ transform: 's1,1' }, 100 , mina.easein); }
          )
      } );

      // add click listener to the objects
      var a = document.getElementsByTagName('g')[0];
      imgs = a.children;

      for (var i = 0; i < display_locs.length; i++) {
        imgs[i].addEventListener('click', function (e) {
          var choice = e.currentTarget.getAttribute('href'); // don't use dataset for jsdom compatibility
          after_response(choice);
         })
      }

      var after_response = function(choice) {
        trial_over = true;
        // measure rt
        var end_time = Date.now();
        var rt = end_time - start_time;
        bare_choice = choice.split('/')[2].split('.')[0];
        console.log('choice',bare_choice);
        console.log('target',trial.target);
        var correct = 0;
        if (bare_choice == trial.target) {
          correct = 1;
        }
        clear_display();
        end_trial(rt, correct, choice);
      }

      function clear_display() {
        paper.clear();
      }

      // wait
      setTimeout(function() {
        // after wait is over
        show_sketch();
      }, trial.timing_sketch);

    }

    function end_trial(rt, correct, choice) {

      var turkInfo = jsPsych.turk.turkInfo();

      // workerID
      var wID = turkInfo.workerId;
      // hitID
      var hitID = turkInfo.hitId;
      // assignmentID
      var aID = turkInfo.assignmentId;

      // data saving
      var current_data = {
        dbname: '3dObjects',
        colname: 'sketchpad_basic_recog',
        trial_num: trial.trial_num,
        rt: rt,
        correct: correct,
        choice: choice.split('/')[2].split('.')[0],
        locations: JSON.stringify(display_locs),
        sketch: trial.sketch,
        target: trial.target,
        category: trial.category,
        distractor1: trial.distractor1,
        distractor2: trial.distractor2,
        distractor3: trial.distractor3,
        context: trial.context,
        draw_duration: trial.draw_duration,
        num_strokes: trial.num_strokes,
        viewer_correct_in_context: trial.viewer_correct_in_context,
        viewer_response_in_context: trial.viewer_response_in_context,
        viewer_RT_in_context: trial.viewer_RT_in_context,
        gameID: trial.gameID,
        original_gameID: trial.original_gameID,
        original_trialNum: trial.original_trialNum,
        wID: wID,
        hitID: hitID,
        aID: aID,
        object_size: trial.object_size,
        sketch_size: trial.sketch_size,
        grid_size: trial.grid_size
      };

      console.log(current_data);

      // this line merges together the trial_data object and the generic
      // data object (trial.data), and then stores them.
      jsPsych.data.write(current_data);

      // send data to server to write to database
      socket.emit('current_data', current_data);

      // go to next trial
      jsPsych.finishTrial();

    }
  };

  // helper function for determining stimulus locations

  function cosd(num) {
    return Math.cos(num / 180 * Math.PI);
  }

  function sind(num) {
    return Math.sin(num / 180 * Math.PI);
  }

  return plugin;
})();
<|endoftext|>


<commit_before>require('sugar');
var basename = require('path').basename;

/**
 * Add additional constraints to Brunch's ignored setting using tests. Tests
 * can be any of the following:
 *    Function: Return true to ignore
 *    RegExp:   Match to ignore
 *    String:   Equal to ignore
 * @param {Object} config
 * @param {...(Function|RegExp|String)} tests
 */
exports.addIgnored = function(config) {
  if(!config.conventions) {
    config.conventions = {};
  }

  // Get original ignored function and all given tests
  var ignored = config.conventions.ignored;
  var tests = Array.prototype.slice.call(arguments, 1);

  // Set up the new ignored function
  config.conventions.ignored = function(file) {
    // Check if any of the given tests pass
    for(var index = 0, test = tests[index]; index < tests.length; index++) {
      if(test === file) {
        return true;
      }
      if(Object.isFunction(test) && test(file)) {
        return true;
      }
      if(Object.isRegExp(test) && test.test(file)) {
        return true;
      }
    }

    // Otherwise, delegate to the original ignored declaration
    if(Object.isFunction(ignored)) {
      return ignored(file);
    }
    if(Object.isRegExp(ignored)) {
      return ignored.test(file);
    }

    // This is the default test if any of the previous tests fail
    return basename(file).indexOf('_') === 0;
  };
}
<commit_msg>Fix issue with addIgnored<commit_after>require('sugar');
var basename = require('path').basename;

/**
 * Add additional constraints to Brunch's ignored setting using tests. Tests
 * can be any of the following:
 *    Function: Return true to ignore
 *    RegExp:   Match to ignore
 *    String:   Equal to ignore
 * @param {Object} config
 * @param {...(Function|RegExp|String)} tests
 */
exports.addIgnored = function(config) {
  if(!config.conventions) {
    config.conventions = {};
  }

  // Get original ignored function and all given tests
  var ignored = config.conventions.ignored;
  var tests = Array.prototype.slice.call(arguments, 1);

  // Set up the new ignored function
  config.conventions.ignored = function(file) {
    // Check if any of the given tests pass
    for(var index = 0; index < tests.length; index++) {
      var test = tests[index];

      if(Object.isString(test) && test === file) {
        return true;
      }
      if(Object.isFunction(test) && test(file)) {
        return true;
      }
      if(Object.isRegExp(test) && test.test(file)) {
        return true;
      }
    }

    // Otherwise, delegate to the original ignored declaration
    if(Object.isString(ignored)) {
      return ignored === file;
    }
    if(Object.isFunction(ignored)) {
      return ignored(file);
    }
    if(Object.isRegExp(ignored)) {
      return ignored.test(file);
    }

    // This is the default test if any of the previous tests fail
    return basename(file).indexOf('_') === 0;
  };
}
<|endoftext|>


<commit_before>/* See license.txt for terms of usage */

define([
    "firebug/lib/trace"
],
function(FBTrace) {

// ********************************************************************************************* //
// Constants

const Cc = Components.classes;
const Ci = Components.interfaces;

var consoleService = Cc["@mozilla.org/consoleservice;1"].getService(Ci["nsIConsoleService"]);

// ********************************************************************************************* //
// Module implementation

var Deprecated = {};
Deprecated.deprecated = function(msg, fnc)
{
    return function deprecationWrapper()
    {
        if (!this.nagged)
        {
            // drop frame with deprecated()
            var caller = Components.stack.caller;
            var explain = "Deprecated function, " + msg;

            if (typeof(FBTrace) !== undefined)
            {
                FBTrace.sysout(explain, getStackDump());

                //if (exc.stack)
                //    exc.stack = exc.stack.split("\n");

                FBTrace.sysout(explain + " " + caller.toString());
            }

            if (consoleService)
                consoleService.logStringMessage(explain + " " + caller.toString());

            this.nagged = true;
        }

        return fnc.apply(this, arguments);
    }
}

// ********************************************************************************************* //
// Local helpers

function getStackDump()
{
    var lines = [];
    for (var frame = Components.stack; frame; frame = frame.caller)
        lines.push(frame.filename + " (" + frame.lineNumber + ")");

    return lines.join("\n");
};

// ********************************************************************************************* //
// Registration

return Deprecated;

// ********************************************************************************************* //
});
<commit_msg>Allow to specify arguments for new function<commit_after>/* See license.txt for terms of usage */

define([
    "firebug/lib/trace"
],
function(FBTrace) {

// ********************************************************************************************* //
// Constants

const Cc = Components.classes;
const Ci = Components.interfaces;

var consoleService = Cc["@mozilla.org/consoleservice;1"].getService(Ci["nsIConsoleService"]);

// ********************************************************************************************* //
// Module implementation

var Deprecated = {};
Deprecated.deprecated = function(msg, fnc, args)
{
    return function deprecationWrapper()
    {
        if (!this.nagged)
        {
            // drop frame with deprecated()
            var caller = Components.stack.caller;
            var explain = "Deprecated function, " + msg;

            if (typeof(FBTrace) !== undefined)
            {
                FBTrace.sysout(explain, getStackDump());

                //if (exc.stack)
                //    exc.stack = exc.stack.split("\n");

                FBTrace.sysout(explain + " " + caller.toString());
            }

            if (consoleService)
                consoleService.logStringMessage(explain + " " + caller.toString());

            this.nagged = true;
        }

        return fnc.apply(this, args || arguments);
    }
};

// ********************************************************************************************* //
// Local helpers

function getStackDump()
{
    var lines = [];
    for (var frame = Components.stack; frame; frame = frame.caller)
        lines.push(frame.filename + " (" + frame.lineNumber + ")");

    return lines.join("\n");
};

// ********************************************************************************************* //
// Registration

return Deprecated;

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


<commit_before>// Copyright 2014 The Oppia Authors. 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.

/**
 * @fileoverview Directives for all reusable data visualization components.
 */

// Each visualization receives two variables: 'data' and 'options'. The exact
// format for each of these is specific to the particular visualization.

oppia.directive('oppiaVisualizationBarChart', [function() {
  return {
    restrict: 'E',
    scope: {},
    controller: [
      '$scope', '$attrs', '$element', 'HtmlEscaperService',
      function($scope, $attrs, $element, HtmlEscaperService) {
        $scope.data = HtmlEscaperService.escapedJsonToObj($attrs.data);
        $scope.options = HtmlEscaperService.escapedJsonToObj($attrs.options);

        var dataArray = [['Answers', '']];
        for (var i = 0; i < $scope.data.length; i++) {
          dataArray.push([
            String($scope.data[i].answer), $scope.data[i].frequency]);
        }

        var data = google.visualization.arrayToDataTable(dataArray);

        var options = {
          chartArea: {
            width: '60%'
          },
          hAxis: {
            title: 'Number of times answer was submitted',
            minValue: 0
          },
          legend: {
            position: 'none'
          },
          vAxis: {
            title: 'Answer choice'
          }
        };

        var chart = new google.visualization.BarChart($element[0]);
        chart.draw(data, options);
      }
    ]
  };
}]);

oppia.directive('oppiaVisualizationFrequencyTable', [function() {
  return {
    restrict: 'E',
    scope: {},
    templateUrl: 'visualizations/FrequencyTable',
    controller: [
      '$scope', '$attrs', 'HtmlEscaperService', 'SolutionVerificationService',
      function(
          $scope, $attrs, HtmlEscaperService, SolutionVerificationService) {
        $scope.data = HtmlEscaperService.escapedJsonToObj($attrs.data);
        $scope.options = HtmlEscaperService.escapedJsonToObj($attrs.options);
        $scope.needsAddressing = $scope.data.map(function(datum, i) {
          return (i % 3 === 0) ? null : 'not-null';
        });
        $scope.addressedCellContent = function(index) {
          return $attrs.data + '\n' + $attrs.options;
          if ($scope.needsAddressing[index] !== null) {
            return "No   Address Now";
          } else {
            return "Yes";
          }
        };
      }
    ]
  };
}]);

oppia.directive('oppiaVisualizationEnumeratedFrequencyTable', [function() {
  return {
    restrict: 'E',
    scope: {},
    templateUrl: 'visualizations/EnumeratedFrequencyTable',
    controller: [
      '$scope', '$attrs', 'HtmlEscaperService',
      function($scope, $attrs, HtmlEscaperService) {
        $scope.data = HtmlEscaperService.escapedJsonToObj($attrs.data);
        $scope.options = HtmlEscaperService.escapedJsonToObj($attrs.options);
        $scope.answerVisible = $scope.data.map(function(_, i) {
          // First element is shown by default, all others are hidden.
          return i === 0;
        });
        $scope.toggleAnswerVisibility = function(index) {
          $scope.answerVisible[index] = !$scope.answerVisible[index];
        };
      }
    ]
  };
}]);
<commit_msg>Fix lint issues.<commit_after>// Copyright 2014 The Oppia Authors. 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.

/**
 * @fileoverview Directives for all reusable data visualization components.
 */

// Each visualization receives two variables: 'data' and 'options'. The exact
// format for each of these is specific to the particular visualization.

oppia.directive('oppiaVisualizationBarChart', [function() {
  return {
    restrict: 'E',
    scope: {},
    controller: [
      '$scope', '$attrs', '$element', 'HtmlEscaperService',
      function($scope, $attrs, $element, HtmlEscaperService) {
        $scope.data = HtmlEscaperService.escapedJsonToObj($attrs.data);
        $scope.options = HtmlEscaperService.escapedJsonToObj($attrs.options);

        var dataArray = [['Answers', '']];
        for (var i = 0; i < $scope.data.length; i++) {
          dataArray.push([
            String($scope.data[i].answer), $scope.data[i].frequency]);
        }

        var data = google.visualization.arrayToDataTable(dataArray);

        var options = {
          chartArea: {
            width: '60%'
          },
          hAxis: {
            title: 'Number of times answer was submitted',
            minValue: 0
          },
          legend: {
            position: 'none'
          },
          vAxis: {
            title: 'Answer choice'
          }
        };

        var chart = new google.visualization.BarChart($element[0]);
        chart.draw(data, options);
      }
    ]
  };
}]);

oppia.directive('oppiaVisualizationFrequencyTable', [function() {
  return {
    restrict: 'E',
    scope: {},
    templateUrl: 'visualizations/FrequencyTable',
    controller: [
      '$scope', '$attrs', 'HtmlEscaperService', 'SolutionVerificationService',
      function(
          $scope, $attrs, HtmlEscaperService, SolutionVerificationService) {
        $scope.data = HtmlEscaperService.escapedJsonToObj($attrs.data);
        $scope.options = HtmlEscaperService.escapedJsonToObj($attrs.options);
        $scope.needsAddressing = $scope.data.map(function(datum, i) {
          return (i % 3 === 0) ? null : 'not-null';
        });
        $scope.addressedCellContent = function(index) {
          if ($scope.needsAddressing[index] !== null) {
            return 'NoAddress Now';
          } else {
            return 'Yes';
          }
        };
      }
    ]
  };
}]);

oppia.directive('oppiaVisualizationEnumeratedFrequencyTable', [function() {
  return {
    restrict: 'E',
    scope: {},
    templateUrl: 'visualizations/EnumeratedFrequencyTable',
    controller: [
      '$scope', '$attrs', 'HtmlEscaperService',
      function($scope, $attrs, HtmlEscaperService) {
        $scope.data = HtmlEscaperService.escapedJsonToObj($attrs.data);
        $scope.options = HtmlEscaperService.escapedJsonToObj($attrs.options);
        $scope.answerVisible = $scope.data.map(function(_, i) {
          // First element is shown by default, all others are hidden.
          return i === 0;
        });
        $scope.toggleAnswerVisibility = function(index) {
          $scope.answerVisible[index] = !$scope.answerVisible[index];
        };
      }
    ]
  };
}]);
<|endoftext|>


<commit_before>"use strict";

const {transform} = require("sucrase");
const fs = require("fs");
const path = require("path");
const child_process = require("child_process");

let force = false;

function needsSucrase(source, dest, path = "") {
	if (path.endsWith(".ts")) {
		if (path.endsWith(".d.ts")) return false;
		const sourceStat = fs.lstatSync(source + path);
		try {
			const destStat = fs.lstatSync(dest + path.slice(0, -2) + "js");
			return sourceStat.ctimeMs > destStat.ctimeMs;
		} catch (e) {
			// dest doesn't exist
			return true;
		}
	}
	if (!path.includes(".")) {
		// probably dir
		if (source === './config' && path) return false;
		try {
			const sourceFiles = fs.readdirSync(source + path);
			for (const file of sourceFiles) {
				if (needsSucrase(source, dest, path + "/" + file)) {
					return true;
				}
			}
			if (path.endsWith('/chat-plugins') || source === './config' || path.includes('/mods/')) {
				const destFiles = fs.readdirSync(dest + path);
				for (const file of destFiles) {
					if (path.endsWith('/config')) console.log(file);
					if (file.endsWith('.js') && !sourceFiles.includes(file.slice(0, -2) + 'ts') && !sourceFiles.includes(file)) {
						fs.unlinkSync(dest + path + "/" + file);
					}
				}
			}
			if (path.endsWith('/mods')) {
				const destFolders = fs.readdirSync(dest + path);
				for (const destFolder of destFolders) {
					if (!destFolder.includes('.') && !sourceFiles.includes(destFolder)) {
						fs.rmSync(dest + path + "/" + destFolder, {recursive: true});
					}
				}
			}
		} catch (e) {
			// not dir
		}
		return false;
	}
	return false;
}

// https://github.com/alangpierce/sucrase/blob/master/src/cli.ts
function findFiles(options) {
	const outDirPath = options.outDirPath;
	const srcDirPath = options.srcDirPath;
	const pathToSource = options.pathToSource || "..";

	const extensions = options.sucraseOptions.transforms.includes("typescript") ?
		[".ts", ".tsx"] :
		[".js", ".jsx"];

	const outArr = [];
	for (const child of fs.readdirSync(srcDirPath)) {
		if (
			["node_modules", ".git"].includes(child) ||
			options.excludeDirs.includes(child)
		) {
			continue;
		}
		const srcChildPath = path.join(srcDirPath, child);
		const outChildPath = path.join(outDirPath, child);
		const pathToSourceChild = path.join(pathToSource, "..");
		if (fs.statSync(srcChildPath).isDirectory()) {
			const innerOptions = {...options};
			innerOptions.srcDirPath = srcChildPath;
			innerOptions.outDirPath = outChildPath;
			innerOptions.pathToSource = pathToSourceChild;
			const innerFiles = findFiles(innerOptions);
			outArr.push(...innerFiles);
		} else if (extensions.some((ext) => srcChildPath.endsWith(ext))) {
			const outPath = outChildPath.replace(
				/\.\w+$/,
				`.${options.outExtension}`
			);
			outArr.push({
				srcPath: srcChildPath,
				outPath,
				pathToSource: pathToSource,
			});
		}
	}

	if (!outArr.length) {
		return outArr;
	}
	fs.mkdirSync(path.join(outDirPath, "sourceMaps"), {recursive: true});

	return outArr;
}

function sucrase(src, out, opts, excludeDirs = []) {
	try {
		if (!force && !needsSucrase(src, out) && src !== "./config") {
			return false;
		}
	} catch {}
	const sucraseOptions = {
		transforms: ["typescript", "imports"],
		enableLegacyTypeScriptModuleInterop: true,

		...opts,
	};
	const files = findFiles({
		outDirPath: out,
		srcDirPath: src,
		sucraseOptions: sucraseOptions,
		excludeDirs: ["sourceMaps", ...excludeDirs],
		outExtension: "js",
	});
	for (const file of files) {
		const sucraseOptionsFile = {
			...sucraseOptions,
			sourceMapOptions: {compiledFilename: file.outPath},
			filePath: path.join(file.pathToSource, file.srcPath),
		};
		const code = fs.readFileSync(file.srcPath, "utf-8");
		const transformed = transform(code, sucraseOptionsFile);
		transformed.code += `\n //# sourceMappingURL=sourceMaps/${path.basename(
			file.outPath
		)}.map`;
		fs.writeFileSync(
			path.join(
				path.dirname(file.outPath),
				"sourceMaps",
				path.basename(`${file.outPath}.map`)
			),
			JSON.stringify(transformed.sourceMap)
		);
		fs.writeFileSync(file.outPath, transformed.code);
	}
	return true;
}

function replace(file, replacements) {
	fs.lstat(file, function (err, stats) {
		if (err) throw err;
		if (stats.isSymbolicLink()) return;
		if (stats.isFile()) {
			if (!file.endsWith('.js') && !file.endsWith('.d.ts')) return;
			fs.readFile(file, "utf-8", function (err, text) {
				if (err) throw err;
				let anyMatch = false;
				for (let i = 0; i < replacements.length; i++) {
					anyMatch = anyMatch || text.match(replacements[i].regex);
					if (anyMatch) text = text.replace(replacements[i].regex, replacements[i].replace);
				}
				if (!anyMatch) return;
				fs.writeFile(file, text, function (err) {
					if (err) throw err;
				});
			});
		} else if (stats.isDirectory()) {
			fs.readdir(file, function (err, files) {
				if (err) throw err;
				for (let i = 0; i < files.length; i++) {
					replace(path.join(file, files[i]), replacements);
				}
			});
		}
	});
}

function copyOverDataJSON(file) {
	const source = './data/' + file;
	const dest = './.data-dist/' + file;
	fs.readFile(source, function (err, text) {
		if (err) throw err;
		fs.writeFile(dest, text, function (err) {
			if (err) throw err;
		});
	});
}

exports.transpile = (doForce, decl) => {
	if (doForce) force = true;
	if (sucrase('./config', './.config-dist')) {
		replace('.config-dist', [
			{regex: /(require\(.*?)(lib|sim)/g, replace: `$1.$2-dist`},
		]);
	}

	if (sucrase('./data', './.data-dist')) {
		replace('.data-dist', [
			{regex: /(require\(.*?)(lib|sim)/g, replace: `$1.$2-dist`},
		]);
	}

	if (sucrase('./sim', './.sim-dist')) {
		replace('.sim-dist', [
			{regex: /(require\(.*?)\/(lib|data|config)/g, replace: `$1/.$2-dist`},
			{regex: /(from '.*?)\/(lib|data|config)/g, replace: `$1/.$2-dist`},
		]);
	}

	sucrase('./lib', './.lib-dist');

	if (sucrase('./server', './.server-dist')) {
		replace('.server-dist', [
			{regex: /(require\(.*?)(data|lib|sim)/g, replace: `$1.$2-dist`},
		]);
	}

	sucrase('./translations', './.translations-dist');

	if (sucrase('./tools', './tools', null, ['.', 'sets', 'simulate'])) {
		replace('tools', [
			{regex: /(require\(.*?)(lib|sim|server)/g, replace: `$1.$2-dist`},
		]);
	}

	if (!fs.existsSync('./.data-dist/README.md')) {
		const text = '**NOTE**: This folder contains the compiled output of the `data/` directory.\n' +
			'You should be editing the `.ts` files there and then running `npm run build` or\n' +
			'`./pokemon-showdown` to force these `.js` files to be recreated.\n';
		fs.writeFile('./.config-dist/README.md', text.replace('data/', 'config/'), function () {});
		fs.writeFile('./.data-dist/README.md', text, function () {});
		fs.writeFile('./.sim-dist/README.md', text.replace('data/', 'sim/'), function () {});
		fs.writeFile('./.server-dist/README.md', text.replace('data/', 'server/'), function () {});
		fs.writeFile('./.translations-dist/README.md', text.replace('data/', 'translations/'), function () {});
		fs.writeFile('./.lib-dist/README.md', text.replace('data/', 'lib/'), function () {});
	}

	// sucrase doesn't copy JSON over, so we'll have to do it ourselves
	copyOverDataJSON('bss-factory-sets.json');
	copyOverDataJSON('cap-1v1-sets.json');
	copyOverDataJSON('mods/gen7/factory-sets.json');
	copyOverDataJSON('mods/gen7/bss-factory-sets.json');
	copyOverDataJSON('mods/gen6/factory-sets.json');

	// NOTE: replace is asynchronous - add additional replacements for the same path in one call instead of making multiple calls.
	if (decl) {
		exports.buildDecls();
	}
};

exports.buildDecls = () => {
	try {
		child_process.execSync(`node ./node_modules/typescript/bin/tsc -p sim`, {stdio: 'inherit'});
	} catch {}
	for (const file of fs.readdirSync(`./.sim-dist/lib/`)) {
		fs.renameSync(`./.sim-dist/lib/${file}`, `./.lib-dist/${file}`);
	}
	for (const file of fs.readdirSync(`./.sim-dist/sim/`)) {
		fs.renameSync(`./.sim-dist/sim/${file}`, `./.sim-dist/${file}`);
	}
};
<commit_msg>Fix Sucrase build script<commit_after>"use strict";

const {transform} = require("sucrase");
const fs = require("fs");
const path = require("path");
const child_process = require("child_process");

let force = false;

function needsSucrase(source, dest, path = "") {
	if (path.endsWith(".ts")) {
		if (path.endsWith(".d.ts")) return false;
		const sourceStat = fs.lstatSync(source + path);
		try {
			const destStat = fs.lstatSync(dest + path.slice(0, -2) + "js");
			return sourceStat.ctimeMs > destStat.ctimeMs;
		} catch (e) {
			// dest doesn't exist
			return true;
		}
	}
	if (!path.includes(".")) {
		// probably dir
		if (source === './config' && path) return false;
		try {
			const sourceFiles = fs.readdirSync(source + path);
			for (const file of sourceFiles) {
				if (needsSucrase(source, dest, path + "/" + file)) {
					return true;
				}
			}
			if (path.endsWith('/chat-plugins') || source === './config' || path.includes('/mods/')) {
				const destFiles = fs.readdirSync(dest + path);
				for (const file of destFiles) {
					if (path.endsWith('/config')) console.log(file);
					if (file.endsWith('.js') && !sourceFiles.includes(file.slice(0, -2) + 'ts') && !sourceFiles.includes(file)) {
						fs.unlinkSync(dest + path + "/" + file);
					}
				}
			}
			if (path.endsWith('/mods')) {
				const destFolders = fs.readdirSync(dest + path);
				for (const destFolder of destFolders) {
					if (!destFolder.includes('.') && !sourceFiles.includes(destFolder)) {
						fs.rmSync(dest + path + "/" + destFolder, {recursive: true});
					}
				}
			}
		} catch (e) {
			// not dir
		}
		return false;
	}
	return false;
}

// https://github.com/alangpierce/sucrase/blob/master/src/cli.ts
function findFiles(options) {
	const outDirPath = options.outDirPath;
	const srcDirPath = options.srcDirPath;
	const pathToSource = options.pathToSource || "..";

	const extensions = options.sucraseOptions.transforms.includes("typescript") ?
		[".ts", ".tsx"] :
		[".js", ".jsx"];

	const outArr = [];
	for (const child of fs.readdirSync(srcDirPath)) {
		if (
			["node_modules", ".git"].includes(child) ||
			options.excludeDirs.includes(child)
		) {
			continue;
		}
		const srcChildPath = path.join(srcDirPath, child);
		const outChildPath = path.join(outDirPath, child);
		const pathToSourceChild = path.join(pathToSource, "..");
		if (fs.statSync(srcChildPath).isDirectory()) {
			const innerOptions = {...options};
			innerOptions.srcDirPath = srcChildPath;
			innerOptions.outDirPath = outChildPath;
			innerOptions.pathToSource = pathToSourceChild;
			const innerFiles = findFiles(innerOptions);
			outArr.push(...innerFiles);
		} else if (extensions.some((ext) => srcChildPath.endsWith(ext))) {
			const outPath = outChildPath.replace(
				/\.\w+$/,
				`.${options.outExtension}`
			);
			outArr.push({
				srcPath: srcChildPath,
				outPath,
				pathToSource: pathToSource,
			});
		}
	}

	if (!outArr.length) {
		return outArr;
	}
	fs.mkdirSync(path.join(outDirPath, "sourceMaps"), {recursive: true});

	return outArr;
}

function sucrase(src, out, opts, excludeDirs = []) {
	try {
		if (!force && !needsSucrase(src, out) && src !== "./config") {
			return false;
		}
	} catch {}
	const sucraseOptions = {
		transforms: ["typescript", "imports", "jsx"],
		enableLegacyTypeScriptModuleInterop: true,

		...opts,
	};
	const files = findFiles({
		outDirPath: out,
		srcDirPath: src,
		sucraseOptions: sucraseOptions,
		excludeDirs: ["sourceMaps", ...excludeDirs],
		outExtension: "js",
	});
	for (const file of files) {
		const sucraseOptionsFile = {
			...sucraseOptions,
			sourceMapOptions: {compiledFilename: file.outPath},
			filePath: path.join(file.pathToSource, file.srcPath),
		};
		const code = fs.readFileSync(file.srcPath, "utf-8");
		const transformed = transform(code, sucraseOptionsFile);
		transformed.code += `\n //# sourceMappingURL=sourceMaps/${path.basename(
			file.outPath
		)}.map`;
		fs.writeFileSync(
			path.join(
				path.dirname(file.outPath),
				"sourceMaps",
				path.basename(`${file.outPath}.map`)
			),
			JSON.stringify(transformed.sourceMap)
		);
		fs.writeFileSync(file.outPath, transformed.code);
	}
	return true;
}

function replace(file, replacements) {
	fs.lstat(file, function (err, stats) {
		if (err) throw err;
		if (stats.isSymbolicLink()) return;
		if (stats.isFile()) {
			if (!file.endsWith('.js') && !file.endsWith('.d.ts')) return;
			fs.readFile(file, "utf-8", function (err, text) {
				if (err) throw err;
				let anyMatch = false;
				for (let i = 0; i < replacements.length; i++) {
					anyMatch = anyMatch || text.match(replacements[i].regex);
					if (anyMatch) text = text.replace(replacements[i].regex, replacements[i].replace);
				}
				if (!anyMatch) return;
				fs.writeFile(file, text, function (err) {
					if (err) throw err;
				});
			});
		} else if (stats.isDirectory()) {
			fs.readdir(file, function (err, files) {
				if (err) throw err;
				for (let i = 0; i < files.length; i++) {
					replace(path.join(file, files[i]), replacements);
				}
			});
		}
	});
}

function copyOverDataJSON(file) {
	const source = './data/' + file;
	const dest = './.data-dist/' + file;
	fs.readFile(source, function (err, text) {
		if (err) throw err;
		fs.writeFile(dest, text, function (err) {
			if (err) throw err;
		});
	});
}

exports.transpile = (doForce, decl) => {
	if (doForce) force = true;
	if (sucrase('./config', './.config-dist')) {
		replace('.config-dist', [
			{regex: /(require\(.*?)(lib|sim)/g, replace: `$1.$2-dist`},
		]);
	}

	if (sucrase('./data', './.data-dist')) {
		replace('.data-dist', [
			{regex: /(require\(.*?)(lib|sim)/g, replace: `$1.$2-dist`},
		]);
	}

	if (sucrase('./sim', './.sim-dist')) {
		replace('.sim-dist', [
			{regex: /(require\(.*?)\/(lib|data|config)/g, replace: `$1/.$2-dist`},
			{regex: /(from '.*?)\/(lib|data|config)/g, replace: `$1/.$2-dist`},
		]);
	}

	sucrase('./lib', './.lib-dist');

	if (sucrase('./server', './.server-dist')) {
		replace('.server-dist', [
			{regex: /(require\(.*?)(data|lib|sim)/g, replace: `$1.$2-dist`},
		]);
	}

	sucrase('./translations', './.translations-dist');

	if (sucrase('./tools', './tools', null, ['.', 'sets', 'simulate'])) {
		replace('tools', [
			{regex: /(require\(.*?)(lib|sim|server)/g, replace: `$1.$2-dist`},
		]);
	}

	if (!fs.existsSync('./.data-dist/README.md')) {
		const text = '**NOTE**: This folder contains the compiled output of the `data/` directory.\n' +
			'You should be editing the `.ts` files there and then running `npm run build` or\n' +
			'`./pokemon-showdown` to force these `.js` files to be recreated.\n';
		fs.writeFile('./.config-dist/README.md', text.replace('data/', 'config/'), function () {});
		fs.writeFile('./.data-dist/README.md', text, function () {});
		fs.writeFile('./.sim-dist/README.md', text.replace('data/', 'sim/'), function () {});
		fs.writeFile('./.server-dist/README.md', text.replace('data/', 'server/'), function () {});
		fs.writeFile('./.translations-dist/README.md', text.replace('data/', 'translations/'), function () {});
		fs.writeFile('./.lib-dist/README.md', text.replace('data/', 'lib/'), function () {});
	}

	// sucrase doesn't copy JSON over, so we'll have to do it ourselves
	copyOverDataJSON('bss-factory-sets.json');
	copyOverDataJSON('cap-1v1-sets.json');
	copyOverDataJSON('mods/gen7/factory-sets.json');
	copyOverDataJSON('mods/gen7/bss-factory-sets.json');
	copyOverDataJSON('mods/gen6/factory-sets.json');

	// NOTE: replace is asynchronous - add additional replacements for the same path in one call instead of making multiple calls.
	if (decl) {
		exports.buildDecls();
	}
};

exports.buildDecls = () => {
	try {
		child_process.execSync(`node ./node_modules/typescript/bin/tsc -p sim`, {stdio: 'inherit'});
	} catch {}
	for (const file of fs.readdirSync(`./.sim-dist/lib/`)) {
		fs.renameSync(`./.sim-dist/lib/${file}`, `./.lib-dist/${file}`);
	}
	for (const file of fs.readdirSync(`./.sim-dist/sim/`)) {
		fs.renameSync(`./.sim-dist/sim/${file}`, `./.sim-dist/${file}`);
	}
};
<|endoftext|>