text:
package org.pentaho.di.ui.trans.step;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Dialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Monitor;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.SourceToTargetMapping;
import org.pentaho.di.core.database.DatabaseInterface;
import org.pentaho.di.core.database.DatabaseMeta;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.logging.LogChannel;
import org.pentaho.di.core.logging.LoggingObjectInterface;
import org.pentaho.di.core.logging.LoggingObjectType;
import org.pentaho.di.core.logging.SimpleLoggingObject;
import org.pentaho.di.core.plugins.PluginRegistry;
import org.pentaho.di.core.plugins.StepPluginType;
import org.pentaho.di.core.row.RowMetaInterface;
import org.pentaho.di.core.row.ValueMetaInterface;
import org.pentaho.di.core.variables.VariableSpace;
import org.pentaho.di.core.variables.Variables;
import org.pentaho.di.i18n.BaseMessages;
import org.pentaho.di.laf.BasePropertyHandler;
import org.pentaho.di.repository.Repository;
import org.pentaho.di.trans.TransMeta;
import org.pentaho.di.trans.step.BaseStepMeta;
import org.pentaho.di.trans.step.StepInterface;
import org.pentaho.di.trans.step.StepMeta;
import org.pentaho.di.trans.step.StepMetaInterface;
import org.pentaho.di.ui.core.PropsUI;
import org.pentaho.di.ui.core.database.dialog.DatabaseDialog;
import org.pentaho.di.ui.core.dialog.EnterMappingDialog;
import org.pentaho.di.ui.core.dialog.ErrorDialog;
import org.pentaho.di.ui.core.gui.GUIResource;
import org.pentaho.di.ui.core.gui.WindowProperty;
import org.pentaho.di.ui.core.widget.ComboVar;
import org.pentaho.di.ui.core.widget.TableView;

/**
 * This class provides functionality common to Step Dialogs.
 */
public class BaseStepDialog extends Dialog {

  /** The package name used for internationalization */
  private static Class<?> PKG = StepInterface.class; // for i18n purposes, needed by Translator2!!   $NON-NLS-1$

  /** The logging object interface for this dialog. */
  public static final LoggingObjectInterface loggingObject = new SimpleLoggingObject("Step dialog", LoggingObjectType.STEPDIALOG, null);

  /** The variable bindings for this dialog. */
  protected static VariableSpace variables = new Variables();

  /** The step name. */
  protected String stepname;

  /** The Step name label. */
  protected Label wlStepname;

  /** The Step name UI component. */
  protected Text wStepname;

  /** The FormData for the step name and its label. */
  protected FormData fdlStepname, fdStepname;

  /** Common dialog buttons. */
  protected Button wOK, wGet, wPreview, wSQL, wCreate, wCancel;

  /** FormData for the common dialog buttons. */
  protected FormData fdOK, fdGet, fdPreview, fdSQL, fdCreate, fdCancel;

  /** Listeners for the common dialog buttons. */
  protected Listener lsOK, lsGet, lsPreview, lsSQL, lsCreate, lsCancel;

  /** The metadata for the associated transformation. */
  protected TransMeta transMeta;

  /** A reference to the shell. */
  protected Shell shell;

  /** A listener adapter for default widget selection. */
  protected SelectionAdapter lsDef;

  /** A listener for dialog resizing. */
  protected Listener lsResize;

  /** Whether the dialog (and its backup) have changed. */
  protected boolean changed, backupChanged;

  /** The base step meta. */
  protected BaseStepMeta baseStepMeta;

  /** The UI properties. */
  protected PropsUI props;

  /** The associated repository. */
  protected Repository repository;

  /** The step meta for this dialog. */
  protected StepMeta stepMeta;

  /** The log channel for this dialog. */
  protected LogChannel log;

  /** A constant indicating a center button alignment. */
  protected static final int BUTTON_ALIGNMENT_CENTER = 0;

  /** A constant indicating a left button alignment. */
  protected static final int BUTTON_ALIGNMENT_LEFT = 1;

  /** A constant indicating a right button alignment. */
  protected static final int BUTTON_ALIGNMENT_RIGHT = 2;

  /** The button alignment (defaults to center). */
  protected static int buttonAlignment = BUTTON_ALIGNMENT_CENTER;

  /** A reference to a database dialog. */
  protected DatabaseDialog databaseDialog;

  static {
    // Get the button alignment
    buttonAlignment = getButtonAlignment();
  }

  /**
   * Instantiates a new base step dialog.
   *
   * @param parent the parent shell
   * @param baseStepMeta the associated base step metadata
   * @param transMeta the associated transformation metadata
   * @param stepname the step name
   */
  public BaseStepDialog(Shell parent, BaseStepMeta baseStepMeta, TransMeta transMeta, String stepname) {
    super(parent, SWT.NONE);

    this.log = new LogChannel(baseStepMeta);
    this.transMeta = transMeta;
    this.stepname = stepname;
    this.stepMeta = transMeta.findStep(stepname);
    this.baseStepMeta = baseStepMeta;
    this.backupChanged = baseStepMeta.hasChanged();
    this.props = PropsUI.getInstance();
  }

  /**
   * Instantiates a new base step dialog.
   *
   * @param parent the parent shell
   * @param nr the number of rows
   * @param in the base step metadata
   * @param tr the transformation metadata
   */
  public BaseStepDialog(Shell parent, int nr, BaseStepMeta in, TransMeta tr) {
    this(parent, in, tr, null);
  }

  /**
   * Sets the shell image.
   *
   * @param shell the shell
   * @param stepMetaInterface the step meta interface
   */
  public void setShellImage(Shell shell, StepMetaInterface stepMetaInterface) {
    try {
      String id = PluginRegistry.getInstance().getPluginId(StepPluginType.class, stepMetaInterface);
      if (id != null) {
        shell.setImage(GUIResource.getInstance().getImagesSteps().get(id));
      }
    } catch (Throwable e) {
    }
  }

  /**
   * Dispose this dialog.
   */
  public void dispose() {
    WindowProperty winprop = new WindowProperty(shell);
    props.setScreen(winprop);
    shell.dispose();
  }

  /**
   * Set the shell size, based upon the previous time the geometry was saved in the Properties file.
   */
  public void setSize() {
    setSize(shell);
  }

  /**
   * Sets the button positions.
   *
   * @param buttons the buttons
   * @param margin the margin between buttons
   * @param lastControl the last control
   */
  protected void setButtonPositions(Button buttons[], int margin, Control lastControl) {
    BaseStepDialog.positionBottomButtons(shell, buttons, margin, lastControl);
  }

  /**
   * Position the specified buttons at the bottom of the parent composite.
   * Also, make the buttons all the same width: the width of the largest button.
   *

   * The default alignment for buttons in the system will be used. This is set as an LAF
   * property with the key Button_Position and has the valid values
   * of left, center, right with center being the default.
   *
   * @param composite the composite
   * @param buttons The buttons to position.
   * @param margin The margin between the buttons in pixels
   * @param lastControl the last control
   */
  public static final void positionBottomButtons(Composite composite, Button buttons[], int margin, Control lastControl) {
    // Determine the largest button in the array
    Rectangle largest = null;
    for (int i = 0; i < buttons.length; i++) {
      buttons[i].pack(true);
      Rectangle r = buttons[i].getBounds();
      if (largest == null || r.width > largest.width)
        largest = r;

      // Also, set the tooltip the same as the name if we don't have one...
      if (buttons[i].getToolTipText() == null) {
        buttons[i].setToolTipText(Const.replace(buttons[i].getText(), "&", "")); //$NON-NLS-1$ //$NON-NLS-2$
      }
    }

    // Make buttons a bit larger... (nicer)
    largest.width += 10;
    if ((largest.width % 2) == 1)
      largest.width++;

    // Compute the left side of the 1st button (based on the system button alignment)
    switch (buttonAlignment) {
      case BUTTON_ALIGNMENT_CENTER:
        centerButtons(buttons, largest.width, margin, lastControl);
        break;
      case BUTTON_ALIGNMENT_LEFT:
        leftAlignButtons(buttons, largest.width, margin, lastControl);
        break;
      case BUTTON_ALIGNMENT_RIGHT:
        rightAlignButtons(buttons, largest.width, margin, lastControl);
        break;
    }
    if (Const.isOSX())
    {
        Shell parentShell=composite.getShell();
        final List<TableView> tableViews = new ArrayList<TableView>();
        getTableViews(parentShell, tableViews);
        for (final Button button : buttons) {
        	// We know the table views
        	// We also know that if a button is hit, the table loses focus
        	// In that case, we can apply the content of an open text editor...
        	//
        	button.addSelectionListener(new SelectionAdapter() {

				public void widgetSelected(SelectionEvent e) {
					for (TableView view : tableViews)
					{
						view.applyOSXChanges();
					}
				}
			});
        }
    }
  }

  /**
   * Gets the table views.
   *
   * @param parentControl the parent control
   * @param tableViews the table views
   * @return the table views
   */
  private static final void getTableViews(Control parentControl, List<TableView> tableViews)
  {
	if (parentControl instanceof TableView)
	{
		tableViews.add((TableView) parentControl);
	}
	else
	{
		if (parentControl instanceof Composite)
		{
			Control[] children = ((Composite)parentControl).getChildren();
			for (Control child : children)
			{
				getTableViews(child, tableViews);
			}
		}
		else
		{
			if (parentControl instanceof Shell)
			{
				Control[] children = ((Shell)parentControl).getChildren();
				for (Control child : children)
				{
					getTableViews(child, tableViews);
				}

			}
		}
	}
  }

  /**
   * Returns the default alignment for the buttons. This is set in the
   * LAF properties with the key Button_Position.
   * The valid values are:

   *
left
   *
center
   *
right
   *

   * NOTE: if the alignment is not provided or contains an invalid value, center
   * will be used as a default
   * @return a constant which indicates the button alignment
   */
  protected static int getButtonAlignment() {
    String buttonAlign = BasePropertyHandler.getProperty("Button_Position", "center").toLowerCase(); //$NON-NLS-1$ //$NON-NLS-2$
    if ("center".equals(buttonAlign)) { //$NON-NLS-1$
      return BUTTON_ALIGNMENT_CENTER;
    } else if ("left".equals(buttonAlign)) { //$NON-NLS-1$
      return BUTTON_ALIGNMENT_LEFT;
    } else {
      return BUTTON_ALIGNMENT_RIGHT;
    }
  }

  /**
   * Creats a default FormData object with the top / bottom / and left set (this is done to
   * cut down on repetitive code lines.
   *
   * @param button the button to which this form data will be applied
   * @param width the width of the button
   * @param margin the margin between buttons
   * @param lastControl the last control above the buttons
   * @return the newly created FormData object
   */
  private static FormData createDefaultFormData(Button button, int width, int margin, Control lastControl) {
    FormData formData = new FormData();
    if (lastControl != null) {
      formData.top = new FormAttachment(lastControl, margin * 3);
    } else {
      formData.bottom = new FormAttachment(100, 0);
    }
    formData.right = new FormAttachment(button, width + margin);
    return formData;
  }

  /**
   * Aligns the buttons as left-aligned on the dialog.
   *
   * @param buttons the array of buttons to align
   * @param width the standardized width of all the buttons
   * @param margin the margin between buttons
   * @param lastControl (optional) the bottom most control used for aligning the buttons relative
   * to the bottom of the controls on the dialog
   */
  protected static void leftAlignButtons(Button[] buttons, int width, int margin, Control lastControl) {
    for (int i = 0; i < buttons.length; ++i) {
      FormData formData = createDefaultFormData(buttons[i], width, margin, lastControl);

      // Set the left side of the buttons (either offset from the edge, or relative to the previous button)
      if (i == 0) {
        formData.left = new FormAttachment(0, margin);
      } else {
        formData.left = new FormAttachment(buttons[i - 1], margin);
      }

      // Apply the layout data
      buttons[i].setLayoutData(formData);
    }
  }

  /**
   * Aligns the buttons as right-aligned on the dialog.
   *
   * @param buttons the array of buttons to align
   * @param width the standardized width of all the buttons
   * @param margin the margin between buttons
   * @param lastControl (optional) the bottom most control used for aligning the buttons relative
   * to the bottom of the controls on the dialog
   */
  protected static void rightAlignButtons(Button[] buttons, int width, int margin, Control lastControl) {
    for (int i = buttons.length - 1; i >= 0; --i) {
      FormData formData = createDefaultFormData(buttons[i], width, margin, lastControl);

      // Set the right side of the buttons (either offset from the edge, or relative to the previous button)
      if (i == buttons.length - 1) {
        formData.left = new FormAttachment(100, -(width + margin));
      } else {
        formData.left = new FormAttachment(buttons[i + 1], -(2 * (width + margin)) - margin);
      }

      // Apply the layout data
      buttons[i].setLayoutData(formData);
    }
  }

  /**
   * Aligns the buttons as centered on the dialog.
   *
   * @param buttons the array of buttons to align
   * @param width the standardized width of all the buttons
   * @param margin the margin between buttons
   * @param lastControl (optional) the bottom most control used for aligning the buttons relative
   * to the bottom of the controls on the dialog
   */
  protected static void centerButtons(Button[] buttons, int width, int margin, Control lastControl) {
    // Setup the middle button
    int middleButtonIndex = buttons.length / 2;
    FormData formData = createDefaultFormData(buttons[middleButtonIndex], width, margin, lastControl);

    // See if we have an even or odd number of buttons...
    int leftOffset = 0;
    if (buttons.length % 2 == 0) {
      // Even number of buttons - the middle is between buttons. The "middle" button is
      // actually to the right of middle
      leftOffset = margin;
    } else {
      // Odd number of buttons - tht middle is in the middle of the button
      leftOffset = -(width + margin) / 2;
    }
    formData.left = new FormAttachment(50, leftOffset);
    buttons[middleButtonIndex].setLayoutData(formData);

    // Do the buttons to the right of the middle
    for (int i = middleButtonIndex + 1; i < buttons.length; ++i) {
      formData = createDefaultFormData(buttons[i], width, margin, lastControl);
      formData.left = new FormAttachment(buttons[i - 1], margin);
      buttons[i].setLayoutData(formData);
    }

    // Do the buttons to the left of the middle
    for (int i = middleButtonIndex - 1; i >= 0; --i) {
      formData = createDefaultFormData(buttons[i], width, margin, lastControl);
      formData.left = new FormAttachment(buttons[i + 1], -(2 * (width + margin)) - margin);
      buttons[i].setLayoutData(formData);
    }
  }

  /**
   * Gets the modify listener tooltip text.
   *
   * @param textField the text field
   * @return the modify listener tooltip text
   */
  public static final ModifyListener getModifyListenerTooltipText(final Text textField) {
    return new ModifyListener() {
      public void modifyText(ModifyEvent e) {
        // maybe replace this with extra arguments
        textField.setToolTipText(variables.environmentSubstitute(textField.getText()));
      }
    };
  }

  /**
   * Adds the databases to the Combo Box component.
   *
   * @param wConnection the Combo Box component
   */
  public void addDatabases(CCombo wConnection) {
	  addDatabases(wConnection, null);
  }

  /**
   * Adds the databases with the specified type to the Combo Box component.
   *
   * @param wConnection the Combo Box component
   * @param databaseType the database type
   */
  public void addDatabases(CCombo wConnection, Class<? extends DatabaseInterface> databaseType) {
    for (int i = 0; i < transMeta.nrDatabases(); i++) {
      DatabaseMeta ci = transMeta.getDatabase(i);
      if (databaseType==null || ci.getDatabaseInterface().getClass().equals(databaseType))
      {
    	  wConnection.add(ci.getName());
      }
    }
  }

  /**
   * Selects the database with the specified name in the Combo Box component.
   *
   * @param wConnection the Combo Box component
   * @param name the name of the database to select
   */
  public void selectDatabase(CCombo wConnection, String name) {
    int idx = wConnection.indexOf(name);
    if (idx >= 0) {
      wConnection.select(idx);
    }
  }

  /**
   * Adds the connection line.
   *
   * @param parent the parent UI component
   * @param previous the previous UI component
   * @param middle the middle
   * @param margin the margin
   * @return the the Combo Box component for the given parameters
   */
  public CCombo addConnectionLine(Composite parent, Control previous, int middle, int margin) {
	  return addConnectionLine(parent, previous, middle, margin, null);
	  }

  /**
   * Adds the connection line.
   *
   * @param parent the parent UI component
   * @param previous the previous UI component
   * @param middle the middle
   * @param margin the margin
   * @param databaseType the database type
   * @return the Combo Box component for the given parameters
   */
  public CCombo addConnectionLine(Composite parent, Control previous, int middle, int margin, Class<? extends DatabaseInterface> databaseType) {
    return addConnectionLine(parent, previous, middle, margin, new Label(parent, SWT.RIGHT), new Button(parent,
        SWT.PUSH), new Button(parent, SWT.PUSH), databaseType);
  }

  /**
   * Adds the connection line.
   *
   * @param parent the parent UI component
   * @param previous the previous UI component
   * @param middle the middle
   * @param margin the margin
   * @param wlConnection the connection label
   * @param wbnConnection the "new connection" button
   * @param wbeConnection the "edit connection" button
   * @return the Combo Box component for the given parameters
   */
  public CCombo addConnectionLine(Composite parent, Control previous, int middle, int margin, final Label wlConnection,
	      final Button wbnConnection, final Button wbeConnection) {
	  return addConnectionLine(parent, previous, middle, margin, wlConnection, wbnConnection, wbeConnection, null);
  }

  /**
   * Adds the connection line.
   *
   * @param parent the parent UI component
   * @param previous the previous UI component
   * @param middle the middle
   * @param margin the margin
   * @param wlConnection the connection label
   * @param wbnConnection the "new connection" button
   * @param wbeConnection the "edit connection" button
   * @param databaseType the database type
   * @return the Combo Box component for the given parameters
   */
  public CCombo addConnectionLine(Composite parent, Control previous, int middle, int margin, final Label wlConnection,
      final Button wbnConnection, final Button wbeConnection, final Class<? extends DatabaseInterface> databaseType) {
    final CCombo wConnection;
    final FormData fdlConnection, fdbConnection, fdeConnection, fdConnection;

    wConnection = new CCombo(parent, SWT.BORDER | SWT.READ_ONLY);
    props.setLook(wConnection);

    addDatabases(wConnection);

    wlConnection.setText(BaseMessages.getString(PKG, "BaseStepDialog.Connection.Label")); //$NON-NLS-1$
    props.setLook(wlConnection);
    fdlConnection = new FormData();
    fdlConnection.left = new FormAttachment(0, 0);
    fdlConnection.right = new FormAttachment(middle, -margin);
    if (previous != null)
      fdlConnection.top = new FormAttachment(previous, margin);
    else
      fdlConnection.top = new FormAttachment(0, 0);
    wlConnection.setLayoutData(fdlConnection);

    //
    // NEW button
    //
    wbnConnection.setText(BaseMessages.getString(PKG, "BaseStepDialog.NewConnectionButton.Label")); //$NON-NLS-1$
    wbnConnection.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        DatabaseMeta databaseMeta = new DatabaseMeta();
        databaseMeta.shareVariablesWith(transMeta);
        DatabaseDialog cid = getDatabaseDialog(shell);
        cid.setDatabaseMeta(databaseMeta);
        cid.setModalDialog(true);
        if (cid.open() != null) {
          transMeta.addDatabase(databaseMeta);
          wConnection.removeAll();
          addDatabases(wConnection, databaseType);
          selectDatabase(wConnection, databaseMeta.getName());
        }
      }
    });
    fdbConnection = new FormData();
    fdbConnection.right = new FormAttachment(100, 0);
    if (previous != null)
      fdbConnection.top = new FormAttachment(previous, margin);
    else
      fdbConnection.top = new FormAttachment(0, 0);
    wbnConnection.setLayoutData(fdbConnection);

    //
    // Edit button
    //
    wbeConnection.setText(BaseMessages.getString(PKG, "BaseStepDialog.EditConnectionButton.Label")); //$NON-NLS-1$
    wbeConnection.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        DatabaseMeta databaseMeta = transMeta.findDatabase(wConnection.getText());
        if (databaseMeta != null) {
          databaseMeta.shareVariablesWith(transMeta);

          DatabaseDialog cid = getDatabaseDialog(shell);
          cid.setDatabaseMeta(databaseMeta);
          cid.setModalDialog(true);
          if (cid.open() != null) {
            wConnection.removeAll();
            addDatabases(wConnection);
            selectDatabase(wConnection, databaseMeta.getName());
          }
        }
      }
    });
    fdeConnection = new FormData();
    fdeConnection.right = new FormAttachment(wbnConnection, -margin);
    if (previous != null)
      fdeConnection.top = new FormAttachment(previous, margin);
    else
      fdeConnection.top = new FormAttachment(0, 0);
    wbeConnection.setLayoutData(fdeConnection);

    //
    // what's left of the line: combo box
    //
    fdConnection = new FormData();
    fdConnection.left = new FormAttachment(middle, 0);
    if (previous != null)
      fdConnection.top = new FormAttachment(previous, margin);
    else
      fdConnection.top = new FormAttachment(0, 0);
    fdConnection.right = new FormAttachment(wbeConnection, -margin);
    wConnection.setLayoutData(fdConnection);

    return wConnection;
  }

  /**
   * Gets the database dialog.
   *
   * @param shell the shell
   * @return the database dialog
   */
  protected DatabaseDialog getDatabaseDialog(Shell shell){
    if(databaseDialog == null){
      databaseDialog = new DatabaseDialog(shell);
    }
    return databaseDialog;
  }

  /**
   * Store screen size.
   */
  public void storeScreenSize() {
    props.setScreen(new WindowProperty(shell));
  }

  /**
   * Gets the repository associated with this dialog.
   *
   * @return Returns the repository.
   */
  public Repository getRepository() {
    return repository;
  }

  /**
   * Sets the repository associated with this dialog.
   *
   * @param repository The repository to set.
   */
  public void setRepository(Repository repository) {
    this.repository = repository;
  }

  /**
   * Sets the minimal shell height.
   *
   * @param shell the shell
   * @param controls the controls to measure
   * @param margin the margin between the components
   * @param extra the extra padding
   */
  public static void setMinimalShellHeight(Shell shell, Control[] controls, int margin, int extra) {
    int height = 0;

    for (int i = 0; i < controls.length; i++) {
      Rectangle bounds = controls[i].getBounds();
      height += bounds.height + margin;
    }
    height += extra;
    shell.setSize(shell.getBounds().width, height);
  }

  /**
   * Sets the size of this dialog with respect to the given shell.
   *
   * @param shell the new size
   */
  public static void setSize(Shell shell) {
    setSize(shell, -1, -1, true);
  }

  /**
   * Sets the size of this dialog with respect to the given parameters.
   *
   * @param shell the shell
   * @param minWidth the minimum width
   * @param minHeight the minimum height
   * @param packIt true to pack the dialog components, false otherwise
   */
  public static void setSize(Shell shell, int minWidth, int minHeight, boolean packIt) {
    PropsUI props = PropsUI.getInstance();

    WindowProperty winprop = props.getScreen(shell.getText());
    if (winprop != null) {
      winprop.setShell(shell, minWidth, minHeight);
    } else {
      if (packIt)
        shell.pack();
      else
        shell.layout();

      // OK, sometimes this produces dialogs that are waay too big.
      // Try to limit this a bit, m'kay?
      // Use the same algorithm by cheating :-)
      //
      winprop = new WindowProperty(shell);
      winprop.setShell(shell, minWidth, minHeight);

      // Now, as this is the first time it gets opened, try to put it in the middle of the screen...
      Rectangle shellBounds = shell.getBounds();
      Monitor monitor = shell.getDisplay().getPrimaryMonitor();
      if (shell.getParent() != null)
      {
          monitor = shell.getParent().getMonitor();
      }
      Rectangle monitorClientArea = monitor.getClientArea();

      int middleX = monitorClientArea.x + (monitorClientArea.width - shellBounds.width) / 2;
      int middleY = monitorClientArea.y + (monitorClientArea.height - shellBounds.height) / 2;

      shell.setLocation(middleX, middleY);
    }
  }

  /**
   * Sets the traverse order for the given controls.
   *
   * @param controls the new traverse order
   */
  public static final void setTraverseOrder(final Control[] controls) {
    for (int i = 0; i < controls.length; i++) {
      final int controlNr = i;
      if (i < controls.length - 1) {
        controls[i].addTraverseListener(new TraverseListener() {
          public void keyTraversed(TraverseEvent te) {
            te.doit = false;
            // set focus on the next control.
            // What is the next control?
            int thisOne = controlNr + 1;
            while (!controls[thisOne].isEnabled()) {
              thisOne++;
              if (thisOne >= controls.length)
                thisOne = 0;
              if (thisOne == controlNr)
                return; // already tried all others, time to quit.
            }
            controls[thisOne].setFocus();
          }
        });
      } else // Link last item to first.
      {
        controls[i].addTraverseListener(new TraverseListener() {
          public void keyTraversed(TraverseEvent te) {
            te.doit = false;
            // set focus on the next control.
            // set focus on the next control.
            // What is the next control : 0
            int thisOne = 0;
            while (!controls[thisOne].isEnabled()) {
              thisOne++;
              if (thisOne >= controls.length)
                return; // already tried all others, time to quit.
            }
            controls[thisOne].setFocus();
          }
        });
      }
    }
  }

  /**
   * Gets unused fields from previous steps and inserts them as rows into a table view.
   *
   * @param transMeta the transformation metadata
   * @param stepMeta the step metadata
   * @param tableView the table view
   * @param keyColumn the key column
   * @param nameColumn the name column
   * @param dataTypeColumn the data type column
   * @param lengthColumn the length column
   * @param precisionColumn the precision column
   * @param listener a listener for tables insert events
   */
  public static final void getFieldsFromPrevious(TransMeta transMeta, StepMeta stepMeta, TableView tableView,
      int keyColumn, int nameColumn[], int dataTypeColumn[], int lengthColumn, int precisionColumn,
      TableItemInsertListener listener) {
    try {
      RowMetaInterface row = transMeta.getPrevStepFields(stepMeta);
      if (row != null) {
        getFieldsFromPrevious(row, tableView, keyColumn, nameColumn, dataTypeColumn, lengthColumn, precisionColumn,
            listener);
      }
    } catch (KettleException ke) {
      new ErrorDialog(
          tableView.getShell(),
          BaseMessages.getString(PKG, "BaseStepDialog.FailedToGetFields.Title"), BaseMessages.getString(PKG, "BaseStepDialog.FailedToGetFields.Message", stepMeta.getName()), ke); //$NON-NLS-1$ //$NON-NLS-2$
    }
  }

  /**
   * Gets unused fields from previous steps and inserts them as rows into a table view.
   *
   * @param row the input fields
   * @param tableView the table view to modify
   * @param keyColumn the column in the table view to match with the names of the fields, checks for existance if >0
   * @param nameColumn the column numbers in which the name should end up in
   * @param dataTypeColumn the target column numbers in which the data type should end up in
   * @param lengthColumn the length column where the length should end up in (if >0)
   * @param precisionColumn the length column where the precision should end up in (if >0)
   * @param listener A listener that you can use to do custom modifications to the inserted table item, based on a value from the provided row
   */
  public static final void getFieldsFromPrevious(RowMetaInterface row, TableView tableView, int keyColumn,
      int nameColumn[], int dataTypeColumn[], int lengthColumn, int precisionColumn, TableItemInsertListener listener) {
    if (row == null || row.size() == 0)
      return; // nothing to do

    Table table = tableView.table;

    // get a list of all the non-empty keys (names)
    //
    List<String> keys = new ArrayList<String>();
    for (int i = 0; i < table.getItemCount(); i++) {
      TableItem tableItem = table.getItem(i);
      String key = tableItem.getText(keyColumn);
      if (!Const.isEmpty(key) && keys.indexOf(key) < 0)
        keys.add(key);
    }

    int choice = 0;

    if (keys.size() > 0) {
      // Ask what we should do with the existing data in the step.
      //
      MessageDialog md = new MessageDialog(tableView.getShell(), BaseMessages.getString(PKG, "BaseStepDialog.GetFieldsChoice.Title"),//"Warning!"  //$NON-NLS-1$
          null, BaseMessages.getString(PKG, "BaseStepDialog.GetFieldsChoice.Message", "" + keys.size(), "" + row.size()), //$NON-NLS-1$  //$NON-NLS-2$  //$NON-NLS-3$
          MessageDialog.WARNING, new String[] { BaseMessages.getString(PKG, "BaseStepDialog.AddNew"), //$NON-NLS-1$
              BaseMessages.getString(PKG, "BaseStepDialog.Add"), BaseMessages.getString(PKG, "BaseStepDialog.ClearAndAdd"), //$NON-NLS-1$  //$NON-NLS-2$
              BaseMessages.getString(PKG, "BaseStepDialog.Cancel"), }, 0); //$NON-NLS-1$
      MessageDialog.setDefaultImage(GUIResource.getInstance().getImageSpoon());
      int idx = md.open();
      choice = idx & 0xFF;
    }

    if (choice == 3 || choice == 255 /* 255 = escape pressed */)
      return; // Cancel clicked

    if (choice == 2) {
      tableView.clearAll(false);
    }

    for (int i = 0; i < row.size(); i++) {
      ValueMetaInterface v = row.getValueMeta(i);

      boolean add = true;

      if (choice == 0) // hang on, see if it's not yet in the table view
      {
        if (keys.indexOf(v.getName()) >= 0)
          add = false;
      }

      if (add) {
        TableItem tableItem = new TableItem(table, SWT.NONE);

        for (int c = 0; c < nameColumn.length; c++) {
          tableItem.setText(nameColumn[c], Const.NVL(v.getName(), "")); //$NON-NLS-1$
        }
        if ( dataTypeColumn != null )
        {
            for (int c = 0; c < dataTypeColumn.length; c++) {
                tableItem.setText(dataTypeColumn[c], v.getTypeDesc());
            }
        }
        if (lengthColumn > 0) {
          if (v.getLength() >= 0)
            tableItem.setText(lengthColumn, Integer.toString(v.getLength()));
        }
        if (precisionColumn > 0) {
          if (v.getPrecision() >= 0)
            tableItem.setText(precisionColumn, Integer.toString(v.getPrecision()));
        }

        if (listener != null) {
          if (!listener.tableItemInserted(tableItem, v)) {
            tableItem.dispose(); // remove it again
          }
        }
      }
    }
    tableView.removeEmptyRows();
    tableView.setRowNums();
    tableView.optWidth(true);
  }

  /**
   * Gets fields from previous steps and populate a ComboVar.
   *
   * @param comboVar the Combo Box (with Variables) to populate
   * @param transMeta the transformation metadata
   * @param stepMeta the step metadata
   */
  public static final void getFieldsFromPrevious(ComboVar comboVar,TransMeta transMeta,StepMeta stepMeta)
	 {
		 String selectedField=null;
		 int indexField=-1;
		 try{
			 RowMetaInterface r = transMeta.getPrevStepFields(stepMeta);
			 selectedField=comboVar.getText();
			 comboVar.removeAll();

			 if (r!=null && !r.isEmpty()) {
	             r.getFieldNames();
	             comboVar.setItems(r.getFieldNames());
	             indexField=r.indexOfValue(selectedField);
			 }
			 // Select value if possible...
			 if(indexField>-1) comboVar.select(indexField); else { if(selectedField!=null) comboVar.setText(selectedField);};
		 }catch(KettleException ke){
				new ErrorDialog(comboVar.getShell(),BaseMessages.getString(PKG, "BaseStepDialog.FailedToGetFieldsPrevious.DialogTitle"),
						BaseMessages.getString(PKG, "BaseStepDialog.FailedToGetFieldsPrevious.DialogMessage"),ke);
			}
	 }

	/**
	 * Create a new field mapping between source and target steps.
	 *
	 * @param shell the shell of the parent window
	 * @param sourceFields the source fields
	 * @param targetFields the target fields
	 * @param fieldMapping the list of source to target mappings to default to (can be empty but not null)
	 *
	 * @throws KettleException in case something goes wrong during the field mapping
	 *
	 */
	public static final void generateFieldMapping(Shell shell, RowMetaInterface sourceFields, RowMetaInterface targetFields, List<SourceToTargetMapping> fieldMapping) throws KettleException {
		// Build the mapping: let the user decide!!
		String[] source = sourceFields.getFieldNames();
		for (int i = 0; i < source.length; i++) {
			ValueMetaInterface v = sourceFields.getValueMeta(i);
			source[i] += EnterMappingDialog.STRING_ORIGIN_SEPARATOR + v.getOrigin() + ")";
		}
		String[] target = targetFields.getFieldNames();

		EnterMappingDialog dialog = new EnterMappingDialog(shell, source, target, fieldMapping);
		List<SourceToTargetMapping> newMapping = dialog.open();
		if (newMapping!=null) {
			fieldMapping.clear();
			fieldMapping.addAll(newMapping);
		}
	}

    /**
     * Checks if the log level is basic.
     *
     * @return true, if the log level is basic, false otherwise
     */
    public boolean isBasic() { return log.isBasic(); }

    /**
     * Checks if the log level is detailed.
     *
     * @return true, if the log level is detailed, false otherwise
     */
    public boolean isDetailed() { return log.isDetailed(); }

    /**
     * Checks if the log level is debug.
     *
     * @return true, if the log level is debug, false otherwise
     */
    public boolean isDebug() { return log.isDebug(); }

    /**
     * Checks if the log level is row level.
     *
     * @return true, if the log level is row level, false otherwise
     */
    public boolean isRowLevel() { return log.isRowLevel(); }

    /**
     * Log the message at a minimal logging level.
     *
     * @param message the message to log
     */
    public void logMinimal(String message) { log.logMinimal(message); }

    /**
     * Log the message with arguments at a minimal logging level.
     *
     * @param message the message
     * @param arguments the arguments
     */
    public void logMinimal(String message, Object...arguments) { log.logMinimal(message, arguments); }

    /**
     * Log the message at a basic logging level.
     *
     * @param message the message
     */
    public void logBasic(String message) { log.logBasic(message); }

    /**
     * Log the message with arguments at a basic logging level.
     *
     * @param message the message
     * @param arguments the arguments
     */
    public void logBasic(String message, Object...arguments) { log.logBasic(message, arguments); }

    /**
     * Log the message at a detailed logging level.
     *
     * @param message the message
     */
    public void logDetailed(String message) { log.logDetailed(message); }

    /**
     * Log the message with arguments at a detailed logging level.
     *
     * @param message the message
     * @param arguments the arguments
     */
    public void logDetailed(String message, Object...arguments) { log.logDetailed(message, arguments); }

    /**
     * Log the message at a debug logging level.
     *
     * @param message the message
     */
    public void logDebug(String message) { log.logDebug(message); }

    /**
     * Log the message with arguments at a debug logging level.
     *
     * @param message the message
     * @param arguments the arguments
     */
    public void logDebug(String message, Object...arguments) { log.logDebug(message, arguments); }

    /**
     * Log the message at a rowlevel logging level.
     *
     * @param message the message
     */
    public void logRowlevel(String message) { log.logRowlevel(message); }

    /**
     * Log the message with arguments at a rowlevel logging level.
     *
     * @param message the message
     * @param arguments the arguments
     */
    public void logRowlevel(String message, Object...arguments) { log.logRowlevel(message, arguments); }

    /**
     * Log the message at a error logging level.
     *
     * @param message the message
     */
    public void logError(String message) { log.logError(message); }

    /**
     * Log the message with the associated Throwable object at a error logging level.
     *
     * @param message the message
     * @param e the e
     */
    public void logError(String message, Throwable e) { log.logError(message, e); }

    /**
     * Log the message with arguments at a error logging level.
     *
     * @param message the message
     * @param arguments the arguments
     */
    public void logError(String message, Object...arguments) { log.logError(message, arguments); }

}


text:
foo.bar

www.foobar

www.foobar.com

http://foobar.com

https://www.foobar.com/baz?bazinga=nhecos;

redpajama_set_name: RedPajamaGithub


text:
require "rails_helper"

describe SearchablePost, solr: true do
  let(:discussion) { create(:discussion, body: "testing discussion") }
  let(:conversation) { create(:conversation, body: "testing conversation") }
  let!(:post) { create(:post, body: "testing post") }
  let!(:trusted_post) { create(:trusted_post, body: "testing trusted post") }
  let!(:discussion_post) { discussion.posts.first }
  let!(:conversation_post) { conversation.posts.first }
  let(:user) { create(:user) }
  let(:trusted_user) { create(:trusted_user) }

  describe ".search_results" do
    before { Sunspot.commit }

    describe "searching all posts" do
      context "as nobody" do
        subject { Post.search_results("testing", user: nil, page: 1) }
        it { is_expected.to match_array([post, discussion_post]) }
      end

      context "as a regular user" do
        subject { Post.search_results("testing", user: user, page: 1) }
        it { is_expected.to match_array([post, discussion_post]) }
      end

      context "as a trusted user" do
        subject { Post.search_results("testing", user: trusted_user, page: 1) }
        it { is_expected.to match_array([post, discussion_post, trusted_post]) }
      end
    end

    describe "searching in a discussion" do
      context "as nobody" do
        subject do
          Post.search_results(
            "testing", user: nil, page: 1, exchange: discussion
          )
        end
        it { is_expected.to match_array([discussion_post]) }
      end

      context "as a regular user" do
        subject do
          Post.search_results(
            "testing", user: user, page: 1, exchange: discussion
          )
        end
        it { is_expected.to match_array([discussion_post]) }
      end
    end

    describe "searching in a conversation" do
      subject do
        Post.search_results(
          "testing", user: user, page: 1, exchange: conversation
        )
      end
      it { is_expected.to match_array([conversation_post]) }
    end
  end
end


text:
using System.Composition;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.Maintainability.Analyzers;

namespace Microsoft.Maintainability.CSharp.Analyzers
{
    /// <summary>
    /// CA1500: Variable names should not match field names
    /// </summary>
    [ExportCodeFixProvider(LanguageNames.CSharp), Shared]
    public sealed class CSharpVariableNamesShouldNotMatchFieldNamesFixer : VariableNamesShouldNotMatchFieldNamesFixer
    {
    }
}


text:
import {expect} from 'chai';
import * as Rx from '../../dist/cjs/Rx';
declare const {hot, cold, asDiagram, expectObservable, expectSubscriptions};

const Observable = Rx.Observable;
const Subject = Rx.Subject;

/** @test {do} */
describe('Observable.prototype.do', () => {
  asDiagram('do(x => console.log(x))')('should mirror multiple values and complete', () => {
    const e1 =  cold('--1--2--3--|');
    const e1subs =   '^          !';
    const expected = '--1--2--3--|';

    const result = e1.do(() => {
      //noop
    });
    expectObservable(result).toBe(expected);
    expectSubscriptions(e1.subscriptions).toBe(e1subs);
  });

  it('should next with a callback', () => {
    let value = null;
    Observable.of(42).do(function (x) {
      value = x;
    })
    .subscribe();

    expect(value).to.equal(42);
  });

  it('should error with a callback', () => {
    let err = null;
    Observable.throw('bad').do(null, function (x) {
      err = x;
    })
    .subscribe(null, function (ex) {
      expect(ex).to.equal('bad');
    });

    expect(err).to.equal('bad');
  });

  it('should handle everything with an observer', (done: MochaDone) => {
    const expected = [1, 2, 3];
    const results = [];

    Observable.of(1, 2, 3)
      .do(<Rx.Observer<number>>{
        next: (x: number) => {
          results.push(x);
        },
        error: (err: any) => {
          done(new Error('should not be called'));
        },
        complete: () => {
          expect(results).to.deep.equal(expected);
          done();
        }
      }).subscribe();
  });

  it('should handle everything with a Subject', (done: MochaDone) => {
    const expected = [1, 2, 3];
    const results = [];
    const subject = new Subject();

    subject.subscribe({
      next: (x: any) => {
        results.push(x);
      },
      error: (err: any) => {
        done(new Error('should not be called'));
      },
      complete: () => {
        expect(results).to.deep.equal(expected);
        done();
      }
    });

    Observable.of(1, 2, 3)
      .do(<Rx.Observer<number>>subject)
      .subscribe();
  });

  it('should handle an error with a callback', () => {
    let errored = false;
    Observable.throw('bad').do(null, (err: any) => {
      expect(err).to.equal('bad');
    })
    .subscribe(null, (err: any) => {
      errored = true;
      expect(err).to.equal('bad');
    });

    expect(errored).to.be.true;
  });

  it('should handle an error with observer', () => {
    let errored = false;
    Observable.throw('bad').do(<any>{ error: function (err) {
      expect(err).to.equal('bad');
    } })
    .subscribe(null, function (err) {
      errored = true;
      expect(err).to.equal('bad');
    });

    expect(errored).to.be.true;
  });

  it('should handle complete with observer', () => {
    let completed = false;

    Observable.empty().do(<any>{
      complete: () => {
        completed = true;
      }
    }).subscribe();

    expect(completed).to.be.true;
  });

  it('should handle next with observer', () => {
    let value = null;

    Observable.of('hi').do(<any>{
      next: (x: string) => {
        value = x;
      }
    }).subscribe();

    expect(value).to.equal('hi');
  });

  it('should raise error if next handler raises error', () => {
    Observable.of('hi').do(<any>{
      next: (x: string) => {
        throw new Error('bad');
      }
    }).subscribe(null, (err: any) => {
      expect(err.message).to.equal('bad');
    });
  });

  it('should raise error if error handler raises error', () => {
    Observable.throw('ops').do(<any>{
      error: (x: any) => {
        throw new Error('bad');
      }
    }).subscribe(null, (err: any) => {
      expect(err.message).to.equal('bad');
    });
  });

  it('should raise error if complete handler raises error', () => {
    Observable.empty().do(<any>{
      complete: () => {
        throw new Error('bad');
      }
    }).subscribe(null, (err: any) => {
      expect(err.message).to.equal('bad');
    });
  });

  it('should allow unsubscribing explicitly and early', () => {
    const e1 =   hot('--1--2--3--#');
    const unsub =    '       !    ';
    const e1subs =   '^      !    ';
    const expected = '--1--2--    ';

    const result = e1.do(() => {
      //noop
    });
    expectObservable(result, unsub).toBe(expected);
    expectSubscriptions(e1.subscriptions).toBe(e1subs);
  });

  it('should not break unsubscription chains when result is unsubscribed explicitly', () => {
    const e1 =   hot('--1--2--3--#');
    const e1subs =   '^      !    ';
    const expected = '--1--2--    ';
    const unsub =    '       !    ';

    const result = e1
      .mergeMap((x: any) => Observable.of(x))
      .do(() => {
        //noop
      })
      .mergeMap((x: any) => Observable.of(x));

    expectObservable(result, unsub).toBe(expected);
    expectSubscriptions(e1.subscriptions).toBe(e1subs);
  });

  it('should mirror multiple values and complete', () => {
    const e1 =  cold('--1--2--3--|');
    const e1subs =   '^          !';
    const expected = '--1--2--3--|';

    const result = e1.do(() => {
      //noop
    });
    expectObservable(result).toBe(expected);
    expectSubscriptions(e1.subscriptions).toBe(e1subs);
  });

  it('should mirror multiple values and terminate with error', () => {
    const e1 =  cold('--1--2--3--#');
    const e1subs =   '^          !';
    const expected = '--1--2--3--#';

    const result = e1.do(() => {
      //noop
    });
    expectObservable(result).toBe(expected);
    expectSubscriptions(e1.subscriptions).toBe(e1subs);
  });
});


text:
package kr.co.google.nougat.fix.fixme;

import android.content.Intent;
import android.os.Bundle;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

public class SplashActivity extends AppCompatActivity {

    private static boolean initFlag = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);
    }

    @Override
    protected void onStart() {
        super.onStart();
        initLibraries();

        if (!initFlag) {
            startAppLoading();
        } else {
            finish();
        }
    }

    //initiate network library
    private void initLibraries() {
        MyNetworkLibrary.initiate();
    }

    //loading animation
    private void startAppLoading() {

        initFlag = true;

        final TextView loadingText = (TextView) findViewById(R.id.loading_text);

        new Thread() {
            @Override
            public void run() {
                int loadingCt = 0;
                while (loadingCt < 100) {
                    SystemClock.sleep(30);
                    ++loadingCt;
                    final int finalLoadingCt = loadingCt;
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            loadingText.setText(finalLoadingCt + "%");
                        }
                    });
                }

                //finish loading.
                finish();
                startActivity(new Intent(SplashActivity.this, MainActivity.class));

            }
        }.start();

    }

    //jump to the main activity.
}


text:
'use strict';

var getContentStateFragment = require('getContentStateFragment');

import type {BlockMap} from 'BlockMap';
import type EditorState from 'EditorState';

function getFragmentFromSelection(editorState: EditorState): ?BlockMap {
  var selectionState = editorState.getSelection();
  if (!selectionState.isCollapsed()) {
    return getContentStateFragment(
      editorState.getCurrentContent(),
      selectionState
    );
  }
}

module.exports = getFragmentFromSelection;


text:
package com.warp.android.utils;

import com.google.gson.internal.LinkedTreeMap;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * Created by TrieNoir on 24/01/2017.
 */

public class Deserializer {

    @SuppressWarnings("unchecked")
    public static DataMap serialize(DataMap dataMap) {
        DataMap map = new DataMap();

        Iterator<Map.Entry<String, Object>> it = dataMap.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry<String, Object> entry = it.next();
            Object object = entry.getValue();
            if (object instanceof LinkedTreeMap) {

                LinkedTreeMap<String,Object> treeMap = (LinkedTreeMap<String,Object>) object;

                HashMap<String,Object> mapper = new HashMap<>(treeMap);
                mapper.putAll(treeMap);

                map.put(entry.getKey(), mapper);

            } else {
                map.put(entry.getKey(), object);
            }
        }

        return map;
    }

}


text:
'use strict';
var Promise = require('promise');

/**
 * A ES6-Promisified XHR request class to make ajax calls.
 * @module request
 * @type {exports}
 * @param {String} url - The url to be requested
 * @param {Object} [options] - The options
 * @param {String} [options.method] - The method (i.e. "GET", "POST"), defaults to GET
 * @param {Object} [options.headers] - Any headers to send with request
 * @param {Boolean} [options.async] - Whether this is an asynchronous call (default to true)
 * @returns {Promise}
 * @TODO: make this return a promise that follows ES6 Promise syntax
 */
module.exports = function (url, options) {
    var client = new XMLHttpRequest();

    options = options || {};
    options.method = options.method || 'GET';
    options.headers = options.headers || {};
    options.async = typeof options.async === 'undefined' ? true : options.async;

    return new Promise(
        function (resolve, reject) {
            // open connection
            client.open(options.method, url);

            // deal with headers
            for (var i in options.headers) {
                if (options.headers.hasOwnProperty(i)) {
                    client.setRequestHeader(i, options.headers[i]);
                }
            }
            // listener
            client.onreadystatechange = function() {
                if (this.readyState == 4 && this.status == 200) {
                    resolve.call(this, this.responseText);
                } else if (this.readyState == 4) {
                    reject.call(this, this.status, this.statusText);
                }
            };
            // send off
            client.send(options.data);
        });

};


text:
Release Notes
=============

v0.2.0
------

* Automatic DePaging of results from ``gagrab.Grabber.query``.

v0.1
----

* This is the initial release of ``gagrab``. It includes the basic
  functionality for setting up a ``Grabber`` object, querying google
  analytics, and converting the output to a convenient format.


text:
/* *
 *
 *  Imports
 *
 * */
import Color from '../../Core/Color/Color.js';
var color = Color.parse;
import H from '../../Core/Globals.js';
var charts = H.charts, _a = H.Renderer.prototype, cuboidPath = _a.cuboidPath, Elements3D = _a.elements3d;
import U from '../../Core/Utilities.js';
var error = U.error, extend = U.extend, merge = U.merge;
import '../../Core/Renderer/SVG/SVGRenderer.js';
/* *
 *
 *  Composition
 *
 * */
/* eslint-disable valid-jsdoc */
Elements3D.funnel3d = merge(Elements3D.cuboid, {
    parts: [
        'top', 'bottom',
        'frontUpper', 'backUpper',
        'frontLower', 'backLower',
        'rightUpper', 'rightLower'
    ],
    mainParts: ['top', 'bottom'],
    sideGroups: [
        'upperGroup', 'lowerGroup'
    ],
    sideParts: {
        upperGroup: ['frontUpper', 'backUpper', 'rightUpper'],
        lowerGroup: ['frontLower', 'backLower', 'rightLower']
    },
    pathType: 'funnel3d',
    // override opacity and color setters to control opacity
    opacitySetter: function (opacity) {
        var funnel3d = this, parts = funnel3d.parts, chart = H.charts[funnel3d.renderer.chartIndex], filterId = 'group-opacity-' + opacity + '-' + chart.index;
        // use default for top and bottom
        funnel3d.parts = funnel3d.mainParts;
        funnel3d.singleSetterForParts('opacity', opacity);
        // restore
        funnel3d.parts = parts;
        if (!chart.renderer.filterId) {
            chart.renderer.definition({
                tagName: 'filter',
                attributes: {
                    id: filterId
                },
                children: [{
                        tagName: 'feComponentTransfer',
                        children: [{
                                tagName: 'feFuncA',
                                attributes: {
                                    type: 'table',
                                    tableValues: '0 ' + opacity
                                }
                            }]
                    }]
            });
            funnel3d.sideGroups.forEach(function (groupName) {
                funnel3d[groupName].attr({
                    filter: 'url(#' + filterId + ')'
                });
            });
            // styled mode
            if (funnel3d.renderer.styledMode) {
                chart.renderer.definition({
                    tagName: 'style',
                    textContent: '.highcharts-' + filterId +
                        ' {filter:url(#' + filterId + ')}'
                });
                funnel3d.sideGroups.forEach(function (group) {
                    group.addClass('highcharts-' + filterId);
                });
            }
        }
        return funnel3d;
    },
    fillSetter: function (fill) {
        // extract alpha channel to use the opacitySetter
        var funnel3d = this, fillColor = color(fill), alpha = fillColor.rgba[3], partsWithColor = {
            // standard color for top and bottom
            top: color(fill).brighten(0.1).get(),
            bottom: color(fill).brighten(-0.2).get()
        };
        if (alpha < 1) {
            fillColor.rgba[3] = 1;
            fillColor = fillColor.get('rgb');
            // set opacity through the opacitySetter
            funnel3d.attr({
                opacity: alpha
            });
        }
        else {
            // use default for full opacity
            fillColor = fill;
        }
        // add gradient for sides
        if (!fillColor.linearGradient &&
            !fillColor.radialGradient &&
            funnel3d.gradientForSides) {
            fillColor = {
                linearGradient: { x1: 0, x2: 1, y1: 1, y2: 1 },
                stops: [
                    [0, color(fill).brighten(-0.2).get()],
                    [0.5, fill],
                    [1, color(fill).brighten(-0.2).get()]
                ]
            };
        }
        // gradient support
        if (fillColor.linearGradient) {
            // color in steps, as each gradient will generate a key
            funnel3d.sideGroups.forEach(function (sideGroupName) {
                var box = funnel3d[sideGroupName].gradientBox, gradient = fillColor.linearGradient, alteredGradient = merge(fillColor, {
                    linearGradient: {
                        x1: box.x + gradient.x1 * box.width,
                        y1: box.y + gradient.y1 * box.height,
                        x2: box.x + gradient.x2 * box.width,
                        y2: box.y + gradient.y2 * box.height
                    }
                });
                funnel3d.sideParts[sideGroupName].forEach(function (partName) {
                    partsWithColor[partName] = alteredGradient;
                });
            });
        }
        else {
            merge(true, partsWithColor, {
                frontUpper: fillColor,
                backUpper: fillColor,
                rightUpper: fillColor,
                frontLower: fillColor,
                backLower: fillColor,
                rightLower: fillColor
            });
            if (fillColor.radialGradient) {
                funnel3d.sideGroups.forEach(function (sideGroupName) {
                    var gradBox = funnel3d[sideGroupName].gradientBox, centerX = gradBox.x + gradBox.width / 2, centerY = gradBox.y + gradBox.height / 2, diameter = Math.min(gradBox.width, gradBox.height);
                    funnel3d.sideParts[sideGroupName].forEach(function (partName) {
                        funnel3d[partName].setRadialReference([
                            centerX, centerY, diameter
                        ]);
                    });
                });
            }
        }
        funnel3d.singleSetterForParts('fill', null, partsWithColor);
        // fill for animation getter (#6776)
        funnel3d.color = funnel3d.fill = fill;
        // change gradientUnits to userSpaceOnUse for linearGradient
        if (fillColor.linearGradient) {
            [funnel3d.frontLower, funnel3d.frontUpper].forEach(function (part) {
                var elem = part.element, grad = elem && funnel3d.renderer.gradients[elem.gradient];
                if (grad && grad.attr('gradientUnits') !== 'userSpaceOnUse') {
                    grad.attr({
                        gradientUnits: 'userSpaceOnUse'
                    });
                }
            });
        }
        return funnel3d;
    },
    adjustForGradient: function () {
        var funnel3d = this, bbox;
        funnel3d.sideGroups.forEach(function (sideGroupName) {
            // use common extremes for groups for matching gradients
            var topLeftEdge = {
                x: Number.MAX_VALUE,
                y: Number.MAX_VALUE
            }, bottomRightEdge = {
                x: -Number.MAX_VALUE,
                y: -Number.MAX_VALUE
            };
            // get extremes
            funnel3d.sideParts[sideGroupName].forEach(function (partName) {
                var part = funnel3d[partName];
                bbox = part.getBBox(true);
                topLeftEdge = {
                    x: Math.min(topLeftEdge.x, bbox.x),
                    y: Math.min(topLeftEdge.y, bbox.y)
                };
                bottomRightEdge = {
                    x: Math.max(bottomRightEdge.x, bbox.x + bbox.width),
                    y: Math.max(bottomRightEdge.y, bbox.y + bbox.height)
                };
            });
            // store for color fillSetter
            funnel3d[sideGroupName].gradientBox = {
                x: topLeftEdge.x,
                width: bottomRightEdge.x - topLeftEdge.x,
                y: topLeftEdge.y,
                height: bottomRightEdge.y - topLeftEdge.y
            };
        });
    },
    zIndexSetter: function () {
        // this.added won't work, because zIndex is set after the prop is set,
        // but before the graphic is really added
        if (this.finishedOnAdd) {
            this.adjustForGradient();
        }
        // run default
        return this.renderer.Element.prototype.zIndexSetter.apply(this, arguments);
    },
    onAdd: function () {
        this.adjustForGradient();
        this.finishedOnAdd = true;
    }
});
extend(H.Renderer.prototype, {
    funnel3d: function (shapeArgs) {
        var renderer = this, funnel3d = renderer.element3d('funnel3d', shapeArgs), styledMode = renderer.styledMode,
        // hide stroke for Firefox
        strokeAttrs = {
            'stroke-width': 1,
            stroke: 'none'
        };
        // create groups for sides for oppacity setter
        funnel3d.upperGroup = renderer.g('funnel3d-upper-group').attr({
            zIndex: funnel3d.frontUpper.zIndex
        }).add(funnel3d);
        [
            funnel3d.frontUpper,
            funnel3d.backUpper,
            funnel3d.rightUpper
        ].forEach(function (upperElem) {
            if (!styledMode) {
                upperElem.attr(strokeAttrs);
            }
            upperElem.add(funnel3d.upperGroup);
        });
        funnel3d.lowerGroup = renderer.g('funnel3d-lower-group').attr({
            zIndex: funnel3d.frontLower.zIndex
        }).add(funnel3d);
        [
            funnel3d.frontLower,
            funnel3d.backLower,
            funnel3d.rightLower
        ].forEach(function (lowerElem) {
            if (!styledMode) {
                lowerElem.attr(strokeAttrs);
            }
            lowerElem.add(funnel3d.lowerGroup);
        });
        funnel3d.gradientForSides = shapeArgs.gradientForSides;
        return funnel3d;
    },
    /**
     * Generates paths and zIndexes.
     * @private
     */
    funnel3dPath: function (shapeArgs // @todo: Type it. It's an extended SVGAttributes.
    ) {
        // Check getCylinderEnd for better error message if
        // the cylinder module is missing
        if (!this.getCylinderEnd) {
            error('A required Highcharts module is missing: cylinder.js', true, charts[this.chartIndex]);
        }
        var renderer = this, chart = charts[renderer.chartIndex],
        // adjust angles for visible edges
        // based on alpha, selected through visual tests
        alphaCorrection = shapeArgs.alphaCorrection = 90 -
            Math.abs((chart.options.chart.options3d.alpha % 180) - 90),
        // set zIndexes of parts based on cubiod logic, for consistency
        cuboidData = cuboidPath.call(renderer, merge(shapeArgs, {
            depth: shapeArgs.width,
            width: (shapeArgs.width + shapeArgs.bottom.width) / 2
        })), isTopFirst = cuboidData.isTop, isFrontFirst = !cuboidData.isFront, hasMiddle = !!shapeArgs.middle,
        //
        top = renderer.getCylinderEnd(chart, merge(shapeArgs, {
            x: shapeArgs.x - shapeArgs.width / 2,
            z: shapeArgs.z - shapeArgs.width / 2,
            alphaCorrection: alphaCorrection
        })), bottomWidth = shapeArgs.bottom.width, bottomArgs = merge(shapeArgs, {
            width: bottomWidth,
            x: shapeArgs.x - bottomWidth / 2,
            z: shapeArgs.z - bottomWidth / 2,
            alphaCorrection: alphaCorrection
        }), bottom = renderer.getCylinderEnd(chart, bottomArgs, true),
        //
        middleWidth = bottomWidth, middleTopArgs = bottomArgs, middleTop = bottom, middleBottom = bottom, ret,
        // masking for cylinders or a missing part of a side shape
        useAlphaCorrection;
        if (hasMiddle) {
            middleWidth = shapeArgs.middle.width;
            middleTopArgs = merge(shapeArgs, {
                y: shapeArgs.y + shapeArgs.middle.fraction * shapeArgs.height,
                width: middleWidth,
                x: shapeArgs.x - middleWidth / 2,
                z: shapeArgs.z - middleWidth / 2
            });
            middleTop = renderer.getCylinderEnd(chart, middleTopArgs, false);
            middleBottom = renderer.getCylinderEnd(chart, middleTopArgs, false);
        }
        ret = {
            top: top,
            bottom: bottom,
            frontUpper: renderer.getCylinderFront(top, middleTop),
            zIndexes: {
                group: cuboidData.zIndexes.group,
                top: isTopFirst !== 0 ? 0 : 3,
                bottom: isTopFirst !== 1 ? 0 : 3,
                frontUpper: isFrontFirst ? 2 : 1,
                backUpper: isFrontFirst ? 1 : 2,
                rightUpper: isFrontFirst ? 2 : 1
            }
        };
        ret.backUpper = renderer.getCylinderBack(top, middleTop);
        useAlphaCorrection = (Math.min(middleWidth, shapeArgs.width) /
            Math.max(middleWidth, shapeArgs.width)) !== 1;
        ret.rightUpper = renderer.getCylinderFront(renderer.getCylinderEnd(chart, merge(shapeArgs, {
            x: shapeArgs.x - shapeArgs.width / 2,
            z: shapeArgs.z - shapeArgs.width / 2,
            alphaCorrection: useAlphaCorrection ? -alphaCorrection : 0
        }), false), renderer.getCylinderEnd(chart, merge(middleTopArgs, {
            alphaCorrection: useAlphaCorrection ? -alphaCorrection : 0
        }), !hasMiddle));
        if (hasMiddle) {
            useAlphaCorrection = (Math.min(middleWidth, bottomWidth) /
                Math.max(middleWidth, bottomWidth)) !== 1;
            merge(true, ret, {
                frontLower: renderer.getCylinderFront(middleBottom, bottom),
                backLower: renderer.getCylinderBack(middleBottom, bottom),
                rightLower: renderer.getCylinderFront(renderer.getCylinderEnd(chart, merge(bottomArgs, {
                    alphaCorrection: useAlphaCorrection ?
                        -alphaCorrection : 0
                }), true), renderer.getCylinderEnd(chart, merge(middleTopArgs, {
                    alphaCorrection: useAlphaCorrection ?
                        -alphaCorrection : 0
                }), false)),
                zIndexes: {
                    frontLower: isFrontFirst ? 2 : 1,
                    backLower: isFrontFirst ? 1 : 2,
                    rightLower: isFrontFirst ? 1 : 2
                }
            });
        }
        return ret;
    }
});
/* eslint-enable valid-jsdoc */


text:
<!DOCTYPE html>

    <meta charset="UTF-8">
    <title>BiggestOfThreeAndSort</title>

Enter positive  number between 0 and 999!

A
<input type="text" id="tb-first"/>

click me for result

The number in words is: