<commit_before>/*
	jQuery.Typograf.js

	https://github.com/Spearance/jQuery.Typograf.js
	http://www.typograf.ru/

	Copyright 2015, Evgeniy Lepeshkin

	Released under the MIT license.
	http://www.opensource.org/licenses/mit-license.php

	Version: v 0.1
	Date: Thu Mar 24, 2015
 */

(function($){
	$.fn.typograf = function(options){
		options = $.extend({
			leftQuote: "«",
			rightQuote: "»",
			handler: "",
			restor: ""
		}, options);

		return this.each(function(){
			var $this = $(this),
			    obj = $this.get(0),
			    exclude = false,
			    selected = false,
			    caretPosition = 0,
			    original;

			// exist Typograf handler
			if (options.handler) {
				original = "";

				$(options.handler).click(function(){
					if (original == "") {
						original = $this.val();
					}

					if (original) {
						if (options.restore) {
							$(options.restore).removeAttr("disabled");
						}
						typograf($this);
					}
				});

				// exist Restore handler
				if (options.restore) {
					$(options.restore).click(function(){
						$(this).attr("disabled", "true");
						$this.val(original);
					});
				}
			} else {
				// auto
				$this
					.keydown(function(e){
						// check for Ctrl/⌘
						exclude = Boolean(
							(e.metaKey || e.ctrlKey) &&
							!(e.altKey && e.ctrlKey) // Do not exclude Alt Gr
						);
					})
					.keyup(function(e){
						// check for selected text
						selected = Boolean(getSelectionText() != "");

						// exclude arrows and Ctrl/⌘ + A/C/V/X/Z
						if (
							// home, end, page up, page down
							e.which != "33" &&
							e.which != "34" &&
							e.which != "35" &&
							e.which != "36" &&

							// arrows
							e.which != "37" &&
							e.which != "38" &&
							e.which != "39" &&
							e.which != "40" &&

							// single Shift
							e.which != "16" &&

							// select all, copy, paste, cut, undo
							!(exclude && (e.altKey ||
							              e.which == "65" ||
							              e.which == "67" ||
							              e.which == "86" ||
							              e.which == "88" ||
							              e.which == "90")) &&

							// selected text
							!selected
						){
							typograf($this);
						}
						return true;
					}
				);
			}

			function typograf (textField){
				var text = textField.val();
				caretPosition = getCaretPosition(obj);

				text = text
					// Dash
					.replace(/(^|\n|\s|>)\-(\s)/g, "$1—$2")

					// Minus
					.replace(/\-{2} /g, function(){
						caretPosition--;
						return "— ";
					})

					// HTML-comment
					.replace(/<!—/ig, function(){
						caretPosition++;
						return "<!--";
					})

					// Multiple spaces
					.replace(/ {2,}/g, function(str){
						caretPosition -= str.length - 1;
						return " ";
					})

					// Multiple nbsp
					.replace(/ {2,}| \s|\s /g, function(str){
						caretPosition -= str.length - 1;
						return " ";
					})

					// Numerical interval
					.replace(/(\d)( )?[-—]( )?(\d)/g, function(str, $1, $2, $3, $4){
						if ($2 == " ") { caretPosition-- }
						if ($3 == " ") { caretPosition-- }
						return $1 + "–" + $4;
					})

					// Copyright
					.replace(/\([cс]\)/ig, function(){
						caretPosition -= 2;
						return "©";
					})

					// Registered trademark
					.replace(/\(r\)/ig, function(){
						caretPosition -= 2;
						return "®";
					})

					// Trademark
					.replace(/\(tm\)/ig, function(){
						caretPosition -= 2;
						return "™";
					})

					// Rouble
					.replace(/\([рp]\)/ig, function(){
						caretPosition -= 2;
						return "₽";
					})

					// Three dots
					.replace(/\.{3}/g, function(){
						caretPosition -= 2;
						return "…";
					})

					// Sizes
					.replace(/(\d)[xх](\d)/ig, "$1×$2")

					// Open quote
					.replace(/\"([a-zа-я0-9…])/ig,
					         options.leftQuote + "$1")

					// Close quote
					.replace(/([a-zа-я0-9…?!])\"/ig,
					         "$1" + options.rightQuote)

					// Open quote
					.replace(new RegExp("\"(" + options.leftQuote + "[a-zа-я0-9…])", "ig"),
					         options.leftQuote + "$1")

					// Close quote
					.replace(new RegExp("([a-zа-я0-9…?!]" + options.rightQuote + ")\"", "ig"),
					         "$1" + options.rightQuote)

					// Fix HTML open quotes
					.replace(new RegExp("([-a-z0-9]+=)" +
					                    "["   + options.leftQuote + options.rightQuote + "]" +
					                    "([^" + options.leftQuote + options.rightQuote + "]*?)", "ig"),
					         "$1\"$2")

					// Fix HTML close quotes
					.replace(new RegExp("([-a-z0-9]+=)[\"]" +
					                    "([^" + options.leftQuote + options.rightQuote + "]*?)" +
					                    "["   + options.leftQuote + options.rightQuote + "]", "ig"),
					         "$1\"$2\"");

				textField.val(text);

				if (!exclude) {
					setCaretPosition(obj, caretPosition);
				}
			}

		});

		function getCaretPosition(ctrl) {
			var CaretPos = 0, // IE Support
			    Sel;

			if (document.selection) {
				ctrl.focus();
				Sel = document.selection.createRange();
				Sel.moveStart('character', -ctrl.value.length);
				CaretPos = Sel.text.length;
			}

			// Firefox support
			else if (ctrl.selectionStart || ctrl.selectionStart == '0') {
				CaretPos = ctrl.selectionStart;
			}

			return CaretPos;
		}

		function setCaretPosition(ctrl, pos){
			if (ctrl.setSelectionRange) {
				ctrl.focus();
				ctrl.setSelectionRange(pos, pos);
			} else if (ctrl.createTextRange) {
				var range = ctrl.createTextRange();
				range.collapse(true);
				range.moveEnd("character", pos);
				range.moveStart("character", pos);
				range.select();
			}
		}

		function getSelectionText(){
			var t = "";

			if (window.getSelection) {
				t = window.getSelection();
			} else if (document.getSelection) {
				t = document.getSelection();
			} else if (document.selection) {
				t = document.selection.createRange().text;
			}

			return t;
		}
	};
})(jQuery);<commit_msg>Fix stupid mistake<commit_after>/*
	jQuery.Typograf.js

	https://github.com/Spearance/jQuery.Typograf.js
	http://www.typograf.ru/

	Copyright 2015, Evgeniy Lepeshkin

	Released under the MIT license.
	http://www.opensource.org/licenses/mit-license.php

	Version: v 0.1
	Date: Thu Mar 24, 2015
 */

(function($){
	$.fn.typograf = function(options){
		options = $.extend({
			leftQuote: "«",
			rightQuote: "»",
			handler: "",
			restor: ""
		}, options);

		return this.each(function(){
			var $this = $(this),
			    obj = $this.get(0),
			    exclude = false,
			    selected = false,
			    caretPosition = 0,
			    original;

			// exist Typograf handler
			if (options.handler) {
				original = "";

				$(options.handler).click(function(){
					if (original == "") {
						original = $this.val();
					}

					if (original) {
						if (options.restore) {
							$(options.restore).removeAttr("disabled");
						}
						typograf($this);
					}
				});

				// exist Restore handler
				if (options.restore) {
					$(options.restore).click(function(){
						$(this).attr("disabled", "true");
						$this.val(original);
					});
				}
			} else {
				// auto
				$this
					.keydown(function(e){
						// check for Ctrl/⌘
						exclude = Boolean(
							(e.metaKey || e.ctrlKey) &&
							!(e.altKey && e.ctrlKey) // Do not exclude Alt Gr
						);
					})
					.keyup(function(e){
						// check for selected text
						selected = Boolean(getSelectionText() != "");

						// exclude arrows and Ctrl/⌘ + A/C/V/X/Z
						if (
							// home, end, page up, page down
							e.which != "33" &&
							e.which != "34" &&
							e.which != "35" &&
							e.which != "36" &&

							// arrows
							e.which != "37" &&
							e.which != "38" &&
							e.which != "39" &&
							e.which != "40" &&

							// single Shift
							e.which != "16" &&

							// select all, copy, paste, cut, undo
							!(exclude && (e.which == "65" ||
							              e.which == "67" ||
							              e.which == "86" ||
							              e.which == "88" ||
							              e.which == "90")) &&

							// selected text
							!selected
						){
							typograf($this);
						}
						return true;
					}
				);
			}

			function typograf (textField){
				var text = textField.val();
				caretPosition = getCaretPosition(obj);

				text = text
					// Dash
					.replace(/(^|\n|\s|>)\-(\s)/g, "$1—$2")

					// Minus
					.replace(/\-{2} /g, function(){
						caretPosition--;
						return "— ";
					})

					// HTML-comment
					.replace(/<!—/ig, function(){
						caretPosition++;
						return "<!--";
					})

					// Multiple spaces
					.replace(/ {2,}/g, function(str){
						caretPosition -= str.length - 1;
						return " ";
					})

					// Multiple nbsp
					.replace(/ {2,}| \s|\s /g, function(str){
						caretPosition -= str.length - 1;
						return " ";
					})

					// Numerical interval
					.replace(/(\d)( )?[-—]( )?(\d)/g, function(str, $1, $2, $3, $4){
						if ($2 == " ") { caretPosition-- }
						if ($3 == " ") { caretPosition-- }
						return $1 + "–" + $4;
					})

					// Copyright
					.replace(/\([cс]\)/ig, function(){
						caretPosition -= 2;
						return "©";
					})

					// Registered trademark
					.replace(/\(r\)/ig, function(){
						caretPosition -= 2;
						return "®";
					})

					// Trademark
					.replace(/\(tm\)/ig, function(){
						caretPosition -= 2;
						return "™";
					})

					// Rouble
					.replace(/\([рp]\)/ig, function(){
						caretPosition -= 2;
						return "₽";
					})

					// Three dots
					.replace(/\.{3}/g, function(){
						caretPosition -= 2;
						return "…";
					})

					// Sizes
					.replace(/(\d)[xх](\d)/ig, "$1×$2")

					// Open quote
					.replace(/\"([a-zа-я0-9…])/ig,
					         options.leftQuote + "$1")

					// Close quote
					.replace(/([a-zа-я0-9…?!])\"/ig,
					         "$1" + options.rightQuote)

					// Open quote
					.replace(new RegExp("\"(" + options.leftQuote + "[a-zа-я0-9…])", "ig"),
					         options.leftQuote + "$1")

					// Close quote
					.replace(new RegExp("([a-zа-я0-9…?!]" + options.rightQuote + ")\"", "ig"),
					         "$1" + options.rightQuote)

					// Fix HTML open quotes
					.replace(new RegExp("([-a-z0-9]+=)" +
					                    "["   + options.leftQuote + options.rightQuote + "]" +
					                    "([^" + options.leftQuote + options.rightQuote + "]*?)", "ig"),
					         "$1\"$2")

					// Fix HTML close quotes
					.replace(new RegExp("([-a-z0-9]+=)[\"]" +
					                    "([^" + options.leftQuote + options.rightQuote + "]*?)" +
					                    "["   + options.leftQuote + options.rightQuote + "]", "ig"),
					         "$1\"$2\"");

				textField.val(text);

				if (!exclude) {
					setCaretPosition(obj, caretPosition);
				}
			}

		});

		function getCaretPosition(ctrl) {
			var CaretPos = 0, // IE Support
			    Sel;

			if (document.selection) {
				ctrl.focus();
				Sel = document.selection.createRange();
				Sel.moveStart('character', -ctrl.value.length);
				CaretPos = Sel.text.length;
			}

			// Firefox support
			else if (ctrl.selectionStart || ctrl.selectionStart == '0') {
				CaretPos = ctrl.selectionStart;
			}

			return CaretPos;
		}

		function setCaretPosition(ctrl, pos){
			if (ctrl.setSelectionRange) {
				ctrl.focus();
				ctrl.setSelectionRange(pos, pos);
			} else if (ctrl.createTextRange) {
				var range = ctrl.createTextRange();
				range.collapse(true);
				range.moveEnd("character", pos);
				range.moveStart("character", pos);
				range.select();
			}
		}

		function getSelectionText(){
			var t = "";

			if (window.getSelection) {
				t = window.getSelection();
			} else if (document.getSelection) {
				t = document.getSelection();
			} else if (document.selection) {
				t = document.selection.createRange().text;
			}

			return t;
		}
	};
})(jQuery);<|endoftext|>


<commit_before>import Exponent from 'exponent';
import React from 'react';
import {
  Platform,
  StatusBar,
  StyleSheet,
  View,
} from 'react-native';
import {
  NavigationProvider,
  StackNavigation,
} from '@exponent/ex-navigation';
import {
  ActionSheetProvider,
} from '@exponent/react-native-action-sheet';
import {
  ApolloProvider,
} from 'react-apollo';

import AuthTokenActions from '../Flux/AuthTokenActions';
import LocalStorage from '../Storage/LocalStorage';
import GlobalLoadingOverlay from './containers/GlobalLoadingOverlay';
import ExStore from '../Flux/ExStore';

import Router from './navigation/Router';
import customNavigationContext from './navigation/customNavigationContext';

export default class AppContainer extends React.Component {
  state = {
    isReady: false,
  }

  async componentDidMount() {
    let storedAuthTokens = await LocalStorage.getAuthTokensAsync();

    if (storedAuthTokens) {
      ExStore.dispatch(AuthTokenActions.setAuthTokens(storedAuthTokens));
    }

    this.setState({isReady: true});
  }

  render() {
    return (
      <View style={styles.container}>
        <ActionSheetProvider>
          <NavigationProvider context={customNavigationContext}>
            { this.state.isReady && (
              <StackNavigation
                id="root"
                initialRoute="rootNavigation"
              />
            )}
          </NavigationProvider>
        </ActionSheetProvider>

        {Platform.OS === 'ios' && <GlobalLoadingOverlay />}
        {Platform.OS === 'ios' && <StatusBar barStyle="default" />}
        {Platform.OS === 'android' && <View style={styles.statusBarUnderlay} />}
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
  statusBarUnderlay: {
    height: 24,
    backgroundColor: 'rgba(0,0,0,0.2)',
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
  },
});
<commit_msg>Precache icon fonts<commit_after>import Exponent, {
  Font,
} from 'exponent';
import React from 'react';
import {
  ActivityIndicator,
  Platform,
  StatusBar,
  StyleSheet,
  View,
} from 'react-native';
import {
  NavigationProvider,
  StackNavigation,
} from '@exponent/ex-navigation';
import {
  ActionSheetProvider,
} from '@exponent/react-native-action-sheet';
import {
  Ionicons,
  MaterialIcons,
} from '@exponent/vector-icons';
import {
  ApolloProvider,
} from 'react-apollo';

import AuthTokenActions from '../Flux/AuthTokenActions';
import LocalStorage from '../Storage/LocalStorage';
import GlobalLoadingOverlay from './containers/GlobalLoadingOverlay';
import ExStore from '../Flux/ExStore';

import Router from './navigation/Router';
import customNavigationContext from './navigation/customNavigationContext';

export default class AppContainer extends React.Component {
  state = {
    isReady: false,
  }

  async componentDidMount() {
    try {
      let storedAuthTokens = await LocalStorage.getAuthTokensAsync();

      if (storedAuthTokens) {
        ExStore.dispatch(AuthTokenActions.setAuthTokens(storedAuthTokens));
      }

      if (Platform.OS === 'ios') {
        await Font.loadAsync(Ionicons.font);
      } else {
        await Font.loadAsync(MaterialIcons.font);
      }
    } catch(e) {

    } finally {
      this.setState({isReady: true});
    }
  }

  render() {
    if (!this.state.isReady) {
      return (
        <View style={{flex: 1, backgroundColor: 'white', alignItems: 'center', justifyContent: 'center'}}>
          <ActivityIndicator />
        </View>
      );
    }

    return (
      <View style={styles.container}>
        <ActionSheetProvider>
          <NavigationProvider context={customNavigationContext}>
            { this.state.isReady && (
              <StackNavigation
                id="root"
                initialRoute="rootNavigation"
              />
            )}
          </NavigationProvider>
        </ActionSheetProvider>

        {Platform.OS === 'ios' && <GlobalLoadingOverlay />}
        {Platform.OS === 'ios' && <StatusBar barStyle="default" />}
        {Platform.OS === 'android' && <View style={styles.statusBarUnderlay} />}
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
  statusBarUnderlay: {
    height: 24,
    backgroundColor: 'rgba(0,0,0,0.2)',
    position: 'absolute',
    top: 0,
    left: 0,
    right: 0,
  },
});
<|endoftext|>


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

/**
 * Base type for instrumented PhET-iO instances, provides support for PhET-iO features when running with PhET-iO enabled.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var escapeHTML = require( 'PHET_CORE/escapeHTML' );
  var inherit = require( 'PHET_CORE/inherit' );
  var ObjectIO = require( 'TANDEM/types/ObjectIO' );
  var Tandem = require( 'TANDEM/Tandem' );
  var tandemNamespace = require( 'TANDEM/tandemNamespace' );

  // ifphetio
  var dataStream = require( 'ifphetio!PHET_IO/dataStream' );

  // constants
  var PHET_IO_ENABLED = !!( window.phet && window.phet.phetio );

  // Flag that indicates a high frequency message was skipped.
  var SKIPPING_HIGH_FREQUENCY_MESSAGE = -1;

  // Factor out to reduce memory footprint, see https://github.com/phetsims/tandem/issues/71
  var EMPTY_OBJECT = {};

  var DEFAULTS = {
    tandem: Tandem.optional,        // By default tandems are optional, but subtypes can specify this as
                                    // `Tandem.tandemRequired` to enforce its presence
    phetioType: ObjectIO,           // Supply the appropriate IO type
    phetioDocumentation: null,      // Useful notes about an instrumented instance, shown in the PhET-iO Studio Wrapper
    phetioState: true,              // When true, includes the instance in the PhET-iO state
    phetioReadOnly: false,          // When true, you can only get values from the instance; no setting allowed.
    phetioEventType: 'model',       // Default event type for this instance, can be overridden in phetioStartEvent options
    phetioHighFrequency: false,     // This instance emits events that are high frequency events such as mouse moves or
                                    // stepSimulation can be omitted from data stream
    phetioPlayback: false,          // This instance emits events that are needed for data streams intended for playback.
                                    // See `handlePlaybackEvent.js` for wrapper-side event playback usage.
    phetioStudioControl: true,      // By default, Studio creates controls for many types of instances.  This option
                                    // can be set to false to direct Studio to omit the control for the instance.
    phetioComponentOptions: null,   // For propagating phetio options to sub-components, see SUPPORTED_PHET_IO_COMPONENT_OPTIONS
    phetioControlledProperty: null  // A common pattern for a UI component is to control a Property. This option makes
                                    // sure both are instrumented and augments the component documentation accordingly.
  };

  var SUPPORTED_PHET_IO_COMPONENT_OPTIONS = [

    // NodeIO
    'visibleProperty', 'pickableProperty', 'opacityProperty',

    // TextIO
    'textProperty'
  ];

  var OPTIONS_KEYS = _.keys( DEFAULTS );

  /**
   * @param {Object} [options]
   * @constructor
   */
  function PhetioObject( options ) {

    // @public (read-only) {Tandem} - assigned in initializePhetioObject - the unique tandem for this instance
    this.tandem = null;

    // @public (read-only) {IOType} - assigned in initializePhetioObject - the IO type associated with this instance
    this.phetioType = null;

    // @public (read-only) {boolean} - assigned in initializePhetioObject - When true, included in the PhET-iO state
    this.phetioState = null;

    // @public (read-only) {boolean} - assigned in initializePhetioObject - When true, values can be get but not set
    this.phetioReadOnly = null;

    // @public (read-only) {string} - assigned in initializePhetioObject - Notes about an instance, shown in the PhET-iO Studio Wrapper
    this.phetioDocumentation = null;

    // @public (read-only) {Object} - assigned in initializePhetioObject - The wrapper instance for PhET-iO interoperation
    this.phetioWrapper = null;

    // @private {boolean} - track whether the object has been initialized.  This is necessary because initialization
    // can happen in the constructor or in a subsequent call to initializePhetioObject (to support scenery Node)
    this.phetioObjectInitialized = false;

    // @private {number|null} - tracks the indices of started messages so that dataStream can check that ends match starts
    this.phetioMessageStack = [];

    // @protected {boolean} - has the instance been disposed?
    this.phetioObjectDisposed = false;

    // @private {string} - 'model' | 'user'
    this.phetioEventType = null;

    // @private {boolean} - If marked as phetioHighFrequency: true, the event will be omitted when the query parameter phetioEmitHighFrequencyEvents=false, also see option in Client.launchSim()
    this.phetioHighFrequency = null;

    // @private {boolean} - This indicates a (usually high-frequency) event that is required for
    // visual playbacks, but can be otherwise overwhelming.  For instance, stepSimulationEmitter emits dt's that are critical to playbacks
    // but not helpful when reading console: colorized.
    this.phetioPlayback = null;

    // @private {boolean} By default, Studio creates controls for many types of instances.  This option can be set to
    // false to direct Studio to omit the control for the instance.
    this.phetioStudioControl = null;

    // @public {Object} options to pass through to direct child subcomponents, see NodeIO
    this.phetioComponentOptions = null;

    if ( options ) {
      this.initializePhetioObject( {}, options );
    }
  }

  tandemNamespace.register( 'PhetioObject', PhetioObject );

  return inherit( Object, PhetioObject, {

    /**
     * @param {Object} baseOptions - only applied if options keys intersect OPTIONS_KEYS
     * @param {Object} options
     * @protected
     */
    initializePhetioObject: function( baseOptions, options ) {
      assert && assert( options, 'initializePhetioObject must be called with options' );

      // TODO: garbage-free implementation
      var intersection = _.intersection( _.keys( options ), OPTIONS_KEYS );
      if ( intersection.length === 0 ) {
        return; // no PhetioObject keys provided, perhaps they will be provided in a subsequent mutate call.
      }
      assert && assert( !this.phetioObjectInitialized, 'cannot initialize twice' );

      assert && assert( options.tandem, 'Component was missing its tandem' );
      assert && assert( options.tandem.phetioID, 'Component was missing its phetioID' );

      if ( assert && options.phetioType && PHET_IO_ENABLED ) {
        assert && assert( options.phetioType.documentation, 'There must be a documentation string for each IO Type.' );

        for ( var methodName in options.phetioType.methods ) {
          var method = options.phetioType.methods[ methodName ];

          if ( typeof method === 'function' ) {

            // This is a private function for internal phet-io mechanics, not for exporting over the API, so it doesn't
            // need to be checked.
          }
          else {
            var IOType = options.phetioType;

            // If you get one of these assertion errors, go to the IOType definition file and check its methods
            assert && assert( !!method.returnType, IOType.typeName + '.' + methodName + ' needs a returnType' );
            assert && assert( !!method.implementation, IOType.typeName + '.' + methodName + ' needs an implementation function' );
            assert && assert( !!method.parameterTypes, IOType.typeName + '.' + methodName + ' needs a parameterTypes array' );
            assert && assert( !!method.documentation, IOType.typeName + '.' + methodName + ' needs a documentation string' );
          }
        }

        assert && assert( options.phetioType !== undefined, options.tandem.phetioID + ' missing type from phetio.api' );
        assert && assert( options.phetioType.typeName, 'no type name for ' + options.tandem.phetioID + '(may be missing type parameter)' );
        assert && assert( options.phetioType.typeName, 'type must be specified and have a typeName for ' + options.tandem.phetioID );
      }

      options = _.extend( {}, DEFAULTS, baseOptions, options );

      // Unpack options to instance properties
      this.tandem = options.tandem;
      this.phetioType = options.phetioType;
      this.phetioState = options.phetioState;
      this.phetioReadOnly = options.phetioReadOnly;
      this.phetioEventType = options.phetioEventType;
      this.phetioDocumentation = options.phetioDocumentation;
      this.phetioHighFrequency = options.phetioHighFrequency;
      this.phetioPlayback = options.phetioPlayback;
      this.phetioStudioControl = options.phetioStudioControl;
      this.phetioComponentOptions = options.phetioComponentOptions || EMPTY_OBJECT;

      if ( options.phetioControlledProperty ) {

        // If either one is instrumented, then the other must be too.
        assert && Tandem.validationEnabled() && assert( this.isPhetioInstrumented() === options.phetioControlledProperty.isPhetioInstrumented(),
          'Property must be instrumented if controlling component is.' );

        // (phet-io) document the instrumented Property that this Checkbox manipulates
        this.phetioDocumentation +=
          ` Controls the ${escapeHTML( options.phetioControlledProperty.phetioType.typeName )}:

        ${options.phetioControlledProperty.tandem.phetioID}`.trim(); // eliminate preceding whitespace, if any.
      }

      // validate phetioComponentOptions
      assert && _.keys( this.phetioComponentOptions ).forEach( option => {
        assert && assert( SUPPORTED_PHET_IO_COMPONENT_OPTIONS.indexOf( option ) >= 0, 'Unsupported phetioComponentOptions: ' + option );
      } );

      // Instantiate the wrapper instance which is used for PhET-iO communication
      if ( PHET_IO_ENABLED && this.tandem.supplied ) {
        // this assertion should be enabled for new phet-io sim publications
        // assert && assert( this.phetioDocumentation, 'Instance documentation is required for: ' + this.tandem.phetioID );
        this.phetioWrapper = new this.phetioType( this, this.tandem.phetioID );
      }

      // Register with the tandem registry
      this.tandem.addInstance( this );

      this.phetioObjectInitialized = true;
    },

    /**
     * Start an event for the nested PhET-iO data stream.
     *
     * @param {string} event - the name of the event
     * @param {Object|function|null} [data] - data for the event, either an object, or a function that returns an object
     * @param {Object|function|null} [metadata] - metadata for the event, either an object, or a function that returns an object
     * @public
     */
    phetioStartEvent: function( event, data, metadata ) {
      assert && assert( this.phetioObjectInitialized, 'phetioObject should be initialized' );
      assert && assert( typeof event === 'string' );
      assert && data && assert( typeof data === 'object' || typeof data === 'function' );
      assert && assert( arguments.length === 1 || arguments.length === 2 || arguments.length === 3, 'Prevent usage of incorrect signature' );

      // Opt out of certain events if queryParameter override is provided
      if ( window.phet && window.phet.phetio ) {
        if ( !window.phet.phetio.queryParameters.phetioEmitHighFrequencyEvents && this.phetioHighFrequency ) {
          this.phetioMessageStack.push( SKIPPING_HIGH_FREQUENCY_MESSAGE );
          return;
        }
      }

      if ( this.isPhetioInstrumented() ) {

        // Only get the args if we are actually going to send the event.
        if ( typeof data === 'function' ) {
          data = data();
        }
        if ( typeof metadata === 'function' ) {
          metadata = metadata();
        }

        // playback is a metadata parameter added to the event
        if ( this.phetioPlayback ) {
          if ( !metadata ) {
            metadata = {};
          }

          // TODO: this doesn't work because Emitter passes in the same reference object each event. We aren't cloning
          // TODO: this object before giving it to `datastream.js`, see https://github.com/phetsims/phet-io/issues/1411
          // assert && assert( !metadata.playback, 'phetioObject sets playback metadata' );
          metadata.playback = this.phetioPlayback;
        }

        this.phetioMessageStack.push( dataStream.start( this.phetioEventType, this, event, data, metadata ) );
      }
    },

    /**
     * End an event on the nested PhET-iO data stream.
     * It this object was disposed or dataStream.start was not called, this is a no-op.
     * @public
     */
    phetioEndEvent: function() {

      var topMessageIndex = this.phetioMessageStack.pop();

      // The message was started as a high frequency event to be skipped, so the end is a no-op
      if ( topMessageIndex === SKIPPING_HIGH_FREQUENCY_MESSAGE ) {
        return;
      }

      if ( this.isPhetioInstrumented() ) {
        dataStream.end( topMessageIndex );
      }
    },

    /**
     * Just because a tandem is passed to an instance doesn't mean that it is instrumented. An instance will only be
     * instrumented if:
     * (1) Running in PhET-iO mode
     * (2) The tandem that was passed in was "supplied". See Tandem.supplied for more info
     * @returns {boolean}
     * @public
     */
    isPhetioInstrumented: function() {
      return this.tandem && this.tandem.supplied && PHET_IO_ENABLED;
    },

    /**
     * JSONifiable metadata that describes the nature of the PhetioObject.
     * @returns {Object}
     * @public
     */
    getMetadata: function() {
      return {
        phetioTypeName: this.phetioType.typeName,
        phetioState: this.phetioState,
        phetioReadOnly: this.phetioReadOnly,
        phetioDocumentation: this.phetioDocumentation,
        phetioEventType: this.phetioEventType,
        phetioHighFrequency: this.phetioHighFrequency,
        phetioPlayback: this.phetioPlayback,
        phetioStudioControl: this.phetioStudioControl
      };
    },

    /**
     * Unregisters from tandem when longer used.
     * @public
     */
    dispose: function() {
      var self = this;
      assert && assert( !this.phetioObjectDisposed, 'PhetioObject can only be disposed once' );

      // In order to support the structured data stream, PhetioObjects must end the messages in the correct
      // sequence, without being interrupted by dispose() calls.  Therefore, we do not clear out any of the state
      // related to the endEvent.  Note this means it is acceptable (and expected) for endEvent() to be called on disposed PhetioObjects.
      //
      // The phetioEvent stack should resolve by the next clock tick, so that's when we check it.
      assert && setTimeout( function() {
        assert && assert( self.phetioMessageStack.length === 0, 'phetioMessageStack should be clear' );
      }, 0 );

      // OK to dispose something that was never phetioObjectInitialized, this means it was an uninstrumented instance
      if ( this.phetioObjectInitialized ) {

        // Tandem de-registration
        this.tandem.removeInstance( this );
        this.phetioWrapper && this.phetioWrapper.dispose && this.phetioWrapper.dispose();
      }

      this.phetioObjectDisposed = true;
    }
  }, {
    DEFAULT_OPTIONS: DEFAULTS // the default options for the phet-io object
  } );
} );<commit_msg>Added phetioFeatured, see https://github.com/phetsims/phet-io-wrappers/issues/143<commit_after>// Copyright 2017-2018, University of Colorado Boulder

/**
 * Base type for instrumented PhET-iO instances, provides support for PhET-iO features when running with PhET-iO enabled.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var escapeHTML = require( 'PHET_CORE/escapeHTML' );
  var inherit = require( 'PHET_CORE/inherit' );
  var ObjectIO = require( 'TANDEM/types/ObjectIO' );
  var Tandem = require( 'TANDEM/Tandem' );
  var tandemNamespace = require( 'TANDEM/tandemNamespace' );

  // ifphetio
  var dataStream = require( 'ifphetio!PHET_IO/dataStream' );

  // constants
  var PHET_IO_ENABLED = !!( window.phet && window.phet.phetio );

  // Flag that indicates a high frequency message was skipped.
  var SKIPPING_HIGH_FREQUENCY_MESSAGE = -1;

  // Factor out to reduce memory footprint, see https://github.com/phetsims/tandem/issues/71
  var EMPTY_OBJECT = {};

  var DEFAULTS = {
    tandem: Tandem.optional,        // By default tandems are optional, but subtypes can specify this as
                                    // `Tandem.tandemRequired` to enforce its presence
    phetioType: ObjectIO,           // Supply the appropriate IO type
    phetioDocumentation: null,      // Useful notes about an instrumented instance, shown in the PhET-iO Studio Wrapper
    phetioState: true,              // When true, includes the instance in the PhET-iO state
    phetioReadOnly: false,          // When true, you can only get values from the instance; no setting allowed.
    phetioEventType: 'model',       // Default event type for this instance, can be overridden in phetioStartEvent options
    phetioHighFrequency: false,     // This instance emits events that are high frequency events such as mouse moves or
                                    // stepSimulation can be omitted from data stream
    phetioPlayback: false,          // This instance emits events that are needed for data streams intended for playback.
                                    // See `handlePlaybackEvent.js` for wrapper-side event playback usage.
    phetioStudioControl: true,      // By default, Studio creates controls for many types of instances.  This option
                                    // can be set to false to direct Studio to omit the control for the instance.
    phetioComponentOptions: null,   // For propagating phetio options to sub-components, see SUPPORTED_PHET_IO_COMPONENT_OPTIONS
    phetioControlledProperty: null, // A common pattern for a UI component is to control a Property. This option makes
                                    // sure both are instrumented and augments the component documentation accordingly.
    phetioFeatured: false           // True if this is an important instance to be "featured" in the PhET-iO API
  };

  var SUPPORTED_PHET_IO_COMPONENT_OPTIONS = [

    // NodeIO
    'visibleProperty', 'pickableProperty', 'opacityProperty',

    // TextIO
    'textProperty'
  ];

  var OPTIONS_KEYS = _.keys( DEFAULTS );

  /**
   * @param {Object} [options]
   * @constructor
   */
  function PhetioObject( options ) {

    // @public (read-only) {Tandem} - assigned in initializePhetioObject - the unique tandem for this instance
    this.tandem = null;

    // @public (read-only) {IOType} - assigned in initializePhetioObject - the IO type associated with this instance
    this.phetioType = null;

    // @public (read-only) {boolean} - assigned in initializePhetioObject - When true, included in the PhET-iO state
    this.phetioState = null;

    // @public (read-only) {boolean} - assigned in initializePhetioObject - When true, values can be get but not set
    this.phetioReadOnly = null;

    // @public (read-only) {string} - assigned in initializePhetioObject - Notes about an instance, shown in the PhET-iO Studio Wrapper
    this.phetioDocumentation = null;

    // @public (read-only) {Object} - assigned in initializePhetioObject - The wrapper instance for PhET-iO interoperation
    this.phetioWrapper = null;

    // @private {boolean} - track whether the object has been initialized.  This is necessary because initialization
    // can happen in the constructor or in a subsequent call to initializePhetioObject (to support scenery Node)
    this.phetioObjectInitialized = false;

    // @private {number|null} - tracks the indices of started messages so that dataStream can check that ends match starts
    this.phetioMessageStack = [];

    // @protected {boolean} - has the instance been disposed?
    this.phetioObjectDisposed = false;

    // @private {string} - 'model' | 'user'
    this.phetioEventType = null;

    // @private {boolean} - If marked as phetioHighFrequency: true, the event will be omitted when the query parameter phetioEmitHighFrequencyEvents=false, also see option in Client.launchSim()
    this.phetioHighFrequency = null;

    // @private {boolean} - This indicates a (usually high-frequency) event that is required for
    // visual playbacks, but can be otherwise overwhelming.  For instance, stepSimulationEmitter emits dt's that are critical to playbacks
    // but not helpful when reading console: colorized.
    this.phetioPlayback = null;

    // @private {boolean} By default, Studio creates controls for many types of instances.  This option can be set to
    // false to direct Studio to omit the control for the instance.
    this.phetioStudioControl = null;

    // @private {boolean} - See docs above
    this.phetioFeatured = false;

    // @public {Object} options to pass through to direct child subcomponents, see NodeIO
    this.phetioComponentOptions = null;

    if ( options ) {
      this.initializePhetioObject( {}, options );
    }
  }

  tandemNamespace.register( 'PhetioObject', PhetioObject );

  return inherit( Object, PhetioObject, {

    /**
     * @param {Object} baseOptions - only applied if options keys intersect OPTIONS_KEYS
     * @param {Object} options
     * @protected
     */
    initializePhetioObject: function( baseOptions, options ) {
      assert && assert( options, 'initializePhetioObject must be called with options' );

      // TODO: garbage-free implementation
      var intersection = _.intersection( _.keys( options ), OPTIONS_KEYS );
      if ( intersection.length === 0 ) {
        return; // no PhetioObject keys provided, perhaps they will be provided in a subsequent mutate call.
      }
      assert && assert( !this.phetioObjectInitialized, 'cannot initialize twice' );

      assert && assert( options.tandem, 'Component was missing its tandem' );
      assert && assert( options.tandem.phetioID, 'Component was missing its phetioID' );

      if ( assert && options.phetioType && PHET_IO_ENABLED ) {
        assert && assert( options.phetioType.documentation, 'There must be a documentation string for each IO Type.' );

        for ( var methodName in options.phetioType.methods ) {
          var method = options.phetioType.methods[ methodName ];

          if ( typeof method === 'function' ) {

            // This is a private function for internal phet-io mechanics, not for exporting over the API, so it doesn't
            // need to be checked.
          }
          else {
            var IOType = options.phetioType;

            // If you get one of these assertion errors, go to the IOType definition file and check its methods
            assert && assert( !!method.returnType, IOType.typeName + '.' + methodName + ' needs a returnType' );
            assert && assert( !!method.implementation, IOType.typeName + '.' + methodName + ' needs an implementation function' );
            assert && assert( !!method.parameterTypes, IOType.typeName + '.' + methodName + ' needs a parameterTypes array' );
            assert && assert( !!method.documentation, IOType.typeName + '.' + methodName + ' needs a documentation string' );
          }
        }

        assert && assert( options.phetioType !== undefined, options.tandem.phetioID + ' missing type from phetio.api' );
        assert && assert( options.phetioType.typeName, 'no type name for ' + options.tandem.phetioID + '(may be missing type parameter)' );
        assert && assert( options.phetioType.typeName, 'type must be specified and have a typeName for ' + options.tandem.phetioID );
      }

      options = _.extend( {}, DEFAULTS, baseOptions, options );

      // Unpack options to instance properties
      this.tandem = options.tandem;
      this.phetioType = options.phetioType;
      this.phetioState = options.phetioState;
      this.phetioReadOnly = options.phetioReadOnly;
      this.phetioEventType = options.phetioEventType;
      this.phetioDocumentation = options.phetioDocumentation;
      this.phetioHighFrequency = options.phetioHighFrequency;
      this.phetioPlayback = options.phetioPlayback;
      this.phetioStudioControl = options.phetioStudioControl;
      this.phetioComponentOptions = options.phetioComponentOptions || EMPTY_OBJECT;
      this.phetioFeatured = options.phetioFeatured;

      if ( options.phetioControlledProperty ) {

        // If either one is instrumented, then the other must be too.
        assert && Tandem.validationEnabled() && assert( this.isPhetioInstrumented() === options.phetioControlledProperty.isPhetioInstrumented(),
          'Property must be instrumented if controlling component is.' );

        // (phet-io) document the instrumented Property that this Checkbox manipulates
        this.phetioDocumentation +=
          ` Controls the ${escapeHTML( options.phetioControlledProperty.phetioType.typeName )}:

        ${options.phetioControlledProperty.tandem.phetioID}`.trim(); // eliminate preceding whitespace, if any.
      }

      // validate phetioComponentOptions
      assert && _.keys( this.phetioComponentOptions ).forEach( option => {
        assert && assert( SUPPORTED_PHET_IO_COMPONENT_OPTIONS.indexOf( option ) >= 0, 'Unsupported phetioComponentOptions: ' + option );
      } );

      // Instantiate the wrapper instance which is used for PhET-iO communication
      if ( PHET_IO_ENABLED && this.tandem.supplied ) {
        // this assertion should be enabled for new phet-io sim publications
        // assert && assert( this.phetioDocumentation, 'Instance documentation is required for: ' + this.tandem.phetioID );
        this.phetioWrapper = new this.phetioType( this, this.tandem.phetioID );
      }

      // Register with the tandem registry
      this.tandem.addInstance( this );

      this.phetioObjectInitialized = true;
    },

    /**
     * Start an event for the nested PhET-iO data stream.
     *
     * @param {string} event - the name of the event
     * @param {Object|function|null} [data] - data for the event, either an object, or a function that returns an object
     * @param {Object|function|null} [metadata] - metadata for the event, either an object, or a function that returns an object
     * @public
     */
    phetioStartEvent: function( event, data, metadata ) {
      assert && assert( this.phetioObjectInitialized, 'phetioObject should be initialized' );
      assert && assert( typeof event === 'string' );
      assert && data && assert( typeof data === 'object' || typeof data === 'function' );
      assert && assert( arguments.length === 1 || arguments.length === 2 || arguments.length === 3, 'Prevent usage of incorrect signature' );

      // Opt out of certain events if queryParameter override is provided
      if ( window.phet && window.phet.phetio ) {
        if ( !window.phet.phetio.queryParameters.phetioEmitHighFrequencyEvents && this.phetioHighFrequency ) {
          this.phetioMessageStack.push( SKIPPING_HIGH_FREQUENCY_MESSAGE );
          return;
        }
      }

      if ( this.isPhetioInstrumented() ) {

        // Only get the args if we are actually going to send the event.
        if ( typeof data === 'function' ) {
          data = data();
        }
        if ( typeof metadata === 'function' ) {
          metadata = metadata();
        }

        // playback is a metadata parameter added to the event
        if ( this.phetioPlayback ) {
          if ( !metadata ) {
            metadata = {};
          }

          // TODO: this doesn't work because Emitter passes in the same reference object each event. We aren't cloning
          // TODO: this object before giving it to `datastream.js`, see https://github.com/phetsims/phet-io/issues/1411
          // assert && assert( !metadata.playback, 'phetioObject sets playback metadata' );
          metadata.playback = this.phetioPlayback;
        }

        this.phetioMessageStack.push( dataStream.start( this.phetioEventType, this, event, data, metadata ) );
      }
    },

    /**
     * End an event on the nested PhET-iO data stream.
     * It this object was disposed or dataStream.start was not called, this is a no-op.
     * @public
     */
    phetioEndEvent: function() {

      var topMessageIndex = this.phetioMessageStack.pop();

      // The message was started as a high frequency event to be skipped, so the end is a no-op
      if ( topMessageIndex === SKIPPING_HIGH_FREQUENCY_MESSAGE ) {
        return;
      }

      if ( this.isPhetioInstrumented() ) {
        dataStream.end( topMessageIndex );
      }
    },

    /**
     * Just because a tandem is passed to an instance doesn't mean that it is instrumented. An instance will only be
     * instrumented if:
     * (1) Running in PhET-iO mode
     * (2) The tandem that was passed in was "supplied". See Tandem.supplied for more info
     * @returns {boolean}
     * @public
     */
    isPhetioInstrumented: function() {
      return this.tandem && this.tandem.supplied && PHET_IO_ENABLED;
    },

    /**
     * JSONifiable metadata that describes the nature of the PhetioObject.
     * @returns {Object}
     * @public
     */
    getMetadata: function() {
      return {
        phetioTypeName: this.phetioType.typeName,
        phetioState: this.phetioState,
        phetioReadOnly: this.phetioReadOnly,
        phetioDocumentation: this.phetioDocumentation,
        phetioEventType: this.phetioEventType,
        phetioHighFrequency: this.phetioHighFrequency,
        phetioPlayback: this.phetioPlayback,
        phetioStudioControl: this.phetioStudioControl,
        phetioFeatured: this.phetioFeatured
      };
    },

    /**
     * Unregisters from tandem when longer used.
     * @public
     */
    dispose: function() {
      var self = this;
      assert && assert( !this.phetioObjectDisposed, 'PhetioObject can only be disposed once' );

      // In order to support the structured data stream, PhetioObjects must end the messages in the correct
      // sequence, without being interrupted by dispose() calls.  Therefore, we do not clear out any of the state
      // related to the endEvent.  Note this means it is acceptable (and expected) for endEvent() to be called on disposed PhetioObjects.
      //
      // The phetioEvent stack should resolve by the next clock tick, so that's when we check it.
      assert && setTimeout( function() {
        assert && assert( self.phetioMessageStack.length === 0, 'phetioMessageStack should be clear' );
      }, 0 );

      // OK to dispose something that was never phetioObjectInitialized, this means it was an uninstrumented instance
      if ( this.phetioObjectInitialized ) {

        // Tandem de-registration
        this.tandem.removeInstance( this );
        this.phetioWrapper && this.phetioWrapper.dispose && this.phetioWrapper.dispose();
      }

      this.phetioObjectDisposed = true;
    }
  }, {
    DEFAULT_OPTIONS: DEFAULTS // the default options for the phet-io object
  } );
} );<|endoftext|>


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

/**
 * Base type for instrumented PhET-io instances, provides support for PhET-iO features when running with PhET-iO enabled.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var inherit = require( 'PHET_CORE/inherit' );
  var ObjectIO = require( 'ifphetio!PHET_IO/types/ObjectIO' );
  var tandemNamespace = require( 'TANDEM/tandemNamespace' );
  var phetioEvents = require( 'ifphetio!PHET_IO/phetioEvents' );
  var Tandem = require( 'TANDEM/Tandem' );

  var DEFAULTS = {
    tandem: Tandem.optional,        // By default tandems are optional, but subtypes can specify this as
                                    // `Tandem.tandemRequired` to enforce its presence
    phetioType: ObjectIO,           // Supply the appropriate IO type
    phetioState: true,              // When true, includes the instance in the PhET-iO state
    phetioEvents: true,             // When true, includes events in the PhET-iO events stream
    phetioReadOnly: false,          // When true, you can only get values from the instance; no setting allowed.
    phetioInstanceDocumentation: '' // Useful notes about an instrumented instance, shown in instance-proxies
  };

  var OPTIONS_KEYS = _.keys( DEFAULTS );

  /**
   * @param {Object} [options]
   * @constructor
   */
  function PhetioObject( options ) {

    // @private - for assertion checking
    this.eventInProgress = false;

    // @private
    this.initialized = false;

    if ( options ) {
      this.initializePhetioObject( {}, options );
    }
  }

  tandemNamespace.register( 'PhetioObject', PhetioObject );

  return inherit( Object, PhetioObject, {

    /**
     * @param {Object} baseOptions - only applied if options keys intersect OPTIONS_KEYS
     * @param {Object} options
     * @protected
     */
    initializePhetioObject: function( baseOptions, options ) {
      assert && assert( options, 'initializePhetioObject must be called with options' );

      // TODO: garbage-free implementation
      var intersection = _.intersection( _.keys( options ), OPTIONS_KEYS );
      if ( intersection.length === 0 ) {
        return; // no PhetioObject keys provided, perhaps they will be provided in a subsequent mutate call.
      }
      assert && assert( !this.initialized, 'cannot initialize twice' );
      this.initialized = true;

      options = _.extend( {}, DEFAULTS, baseOptions, options );

      // @public (read-only) - the unique tandem for this instance
      // TODO: rename to this.tandem after all other this.*tandems deleted
      // TODO: do we need phetioID if we have phetioObjectTandem?
      this.phetioObjectTandem = options.tandem;

      // @private - the IO type associated with this instance
      this.phetioType = options.phetioType;

      // Register with the tandem registry
      this.phetioObjectTandem.addInstance( this, options );
    },

    /**
     * Start an event for the nested PhET-iO event stream.
     *
     * @param {string} eventType - 'model' | 'view'
     * @param {string} event - the name of the event
     * @param {Object} [args] - arguments for the event
     * @returns {number|boolean} -
     *   If the tandem is not legal and usable, then returns false.
     *   If the tandem is legal and usable, it returns a numeric identifier that is
     *   used to cross-check the endEvent call (to make sure starts and ends match).
     * @public
     */
    startEvent: function( eventType, event, args ) {
      assert && assert( !this.eventInProgress, 'cannot start event while event is in progress' );
      this.eventInProgress = true;
      var id = this.phetioObjectTandem.id;
      return this.phetioObjectTandem.isLegalAndUsable() && phetioEvents.start( eventType, id, this.phetioType, event, args );
    },

    /**
     * End an event on the nested PhET-iO event stream.
     * @param {number|boolean} id - identifier that was returned by the corresponding startEvent
     * @public
     */
    endEvent: function( id ) {
      assert && assert( this.eventInProgress, 'cannot end an event that hasn\'t started' );
      this.phetioObjectTandem.isLegalAndUsable() && phetioEvents.end( id );
      this.eventInProgress = false;
    },

    /**
     * Unregisters from tandem when longer used.
     * @public
     */
    dispose: function() {
      //TODO enable this assertion when https://github.com/phetsims/equality-explorer/issues/25 is resolved
      // assert && assert( !this.eventInProgress, 'cannot dispose while event is in progress' );

      // OK to dispose something that was never initialized, this means it was an uninstrumented instance
      if ( this.initialized ) {

        // Tandem de-registration
        this.phetioObjectTandem.removeInstance( this );
      }
    }
  } );
} );<commit_msg>Restored assertion check and endEvent on SimpleDragHandler dispose, see https://github.com/phetsims/joist/issues/469<commit_after>// Copyright 2017, University of Colorado Boulder

/**
 * Base type for instrumented PhET-io instances, provides support for PhET-iO features when running with PhET-iO enabled.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var inherit = require( 'PHET_CORE/inherit' );
  var ObjectIO = require( 'ifphetio!PHET_IO/types/ObjectIO' );
  var tandemNamespace = require( 'TANDEM/tandemNamespace' );
  var phetioEvents = require( 'ifphetio!PHET_IO/phetioEvents' );
  var Tandem = require( 'TANDEM/Tandem' );

  var DEFAULTS = {
    tandem: Tandem.optional,        // By default tandems are optional, but subtypes can specify this as
                                    // `Tandem.tandemRequired` to enforce its presence
    phetioType: ObjectIO,           // Supply the appropriate IO type
    phetioState: true,              // When true, includes the instance in the PhET-iO state
    phetioEvents: true,             // When true, includes events in the PhET-iO events stream
    phetioReadOnly: false,          // When true, you can only get values from the instance; no setting allowed.
    phetioInstanceDocumentation: '' // Useful notes about an instrumented instance, shown in instance-proxies
  };

  var OPTIONS_KEYS = _.keys( DEFAULTS );

  /**
   * @param {Object} [options]
   * @constructor
   */
  function PhetioObject( options ) {

    // @protected {boolean} - true if an event is in progress
    this.eventInProgress = false;

    // @private
    this.initialized = false;

    // @private {number|boolean|null} - tracks the index of the message to make sure ends match starts.
    // If the tandem is not legal and usable, then returns false.
    // If the tandem is legal and usable, it returns a numeric identifier that is
    // used to cross-check the endEvent call (to make sure starts and ends match).
    // Null if an event is not in progress
    this.eventID = null;

    if ( options ) {
      this.initializePhetioObject( {}, options );
    }
  }

  tandemNamespace.register( 'PhetioObject', PhetioObject );

  return inherit( Object, PhetioObject, {

    /**
     * @param {Object} baseOptions - only applied if options keys intersect OPTIONS_KEYS
     * @param {Object} options
     * @protected
     */
    initializePhetioObject: function( baseOptions, options ) {
      assert && assert( options, 'initializePhetioObject must be called with options' );

      // TODO: garbage-free implementation
      var intersection = _.intersection( _.keys( options ), OPTIONS_KEYS );
      if ( intersection.length === 0 ) {
        return; // no PhetioObject keys provided, perhaps they will be provided in a subsequent mutate call.
      }
      assert && assert( !this.initialized, 'cannot initialize twice' );
      this.initialized = true;

      options = _.extend( {}, DEFAULTS, baseOptions, options );

      // @public (read-only) - the unique tandem for this instance
      // TODO: rename to this.tandem after all other this.*tandems deleted
      // TODO: do we need phetioID if we have phetioObjectTandem?
      this.phetioObjectTandem = options.tandem;

      // @private - the IO type associated with this instance
      this.phetioType = options.phetioType;

      // Register with the tandem registry
      this.phetioObjectTandem.addInstance( this, options );
    },

    /**
     * Start an event for the nested PhET-iO event stream.  Does not support re-entrant events on the same instance.
     *
     * @param {string} eventType - 'model' | 'view'
     * @param {string} event - the name of the event
     * @param {Object} [args] - arguments for the event
     * @public
     */
    startEvent: function( eventType, event, args ) {
      assert && assert( !this.eventInProgress, 'cannot start event while event is in progress' );
      this.eventInProgress = true;
      var id = this.phetioObjectTandem.id;
      var index = this.phetioObjectTandem.isLegalAndUsable() && phetioEvents.start( eventType, id, this.phetioType, event, args );
      this.eventID = index;
    },

    /**
     * End an event on the nested PhET-iO event stream.
     * @public
     */
    endEvent: function() {
      assert && assert( this.eventInProgress, 'cannot end an event that hasn\'t started' );
      this.phetioObjectTandem.isLegalAndUsable() && phetioEvents.end( this.eventID );
      this.eventInProgress = false;
      this.eventID = null;
    },

    /**
     * Unregisters from tandem when longer used.
     * @public
     */
    dispose: function() {
      //TODO enable this assertion when https://github.com/phetsims/equality-explorer/issues/25 is resolved
      assert && assert( !this.eventInProgress, 'cannot dispose while event is in progress' );

      // OK to dispose something that was never initialized, this means it was an uninstrumented instance
      if ( this.initialized ) {

        // Tandem de-registration
        this.phetioObjectTandem.removeInstance( this );
      }
    }
  } );
} );<|endoftext|>


<commit_before>var crud = rest2crud;

/**
 * Base constructor for the ActiveRecord
 *
 * Use this only to set default properties.
 * Initialization should happen in init()
 */
function ActiveRecord() {
    this.values             = {};
    this.isNew              = true; // Whether it is a new record, or an existing one
    this.isDirty            = false; // Whether the values have changed since the last save
}

/**
 * Initialization function
 *
 * @return {promise}
 */
ActiveRecord.prototype.init = function () {
    this._setup();
}

/**
 * Initial setup of the model
 *
 * - create getters and setters
 *
 * @return {Model}
 */
ActiveRecord.prototype._setup = function () {
    var i;

    // Define getters and setters for each attribute
    for (i in this.config.columns) {
        this.defineGetterSetter(this.config.columns[i]);
    }

    return this;
}

ActiveRecord.prototype.defineGetterSetter = function (column) {
    this.__defineGetter__(column, function () {
        return this.values[column];
    });
    this.__defineSetter__(column, function (value) {
        if (this.values[column] !== value) {
            this.isDirty = true;
        }
        this.values[column] = value;
    });
}

ActiveRecord.prototype.save = function () {
    if (!this.isNew && !this.isDirty) {
        return;
    }

    var xhr, self = this;
    if (this.beforeSave === "function") {
        this.beforeSave();
    }

    // TODO validate

    if (this.isNew) {
        xhr = crud.create(this.config.url, this.values);
    } else if (this.isDirty) {
        xhr = crud.update(this.config.url+"/"+this.values[this.config.primaryKey], this.values);
    }

    this.isNew = false;
    this.isDirty = false;

    if (this.afterSave === "function") {
        xhr.done(function (response) {
                this.afterSave();
        });
    }

    return xhr;
}

////////////////////////////////////////// STATIC METHODS & PROPERTIES

ActiveRecord.functions = {};

ActiveRecord.defaults = {
    ENDPOINT : "/activerecord.js/rest/",
    PRIMARY_KEY : "id"
};

/**
 * Search over the models
 *
 * Possible arguments:
 *
 * - object
 * - value
 * - attribute, value
 * @return {xhr}
 */
ActiveRecord.functions.find = function () {
    var xhr;

    if (arguments.length === 1 && typeof arguments[0] !== "object") {
        xhr = this.__findById(arguments[0]);
    } else if (arguments.length === 2) {
        xhr = this.__findByAttribute(arguments[0], arguments[1]);
    } else if (arguments.length === 1 && typeof arguments[0] === "object") {
        // TODO pass parameters as an object
    } else {
        throw "Wrong number of parameters in find() function";
    }

    return xhr;
}

ActiveRecord.functions.__findById = function (id) {
    var xhr = crud.read(this.config.url+'/'+id), self = this;
    return xhr.then(function (records) {
        return new self(records[0]);
    });
}

ActiveRecord.functions.__findByAttribute = function (attributeName, value) {
    var xhr, params = {}, self = this, i;
    params[attributeName] = value;
    xhr = crud.read(this.config.url, params);

    return xhr.then(function (records) {
        var models = [];

        for(i in records) {
            models.push(new self(records[i]));
        }

       return models;
    });
}

ActiveRecord.functions.update = function () {
    var params = {};
    if (arguments.length === 2 && typeof arguments[0] !== "object") {
        return crud.update(this.config.url+"/"+arguments[0], arguments[1]);
    } else if (arguments.length === 3 && typeof arguments[0] !== "object") {
        params[arguments[1]] = arguments[2];
        return crud.update(this.config.url+"/"+arguments[0], params);
    } else {
        throw "Wrong number of parameters in update() function";
    }
}

ActiveRecord.functions.extend = function (proto) {
    var i, option;
    for (option in proto) {
        this.prototype[option] = proto[option];
    }

    return this;
};

/**
 * Base method for creating new models definition and
 * extending the base ActiveRecord
 *
 * @return {ActiveRecord} the extended model
 */
ActiveRecord.register = function (modelName, config) {
    var option, i;

    var Model = function () {
        // Run the AR constructor (Setting default values)
        ActiveRecord.apply(this, arguments);

        this.init();
        if (arguments.length === 1 && typeof arguments[0] === "object") {
            this.values = arguments[0];
            if (arguments[0][this.config.primaryKey]) {
                this.isNew = false;
            }
        }
    };

    Model.config = {};

    // First set the user overriden properties
    if (typeof config === "object") {
        for (option in config) {
            Model.config[option] = config[option];
        }
    }

    // For all missing properties apply defaults or calculate
    Model.config.modelName     = modelName;
    Model.config.endpoint      = Model.config.endpoint || ActiveRecord.defaults.ENDPOINT;
    Model.config.primaryKey    = Model.config.primaryKey || ActiveRecord.defaults.PRIMARY_KEY;
    Model.config.url           = Model.config.url || Model.config.endpoint + _plurilize(Model.config.modelName.toLowerCase());

    if (!Model.config.columns || Model.config.columns.length === 0) {
        Model.config.columns = [];
        crud.read(Model.config.url).done(function (records) {
            for (i in records[0]) {
                Model.config.columns.push(i);
            }
        });
    }

    for (i in ActiveRecord.functions) {
        Model[i] = ActiveRecord.functions[i];
    }

    Model.prototype = new ActiveRecord();
    Model.prototype._super = ActiveRecord.prototype;
    Model.prototype.config = Model.config;

    return Model;
};

////////////////////////////////////////// PRIVATE UTILITIES

/**
 * Convert a word to plural
 *
 * TODO: implement this
 *
 * @param  {String} str word to be converted
 * @return {String}     word in plural
 */
function _plurilize(str) {
    return str+"s";
}<commit_msg>Added some comments<commit_after>var crud = rest2crud;

/**
 * Base constructor for the ActiveRecord
 *
 * Use this only to set default properties.
 * Initialization should happen in init()
 */
function ActiveRecord() {
    this.values             = {}; // this will hold the values of the instanced AR
    this.isNew              = true; // Whether it is a new record, or an existing one
    this.isDirty            = false; // Whether the values have changed since the last save
}

/**
 * Initialization function
 *
 * @return {Model}
 */
ActiveRecord.prototype.init = function () {
    return this._setup();
}

/**
 * Initial setup of the model
 *
 * - create getters and setters
 *
 * @return {Model}
 */
ActiveRecord.prototype._setup = function () {
    var i;

    // Define getters and setters for each attribute
    for (i in this.config.columns) {
        this.defineGetterSetter(this.config.columns[i]);
    }

    return this;
}

/**
 * Registers the getter and setter for a given column name
 *
 * @param  {String} prop A property in the active record
 * @return null
 */
ActiveRecord.prototype.defineGetterSetter = function (prop) {
    this.__defineGetter__(prop, function () {
        return this.values[prop];
    });
    this.__defineSetter__(prop, function (value) {
        if (this.values[prop] !== value) {
            this.isDirty = true;
        }
        this.values[prop] = value;
    });
}

/**
 * Saves the current instance.
 *
 * @return {Promise}
 */
ActiveRecord.prototype.save = function () {
    if (!this.isNew && !this.isDirty) {
        console.warn("You are trying to save() but there are no modifications in the values");
    }

    var xhr, self = this;
    if (this.beforeSave === "function") {
        this.beforeSave();
    }

    // TODO validate

    if (this.isNew) {
        xhr = crud.create(this.config.url, this.values);
    } else if (this.isDirty) {
        xhr = crud.update(this.config.url+"/"+this.values[this.config.primaryKey], this.values);
    }

    this.isNew = false;
    this.isDirty = false;

    // we don't need an after save event. The developer can simple do: AR.save().done()

    return xhr;
}

////////////////////////////////////////// STATIC METHODS & PROPERTIES

/**
 * Object to hold all the public static methods
 * @type {Object}
 */
ActiveRecord.functions = {};

/**
 * "constants" that act as defaults
 * @type {Object}
 */
ActiveRecord.defaults = {
    ENDPOINT : "/activerecord.js/rest/",
    PRIMARY_KEY : "id"
};

/**
 * Search over the models
 *
 * Possible arguments:
 *
 * - object
 * - value (just providing a value, assumes the PK)
 * - attribute, value
 * @return {xhr}
 */
ActiveRecord.functions.find = function () {
    var xhr;

    if (arguments.length === 1 && typeof arguments[0] !== "object") {
        xhr = this.functions.__findById(arguments[0]);
    } else if (arguments.length === 2) {
        xhr = this.functions.__findByAttribute(arguments[0], arguments[1]);
    } else if (arguments.length === 1 && typeof arguments[0] === "object") {
        // TODO pass parameters as an object
    } else {
        throw "Wrong number of parameters in find() function";
    }

    return xhr;
}

/**
 * Private function for searching by id
 * @param  {mixed} id the primary key value
 * @return {Promise} A promise with the AR passed as param
 */
ActiveRecord.__findById = function (id) {
    var xhr = crud.read(this.config.url+'/'+id), self = this;
    return xhr.then(function (records) {
        return new self(records[0]);
    });
}

/**
 * Private function for search by attribute
 *
 * @param  {String} attributeName The name of the attribute to search by
 * @param  {mixed} value         The value to search for
 * @return {Promise}
 */
ActiveRecord.__findByAttribute = function (attributeName, value) {
    var xhr, params = {}, self = this, i;
    params[attributeName] = value;
    xhr = crud.read(this.config.url, params);

    return xhr.then(function (records) {
        var models = [];

        for(i in records) {
            models.push(new self(records[i]));
        }

       return models;
    });
}

/**
 * Static method for updating
 *
 * Possible params
 * - value + object = PK to update + new values
 * - value + value + value = PK to update + attribute to change + new value
 *
 * @return {[type]} [description]
 */
ActiveRecord.functions.update = function () {
    var params = {};
    if (arguments.length === 2 && typeof arguments[0] !== "object") {
        return crud.update(this.config.url+"/"+arguments[0], arguments[1]);
    } else if (arguments.length === 3 && typeof arguments[0] !== "object") {
        params[arguments[1]] = arguments[2];
        return crud.update(this.config.url+"/"+arguments[0], params);
    } else {
        throw "Wrong number of parameters in update() function";
    }
}

/**
 * Method for extending the base AR functionality. Everything you extend with this function,
 * affects the "live" object and not the static one
 *
 * @param  {Object} proto methods and properties to extend with
 * @return {Model}        the AR itself
 */
ActiveRecord.functions.extend = function (proto) {
    var i, option;
    for (option in proto) {
        this.prototype[option] = proto[option];
    }

    return this;
};

/**
 * Base method for creating new models definition and
 * extending the base ActiveRecord
 *
 * @return {ActiveRecord} the extended model
 */
ActiveRecord.register = function (modelName, config) {
    var option, i;

    /**
     * The basic concept is: we use the ActiveRecord object as our infrastructure and then
     * we create the Model object based on that
     */
    var Model = function () {
        // Run the AR constructor (Setting default values)
        ActiveRecord.apply(this, arguments);

        this.init();
        if (arguments.length === 1 && typeof arguments[0] === "object") {
            this.values = arguments[0];
            if (arguments[0][this.config.primaryKey]) {
                this.isNew = false;
            }
        }
    };

    Model.config = {};

    // First set the user overriden properties
    if (typeof config === "object") {
        for (option in config) {
            Model.config[option] = config[option];
        }
    }

    // For all missing properties apply defaults or calculate
    Model.config.modelName     = modelName;
    Model.config.endpoint      = Model.config.endpoint || ActiveRecord.defaults.ENDPOINT;
    Model.config.primaryKey    = Model.config.primaryKey || ActiveRecord.defaults.PRIMARY_KEY;
    Model.config.url           = Model.config.url || Model.config.endpoint + _plurilize(Model.config.modelName.toLowerCase());

    if (!Model.config.columns || Model.config.columns.length === 0) {
        Model.config.columns = [];
        crud.read(Model.config.url).done(function (records) {
            for (i in records[0]) {
                Model.config.columns.push(i);
            }
        });
    }

    for (i in ActiveRecord.functions) {
        Model[i] = ActiveRecord.functions[i];
    }

    Model.prototype = new ActiveRecord();
    Model.prototype._super = ActiveRecord.prototype;
    Model.prototype.config = Model.config;

    return Model;
};

////////////////////////////////////////// PRIVATE UTILITIES

/**
 * Convert a word to plural
 *
 * TODO: implement this
 *
 * @param  {String} str word to be converted
 * @return {String}     word in plural
 */
function _plurilize(str) {
    return str+"s";
}<|endoftext|>


<commit_before>$(document).ready(function(){
	var params = OC.Util.History.parseUrlQuery();

	// Hack to add a trusted domain
	if (params.trustDomain) {
		OC.dialogs.confirm(t('settings', 'Are you really sure you want add "{domain}" as trusted domain?',
				{domain: params.trustDomain}),
			t('settings', 'Add trusted domain'), function(answer) {
				if(answer) {
					$.ajax({
						type: 'POST',
						url: OC.generateUrl('settings/admin/security/trustedDomains'),
						data: { newTrustedDomain: params.trustDomain }
					}).done(function() {
						window.location.replace(OC.generateUrl('settings/admin/security'));
					});
				}
			});
	}

	$('#excludedGroups').each(function (index, element) {
		OC.Settings.setupGroupsSelect($(element));
		$(element).change(function(ev) {
			var groups = ev.val || [];
			groups = JSON.stringify(groups);
			OC.AppConfig.setValue('core', $(this).attr('name'), groups);
		});
	});

	$('#loglevel').change(function(){
		$.post(OC.generateUrl('/settings/admin/log/level'), {level: $(this).val()},function(){
			OC.Log.reload();
		} );
	});

	$('#backgroundjobs span.crondate').tipsy({gravity: 's', live: true});

	$('#backgroundjobs input').change(function(){
		if($(this).is(':checked')){
			var mode = $(this).val();
			if (mode === 'ajax' || mode === 'webcron' || mode === 'cron') {
				OC.AppConfig.setValue('core', 'backgroundjobs_mode', mode);
				// clear cron errors on background job mode change
				OC.AppConfig.deleteKey('core', 'cronErrors');
			}
		}
	});

	$('#shareAPIEnabled').change(function() {
		$('#shareAPI p:not(#enable)').toggleClass('hidden', !this.checked);
	});

	$('#enableEncryption').change(function() {
		$('#encryptionAPI div#EncryptionWarning').toggleClass('hidden');
	});

	$('#reallyEnableEncryption').click(function() {
		$('#encryptionAPI div#EncryptionWarning').toggleClass('hidden');
		$('#encryptionAPI div#EncryptionSettingsArea').toggleClass('hidden');
		OC.AppConfig.setValue('core', 'encryption_enabled', 'yes');
		$('#enableEncryption').attr('disabled', 'disabled');
	});

	$('#startmigration').click(function(event){
		$(window).on('beforeunload.encryption', function(e) {
			return t('settings', 'Migration in progress. Please wait until the migration is finished');
		});
		event.preventDefault();
		$('#startmigration').prop('disabled', true);
		OC.msg.startAction('#startmigration_msg', t('settings', 'Migration started …'));
		$.post(OC.generateUrl('/settings/admin/startmigration'), '', function(data){
			OC.msg.finishedAction('#startmigration_msg', data);
			if (data['status'] === 'success') {
				$('#encryptionAPI div#selectEncryptionModules').toggleClass('hidden');
				$('#encryptionAPI div#migrationWarning').toggleClass('hidden');
			} else {
				$('#startmigration').prop('disabled', false);
			}
			$(window).off('beforeunload.encryption');

		});
	});

	$('#shareapiExpireAfterNDays').change(function() {
		var value = $(this).val();
		if (value <= 0) {
			$(this).val("1");
		}
	});

	$('#shareAPI input:not(#excludedGroups)').change(function() {
		var value = $(this).val();
		if ($(this).attr('type') === 'checkbox') {
			if (this.checked) {
				value = 'yes';
			} else {
				value = 'no';
			}
		}
		OC.AppConfig.setValue('core', $(this).attr('name'), value);
	});

	$('#shareapiDefaultExpireDate').change(function() {
		$("#setDefaultExpireDate").toggleClass('hidden', !this.checked);
	});

	$('#allowLinks').change(function() {
		$("#publicLinkSettings").toggleClass('hidden', !this.checked);
		$('#setDefaultExpireDate').toggleClass('hidden', !(this.checked && $('#shareapiDefaultExpireDate')[0].checked));
	});

	$('#mail_smtpauth').change(function() {
		if (!this.checked) {
			$('#mail_credentials').addClass('hidden');
		} else {
			$('#mail_credentials').removeClass('hidden');
		}
	});

	$('#mail_smtpmode').change(function() {
		if ($(this).val() !== 'smtp') {
			$('#setting_smtpauth').addClass('hidden');
			$('#setting_smtphost').addClass('hidden');
			$('#mail_smtpsecure_label').addClass('hidden');
			$('#mail_smtpsecure').addClass('hidden');
			$('#mail_credentials').addClass('hidden');
		} else {
			$('#setting_smtpauth').removeClass('hidden');
			$('#setting_smtphost').removeClass('hidden');
			$('#mail_smtpsecure_label').removeClass('hidden');
			$('#mail_smtpsecure').removeClass('hidden');
			if ($('#mail_smtpauth').is(':checked')) {
				$('#mail_credentials').removeClass('hidden');
			}
		}
	});

	$('#mail_general_settings_form').change(function(){
		OC.msg.startSaving('#mail_settings_msg');
		var post = $( "#mail_general_settings_form" ).serialize();
		$.post(OC.generateUrl('/settings/admin/mailsettings'), post, function(data){
			OC.msg.finishedSaving('#mail_settings_msg', data);
		});
	});

	$('#mail_credentials_settings_submit').click(function(){
		OC.msg.startSaving('#mail_settings_msg');
		var post = $( "#mail_credentials_settings" ).serialize();
		$.post(OC.generateUrl('/settings/admin/mailsettings/credentials'), post, function(data){
			OC.msg.finishedSaving('#mail_settings_msg', data);
		});
	});

	$('#sendtestemail').click(function(event){
		event.preventDefault();
		OC.msg.startAction('#sendtestmail_msg', t('settings', 'Sending...'));
		$.post(OC.generateUrl('/settings/admin/mailtest'), '', function(data){
			OC.msg.finishedAction('#sendtestmail_msg', data);
		});
	});

	$('#allowGroupSharing').change(function() {
		$('#allowGroupSharing').toggleClass('hidden', !this.checked);
	});

	$('#shareapiExcludeGroups').change(function() {
		$("#selectExcludedGroups").toggleClass('hidden', !this.checked);
	});

	// run setup checks then gather error messages
	$.when(
		OC.SetupChecks.checkWebDAV(),
		OC.SetupChecks.checkWellKnownUrl('/.well-known/caldav/', oc_defaults.docPlaceholderUrl, $('#postsetupchecks').data('check-wellknown') === 'true'),
		OC.SetupChecks.checkWellKnownUrl('/.well-known/carddav/', oc_defaults.docPlaceholderUrl, $('#postsetupchecks').data('check-wellknown') === 'true'),
		OC.SetupChecks.checkSetup(),
		OC.SetupChecks.checkGeneric(),
		OC.SetupChecks.checkDataProtected()
	).then(function(check1, check2, check3, check4, check5, check6) {
		var messages = [].concat(check1, check2, check3, check4, check5, check6);
		var $el = $('#postsetupchecks');
		$el.find('.loading').addClass('hidden');

		var hasMessages = false;
		var $errorsEl = $el.find('.errors');
		var $warningsEl = $el.find('.warnings');
		var $infoEl = $el.find('.info');

		for (var i = 0; i < messages.length; i++ ) {
			switch(messages[i].type) {
				case OC.SetupChecks.MESSAGE_TYPE_INFO:
					$infoEl.append('
' + messages[i].msg + '
');
					break;
				case OC.SetupChecks.MESSAGE_TYPE_WARNING:
					$warningsEl.append('
' + messages[i].msg + '
');
					break;
				case OC.SetupChecks.MESSAGE_TYPE_ERROR:
				default:
					$errorsEl.append('
' + messages[i].msg + '
');
			}
		}

		if ($errorsEl.find('li').length > 0) {
			$errorsEl.removeClass('hidden');
			hasMessages = true;
		}
		if ($warningsEl.find('li').length > 0) {
			$warningsEl.removeClass('hidden');
			hasMessages = true;
		}
		if ($infoEl.find('li').length > 0) {
			$infoEl.removeClass('hidden');
			hasMessages = true;
		}

		if (hasMessages) {
			$el.find('.hint').removeClass('hidden');
		} else {
			var securityWarning = $('#security-warning');
			if (securityWarning.children('ul').children().length === 0) {
				$('#security-warning-state').find('span').removeClass('hidden');
			}
		}
	});
});
<commit_msg>Dont try to refresh log entries on log level change<commit_after>$(document).ready(function(){
	var params = OC.Util.History.parseUrlQuery();

	// Hack to add a trusted domain
	if (params.trustDomain) {
		OC.dialogs.confirm(t('settings', 'Are you really sure you want add "{domain}" as trusted domain?',
				{domain: params.trustDomain}),
			t('settings', 'Add trusted domain'), function(answer) {
				if(answer) {
					$.ajax({
						type: 'POST',
						url: OC.generateUrl('settings/admin/security/trustedDomains'),
						data: { newTrustedDomain: params.trustDomain }
					}).done(function() {
						window.location.replace(OC.generateUrl('settings/admin/security'));
					});
				}
			});
	}

	$('#excludedGroups').each(function (index, element) {
		OC.Settings.setupGroupsSelect($(element));
		$(element).change(function(ev) {
			var groups = ev.val || [];
			groups = JSON.stringify(groups);
			OC.AppConfig.setValue('core', $(this).attr('name'), groups);
		});
	});

	$('#loglevel').change(function(){
		$.post(OC.generateUrl('/settings/admin/log/level'), {level: $(this).val()});
	});

	$('#backgroundjobs span.crondate').tipsy({gravity: 's', live: true});

	$('#backgroundjobs input').change(function(){
		if($(this).is(':checked')){
			var mode = $(this).val();
			if (mode === 'ajax' || mode === 'webcron' || mode === 'cron') {
				OC.AppConfig.setValue('core', 'backgroundjobs_mode', mode);
				// clear cron errors on background job mode change
				OC.AppConfig.deleteKey('core', 'cronErrors');
			}
		}
	});

	$('#shareAPIEnabled').change(function() {
		$('#shareAPI p:not(#enable)').toggleClass('hidden', !this.checked);
	});

	$('#enableEncryption').change(function() {
		$('#encryptionAPI div#EncryptionWarning').toggleClass('hidden');
	});

	$('#reallyEnableEncryption').click(function() {
		$('#encryptionAPI div#EncryptionWarning').toggleClass('hidden');
		$('#encryptionAPI div#EncryptionSettingsArea').toggleClass('hidden');
		OC.AppConfig.setValue('core', 'encryption_enabled', 'yes');
		$('#enableEncryption').attr('disabled', 'disabled');
	});

	$('#startmigration').click(function(event){
		$(window).on('beforeunload.encryption', function(e) {
			return t('settings', 'Migration in progress. Please wait until the migration is finished');
		});
		event.preventDefault();
		$('#startmigration').prop('disabled', true);
		OC.msg.startAction('#startmigration_msg', t('settings', 'Migration started …'));
		$.post(OC.generateUrl('/settings/admin/startmigration'), '', function(data){
			OC.msg.finishedAction('#startmigration_msg', data);
			if (data['status'] === 'success') {
				$('#encryptionAPI div#selectEncryptionModules').toggleClass('hidden');
				$('#encryptionAPI div#migrationWarning').toggleClass('hidden');
			} else {
				$('#startmigration').prop('disabled', false);
			}
			$(window).off('beforeunload.encryption');

		});
	});

	$('#shareapiExpireAfterNDays').change(function() {
		var value = $(this).val();
		if (value <= 0) {
			$(this).val("1");
		}
	});

	$('#shareAPI input:not(#excludedGroups)').change(function() {
		var value = $(this).val();
		if ($(this).attr('type') === 'checkbox') {
			if (this.checked) {
				value = 'yes';
			} else {
				value = 'no';
			}
		}
		OC.AppConfig.setValue('core', $(this).attr('name'), value);
	});

	$('#shareapiDefaultExpireDate').change(function() {
		$("#setDefaultExpireDate").toggleClass('hidden', !this.checked);
	});

	$('#allowLinks').change(function() {
		$("#publicLinkSettings").toggleClass('hidden', !this.checked);
		$('#setDefaultExpireDate').toggleClass('hidden', !(this.checked && $('#shareapiDefaultExpireDate')[0].checked));
	});

	$('#mail_smtpauth').change(function() {
		if (!this.checked) {
			$('#mail_credentials').addClass('hidden');
		} else {
			$('#mail_credentials').removeClass('hidden');
		}
	});

	$('#mail_smtpmode').change(function() {
		if ($(this).val() !== 'smtp') {
			$('#setting_smtpauth').addClass('hidden');
			$('#setting_smtphost').addClass('hidden');
			$('#mail_smtpsecure_label').addClass('hidden');
			$('#mail_smtpsecure').addClass('hidden');
			$('#mail_credentials').addClass('hidden');
		} else {
			$('#setting_smtpauth').removeClass('hidden');
			$('#setting_smtphost').removeClass('hidden');
			$('#mail_smtpsecure_label').removeClass('hidden');
			$('#mail_smtpsecure').removeClass('hidden');
			if ($('#mail_smtpauth').is(':checked')) {
				$('#mail_credentials').removeClass('hidden');
			}
		}
	});

	$('#mail_general_settings_form').change(function(){
		OC.msg.startSaving('#mail_settings_msg');
		var post = $( "#mail_general_settings_form" ).serialize();
		$.post(OC.generateUrl('/settings/admin/mailsettings'), post, function(data){
			OC.msg.finishedSaving('#mail_settings_msg', data);
		});
	});

	$('#mail_credentials_settings_submit').click(function(){
		OC.msg.startSaving('#mail_settings_msg');
		var post = $( "#mail_credentials_settings" ).serialize();
		$.post(OC.generateUrl('/settings/admin/mailsettings/credentials'), post, function(data){
			OC.msg.finishedSaving('#mail_settings_msg', data);
		});
	});

	$('#sendtestemail').click(function(event){
		event.preventDefault();
		OC.msg.startAction('#sendtestmail_msg', t('settings', 'Sending...'));
		$.post(OC.generateUrl('/settings/admin/mailtest'), '', function(data){
			OC.msg.finishedAction('#sendtestmail_msg', data);
		});
	});

	$('#allowGroupSharing').change(function() {
		$('#allowGroupSharing').toggleClass('hidden', !this.checked);
	});

	$('#shareapiExcludeGroups').change(function() {
		$("#selectExcludedGroups").toggleClass('hidden', !this.checked);
	});

	// run setup checks then gather error messages
	$.when(
		OC.SetupChecks.checkWebDAV(),
		OC.SetupChecks.checkWellKnownUrl('/.well-known/caldav/', oc_defaults.docPlaceholderUrl, $('#postsetupchecks').data('check-wellknown') === 'true'),
		OC.SetupChecks.checkWellKnownUrl('/.well-known/carddav/', oc_defaults.docPlaceholderUrl, $('#postsetupchecks').data('check-wellknown') === 'true'),
		OC.SetupChecks.checkSetup(),
		OC.SetupChecks.checkGeneric(),
		OC.SetupChecks.checkDataProtected()
	).then(function(check1, check2, check3, check4, check5, check6) {
		var messages = [].concat(check1, check2, check3, check4, check5, check6);
		var $el = $('#postsetupchecks');
		$el.find('.loading').addClass('hidden');

		var hasMessages = false;
		var $errorsEl = $el.find('.errors');
		var $warningsEl = $el.find('.warnings');
		var $infoEl = $el.find('.info');

		for (var i = 0; i < messages.length; i++ ) {
			switch(messages[i].type) {
				case OC.SetupChecks.MESSAGE_TYPE_INFO:
					$infoEl.append('
' + messages[i].msg + '
');
					break;
				case OC.SetupChecks.MESSAGE_TYPE_WARNING:
					$warningsEl.append('
' + messages[i].msg + '
');
					break;
				case OC.SetupChecks.MESSAGE_TYPE_ERROR:
				default:
					$errorsEl.append('
' + messages[i].msg + '
');
			}
		}

		if ($errorsEl.find('li').length > 0) {
			$errorsEl.removeClass('hidden');
			hasMessages = true;
		}
		if ($warningsEl.find('li').length > 0) {
			$warningsEl.removeClass('hidden');
			hasMessages = true;
		}
		if ($infoEl.find('li').length > 0) {
			$infoEl.removeClass('hidden');
			hasMessages = true;
		}

		if (hasMessages) {
			$el.find('.hint').removeClass('hidden');
		} else {
			var securityWarning = $('#security-warning');
			if (securityWarning.children('ul').children().length === 0) {
				$('#security-warning-state').find('span').removeClass('hidden');
			}
		}
	});
});
<|endoftext|>


<commit_before>define( ["three", "container"], function ( THREE, container ) {
  container.innerHTML = "";
  var renderer = new THREE.WebGLRenderer( { clearColor: 0x000000 } );
  renderer.sortObjects = false;
  renderer.autoClear = false;
  renderer.shadowMapEnabled = true;
  renderer.shadowMapType = THREE.PCFShadowMap;
  container.appendChild( renderer.domElement );

  var updateSize = function () {
    renderer.setSize( container.offsetWidth, container.offsetHeight );
  };
  window.addEventListener( 'resize', updateSize, false );
  updateSize();

  return renderer;
} );
<commit_msg>Support changing dom element for renderer<commit_after>define( ["three", "container"], function ( THREE, container ) {
  container.innerHTML = "";
  var renderer = new THREE.WebGLRenderer( { clearColor: 0x000000 } );
  renderer.sortObjects = false;
  renderer.autoClear = false;
  renderer.shadowMapEnabled = true;
  renderer.shadowMapType = THREE.PCFShadowMap;

  // Allow changing of DOM element for renderer
  renderer.setContainer = function( newContainer ) {
    try {
      _container.removeChild( renderer.domElement );
    } catch ( e ) {

    }
    _container = newContainer;
    _container.appendChild( renderer.domElement );
  };
  var _container = container;
  renderer.setContainer( _container );

  // Update size on window resize
  renderer.updateSize = function () {
    renderer.setSize( _container.offsetWidth, _container.offsetHeight );
  };
  window.addEventListener( 'resize', renderer.updateSize, false );
  renderer.updateSize();

  return renderer;
} );
<|endoftext|>


<commit_before>var _config = {

};<commit_msg>Built config and quick-access function<commit_after>function log( msg ) {
	console.log("BANTP Log: ", msg );
}
function warn( msg ) {
	console.warn("BANTP Warning: ", msg );
}
function error( msg ) {
	console.error("BANTP ERROR: ", msg );
}

var _config = {
	defaults : {
		useSync : true,
		startModule : "bantp_m1"
	},
	app : {
		name : "Bad Ass New Tab Page",
		url : "https://www.bantp.com",
		api_url : "https://api.bantp.com",
		bug_url : "https://api.bantp.com/bug",
		developer : "Pierce Moore",
		dev_twitter_handle : "@KiaPierce",
		dev_twitter_url : "http://twitter.com/KiaPierce"
	},
	modules : [
		{
			name : "DASHBOARD",
			id : "bantp_m1",
			version : "0.1.0",
			updated : "Sat, 16 Feb 2013 13:58:04 GMT",
			config : {
				//
			}
		},
		{
			name : "SETTINGS",
			id : "bantp_m2",
			version : "0.1.0",
			updated : "Sat, 16 Feb 2013 13:58:04 GMT",
			config : {
				//
			}

		},
		{
			name : "BOOKMARKS",
			id : "bantp_m3",
			version : "0.1.0",
			updated : "Sat, 16 Feb 2013 13:58:04 GMT",
			config : {
				//
			}
		},
		{
			name : "MODULES",
			id : "bantp_m4",
			version : "0.1.0",
			updated : "Sat, 16 Feb 2013 13:58:04 GMT",
			config : {
				//
			}
		}
	],
	apps : [
		{
			name : "APPLICATION MANAGER",
			id : "bantp_a1",
			version : "0.1.0",
			updated : "Sat, 16 Feb 2013 13:58:04 GMT",
			config : {
				//
			}
		},
		{
			name : "TO-DO LIST",
			id : "bantp_a2",
			version : "0.1.0",
			updated : "Sat, 16 Feb 2013 13:58:04 GMT",
			config : {
				//
			}
		},
	]
};

var config = function( slug ) {
	console.group("Fetching Config Item '" + slug + "'");
	//chrome.storage.local.clear();
	try {
		log("Fetching config item: " + typeof slug + " '" + slug + "'");
		chrome.storage.local.get(slug, function(items) {
			if( _.size(items) ) {
				console.log("Found in local storage!", items );
				return items;
			} else {
				console.log("Slug '" + slug + "' not found in local storage, searching.");
				parts = slug.split(":");
				var fullConfig = _config;
				_.each(parts, function(property) {
					if( _.has(fullConfig, property) ) {
						console.log("Property '" + property + "' found in config, digging deeper", fullConfig );
						fullConfig = fullConfig[property];
					} else {
						throw error("Config item not found '" + slug + "', failed at node: '" + property + "'");
					}
				});
				console.log("Found item for slug '" + slug + "':", fullConfig );
				var toStore = {};
				toStore[slug] = fullConfig;
				chrome.storage.local.set(toStore);
			}
		});

		return chrome.storage.local.get(function(items) {
			console.log("Full local storage object", items );
			console.groupEnd();
			return items;
		});

	} catch(err) {
		error(err.stack );
		groupEnd();
		return false;
	}
};<|endoftext|>