<commit_before>//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
//>>description: Custom events and shortcuts.
//>>label: Events
//>>group: core
//>>required: true

define( [ "jquery", "./jquery.mobile.core", "./jquery.mobile.media", "./jquery.mobile.support", "./jquery.mobile.vmouse" ], function( $ ) {
//>>excludeEnd("jqmBuildExclude");
(function( $, window, undefined ) {

// add new event shortcuts
$.each( ( "touchstart touchmove touchend orientationchange throttledresize " +
					"tap taphold swipe swipeleft swiperight scrollstart scrollstop" ).split( " " ), function( i, name ) {

	$.fn[ name ] = function( fn ) {
		return fn ? this.bind( name, fn ) : this.trigger( name );
	};

	$.attrFn[ name ] = true;
});

var supportTouch = $.support.touch,
	scrollEvent = "touchmove scroll",
	touchStartEvent = supportTouch ? "touchstart" : "mousedown",
	touchStopEvent = supportTouch ? "touchend" : "mouseup",
	touchMoveEvent = supportTouch ? "touchmove" : "mousemove";

function triggerCustomEvent( obj, eventType, event ) {
	var originalType = event.type;
	event.type = eventType;
	$.event.handle.call( obj, event );
	event.type = originalType;
}

// also handles scrollstop
$.event.special.scrollstart = {

	enabled: true,

	setup: function() {

		var thisObject = this,
			$this = $( thisObject ),
			scrolling,
			timer;

		function trigger( event, state ) {
			scrolling = state;
			triggerCustomEvent( thisObject, scrolling ? "scrollstart" : "scrollstop", event );
		}

		// iPhone triggers scroll after a small delay; use touchmove instead
		$this.bind( scrollEvent, function( event ) {

			if ( !$.event.special.scrollstart.enabled ) {
				return;
			}

			if ( !scrolling ) {
				trigger( event, true );
			}

			clearTimeout( timer );
			timer = setTimeout(function() {
				trigger( event, false );
			}, 50 );
		});
	}
};

// also handles taphold
$.event.special.tap = {
	setup: function() {
		var thisObject = this,
			$this = $( thisObject );

		$this.bind( "vmousedown", function( event ) {

			if ( event.which && event.which !== 1 ) {
				return false;
			}

			var origTarget = event.target,
				origEvent = event.originalEvent,
				timer;

			function clearTapTimer() {
				clearTimeout( timer );
			}

			function clearTapHandlers() {
				clearTapTimer();

				$this.unbind( "vclick", clickHandler )
					.unbind( "vmouseup", clearTapTimer );
				$( document ).unbind( "vmousecancel", clearTapHandlers );
			}

			function clickHandler(event) {
				clearTapHandlers();

				// ONLY trigger a 'tap' event if the start target is
				// the same as the stop target.
				if ( origTarget == event.target ) {
					triggerCustomEvent( thisObject, "tap", event );
				}
			}

			$this.bind( "vmouseup", clearTapTimer )
				.bind( "vclick", clickHandler );
			$( document ).bind( "vmousecancel", clearTapHandlers );

			timer = setTimeout(function() {
					triggerCustomEvent( thisObject, "taphold", $.Event( "taphold" ) );
			}, 750 );
		});
	}
};

// also handles swipeleft, swiperight
$.event.special.swipe = {
	scrollSupressionThreshold: 10, // More than this horizontal displacement, and we will suppress scrolling.

	durationThreshold: 1000, // More time than this, and it isn't a swipe.

	horizontalDistanceThreshold: 30,  // Swipe horizontal displacement must be more than this.

	verticalDistanceThreshold: 75,  // Swipe vertical displacement must be less than this.

	setup: function() {
		var thisObject = this,
			$this = $( thisObject );

		$this.bind( touchStartEvent, function( event ) {
			var data = event.originalEvent.touches ?
								event.originalEvent.touches[ 0 ] : event,
				start = {
					time: ( new Date() ).getTime(),
					coords: [ data.pageX, data.pageY ],
					origin: $( event.target )
				},
				stop;

			function moveHandler( event ) {

				if ( !start ) {
					return;
				}

				var data = event.originalEvent.touches ?
						event.originalEvent.touches[ 0 ] : event;

				stop = {
					time: ( new Date() ).getTime(),
					coords: [ data.pageX, data.pageY ]
				};

				// prevent scrolling
				if ( Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.scrollSupressionThreshold ) {
					event.preventDefault();
				}
			}

			$this.bind( touchMoveEvent, moveHandler )
				.one( touchStopEvent, function( event ) {
					$this.unbind( touchMoveEvent, moveHandler );

					if ( start && stop ) {
						if ( stop.time - start.time < $.event.special.swipe.durationThreshold &&
								Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.horizontalDistanceThreshold &&
								Math.abs( start.coords[ 1 ] - stop.coords[ 1 ] ) < $.event.special.swipe.verticalDistanceThreshold ) {

							start.origin.trigger( "swipe" )
								.trigger( start.coords[0] > stop.coords[ 0 ] ? "swipeleft" : "swiperight" );
						}
					}
					start = stop = undefined;
				});
		});
	}
};

(function( $, window ) {
	// "Cowboy" Ben Alman

	var win = $( window ),
		special_event,
		get_orientation,
		last_orientation,
		initial_orientation_is_landscape,
		initial_orientation_is_default,
		portrait_map = { "0": true, "180": true };

	// It seems that some device/browser vendors use window.orientation values 0 and 180 to
	// denote the "default" orientation. For iOS devices, and most other smart-phones tested,
	// the default orientation is always "portrait", but in some Android and RIM based tablets,
	// the default orientation is "landscape". The following code injects a landscape orientation
	// media query into the document to figure out what the current orientation is, and then
	// makes adjustments to the portrait_map if necessary, so that we can properly
	// decode the window.orientation value whenever get_orientation() is called.
	if ( $.support.orientation ) {

		// Use a media query to figure out the true orientation of the device at this moment.
		// Note that we've initialized the portrait map values to 0 and 180, *AND* we purposely
		// use a landscape media query so that if the device/browser does not support this particular
		// media query, we default to the assumption that portrait is the default orientation.
		initial_orientation_is_landscape = $.mobile.media("all and (orientation: landscape)");

		// Now check to see if the current window.orientation is 0 or 180.
		initial_orientation_is_default = portrait_map[ window.orientation ];

		// If the initial orientation is landscape, but window.orientation reports 0 or 180, *OR*
		// if the initial orientation is portrait, but window.orientation reports 90 or -90, we
		// need to flip our portrait_map values because landscape is the default orientation for
		// this device/browser.
		if ( ( initial_orientation_is_landscape && initial_orientation_is_default ) || ( !initial_orientation_is_landscape && !initial_orientation_is_default ) ) {
			portrait_map = { "-90": true, "90": true };
		}
	}

	$.event.special.orientationchange = special_event = {
		setup: function() {
			// If the event is supported natively, return false so that jQuery
			// will bind to the event using DOM methods.
			if ( $.support.orientation && $.mobile.orientationChangeEnabled ) {
				return false;
			}

			// Get the current orientation to avoid initial double-triggering.
			last_orientation = get_orientation();

			// Because the orientationchange event doesn't exist, simulate the
			// event by testing window dimensions on resize.
			win.bind( "throttledresize", handler );
		},
		teardown: function(){
			// If the event is not supported natively, return false so that
			// jQuery will unbind the event using DOM methods.
			if ( $.support.orientation && $.mobile.orientationChangeEnabled ) {
				return false;
			}

			// Because the orientationchange event doesn't exist, unbind the
			// resize event handler.
			win.unbind( "throttledresize", handler );
		},
		add: function( handleObj ) {
			// Save a reference to the bound event handler.
			var old_handler = handleObj.handler;

			handleObj.handler = function( event ) {
				// Modify event object, adding the .orientation property.
				event.orientation = get_orientation();

				// Call the originally-bound event handler and return its result.
				return old_handler.apply( this, arguments );
			};
		}
	};

	// If the event is not supported natively, this handler will be bound to
	// the window resize event to simulate the orientationchange event.
	function handler() {
		// Get the current orientation.
		var orientation = get_orientation();

		if ( orientation !== last_orientation ) {
			// The orientation has changed, so trigger the orientationchange event.
			last_orientation = orientation;
			win.trigger( "orientationchange" );
		}
	}

	// Get the current page orientation. This method is exposed publicly, should it
	// be needed, as jQuery.event.special.orientationchange.orientation()
	$.event.special.orientationchange.orientation = get_orientation = function() {
		var isPortrait = true, elem = document.documentElement;

		// prefer window orientation to the calculation based on screensize as
		// the actual screen resize takes place before or after the orientation change event
		// has been fired depending on implementation (eg android 2.3 is before, iphone after).
		// More testing is required to determine if a more reliable method of determining the new screensize
		// is possible when orientationchange is fired. (eg, use media queries + element + opacity)
		if ( $.support.orientation ) {
			// if the window orientation registers as 0 or 180 degrees report
			// portrait, otherwise landscape
			isPortrait = portrait_map[ window.orientation ];
		} else {
			isPortrait = elem && elem.clientWidth / elem.clientHeight < 1.1;
		}

		return isPortrait ? "portrait" : "landscape";
	};

})( jQuery, window );

// throttled resize event
(function() {

	$.event.special.throttledresize = {
		setup: function() {
			$( this ).bind( "resize", handler );
		},
		teardown: function(){
			$( this ).unbind( "resize", handler );
		}
	};

	var throttle = 250,
		handler = function() {
			curr = ( new Date() ).getTime();
			diff = curr - lastCall;

			if ( diff >= throttle ) {

				lastCall = curr;
				$( this ).trigger( "throttledresize" );

			} else {

				if ( heldCall ) {
					clearTimeout( heldCall );
				}

				// Promise a held call will still execute
				heldCall = setTimeout( handler, throttle - diff );
			}
		},
		lastCall = 0,
		heldCall,
		curr,
		diff;
})();

$.each({
	scrollstop: "scrollstart",
	taphold: "tap",
	swipeleft: "swipe",
	swiperight: "swipe"
}, function( event, sourceEvent ) {

	$.event.special[ event ] = {
		setup: function() {
			$( this ).bind( sourceEvent, $.noop );
		}
	};
});

})( jQuery, this );
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
});
//>>excludeEnd("jqmBuildExclude");
<commit_msg>May be related: https://forum.jquery.com/topic/taphold-don-t-work<commit_after>//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
//>>description: Custom events and shortcuts.
//>>label: Events
//>>group: core
//>>required: true

define( [ "jquery", "./jquery.mobile.core", "./jquery.mobile.media", "./jquery.mobile.support", "./jquery.mobile.vmouse" ], function( $ ) {
//>>excludeEnd("jqmBuildExclude");
(function( $, window, undefined ) {

// add new event shortcuts
$.each( ( "touchstart touchmove touchend orientationchange throttledresize " +
					"tap taphold swipe swipeleft swiperight scrollstart scrollstop" ).split( " " ), function( i, name ) {

	$.fn[ name ] = function( fn ) {
		return fn ? this.bind( name, fn ) : this.trigger( name );
	};

	$.attrFn[ name ] = true;
});

var supportTouch = $.support.touch,
	scrollEvent = "touchmove scroll",
	touchStartEvent = supportTouch ? "touchstart" : "mousedown",
	touchStopEvent = supportTouch ? "touchend" : "mouseup",
	touchMoveEvent = supportTouch ? "touchmove" : "mousemove";

function triggerCustomEvent( obj, eventType, event ) {
	var originalType = event.type;
	event.type = eventType;
	$.event.handle.call( obj, event );
	event.type = originalType;
}

// also handles scrollstop
$.event.special.scrollstart = {

	enabled: true,

	setup: function() {

		var thisObject = this,
			$this = $( thisObject ),
			scrolling,
			timer;

		function trigger( event, state ) {
			scrolling = state;
			triggerCustomEvent( thisObject, scrolling ? "scrollstart" : "scrollstop", event );
		}

		// iPhone triggers scroll after a small delay; use touchmove instead
		$this.bind( scrollEvent, function( event ) {

			if ( !$.event.special.scrollstart.enabled ) {
				return;
			}

			if ( !scrolling ) {
				trigger( event, true );
			}

			clearTimeout( timer );
			timer = setTimeout(function() {
				trigger( event, false );
			}, 50 );
		});
	}
};

// also handles taphold
$.event.special.tap = {
	setup: function() {
		var thisObject = this,
			$this = $( thisObject );

		$this.bind( "vmousedown", function( event ) {

			if ( event.which && event.which !== 1 ) {
				return false;
			}

			var origTarget = event.target,
				origEvent = event.originalEvent,
				timer;

			function clearTapTimer() {
				clearTimeout( timer );
			}

			function clearTapHandlers() {
				clearTapTimer();

				$this.unbind( "vclick", clickHandler )
					.unbind( "vmouseup", clearTapTimer );
				$( document ).unbind( "vmousecancel", clearTapHandlers );
			}

			function clickHandler(event) {
				clearTapHandlers();

				// ONLY trigger a 'tap' event if the start target is
				// the same as the stop target.
				if ( origTarget == event.target ) {
					triggerCustomEvent( thisObject, "tap", event );
				}
			}

			$this.bind( "vmouseup", clearTapTimer )
				.bind( "vclick", clickHandler );
			$( document ).bind( "vmousecancel", clearTapHandlers );

			timer = setTimeout(function() {
					triggerCustomEvent( thisObject, "taphold", $.Event( "taphold", { target: origTarget } ) );
			}, 750 );
		});
	}
};

// also handles swipeleft, swiperight
$.event.special.swipe = {
	scrollSupressionThreshold: 10, // More than this horizontal displacement, and we will suppress scrolling.

	durationThreshold: 1000, // More time than this, and it isn't a swipe.

	horizontalDistanceThreshold: 30,  // Swipe horizontal displacement must be more than this.

	verticalDistanceThreshold: 75,  // Swipe vertical displacement must be less than this.

	setup: function() {
		var thisObject = this,
			$this = $( thisObject );

		$this.bind( touchStartEvent, function( event ) {
			var data = event.originalEvent.touches ?
								event.originalEvent.touches[ 0 ] : event,
				start = {
					time: ( new Date() ).getTime(),
					coords: [ data.pageX, data.pageY ],
					origin: $( event.target )
				},
				stop;

			function moveHandler( event ) {

				if ( !start ) {
					return;
				}

				var data = event.originalEvent.touches ?
						event.originalEvent.touches[ 0 ] : event;

				stop = {
					time: ( new Date() ).getTime(),
					coords: [ data.pageX, data.pageY ]
				};

				// prevent scrolling
				if ( Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.scrollSupressionThreshold ) {
					event.preventDefault();
				}
			}

			$this.bind( touchMoveEvent, moveHandler )
				.one( touchStopEvent, function( event ) {
					$this.unbind( touchMoveEvent, moveHandler );

					if ( start && stop ) {
						if ( stop.time - start.time < $.event.special.swipe.durationThreshold &&
								Math.abs( start.coords[ 0 ] - stop.coords[ 0 ] ) > $.event.special.swipe.horizontalDistanceThreshold &&
								Math.abs( start.coords[ 1 ] - stop.coords[ 1 ] ) < $.event.special.swipe.verticalDistanceThreshold ) {

							start.origin.trigger( "swipe" )
								.trigger( start.coords[0] > stop.coords[ 0 ] ? "swipeleft" : "swiperight" );
						}
					}
					start = stop = undefined;
				});
		});
	}
};

(function( $, window ) {
	// "Cowboy" Ben Alman

	var win = $( window ),
		special_event,
		get_orientation,
		last_orientation,
		initial_orientation_is_landscape,
		initial_orientation_is_default,
		portrait_map = { "0": true, "180": true };

	// It seems that some device/browser vendors use window.orientation values 0 and 180 to
	// denote the "default" orientation. For iOS devices, and most other smart-phones tested,
	// the default orientation is always "portrait", but in some Android and RIM based tablets,
	// the default orientation is "landscape". The following code injects a landscape orientation
	// media query into the document to figure out what the current orientation is, and then
	// makes adjustments to the portrait_map if necessary, so that we can properly
	// decode the window.orientation value whenever get_orientation() is called.
	if ( $.support.orientation ) {

		// Use a media query to figure out the true orientation of the device at this moment.
		// Note that we've initialized the portrait map values to 0 and 180, *AND* we purposely
		// use a landscape media query so that if the device/browser does not support this particular
		// media query, we default to the assumption that portrait is the default orientation.
		initial_orientation_is_landscape = $.mobile.media("all and (orientation: landscape)");

		// Now check to see if the current window.orientation is 0 or 180.
		initial_orientation_is_default = portrait_map[ window.orientation ];

		// If the initial orientation is landscape, but window.orientation reports 0 or 180, *OR*
		// if the initial orientation is portrait, but window.orientation reports 90 or -90, we
		// need to flip our portrait_map values because landscape is the default orientation for
		// this device/browser.
		if ( ( initial_orientation_is_landscape && initial_orientation_is_default ) || ( !initial_orientation_is_landscape && !initial_orientation_is_default ) ) {
			portrait_map = { "-90": true, "90": true };
		}
	}

	$.event.special.orientationchange = special_event = {
		setup: function() {
			// If the event is supported natively, return false so that jQuery
			// will bind to the event using DOM methods.
			if ( $.support.orientation && $.mobile.orientationChangeEnabled ) {
				return false;
			}

			// Get the current orientation to avoid initial double-triggering.
			last_orientation = get_orientation();

			// Because the orientationchange event doesn't exist, simulate the
			// event by testing window dimensions on resize.
			win.bind( "throttledresize", handler );
		},
		teardown: function(){
			// If the event is not supported natively, return false so that
			// jQuery will unbind the event using DOM methods.
			if ( $.support.orientation && $.mobile.orientationChangeEnabled ) {
				return false;
			}

			// Because the orientationchange event doesn't exist, unbind the
			// resize event handler.
			win.unbind( "throttledresize", handler );
		},
		add: function( handleObj ) {
			// Save a reference to the bound event handler.
			var old_handler = handleObj.handler;

			handleObj.handler = function( event ) {
				// Modify event object, adding the .orientation property.
				event.orientation = get_orientation();

				// Call the originally-bound event handler and return its result.
				return old_handler.apply( this, arguments );
			};
		}
	};

	// If the event is not supported natively, this handler will be bound to
	// the window resize event to simulate the orientationchange event.
	function handler() {
		// Get the current orientation.
		var orientation = get_orientation();

		if ( orientation !== last_orientation ) {
			// The orientation has changed, so trigger the orientationchange event.
			last_orientation = orientation;
			win.trigger( "orientationchange" );
		}
	}

	// Get the current page orientation. This method is exposed publicly, should it
	// be needed, as jQuery.event.special.orientationchange.orientation()
	$.event.special.orientationchange.orientation = get_orientation = function() {
		var isPortrait = true, elem = document.documentElement;

		// prefer window orientation to the calculation based on screensize as
		// the actual screen resize takes place before or after the orientation change event
		// has been fired depending on implementation (eg android 2.3 is before, iphone after).
		// More testing is required to determine if a more reliable method of determining the new screensize
		// is possible when orientationchange is fired. (eg, use media queries + element + opacity)
		if ( $.support.orientation ) {
			// if the window orientation registers as 0 or 180 degrees report
			// portrait, otherwise landscape
			isPortrait = portrait_map[ window.orientation ];
		} else {
			isPortrait = elem && elem.clientWidth / elem.clientHeight < 1.1;
		}

		return isPortrait ? "portrait" : "landscape";
	};

})( jQuery, window );

// throttled resize event
(function() {

	$.event.special.throttledresize = {
		setup: function() {
			$( this ).bind( "resize", handler );
		},
		teardown: function(){
			$( this ).unbind( "resize", handler );
		}
	};

	var throttle = 250,
		handler = function() {
			curr = ( new Date() ).getTime();
			diff = curr - lastCall;

			if ( diff >= throttle ) {

				lastCall = curr;
				$( this ).trigger( "throttledresize" );

			} else {

				if ( heldCall ) {
					clearTimeout( heldCall );
				}

				// Promise a held call will still execute
				heldCall = setTimeout( handler, throttle - diff );
			}
		},
		lastCall = 0,
		heldCall,
		curr,
		diff;
})();

$.each({
	scrollstop: "scrollstart",
	taphold: "tap",
	swipeleft: "swipe",
	swiperight: "swipe"
}, function( event, sourceEvent ) {

	$.event.special[ event ] = {
		setup: function() {
			$( this ).bind( sourceEvent, $.noop );
		}
	};
});

})( jQuery, this );
//>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
});
//>>excludeEnd("jqmBuildExclude");
<|endoftext|>


<commit_before>// Copyright 2019-2021, University of Colorado Boulder

/**
 * This singleton is responsible for ensuring that the PhET-iO API is correct through the lifetime of the simulation.
 * The PhET-iO API is defined through multiple preloaded files. The "elements baseline" API holds an exact match of
 * what PhetioObject instances/metadata the sim should create on startup, where the "elements overrides" file is a
 * sparse list that can overwrite metadata without changing the code. See `grunt generate-phet-io-api` for
 * more information. The complete list of checks was decided on in https://github.com/phetsims/phet-io/issues/1453
 * (and later trimmed down) and is as follows:
 *
 * 1. After startup, only dynamic instances prescribed by the baseline API can be registered.
 * 2. Any static, registered PhetioObject can never be deregistered.
 * 3. Any schema entries in the overrides file must exist in the baseline API
 * 4. Any schema entries in the overrides file must be different from its baseline counterpart
 * 5. Dynamic element metadata should match the archetype in the API.
 *
 * Terminology:
 * schema: specified through preloads. The full schema is the baseline plus the overrides, but those parts can be
 *         referred to separately.
 * registered: the process of instrumenting a PhetioObject and it "becoming" a PhET-iO Element on the wrapper side.
 * static PhetioObject: A registered PhetioObject that exists for the lifetime of the sim. It should not be removed
 *                      (even intermittently) and must be created during startup so that it is immediately interoperable.
 * dynamic PhetioObject: A registered PhetioObject that can be created and/or destroyed at any point. Only dynamic
 *                       PhetioObjects can be created after startup.
 *
 * See https://github.com/phetsims/phet-io/issues/1443#issuecomment-484306552 for an explanation of how to maintain the
 * PhET-iO API for a simulation.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Michael Kauzmann (PhET Interactive Simulations)
 * @author Chris Klusendorf (PhET Interactive Simulations)
 */

import DynamicTandem from './DynamicTandem.js';
import Tandem from './Tandem.js';
import tandemNamespace from './tandemNamespace.js';

// constants
// The API-tracked and validated metadata keys
const KEYS_TO_CHECK = [
  'phetioDynamicElement',
  'phetioEventType',
  'phetioIsArchetype',
  'phetioPlayback',
  'phetioReadOnly',
  'phetioState',
  'phetioTypeName'
];

class PhetioAPIValidation {

  constructor() {

    /**
     * {Object[]} - Each object holds a single "API mismatch" with the following keys:
     *                phetioID: {string}
     *                stack: {string} - for a stack trace
     *                ruleInViolation: {string} - one of the numbered list in the header doc.
     *                [message]: {string} - specific problem
     *
     * Feel free to add any other JSONifyable keys to this to make the error more clear! All mismatches are printed
     * at once for clarity, see PhetioEngine.
     * @private
     */
    this.apiMismatches = [];

    // @private - keep track of when the sim has started.
    this.simHasStarted = false;

    // @public {boolean} - settable by qunitStart.js. Validation is only enabled when all screens are present.
    this.enabled = assert && Tandem.VALIDATION;

    // @private {Object.<typeName:string, IOType>} - this must be all phet-io types so that the
    // following would fail: add a phetioType, then remove it, then add a different one under the same typeName.
    // A Note about memory: Every IOType that is loaded as a module is already loaded on the namespace. Therefore
    // this map doesn't add any memory by storing these. The exception to this is parametric IOTypes. It should be
    // double checked that anything being passed into a parametric type is memory safe. As of this writing, only IOTypes
    // are passed to parametric IOTypes, so this pattern remains memory leak free. Furthermore, this list is only
    // populated when `this.enabled`.
    this.everyPhetioType = {};
  }

  /**
   * Callback when the simulation is ready to go, and all static PhetioObjects have been created.
   * @param {PhetioEngine} phetioEngine
   * @public
   */
  onSimStarted( phetioEngine ) {
    this.simHasStarted = true;
    if ( this.enabled && phet.joist.sim.allScreensCreated ) {
      this.validateOverridesFile();
    }
  }

  /**
   * Checks if a removed phetioObject is part of a Group
   * @param {PhetioObject} phetioObject
   * @public
   */
  onPhetioObjectRemoved( phetioObject ) {
    if ( !this.enabled ) {
      return;
    }

    const phetioID = phetioObject.tandem.phetioID;

    // if it isn't dynamic, then it shouldn't be removed during the lifetime of the sim.
    if ( !phetioObject.phetioDynamicElement ) {
      this.assertAPIError( {
        phetioID: phetioID,
        ruleInViolation: '2. Any static, registered PhetioObject can never be deregistered.'
      } );
    }
  }

  /**
   * Should be called from phetioEngine when a PhetioObject is added to the PhET-iO
   * @param {PhetioObject} phetioObject
   * @public
   */
  onPhetioObjectAdded( phetioObject ) {
    if ( !this.enabled ) {
      return;
    }

    const newPhetioType = phetioObject.phetioType;
    const oldPhetioType = this.everyPhetioType[ newPhetioType.typeName ];

    if ( !oldPhetioType ) { // This may not be necessary, but may be helpful so that we don't overwrite if rule 10 is in violation
      this.everyPhetioType[ newPhetioType.typeName ] = newPhetioType;
    }

    if ( this.simHasStarted ) {

      // Here we need to kick this validation to the next frame to support construction in any order. Parent first, or
      // child first. Use namespace to avoid because timer is a PhetioObject.
      phet.axon.animationFrameTimer.runOnNextTick( () => {

        // The only instances that it's OK to create after startup are "dynamic instances" which are marked as such.
        if ( !phetioObject.phetioDynamicElement ) {
          this.assertAPIError( {
            phetioID: phetioObject.tandem.phetioID,
            ruleInViolation: '1. After startup, only dynamic instances prescribed by the baseline file can be registered.'
          } );
        }
        else {

          // Compare the dynamic element to the archetype if creating them this runtime. Don't check this if it has
          // already been disposed.
          if ( phet.preloads.phetio.createArchetypes && !phetioObject.isDisposed ) {
            const archetypeID = phetioObject.tandem.getArchetypalPhetioID();
            const archetypeMetadata = phet.phetio.phetioEngine.getPhetioObject( archetypeID ).getMetadata();

            // Compare to the simulation-defined archetype
            checkDynamicInstanceAgainstArchetype( this, phetioObject, archetypeMetadata, 'simulation archetype' );
          }
        }
      } );
    }
  }

  /**
   * @private
   */
  validateOverridesFile() {

    // import phetioEngine causes a cycle and cannot be used, hence we must use the namespace
    const entireBaseline = phet.phetio.phetioEngine.getPhetioElementsBaseline();

    for ( const phetioID in window.phet.preloads.phetio.phetioElementsOverrides ) {
      const isArchetype = phetioID.indexOf( DynamicTandem.DYNAMIC_ARCHETYPE_NAME ) >= 0;
      if ( !phet.preloads.phetio.createArchetypes && !entireBaseline.hasOwnProperty( phetioID ) ) {
        assert && assert( isArchetype, `phetioID missing from the baseline that was not an archetype: ${phetioID}` );
      }
      else {
        if ( !entireBaseline.hasOwnProperty( phetioID ) ) {
          this.assertAPIError( {
            phetioID: phetioID,
            ruleInViolation: '3. Any schema entries in the overrides file must exist in the baseline file.',
            message: 'phetioID expected in the baseline file but does not exist'
          } );
        }
        else {

          const override = window.phet.preloads.phetio.phetioElementsOverrides[ phetioID ];
          const baseline = entireBaseline[ phetioID ];

          if ( Object.keys( override ).length === 0 ) {
            this.assertAPIError( {
              phetioID: phetioID,
              ruleInViolation: '4. Any schema entries in the overrides file must be different from its baseline counterpart.',
              message: 'no metadata keys found for this override.'
            } );
          }

          for ( const metadataKey in override ) {
            if ( !baseline.hasOwnProperty( metadataKey ) ) {
              this.assertAPIError( {
                phetioID: phetioID,
                ruleInViolation: '8. Any schema entries in the overrides file must be different from its baseline counterpart.',
                message: `phetioID metadata key not found in the baseline: ${metadataKey}`
              } );
            }

            if ( override[ metadataKey ] === baseline[ metadataKey ] ) {
              this.assertAPIError( {
                phetioID: phetioID,
                ruleInViolation: '8. Any schema entries in the overrides file must be different from its baseline counterpart.',
                message: 'phetioID metadata override value is the same as the corresponding metadata value in the baseline.'
              } );
            }
          }
        }
      }
    }
  }

  /**
   * Assert out the failed API validation rule.
   * @param {Object} apiErrorObject - see doc for this.apiMismatches
   * @private
   */
  assertAPIError( apiErrorObject ) {

    const mismatchMessage = apiErrorObject.phetioID ? `${apiErrorObject.phetioID}:  ${apiErrorObject.ruleInViolation}` :
                            `${apiErrorObject.ruleInViolation}`;

    console.log( 'error data:', apiErrorObject );
    assert && assert( false, `PhET-iO API error:\n${mismatchMessage}` );
  }
}

/**
 * Compare a dynamic phetioObject's metadata to the expected metadata
 * @param {phetioAPIValidation} phetioAPIValidation
 * @param {PhetioObject} phetioObject
 * @param {Object} archetypeMetadata - from an archetype of the dynamic element
 * @param {string} source - where the archetype came from, for debugging
 */
const checkDynamicInstanceAgainstArchetype = ( phetioAPIValidation, phetioObject, archetypeMetadata, source ) => {
  const actualMetadata = phetioObject.getMetadata();
  KEYS_TO_CHECK.forEach( key => {

    // These attributes are different for archetype vs actual
    if ( key !== 'phetioDynamicElement' && key !== 'phetioArchetypePhetioID' && key !== 'phetioIsArchetype' ) {

      if ( archetypeMetadata[ key ] !== actualMetadata[ key ] ) {
        phetioAPIValidation.assertAPIError( {
          phetioID: phetioObject.tandem.phetioID,
          ruleInViolation: '5. Dynamic element metadata should match the archetype in the API.',
          source: source,
          message: `mismatched metadata: ${key}`
        } );
      }
    }
  } );
};

const phetioAPIValidation = new PhetioAPIValidation();
tandemNamespace.register( 'phetioAPIValidation', phetioAPIValidation );
export default phetioAPIValidation;<commit_msg>update copyright dates from daily grunt work<commit_after>// Copyright 2019-2022, University of Colorado Boulder

/**
 * This singleton is responsible for ensuring that the PhET-iO API is correct through the lifetime of the simulation.
 * The PhET-iO API is defined through multiple preloaded files. The "elements baseline" API holds an exact match of
 * what PhetioObject instances/metadata the sim should create on startup, where the "elements overrides" file is a
 * sparse list that can overwrite metadata without changing the code. See `grunt generate-phet-io-api` for
 * more information. The complete list of checks was decided on in https://github.com/phetsims/phet-io/issues/1453
 * (and later trimmed down) and is as follows:
 *
 * 1. After startup, only dynamic instances prescribed by the baseline API can be registered.
 * 2. Any static, registered PhetioObject can never be deregistered.
 * 3. Any schema entries in the overrides file must exist in the baseline API
 * 4. Any schema entries in the overrides file must be different from its baseline counterpart
 * 5. Dynamic element metadata should match the archetype in the API.
 *
 * Terminology:
 * schema: specified through preloads. The full schema is the baseline plus the overrides, but those parts can be
 *         referred to separately.
 * registered: the process of instrumenting a PhetioObject and it "becoming" a PhET-iO Element on the wrapper side.
 * static PhetioObject: A registered PhetioObject that exists for the lifetime of the sim. It should not be removed
 *                      (even intermittently) and must be created during startup so that it is immediately interoperable.
 * dynamic PhetioObject: A registered PhetioObject that can be created and/or destroyed at any point. Only dynamic
 *                       PhetioObjects can be created after startup.
 *
 * See https://github.com/phetsims/phet-io/issues/1443#issuecomment-484306552 for an explanation of how to maintain the
 * PhET-iO API for a simulation.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Michael Kauzmann (PhET Interactive Simulations)
 * @author Chris Klusendorf (PhET Interactive Simulations)
 */

import DynamicTandem from './DynamicTandem.js';
import Tandem from './Tandem.js';
import tandemNamespace from './tandemNamespace.js';

// constants
// The API-tracked and validated metadata keys
const KEYS_TO_CHECK = [
  'phetioDynamicElement',
  'phetioEventType',
  'phetioIsArchetype',
  'phetioPlayback',
  'phetioReadOnly',
  'phetioState',
  'phetioTypeName'
];

class PhetioAPIValidation {

  constructor() {

    /**
     * {Object[]} - Each object holds a single "API mismatch" with the following keys:
     *                phetioID: {string}
     *                stack: {string} - for a stack trace
     *                ruleInViolation: {string} - one of the numbered list in the header doc.
     *                [message]: {string} - specific problem
     *
     * Feel free to add any other JSONifyable keys to this to make the error more clear! All mismatches are printed
     * at once for clarity, see PhetioEngine.
     * @private
     */
    this.apiMismatches = [];

    // @private - keep track of when the sim has started.
    this.simHasStarted = false;

    // @public {boolean} - settable by qunitStart.js. Validation is only enabled when all screens are present.
    this.enabled = assert && Tandem.VALIDATION;

    // @private {Object.<typeName:string, IOType>} - this must be all phet-io types so that the
    // following would fail: add a phetioType, then remove it, then add a different one under the same typeName.
    // A Note about memory: Every IOType that is loaded as a module is already loaded on the namespace. Therefore
    // this map doesn't add any memory by storing these. The exception to this is parametric IOTypes. It should be
    // double checked that anything being passed into a parametric type is memory safe. As of this writing, only IOTypes
    // are passed to parametric IOTypes, so this pattern remains memory leak free. Furthermore, this list is only
    // populated when `this.enabled`.
    this.everyPhetioType = {};
  }

  /**
   * Callback when the simulation is ready to go, and all static PhetioObjects have been created.
   * @param {PhetioEngine} phetioEngine
   * @public
   */
  onSimStarted( phetioEngine ) {
    this.simHasStarted = true;
    if ( this.enabled && phet.joist.sim.allScreensCreated ) {
      this.validateOverridesFile();
    }
  }

  /**
   * Checks if a removed phetioObject is part of a Group
   * @param {PhetioObject} phetioObject
   * @public
   */
  onPhetioObjectRemoved( phetioObject ) {
    if ( !this.enabled ) {
      return;
    }

    const phetioID = phetioObject.tandem.phetioID;

    // if it isn't dynamic, then it shouldn't be removed during the lifetime of the sim.
    if ( !phetioObject.phetioDynamicElement ) {
      this.assertAPIError( {
        phetioID: phetioID,
        ruleInViolation: '2. Any static, registered PhetioObject can never be deregistered.'
      } );
    }
  }

  /**
   * Should be called from phetioEngine when a PhetioObject is added to the PhET-iO
   * @param {PhetioObject} phetioObject
   * @public
   */
  onPhetioObjectAdded( phetioObject ) {
    if ( !this.enabled ) {
      return;
    }

    const newPhetioType = phetioObject.phetioType;
    const oldPhetioType = this.everyPhetioType[ newPhetioType.typeName ];

    if ( !oldPhetioType ) { // This may not be necessary, but may be helpful so that we don't overwrite if rule 10 is in violation
      this.everyPhetioType[ newPhetioType.typeName ] = newPhetioType;
    }

    if ( this.simHasStarted ) {

      // Here we need to kick this validation to the next frame to support construction in any order. Parent first, or
      // child first. Use namespace to avoid because timer is a PhetioObject.
      phet.axon.animationFrameTimer.runOnNextTick( () => {

        // The only instances that it's OK to create after startup are "dynamic instances" which are marked as such.
        if ( !phetioObject.phetioDynamicElement ) {
          this.assertAPIError( {
            phetioID: phetioObject.tandem.phetioID,
            ruleInViolation: '1. After startup, only dynamic instances prescribed by the baseline file can be registered.'
          } );
        }
        else {

          // Compare the dynamic element to the archetype if creating them this runtime. Don't check this if it has
          // already been disposed.
          if ( phet.preloads.phetio.createArchetypes && !phetioObject.isDisposed ) {
            const archetypeID = phetioObject.tandem.getArchetypalPhetioID();
            const archetypeMetadata = phet.phetio.phetioEngine.getPhetioObject( archetypeID ).getMetadata();

            // Compare to the simulation-defined archetype
            checkDynamicInstanceAgainstArchetype( this, phetioObject, archetypeMetadata, 'simulation archetype' );
          }
        }
      } );
    }
  }

  /**
   * @private
   */
  validateOverridesFile() {

    // import phetioEngine causes a cycle and cannot be used, hence we must use the namespace
    const entireBaseline = phet.phetio.phetioEngine.getPhetioElementsBaseline();

    for ( const phetioID in window.phet.preloads.phetio.phetioElementsOverrides ) {
      const isArchetype = phetioID.indexOf( DynamicTandem.DYNAMIC_ARCHETYPE_NAME ) >= 0;
      if ( !phet.preloads.phetio.createArchetypes && !entireBaseline.hasOwnProperty( phetioID ) ) {
        assert && assert( isArchetype, `phetioID missing from the baseline that was not an archetype: ${phetioID}` );
      }
      else {
        if ( !entireBaseline.hasOwnProperty( phetioID ) ) {
          this.assertAPIError( {
            phetioID: phetioID,
            ruleInViolation: '3. Any schema entries in the overrides file must exist in the baseline file.',
            message: 'phetioID expected in the baseline file but does not exist'
          } );
        }
        else {

          const override = window.phet.preloads.phetio.phetioElementsOverrides[ phetioID ];
          const baseline = entireBaseline[ phetioID ];

          if ( Object.keys( override ).length === 0 ) {
            this.assertAPIError( {
              phetioID: phetioID,
              ruleInViolation: '4. Any schema entries in the overrides file must be different from its baseline counterpart.',
              message: 'no metadata keys found for this override.'
            } );
          }

          for ( const metadataKey in override ) {
            if ( !baseline.hasOwnProperty( metadataKey ) ) {
              this.assertAPIError( {
                phetioID: phetioID,
                ruleInViolation: '8. Any schema entries in the overrides file must be different from its baseline counterpart.',
                message: `phetioID metadata key not found in the baseline: ${metadataKey}`
              } );
            }

            if ( override[ metadataKey ] === baseline[ metadataKey ] ) {
              this.assertAPIError( {
                phetioID: phetioID,
                ruleInViolation: '8. Any schema entries in the overrides file must be different from its baseline counterpart.',
                message: 'phetioID metadata override value is the same as the corresponding metadata value in the baseline.'
              } );
            }
          }
        }
      }
    }
  }

  /**
   * Assert out the failed API validation rule.
   * @param {Object} apiErrorObject - see doc for this.apiMismatches
   * @private
   */
  assertAPIError( apiErrorObject ) {

    const mismatchMessage = apiErrorObject.phetioID ? `${apiErrorObject.phetioID}:  ${apiErrorObject.ruleInViolation}` :
                            `${apiErrorObject.ruleInViolation}`;

    console.log( 'error data:', apiErrorObject );
    assert && assert( false, `PhET-iO API error:\n${mismatchMessage}` );
  }
}

/**
 * Compare a dynamic phetioObject's metadata to the expected metadata
 * @param {phetioAPIValidation} phetioAPIValidation
 * @param {PhetioObject} phetioObject
 * @param {Object} archetypeMetadata - from an archetype of the dynamic element
 * @param {string} source - where the archetype came from, for debugging
 */
const checkDynamicInstanceAgainstArchetype = ( phetioAPIValidation, phetioObject, archetypeMetadata, source ) => {
  const actualMetadata = phetioObject.getMetadata();
  KEYS_TO_CHECK.forEach( key => {

    // These attributes are different for archetype vs actual
    if ( key !== 'phetioDynamicElement' && key !== 'phetioArchetypePhetioID' && key !== 'phetioIsArchetype' ) {

      if ( archetypeMetadata[ key ] !== actualMetadata[ key ] ) {
        phetioAPIValidation.assertAPIError( {
          phetioID: phetioObject.tandem.phetioID,
          ruleInViolation: '5. Dynamic element metadata should match the archetype in the API.',
          source: source,
          message: `mismatched metadata: ${key}`
        } );
      }
    }
  } );
};

const phetioAPIValidation = new PhetioAPIValidation();
tandemNamespace.register( 'phetioAPIValidation', phetioAPIValidation );
export default phetioAPIValidation;<|endoftext|>


<commit_before>// Copyright 2014-2017, University of Colorado Boulder

// RequireJS configuration file for scenery-phet
require.config( {
  deps: [ 'scenery-phet-main' ],

  paths: {

    // third party libs
    text: '../../sherpa/lib/text-2.0.12',

    // plugins
    image: '../../chipper/js/requirejs-plugins/image',
    mipmap: '../../chipper/js/requirejs-plugins/mipmap',
    string: '../../chipper/js/requirejs-plugins/string',
    ifphetio: '../../chipper/js/requirejs-plugins/ifphetio',

    // PhET libs, uppercase names to identify them in require.js imports
    AXON: '../../axon/js',
    BRAND: '../../brand/' + phet.chipper.brand + '/js',
    DOT: '../../dot/js',
    JOIST: '../../joist/js',
    KITE: '../../kite/js',
    PHETCOMMON: '../../phetcommon/js',
    REPOSITORY: '..',
    PHET_CORE: '../../phet-core/js',
    PHET_IO: '../../phet-io/js',
    SCENERY: '../../scenery/js',
    SUN: '../../sun/js',
    TANDEM: '../../tandem/js',
    TWIXT: '../../twixt/js',

    // this runnable
    SCENERY_PHET: '.'
  },

  // optional cache buster to make browser refresh load all included scripts, can be disabled with ?cacheBuster=false
  urlArgs: phet.chipper.getCacheBusterArgs()

} );
<commit_msg>Generated config file, see https://github.com/phetsims/chipper/issues/580<commit_after>// Copyright 2017, University of Colorado Boulder

/*
 * IMPORTANT: This file was auto-generated by "grunt generate-config". Please do not modify this directly. Instead
 * please modify scenery-phet/package.json to control dependencies.
 *
 * RequireJS configuration file for the scenery-phet sim.
 * Paths are relative to the location of this file.
 */

require.config( {

  deps: [ 'scenery-phet-main' ],

  paths: {

    // Third-party libs
    text: '../../sherpa/lib/text-2.0.12',

    // PhET plugins
    audio: '../../chipper/js/requirejs-plugins/audio',
    image: '../../chipper/js/requirejs-plugins/image',
    mipmap: '../../chipper/js/requirejs-plugins/mipmap',
    string: '../../chipper/js/requirejs-plugins/string',
    ifphetio: '../../chipper/js/requirejs-plugins/ifphetio',

    // PhET libs, uppercase names to identify them in require.js imports.
    // IMPORTANT: DO NOT modify. This file is auto-generated. See documentation at the top.
    AXON: '../../axon/js',
    BRAND: '../../brand/' + phet.chipper.brand + '/js',
    DOT: '../../dot/js',
    JOIST: '../../joist/js',
    KITE: '../../kite/js',
    PHETCOMMON: '../../phetcommon/js',
    PHET_CORE: '../../phet-core/js',
    PHET_IO: '../../phet-io/js',
    REPOSITORY: '..',
    SCENERY: '../../scenery/js',
    SCENERY_PHET: '.',
    SUN: '../../sun/js',
    TANDEM: '../../tandem/js',
    TWIXT: '../../twixt/js'
  },

  // optional cache buster to make browser refresh load all included scripts, can be disabled with ?cacheBuster=false
  urlArgs: phet.chipper.getCacheBusterArgs()
} );
<|endoftext|>


<commit_before>var FileNavigator = require('../services/file-navigator');
var _navigator = null;

var _nodeCols = ['id'];
var _edgeCols = ['source', 'target'];

var _file = null, _delimiter = '\t';

var _parseObj = function(keys, vals){

    var obj = {};
    _.each(keys, function(k,i){
        obj[k] = vals[i];
    });

    return obj;
};

// callback: function(err, headers)
var _hasCols = function(cols, cb){

    _navigator.readLines(0, 1, function(err, index, lines, eof){
        if (err) {
            cb(err);
            return;
        }

        var headers = lines[0].toLowerCase().split(_delimiter);

        var missing =[];
        _.each(cols, function(c){
            var i = _.indexOf(headers, c);
            if(i === -1) missing.push(c);
        });

        if(missing.length){
            cb('File '+_file.name+' is missing mandatory column(s) ( ' + missing.join(', ')+' )');
        }else{
            cb(null, headers);
        }

    });
};

//Public members
var reader = function(){};

reader.file = function(_){
    if (!arguments.length)
        return _file;
    _file = _;
    return reader;
};

// callback: function(err, nodes)
reader.parseNodes = function(cb){

    _navigator = new FileNavigator(_file);

    _hasCols(_nodeCols, function(err, headers){
        if(err){
            cb(err);
            return;
        }

        var nodes = [];
        _navigator.readSomeLines(1, function linesHandler(err,index,lines,eof,progress){
            if(err){
                cb(err);
                return;
            }

            _.each(lines, function(l){
                nodes.push(_parseObj(headers, l.split(_delimiter)));
            });

            if(eof){
                cb(null, nodes);
                return;
            }
            _navigator.readSomeLines(index + lines.length, linesReadHandler);

        });
    });
};

// callback: function(err, edges)
reader.parseEdges = function(cb){

    _navigator = new FileNavigator(_file);

    _hasCols(_edgeCols, function(err, headers){
        if(err){
            cb(err);
            return;
        }

        var nodes = [];
        _navigator.readSomeLines(1, function linesHandler(err,index,lines,eof,progress){
            if(err){
                cb(err);
                return;
            }

            _.each(lines, function(l){
                nodes.push(_parseObj(headers, l.split(_delimiter)));
            });

            if(eof){
                cb(null, nodes);
                return;
            }
            _navigator.readSomeLines(index + lines.length, linesReadHandler);

        });
    });
};

module.exports = reader;<commit_msg>implemented function to parse sif files<commit_after>var SIFJS = require('sif.js');
var FileNavigator = require('../services/file-navigator');
var _navigator = null;

var _nodeCols = ['id'];
var _edgeCols = ['source', 'target'];

var _file = null, _delimiter = '\t';

var _parseObj = function(keys, vals){

    var obj = {};
    _.each(keys, function(k,i){
        obj[k] = vals[i];
    });

    return obj;
};

// callback: function(err, headers)
var _hasCols = function(cols, cb){

    _navigator.readLines(0, 1, function(err, index, lines, eof){
        if (err) {
            cb(err);
            return;
        }

        var headers = lines[0].toLowerCase().split(_delimiter);

        var missing =[];
        _.each(cols, function(c){
            var i = _.indexOf(headers, c);
            if(i === -1) missing.push(c);
        });

        if(missing.length){
            cb('File '+_file.name+' is missing mandatory column(s) ( ' + missing.join(', ')+' )');
        }else{
            cb(null, headers);
        }

    });
};

//Public members
var reader = function(){};

reader.file = function(_){
    if (!arguments.length)
        return _file;
    _file = _;
    return reader;
};

// callback: function(err, nodes)
reader.parseNodes = function(cb){

    _navigator = new FileNavigator(_file);

    _hasCols(_nodeCols, function(err, headers){
        if(err){
            cb(err);
            return;
        }

        var nodes = [];
        _navigator.readSomeLines(1, function linesHandler(err,index,lines,eof,progress){
            if(err){
                cb(err);
                return;
            }

            _.each(lines, function(l){
                nodes.push(_parseObj(headers, l.split(_delimiter)));
            });

            if(eof){
                cb(null, nodes);
                return;
            }
            _navigator.readSomeLines(index + lines.length, linesReadHandler);

        });
    });
};

// callback: function(err, edges)
reader.parseEdges = function(cb){

    _navigator = new FileNavigator(_file);

    _hasCols(_edgeCols, function(err, headers){
        if(err){
            cb(err);
            return;
        }

        var nodes = [];
        _navigator.readSomeLines(1, function linesHandler(err,index,lines,eof,progress){
            if(err){
                cb(err);
                return;
            }

            _.each(lines, function(l){
                nodes.push(_parseObj(headers, l.split(_delimiter)));
            });

            if(eof){
                cb(null, nodes);
                return;
            }
            _navigator.readSomeLines(index + lines.length, linesReadHandler);

        });
    });
};

// callback: function(err, elements)
reader.parseSIF = function(cb){
    _navigator = new FileNavigator(_file);
    var linesArr = [];

    _navigator.readSomeLines(1, function linesHandler(err,index,lines,eof,progress){
        if(err){
            cb(err);
            return;
        }

        linesArr = linesArr.concat(lines);

        if(eof){
            cb(null, SIFJS.parse(linesArr.join('\n')));
            return;
        }
        _navigator.readSomeLines(index + lines.length, linesReadHandler);
    });
};

module.exports = reader;<|endoftext|>


<commit_before><commit_msg>make scatter plot for selection population page<commit_after><|endoftext|>


<commit_before>$(function () {
  'use strict';

  module('collapse plugin')

  test('should be defined on jquery object', function () {
    ok($(document.body).collapse, 'collapse method is defined')
  })

  module('collapse', {
    setup: function () {
      // Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
      $.fn.bootstrapCollapse = $.fn.collapse.noConflict()
    },
    teardown: function () {
      $.fn.collapse = $.fn.bootstrapCollapse
      delete $.fn.bootstrapCollapse
    }
  })

  test('should provide no conflict', function () {
    ok(!$.fn.collapse, 'collapse was set back to undefined (org value)')
  })

  test('should return element', function () {
    ok($(document.body).bootstrapCollapse()[0] == document.body, 'document.body returned')
  })

  test('should show a collapsed element', function () {
    var el = $('

').bootstrapCollapse('show')
    ok(el.hasClass('in'), 'has class in')
    ok(!/height/.test(el.attr('style')), 'has height reset')
  })

  test('should hide a collapsed element', function () {
    var el = $('

').bootstrapCollapse('hide')
    ok(!el.hasClass('in'), 'does not have class in')
    ok(/height/.test(el.attr('style')), 'has height set')
  })

  test('should not fire shown when show is prevented', function () {
    $.support.transition = false
    stop()
    $('
')
      .on('show.bs.collapse', function (e) {
        e.preventDefault()
        ok(true)
        start()
      })
      .on('shown.bs.collapse', function () {
        ok(false)
      })
      .bootstrapCollapse('show')
  })

  test('should reset style to auto after finishing opening collapse', function () {
    $.support.transition = false
    stop()
    $('
')
      .on('show.bs.collapse', function () {
        ok(this.style.height == '0px')
      })
      .on('shown.bs.collapse', function () {
        ok(this.style.height === '')
        start()
      })
      .bootstrapCollapse('show')
  })

  test('should add active class to target when collapse shown', function () {
    $.support.transition = false
    stop()

    var target = $('')
      .appendTo($('#qunit-fixture'))

    $('

')
      .appendTo($('#qunit-fixture'))
      .on('show.bs.collapse', function () {
        ok(!target.hasClass('collapsed'))
        start()
      })

    target.click()
  })

  test('should remove active class to target when collapse hidden', function () {
    $.support.transition = false
    stop()

    var target = $('')
      .appendTo($('#qunit-fixture'))

    $('

')
      .appendTo($('#qunit-fixture'))
      .on('hide.bs.collapse', function () {
        ok(target.hasClass('collapsed'))
        start()
      })

    target.click()
  })

  test('should remove active class from inactive accordion targets', function () {
    $.support.transition = false
    stop()

    var accordion = $('

')
      .appendTo($('#qunit-fixture'))

    var target1 = $('')
      .appendTo(accordion.find('.accordion-group').eq(0))

    $('

')
      .appendTo(accordion.find('.accordion-group').eq(0))

    var target2 = $('')
      .appendTo(accordion.find('.accordion-group').eq(1))

    $('

')
      .appendTo(accordion.find('.accordion-group').eq(1))

    var target3 = $('')
      .appendTo(accordion.find('.accordion-group').eq(2))

    $('

')
      .appendTo(accordion.find('.accordion-group').eq(2))
      .on('show.bs.collapse', function () {
        ok(target1.hasClass('collapsed'))
        ok(target2.hasClass('collapsed'))
        ok(!target3.hasClass('collapsed'))

        start()
      })

    target3.click()
  })

  test('should allow dots in data-parent', function () {
    $.support.transition = false
    stop()

    var accordion = $('

')
      .appendTo($('#qunit-fixture'))

    var target1 = $('')
      .appendTo(accordion.find('.accordion-group').eq(0))

    $('

')
      .appendTo(accordion.find('.accordion-group').eq(0))

    var target2 = $('')
      .appendTo(accordion.find('.accordion-group').eq(1))

    $('

')
      .appendTo(accordion.find('.accordion-group').eq(1))

    var target3 = $('')
      .appendTo(accordion.find('.accordion-group').eq(2))

    $('

')
      .appendTo(accordion.find('.accordion-group').eq(2))
      .on('show.bs.collapse', function () {
        ok(target1.hasClass('collapsed'))
        ok(target2.hasClass('collapsed'))
        ok(!target3.hasClass('collapsed'))

        start()
      })

    target3.click()
  })

})
<commit_msg>Clean collapse unit tests up<commit_after>$(function () {
  'use strict';

  module('collapse plugin')

  test('should be defined on jquery object', function () {
    ok($(document.body).collapse, 'collapse method is defined')
  })

  module('collapse', {
    setup: function () {
      // Run all tests in noConflict mode -- it's the only way to ensure that the plugin works in noConflict mode
      $.fn.bootstrapCollapse = $.fn.collapse.noConflict()
    },
    teardown: function () {
      $.fn.collapse = $.fn.bootstrapCollapse
      delete $.fn.bootstrapCollapse
    }
  })

  test('should provide no conflict', function () {
    strictEqual($.fn.collapse, undefined, 'collapse was set back to undefined (org value)')
  })

  test('should return jquery collection containing the element', function () {
    var $el = $('
')
    var $collapse = $el.bootstrapCollapse()
    ok($collapse instanceof $, 'returns jquery collection')
    strictEqual($collapse[0], $el[0], 'collection contains element')
  })

  test('should show a collapsed element', function () {
    var $el = $('
').bootstrapCollapse('show')

    ok($el.hasClass('in'), 'has class "in"')
    ok(!/height/.test($el.attr('style')), 'has height reset')
  })

  test('should hide a collapsed element', function () {
    var $el = $('
').bootstrapCollapse('hide')

    ok(!$el.hasClass('in'), 'does not have class "in"')
    ok(/height/.test($el.attr('style')), 'has height set')
  })

  test('should not fire shown when show is prevented', function () {
    stop()

    $('
')
      .on('show.bs.collapse', function (e) {
        e.preventDefault()
        ok(true, 'show event fired')
        start()
      })
      .on('shown.bs.collapse', function () {
        ok(false, 'shown event fired')
      })
      .bootstrapCollapse('show')
  })

  test('should reset style to auto after finishing opening collapse', function () {
    stop()

    $('
')
      .on('show.bs.collapse', function () {
        equal(this.style.height, '0px', 'height is 0px')
      })
      .on('shown.bs.collapse', function () {
        strictEqual(this.style.height, '', 'height is auto')
        start()
      })
      .bootstrapCollapse('show')
  })

  test('should remove "collapsed" class from target when collapse is shown', function () {
    stop()

    var $target = $('').appendTo('#qunit-fixture')

    $('
')
      .appendTo('#qunit-fixture')
      .on('show.bs.collapse', function () {
        ok(!$target.hasClass('collapsed'))
        start()
      })

    $target.click()
  })

  test('should add "collapsed" class to target when collapse is hidden', function () {
    stop()

    var $target = $('').appendTo('#qunit-fixture')

    $('
')
      .appendTo('#qunit-fixture')
      .on('hide.bs.collapse', function () {
        ok($target.hasClass('collapsed'))
        start()
      })

    $target.click()
  })

  test('should remove "collapsed" class from active accordion target', function () {
    stop()

    var accordionHTML = '
' +
        '
' +
        '
' +
        '
' +
        '
'
    var $groups = $(accordionHTML).appendTo('#qunit-fixture').find('.accordion-group')

    var $target1 = $('').appendTo($groups.eq(0))

    $('
').appendTo($groups.eq(0))

    var $target2 = $('').appendTo($groups.eq(1))

    $('
').appendTo($groups.eq(1))

    var $target3 = $('').appendTo($groups.eq(2))

    $('
')
      .appendTo($groups.eq(2))
      .on('show.bs.collapse', function () {
        ok($target1.hasClass('collapsed'), 'inactive target 1 does have class "collapsed"')
        ok($target2.hasClass('collapsed'), 'inactive target 2 does have class "collapsed"')
        ok(!$target3.hasClass('collapsed'), 'active target 3 does not have class "collapsed"')

        start()
      })

    $target3.click()
  })

  test('should allow dots in data-parent', function () {
    stop()

    var accordionHTML = '
' +
        '
' +
        '
' +
        '
' +
        '
'
    var $groups = $(accordionHTML).appendTo('#qunit-fixture').find('.accordion-group')

    var $target1 = $('').appendTo($groups.eq(0))

    $('
').appendTo($groups.eq(0))

    var $target2 = $('').appendTo($groups.eq(1))

    $('
').appendTo($groups.eq(1))

    var $target3 = $('').appendTo($groups.eq(2))

    $('
')
      .appendTo($groups.eq(2))
      .on('show.bs.collapse', function () {
        ok($target1.hasClass('collapsed'), 'inactive target 1 does have class "collapsed"')
        ok($target2.hasClass('collapsed'), 'inactive target 2 does have class "collapsed"')
        ok(!$target3.hasClass('collapsed'), 'active target 3 does not have class "collapsed"')

        start()
      })

    $target3.click()
  })

})
<|endoftext|>


<commit_before>define(function(require) {
	var registerSuite = require('intern!object'),
		assert = require('intern/chai!assert');

	require('js/tests/support/exports.js');

	registerSuite({
		name: 'Data',
		request: {
			afterEach: function() {
				Wee.$set('done', false);
			},

			get: {
				standard: function() {
					var promise = this.async(1000);

					Wee.data.request({
						url: '/js/tests/support/sample-files/sample.json',
						json: true,
						success: promise.callback(function(data) {
							assert.strictEqual(data.person.firstName, 'Don',
								'Sample file was not loaded successfully'
							);

							Wee.$set('done', true);
						}),
						complete: promise.callback(function() {
							assert.isTrue(Wee.$get('done'),
								'Complete callback was not executed correctly'
							);
						})
					});

					Wee.data.request({
						url: 'js/tests/support/sample-files/sample.json',
						json: true,
						cache: false,
						send: promise.callback(function() {
							// TODO: Find a better way to test this
							// TODO: Use state variable to verify all lifecycle methods
							assert.isTrue(true);
						}),
						responseType: 'text',
						success: promise.callback(function(data) {
							assert.strictEqual(data.person.firstName, 'Don',
								'Sample file was not loaded successfully'
							);
						})
					});
				},
				'with data': function() {
					var promise = this.async(1000);

					Wee.data.request({
						url: 'https://httpbin.org/get',
						json: true,
						data: {
							test: 'test'
						},
						success: promise.callback(function(data) {
							assert.strictEqual(data.args.test, 'test',
								'Data posted successfully'
							);

							Wee.$set('done', true);
						}),
						complete: function() {
							assert.isTrue(Wee.$get('done'),
								'Complete callback called on error'
							);
						}
					});
				},
				'query string': function() {
					var promise = this.async(1000);

					Wee.data.request({
						url: 'https://httpbin.org/get?test=test',
						json: true,
						success: promise.callback(function(data) {
							assert.strictEqual(data.args.test, 'test',
								'Data posted unsuccessfully'
							);
						})
					});
				},
			},
			post: {
				simple: function() {
					var promise = this.async(1000);

					Wee.data.request({
						root: 'https://httpbin.org/',
						url: 'post',
						method: 'post',
						json: true,
						data: {
							test: 'test'
						},
						success: promise.callback(function(data) {
							var response = JSON.parse(data.data);

							assert.strictEqual(response.test, 'test',
								'Data posted successfully'
							);
						})
					});
				},
				type: {
					form: function() {
						var promise = this.async(1000);

						Wee.data.request({
							url: 'https://httpbin.org/post',
							method: 'post',
							json: true,
							type: 'form',
							data: {
								test: 'test'
							},
							success: promise.callback(function(data) {
								assert.strictEqual(data.form.test, 'test',
									'Data posted successfully'
								);

								assert.strictEqual(data.headers['Content-Type'],
									'application/x-www-form-urlencoded; charset=UTF-8',
									'Incorrect headers sent'
								);
							})
						});
					},
					html: function() {
						var promise = this.async(1000);

						Wee.data.request({
							url: 'https://httpbin.org/post',
							method: 'post',
							json: true,
							type: 'xml',
							success: promise.callback(function(data) {
								assert.strictEqual(data.headers['Content-Type'],
									'text/xml; charset=UTF-8',
									'Incorrect headers sent'
								);
							})
						});
					},
					json: function() {
						var promise = this.async(1000);

						Wee.data.request({
							url: 'https://httpbin.org/post',
							method: 'post',
							json: true,
							type: 'json',
							success: promise.callback(function(data) {
								assert.equal(data.data, '{}',
									'Data posted successfully'
								);

								assert.strictEqual(data.headers['Content-Type'],
									'application/json; charset=UTF-8',
									'Incorrect headers sent'
								);
							})
						});
					},
					xml: function() {
						var promise = this.async(1000);

						Wee.data.request({
							url: 'https://httpbin.org/post',
							method: 'post',
							json: true,
							type: 'xml',
							success: promise.callback(function(data) {
								assert.strictEqual(data.headers['Content-Type'],
									'text/xml; charset=UTF-8',
									'Incorrect headers sent'
								);
							})
						});
					}
				},
				error: {
					// TODO: Find out why this is timing out
					'not found': function() {
						var promise = this.async(1000);

						Wee.data.request({
							url: 'asdfasdf.com',
							method: 'get',
							json: true,
							success: promise.callback(function() {
								assert.isTrue(true,
									'Success on 404'
								);
							}),
							error: promise.callback(function(data) {
								assert.strictEqual(data.status, 404,
									'404 status not processed correctly'
								);
							})
						});
					},

					'invalid json': function() {
						var promise = this.async(1000);

						Wee.data.request({
							url: '/js/tests/support/sample-files/invalid-json.json',
							json: true,
							success: promise.callback(function(data) {
								assert.isObject(data, {},
									'Invalid JSON parsed'
								);

								assert.equal(Object.keys(data).length, 0,
									'Invalid JSON parsed'
								);
							})
						});
					}
				}
			}
		}
	});
});<commit_msg>Test JSONP<commit_after>define(function(require) {
	var registerSuite = require('intern!object'),
		assert = require('intern/chai!assert');

	require('js/tests/support/exports.js');

	registerSuite({
		name: 'Data',
		request: {
			afterEach: function() {
				Wee.$set('done', false);
			},

			get: {
				standard: function() {
					var promise = this.async(1000);

					Wee.data.request({
						url: '/js/tests/support/sample-files/sample.json',
						json: true,
						success: promise.callback(function(data) {
							assert.strictEqual(data.person.firstName, 'Don',
								'Sample file was not loaded successfully'
							);

							Wee.$set('done', true);
						}),
						complete: promise.callback(function() {
							assert.isTrue(Wee.$get('done'),
								'Complete callback was not executed correctly'
							);
						})
					});

					Wee.data.request({
						url: 'js/tests/support/sample-files/sample.json',
						json: true,
						cache: false,
						send: promise.callback(function() {
							// TODO: Find a better way to test this
							// TODO: Use state variable to verify all lifecycle methods
							assert.isTrue(true);
						}),
						responseType: 'text',
						success: promise.callback(function(data) {
							assert.strictEqual(data.person.firstName, 'Don',
								'Sample file was not loaded successfully'
							);
						})
					});
				},
				'with data': function() {
					var promise = this.async(1000);

					Wee.data.request({
						url: 'https://httpbin.org/get',
						json: true,
						data: {
							test: 'test'
						},
						success: promise.callback(function(data) {
							assert.strictEqual(data.args.test, 'test',
								'Data posted successfully'
							);

							Wee.$set('done', true);
						}),
						complete: function() {
							assert.isTrue(Wee.$get('done'),
								'Complete callback called on error'
							);
						}
					});
				},
				'query string': function() {
					var promise = this.async(1000);

					Wee.data.request({
						url: 'https://httpbin.org/get?test=test',
						json: true,
						success: promise.callback(function(data) {
							assert.strictEqual(data.args.test, 'test',
								'Data posted unsuccessfully'
							);
						})
					});
				},
				jsonp: function() {
					var promise = this.async(1000);

					Wee.data.request({
						url: 'http://api-test.dev/get/jsonp',
						method: 'get',
						jsonp: 'cb',
						success: promise.callback(function(data) {
							assert.strictEqual(data.key, 'value',
								'JSONP was not executed correctly'
							);
						})
					});

					Wee.data.request({
						url: 'http://api-test.dev/get/jsonp123',
						method: 'get',
						jsonp: 'cb',
						error: promise.callback(function(data) {
							// TODO: test this better - ask nathan to gander at it
							assert.strictEqual(data.key, 'value',
								'JSONP was not executed correctly'
							);
						})
					});
				}
			},
			post: {
				simple: function() {
					var promise = this.async(1000);

					Wee.data.request({
						root: 'https://httpbin.org/',
						url: 'post',
						method: 'post',
						json: true,
						data: {
							test: 'test'
						},
						success: promise.callback(function(data) {
							var response = JSON.parse(data.data);

							assert.strictEqual(response.test, 'test',
								'Data posted successfully'
							);
						})
					});
				},
				type: {
					form: function() {
						var promise = this.async(1000);

						Wee.data.request({
							url: 'https://httpbin.org/post',
							method: 'post',
							json: true,
							type: 'form',
							data: {
								test: 'test'
							},
							success: promise.callback(function(data) {
								assert.strictEqual(data.form.test, 'test',
									'Data posted successfully'
								);

								assert.strictEqual(data.headers['Content-Type'],
									'application/x-www-form-urlencoded; charset=UTF-8',
									'Incorrect headers sent'
								);
							})
						});
					},
					html: function() {
						var promise = this.async(1000);

						Wee.data.request({
							url: 'https://httpbin.org/post',
							method: 'post',
							json: true,
							type: 'xml',
							success: promise.callback(function(data) {
								assert.strictEqual(data.headers['Content-Type'],
									'text/xml; charset=UTF-8',
									'Incorrect headers sent'
								);
							})
						});
					},
					json: function() {
						var promise = this.async(1000);

						Wee.data.request({
							url: 'https://httpbin.org/post',
							method: 'post',
							json: true,
							type: 'json',
							success: promise.callback(function(data) {
								assert.equal(data.data, '{}',
									'Data posted successfully'
								);

								assert.strictEqual(data.headers['Content-Type'],
									'application/json; charset=UTF-8',
									'Incorrect headers sent'
								);
							})
						});
					},
					xml: function() {
						var promise = this.async(1000);

						Wee.data.request({
							url: 'https://httpbin.org/post',
							method: 'post',
							json: true,
							type: 'xml',
							success: promise.callback(function(data) {
								assert.strictEqual(data.headers['Content-Type'],
									'text/xml; charset=UTF-8',
									'Incorrect headers sent'
								);
							})
						});
					}
				},
				error: {
					// TODO: Find out why this is timing out
					'not found': function() {
						var promise = this.async(1000);

						Wee.data.request({
							url: 'asdfasdf.com',
							method: 'get',
							json: true,
							success: promise.callback(function() {
								assert.isTrue(true,
									'Success on 404'
								);
							}),
							error: promise.callback(function(data) {
								assert.strictEqual(data.status, 404,
									'404 status not processed correctly'
								);
							})
						});
					},

					'invalid json': function() {
						var promise = this.async(1000);

						Wee.data.request({
							url: '/js/tests/support/sample-files/invalid-json.json',
							json: true,
							success: promise.callback(function(data) {
								assert.isObject(data, {},
									'Invalid JSON parsed'
								);

								assert.equal(Object.keys(data).length, 0,
									'Invalid JSON parsed'
								);
							})
						});
					}
				}
			}
		}
	});
});<|endoftext|>


<commit_before>Ext.namespace('ui','ui.task');

// config - { prefix, ftype, fid, fpath, fname }
ui.task.GetFileTask = function(config)
{
    Ext.apply(this, config);

    var id_prefix    = this.prefix + '-' + this.ftype,
        readOriginal = (this.ftype == 'NotInEN') ? true : false;

    // Mask the panel
    Ext.get(id_prefix + '-PANEL-' + this.fid).mask(
        ' '+
        _('Loading...')
    );

    // We load the File
    XHR({
        scope  : this,
        params : {
            task     : 'getFile',
            FilePath : this.fpath,
            FileName : this.fname,
            readOriginal: readOriginal
        },
        success : function(response)
        {
            var o    = Ext.util.JSON.decode(response.responseText),
                path = 'http://' + window.location.host + ':' + window.location.port + window.location.pathname
                       + '?perm=/' + this.fpath.split('/')[0] + '/' + o.xmlid.split('|')[0] + '.php',
                perm = ' permlink',
                p    = Ext.getCmp(id_prefix + '-PANEL-' + this.fid);

            // We set the permLink (exclude for file patch)
            if( this.prefix === 'PP' && this.ftype === 'ORIGIN' ) {
                p.permlink = (o.xmlid != 'NULL')? perm : '';
                p.setTitle(p.originTitle + p.permlink);
            }

            // We define the content into the editor
            Ext.getCmp(id_prefix + '-FILE-' + this.fid).setCode(o.content);

            // Remove the mask from the editor
            Ext.get(id_prefix + '-PANEL-' + this.fid).unmask();

            if( o.warn_tab ) {

                // Display a warn message if this file containes some tab caracter.
                Ext.MessageBox.show({
                    title: 'Warning',
                    msg: String.format(_('The file  {0} contains some tab caracters.
The editor have replace it with space caracters.'), this.fpath+this.fname),
                    buttons: Ext.MessageBox.OK,
                    icon: Ext.MessageBox.WARNING
                });

            }

        },
        callback : function()
        {
            // Reviewed function to open all files of an extension
            if (phpDoc.filePendingOpen[0]) {
                phpDoc.filePendingOpen.shift();
                if (phpDoc.filePendingOpen[0]) {
                    if (phpDoc.filePendingOpen[0].fpath) {
                        ui.component.RepositoryTree.getInstance().openFile(
                            phpDoc.filePendingOpen[0].fpath,
                            phpDoc.filePendingOpen[0].fname
                        );
                    }
                }
            }
        }
    });
};
<commit_msg>Fix a bug on permlink introduice by me a few commit later<commit_after>Ext.namespace('ui','ui.task');

// config - { prefix, ftype, fid, fpath, fname }
ui.task.GetFileTask = function(config)
{
    Ext.apply(this, config);

    var id_prefix    = this.prefix + '-' + this.ftype,
        readOriginal = (this.ftype == 'NotInEN') ? true : false;

    // Mask the panel
    Ext.get(id_prefix + '-PANEL-' + this.fid).mask(
        ' '+
        _('Loading...')
    );

    // We load the File
    XHR({
        scope  : this,
        params : {
            task     : 'getFile',
            FilePath : this.fpath,
            FileName : this.fname,
            readOriginal: readOriginal
        },
        success : function(response)
        {
            var o    = Ext.util.JSON.decode(response.responseText),
                path = 'http://' + window.location.host + ':' + window.location.port + window.location.pathname
                       + '?perm=/' + this.fpath.split('/')[0] + '/' + o.xmlid.split('|')[0] + '.php',
                perm = ' permlink',
                p    = Ext.getCmp(id_prefix + '-PANEL-' + this.fid);

            // We set the permLink (exclude for file patch)
            if( this.prefix === 'PP' && this.ftype === 'ORIGIN' ) {
                p.permlink = '';
            } else {
                p.permlink = (o.xmlid != 'NULL')? perm : '';
                p.setTitle(p.originTitle + p.permlink);
            }

            // We define the content into the editor
            Ext.getCmp(id_prefix + '-FILE-' + this.fid).setCode(o.content);

            // Remove the mask from the editor
            Ext.get(id_prefix + '-PANEL-' + this.fid).unmask();

            if( o.warn_tab ) {

                // Display a warn message if this file containes some tab caracter.
                Ext.MessageBox.show({
                    title: 'Warning',
                    msg: String.format(_('The file  {0} contains some tab caracters.
The editor have replace it with space caracters.'), this.fpath+this.fname),
                    buttons: Ext.MessageBox.OK,
                    icon: Ext.MessageBox.WARNING
                });

            }

        },
        callback : function()
        {
            // Reviewed function to open all files of an extension
            if (phpDoc.filePendingOpen[0]) {
                phpDoc.filePendingOpen.shift();
                if (phpDoc.filePendingOpen[0]) {
                    if (phpDoc.filePendingOpen[0].fpath) {
                        ui.component.RepositoryTree.getInstance().openFile(
                            phpDoc.filePendingOpen[0].fpath,
                            phpDoc.filePendingOpen[0].fname
                        );
                    }
                }
            }
        }
    });
};
<|endoftext|>


<commit_before><commit_msg>updating data types function<commit_after><|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

import {render} from "inferno";
import {h} from "inferno-hyperscript";

/** Base class for editor views using the virtual DOM.
 *
 * @category view
 */
export class VirtualDOMView {

    /** Create a new virtual DOM view.
     *
     * @param {HTMLElement} container - The parent HTML element that will contain this view.
     * @param {Controller} controller - The controller that will manage the user actions from this view.
     * @param {}
     */
    constructor(container, controller) {
        /** The parent HTML element that will contain this view.
         * @type {HTMLElement} */
        this.container = container;

        /** The controller that will manage the user actions from this view.
         * @type {Controller} */
        this.controller = controller;

        /** Form field values that need to be set after rendering.
         * @type {Object} */
        this.state = {};

        const repaintHandler = () => this.repaint();
        controller.addListener("repaint", repaintHandler);
        window.addEventListener("resize", repaintHandler);

        while (container.firstChild) {
            container.removeChild(container.firstChild);
        }
    }

    /** Repaint this view.
     *
     * This will render the current view using the result of {@link VirtualDOMView#render}.
     */
    repaint() {
        render(this.render(), this.container, () => {
            for (let prop in this.state) {
                const elt = document.getElementById("field-" + prop);
                if (elt) {
                    for (let attr in this.state[prop]) {
                        elt[attr] = this.state[prop][attr];
                    }
                }
            }
        });
    }

    /** Render this view as a virtual DOM tree.
     *
     * @return A virtual DOM tree for this view.
     */
    render() {
        return h("div");
    }
}
<commit_msg>JSDoc fix<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

import {render} from "inferno";
import {h} from "inferno-hyperscript";

/** Base class for editor views using the virtual DOM.
 *
 * @category view
 */
export class VirtualDOMView {

    /** Create a new virtual DOM view.
     *
     * @param {HTMLElement} container - The parent HTML element that will contain this view.
     * @param {Controller} controller - The controller that will manage the user actions from this view.
     */
    constructor(container, controller) {
        /** The parent HTML element that will contain this view.
         * @type {HTMLElement} */
        this.container = container;

        /** The controller that will manage the user actions from this view.
         * @type {Controller} */
        this.controller = controller;

        /** Form field values that need to be set after rendering.
         * @type {Object} */
        this.state = {};

        const repaintHandler = () => this.repaint();
        controller.addListener("repaint", repaintHandler);
        window.addEventListener("resize", repaintHandler);

        while (container.firstChild) {
            container.removeChild(container.firstChild);
        }
    }

    /** Repaint this view.
     *
     * This will render the current view using the result of {@link VirtualDOMView#render}.
     */
    repaint() {
        render(this.render(), this.container, () => {
            for (let prop in this.state) {
                const elt = document.getElementById("field-" + prop);
                if (elt) {
                    for (let attr in this.state[prop]) {
                        elt[attr] = this.state[prop][attr];
                    }
                }
            }
        });
    }

    /** Render this view as a virtual DOM tree.
     *
     * @return A virtual DOM tree for this view.
     */
    render() {
        return h("div");
    }
}
<|endoftext|>