<commit_before>// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('views/view') ;
require('views/mixins/control') ;
require('system/mixins/delegate_support');
require('views/inline_text_field');
require('views/mixins/inline_editor_delegate');

SC.ALIGN_LEFT = 'left';
SC.ALIGN_RIGHT = 'right';
SC.ALIGN_CENTER = 'center';

SC.REGULAR_FONT = 'normal';
SC.BOLD_FONT = 'bold';

/**
  @class

  Displays a static string of text.

  You use a label view anytime you need to display a static string of text
  or to display text that may need to be edited using only an inline control.

  @extends SC.View
  @extends SC.Control
  @extends SC.DelegateSupport
  @extends SC.InlineEditorDelegate
  @extends SC.Editable
  @since SproutCore 1.0
*/
SC.LabelView = SC.View.extend(SC.DelegateSupport, SC.Control, SC.InlineEditorDelegate,
/** @scope SC.LabelView.prototype */ {

  styleClass: ['sc-label-view'],

  /**
    Specify the font weight for this.  You may pass SC.REGULAR_FONT, or SC.BOLD_FONT.
  */
  fontWeight: SC.REGULAR_FONT,

  /**
    If true, value will be escaped to avoid scripting attacks.

    This is a default value that can be overridden by the
    settings on the owner view.
  */
  escapeHTML: true,
  escapeHTMLBindingDefault: SC.Binding.oneWay().bool(),

  /**
    If true, then the value will be localized.

    This is a default default that can be overidden by the
    settings in the owner view.
  */
  localize: false,
  localizeBindingDefault: SC.Binding.oneWay().bool(),

  /**
    Set this to a validator or to a function and the value
    will be passed through it before being set.

    This is a default default that can be overidden by the
    settings in the owner view.
  */
  formatter: null,

  /**
    The value of the label.

    You may also set the value using a content object and a contentValueKey.

    @field {String}
  */
  value: '',

  /**
    An optional icon to display to the left of the label.  Set this value
    to either a CSS class name (for spriting) or an image URL.
  */
  icon: null,

  /**
    Set the alignment of the label view.
  */
  textAlign: SC.ALIGN_LEFT,

  /**
    [RO] The value that will actually be displayed.

    This property is dynamically computed by applying localization,
    string conversion and other normalization utilities.

    @field
  */
  displayValue: function() {
    var value = this.get('value') ;

    // 1. apply the formatter
    var formatter = this.getDelegateProperty(this.displayDelegate, 'formatter') ;
    if (formatter) {
      var formattedValue = (SC.typeOf(formatter) === SC.T_FUNCTION) ? formatter(value, this) : formatter.fieldValueForObject(value, this) ;
      if (!SC.none(formattedValue)) value = formattedValue ;
    }

    // 2. If the returned value is an array, convert items to strings and
    // join with commas.
    if (SC.typeOf(value) === SC.T_ARRAY) {
      var ary = [];
      for(var idx=0;idx<value.get('length');idx++) {
        var x = value.objectAt(idx) ;
        if (!SC.none(x) && x.toString) x = x.toString() ;
        ary.push(x) ;
      }
      value = ary.join(',') ;
    }

    // 3. If value is not a string, convert to string. (handles 0)
    if (!SC.none(value) && value.toString) value = value.toString() ;

    // 4. Localize
    if (value && this.getDelegateProperty(this.displayDelegate, 'localize')) value = value.loc() ;

    return value ;
  }.property('value', 'localize', 'formatter').cacheable(),

  /**
    enables editing using the inline editor
  */
  isEditable: NO,
  isEditableBindingDefault: SC.Binding.bool(),

  /**
    YES if currently editing label view.
  */
  isEditing: NO,

  /**
    Validator to use during inline editing.

    If you have set isEditing to YES, then any validator you set on this
    property will be used when the label view is put into edit mode.

    @type {SC.Validator}
  */
  validator: null,

  /**
    Event dispatcher callback.
    If isEditable is set to true, opens the inline text editor view.

    @param {DOMMouseEvent} evt DOM event

  */
  doubleClick: function( evt ) { return this.beginEditing(); },

  /**
    Opens the inline text editor (closing it if it was already open for
    another view).

    @return {Boolean} YES if did begin editing
  */
  beginEditing: function()
  {
    if (this.get('isEditing')) return YES ;
    if (!this.get('isEditable')) return NO ;

    var value = this.get('value') || '' ;
    var f = this.convertFrameToView(this.get('frame'), null) ;
    var el = this.rootElement;
    SC.InlineTextFieldView.beginEditing({
      frame: f,
      delegate: this,
      exampleElement: el,
      value: value,
      multiline: NO,
      validator: this.get('validator')
    });
  },

  /**
    Cancels the current inline editor and then exits editor.

    @return {Boolean} NO if the editor could not exit.
  */
  discardEditing: function() {
    if (!this.get('isEditing')) return YES ;
    return SC.InlineTextFieldView.discardEditing() ;
  },

  /**
    Commits current inline editor and then exits editor.

    @return {Boolean} NO if the editor could not exit
  */
  commitEditing: function() {
    if (!this.get('isEditing')) return YES ;
    return SC.InlineTextFieldView.commitEditing() ;
  },

  /** @private
    Set editing to true so edits will no longer be allowed.
  */
  inlineEditorWillBeginEditing: function(inlineEditor) {
    this.set('isEditing', YES);
  },

  /** @private
    Hide the label view while the inline editor covers it.
  */
  inlineEditorDidBeginEditing: function(inlineEditor) {
    this._oldOpacity = this.$().css('opacity') ;
    this.$().css('opacity', 0.0);
  },

  /** @private
    Could check with a validator someday...
  */
  inlineEditorShouldEndEditing: function(inlineEditor, finalValue) {
    return YES ;
  },

  /** @private
    Update the field value and make it visible again.
  */
  inlineEditorDidEndEditing: function(inlineEditor, finalValue) {
    this.setIfChanged('value', finalValue) ;
    this.$().css('opacity', this._oldOpacity);
    this._oldOpacity = null ;
    this.set('isEditing', NO) ;
  },

  displayProperties: ['displayValue', 'textAlign', 'fontWeight', 'icon'],

  updateDisplay: function() {
    var ret = sc_super();
    var value = this.get('displayValue');
    var icon = this.get('icon') ;
    if ((icon !== this._label_lastIcon) || (value !== this._label_lastDisplayValue)) {

      this._label_lastDisplayValue = value ;
      this._label_lastIcon = icon ;
      if (this.getDelegateProperty(this.displayDelegate, 'escapeHTML')) {
        this.$().text(value || '');
      } else this.$().html(value || '') ;

      // insert an icon img if needed.
      if (icon) {
        var url = (icon.indexOf('/')>=0) ? icon : static_url('blank');
        var className = (url === icon) ? '' : icon ;
        icon = SC.$(''.fmt(url, className)) ;
        this.$().prepend(icon) ;
      }
    }

    this.$().css('text-align', this.get('textAlign')).css('font-weight', this.get('fontWeight'));

    return ret ;
  },

  prepareDisplay: function() {
    var ret = sc_super();
    this.updateDisplay();
    return ret ;
  }

});
<commit_msg>minor cleanup<commit_after>// ========================================================================
// SproutCore
// copyright 2006-2008 Sprout Systems, Inc.
// ========================================================================

require('views/view') ;
require('views/mixins/control') ;
require('views/mixins/inline_editor_delegate');

require('views/inline_text_field');

SC.ALIGN_LEFT = 'left';
SC.ALIGN_RIGHT = 'right';
SC.ALIGN_CENTER = 'center';

SC.REGULAR_WEIGHT = 'normal';
SC.BOLD_WEIGHT = 'bold';

/**
  @class

  Displays a static string of text.

  You use a label view anytime you need to display a static string of text
  or to display text that may need to be edited using only an inline control.

  @extends SC.View
  @extends SC.Control
  @extends SC.InlineEditorDelegate
  @since SproutCore 1.0
*/
SC.LabelView = SC.View.extend(SC.Control, SC.InlineEditorDelegate,
/** @scope SC.LabelView.prototype */ {

  styleClass: ['sc-label-view'],

  /**
    Specify the font weight for this.  You may pass SC.REGULAR_WEIGHT, or SC.BOLD_WEIGHT.
  */
  fontWeight: SC.REGULAR_WEIGHT,

  /**
    If true, value will be escaped to avoid scripting attacks.

    This is a default value that can be overridden by the
    settings on the owner view.
  */
  escapeHTML: true,
  escapeHTMLBindingDefault: SC.Binding.oneWay().bool(),

  /**
    If true, then the value will be localized.

    This is a default default that can be overidden by the
    settings in the owner view.
  */
  localize: false,
  localizeBindingDefault: SC.Binding.oneWay().bool(),

  /**
    Set this to a validator or to a function and the value
    will be passed through it before being set.

    This is a default default that can be overidden by the
    settings in the owner view.
  */
  formatter: null,

  /**
    The value of the label.

    You may also set the value using a content object and a contentValueKey.

    @field {String}
  */
  value: '',

  /**
    An optional icon to display to the left of the label.  Set this value
    to either a CSS class name (for spriting) or an image URL.
  */
  icon: null,

  /**
    Set the alignment of the label view.
  */
  textAlign: SC.ALIGN_LEFT,

  /**
    [RO] The value that will actually be displayed.

    This property is dynamically computed by applying localization,
    string conversion and other normalization utilities.

    @field
  */
  displayValue: function() {
    var value = this.get('value') ;

    // 1. apply the formatter
    var formatter = this.getDelegateProperty(this.displayDelegate, 'formatter') ;
    if (formatter) {
      var formattedValue = (SC.typeOf(formatter) === SC.T_FUNCTION) ? formatter(value, this) : formatter.fieldValueForObject(value, this) ;
      if (!SC.none(formattedValue)) value = formattedValue ;
    }

    // 2. If the returned value is an array, convert items to strings and
    // join with commas.
    if (SC.typeOf(value) === SC.T_ARRAY) {
      var ary = [];
      for(var idx=0;idx<value.get('length');idx++) {
        var x = value.objectAt(idx) ;
        if (!SC.none(x) && x.toString) x = x.toString() ;
        ary.push(x) ;
      }
      value = ary.join(',') ;
    }

    // 3. If value is not a string, convert to string. (handles 0)
    if (!SC.none(value) && value.toString) value = value.toString() ;

    // 4. Localize
    if (value && this.getDelegateProperty(this.displayDelegate, 'localize')) value = value.loc() ;

    return value ;
  }.property('value', 'localize', 'formatter').cacheable(),

  /**
    Enables editing using the inline editor.
  */
  isEditable: NO,
  isEditableBindingDefault: SC.Binding.bool(),

  /**
    YES if currently editing label view.
  */
  isEditing: NO,

  /**
    Validator to use during inline editing.

    If you have set isEditing to YES, then any validator you set on this
    property will be used when the label view is put into edit mode.

    @type {SC.Validator}
  */
  validator: null,

  /**
    Event dispatcher callback.
    If isEditable is set to true, opens the inline text editor view.

    @param {DOMMouseEvent} evt DOM event

  */
  doubleClick: function( evt ) { return this.beginEditing(); },

  /**
    Opens the inline text editor (closing it if it was already open for
    another view).

    @return {Boolean} YES if did begin editing
  */
  beginEditing: function() {
    if (this.get('isEditing')) return YES ;
    if (!this.get('isEditable')) return NO ;

    var value = this.get('value') || '' ;
    var f = this.convertFrameToView(this.get('frame'), null) ;
    var el = this.rootElement;
    SC.InlineTextFieldView.beginEditing({
      frame: f,
      delegate: this,
      exampleElement: el,
      value: value,
      multiline: NO,
      validator: this.get('validator')
    });
  },

  /**
    Cancels the current inline editor and then exits editor.

    @return {Boolean} NO if the editor could not exit.
  */
  discardEditing: function() {
    if (!this.get('isEditing')) return YES ;
    return SC.InlineTextFieldView.discardEditing() ;
  },

  /**
    Commits current inline editor and then exits editor.

    @return {Boolean} NO if the editor could not exit
  */
  commitEditing: function() {
    if (!this.get('isEditing')) return YES ;
    return SC.InlineTextFieldView.commitEditing() ;
  },

  /** @private
    Set editing to true so edits will no longer be allowed.
  */
  inlineEditorWillBeginEditing: function(inlineEditor) {
    this.set('isEditing', YES);
  },

  /** @private
    Hide the label view while the inline editor covers it.
  */
  inlineEditorDidBeginEditing: function(inlineEditor) {
    this._oldOpacity = this.$().css('opacity') ;
    this.$().css('opacity', 0.0);
  },

  /** @private
    Could check with a validator someday...
  */
  inlineEditorShouldEndEditing: function(inlineEditor, finalValue) {
    return YES ;
  },

  /** @private
    Update the field value and make it visible again.
  */
  inlineEditorDidEndEditing: function(inlineEditor, finalValue) {
    this.setIfChanged('value', finalValue) ;
    this.$().css('opacity', this._oldOpacity);
    this._oldOpacity = null ;
    this.set('isEditing', NO) ;
  },

  displayProperties: ['displayValue', 'textAlign', 'fontWeight', 'icon'],

  updateDisplay: function() {
    var ret = sc_super();
    var value = this.get('displayValue');
    var icon = this.get('icon') ;

    if ((icon !== this._label_lastIcon) || (value !== this._label_lastDisplayValue)) {
      this._label_lastDisplayValue = value ;
      this._label_lastIcon = icon ;
      if (this.getDelegateProperty(this.displayDelegate, 'escapeHTML')) {
        this.$().text(value || '');
      } else this.$().html(value || '') ;

      // insert an icon img if needed.
      if (icon) {
        var url = (icon.indexOf('/')>=0) ? icon : static_url('blank');
        var className = (url === icon) ? '' : icon ;
        icon = SC.$(''.fmt(url, className)) ;
        this.$().prepend(icon) ;
      }
    }

    this.$().css('text-align', this.get('textAlign')).css('font-weight', this.get('fontWeight'));
    return ret ;
  },

  prepareDisplay: function() {
    var ret = sc_super();
    this.updateDisplay();
    return ret ;
  }

});
<|endoftext|>


<commit_before>/**
 * @author mrdoob / http://mrdoob.com/
 */

var FRAME = ( function () {

	return {

		VERSION: 4,

		Player: function () {

			var audio = null;

			var isPlaying = false;

			var currentTime = 0;
			var playbackRate = 1;

			var loop = null;

			return {
				get isPlaying() {
					return isPlaying;
				},
				get currentTime() {
					if ( audio ) return audio.currentTime;
					return currentTime;
				},
				set currentTime( value ) {
					if ( audio ) audio.currentTime = value;
					currentTime = value;
				},
				get playbackRate() {
					if ( audio ) return audio.playbackRate;
					return playbackRate;
				},
				set playbackRate( value ) {
					playbackRate = value;
					if ( audio ) audio.playbackRate = value;
				},
				getAudio: function () {
					return audio;
				},
				setAudio: function ( value ) {
					if ( audio ) audio.pause();
					if ( value ) {
						value.currentTime = currentTime;
						if ( isPlaying ) value.play();
					}
					audio = value;
				},
				setLoop: function ( value ) {
					loop = value;
				},
				play: function () {
					if ( audio ) audio.play();
					isPlaying = true;
				},
				pause: function () {
					if ( audio ) audio.pause();
					isPlaying = false;
				},
				tick: function ( delta ) {
					if ( audio ) {
						currentTime = audio.currentTime;
					} else if ( isPlaying ) {
						currentTime += ( delta / 1000 ) * playbackRate;
					}
					if ( loop ) {
						if ( currentTime > loop[ 1 ] ) currentTime = loop[ 0 ];
					}
				}

			}

		},

		Resources: function () {

			var resources = {};

			return {

				get: function ( name ) {

					return resources[ name ];

				},

				set: function ( name, resource ) {

					resources[ name ] = resource;

				}

			}

		},

		//

		/*
		Curves: {

			Linear: function ( points ) {

				function linear( p0, p1, t0, t1, t ) {

					return ( p1 - p0 ) * ( ( t - t0 ) / ( t1 - t0 ) ) + p0;

				}

				this.points = points;
				this.value = 0;

				this.update = function ( time ) {

					if ( time <= points[ 0 ] ) {

						this.value = points[ 1 ];

					} else if ( time >= points[ points.length - 2 ] ) {

						this.value = points[ points.length - 1 ];

					} else {

						for ( var i = 0, l = points.length; i < l; i += 2 ) {

							if ( time < points[ i + 2 ] ) {

								this.value = linear( points[ i + 1 ], points[ i + 3 ], points[ i ], points[ i + 2 ], time );
								break;

							}

						}

					}

				};

			},

			Sin: function () {

				var frequency = 10;

				this.value = 0;

				this.update = function ( time ) {

					this.value = Math.sin( time * frequency );

				};

			},

			Saw: function ( frequency, offset, min, max ) {

				var delta = max - min;

				this.frequency = frequency;
				this.offset = offset;
				this.min = min;
				this.max = max;
				this.value = 0;

				this.update = function ( time ) {

					this.value = ( ( ( time - offset ) % frequency ) / frequency ) * delta + min;

				};

			}

		},
		*/

		Parameters: {

			Boolean: function ( name, value ) {
				this.name = name;
				this.value = value !== undefined ? value : true;
			},

			Color: function ( name, value ) {
				this.name = name;
				this.value = value !== undefined ? value : 0xffffff;
			},

			Float: function ( name, value, min, max ) {
				this.name = name;
				this.value = value || 0.0;
				this.min = min !== undefined ? min : - Infinity;
				this.max = max !== undefined ? max : Infinity;
			},

			Integer: function ( name, value, min, max ) {
				this.name = name;
				this.value = value || 0;
				this.min = min !== undefined ? min : - Infinity;
				this.max = max !== undefined ? max : Infinity;
			},

			String: function ( name, value ) {
				this.name = name;
				this.value = value !== undefined ? value : '';
			},

			Vector2: function ( name, value ) {
				this.name = name;
				this.value = value !== undefined ? value : [ 0, 0 ];
			},

			Vector3: function ( name, value ) {
				this.name = name;
				this.value = value !== undefined ? value : [ 0, 0, 0 ];
			}

		},

		Effect: function ( name, source ) {

			this.name = name;
			this.source = source || 'var parameters = {\n\tvalue: new FRAME.Parameters.Float( \'Value\', 1.0 )\n};\n\nfunction start(){}\n\nfunction end(){}\n\nfunction update( progress ){}';
			this.program = null;
			this.compile = function ( resources, player ) {

				this.program = ( new Function( 'resources, player, parameters, start, end, update', this.source + '\nreturn { parameters: parameters, start: start, end: end, update: update };' ) )( resources, player );

			};

		},

		Animation: function () {

			var id = 0;

			return function ( name, start, end, layer, effect, enabled ) {

				if ( enabled === undefined ) enabled = true; // TODO remove this

				this.id = id ++;
				this.name = name;
				this.start = start;
				this.end = end;
				this.layer = layer;
				this.effect = effect;
				this.enabled = enabled;

			};

		}(),

		Timeline: function () {

			var effects = [];

			var animations = [];
			var curves = [];

			var active = [];

			var next = 0, prevtime = 0;

			function layerSort( a, b ) { return a.layer - b.layer; }
			function startSort( a, b ) { return a.start === b.start ? layerSort( a, b ) : a.start - b.start; }

			function loadFile( url, onLoad ) {

				var request = new XMLHttpRequest();
				request.open( 'GET', url, true );
				request.addEventListener( 'load', function ( event ) {

					onLoad( event.target.response );

				} );
				request.send( null );

			}

			return {

				animations: animations,
				curves: curves,

				load: function ( url, onLoad ) {

					var scope = this;

					loadFile( url, function ( text ) {

						scope.parse( JSON.parse( text ), onLoad );

					} );

				},

				loadLibraries: function ( libraries, onLoad ) {

					var count = 0;

					function loadNext() {

						if ( count === libraries.length ) {

							onLoad();
							return;

						}

						var url = libraries[ count ++ ];

						loadFile( url, function ( content ) {

							var script = document.createElement( 'script' );
							script.id = 'library-' + count;
							script.textContent = '( function () { ' + content + '} )()';
							document.head.appendChild( script );

							loadNext();

						} );

					}

					loadNext();

				},

				parse: function ( json, onLoad ) {

					var scope = this;

					var libraries = json.libraries || [];

					this.loadLibraries( libraries, function () {

						for ( var i = 0; i < json.includes.length; i ++ ) {

							var data = json.includes[ i ];
							var source = data[ 1 ];

							if ( Array.isArray( source ) ) source = source.join( '\n' );

							var script = document.createElement( 'script' );
							script.id = 'library-' + i;
							script.textContent = '( function () { ' + source + '} )()';
							document.head.appendChild( script );

						}

						// Effects

						for ( var i = 0; i < json.effects.length; i ++ ) {

							var data = json.effects[ i ];

							var name = data[ 0 ];
							var source = data[ 1 ];

							if ( Array.isArray( source ) ) source = source.join( '\n' );

							effects.push( new FRAME.Effect( name, source ) );

						}

						for ( var i = 0; i < json.animations.length; i ++ ) {

							var data = json.animations[ i ];

							var animation = new FRAME.Animation(
								data[ 0 ],
								data[ 1 ],
								data[ 2 ],
								data[ 3 ],
								effects[ data[ 4 ] ],
								data[ 5 ]
							);

							animations.push( animation );

						}

						scope.sort();

						if ( onLoad ) onLoad();

					} );

				},

				compile: function ( resources, player ) {

					var animations = this.animations;

					for ( var i = 0, l = animations.length; i < l; i ++ ) {

						var animation = animations[ i ];

						if ( animation.effect.program === null ) {

							animation.effect.compile( resources, player );

						}

					}

				},

				add: function ( animation ) {

					animations.push( animation );
					this.sort();

				},

				remove: function ( animation ) {

					var i = animations.indexOf( animation );

					if ( i !== -1 ) {

						animations.splice( i, 1 );

					}

				},

				sort: function () {

					animations.sort( startSort );

				},

				update: function ( time ) {

					if ( prevtime > time ) {

						this.reset();

					}

					var animation;

					// add to active

					while ( animations[ next ] ) {

						animation = animations[ next ];

						if ( animation.enabled ) {

							if ( animation.start > time ) break;

							if ( animation.end > time ) {

								if ( animation.effect.program.start ) {

									animation.effect.program.start();

								}

								active.push( animation );

							}

						}

						next ++;

					}

					// remove from active

					var i = 0;

					while ( active[ i ] ) {

						animation = active[ i ];

						if ( animation.start > time || animation.end < time ) {

							if ( animation.effect.program.end ) {

								animation.effect.program.end();

							}

							active.splice( i, 1 );

							continue;

						}

						i ++;

					}

					/*
					// update curves

					for ( var i = 0, l = curves.length; i < l; i ++ ) {

						curves[ i ].update( time, time - prevtime );

					}
					*/

					// render

					active.sort( layerSort );

					for ( var i = 0, l = active.length; i < l; i ++ ) {

						animation = active[ i ];
						animation.effect.program.update( ( time - animation.start ) / ( animation.end - animation.start ), time - prevtime );

					}

					prevtime = time;

				},

				reset: function () {

					while ( active.length ) {

						var animation = active.pop();
						var program = animation.effect.program;

						if ( program.end ) program.end();

					}

					next = 0;

				}

			};

		},

		// DEPRECATED

		getDOM: function () {

			console.error( 'FRAME.getDOM() has been removed.' );
			return document.createElement( 'div' );

		},

		setDOM: function ( value ) {

			console.error( 'FRAME.setDOM() has been removed.' );

		},

		addResource: function () {

			console.error( 'FRAME.addResource() has been removed.' );

		},

		getResource: function () {

			console.error( 'FRAME.getResource() has been removed.' );

		}

	};

} )();
<commit_msg>Removed unneeded IIFE.<commit_after>/**
 * @author mrdoob / http://mrdoob.com/
 */

var FRAME = {

	VERSION: 4,

	Player: function () {

		var audio = null;

		var isPlaying = false;

		var currentTime = 0;
		var playbackRate = 1;

		var loop = null;

		return {
			get isPlaying() {
				return isPlaying;
			},
			get currentTime() {
				if ( audio ) return audio.currentTime;
				return currentTime;
			},
			set currentTime( value ) {
				if ( audio ) audio.currentTime = value;
				currentTime = value;
			},
			get playbackRate() {
				if ( audio ) return audio.playbackRate;
				return playbackRate;
			},
			set playbackRate( value ) {
				playbackRate = value;
				if ( audio ) audio.playbackRate = value;
			},
			getAudio: function () {
				return audio;
			},
			setAudio: function ( value ) {
				if ( audio ) audio.pause();
				if ( value ) {
					value.currentTime = currentTime;
					if ( isPlaying ) value.play();
				}
				audio = value;
			},
			setLoop: function ( value ) {
				loop = value;
			},
			play: function () {
				if ( audio ) audio.play();
				isPlaying = true;
			},
			pause: function () {
				if ( audio ) audio.pause();
				isPlaying = false;
			},
			tick: function ( delta ) {
				if ( audio ) {
					currentTime = audio.currentTime;
				} else if ( isPlaying ) {
					currentTime += ( delta / 1000 ) * playbackRate;
				}
				if ( loop ) {
					if ( currentTime > loop[ 1 ] ) currentTime = loop[ 0 ];
				}
			}

		}

	},

	Resources: function () {

		var resources = {};

		return {

			get: function ( name ) {

				return resources[ name ];

			},

			set: function ( name, resource ) {

				resources[ name ] = resource;

			}

		}

	},

	//

	/*
	Curves: {

		Linear: function ( points ) {

			function linear( p0, p1, t0, t1, t ) {

				return ( p1 - p0 ) * ( ( t - t0 ) / ( t1 - t0 ) ) + p0;

			}

			this.points = points;
			this.value = 0;

			this.update = function ( time ) {

				if ( time <= points[ 0 ] ) {

					this.value = points[ 1 ];

				} else if ( time >= points[ points.length - 2 ] ) {

					this.value = points[ points.length - 1 ];

				} else {

					for ( var i = 0, l = points.length; i < l; i += 2 ) {

						if ( time < points[ i + 2 ] ) {

							this.value = linear( points[ i + 1 ], points[ i + 3 ], points[ i ], points[ i + 2 ], time );
							break;

						}

					}

				}

			};

		},

		Sin: function () {

			var frequency = 10;

			this.value = 0;

			this.update = function ( time ) {

				this.value = Math.sin( time * frequency );

			};

		},

		Saw: function ( frequency, offset, min, max ) {

			var delta = max - min;

			this.frequency = frequency;
			this.offset = offset;
			this.min = min;
			this.max = max;
			this.value = 0;

			this.update = function ( time ) {

				this.value = ( ( ( time - offset ) % frequency ) / frequency ) * delta + min;

			};

		}

	},
	*/

	Parameters: {

		Boolean: function ( name, value ) {
			this.name = name;
			this.value = value !== undefined ? value : true;
		},

		Color: function ( name, value ) {
			this.name = name;
			this.value = value !== undefined ? value : 0xffffff;
		},

		Float: function ( name, value, min, max ) {
			this.name = name;
			this.value = value || 0.0;
			this.min = min !== undefined ? min : - Infinity;
			this.max = max !== undefined ? max : Infinity;
		},

		Integer: function ( name, value, min, max ) {
			this.name = name;
			this.value = value || 0;
			this.min = min !== undefined ? min : - Infinity;
			this.max = max !== undefined ? max : Infinity;
		},

		String: function ( name, value ) {
			this.name = name;
			this.value = value !== undefined ? value : '';
		},

		Vector2: function ( name, value ) {
			this.name = name;
			this.value = value !== undefined ? value : [ 0, 0 ];
		},

		Vector3: function ( name, value ) {
			this.name = name;
			this.value = value !== undefined ? value : [ 0, 0, 0 ];
		}

	},

	Effect: function ( name, source ) {

		this.name = name;
		this.source = source || 'var parameters = {\n\tvalue: new FRAME.Parameters.Float( \'Value\', 1.0 )\n};\n\nfunction start(){}\n\nfunction end(){}\n\nfunction update( progress ){}';
		this.program = null;
		this.compile = function ( resources, player ) {

			this.program = ( new Function( 'resources, player, parameters, start, end, update', this.source + '\nreturn { parameters: parameters, start: start, end: end, update: update };' ) )( resources, player );

		};

	},

	Animation: function () {

		var id = 0;

		return function ( name, start, end, layer, effect, enabled ) {

			if ( enabled === undefined ) enabled = true; // TODO remove this

			this.id = id ++;
			this.name = name;
			this.start = start;
			this.end = end;
			this.layer = layer;
			this.effect = effect;
			this.enabled = enabled;

		};

	}(),

	Timeline: function () {

		var effects = [];

		var animations = [];
		var curves = [];

		var active = [];

		var next = 0, prevtime = 0;

		function layerSort( a, b ) { return a.layer - b.layer; }
		function startSort( a, b ) { return a.start === b.start ? layerSort( a, b ) : a.start - b.start; }

		function loadFile( url, onLoad ) {

			var request = new XMLHttpRequest();
			request.open( 'GET', url, true );
			request.addEventListener( 'load', function ( event ) {

				onLoad( event.target.response );

			} );
			request.send( null );

		}

		return {

			animations: animations,
			curves: curves,

			load: function ( url, onLoad ) {

				var scope = this;

				loadFile( url, function ( text ) {

					scope.parse( JSON.parse( text ), onLoad );

				} );

			},

			loadLibraries: function ( libraries, onLoad ) {

				var count = 0;

				function loadNext() {

					if ( count === libraries.length ) {

						onLoad();
						return;

					}

					var url = libraries[ count ++ ];

					loadFile( url, function ( content ) {

						var script = document.createElement( 'script' );
						script.id = 'library-' + count;
						script.textContent = '( function () { ' + content + '} )()';
						document.head.appendChild( script );

						loadNext();

					} );

				}

				loadNext();

			},

			parse: function ( json, onLoad ) {

				var scope = this;

				var libraries = json.libraries || [];

				this.loadLibraries( libraries, function () {

					for ( var i = 0; i < json.includes.length; i ++ ) {

						var data = json.includes[ i ];
						var source = data[ 1 ];

						if ( Array.isArray( source ) ) source = source.join( '\n' );

						var script = document.createElement( 'script' );
						script.id = 'library-' + i;
						script.textContent = '( function () { ' + source + '} )()';
						document.head.appendChild( script );

					}

					// Effects

					for ( var i = 0; i < json.effects.length; i ++ ) {

						var data = json.effects[ i ];

						var name = data[ 0 ];
						var source = data[ 1 ];

						if ( Array.isArray( source ) ) source = source.join( '\n' );

						effects.push( new FRAME.Effect( name, source ) );

					}

					for ( var i = 0; i < json.animations.length; i ++ ) {

						var data = json.animations[ i ];

						var animation = new FRAME.Animation(
							data[ 0 ],
							data[ 1 ],
							data[ 2 ],
							data[ 3 ],
							effects[ data[ 4 ] ],
							data[ 5 ]
						);

						animations.push( animation );

					}

					scope.sort();

					if ( onLoad ) onLoad();

				} );

			},

			compile: function ( resources, player ) {

				var animations = this.animations;

				for ( var i = 0, l = animations.length; i < l; i ++ ) {

					var animation = animations[ i ];

					if ( animation.effect.program === null ) {

						animation.effect.compile( resources, player );

					}

				}

			},

			add: function ( animation ) {

				animations.push( animation );
				this.sort();

			},

			remove: function ( animation ) {

				var i = animations.indexOf( animation );

				if ( i !== -1 ) {

					animations.splice( i, 1 );

				}

			},

			sort: function () {

				animations.sort( startSort );

			},

			update: function ( time ) {

				if ( prevtime > time ) {

					this.reset();

				}

				var animation;

				// add to active

				while ( animations[ next ] ) {

					animation = animations[ next ];

					if ( animation.enabled ) {

						if ( animation.start > time ) break;

						if ( animation.end > time ) {

							if ( animation.effect.program.start ) {

								animation.effect.program.start();

							}

							active.push( animation );

						}

					}

					next ++;

				}

				// remove from active

				var i = 0;

				while ( active[ i ] ) {

					animation = active[ i ];

					if ( animation.start > time || animation.end < time ) {

						if ( animation.effect.program.end ) {

							animation.effect.program.end();

						}

						active.splice( i, 1 );

						continue;

					}

					i ++;

				}

				/*
				// update curves

				for ( var i = 0, l = curves.length; i < l; i ++ ) {

					curves[ i ].update( time, time - prevtime );

				}
				*/

				// render

				active.sort( layerSort );

				for ( var i = 0, l = active.length; i < l; i ++ ) {

					animation = active[ i ];
					animation.effect.program.update( ( time - animation.start ) / ( animation.end - animation.start ), time - prevtime );

				}

				prevtime = time;

			},

			reset: function () {

				while ( active.length ) {

					var animation = active.pop();
					var program = animation.effect.program;

					if ( program.end ) program.end();

				}

				next = 0;

			}

		};

	},

	// DEPRECATED

	getDOM: function () {

		console.error( 'FRAME.getDOM() has been removed.' );
		return document.createElement( 'div' );

	},

	setDOM: function ( value ) {

		console.error( 'FRAME.setDOM() has been removed.' );

	},

	addResource: function () {

		console.error( 'FRAME.addResource() has been removed.' );

	},

	getResource: function () {

		console.error( 'FRAME.getResource() has been removed.' );

	}

};
<|endoftext|>


<commit_before>import './Config'
import './ErrorHandler'
import './Paths'
import './ProviderCollection'
import './UrlGenerator'

import './Routing/Router'
import './Routing/RoutingProvider'
import './Routing/UpgradeDispatcher'

import './Routing/Extensions/RequestExtension'
import './Routing/Extensions/ResponseExtension'
import './Routing/Extensions/RouteExtension'

const Express = require('express/lib/express.js')
const EventEmitter = require('events')

export class Grind extends EventEmitter {
	express = null
	_env = null

	config = null
	errorHandler = null
	paths = null
	routes = null
	url = null

	booted = false
	port = 3000
	providers = null

	constructor(parameters = { }) {
		RequestExtension()
		ResponseExtension()
		RouteExtension()

		super()

		this.express = Express()
		this.express.disable('etag')
		this.express._grind = this

		this._env = parameters.env

		const routerClass = parameters.routerClass || Router
		const configClass = parameters.configClass || Config
		const errorHandlerClass = parameters.errorHandlerClass || ErrorHandler
		const urlGeneratorClass = parameters.urlGeneratorClass || UrlGenerator
		const pathsClass = parameters.pathsClass || Paths

		const routingProvider = parameters.routingProvider || RoutingProvider
		routingProvider.priority = RoutingProvider.priority

		const parent = module.parent.parent === null ? module.parent : (
			module.parent.parent.parent === null ? module.parent.parent : module.parent.parent.parent
		)

		this.paths = new pathsClass(parent.filename)
		this.config = new configClass(this)
		this.routes = new routerClass(this)
		this.errorHandler = new errorHandlerClass(this)

		this.providers = new ProviderCollection
		this.providers.add(routingProvider)

		this.debug = this.config.get('app.debug', this.env() === 'local')
		this.port = parameters.port || process.env.NODE_PORT || this.config.get('app.port', 3000)

		this.url = new urlGeneratorClass(this)

		this.on('error', err => Log.error('EventEmitter error', err))
	}

	env() {
		return this._env || process.env.NODE_ENV || 'local'
	}

	async boot() {
		if(this.booted) {
			return
		}

		this.providers.sort((a, b) => a.priority > b.priority ? -1 : 1)

		for(const provider of this.providers) {
			await provider(this)
		}

		this.emit('boot', this)

		this.booted = true
	}

	async listen(...args) {
		await this.boot()

		// Register error handler
		this.express.use((err, req, res, next) => {
			this.errorHandler.handle(err, req, res, next)
		})

		// Register 404 handler
		this.express.use((req, res, next) => {
			this.errorHandler.handle(new NotFoundError, req, res, next)
		})

		this.server = require('http').createServer(this.express)

		// Register upgrade handlers
		if(Object.keys(this.routes.upgraders).length > 0) {
			this.server.on('upgrade', UpgradeDispatcher.bind(this.routes, this.routes.upgraders))
		}

		this.emit('listen', this, this.server)

		return this.server.listen(...args)
	}

	async shutdown() {
		if(!this.booted) {
			return
		}

		for(const provider of this.providers) {
			if(typeof provider.shutdown !== 'function') {
				continue
			}

			try {
				await provider.shutdown(this)
			} catch(e) {
				Log.error(`Error while shutting ${provider.name} down`, e)
			}
		}

		this.emit('shutdown', this)

		this.booted = false
	}

	lazy(name, callback) {
		Object.defineProperty(this, name, {
			configurable: true,
			get: () => {
				const value = callback(this)

				Object.defineProperty(this, name, {
					value: value,
					writeable: false
				})

				return value
			}
		})
	}

	enable(...args) { return this.express.enable(...args) }
	disable(...args) { return this.express.disable(...args) }

}
<commit_msg>Added comments to Grind.js<commit_after>import './Config'
import './ErrorHandler'
import './Paths'
import './ProviderCollection'
import './UrlGenerator'

import './Routing/Router'
import './Routing/RoutingProvider'
import './Routing/UpgradeDispatcher'

import './Routing/Extensions/RequestExtension'
import './Routing/Extensions/ResponseExtension'
import './Routing/Extensions/RouteExtension'

const Express = require('express/lib/express.js')
const EventEmitter = require('events')

/**
 * Main Application class for Grind
 */
export class Grind extends EventEmitter {
	express = null
	_env = null

	config = null
	errorHandler = null
	paths = null
	routes = null
	url = null

	booted = false
	port = 3000
	providers = null

	/**
	 * Create an instance of the Grind Application
	 *
	 * @param  {string}   options.env               Override env name
	 *                                              By default, env is populated by the NODE_ENV
	 *                                              environment variable. This value takes
	 *                                              precedence over all other values.
	 * @param  {integer}  options.port              Override port to listen on
	 *                                              By default, port is populated by the app.port
	 *                                              config value, with an optional override via the
	 *                                              NODE_PORT environment variable.  This value
	 *                                              takes precedence over all other values.
	 * @param  {Class}   options.routerClass        Override for Router class
	 * @param  {Class}   options.configClass        Override for Config class
	 * @param  {Class}   options.errorHandlerClass  Override for ErrorHandler class
	 * @param  {Class}   options.urlGeneratorClass  Override for UrlGenerator class
	 * @param  {Class}   options.pathsClass         Override for Paths class
	 * @param  {Function} options.routingProvider   Override for RoutingProvider
	 */
	constructor({
		env,
		port,
		routerClass,
		configClass,
		errorHandlerClass,
		urlGeneratorClass,
		pathsClass,
		routingProvider
	}) {
		RequestExtension()
		ResponseExtension()
		RouteExtension()

		super()

		this.express = Express()
		this.express.disable('etag')
		this.express._grind = this

		this._env = env

		routerClass = routerClass || Router
		configClass = configClass || Config
		errorHandlerClass = errorHandlerClass || ErrorHandler
		urlGeneratorClass = urlGeneratorClass || UrlGenerator
		pathsClass = pathsClass || Paths

		routingProvider = routingProvider || RoutingProvider
		routingProvider.priority = RoutingProvider.priority

		const parent = module.parent.parent === null ? module.parent : (
			module.parent.parent.parent === null ? module.parent.parent : module.parent.parent.parent
		)

		this.paths = new pathsClass(parent.filename)
		this.config = new configClass(this)
		this.routes = new routerClass(this)
		this.errorHandler = new errorHandlerClass(this)

		this.providers = new ProviderCollection
		this.providers.add(routingProvider)

		this.debug = this.config.get('app.debug', this.env() === 'local')
		this.port = port || process.env.NODE_PORT || this.config.get('app.port', 3000)

		this.url = new urlGeneratorClass(this)

		this.on('error', err => Log.error('EventEmitter error', err))
	}

	/**
	 * @return {string} Current environment
	 */
	env() {
		return this._env || process.env.NODE_ENV || 'local'
	}

	/**
	 * Boots the current application, if not already booted.
	 * This will notify all registered providers and start them.
	 *
	 * @return {Promise}
	 */
	async boot() {
		if(this.booted) {
			return
		}

		this.providers.sort((a, b) => a.priority > b.priority ? -1 : 1)

		for(const provider of this.providers) {
			await provider(this)
		}

		this.emit('boot', this)

		this.booted = true
	}

	/**
	 * Starts the HTTP server
	 * @return {object} HTTP server instance
	 */
	async listen(...args) {
		await this.boot()

		// Register error handler
		this.express.use((err, req, res, next) => {
			this.errorHandler.handle(err, req, res, next)
		})

		// Register 404 handler
		this.express.use((req, res, next) => {
			this.errorHandler.handle(new NotFoundError, req, res, next)
		})

		this.server = require('http').createServer(this.express)

		// Register upgrade handlers
		if(Object.keys(this.routes.upgraders).length > 0) {
			this.server.on('upgrade', UpgradeDispatcher.bind(this.routes, this.routes.upgraders))
		}

		this.emit('listen', this, this.server)

		return this.server.listen(...args)
	}

	/**
	 * Shutsdown the current application, if booted.
	 * This will notify all registered providers with a shutdown handler.
	 *
	 * @return {Promise}
	 */
	async shutdown() {
		if(!this.booted) {
			return
		}

		for(const provider of this.providers) {
			if(typeof provider.shutdown !== 'function') {
				continue
			}

			try {
				await provider.shutdown(this)
			} catch(e) {
				Log.error(`Error while shutting ${provider.name} down`, e)
			}
		}

		this.emit('shutdown', this)

		this.booted = false
	}

	/**
	 * Register a property on the app instance that will be
	 * populated with the value of `callback` after the first
	 * time it’s called.
	 *
	 * @param  {string}   name     Name of the property to registe
	 * @param  {Function} callback Callback handler that should return
	 *                             the value of the property
	 */
	lazy(name, callback) {
		Object.defineProperty(this, name, {
			configurable: true,
			get: () => {
				const value = callback(this)

				Object.defineProperty(this, name, {
					value: value,
					writeable: false
				})

				return value
			}
		})
	}

	/**
	 * Passthrough for express.enable()
	 */
	enable(...args) {
		return this.express.enable(...args)
	}

	/**
	 * Passthrough for express.disable()
	 */
	disable(...args) {
		return this.express.disable(...args)
	}

}
<|endoftext|>


<commit_before>var Component = require('yacomponent'),
	inheritClass = require('inherit-class'),
	Item = Collection.Item = require('./Item');

function Collection() {
	this.items = [];

	this.fakeItem = undefined;

	this.lastIndex = 0;
}

Collection.defaults = {
	dName: 'list',
	parent: null,
	Item: Item
};

inheritClass(Collection, Component, 'component');

Collection.prototype = $.extend({}, Collection.prototype, {
	init: function() {
		this.items = [];
		this.defineParentName();
		Component.prototype.init.call(this);
		this.makeFakeItem();
		this.defineItems();
		this.defineEvents();
	},

	makeFakeItem: function() {
		this.fakeItem = this.initItem({$el: $([]), fake: true, instance: false});
	},

	defineParentName: function() {
		if (this.options.parent) return;
		this.options.parent = this;
	},

	defineEvents: function() {
		var self = this;
		this.$el.on('click', this.elDel('makeSelector', 'remove'), function(e) {
			e.preventDefault();
			self.onRemove(e);
		});
		$('.' + this.options.dName + 'Add').on('click', function(e) {
			e.preventDefault();
			self.clickAdd();
		});
	},

	elDel: function(method, arg) {
		var args = [this.getItemDName()];
		args.push(arg);
		return this.options.parent[method].call(this.options.parent, args);
	},

	onRemove: function(event) {
		var item = this.getByEl(event.currentTarget);
		this.remove(item);
	},

	getByEl: function(el) {
		el = el instanceof $ ? el[0] : el;
		return this.items.filter(function(item) {
			return item.$el[0] === el || item.$el.find(el).length;
		})[0];
	},

	getItemDName: function() {
		return this.options.Item.defaults.dName;
	},

	defineItems: function() {
		this.$items = this.options.parent.find(this.getItemDName());
		this.setLimit();
		this.$items.each(this.define.bind(this));
	},

	define: function(i, el) {
		this.add(this.make(el));
	},

	make: function(el) {
		var $el = $(el);
		var data = $el.data(this.options.parent.dName + '-' + this.options.Item.defaults.dName);
		data = data || {};
		data.index = this.lastIndex++;
		return this.initItem({$el: $el, data: data});
	},

	setLimit: function() {
		if (!this.options.limit) return;
		this.$items.slice(this.options.limit)
			.detach()
			.each(this.define.bind(this))
			.get();
		this.removedItems = this.items;
		this.items = [];
	},

	add: function(item, insert) {
		insert = typeof insert == 'undefined' ? false : insert;
		this.items.push(item);
		if (insert) this.$el.append(item.$el);
	},

	addByData: function(data) {
		var html = this.render(data);
		var item = this.initItem({$el: $(html), data: data});
		this.add(item, true);
	},

	render: function(data) {
		throw new Error('yacollection: #render is not implemented');
	},

	addPatchByData: function(data) {
		data.forEach(this.addByData.bind(this));
	},

	clickAdd: function() {
		var data = this.getClickAddData();
		this.addByData(data);
	},

	getClickAddData: function() {
		return {};
	},

	remove: function(item) {
		var index = this.items.indexOf(item);
		item.$el.remove();
		this.items.splice(index, 1);
	},

	initItem: function(options) {
		options = $.extend({
			dName: this.options.parent.makeName(this.getItemDName()),
			template: this.options.itemTemplate
		}, options, this.getItemOptions());
		return this.options.Item.init(options);
	},

	getItemOptions: function() {
		return this.options.item;
	},

	clear: function() {
		this.items.forEach(function(i) {
			i.$el.remove();
		});
		this.items = [];
		this.lastIndex = 0;
	},

	getData: function() {
		return this.items.map(function(i) {
			return i.data;
		});
	},

	insert: function(html) {
		this.$el[0].innerHTML = html;
		this.items = [];
		this.defineItems();
	},

	append: function(html) {
		var $items = $(html).appendTo(this.$el);
		$items.each(this.define.bind(this));
	},
});
Collection.prototype.constructor = Collection;

module.exports = Collection;<commit_msg>improve append<commit_after>var Component = require('yacomponent'),
	inheritClass = require('inherit-class'),
	Item = Collection.Item = require('./Item');

function Collection() {
	this.items = [];

	this.fakeItem = undefined;

	this.lastIndex = 0;
}

Collection.defaults = {
	dName: 'list',
	parent: null,
	Item: Item
};

inheritClass(Collection, Component, 'component');

Collection.prototype = $.extend({}, Collection.prototype, {
	init: function() {
		this.items = [];
		this.defineParentName();
		Component.prototype.init.call(this);
		this.makeFakeItem();
		this.defineItems();
		this.defineEvents();
	},

	makeFakeItem: function() {
		this.fakeItem = this.initItem({$el: $([]), fake: true, instance: false});
	},

	defineParentName: function() {
		if (this.options.parent) return;
		this.options.parent = this;
	},

	defineEvents: function() {
		var self = this;
		this.$el.on('click', this.elDel('makeSelector', 'remove'), function(e) {
			e.preventDefault();
			self.onRemove(e);
		});
		$('.' + this.options.dName + 'Add').on('click', function(e) {
			e.preventDefault();
			self.clickAdd();
		});
	},

	elDel: function(method, arg) {
		var args = [this.getItemDName()];
		args.push(arg);
		return this.options.parent[method].call(this.options.parent, args);
	},

	onRemove: function(event) {
		var item = this.getByEl(event.currentTarget);
		this.remove(item);
	},

	getByEl: function(el) {
		el = el instanceof $ ? el[0] : el;
		return this.items.filter(function(item) {
			return item.$el[0] === el || item.$el.find(el).length;
		})[0];
	},

	getItemDName: function() {
		return this.options.Item.defaults.dName;
	},

	defineItems: function() {
		this.$items = this.options.parent.find(this.getItemDName());
		this.setLimit();
		this.$items.each(this.define.bind(this));
	},

	define: function(i, el) {
		return this.add(this.make(el));
	},

	make: function(el) {
		var $el = $(el);
		var data = $el.data(this.options.parent.dName + '-' + this.options.Item.defaults.dName);
		data = data || {};
		data.index = this.lastIndex++;
		return this.initItem({$el: $el, data: data});
	},

	setLimit: function() {
		if (!this.options.limit) return;
		this.$items.slice(this.options.limit)
			.detach()
			.each(this.define.bind(this))
			.get();
		this.removedItems = this.items;
		this.items = [];
	},

	add: function(item, insert) {
		insert = typeof insert == 'undefined' ? false : insert;
		this.items.push(item);
		if (insert) this.$el.append(item.$el);
		return item;
	},

	addByData: function(data) {
		var html = this.render(data);
		var item = this.initItem({$el: $(html), data: data});
		this.add(item, true);
	},

	render: function(data) {
		throw new Error('yacollection: #render is not implemented');
	},

	addPatchByData: function(data) {
		data.forEach(this.addByData.bind(this));
	},

	clickAdd: function() {
		var data = this.getClickAddData();
		this.addByData(data);
	},

	getClickAddData: function() {
		return {};
	},

	remove: function(item) {
		var index = this.items.indexOf(item);
		item.$el.remove();
		this.items.splice(index, 1);
	},

	initItem: function(options) {
		options = $.extend({
			dName: this.options.parent.makeName(this.getItemDName()),
			template: this.options.itemTemplate
		}, options, this.getItemOptions());
		return this.options.Item.init(options);
	},

	getItemOptions: function() {
		return this.options.item;
	},

	clear: function() {
		this.items.forEach(function(i) {
			i.$el.remove();
		});
		this.items = [];
		this.lastIndex = 0;
	},

	getData: function() {
		return this.items.map(function(i) {
			return i.data;
		});
	},

	insert: function(html) {
		this.$el[0].innerHTML = html;
		this.items = [];
		this.defineItems();
	},

	append: function(html) {
		var $html = $(html);
		var $items;
		var itemSelector = this.options.parent.makeSelector(this.getItemDName());
		if ($html.is(itemSelector)) $items = $html;
		else $items = $html.find(itemSelector);
		return $items.appendTo(this.$el).map(this.define.bind(this));
	},
});
Collection.prototype.constructor = Collection;

module.exports = Collection;<|endoftext|>


<commit_before>import React from 'react';
import { render } from 'react-dom';

window.React = React;

let data = {
  id: 1,
  commentaire: 'Un commentaire'
};

class Comment extends React.Component {
  render() {
    let commentaire = this.props.commentaire;
    return (

        { commentaire }

    );
  }
}

class Contenu extends React.Component {
  render() {
    return (

Hello!!!

        <Comment data= {this.props.data} />

    );
  }
}

render(
  <Contenu data= { data } />,
  // React.createElement(Contenu, null),
  document.getElementById('content')
);
<commit_msg>feat(begin) : Began to play with react<commit_after>import React from 'react';
import { render } from 'react-dom';

window.React = React;

let data = {
  id: 1,
  commentaire: 'Un commentaire'
};

class NextButton extends React.Component {
  constructor() {
    super();

    this.handleclick = this.handleclick.bind(this);
  }
  handleclick() {
    alert("Go to the next photo");
  }
  render() {
    return (

        Right button

    );
  }
}

class Comment extends React.Component {
  render() {
    let commentaire = this.props.data.commentaire;
    return (

        { commentaire }

    );
  }
}

class Contenu extends React.Component {
  render() {
    return (

Hello!!!

        <Comment data= {this.props.data} />

    );
  }
}

render(

    <Contenu data= { data } />
    <NextButton />

,
  // React.createElement(Contenu, null),
  document.getElementById('content')
);
<|endoftext|>


<commit_before>L.Label = L.Popup.extend({

	includes: L.Mixin.Events,

	options: {
		autoPan: false,
		className: '',
		clickable: false,
		closePopupOnClick: false,
		noHide: false,
		offset: new L.Point(12, -15), // 6 (width of the label triangle) + 6 (padding)
		opacity: 1
	},

	onAdd: function (map) {
		this._map = map;

		this._pane = this._source instanceof L.Marker ? map._panes.markerPane : map._panes.popupPane;

		if (!this._container) {
			this._initLayout();
		}
		this._updateContent();

		var animFade = map.options.fadeAnimation;

		if (animFade) {
			L.DomUtil.setOpacity(this._container, 0);
		}
		this._pane.appendChild(this._container);

		map.on('viewreset', this._updatePosition, this);

		if (this._animated) {
			map.on('zoomanim', this._zoomAnimation, this);
		}

		if (L.Browser.touch && !this.options.noHide) {
			L.DomEvent.on(this._container, 'click', this.close, this);
		}

		this._initInteraction();

		this._update();

		this.setOpacity(this.options.opacity);
	},

	onRemove: function (map) {
		this._pane.removeChild(this._container);

		L.Util.falseFn(this._container.offsetWidth); // force reflow

		map.off({
			viewreset: this._updatePosition,
			zoomanim: this._zoomAnimation
		}, this);

		if (map.options.fadeAnimation) {
			L.DomUtil.setOpacity(this._container, 0);
		}

		this._removeInteraction();

		this._map = null;
	},

	close: function () {
		var map = this._map;

		if (map) {
			if (L.Browser.touch && !this.options.noHide) {
				L.DomEvent.off(this._container, 'click', this.close);
			}

			map.removeLayer(this);
		}
	},

	updateZIndex: function (zIndex) {
		this._zIndex = zIndex;

		if (this._container && this._zIndex) {
			this._container.style.zIndex = zIndex;
		}
	},

	setOpacity: function (opacity) {
		this.options.opacity = opacity;

		if (this._container) {
			L.DomUtil.setOpacity(this._container, opacity);
		}
	},

	_initLayout: function () {
		this._container = L.DomUtil.create('div', 'leaflet-label ' + this.options.className + ' leaflet-zoom-animated');
		this.updateZIndex(this._zIndex);
	},

	_updateContent: function () {
		if (!this._content) { return; }

		if (typeof this._content === 'string') {
			this._container.innerHTML = this._content;
		}
	},

	_updateLayout: function () {
		// Do nothing
	},

	_updatePosition: function () {
		var pos = this._map.latLngToLayerPoint(this._latlng);

		this._setPosition(pos);
	},

	_setPosition: function (pos) {
		pos = pos.add(this.options.offset);

		L.DomUtil.setPosition(this._container, pos);
	},

	_zoomAnimation: function (opt) {
		var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center);

		this._setPosition(pos);
	},

	_initInteraction: function () {
		if (!this.options.clickable) { return; }

		var container = this._container,
			events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu'];

		L.DomUtil.addClass(container, 'leaflet-clickable');
		L.DomEvent.on(container, 'click', this._onMouseClick, this);

		for (var i = 0; i < events.length; i++) {
			L.DomEvent.on(container, events[i], this._fireMouseEvent, this);
		}
	},

	_removeInteraction: function () {
		if (!this.options.clickable) { return; }

		var container = this._container,
			events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu'];

		L.DomUtil.removeClass(container, 'leaflet-clickable');
		L.DomEvent.off(container, 'click', this._onMouseClick, this);

		for (var i = 0; i < events.length; i++) {
			L.DomEvent.off(container, events[i], this._fireMouseEvent, this);
		}
	},

	_onMouseClick: function (e) {
		if (this.hasEventListeners(e.type)) {
			L.DomEvent.stopPropagation(e);
		}

		this.fire(e.type, {
			originalEvent: e
		});
	},

	_fireMouseEvent: function (e) {
		this.fire(e.type, {
			originalEvent: e
		});

		// TODO proper custom event propagation
		// this line will always be called if marker is in a FeatureGroup
		if (e.type === 'contextmenu' && this.hasEventListeners(e.type)) {
			L.DomEvent.preventDefault(e);
		}
		if (e.type !== 'mousedown') {
			L.DomEvent.stopPropagation(e);
		} else {
			L.DomEvent.preventDefault(e);
		}
	}
});<commit_msg>Don't force reflow when removing a label and don't do setOpacity things as they don't do anything as we are already removed. Greatly increases performance of hideLabel<commit_after>L.Label = L.Popup.extend({

	includes: L.Mixin.Events,

	options: {
		autoPan: false,
		className: '',
		clickable: false,
		closePopupOnClick: false,
		noHide: false,
		offset: new L.Point(12, -15), // 6 (width of the label triangle) + 6 (padding)
		opacity: 1
	},

	onAdd: function (map) {
		this._map = map;

		this._pane = this._source instanceof L.Marker ? map._panes.markerPane : map._panes.popupPane;

		if (!this._container) {
			this._initLayout();
		}
		this._updateContent();

		this._pane.appendChild(this._container);

		map.on('viewreset', this._updatePosition, this);

		if (this._animated) {
			map.on('zoomanim', this._zoomAnimation, this);
		}

		if (L.Browser.touch && !this.options.noHide) {
			L.DomEvent.on(this._container, 'click', this.close, this);
		}

		this._initInteraction();

		this._update();

		this.setOpacity(this.options.opacity);
	},

	onRemove: function (map) {
		this._pane.removeChild(this._container);

		map.off({
			viewreset: this._updatePosition,
			zoomanim: this._zoomAnimation
		}, this);

		this._removeInteraction();

		this._map = null;
	},

	close: function () {
		var map = this._map;

		if (map) {
			if (L.Browser.touch && !this.options.noHide) {
				L.DomEvent.off(this._container, 'click', this.close);
			}

			map.removeLayer(this);
		}
	},

	updateZIndex: function (zIndex) {
		this._zIndex = zIndex;

		if (this._container && this._zIndex) {
			this._container.style.zIndex = zIndex;
		}
	},

	setOpacity: function (opacity) {
		this.options.opacity = opacity;

		if (this._container) {
			L.DomUtil.setOpacity(this._container, opacity);
		}
	},

	_initLayout: function () {
		this._container = L.DomUtil.create('div', 'leaflet-label ' + this.options.className + ' leaflet-zoom-animated');
		this.updateZIndex(this._zIndex);
	},

	_updateContent: function () {
		if (!this._content) { return; }

		if (typeof this._content === 'string') {
			this._container.innerHTML = this._content;
		}
	},

	_updateLayout: function () {
		// Do nothing
	},

	_updatePosition: function () {
		var pos = this._map.latLngToLayerPoint(this._latlng);

		this._setPosition(pos);
	},

	_setPosition: function (pos) {
		pos = pos.add(this.options.offset);

		L.DomUtil.setPosition(this._container, pos);
	},

	_zoomAnimation: function (opt) {
		var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center);

		this._setPosition(pos);
	},

	_initInteraction: function () {
		if (!this.options.clickable) { return; }

		var container = this._container,
			events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu'];

		L.DomUtil.addClass(container, 'leaflet-clickable');
		L.DomEvent.on(container, 'click', this._onMouseClick, this);

		for (var i = 0; i < events.length; i++) {
			L.DomEvent.on(container, events[i], this._fireMouseEvent, this);
		}
	},

	_removeInteraction: function () {
		if (!this.options.clickable) { return; }

		var container = this._container,
			events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu'];

		L.DomUtil.removeClass(container, 'leaflet-clickable');
		L.DomEvent.off(container, 'click', this._onMouseClick, this);

		for (var i = 0; i < events.length; i++) {
			L.DomEvent.off(container, events[i], this._fireMouseEvent, this);
		}
	},

	_onMouseClick: function (e) {
		if (this.hasEventListeners(e.type)) {
			L.DomEvent.stopPropagation(e);
		}

		this.fire(e.type, {
			originalEvent: e
		});
	},

	_fireMouseEvent: function (e) {
		this.fire(e.type, {
			originalEvent: e
		});

		// TODO proper custom event propagation
		// this line will always be called if marker is in a FeatureGroup
		if (e.type === 'contextmenu' && this.hasEventListeners(e.type)) {
			L.DomEvent.preventDefault(e);
		}
		if (e.type !== 'mousedown') {
			L.DomEvent.stopPropagation(e);
		} else {
			L.DomEvent.preventDefault(e);
		}
	}
});<|endoftext|>


<commit_before>var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var morgan = require('morgan');
var MongoClient = require('mongodb').MongoClient;
var mongoUrl = 'mongodb://localhost:27017/mission';
var _db;
var ObjectID = require('mongodb').ObjectID;

app.use(morgan('dev'));
app.use(bodyParser.json());
app.use(express.static('dist'));

MongoClient.connect(mongoUrl, function(err, db) {
  if (err) {
    console.error(err);
    return;
  }

  console.log('connected to mongo');
  _db = db;
  app.listen(8888, function() {
    console.log('server is running...');
  });
});

// 设置response header，实现CORS(Cross-Origin resource sharing)
app.all("*", function(req, res, next) {
  res.header('Access-Control-Allow-Origin', 'http://localhost:8080');
  res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With");
  res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
  if (req.method == 'OPTIONS') {
    res.sendStatus(200);
  } else {
    next();
  }
});

// 使用post方法
app.post('/create', function(req, res, next) {
  // 接受前端发送的字段
  var mission = req.body;
  // 选择一个表，若无则自动创建
  var collection = _db.collection('my_mission');

  // 如果需要的字段不存在，则返回错误信息
  if (!mission.comment || !mission.totalTime || !mission.date) {
    res.send({ errcode: -1, errmsg: "params missed" });
    return;
  }

  collection.insert({ comment: mission.comment, totalTime: mission.totalTime, date: mission.date }, function(err, ret) {
    if (err) {
      console.error(err);
      res.status(500).end();
    } else {
      res.send({ errcode: 0, errmsg: 'ok' });
    }
  });
});

// 获取总时长
app.get('/time', function(req, res, next) {
  // 获取数据表
  var collection = _db.collection('my_mission');
  var time = 0;
  // 查询出所有计划
  collection.find({}).toArray(function(err, ret) {
    if (err) {
      console.error(err);
      return;
    }
    // 所有计划累加时长
    ret.forEach(function(item, index) {
      time += item.totalTime;
    });
    // 返回时长
    res.json({ errcode: 0, errmsg: 'ok', time: time });
  });
});

// 获取列表
app.get('/time-entries', function(req, res, next) {
  var collection = _db.collection('my_mission');
  collection.find({}).toArray(function(err, ret) {
    if (err) {
      console.error(err);
      return;
    }
    res.json(ret);
  });
});

// 删除计划
app.delete('/delete/:id', function(req, res, next) {
  var _id = req.params.id;
  var collection = _db.collection('my_mission');
  console.log(_id);
  //使用mongodb的唯一ObjectId字段查找出对应id删除记录
  collection.remove({ _id: new ObjectID(_id) }, function(err, ret) {
    if (err) {
      console.error(err);
      res.status(500).end();
    } else {
      res.send({ errcode: 0, errmsg: 'ok' });
    }
  });
});
<commit_msg>update<commit_after>var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var morgan = require('morgan');
var MongoClient = require('mongodb').MongoClient;
var mongoUrl = 'mongodb://localhost:27017/mission';
var _db;
var ObjectID = require('mongodb').ObjectID;

app.use(morgan('dev'));
app.use(bodyParser.json());
app.use(express.static('dist'));

MongoClient.connect(mongoUrl, function(err, db) {
  if (err) {
    console.error(err);
    return;
  }

  console.log('connected to mongo');
  _db = db;
  app.listen(8888, function() {
    console.log('server is running...');
  });
});

// 设置response header，实现CORS(Cross-Origin resource sharing)
app.all("*", function(req, res, next) {
  res.header('Access-Control-Allow-Origin', 'http://localhost:8080');
  res.header("Access-Control-Allow-Headers", "Content-Type,Content-Length, Authorization, Accept,X-Requested-With");
  res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
  if (req.method == 'OPTIONS') {
    res.sendStatus(200);
  } else {
    next();
  }
});

// 使用post方法
app.post('/create', function(req, res, next) {
  // 接受前端发送的字段
  var mission = req.body;
  // 选择一个表，若无则自动创建
  var collection = _db.collection('my_mission');

  // 如果需要的字段不存在，则返回错误信息
  if (!mission.comment || !mission.totalTime || !mission.date) {
    res.send({ errcode: -1, errmsg: "params missed" });
    return;
  }

  collection.insert({ comment: mission.comment, totalTime: mission.totalTime, date: mission.date }, function(err, ret) {
    if (err) {
      console.error(err);
      res.status(500).end();
    } else {
      res.send({ errcode: 0, errmsg: 'ok' });
    }
  });
});

// 获取总时长
app.get('/time', function(req, res, next) {
  // 获取数据表
  var collection = _db.collection('my_mission');
  var time = 0;
  // 查询出所有计划
  collection.find({}).toArray(function(err, ret) {
    if (err) {
      console.error(err);
      return;
    }
    // 所有计划累加时长
    ret.forEach(function(item, index) {
      time += item.totalTime;
    });
    // 返回时长
    res.json({ errcode: 0, errmsg: 'ok', time: time });
  });
});

// 获取列表
app.get('/time-entries', function(req, res, next) {
  var collection = _db.collection('my_mission');
  collection.find({}).toArray(function(err, ret) {
    if (err) {
      console.error(err);
      return
    }
    res.json(ret);
  });
});

// 删除计划
app.delete('/delete/:id', function(req, res, next) {
  var _id = req.params.id;
  var collection = _db.collection('my_mission');
  console.log(_id);
  //使用mongodb的唯一ObjectId字段查找出对应id删除记录
  collection.remove({ _id: new ObjectID(_id) }, function(err, ret) {
    if (err) {
      console.error(err);
      res.status(500).end();
    } else {
      res.send({ errcode: 0, errmsg: 'ok' });
    }
  });
});
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import Portal from './Portal';
import Fade from './Fade';
import {
  getOriginalBodyPadding,
  conditionallyUpdateScrollbar,
  setScrollbarWidth,
  mapToCssModules,
  omit,
  focusableElements,
  TransitionTimeouts
} from './utils';

function noop() { }

const FadePropTypes = PropTypes.shape(Fade.propTypes);

const propTypes = {
  isOpen: PropTypes.bool,
  autoFocus: PropTypes.bool,
  centered: PropTypes.bool,
  scrollable: PropTypes.bool,
  size: PropTypes.string,
  toggle: PropTypes.func,
  keyboard: PropTypes.bool,
  role: PropTypes.string,
  labelledBy: PropTypes.string,
  backdrop: PropTypes.oneOfType([
    PropTypes.bool,
    PropTypes.oneOf(['static'])
  ]),
  onEnter: PropTypes.func,
  onExit: PropTypes.func,
  onOpened: PropTypes.func,
  onClosed: PropTypes.func,
  children: PropTypes.node,
  className: PropTypes.string,
  wrapClassName: PropTypes.string,
  modalClassName: PropTypes.string,
  backdropClassName: PropTypes.string,
  contentClassName: PropTypes.string,
  external: PropTypes.node,
  fade: PropTypes.bool,
  cssModule: PropTypes.object,
  zIndex: PropTypes.oneOfType([
    PropTypes.number,
    PropTypes.string,
  ]),
  backdropTransition: FadePropTypes,
  modalTransition: FadePropTypes,
  innerRef: PropTypes.oneOfType([
    PropTypes.object,
    PropTypes.string,
    PropTypes.func,
  ]),
  unmountOnClose: PropTypes.bool,
  returnFocusAfterClose: PropTypes.bool
};

const propsToOmit = Object.keys(propTypes);

const defaultProps = {
  isOpen: false,
  autoFocus: true,
  centered: false,
  scrollable: false,
  role: 'dialog',
  backdrop: true,
  keyboard: true,
  zIndex: 1050,
  fade: true,
  onOpened: noop,
  onClosed: noop,
  modalTransition: {
    timeout: TransitionTimeouts.Modal,
  },
  backdropTransition: {
    mountOnEnter: true,
    timeout: TransitionTimeouts.Fade, // uses standard fade transition
  },
  unmountOnClose: true,
  returnFocusAfterClose: true
};

class Modal extends React.Component {
  constructor(props) {
    super(props);

    this._element = null;
    this._originalBodyPadding = null;
    this.getFocusableChildren = this.getFocusableChildren.bind(this);
    this.handleBackdropClick = this.handleBackdropClick.bind(this);
    this.handleBackdropMouseDown = this.handleBackdropMouseDown.bind(this);
    this.handleEscape = this.handleEscape.bind(this);
    this.handleTab = this.handleTab.bind(this);
    this.onOpened = this.onOpened.bind(this);
    this.onClosed = this.onClosed.bind(this);
    this.manageFocusAfterClose = this.manageFocusAfterClose.bind(this);

    this.state = {
      isOpen: false,
    };
  }

  componentDidMount() {
    if (this.props.isOpen) {
      this.init();
      this.setState({ isOpen: true })
    }

    if (this.props.onEnter) {
      this.props.onEnter();
    }

    if (this.state.isOpen && this.props.autoFocus) {
      this.setFocus();
    }

    this._isMounted = true;
  }

  componentDidUpdate(prevProps, prevState) {
    if (this.props.isOpen && !prevProps.isOpen) {
      this.init();
      this.setState({ isOpen: true});
      // let render() renders Modal Dialog first
      return;
    }

    // now Modal Dialog is rendered and we can refer this._element and this._dialog
    if (this.props.autoFocus && this.state.isOpen && !prevState.isOpen) {
      this.setFocus();
    }

    if (this._element && prevProps.zIndex !== this.props.zIndex) {
      this._element.style.zIndex = this.props.zIndex;
    }
  }

  componentWillUnmount() {
    if (this.props.onExit) {
      this.props.onExit();
    }

    if (this._element) {
      this.destroy();
      if (this.state.isOpen) {
        this.close();
      }
    }

    this._isMounted = false;
  }

  onOpened(node, isAppearing) {
    this.props.onOpened();
    (this.props.modalTransition.onEntered || noop)(node, isAppearing);
  }

  onClosed(node) {
    const { unmountOnClose } = this.props;
    // so all methods get called before it is unmounted
    this.props.onClosed();
    (this.props.modalTransition.onExited || noop)(node);

    if (unmountOnClose) {
      this.destroy();
    }
    this.close();

    if (this._isMounted) {
      this.setState({ isOpen: false });
    }
  }

  setFocus() {
    if (this._dialog && this._dialog.parentNode && typeof this._dialog.parentNode.focus === 'function') {
      this._dialog.parentNode.focus();
    }
  }

  getFocusableChildren() {
    return this._element.querySelectorAll(focusableElements.join(', '));
  }

  getFocusedChild() {
    let currentFocus;
    const focusableChildren = this.getFocusableChildren();

    try {
      currentFocus = document.activeElement;
    } catch (err) {
      currentFocus = focusableChildren[0];
    }
    return currentFocus;
  }

  // not mouseUp because scrollbar fires it, shouldn't close when user scrolls
  handleBackdropClick(e) {
    if (e.target === this._mouseDownElement) {
      e.stopPropagation();
      if (!this.props.isOpen || this.props.backdrop !== true) return;

      const backdrop = this._dialog ? this._dialog.parentNode : null;

      if (backdrop && e.target === backdrop && this.props.toggle) {
        this.props.toggle(e);
      }
    }
  }

  handleTab(e) {
    if (e.which !== 9) return;

    const focusableChildren = this.getFocusableChildren();
    const totalFocusable = focusableChildren.length;
    if (totalFocusable === 0) return;
    const currentFocus = this.getFocusedChild();

    let focusedIndex = 0;

    for (let i = 0; i < totalFocusable; i += 1) {
      if (focusableChildren[i] === currentFocus) {
        focusedIndex = i;
        break;
      }
    }

    if (e.shiftKey && focusedIndex === 0) {
      e.preventDefault();
      focusableChildren[totalFocusable - 1].focus();
    } else if (!e.shiftKey && focusedIndex === totalFocusable - 1) {
      e.preventDefault();
      focusableChildren[0].focus();
    }
  }

  handleBackdropMouseDown(e) {
    this._mouseDownElement = e.target;
  }

  handleEscape(e) {
    if (this.props.isOpen && this.props.keyboard && e.keyCode === 27 && this.props.toggle) {
      e.preventDefault();
      e.stopPropagation();
      this.props.toggle(e);
    }
  }

  init() {
    try {
      this._triggeringElement = document.activeElement;
    } catch (err) {
      this._triggeringElement = null;
    }

    if (!this._element) {
      this._element = document.createElement('div');
      this._element.setAttribute('tabindex', '-1');
      this._element.style.position = 'relative';
      this._element.style.zIndex = this.props.zIndex;
      document.body.appendChild(this._element);
    }

    this._originalBodyPadding = getOriginalBodyPadding();
    conditionallyUpdateScrollbar();

    if (Modal.openCount === 0) {
      document.body.className = classNames(
        document.body.className,
        mapToCssModules('modal-open', this.props.cssModule)
      );
    }

    Modal.openCount += 1;
  }

  destroy() {
    if (this._element) {
      document.body.removeChild(this._element);
      this._element = null;
    }

    this.manageFocusAfterClose();
  }

  manageFocusAfterClose() {
    if (this._triggeringElement) {
      const { returnFocusAfterClose } = this.props;
      if (this._triggeringElement.focus && returnFocusAfterClose) this._triggeringElement.focus();
      this._triggeringElement = null;
    }
  }

  close() {
    if (Modal.openCount <= 1) {
      const modalOpenClassName = mapToCssModules('modal-open', this.props.cssModule);
      // Use regex to prevent matching `modal-open` as part of a different class, e.g. `my-modal-opened`
      const modalOpenClassNameRegex = new RegExp(`(^| )${modalOpenClassName}( |$)`);
      document.body.className = document.body.className.replace(modalOpenClassNameRegex, ' ').trim();
    }
    this.manageFocusAfterClose();
    Modal.openCount = Math.max(0, Modal.openCount - 1);

    setScrollbarWidth(this._originalBodyPadding);
  }

  renderModalDialog() {
    const attributes = omit(this.props, propsToOmit);
    const dialogBaseClass = 'modal-dialog';

    return (
      <div
        {...attributes}
        className={mapToCssModules(classNames(dialogBaseClass, this.props.className, {
          [`modal-${this.props.size}`]: this.props.size,
          [`${dialogBaseClass}-centered`]: this.props.centered,
          [`${dialogBaseClass}-scrollable`]: this.props.scrollable,
        }), this.props.cssModule)}
        role="document"
        ref={(c) => {
          this._dialog = c;
        }}
      >

          {this.props.children}

    );
  }

  render() {
    const {
      unmountOnClose
    } = this.props;

    if (!!this._element && (this.state.isOpen || !unmountOnClose)) {
      const isModalHidden = !!this._element && !this.state.isOpen && !unmountOnClose;
      this._element.style.display = isModalHidden ? 'none' : 'block';

      const {
        wrapClassName,
        modalClassName,
        backdropClassName,
        cssModule,
        isOpen,
        backdrop,
        role,
        labelledBy,
        external,
        innerRef,
      } = this.props;

      const modalAttributes = {
        onClick: this.handleBackdropClick,
        onMouseDown: this.handleBackdropMouseDown,
        onKeyUp: this.handleEscape,
        onKeyDown: this.handleTab,
        style: { display: 'block' },
        'aria-labelledby': labelledBy,
        role,
        tabIndex: '-1'
      };

      const hasTransition = this.props.fade;
      const modalTransition = {
        ...Fade.defaultProps,
        ...this.props.modalTransition,
        baseClass: hasTransition ? this.props.modalTransition.baseClass : '',
        timeout: hasTransition ? this.props.modalTransition.timeout : 0,
      };
      const backdropTransition = {
        ...Fade.defaultProps,
        ...this.props.backdropTransition,
        baseClass: hasTransition ? this.props.backdropTransition.baseClass : '',
        timeout: hasTransition ? this.props.backdropTransition.timeout : 0,
      };

      const Backdrop = backdrop && (
        hasTransition ?
          (<Fade
            {...backdropTransition}
            in={isOpen && !!backdrop}
            cssModule={cssModule}
            className={mapToCssModules(classNames('modal-backdrop', backdropClassName), cssModule)}
          />)
          :

      );

      return (
        <Portal node={this._element}>

            <Fade
              {...modalAttributes}
              {...modalTransition}
              in={isOpen}
              onEntered={this.onOpened}
              onExited={this.onClosed}
              cssModule={cssModule}
              className={mapToCssModules(classNames('modal', modalClassName), cssModule)}
              innerRef={innerRef}
            >
              {external}
              {this.renderModalDialog()}
            </Fade>
            {Backdrop}

        </Portal>
      );
    }
    return null;
  }
}

Modal.propTypes = propTypes;
Modal.defaultProps = defaultProps;
Modal.openCount = 0;

export default Modal;
<commit_msg>fix(Modal): update condition to call `setFocus` in case open mo… (#1514)<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import Portal from './Portal';
import Fade from './Fade';
import {
  getOriginalBodyPadding,
  conditionallyUpdateScrollbar,
  setScrollbarWidth,
  mapToCssModules,
  omit,
  focusableElements,
  TransitionTimeouts
} from './utils';

function noop() { }

const FadePropTypes = PropTypes.shape(Fade.propTypes);

const propTypes = {
  isOpen: PropTypes.bool,
  autoFocus: PropTypes.bool,
  centered: PropTypes.bool,
  scrollable: PropTypes.bool,
  size: PropTypes.string,
  toggle: PropTypes.func,
  keyboard: PropTypes.bool,
  role: PropTypes.string,
  labelledBy: PropTypes.string,
  backdrop: PropTypes.oneOfType([
    PropTypes.bool,
    PropTypes.oneOf(['static'])
  ]),
  onEnter: PropTypes.func,
  onExit: PropTypes.func,
  onOpened: PropTypes.func,
  onClosed: PropTypes.func,
  children: PropTypes.node,
  className: PropTypes.string,
  wrapClassName: PropTypes.string,
  modalClassName: PropTypes.string,
  backdropClassName: PropTypes.string,
  contentClassName: PropTypes.string,
  external: PropTypes.node,
  fade: PropTypes.bool,
  cssModule: PropTypes.object,
  zIndex: PropTypes.oneOfType([
    PropTypes.number,
    PropTypes.string,
  ]),
  backdropTransition: FadePropTypes,
  modalTransition: FadePropTypes,
  innerRef: PropTypes.oneOfType([
    PropTypes.object,
    PropTypes.string,
    PropTypes.func,
  ]),
  unmountOnClose: PropTypes.bool,
  returnFocusAfterClose: PropTypes.bool
};

const propsToOmit = Object.keys(propTypes);

const defaultProps = {
  isOpen: false,
  autoFocus: true,
  centered: false,
  scrollable: false,
  role: 'dialog',
  backdrop: true,
  keyboard: true,
  zIndex: 1050,
  fade: true,
  onOpened: noop,
  onClosed: noop,
  modalTransition: {
    timeout: TransitionTimeouts.Modal,
  },
  backdropTransition: {
    mountOnEnter: true,
    timeout: TransitionTimeouts.Fade, // uses standard fade transition
  },
  unmountOnClose: true,
  returnFocusAfterClose: true
};

class Modal extends React.Component {
  constructor(props) {
    super(props);

    this._element = null;
    this._originalBodyPadding = null;
    this.getFocusableChildren = this.getFocusableChildren.bind(this);
    this.handleBackdropClick = this.handleBackdropClick.bind(this);
    this.handleBackdropMouseDown = this.handleBackdropMouseDown.bind(this);
    this.handleEscape = this.handleEscape.bind(this);
    this.handleTab = this.handleTab.bind(this);
    this.onOpened = this.onOpened.bind(this);
    this.onClosed = this.onClosed.bind(this);
    this.manageFocusAfterClose = this.manageFocusAfterClose.bind(this);

    this.state = {
      isOpen: false,
    };
  }

  componentDidMount() {
    const { isOpen, autoFocus, onEnter } = this.props;

    if (isOpen) {
      this.init();
      this.setState({ isOpen: true })
      if (autoFocus) {
        this.setFocus();
      }
    }

    if (onEnter) {
      onEnter();
    }

    this._isMounted = true;
  }

  componentDidUpdate(prevProps, prevState) {
    if (this.props.isOpen && !prevProps.isOpen) {
      this.init();
      this.setState({ isOpen: true});
      // let render() renders Modal Dialog first
      return;
    }

    // now Modal Dialog is rendered and we can refer this._element and this._dialog
    if (this.props.autoFocus && this.state.isOpen && !prevState.isOpen) {
      this.setFocus();
    }

    if (this._element && prevProps.zIndex !== this.props.zIndex) {
      this._element.style.zIndex = this.props.zIndex;
    }
  }

  componentWillUnmount() {
    if (this.props.onExit) {
      this.props.onExit();
    }

    if (this._element) {
      this.destroy();
      if (this.state.isOpen) {
        this.close();
      }
    }

    this._isMounted = false;
  }

  onOpened(node, isAppearing) {
    this.props.onOpened();
    (this.props.modalTransition.onEntered || noop)(node, isAppearing);
  }

  onClosed(node) {
    const { unmountOnClose } = this.props;
    // so all methods get called before it is unmounted
    this.props.onClosed();
    (this.props.modalTransition.onExited || noop)(node);

    if (unmountOnClose) {
      this.destroy();
    }
    this.close();

    if (this._isMounted) {
      this.setState({ isOpen: false });
    }
  }

  setFocus() {
    if (this._dialog && this._dialog.parentNode && typeof this._dialog.parentNode.focus === 'function') {
      this._dialog.parentNode.focus();
    }
  }

  getFocusableChildren() {
    return this._element.querySelectorAll(focusableElements.join(', '));
  }

  getFocusedChild() {
    let currentFocus;
    const focusableChildren = this.getFocusableChildren();

    try {
      currentFocus = document.activeElement;
    } catch (err) {
      currentFocus = focusableChildren[0];
    }
    return currentFocus;
  }

  // not mouseUp because scrollbar fires it, shouldn't close when user scrolls
  handleBackdropClick(e) {
    if (e.target === this._mouseDownElement) {
      e.stopPropagation();
      if (!this.props.isOpen || this.props.backdrop !== true) return;

      const backdrop = this._dialog ? this._dialog.parentNode : null;

      if (backdrop && e.target === backdrop && this.props.toggle) {
        this.props.toggle(e);
      }
    }
  }

  handleTab(e) {
    if (e.which !== 9) return;

    const focusableChildren = this.getFocusableChildren();
    const totalFocusable = focusableChildren.length;
    if (totalFocusable === 0) return;
    const currentFocus = this.getFocusedChild();

    let focusedIndex = 0;

    for (let i = 0; i < totalFocusable; i += 1) {
      if (focusableChildren[i] === currentFocus) {
        focusedIndex = i;
        break;
      }
    }

    if (e.shiftKey && focusedIndex === 0) {
      e.preventDefault();
      focusableChildren[totalFocusable - 1].focus();
    } else if (!e.shiftKey && focusedIndex === totalFocusable - 1) {
      e.preventDefault();
      focusableChildren[0].focus();
    }
  }

  handleBackdropMouseDown(e) {
    this._mouseDownElement = e.target;
  }

  handleEscape(e) {
    if (this.props.isOpen && this.props.keyboard && e.keyCode === 27 && this.props.toggle) {
      e.preventDefault();
      e.stopPropagation();
      this.props.toggle(e);
    }
  }

  init() {
    try {
      this._triggeringElement = document.activeElement;
    } catch (err) {
      this._triggeringElement = null;
    }

    if (!this._element) {
      this._element = document.createElement('div');
      this._element.setAttribute('tabindex', '-1');
      this._element.style.position = 'relative';
      this._element.style.zIndex = this.props.zIndex;
      document.body.appendChild(this._element);
    }

    this._originalBodyPadding = getOriginalBodyPadding();
    conditionallyUpdateScrollbar();

    if (Modal.openCount === 0) {
      document.body.className = classNames(
        document.body.className,
        mapToCssModules('modal-open', this.props.cssModule)
      );
    }

    Modal.openCount += 1;
  }

  destroy() {
    if (this._element) {
      document.body.removeChild(this._element);
      this._element = null;
    }

    this.manageFocusAfterClose();
  }

  manageFocusAfterClose() {
    if (this._triggeringElement) {
      const { returnFocusAfterClose } = this.props;
      if (this._triggeringElement.focus && returnFocusAfterClose) this._triggeringElement.focus();
      this._triggeringElement = null;
    }
  }

  close() {
    if (Modal.openCount <= 1) {
      const modalOpenClassName = mapToCssModules('modal-open', this.props.cssModule);
      // Use regex to prevent matching `modal-open` as part of a different class, e.g. `my-modal-opened`
      const modalOpenClassNameRegex = new RegExp(`(^| )${modalOpenClassName}( |$)`);
      document.body.className = document.body.className.replace(modalOpenClassNameRegex, ' ').trim();
    }
    this.manageFocusAfterClose();
    Modal.openCount = Math.max(0, Modal.openCount - 1);

    setScrollbarWidth(this._originalBodyPadding);
  }

  renderModalDialog() {
    const attributes = omit(this.props, propsToOmit);
    const dialogBaseClass = 'modal-dialog';

    return (
      <div
        {...attributes}
        className={mapToCssModules(classNames(dialogBaseClass, this.props.className, {
          [`modal-${this.props.size}`]: this.props.size,
          [`${dialogBaseClass}-centered`]: this.props.centered,
          [`${dialogBaseClass}-scrollable`]: this.props.scrollable,
        }), this.props.cssModule)}
        role="document"
        ref={(c) => {
          this._dialog = c;
        }}
      >

          {this.props.children}

    );
  }

  render() {
    const {
      unmountOnClose
    } = this.props;

    if (!!this._element && (this.state.isOpen || !unmountOnClose)) {
      const isModalHidden = !!this._element && !this.state.isOpen && !unmountOnClose;
      this._element.style.display = isModalHidden ? 'none' : 'block';

      const {
        wrapClassName,
        modalClassName,
        backdropClassName,
        cssModule,
        isOpen,
        backdrop,
        role,
        labelledBy,
        external,
        innerRef,
      } = this.props;

      const modalAttributes = {
        onClick: this.handleBackdropClick,
        onMouseDown: this.handleBackdropMouseDown,
        onKeyUp: this.handleEscape,
        onKeyDown: this.handleTab,
        style: { display: 'block' },
        'aria-labelledby': labelledBy,
        role,
        tabIndex: '-1'
      };

      const hasTransition = this.props.fade;
      const modalTransition = {
        ...Fade.defaultProps,
        ...this.props.modalTransition,
        baseClass: hasTransition ? this.props.modalTransition.baseClass : '',
        timeout: hasTransition ? this.props.modalTransition.timeout : 0,
      };
      const backdropTransition = {
        ...Fade.defaultProps,
        ...this.props.backdropTransition,
        baseClass: hasTransition ? this.props.backdropTransition.baseClass : '',
        timeout: hasTransition ? this.props.backdropTransition.timeout : 0,
      };

      const Backdrop = backdrop && (
        hasTransition ?
          (<Fade
            {...backdropTransition}
            in={isOpen && !!backdrop}
            cssModule={cssModule}
            className={mapToCssModules(classNames('modal-backdrop', backdropClassName), cssModule)}
          />)
          :

      );

      return (
        <Portal node={this._element}>

            <Fade
              {...modalAttributes}
              {...modalTransition}
              in={isOpen}
              onEntered={this.onOpened}
              onExited={this.onClosed}
              cssModule={cssModule}
              className={mapToCssModules(classNames('modal', modalClassName), cssModule)}
              innerRef={innerRef}
            >
              {external}
              {this.renderModalDialog()}
            </Fade>
            {Backdrop}

        </Portal>
      );
    }
    return null;
  }
}

Modal.propTypes = propTypes;
Modal.defaultProps = defaultProps;
Modal.openCount = 0;

export default Modal;
<|endoftext|>