Butter: Bugs 434, 458, and 493

Bug 434

Bug 434 is allowing users to drag multiple track events together. I talked about the problems I ran in into in this post. Since then, I got the multiple events dragging using an external plugin. For now, the code is here. It doesn’t completely replace the behaviour provided by jQuery’s draggable plugin, though. There is no scrolling, and it cannot yet drop to other tracks. On Lighthouse I have asked if we should be creating our own draggable and droppable code for Butter, since we do not want to add more external dependencies.

In Butter, shift-clicking a track event adds it to the group of selected track events. In the code, we need to determine, after a user clicks on a track event, whether the intended action is to drag, or to deselect all but the currently selected track event. To solve this, I kept track of the amount of time between the mousedown and mouseup event on the trackevent. If the mouseup occurs between 100 and 300 milliseconds after the mousedown event, which is an arbitrary length of time I chose after a little bit of testing, then I assume that the user intended to deselect all but the currently selected elements. Otherwise, I assume the user wanted to drag. The code for that looks like this:

    function onTrackEventMouseDown( e ){
      var start = +new Date();
      e.originalEvent.target.addEventListener( "mouseup", function(){
        var trackEvent = e.trackEvent,
            corn = trackEvent.popcornOptions,
            originalEvent = e.originalEvent,
            end = +new Date(),
            timeElapsed = end - start;

        if ( timeElapsed >= 100 && timeElapsed  1 ){
            trackEvent.selected = false;
          else {
            trackEvent.selected = true;
            if( !originalEvent.shiftKey ){
              for( var t in _tracks ){
                if( _tracks.hasOwnProperty( t ) ){
                  _tracks[ t ].deselectEvents( trackEvent );
                } //if
              } //for
              _selectedEvents = [ trackEvent ];
            else {
              _selectedEvents.push( trackEvent );
            } //if
          } //if

For the actual dragging, I used the following code, which uses a little bit of arithmetic to determine how far each selected track event is allowed to move:

    function dragInit( e, dd ) {
      var selectedEvents = $( "div[selected=true]" ),
          leftPositions = [],
          p = $.event.special.drag.callback.prototype;

      for ( var idx = 0; idx < selectedEvents.length; idx++ )  {
        leftPositions.push( $( selectedEvents[ idx ] ).position().left );

      p.lowestLeft = Math.min.apply( Math, leftPositions );

      return selectedEvents;

    function dragStart( e, dd ) {
      var container = $( _element.parentNode.parentNode );
      dd.limit = container.position();
      dd.limit.left = dd.limit.left + $( this ).position().left - dd.lowestLeft;
      dd.limit.bottom = dd.limit.top + container.outerHeight() - $( this ).outerHeight();
      dd.limit.right = dd.limit.left + container.outerWidth() - $( this ).outerWidth();

    function drag( e, dd ) {
      $( this ).css({
        top: Math.min( dd.limit.bottom, Math.max( dd.limit.top, dd.deltaY ) ),
        left: Math.min( dd.limit.right, Math.max( dd.limit.left, dd.offsetX ) )

    var handles;
    this.activate = function(){
      if( !handles ) {
        $( _element ).css({
          zIndex: 9001
        .drag( "init", dragInit )
        .drag( "start", dragStart )
        .drag( "end", movedCallback )
        .drag( drag, { relative: true } )
          autoHide: false, 
          containment: "parent", 
          handles: "e, w", 
          scroll: false,
          stop: movedCallback

        handles = _element.querySelectorAll( ".ui-resizable-handle" );
        function toggleHandles( state ){
          handles[ 0 ].style.visibility = state ? "visible" : "hidden";
          handles[ 1 ].style.visibility = state ? "visible" : "hidden";
        } //toggleHandles
        _element.addEventListener( "mouseover", function( e ){
          toggleHandles( true );
        }, false );
        _element.addEventListener( "mouseout", function( e ){
          toggleHandles( false );
        }, false );
        toggleHandles( false );

      } //if

    }; //activate

Bug 458

Bug 458 is putting the code that validates the times entered for track event updates in a better place. Before, this code was only in the editor, which means that if a track event was altered programatically, the validation would be bypassed. I moved the validation from src/editor/editor.js to src/core/trackevent.js; all track event updates have to hit the update method in src/core/trackevent.js, so it is a good place to put the validation (for now, until MVC is implemented).

As discussed with David Humphrey, I also made a few optimizations to the code. Namely, I changed calls such as Number( popcornData.start ) to +popcornData.start, and isNan( popcornData.start ) to popcornData.start != popcornData.start (there is, or at least, was, a bug in isNan).

I also changed the logic that decides whether or not to close the editor. Before, there was a boolean variable passed in that would make this decision. I removed this, and just made it so that the editor only closes when the trackeventupdate event is received (because that only fires if the track event was successfully updated). This also gives authors of future editors more control of when to keep the editor open, and when to close it.

The pull request is here.

Bug 493

I filed bug 493 when I was working on bug 458. If the track event was double clicked and the editor was already open, but behind the main browser’s window, then the editor did not come to the front. It stayed hidden in the background, which makes it seems like the double click didn’t work. I changed it so that the track event editor’s window comes to focus if it already open; the pull request for that is here.

This entry was posted in Open Source. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s