<commit_before>/**
 *  Copyright (c) 2010 Alethia Inc,
 *  http://www.alethia-inc.com
 *  Developed by Travis Tidwell | travist at alethia-inc.com
 *
 *  License:  GPL version 3.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.

 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */
(function($) {
   jQuery.media = jQuery.media ? jQuery.media : {};

   // Set up our defaults for this component.
   jQuery.media.defaults = jQuery.extend( jQuery.media.defaults, {
      logo:"logo.png",
      logoWidth:49,
      logoHeight:15,
      logopos:"sw",
      logox:5,
      logoy:5,
      link:"http://www.mediafront.org",
      file:"",
      image:"",
      timeout:2000,
      autoLoad:true
   });

   jQuery.media.ids = jQuery.extend( jQuery.media.ids, {
      busy:"#mediabusy",
      preview:"#mediapreview",
      play:"#mediaplay",
      media:"#mediadisplay",
      control:"#mediacontrol"
   });

   jQuery.fn.minplayer = function( settings ) {
      if( this.length === 0 ) {
         return null;
      }
      return new (function( player, settings ) {
         // Get the settings.
         settings = jQuery.media.utils.getSettings(settings);

         // Save the jQuery display.
         this.display = player;
         var _this = this;

         // If the player should auto load or not.
         this.autoLoad = settings.autoLoad;

         // Our attached controller.
         this.controller = null;

         // The active controller.
         this.activeController = null;

         // Store the busy cursor and data.
         this.busy = player.find( settings.ids.busy );
         this.busyImg = this.busy.find("img");
         this.busyWidth = this.busyImg.width();
         this.busyHeight = this.busyImg.height();

         // Store the play overlay.
         this.play = player.find( settings.ids.play );
         // Toggle the play/pause state if they click on the play button.
         this.play.bind("click", function() {
            _this.togglePlayPause();
         });
         this.playImg = this.play.find("img");
         this.playWidth = this.playImg.width();
         this.playHeight = this.playImg.height();

         // Store the preview image.
         this.preview = null;

         // The internal player controls.
         this.usePlayerControls = false;
         this.busyFlags = 0;
         this.playVisible = false;
         this.previewVisible = false;
         this.controllerVisible = true;
         this.hasMedia = false;
         this.playing = false;

         // Cache the width and height.
         this.width = this.display.width();
         this.height = this.display.height();

         // Hide or show an element.
         this.showElement = function( element, show, tween ) {
            if( element && !this.usePlayerControls ) {
               if( show ) {
                  element.show(tween);
               }
               else {
                  element.hide(tween);
               }
            }
         };

         this.showPlay = function( show, tween ) {
            this.playVisible = show;
            this.showElement( this.play, show, tween );
         };

         this.showBusy = function( id, show, tween ) {
            if( show ) {
               this.busyFlags |= (1 << id);
            }
            else {
               this.busyFlags &= ~(1 << id);
            }
            this.showElement( this.busy, (this.busyFlags > 0), tween );
         };

         this.showPreview = function( show, tween ) {
            this.previewVisible = show;
            if( this.preview ) {
               this.showElement( this.preview.display, show, tween );
            }
         };

         this.showController = function( show, tween ) {
            this.controllerVisible = show;
            if( this.controller ) {
               this.showElement( this.controller.display, show, tween );
            }
         };

         // Handle the control events.
         this.onControlUpdate = function( data ) {
            if( this.media ) {
               // If the player is ready.
               if( this.media.playerReady ) {
                  switch( data.type ) {
                     case "play":
                        this.media.player.playMedia();
                        break;
                     case "pause":
                        this.media.player.pauseMedia();
                        break;
                     case "seek":
                        this.media.player.seekMedia( data.value );
                        break;
                     case "volume":
                        this.media.player.setVolume( data.value );
                        break;
                     case "mute":
                        this.media.mute( data.value );
                        break;
                  }
               }
               // If there are files in the queue but no current media file.
               else if( (this.media.playQueue.length > 0) && !this.media.mediaFile ) {
                  // They interacted with the player.  Always autoload at this point on.
                  this.autoLoad = true;

                  // Then play the next file in the queue.
                  this.playNext();
               }

               // Let the template do something...
               if( settings.template && settings.template.onControlUpdate ) {
                  settings.template.onControlUpdate( data );
               }
            }
         };

         // Handle the full screen event requests.
         this.fullScreen = function( full ) {
            if( settings.template.onFullScreen ) {
               settings.template.onFullScreen( full );
            }
         };

         // Handle when the preview image loads.
         this.onPreviewLoaded = function() {
            this.previewVisible = true;
         // If we don't have any media, then we will assume that they
         // just want an image viewer.  Trigger a complete event after the timeout
         // interval.
         /* Doesn't quite work... need to investigate further.
            if( !this.hasMedia ) {
               setTimeout( function() {
                  _this.display.trigger("mediaupdate", {type:"complete"});
               }, settings.timeout );
            }
            */
         };

         // Handle the media events.
         this.onMediaUpdate = function( data ) {
            switch( data.type ) {
               case "paused":
                  this.playing = false;
                  this.showPlay(true);
                  this.showBusy(1, false);
                  break;
               case "playing":
                  this.playing = true;
                  this.showPlay(false);
                  this.showBusy(1, false);
                  this.showPreview((this.media.mediaFile.type == "audio"));
                  break;
               case "initialize":
                  this.playing = false;
                  this.showPlay(true);
                  this.showBusy(1, this.autoLoad);
                  this.showPreview(true);
                  break;
               case "buffering":
                  this.showPlay(true);
                  this.showBusy(1, true);
                  this.showPreview((this.media.mediaFile.type == "audio"));
                  break;
            }

            // Update our controller.
            if( this.controller ) {
               this.controller.onMediaUpdate( data );
            }

            // Update our active controller.
            if( this.activeController ) {
               this.activeController.onMediaUpdate( data );
            }

            // Let the template do something...
            if( settings.template && settings.template.onMediaUpdate ) {
               settings.template.onMediaUpdate( data );
            }
         };

         // Allow mulitple controllers to control this media.
         this.addController = function( newController, active ) {
            if( newController ) {
               newController.display.bind( "controlupdate", newController, function( event, data ) {
                  _this.activeController = event.data;
                  _this.onControlUpdate( data );
               });

               if( active && !this.activeController ) {
                  this.activeController = newController;
               }
            }
            return newController;
         };

         // Set the media player.
         this.media = this.display.find( settings.ids.media ).mediadisplay( settings );
         if( this.media ) {
            this.media.display.bind( "mediaupdate", function( event, data ) {
               _this.onMediaUpdate( data );
            });
         }

         // Add the control bar to the media.
         this.controller = this.addController( this.display.find( settings.ids.control ).mediacontrol( settings ), false );

         // Now add any queued controllers...
         if( jQuery.media.controllers[settings.id] ) {
            var controllers = jQuery.media.controllers[settings.id];
            var i = controllers.length;
            while(i--) {
               this.addController( controllers[i], true );
            }
         }

         // Set the size of this media player.
         this.setSize = function( newWidth, newHeight ) {
            this.width = newWidth ? newWidth : this.width;
            this.height = newHeight ? newHeight : this.height;

            if( this.width && this.height ) {
               // Set the position of the logo.
               this.setLogoPos();

               // Resize the preview image.
               if( this.preview ) {
                  this.preview.resize( this.width, this.height );
               }

               // Resize the busy symbol.
               this.busy.css({
                  width:this.width,
                  height:this.height
                  });
               this.busyImg.css({
                  marginLeft:((this.width - this.busyWidth)/2) + "px",
                  marginTop:((this.height - this.busyHeight)/2) + "px"
               });

               // Resize the play symbol.
               var playMLeft = Math.ceil((this.width - this.playWidth)/2);
               var playMTop = Math.ceil((this.height - this.playHeight)/2);
               this.play.css({
                  width:this.width,
                  height:this.height
                  });
               this.playImg.css({
                  marginLeft:playMLeft + "px",
                  marginTop:playMTop + "px"
               });

               // Resize the media.
               if( this.media ) {
                  this.media.display.css({
                     width:this.width,
                     height:this.height
                     });
                  this.media.setSize( this.width, this.height );
               }
            }
         };

         // Function to show the built in controls or not.
         this.showPlayerController = function( show ) {
            if( this.media && this.media.hasControls() ) {
               this.usePlayerControls = show;
               if( show ) {
                  this.busy.hide();
                  this.play.hide();
                  if( this.preview ) {
                     this.preview.display.hide();
                  }
                  if( this.controller ) {
                     this.controller.display.hide();
                  }
               }
               else {
                  this.showBusy( 1, ((this.busyFlags & 0x2) == 0x2) );
                  this.showPlay( this.playVisible );
                  this.showPreview( this.previewVisible );
                  this.showController( this.controllerVisible );
               }
               this.media.showControls( show );
            }
         };

         // Add the logo.
         if( this.media ) {
            this.display.prepend('

');
            this.logo = this.display.find(".medialogo").mediaimage( settings.link );
            this.logo.display.css({
               position:"absolute",
               zIndex:490
            });
            this.logo.width = settings.logoWidth;
            this.logo.height = settings.logoHeight;
            this.logo.loadImage( settings.logo );
         }

         // Sets the logo position.
         this.setLogoPos = function() {
            if( this.logo ) {
               var mediaTop = parseInt(this.media.display.css("marginTop"), 0);
               var mediaLeft = parseInt(this.media.display.css("marginLeft"), 0);
               var marginTop = (settings.logopos=="se" || settings.logopos=="sw") ? (mediaTop + this.height - this.logo.height - settings.logoy) : mediaTop + settings.logoy;
               var marginLeft = (settings.logopos=="ne" || settings.logopos=="se") ? (mediaLeft + this.width - this.logo.width - settings.logox) : mediaLeft + settings.logox;
               this.logo.display.css({
                  marginTop:marginTop,
                  marginLeft:marginLeft
               });
            }
         };

         this.onResize = function( deltaX, deltaY ) {
            // Resize the attached control region.
            if( this.controller ) {
               this.controller.onResize( deltaX, deltaY );
            }

            // Resize the media region.
            this.setSize( this.width + deltaX, this.height + deltaY );
         };

         // Reset to previous state...
         this.reset = function() {
            this.hasMedia = false;
            this.playing = false;
            if( this.controller ) {
               this.controller.reset();
            }
            if( this.activeController ) {
               this.activeController.reset();
            }

            this.showBusy(1, this.autoLoad);

            if( this.media ) {
               this.media.reset();
            }
         };

         // Toggle the play/pause state.
         this.togglePlayPause = function() {
            if( this.media ) {
               if( this.media.playerReady ) {
                  if( this.playing ) {
                     this.showPlay(true);
                     this.media.player.pauseMedia();
                  }
                  else {
                     this.showPlay(false);
                     this.media.player.playMedia();
                  }
               }
               else if( (this.media.playQueue.length > 0) && !this.media.mediaFile ) {
                  // They interacted with the player.  Always autoload at this point on.
                  this.autoLoad = true;

                  // Then play the next file in the queue.
                  this.playNext();
               }
            }
         };

         // Loads an image...
         this.loadImage = function( image ) {
            this.preview = player.find( settings.ids.preview ).mediaimage();

            if( this.preview ) {
               // Set the size of the preview.
               this.preview.resize( this.width, this.height );

               // Bind to the image loaded event.
               this.preview.display.bind("imageLoaded", function() {
                  _this.onPreviewLoaded();
               });

               // Load the image.
               this.preview.loadImage( image );

               // Now set the preview image in the media player.
               if( this.media ) {
                  this.media.preview = image;
               }
            }
         };

         // Clears the loaded image.
         this.clearImage = function() {
            if( this.preview ) {
               this.preview.clear();
            }
         };

         // Expose the public load functions from the media display.
         this.loadFiles = function( files ) {
            this.reset();
            if( this.media && this.media.loadFiles( files ) && this.autoLoad ) {
               this.media.playNext();
            }
         };

         // Play the next file.
         this.playNext = function() {
            if( this.media ) {
               this.media.playNext();
            }
         };

         // Loads a single media file.
         this.loadMedia = function( file ) {
            this.reset();
            if( this.media ) {
               this.media.loadMedia( file );
            }
         };

         // If they provide a file, then load it.
         if( settings.file ) {
            this.loadMedia( settings.file );
         }

         // If they provide the image, then load it.
         if( settings.image ) {
            this.loadImage( settings.image );
         }
      })( this, settings );
   };
})(jQuery);<commit_msg>Added a ceil to the busy positioning.<commit_after>/**
 *  Copyright (c) 2010 Alethia Inc,
 *  http://www.alethia-inc.com
 *  Developed by Travis Tidwell | travist at alethia-inc.com
 *
 *  License:  GPL version 3.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.

 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */
(function($) {
   jQuery.media = jQuery.media ? jQuery.media : {};

   // Set up our defaults for this component.
   jQuery.media.defaults = jQuery.extend( jQuery.media.defaults, {
      logo:"logo.png",
      logoWidth:49,
      logoHeight:15,
      logopos:"sw",
      logox:5,
      logoy:5,
      link:"http://www.mediafront.org",
      file:"",
      image:"",
      timeout:2000,
      autoLoad:true
   });

   jQuery.media.ids = jQuery.extend( jQuery.media.ids, {
      busy:"#mediabusy",
      preview:"#mediapreview",
      play:"#mediaplay",
      media:"#mediadisplay",
      control:"#mediacontrol"
   });

   jQuery.fn.minplayer = function( settings ) {
      if( this.length === 0 ) {
         return null;
      }
      return new (function( player, settings ) {
         // Get the settings.
         settings = jQuery.media.utils.getSettings(settings);

         // Save the jQuery display.
         this.display = player;
         var _this = this;

         // If the player should auto load or not.
         this.autoLoad = settings.autoLoad;

         // Our attached controller.
         this.controller = null;

         // The active controller.
         this.activeController = null;

         // Store the busy cursor and data.
         this.busy = player.find( settings.ids.busy );
         this.busyImg = this.busy.find("img");
         this.busyWidth = this.busyImg.width();
         this.busyHeight = this.busyImg.height();

         // Store the play overlay.
         this.play = player.find( settings.ids.play );
         // Toggle the play/pause state if they click on the play button.
         this.play.bind("click", function() {
            _this.togglePlayPause();
         });
         this.playImg = this.play.find("img");
         this.playWidth = this.playImg.width();
         this.playHeight = this.playImg.height();

         // Store the preview image.
         this.preview = null;

         // The internal player controls.
         this.usePlayerControls = false;
         this.busyFlags = 0;
         this.playVisible = false;
         this.previewVisible = false;
         this.controllerVisible = true;
         this.hasMedia = false;
         this.playing = false;

         // Cache the width and height.
         this.width = this.display.width();
         this.height = this.display.height();

         // Hide or show an element.
         this.showElement = function( element, show, tween ) {
            if( element && !this.usePlayerControls ) {
               if( show ) {
                  element.show(tween);
               }
               else {
                  element.hide(tween);
               }
            }
         };

         this.showPlay = function( show, tween ) {
            this.playVisible = show;
            this.showElement( this.play, show, tween );
         };

         this.showBusy = function( id, show, tween ) {
            if( show ) {
               this.busyFlags |= (1 << id);
            }
            else {
               this.busyFlags &= ~(1 << id);
            }
            this.showElement( this.busy, (this.busyFlags > 0), tween );
         };

         this.showPreview = function( show, tween ) {
            this.previewVisible = show;
            if( this.preview ) {
               this.showElement( this.preview.display, show, tween );
            }
         };

         this.showController = function( show, tween ) {
            this.controllerVisible = show;
            if( this.controller ) {
               this.showElement( this.controller.display, show, tween );
            }
         };

         // Handle the control events.
         this.onControlUpdate = function( data ) {
            if( this.media ) {
               // If the player is ready.
               if( this.media.playerReady ) {
                  switch( data.type ) {
                     case "play":
                        this.media.player.playMedia();
                        break;
                     case "pause":
                        this.media.player.pauseMedia();
                        break;
                     case "seek":
                        this.media.player.seekMedia( data.value );
                        break;
                     case "volume":
                        this.media.player.setVolume( data.value );
                        break;
                     case "mute":
                        this.media.mute( data.value );
                        break;
                  }
               }
               // If there are files in the queue but no current media file.
               else if( (this.media.playQueue.length > 0) && !this.media.mediaFile ) {
                  // They interacted with the player.  Always autoload at this point on.
                  this.autoLoad = true;

                  // Then play the next file in the queue.
                  this.playNext();
               }

               // Let the template do something...
               if( settings.template && settings.template.onControlUpdate ) {
                  settings.template.onControlUpdate( data );
               }
            }
         };

         // Handle the full screen event requests.
         this.fullScreen = function( full ) {
            if( settings.template.onFullScreen ) {
               settings.template.onFullScreen( full );
            }
         };

         // Handle when the preview image loads.
         this.onPreviewLoaded = function() {
            this.previewVisible = true;
         // If we don't have any media, then we will assume that they
         // just want an image viewer.  Trigger a complete event after the timeout
         // interval.
         /* Doesn't quite work... need to investigate further.
            if( !this.hasMedia ) {
               setTimeout( function() {
                  _this.display.trigger("mediaupdate", {type:"complete"});
               }, settings.timeout );
            }
            */
         };

         // Handle the media events.
         this.onMediaUpdate = function( data ) {
            switch( data.type ) {
               case "paused":
                  this.playing = false;
                  this.showPlay(true);
                  this.showBusy(1, false);
                  break;
               case "playing":
                  this.playing = true;
                  this.showPlay(false);
                  this.showBusy(1, false);
                  this.showPreview((this.media.mediaFile.type == "audio"));
                  break;
               case "initialize":
                  this.playing = false;
                  this.showPlay(true);
                  this.showBusy(1, this.autoLoad);
                  this.showPreview(true);
                  break;
               case "buffering":
                  this.showPlay(true);
                  this.showBusy(1, true);
                  this.showPreview((this.media.mediaFile.type == "audio"));
                  break;
            }

            // Update our controller.
            if( this.controller ) {
               this.controller.onMediaUpdate( data );
            }

            // Update our active controller.
            if( this.activeController ) {
               this.activeController.onMediaUpdate( data );
            }

            // Let the template do something...
            if( settings.template && settings.template.onMediaUpdate ) {
               settings.template.onMediaUpdate( data );
            }
         };

         // Allow mulitple controllers to control this media.
         this.addController = function( newController, active ) {
            if( newController ) {
               newController.display.bind( "controlupdate", newController, function( event, data ) {
                  _this.activeController = event.data;
                  _this.onControlUpdate( data );
               });

               if( active && !this.activeController ) {
                  this.activeController = newController;
               }
            }
            return newController;
         };

         // Set the media player.
         this.media = this.display.find( settings.ids.media ).mediadisplay( settings );
         if( this.media ) {
            this.media.display.bind( "mediaupdate", function( event, data ) {
               _this.onMediaUpdate( data );
            });
         }

         // Add the control bar to the media.
         this.controller = this.addController( this.display.find( settings.ids.control ).mediacontrol( settings ), false );

         // Now add any queued controllers...
         if( jQuery.media.controllers[settings.id] ) {
            var controllers = jQuery.media.controllers[settings.id];
            var i = controllers.length;
            while(i--) {
               this.addController( controllers[i], true );
            }
         }

         // Set the size of this media player.
         this.setSize = function( newWidth, newHeight ) {
            this.width = newWidth ? newWidth : this.width;
            this.height = newHeight ? newHeight : this.height;

            if( this.width && this.height ) {
               // Set the position of the logo.
               this.setLogoPos();

               // Resize the preview image.
               if( this.preview ) {
                  this.preview.resize( this.width, this.height );
               }

               // Resize the busy symbol.
               var busyMLeft = Math.ceil((this.width - this.busyWidth)/2);
               var busyMTop = Math.ceil((this.height - this.busyHeight)/2);
               this.busy.css({
                  width:this.width,
                  height:this.height
                  });
               this.busyImg.css({
                  marginLeft:((this.width - this.busyWidth)/2) + "px",
                  marginTop:((this.height - this.busyHeight)/2) + "px"
               });

               // Resize the play symbol.
               var playMLeft = Math.ceil((this.width - this.playWidth)/2);
               var playMTop = Math.ceil((this.height - this.playHeight)/2);
               this.play.css({
                  width:this.width,
                  height:this.height
                  });
               this.playImg.css({
                  marginLeft:playMLeft + "px",
                  marginTop:playMTop + "px"
               });

               // Resize the media.
               if( this.media ) {
                  this.media.display.css({
                     width:this.width,
                     height:this.height
                     });
                  this.media.setSize( this.width, this.height );
               }
            }
         };

         // Function to show the built in controls or not.
         this.showPlayerController = function( show ) {
            if( this.media && this.media.hasControls() ) {
               this.usePlayerControls = show;
               if( show ) {
                  this.busy.hide();
                  this.play.hide();
                  if( this.preview ) {
                     this.preview.display.hide();
                  }
                  if( this.controller ) {
                     this.controller.display.hide();
                  }
               }
               else {
                  this.showBusy( 1, ((this.busyFlags & 0x2) == 0x2) );
                  this.showPlay( this.playVisible );
                  this.showPreview( this.previewVisible );
                  this.showController( this.controllerVisible );
               }
               this.media.showControls( show );
            }
         };

         // Add the logo.
         if( this.media ) {
            this.display.prepend('

');
            this.logo = this.display.find(".medialogo").mediaimage( settings.link );
            this.logo.display.css({
               position:"absolute",
               zIndex:490
            });
            this.logo.width = settings.logoWidth;
            this.logo.height = settings.logoHeight;
            this.logo.loadImage( settings.logo );
         }

         // Sets the logo position.
         this.setLogoPos = function() {
            if( this.logo ) {
               var mediaTop = parseInt(this.media.display.css("marginTop"), 0);
               var mediaLeft = parseInt(this.media.display.css("marginLeft"), 0);
               var marginTop = (settings.logopos=="se" || settings.logopos=="sw") ? (mediaTop + this.height - this.logo.height - settings.logoy) : mediaTop + settings.logoy;
               var marginLeft = (settings.logopos=="ne" || settings.logopos=="se") ? (mediaLeft + this.width - this.logo.width - settings.logox) : mediaLeft + settings.logox;
               this.logo.display.css({
                  marginTop:marginTop,
                  marginLeft:marginLeft
               });
            }
         };

         this.onResize = function( deltaX, deltaY ) {
            // Resize the attached control region.
            if( this.controller ) {
               this.controller.onResize( deltaX, deltaY );
            }

            // Resize the media region.
            this.setSize( this.width + deltaX, this.height + deltaY );
         };

         // Reset to previous state...
         this.reset = function() {
            this.hasMedia = false;
            this.playing = false;
            if( this.controller ) {
               this.controller.reset();
            }
            if( this.activeController ) {
               this.activeController.reset();
            }

            this.showBusy(1, this.autoLoad);

            if( this.media ) {
               this.media.reset();
            }
         };

         // Toggle the play/pause state.
         this.togglePlayPause = function() {
            if( this.media ) {
               if( this.media.playerReady ) {
                  if( this.playing ) {
                     this.showPlay(true);
                     this.media.player.pauseMedia();
                  }
                  else {
                     this.showPlay(false);
                     this.media.player.playMedia();
                  }
               }
               else if( (this.media.playQueue.length > 0) && !this.media.mediaFile ) {
                  // They interacted with the player.  Always autoload at this point on.
                  this.autoLoad = true;

                  // Then play the next file in the queue.
                  this.playNext();
               }
            }
         };

         // Loads an image...
         this.loadImage = function( image ) {
            this.preview = player.find( settings.ids.preview ).mediaimage();

            if( this.preview ) {
               // Set the size of the preview.
               this.preview.resize( this.width, this.height );

               // Bind to the image loaded event.
               this.preview.display.bind("imageLoaded", function() {
                  _this.onPreviewLoaded();
               });

               // Load the image.
               this.preview.loadImage( image );

               // Now set the preview image in the media player.
               if( this.media ) {
                  this.media.preview = image;
               }
            }
         };

         // Clears the loaded image.
         this.clearImage = function() {
            if( this.preview ) {
               this.preview.clear();
            }
         };

         // Expose the public load functions from the media display.
         this.loadFiles = function( files ) {
            this.reset();
            if( this.media && this.media.loadFiles( files ) && this.autoLoad ) {
               this.media.playNext();
            }
         };

         // Play the next file.
         this.playNext = function() {
            if( this.media ) {
               this.media.playNext();
            }
         };

         // Loads a single media file.
         this.loadMedia = function( file ) {
            this.reset();
            if( this.media ) {
               this.media.loadMedia( file );
            }
         };

         // If they provide a file, then load it.
         if( settings.file ) {
            this.loadMedia( settings.file );
         }

         // If they provide the image, then load it.
         if( settings.image ) {
            this.loadImage( settings.image );
         }
      })( this, settings );
   };
})(jQuery);<|endoftext|>


<commit_before>'use strict';
var BaseController = require('./base_controller');
var util = require('util');
var connectorModule = require('../modules/connector_module');
var BBPromise = require('bluebird');
var errors = require('@hoist/errors');
var Model = require('@hoist/model');
var _ = require('lodash');
var logger = require('@hoist/logger');
var url = require('url');

function Bounce(request, reply, bounceToken) {
  _.bindAll(this);
  this._request = request;
  this._reply = reply;
  this._bounceToken = bounceToken;
  this.query = request.query;
}
Bounce.prototype.set = function (key, value, callback) {
  this._bounceToken.state[key] = value;
  this._bounceToken.markModified('state');
  return this._bounceToken.saveAsync().bind(this).then(function () {
    return this;
  }).nodeify(callback);
};
Bounce.prototype.setDisplayProperty = function(name, value, callback) {
  var existingProperty = _.where(this._bounceToken.displayProperties, {name: name});
  if(existingProperty.length !== 0) {
    existingProperty[0].value = value;
  } else {
    this._bounceToken.displayProperties.push({
      name: name,
      value: value
    });
  }
  return this._bounceToken.saveAsync().bind(this).then(function () {
    return this;
  }).nodeify(callback);
};
Bounce.prototype.get = function (key) {
  return this._bounceToken.state[key];
};
Bounce.prototype.done = function (err) {
  /* istanbul ignore next */
  BBPromise.try(function () {
    if (this._bounceToken.saveTo && this._bounceToken.saveId) {
      /*istanbul ignore next */
      if (this._bounceToken.saveTo.toLowerCase() === 'bucket') {
        return Model.Bucket.findOneAsync({
          _id: this._bounceToken.saveId,
          application: this._bounceToken.application,
          environment: this._bounceToken.environment
        }).bind(this).then(function (bucket) {
          if (!bucket.meta.authToken) {
            bucket.meta.authToken = {};
          }
          bucket.meta.authToken[this._bounceToken.connectorKey] = this._bounceToken.key;
          bucket.markModified('meta');
          return bucket.saveAsync();
        });
      }
      /*istanbul ignore next */
      else if (this._bounceToken.saveTo.toLowerCase() === 'user') {
        return Model.AppUser.findOneAsync({
          _id: this._bounceToken.saveId,
          application: this._bounceToken.application,
          environment: this._bounceToken.environment
        }).bind(this).then(function (appUser) {
          appUser.meta.authToken = this._bounceToken.key;
          appUser.markModified('meta');
          return appUser.saveAsync();
        });
      }
    }
  }, [], this).bind(this).then(function () {
    if (this._bounceToken.returnUrl) {
      new Model.ExecutionLogEvent({
        application: this._bounceToken.application,
        environment: 'live',
        message: 'redirecting back to application for token:' + this._bounceToken.key,
        type: 'LOG'
      }).saveAsync().catch(function (err) {
        logger.error(err);
        logger.alert(err);
      });
      if (this._bounceToken.saveTo && this._bounceToken.saveId) {
        this._reply.redirect(this._bounceToken.returnUrl)
          .state('bouncer-token', this._bounceToken.toObject());
      } else {
        var _url = url.parse(this._bounceToken.returnUrl, true);
        _url.query.token = this._bounceToken.key;
        delete _url.search;
        this._reply.redirect(url.format(_url))
          .state('bouncer-token', this._bounceToken.toObject());
      }
    } else {
      var error;
      if (err) {
        if (err.stack) {
          error = err.stack;
        } else if (err.message) {
          error = err.message;
        } else {
          error = err;
        }
      }
      var options = {
        connectorType: this._bounceToken.connectorType,
        error: error
      };
      /* Only attach the token to the page if they didn't save
         to a bucket or a user */
      if (!this._bounceToken.saveTo) {
        options.token = this._bounceToken.key;
      }
      this._reply.view('done', options).state('bouncer-token', this._bounceToken.toObject());
    }
  }).catch(function (err) {
    logger.error(err);
    logger.alert(err);
    this._reply.view('done', {
      error: err.stack
    });
  });
};
Bounce.prototype.delete = function (key, callback) {
  delete this._bounceToken.state[key];
  this._bounceToken.markModified('state');
  return this._bounceToken.saveAsync().bind(this).then(function () {
    return this;
  }).nodeify(callback);
};
Bounce.prototype.redirect = function (url) {
  this._reply.redirect(url).state('bouncer-token', this._bounceToken.toObject());
};

function StateController() {
  BaseController.apply(this, arguments);
}

util.inherits(StateController, BaseController);

StateController.prototype.bounce = function (request, reply) {

  BBPromise.try(function () {
    logger.info({
      params: request.params.key,
      state: request.state
    });
    var key = request.params.key || request.state['bouncer-token'].key;
    if (!key) {
      throw new errors.Http404Error('invalid bounce key');
    }
    return Model.BouncerToken.findOneAsync({
      key: key
    });
  }).then(function (token) {
    if (!token) {
      throw new errors.Http404Error('invalid bounce key');
    }
    new Model.ExecutionLogEvent({
      application: token._id,
      environment: 'live',
      message: 'Bounce recieved for ' + token.key,
      type: 'LOG'
    }).saveAsync().catch(function (err) {
      logger.error(err);
    });
    return Model.ConnectorSetting.findOneAsync({
      application: token.application,
      environment: token.environment,
      key: token.connectorKey
    }).then(function (connectorSettings) {
      if (!connectorSettings) {
        throw new errors.Http404Error('invalid bounce key');
      }
      var bounce = new Bounce(request, reply, token);
      return connectorModule.loadConnector(connectorSettings)
        .then(function (connector) {
          return connector.receiveBounce(bounce);
        });
    });
  }).catch(this.errorProcessor(reply));
};

StateController.prototype.initRoutes = function (server) {
  server.route({
    method: 'GET',
    path: '/bounce/{key?}',
    handler: this.bounce
  });
};

module.exports = new StateController();
<commit_msg>matches based on lowecase prop name<commit_after>'use strict';
var BaseController = require('./base_controller');
var util = require('util');
var connectorModule = require('../modules/connector_module');
var BBPromise = require('bluebird');
var errors = require('@hoist/errors');
var Model = require('@hoist/model');
var _ = require('lodash');
var logger = require('@hoist/logger');
var url = require('url');

function Bounce(request, reply, bounceToken) {
  _.bindAll(this);
  this._request = request;
  this._reply = reply;
  this._bounceToken = bounceToken;
  this.query = request.query;
}
Bounce.prototype.set = function (key, value, callback) {
  this._bounceToken.state[key] = value;
  this._bounceToken.markModified('state');
  return this._bounceToken.saveAsync().bind(this).then(function () {
    return this;
  }).nodeify(callback);
};
Bounce.prototype.setDisplayProperty = function(name, value, callback) {
  var existingProperty = _.where(this._bounceToken.displayProperties, function(prop) {
    return prop.name.toLowerCase() === name.toLowerCase();
  });
  if(existingProperty.length !== 0) {
    existingProperty[0].value = value;
  } else {
    this._bounceToken.displayProperties.push({
      name: name,
      value: value
    });
  }
  return this._bounceToken.saveAsync().bind(this).then(function () {
    return this;
  }).nodeify(callback);
};
Bounce.prototype.get = function (key) {
  return this._bounceToken.state[key];
};
Bounce.prototype.done = function (err) {
  /* istanbul ignore next */
  BBPromise.try(function () {
    if (this._bounceToken.saveTo && this._bounceToken.saveId) {
      /*istanbul ignore next */
      if (this._bounceToken.saveTo.toLowerCase() === 'bucket') {
        return Model.Bucket.findOneAsync({
          _id: this._bounceToken.saveId,
          application: this._bounceToken.application,
          environment: this._bounceToken.environment
        }).bind(this).then(function (bucket) {
          if (!bucket.meta.authToken) {
            bucket.meta.authToken = {};
          }
          bucket.meta.authToken[this._bounceToken.connectorKey] = this._bounceToken.key;
          bucket.markModified('meta');
          return bucket.saveAsync();
        });
      }
      /*istanbul ignore next */
      else if (this._bounceToken.saveTo.toLowerCase() === 'user') {
        return Model.AppUser.findOneAsync({
          _id: this._bounceToken.saveId,
          application: this._bounceToken.application,
          environment: this._bounceToken.environment
        }).bind(this).then(function (appUser) {
          appUser.meta.authToken = this._bounceToken.key;
          appUser.markModified('meta');
          return appUser.saveAsync();
        });
      }
    }
  }, [], this).bind(this).then(function () {
    if (this._bounceToken.returnUrl) {
      new Model.ExecutionLogEvent({
        application: this._bounceToken.application,
        environment: 'live',
        message: 'redirecting back to application for token:' + this._bounceToken.key,
        type: 'LOG'
      }).saveAsync().catch(function (err) {
        logger.error(err);
        logger.alert(err);
      });
      if (this._bounceToken.saveTo && this._bounceToken.saveId) {
        this._reply.redirect(this._bounceToken.returnUrl)
          .state('bouncer-token', this._bounceToken.toObject());
      } else {
        var _url = url.parse(this._bounceToken.returnUrl, true);
        _url.query.token = this._bounceToken.key;
        delete _url.search;
        this._reply.redirect(url.format(_url))
          .state('bouncer-token', this._bounceToken.toObject());
      }
    } else {
      var error;
      if (err) {
        if (err.stack) {
          error = err.stack;
        } else if (err.message) {
          error = err.message;
        } else {
          error = err;
        }
      }
      var options = {
        connectorType: this._bounceToken.connectorType,
        error: error
      };
      /* Only attach the token to the page if they didn't save
         to a bucket or a user */
      if (!this._bounceToken.saveTo) {
        options.token = this._bounceToken.key;
      }
      this._reply.view('done', options).state('bouncer-token', this._bounceToken.toObject());
    }
  }).catch(function (err) {
    logger.error(err);
    logger.alert(err);
    this._reply.view('done', {
      error: err.stack
    });
  });
};
Bounce.prototype.delete = function (key, callback) {
  delete this._bounceToken.state[key];
  this._bounceToken.markModified('state');
  return this._bounceToken.saveAsync().bind(this).then(function () {
    return this;
  }).nodeify(callback);
};
Bounce.prototype.redirect = function (url) {
  this._reply.redirect(url).state('bouncer-token', this._bounceToken.toObject());
};

function StateController() {
  BaseController.apply(this, arguments);
}

util.inherits(StateController, BaseController);

StateController.prototype.bounce = function (request, reply) {

  BBPromise.try(function () {
    logger.info({
      params: request.params.key,
      state: request.state
    });
    var key = request.params.key || request.state['bouncer-token'].key;
    if (!key) {
      throw new errors.Http404Error('invalid bounce key');
    }
    return Model.BouncerToken.findOneAsync({
      key: key
    });
  }).then(function (token) {
    if (!token) {
      throw new errors.Http404Error('invalid bounce key');
    }
    new Model.ExecutionLogEvent({
      application: token._id,
      environment: 'live',
      message: 'Bounce recieved for ' + token.key,
      type: 'LOG'
    }).saveAsync().catch(function (err) {
      logger.error(err);
    });
    return Model.ConnectorSetting.findOneAsync({
      application: token.application,
      environment: token.environment,
      key: token.connectorKey
    }).then(function (connectorSettings) {
      if (!connectorSettings) {
        throw new errors.Http404Error('invalid bounce key');
      }
      var bounce = new Bounce(request, reply, token);
      return connectorModule.loadConnector(connectorSettings)
        .then(function (connector) {
          return connector.receiveBounce(bounce);
        });
    });
  }).catch(this.errorProcessor(reply));
};

StateController.prototype.initRoutes = function (server) {
  server.route({
    method: 'GET',
    path: '/bounce/{key?}',
    handler: this.bounce
  });
};

module.exports = new StateController();
<|endoftext|>


<commit_before>/**
 * MessageBox.js
 *
 * Copyright, Moxiecode Systems AB
 * Released under LGPL License.
 *
 * License: http://www.tinymce.com/license
 * Contributing: http://www.tinymce.com/contributing
 */

/**
 * This class is used to create MessageBoxes like alerts/confirms etc.
 *
 * @class tinymce.ui.Window
 * @extends tinymce.ui.FloatPanel
 */
define("tinymce/ui/MessageBox", [
	"tinymce/ui/Window"
], function(Window) {
	"use strict";

	var MessageBox = Window.extend({
		/**
		 * Constructs a instance with the specified settings.
		 *
		 * @constructor
		 * @param {Object} settings Name/value object with settings.
		 */
		init: function(settings) {
			settings = {
				border: 1,
				padding: 20,
				layout: 'flex',
				pack: "center",
				align: "center",
				containerCls: 'panel',
				autoScroll: true,
				buttons: {type: "button", text: "Ok", action: "ok"},
				items: {
					type: "label",
					multiline: true,
					maxWidth: 500,
					maxHeight: 200
				}
			};

			this._super(settings);
		},

		Statics: {
			/**
			 * Ok buttons constant.
			 *
			 * @static
			 * @final
			 * @field {Number} OK
			 */
			OK: 1,

			/**
			 * Ok/cancel buttons constant.
			 *
			 * @static
			 * @final
			 * @field {Number} OK_CANCEL
			 */
			OK_CANCEL: 2,

			/**
			 * yes/no buttons constant.
			 *
			 * @static
			 * @final
			 * @field {Number} YES_NO
			 */
			YES_NO: 3,

			/**
			 * yes/no/cancel buttons constant.
			 *
			 * @static
			 * @final
			 * @field {Number} YES_NO_CANCEL
			 */
			YES_NO_CANCEL: 4,

			/**
			 * Constructs a new message box and renders it to the body element.
			 *
			 * @static
			 * @method msgBox
			 * @param {Object} settings Name/value object with settings.
			 */
			msgBox: function(settings) {
				var buttons, callback = settings.callback || function() {};

				switch (settings.buttons) {
					case MessageBox.OK_CANCEL:
						buttons = [
							{type: "button", text: "Ok", subtype: "primary", onClick: function(e) {
								e.control.parents()[1].close();
								callback(true);
							}},

							{type: "button", text: "Cancel", onClick: function(e) {
								e.control.parents()[1].close();
								callback(false);
							}}
						];
						break;

					case MessageBox.YES_NO:
						buttons = [
							{type: "button", text: "Ok", subtype: "primary", onClick: function(e) {
								e.control.parents()[1].close();
								callback(true);
							}}
						];
						break;

					case MessageBox.YES_NO_CANCEL:
						buttons = [
							{type: "button", text: "Ok", subtype: "primary", onClick: function(e) {
								e.control.parents()[1].close();
							}}
						];
						break;

					default:
						buttons = [
							{type: "button", text: "Ok", subtype: "primary", onClick: function(e) {
								e.control.parents()[1].close();
							}}
						];
						break;
				}

				return new Window({
					padding: 20,
					x: settings.x,
					y: settings.y,
					minWidth: 300,
					minHeight: 100,
					layout: "flex",
					pack: "center",
					align: "center",
					buttons: buttons,
					title: settings.title,
					items: {
						type: "label",
						multiline: true,
						maxWidth: 500,
						maxHeight: 200,
						text: settings.text
					},
					onClose: settings.onClose
				}).renderTo(document.body).reflow();
			},

			/**
			 * Creates a new alert dialog.
			 *
			 * @method alert
			 * @param {Object} settings Settings for the alert dialog.
			 * @param {function} [callback] Callback to execute when the user makes a choice.
			 */
			alert: function(settings, callback) {
				if (typeof(settings) == "string") {
					settings = {text: settings};
				}

				settings.callback = callback;
				return MessageBox.msgBox(settings);
			},

			/**
			 * Creates a new confirm dialog.
			 *
			 * @method confirm
			 * @param {Object} settings Settings for the confirm dialog.
			 * @param {function} [callback] Callback to execute when the user makes a choice.
			 */
			confirm: function(settings, callback) {
				if (typeof(settings) == "string") {
					settings = {text: settings};
				}

				settings.callback = callback;
				settings.buttons = MessageBox.OK_CANCEL;

				return MessageBox.msgBox(settings);
			}
		}
	});

	return MessageBox;
});<commit_msg>Fixed so alert message box executes a callback when it's done.<commit_after>/**
 * MessageBox.js
 *
 * Copyright, Moxiecode Systems AB
 * Released under LGPL License.
 *
 * License: http://www.tinymce.com/license
 * Contributing: http://www.tinymce.com/contributing
 */

/**
 * This class is used to create MessageBoxes like alerts/confirms etc.
 *
 * @class tinymce.ui.Window
 * @extends tinymce.ui.FloatPanel
 */
define("tinymce/ui/MessageBox", [
	"tinymce/ui/Window"
], function(Window) {
	"use strict";

	var MessageBox = Window.extend({
		/**
		 * Constructs a instance with the specified settings.
		 *
		 * @constructor
		 * @param {Object} settings Name/value object with settings.
		 */
		init: function(settings) {
			settings = {
				border: 1,
				padding: 20,
				layout: 'flex',
				pack: "center",
				align: "center",
				containerCls: 'panel',
				autoScroll: true,
				buttons: {type: "button", text: "Ok", action: "ok"},
				items: {
					type: "label",
					multiline: true,
					maxWidth: 500,
					maxHeight: 200
				}
			};

			this._super(settings);
		},

		Statics: {
			/**
			 * Ok buttons constant.
			 *
			 * @static
			 * @final
			 * @field {Number} OK
			 */
			OK: 1,

			/**
			 * Ok/cancel buttons constant.
			 *
			 * @static
			 * @final
			 * @field {Number} OK_CANCEL
			 */
			OK_CANCEL: 2,

			/**
			 * yes/no buttons constant.
			 *
			 * @static
			 * @final
			 * @field {Number} YES_NO
			 */
			YES_NO: 3,

			/**
			 * yes/no/cancel buttons constant.
			 *
			 * @static
			 * @final
			 * @field {Number} YES_NO_CANCEL
			 */
			YES_NO_CANCEL: 4,

			/**
			 * Constructs a new message box and renders it to the body element.
			 *
			 * @static
			 * @method msgBox
			 * @param {Object} settings Name/value object with settings.
			 */
			msgBox: function(settings) {
				var buttons, callback = settings.callback || function() {};

				switch (settings.buttons) {
					case MessageBox.OK_CANCEL:
						buttons = [
							{type: "button", text: "Ok", subtype: "primary", onClick: function(e) {
								e.control.parents()[1].close();
								callback(true);
							}},

							{type: "button", text: "Cancel", onClick: function(e) {
								e.control.parents()[1].close();
								callback(false);
							}}
						];
						break;

					case MessageBox.YES_NO:
						buttons = [
							{type: "button", text: "Ok", subtype: "primary", onClick: function(e) {
								e.control.parents()[1].close();
								callback(true);
							}}
						];
						break;

					case MessageBox.YES_NO_CANCEL:
						buttons = [
							{type: "button", text: "Ok", subtype: "primary", onClick: function(e) {
								e.control.parents()[1].close();
							}}
						];
						break;

					default:
						buttons = [
							{type: "button", text: "Ok", subtype: "primary", onClick: function(e) {
								e.control.parents()[1].close();
								callback(true);
							}}
						];
						break;
				}

				return new Window({
					padding: 20,
					x: settings.x,
					y: settings.y,
					minWidth: 300,
					minHeight: 100,
					layout: "flex",
					pack: "center",
					align: "center",
					buttons: buttons,
					title: settings.title,
					items: {
						type: "label",
						multiline: true,
						maxWidth: 500,
						maxHeight: 200,
						text: settings.text
					},
					onClose: settings.onClose
				}).renderTo(document.body).reflow();
			},

			/**
			 * Creates a new alert dialog.
			 *
			 * @method alert
			 * @param {Object} settings Settings for the alert dialog.
			 * @param {function} [callback] Callback to execute when the user makes a choice.
			 */
			alert: function(settings, callback) {
				if (typeof(settings) == "string") {
					settings = {text: settings};
				}

				settings.callback = callback;
				return MessageBox.msgBox(settings);
			},

			/**
			 * Creates a new confirm dialog.
			 *
			 * @method confirm
			 * @param {Object} settings Settings for the confirm dialog.
			 * @param {function} [callback] Callback to execute when the user makes a choice.
			 */
			confirm: function(settings, callback) {
				if (typeof(settings) == "string") {
					settings = {text: settings};
				}

				settings.callback = callback;
				settings.buttons = MessageBox.OK_CANCEL;

				return MessageBox.msgBox(settings);
			}
		}
	});

	return MessageBox;
});<|endoftext|>


<commit_before>/**
 * EventEmitter v3.1.1
 * https://github.com/Wolfy87/EventEmitter
 *
 * Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
 * Oliver Caldwell (olivercaldwell.co.uk)
 */

(function(exports) {
	'use strict';

	/**
	 * EventEmitter class
	 * Creates an object with event registering and firing methods
	 */
	function EventEmitter() {
		// Initialise required storage variables
		this._events = {};
		this._maxListeners = 10;
	}

	/**
	 * Event class
	 * Contains Event methods and property storage
	 *
	 * @param {String} type Event type name
	 * @param {Function} listener Function to be called when the event is fired
	 * @param {Object} scope Object that this should be set to when the listener is called
	 * @param {Boolean} once If true then the listener will be removed after the first call
	 * @param {Object} instance The parent EventEmitter instance
	 */
	function Event(type, listener, scope, once, instance) {
		// Store arguments
		this.type = type;
		this.listener = listener;
		this.scope = scope;
		this.once = once;
		this.instance = instance;
	}

	/**
	 * Executes the listener
	 *
	 * @param {Array} args List of arguments to pass to the listener
	 * @return {Boolean} If false then it was a once event
	 */
	Event.prototype.fire = function(args) {
		this.listener.apply(this.scope || this, args);

		// Remove the listener if this is a once only listener
		if(this.once) {
			this.instance.removeListener(this.type, this.listener, this.scope);
			return false;
		}
	};

	/**
	 * Passes every listener for a specified event to a function one at a time
	 *
	 * @param {String} type Event type name
	 * @param {Function} callback Function to pass each listener to
	 * @return {Object} The current EventEmitter instance to allow chaining
	 */
	EventEmitter.prototype.eachListener = function(type, callback) {
		// Initialise variables
		var i = null,
			possibleListeners = null,
			result = null;

		// Only loop if the type exists
		if(this._events.hasOwnProperty(type)) {
			possibleListeners = this._events[type];

			for(i = 0; i < possibleListeners.length; i += 1) {
				result = callback.call(this, possibleListeners[i], i);

				if(result === false) {
					i -= 1;
				}
				else if(result === true) {
					break;
				}
			}
		}

		// Return the instance to allow chaining
		return this;
	};

	/**
	 * Adds an event listener for the specified event
	 *
	 * @param {String} type Event type name
	 * @param {Function} listener Function to be called when the event is fired
	 * @param {Object} scope Object that this should be set to when the listener is called
	 * @param {Boolean} once If true then the listener will be removed after the first call
	 * @return {Object} The current EventEmitter instance to allow chaining
	 */
	EventEmitter.prototype.addListener = function(type, listener, scope, once) {
		// Create the listener array if it does not exist yet
		if(!this._events.hasOwnProperty(type)) {
			this._events[type] = [];
		}

		// Push the new event to the array
		this._events[type].push(new Event(type, listener, scope, once, this));

		// Emit the new listener event
		this.emit('newListener', type, listener, scope, once);

		// Check if we have exceeded the maxListener count
		// Ignore this check if the count is 0
		// Also don't check if we have already fired a warning
		if(this._maxListeners && !this._events[type].warned && this._events[type].length > this._maxListeners) {
			// The max listener count has been exceeded!
			// Warn via the console if it exists
			if(typeof console !== 'undefined') {
				console.warn('Possible EventEmitter memory leak detected. ' + this._events[type].length + ' listeners added. Use emitter.setMaxListeners() to increase limit.');
			}

			// Set the flag so it doesn't fire again
			this._events[type].warned = true;
		}

		// Return the instance to allow chaining
		return this;
	};

	/**
	 * Alias of the addListener method
	 *
	 * @param {String} type Event type name
	 * @param {Function} listener Function to be called when the event is fired
	 * @param {Object} scope Object that this should be set to when the listener is called
	 * @param {Boolean} once If true then the listener will be removed after the first call
	 */
	EventEmitter.prototype.on = EventEmitter.prototype.addListener;

	/**
	 * Alias of the addListener method but will remove the event after the first use
	 *
	 * @param {String} type Event type name
	 * @param {Function} listener Function to be called when the event is fired
	 * @param {Object} scope Object that this should be set to when the listener is called
	 * @return {Object} The current EventEmitter instance to allow chaining
	 */
	EventEmitter.prototype.once = function(type, listener, scope) {
		return this.addListener(type, listener, scope, true);
	};

	/**
	 * Removes the a listener for the specified event
	 *
	 * @param {String} type Event type name the listener must have for the event to be removed
	 * @param {Function} listener Listener the event must have to be removed
	 * @param {Object} scope The scope the event must have to be removed
	 * @return {Object} The current EventEmitter instance to allow chaining
	 */
	EventEmitter.prototype.removeListener = function(type, listener, scope) {
		this.eachListener(type, function(currentListener, index) {
			// If this is the listener remove it from the array
			// We also compare the scope if it was passed
			if(currentListener.listener === listener && (!scope || currentListener.scope === scope)) {
				this._events[type].splice(index, 1);
			}
		});

		// Remove the property if there are no more listeners
		if(this._events[type] && this._events[type].length === 0) {
			delete this._events[type];
		}

		// Return the instance to allow chaining
		return this;
	};

	/**
	 * Removes all listeners for a specified event
	 * If no event type is passed it will remove every listener
	 *
	 * @param {String} type Event type name to remove all listeners from
	 * @return {Object} The current EventEmitter instance to allow chaining
	 */
	EventEmitter.prototype.removeAllListeners = function(type) {
		// Check for a type, if there is none remove all listeners
		// If there is a type however, just remove the listeners for that type
		if(type && this._events.hasOwnProperty(type)) {
			delete this._events[type];
		}
		else if(!type) {
			this._events = {};
		}

		// Return the instance to allow chaining
		return this;
	};

	/**
	 * Retrieves the array of listeners for a specified event
	 *
	 * @param {String} type Event type name to return all listeners from
	 * @return {Array} Will return either an array of listeners or an empty array if there are none
	 */
	EventEmitter.prototype.listeners = function(type) {
		// Return the array of listeners or an empty array if it does not exist
		if(this._events.hasOwnProperty(type)) {
			// It does exist, loop over building the array
			var listeners = [];

			this.eachListener(type, function(evt) {
				listeners.push(evt.listener);
			});

			return listeners;
		}

		return [];
	};

	/**
	 * Emits an event executing all appropriate listeners
	 * All values passed after the type will be passed as arguments to the listeners
	 *
	 * @param {String} type Event type name to run all listeners from
	 * @return {Object} The current EventEmitter instance to allow chaining
	 */
	EventEmitter.prototype.emit = function(type) {
		// Calculate the arguments
		var args = [],
			i = null;

		for(i = 1; i < arguments.length; i += 1) {
			args.push(arguments[i]);
		}

		this.eachListener(type, function(currentListener) {
			return currentListener.fire(args);
		});

		// Return the instance to allow chaining
		return this;
	};

	/**
	 * Sets the max listener count for the EventEmitter
	 * When the count of listeners for an event exceeds this limit a warning will be printed
	 * Set to 0 for no limit
	 *
	 * @param {Number} maxListeners The new max listener limit
	 * @return {Object} The current EventEmitter instance to allow chaining
	 */
	EventEmitter.prototype.setMaxListeners = function(maxListeners) {
		this._maxListeners = maxListeners;

		// Return the instance to allow chaining
		return this;
	};

	// Export the class
  if (typeof define === 'function' && define.amd) {
    define(function() {
      return EventEmitter;
    });
  } else {
    exports.EventEmitter = EventEmitter;
  }

}(this));
<commit_msg>Changed the formatting of the latest pull request to add more module support. Now it uses tabs and matches the rest of the source.<commit_after>/**
 * EventEmitter v3.1.1
 * https://github.com/Wolfy87/EventEmitter
 *
 * Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
 * Oliver Caldwell (olivercaldwell.co.uk)
 */

(function(exports) {
	'use strict';

	/**
	 * EventEmitter class
	 * Creates an object with event registering and firing methods
	 */
	function EventEmitter() {
		// Initialise required storage variables
		this._events = {};
		this._maxListeners = 10;
	}

	/**
	 * Event class
	 * Contains Event methods and property storage
	 *
	 * @param {String} type Event type name
	 * @param {Function} listener Function to be called when the event is fired
	 * @param {Object} scope Object that this should be set to when the listener is called
	 * @param {Boolean} once If true then the listener will be removed after the first call
	 * @param {Object} instance The parent EventEmitter instance
	 */
	function Event(type, listener, scope, once, instance) {
		// Store arguments
		this.type = type;
		this.listener = listener;
		this.scope = scope;
		this.once = once;
		this.instance = instance;
	}

	/**
	 * Executes the listener
	 *
	 * @param {Array} args List of arguments to pass to the listener
	 * @return {Boolean} If false then it was a once event
	 */
	Event.prototype.fire = function(args) {
		this.listener.apply(this.scope || this, args);

		// Remove the listener if this is a once only listener
		if(this.once) {
			this.instance.removeListener(this.type, this.listener, this.scope);
			return false;
		}
	};

	/**
	 * Passes every listener for a specified event to a function one at a time
	 *
	 * @param {String} type Event type name
	 * @param {Function} callback Function to pass each listener to
	 * @return {Object} The current EventEmitter instance to allow chaining
	 */
	EventEmitter.prototype.eachListener = function(type, callback) {
		// Initialise variables
		var i = null,
			possibleListeners = null,
			result = null;

		// Only loop if the type exists
		if(this._events.hasOwnProperty(type)) {
			possibleListeners = this._events[type];

			for(i = 0; i < possibleListeners.length; i += 1) {
				result = callback.call(this, possibleListeners[i], i);

				if(result === false) {
					i -= 1;
				}
				else if(result === true) {
					break;
				}
			}
		}

		// Return the instance to allow chaining
		return this;
	};

	/**
	 * Adds an event listener for the specified event
	 *
	 * @param {String} type Event type name
	 * @param {Function} listener Function to be called when the event is fired
	 * @param {Object} scope Object that this should be set to when the listener is called
	 * @param {Boolean} once If true then the listener will be removed after the first call
	 * @return {Object} The current EventEmitter instance to allow chaining
	 */
	EventEmitter.prototype.addListener = function(type, listener, scope, once) {
		// Create the listener array if it does not exist yet
		if(!this._events.hasOwnProperty(type)) {
			this._events[type] = [];
		}

		// Push the new event to the array
		this._events[type].push(new Event(type, listener, scope, once, this));

		// Emit the new listener event
		this.emit('newListener', type, listener, scope, once);

		// Check if we have exceeded the maxListener count
		// Ignore this check if the count is 0
		// Also don't check if we have already fired a warning
		if(this._maxListeners && !this._events[type].warned && this._events[type].length > this._maxListeners) {
			// The max listener count has been exceeded!
			// Warn via the console if it exists
			if(typeof console !== 'undefined') {
				console.warn('Possible EventEmitter memory leak detected. ' + this._events[type].length + ' listeners added. Use emitter.setMaxListeners() to increase limit.');
			}

			// Set the flag so it doesn't fire again
			this._events[type].warned = true;
		}

		// Return the instance to allow chaining
		return this;
	};

	/**
	 * Alias of the addListener method
	 *
	 * @param {String} type Event type name
	 * @param {Function} listener Function to be called when the event is fired
	 * @param {Object} scope Object that this should be set to when the listener is called
	 * @param {Boolean} once If true then the listener will be removed after the first call
	 */
	EventEmitter.prototype.on = EventEmitter.prototype.addListener;

	/**
	 * Alias of the addListener method but will remove the event after the first use
	 *
	 * @param {String} type Event type name
	 * @param {Function} listener Function to be called when the event is fired
	 * @param {Object} scope Object that this should be set to when the listener is called
	 * @return {Object} The current EventEmitter instance to allow chaining
	 */
	EventEmitter.prototype.once = function(type, listener, scope) {
		return this.addListener(type, listener, scope, true);
	};

	/**
	 * Removes the a listener for the specified event
	 *
	 * @param {String} type Event type name the listener must have for the event to be removed
	 * @param {Function} listener Listener the event must have to be removed
	 * @param {Object} scope The scope the event must have to be removed
	 * @return {Object} The current EventEmitter instance to allow chaining
	 */
	EventEmitter.prototype.removeListener = function(type, listener, scope) {
		this.eachListener(type, function(currentListener, index) {
			// If this is the listener remove it from the array
			// We also compare the scope if it was passed
			if(currentListener.listener === listener && (!scope || currentListener.scope === scope)) {
				this._events[type].splice(index, 1);
			}
		});

		// Remove the property if there are no more listeners
		if(this._events[type] && this._events[type].length === 0) {
			delete this._events[type];
		}

		// Return the instance to allow chaining
		return this;
	};

	/**
	 * Removes all listeners for a specified event
	 * If no event type is passed it will remove every listener
	 *
	 * @param {String} type Event type name to remove all listeners from
	 * @return {Object} The current EventEmitter instance to allow chaining
	 */
	EventEmitter.prototype.removeAllListeners = function(type) {
		// Check for a type, if there is none remove all listeners
		// If there is a type however, just remove the listeners for that type
		if(type && this._events.hasOwnProperty(type)) {
			delete this._events[type];
		}
		else if(!type) {
			this._events = {};
		}

		// Return the instance to allow chaining
		return this;
	};

	/**
	 * Retrieves the array of listeners for a specified event
	 *
	 * @param {String} type Event type name to return all listeners from
	 * @return {Array} Will return either an array of listeners or an empty array if there are none
	 */
	EventEmitter.prototype.listeners = function(type) {
		// Return the array of listeners or an empty array if it does not exist
		if(this._events.hasOwnProperty(type)) {
			// It does exist, loop over building the array
			var listeners = [];

			this.eachListener(type, function(evt) {
				listeners.push(evt.listener);
			});

			return listeners;
		}

		return [];
	};

	/**
	 * Emits an event executing all appropriate listeners
	 * All values passed after the type will be passed as arguments to the listeners
	 *
	 * @param {String} type Event type name to run all listeners from
	 * @return {Object} The current EventEmitter instance to allow chaining
	 */
	EventEmitter.prototype.emit = function(type) {
		// Calculate the arguments
		var args = [],
			i = null;

		for(i = 1; i < arguments.length; i += 1) {
			args.push(arguments[i]);
		}

		this.eachListener(type, function(currentListener) {
			return currentListener.fire(args);
		});

		// Return the instance to allow chaining
		return this;
	};

	/**
	 * Sets the max listener count for the EventEmitter
	 * When the count of listeners for an event exceeds this limit a warning will be printed
	 * Set to 0 for no limit
	 *
	 * @param {Number} maxListeners The new max listener limit
	 * @return {Object} The current EventEmitter instance to allow chaining
	 */
	EventEmitter.prototype.setMaxListeners = function(maxListeners) {
		this._maxListeners = maxListeners;

		// Return the instance to allow chaining
		return this;
	};

	// Export the class
	if(typeof define === 'function' && define.amd) {
		define(function() {
			return EventEmitter;
		});
	}
	else {
		exports.EventEmitter = EventEmitter;
	}
}(this));
<|endoftext|>


<commit_before>import $ from 'jquery';
import React from 'react';
import reactMixin from 'react-mixin';
import Reflux from 'reflux';
import Select from 'react-select';
import {bem} from '../bem';
import ui from '../ui';
import autoBind from 'react-autobind';
import {actions} from '../actions';
import mixins from '../mixins';
import Dropzone from 'react-dropzone';
import alertify from 'alertifyjs';
import { QUERY_LIMIT_DEFAULT } from './map';

import { assign, t, validFileTypes, notify } from '../utils';
import { dataInterface } from '../dataInterface';

let colorSets = ['a', 'b', 'c', 'd', 'e'];
// see kobo.map.marker-colors.scss for styling details of each set
const QUERY_LIMIT_MINIMUM = 1000;
const QUERY_LIMIT_MAXIMUM = 30000;

class MapColorPicker extends React.Component {
  constructor(props) {
    super(props);
    autoBind(this);

    this.state = {
      selected: props.mapSettings.colorSet ? props.mapSettings.colorSet : 'a'
    };

  }
  onChange (e) {
    this.props.onChange(e.currentTarget.value);
    this.setState({
      selected: e.currentTarget.value
    });
  }
  defaultValue (set) {
    return this.state.selected === set;
  }
  colorRows(set, length = 10) {
    let colorRows = [];
    for (let i = 1; i < length; i++) {
      colorRows.push(<span key={i} className={`map-marker map-marker-${set}${i}`} />);
    }
    return colorRows;
  }
  render () {
    var radioButtons = colorSets.map(function(set, index){
      var length = 10;
      var label = false;
      if (set === 'a') length = 16;
      if (set === 'a') label = t('Best for qualitative data');
      if (set === 'b') label = t('Best for sequential data');
      if (set === 'd') label = t('Best for diverging data');
      return (
        <bem.FormModal__item m='map-color-item' key={index}>
          {label &&
            {label}
          }
          <bem.GraphSettings__radio>
            <input type='radio' name='chart_colors'
              value={set}
              checked={this.defaultValue(set)}
              onChange={this.onChange}
              id={'c-' + index} />

              {this.colorRows(set, length)}

          </bem.GraphSettings__radio>
        </bem.FormModal__item>

      );
    }, this);

    return (
      <bem.GraphSettings__colors>{radioButtons}</bem.GraphSettings__colors>
    );
  }
}

class MapSettings extends React.Component {
  constructor(props) {
    super(props);
    autoBind(this);

    var geoQuestions = [];

    props.asset.content.survey.forEach(function(question) {
      if (question.type && question.type === 'geopoint') {
        geoQuestions.push({
          value: question.name || question.$autoname,
          label: question.label[0]
        });
      }
    });
    var queryCount = props.asset.deployment__submission_count;

    let defaultActiveTab = 'colors';
    if (this.userCan('change_asset', this.props.asset)) {defaultActiveTab = 'overlays';}
    if (geoQuestions.length > 1) {defaultActiveTab = 'geoquestion';}
    if (queryCount > QUERY_LIMIT_MINIMUM) {defaultActiveTab = 'querylimit';}

    this.state = {
      activeModalTab: defaultActiveTab,
      geoQuestions: geoQuestions,
      mapSettings: this.props.asset.map_styles,
      files: [],
      layerName: '',
      queryCount: queryCount
    };
  }
  componentDidMount() {
    actions.resources.getAssetFiles(this.props.asset.uid);
    this.listenTo(
      actions.resources.getAssetFiles.completed,
      this.updateFileList
    );
  }
  toggleTab(evt) {
    var n = evt.target.getAttribute('data-tabname');
    this.setState({
      activeModalTab: n
    });
  }
  geoPointQuestionChange(evt) {
    let settings = this.state.mapSettings;
    settings.selectedQuestion = evt.target.value;
    this.setState({ mapSettings: settings });
  }
  queryLimitChange(evt) {
    let settings = this.state.mapSettings;
    settings.querylimit = evt.target.value;
    this.setState({ mapSettings: settings });
  }
  resetMapSettings() {
    actions.map.setMapSettings(this.props.asset.uid, {});
    this.props.toggleMapSettings();
  }
  saveMapSettings() {
    let settings = this.state.mapSettings,
      new_querylimit = settings.querylimit,
      assetUid = this.props.asset.uid;
      console.log(new_querylimit);
      console.log('t/f? : ' + 'null: ' + new_querylimit === '' + ' the rest: ' + new_querylimit < QUERY_LIMIT_MINIMUM || new_querylimit > QUERY_LIMIT_MAXIMUM);
    if (!new_querylimit === null || (new_querylimit < QUERY_LIMIT_MINIMUM || new_querylimit > QUERY_LIMIT_MAXIMUM)) {
        notify(t('Please enter an integer greater than ' + QUERY_LIMIT_MINIMUM + ' or less than ' + QUERY_LIMIT_MAXIMUM + '.'));
    } else {
      if (new_querylimit === null) {
        settings.querylimit = QUERY_LIMIT_DEFAULT;
      }
      if (this.userCan('change_asset', this.props.asset)) {
        actions.map.setMapSettings(assetUid, settings);
      } else {
        // pass settings to parent component directly
        // for users with no permission to edit asset
        this.props.overrideStyles(settings);
      }
      this.props.toggleMapSettings();
    }
  }
  updateFileList(data) {
    if (data.results) {
      this.setState({ files: data.results });
    }
  }
  dropFiles(files, rejectedFiles) {
    let uid = this.props.asset.uid,
      _this = this,
      name = this.state.layerName;

    if (!name) {
      alertify.error(t('Please add a name for your layer file.'));
      return false;
    }

    files.map(file => {
      let metadata = {
        type: file.name.split('.').pop(),
        size: file.size
      };
      let data = {
        content: file,
        name: name,
        file_type: 'map_layer',
        metadata: JSON.stringify(metadata)
      };
      dataInterface.uploadAssetFile(uid, data).done(result => {
        _this.setState({ layerName: '' });
        actions.resources.getAssetFiles(this.props.asset.uid);
      }).fail((jqxhr) => {
        var errMsg = t('Upload error: ##error_message##.').replace('##error_message##', jqxhr.statusText);
        alertify.error(errMsg);
      });
    });

    rejectedFiles.map(rej => {
      var errMsg = t('Upload error: not a valid map overlay format.');
      alertify.error(errMsg);
    });
  }
  layerNameChange(e) {
    this.setState({ layerName: e.target.value });
  }
  deleteFile(evt) {
    let el = $(evt.target)
        .closest('[data-uid]')
        .get(0),
      uid = el.getAttribute('data-uid'),
      dialog = alertify.dialog('confirm');

    let opts = {
      title: t('Delete File'),
      message: t(
        'Are you sure you want to delete this file? ' +
          '

This action cannot be undone.'
      ),
      labels: { ok: t('Delete'), cancel: t('Cancel') },
      onok: (evt, val) => {
        dataInterface.deleteAssetFile(this.props.asset.uid, uid).done(r => {
          actions.resources.getAssetFiles(this.props.asset.uid);
          dialog.destroy();
        });
      },
      oncancel: () => {
        dialog.destroy();
      }
    };
    dialog.set(opts).show();
  }
  tabLabel(tab) {
    switch (tab) {
      case 'overlays':
        return t('Overlays');
      case 'colors':
        return t('Marker Colors');
      case 'geoquestion':
        return t('Geopoint question');
      case 'querylimit':
        return t('Query Limit');
    }
  }
  colorChange(val) {
    let settings = this.state.mapSettings;
    settings.colorSet = val;
    this.setState({ mapSettings: settings });
  }
  render() {
    let asset = this.props.asset,
      geoQuestions = this.state.geoQuestions,
      activeTab = this.state.activeModalTab,
      queryLimit = this.state.mapSettings.querylimit || QUERY_LIMIT_DEFAULT,
      queryCount = this.state.queryCount;

    var tabs = ['colors'];

    if (this.userCan('change_asset', asset)) {tabs.unshift('overlays');}
    if (geoQuestions.length > 1) {tabs.unshift('geoquestion');}
    if (queryCount > QUERY_LIMIT_MINIMUM) {tabs.unshift('querylimit');}

    var modalTabs = tabs.map(function(tab, i) {
      return (
        <button
          className={`mdl-button mdl-button--tab ${
            this.state.activeModalTab === tab ? 'active' : ''
          }`}
          onClick={this.toggleTab}
          data-tabname={tab}
          key={i}
        >
          {this.tabLabel(tab)}
        </button>
      );
    }, this);
    return (
      <bem.GraphSettings>
        <ui.Modal.Tabs>{modalTabs}</ui.Modal.Tabs>
        <ui.Modal.Body>

            {activeTab === 'geoquestion' && (

                  {t(
                    'Choose the Geopoint question you would like to display on the map:'
                  )}

                {geoQuestions.map((question, i) => {
                  return (

                      <input
                        type='radio'
                        name='trnsltn'
                        value={question.value}
                        onChange={this.geoPointQuestionChange}
                        checked={
                          this.state.mapSettings.selectedQuestion ===
                          question.value
                            ? true
                            : false
                        }
                        id={'GeopointQuestion-' + i}
                      />
                      {question.label}

                  );
                })}

            )}
            {activeTab === 'overlays' && (

                {this.state.files.length > 0 && (
                  <bem.FormModal__item m='list-files'>
                    {t('Uploaded layers')}
                    {this.state.files.map((file, i) => {
                      return (

                            {file.metadata.type}

                          {file.name}

                      );
                    })}
                  </bem.FormModal__item>
                )}
                <bem.FormModal__item m='layer-upload'>

                    {t('Use the form below to upload files with map data in one of these formats: CSV, KML, KMZ, WKT or GEOJSON. The data will be made available as layers for display on the map.')}

                  <input
                    type='text'
                    id='name'
                    placeholder={t('Layer name')}
                    value={this.state.layerName}
                    onChange={this.layerNameChange}
                  />
                  <Dropzone
                    onDrop={this.dropFiles}
                    multiple={false}
                    className='dropzone'
                    accept={'.csv,.kml,.geojson,.wkt,.json,.kmz'}
                  >
                    <button className='mdl-button mdl-button--raised mdl-button--colored'>
                      {t('Upload')}
                    </button>
                  </Dropzone>
                </bem.FormModal__item>

            )}
            {activeTab === 'colors' && (
              <bem.FormModal__item>

                  {t('Choose the color set for the disaggregated map markers.')}
                  <MapColorPicker onChange={this.colorChange} mapSettings={this.state.mapSettings}/>

              </bem.FormModal__item>
            )}
            {activeTab === 'querylimit' && (
              <bem.FormModal__item>

                  {t('By default the map is limited to the ' + QUERY_LIMIT_DEFAULT + ' most recent submissions. You can temporarily increase this limit to a different value. Note that this is reset whenever you reopen the map.')}

Warning: Displaying a large number of points requires a lot of memory.

                  <form>
                    <input id='limit-slider' className='change-limit-slider' type='range' step='1000' min={QUERY_LIMIT_MINIMUM} max={QUERY_LIMIT_MAXIMUM} value={queryLimit} onChange={this.queryLimitChange}/>
                    <input id='limit-input' className='change-limit-input' type='number' step='1000' min={QUERY_LIMIT_MINIMUM} max={QUERY_LIMIT_MAXIMUM} value={queryLimit} onChange={this.queryLimitChange}/>
                  </form>

              </bem.FormModal__item>
            )}

        </ui.Modal.Body>

        {(activeTab === 'geoquestion' || activeTab === 'colors' || activeTab === 'querylimit') &&
          <bem.Modal__footer>
            {this.userCan('change_asset', this.props.asset) &&
              <bem.Modal__footerButton
                onClick={this.resetMapSettings}
              >
                {t('Reset')}
              </bem.Modal__footerButton>
            }
            <bem.Modal__footerButton
              m='primary'
              onClick={this.saveMapSettings}
            >
              {t('Save')}
            </bem.Modal__footerButton>
          </bem.Modal__footer>
        }
      </bem.GraphSettings>
    );
  }
}

reactMixin(MapSettings.prototype, Reflux.ListenerMixin);
reactMixin(MapSettings.prototype, mixins.permissions);

export default MapSettings;
<commit_msg>fixed SAVE button input bug<commit_after>import $ from 'jquery';
import React from 'react';
import reactMixin from 'react-mixin';
import Reflux from 'reflux';
import Select from 'react-select';
import {bem} from '../bem';
import ui from '../ui';
import autoBind from 'react-autobind';
import {actions} from '../actions';
import mixins from '../mixins';
import Dropzone from 'react-dropzone';
import alertify from 'alertifyjs';
import { QUERY_LIMIT_DEFAULT } from './map';

import { assign, t, validFileTypes, notify } from '../utils';
import { dataInterface } from '../dataInterface';

let colorSets = ['a', 'b', 'c', 'd', 'e'];
// see kobo.map.marker-colors.scss for styling details of each set
const QUERY_LIMIT_MINIMUM = 1000;
const QUERY_LIMIT_MAXIMUM = 30000;

class MapColorPicker extends React.Component {
  constructor(props) {
    super(props);
    autoBind(this);

    this.state = {
      selected: props.mapSettings.colorSet ? props.mapSettings.colorSet : 'a'
    };

  }
  onChange (e) {
    this.props.onChange(e.currentTarget.value);
    this.setState({
      selected: e.currentTarget.value
    });
  }
  defaultValue (set) {
    return this.state.selected === set;
  }
  colorRows(set, length = 10) {
    let colorRows = [];
    for (let i = 1; i < length; i++) {
      colorRows.push(<span key={i} className={`map-marker map-marker-${set}${i}`} />);
    }
    return colorRows;
  }
  render () {
    var radioButtons = colorSets.map(function(set, index){
      var length = 10;
      var label = false;
      if (set === 'a') length = 16;
      if (set === 'a') label = t('Best for qualitative data');
      if (set === 'b') label = t('Best for sequential data');
      if (set === 'd') label = t('Best for diverging data');
      return (
        <bem.FormModal__item m='map-color-item' key={index}>
          {label &&
            {label}
          }
          <bem.GraphSettings__radio>
            <input type='radio' name='chart_colors'
              value={set}
              checked={this.defaultValue(set)}
              onChange={this.onChange}
              id={'c-' + index} />

              {this.colorRows(set, length)}

          </bem.GraphSettings__radio>
        </bem.FormModal__item>

      );
    }, this);

    return (
      <bem.GraphSettings__colors>{radioButtons}</bem.GraphSettings__colors>
    );
  }
}

class MapSettings extends React.Component {
  constructor(props) {
    super(props);
    autoBind(this);

    var geoQuestions = [];

    props.asset.content.survey.forEach(function(question) {
      if (question.type && question.type === 'geopoint') {
        geoQuestions.push({
          value: question.name || question.$autoname,
          label: question.label[0]
        });
      }
    });
    var queryCount = props.asset.deployment__submission_count;

    let defaultActiveTab = 'colors';
    if (this.userCan('change_asset', this.props.asset)) {defaultActiveTab = 'overlays';}
    if (geoQuestions.length > 1) {defaultActiveTab = 'geoquestion';}
    if (queryCount > QUERY_LIMIT_MINIMUM) {defaultActiveTab = 'querylimit';}

    this.state = {
      activeModalTab: defaultActiveTab,
      geoQuestions: geoQuestions,
      mapSettings: this.props.asset.map_styles,
      files: [],
      layerName: '',
      queryCount: queryCount
    };
  }
  componentDidMount() {
    actions.resources.getAssetFiles(this.props.asset.uid);
    this.listenTo(
      actions.resources.getAssetFiles.completed,
      this.updateFileList
    );
  }
  toggleTab(evt) {
    var n = evt.target.getAttribute('data-tabname');
    this.setState({
      activeModalTab: n
    });
  }
  geoPointQuestionChange(evt) {
    let settings = this.state.mapSettings;
    settings.selectedQuestion = evt.target.value;
    this.setState({ mapSettings: settings });
  }
  queryLimitChange(evt) {
    let settings = this.state.mapSettings,
      new_querylimit = evt.target.value;
    if (new_querylimit.length === 0) {
      settings.querylimit = '';
    } else {
      settings.querylimit = new_querylimit;
    }
    this.setState({ mapSettings: settings });
  }
  resetMapSettings() {
    actions.map.setMapSettings(this.props.asset.uid, {});
    this.props.toggleMapSettings();
  }
  saveMapSettings() {
    let settings = this.state.mapSettings,
      new_querylimit = settings.querylimit,
      assetUid = this.props.asset.uid;
    if (new_querylimit < QUERY_LIMIT_MINIMUM || new_querylimit > QUERY_LIMIT_MAXIMUM) {
        notify(t('Please enter an integer greater than ' + QUERY_LIMIT_MINIMUM + ' or less than ' + QUERY_LIMIT_MAXIMUM + '.'));
    } else {
      if (this.userCan('change_asset', this.props.asset)) {
        actions.map.setMapSettings(assetUid, settings);
      } else {
        // pass settings to parent component directly
        // for users with no permission to edit asset
        this.props.overrideStyles(settings);
      }
      this.props.toggleMapSettings();
    }
  }
  updateFileList(data) {
    if (data.results) {
      this.setState({ files: data.results });
    }
  }
  dropFiles(files, rejectedFiles) {
    let uid = this.props.asset.uid,
      _this = this,
      name = this.state.layerName;

    if (!name) {
      alertify.error(t('Please add a name for your layer file.'));
      return false;
    }

    files.map(file => {
      let metadata = {
        type: file.name.split('.').pop(),
        size: file.size
      };
      let data = {
        content: file,
        name: name,
        file_type: 'map_layer',
        metadata: JSON.stringify(metadata)
      };
      dataInterface.uploadAssetFile(uid, data).done(result => {
        _this.setState({ layerName: '' });
        actions.resources.getAssetFiles(this.props.asset.uid);
      }).fail((jqxhr) => {
        var errMsg = t('Upload error: ##error_message##.').replace('##error_message##', jqxhr.statusText);
        alertify.error(errMsg);
      });
    });

    rejectedFiles.map(rej => {
      var errMsg = t('Upload error: not a valid map overlay format.');
      alertify.error(errMsg);
    });
  }
  layerNameChange(e) {
    this.setState({ layerName: e.target.value });
  }
  deleteFile(evt) {
    let el = $(evt.target)
        .closest('[data-uid]')
        .get(0),
      uid = el.getAttribute('data-uid'),
      dialog = alertify.dialog('confirm');

    let opts = {
      title: t('Delete File'),
      message: t(
        'Are you sure you want to delete this file? ' +
          '

This action cannot be undone.'
      ),
      labels: { ok: t('Delete'), cancel: t('Cancel') },
      onok: (evt, val) => {
        dataInterface.deleteAssetFile(this.props.asset.uid, uid).done(r => {
          actions.resources.getAssetFiles(this.props.asset.uid);
          dialog.destroy();
        });
      },
      oncancel: () => {
        dialog.destroy();
      }
    };
    dialog.set(opts).show();
  }
  tabLabel(tab) {
    switch (tab) {
      case 'overlays':
        return t('Overlays');
      case 'colors':
        return t('Marker Colors');
      case 'geoquestion':
        return t('Geopoint question');
      case 'querylimit':
        return t('Query Limit');
    }
  }
  colorChange(val) {
    let settings = this.state.mapSettings;
    settings.colorSet = val;
    this.setState({ mapSettings: settings });
  }
  render() {
    let asset = this.props.asset,
      geoQuestions = this.state.geoQuestions,
      activeTab = this.state.activeModalTab,
      queryLimit = this.state.mapSettings.querylimit,
      queryCount = this.state.queryCount;
    if (queryLimit === undefined) {
      queryLimit = QUERY_LIMIT_DEFAULT;
    }
    var tabs = ['colors']; console.log('render querylimit: ' + queryLimit + 'the state: ' + this.state.mapSettings.querylimit);

    if (this.userCan('change_asset', asset)) {tabs.unshift('overlays');}
    if (geoQuestions.length > 1) {tabs.unshift('geoquestion');}
    if (queryCount > QUERY_LIMIT_MINIMUM) {tabs.unshift('querylimit');}

    var modalTabs = tabs.map(function(tab, i) {
      return (
        <button
          className={`mdl-button mdl-button--tab ${
            this.state.activeModalTab === tab ? 'active' : ''
          }`}
          onClick={this.toggleTab}
          data-tabname={tab}
          key={i}
        >
          {this.tabLabel(tab)}
        </button>
      );
    }, this);
    return (
      <bem.GraphSettings>
        <ui.Modal.Tabs>{modalTabs}</ui.Modal.Tabs>
        <ui.Modal.Body>

            {activeTab === 'geoquestion' && (

                  {t(
                    'Choose the Geopoint question you would like to display on the map:'
                  )}

                {geoQuestions.map((question, i) => {
                  return (

                      <input
                        type='radio'
                        name='trnsltn'
                        value={question.value}
                        onChange={this.geoPointQuestionChange}
                        checked={
                          this.state.mapSettings.selectedQuestion ===
                          question.value
                            ? true
                            : false
                        }
                        id={'GeopointQuestion-' + i}
                      />
                      {question.label}

                  );
                })}

            )}
            {activeTab === 'overlays' && (

                {this.state.files.length > 0 && (
                  <bem.FormModal__item m='list-files'>
                    {t('Uploaded layers')}
                    {this.state.files.map((file, i) => {
                      return (

                            {file.metadata.type}

                          {file.name}

                      );
                    })}
                  </bem.FormModal__item>
                )}
                <bem.FormModal__item m='layer-upload'>

                    {t('Use the form below to upload files with map data in one of these formats: CSV, KML, KMZ, WKT or GEOJSON. The data will be made available as layers for display on the map.')}

                  <input
                    type='text'
                    id='name'
                    placeholder={t('Layer name')}
                    value={this.state.layerName}
                    onChange={this.layerNameChange}
                  />
                  <Dropzone
                    onDrop={this.dropFiles}
                    multiple={false}
                    className='dropzone'
                    accept={'.csv,.kml,.geojson,.wkt,.json,.kmz'}
                  >
                    <button className='mdl-button mdl-button--raised mdl-button--colored'>
                      {t('Upload')}
                    </button>
                  </Dropzone>
                </bem.FormModal__item>

            )}
            {activeTab === 'colors' && (
              <bem.FormModal__item>

                  {t('Choose the color set for the disaggregated map markers.')}
                  <MapColorPicker onChange={this.colorChange} mapSettings={this.state.mapSettings}/>

              </bem.FormModal__item>
            )}
            {activeTab === 'querylimit' && (
              <bem.FormModal__item>

                  {t('By default the map is limited to the ' + QUERY_LIMIT_DEFAULT + ' most recent submissions. You can temporarily increase this limit to a different value. Note that this is reset whenever you reopen the map.')}

Warning: Displaying a large number of points requires a lot of memory.

                  <form>
                    <input id='limit-slider' className='change-limit-slider' type='range' step='1000' min={QUERY_LIMIT_MINIMUM} max={QUERY_LIMIT_MAXIMUM} value={queryLimit} onChange={this.queryLimitChange}/>
                    <input id='limit-input' className='change-limit-input' type='number' step='1000' min={QUERY_LIMIT_MINIMUM} max={QUERY_LIMIT_MAXIMUM} value={queryLimit} onChange={this.queryLimitChange}/>
                  </form>

              </bem.FormModal__item>
            )}

        </ui.Modal.Body>

        {(activeTab === 'geoquestion' || activeTab === 'colors' || activeTab === 'querylimit') &&
          <bem.Modal__footer>
            {this.userCan('change_asset', this.props.asset) &&
              <bem.Modal__footerButton
                onClick={this.resetMapSettings}
              >
                {t('Reset')}
              </bem.Modal__footerButton>
            }
            <bem.Modal__footerButton
              m='primary'
              onClick={this.saveMapSettings}
            >
              {t('Save')}
            </bem.Modal__footerButton>
          </bem.Modal__footer>
        }
      </bem.GraphSettings>
    );
  }
}

reactMixin(MapSettings.prototype, Reflux.ListenerMixin);
reactMixin(MapSettings.prototype, mixins.permissions);

export default MapSettings;
<|endoftext|>