#usage "en: <b>DesignLink </b>"
           "<p>"
           "This is an include ULP needed from other DesignLink ULPs.<br>"
           "<author>Author: librarian@cadsoft.de</author>"

// THIS PROGRAM IS PROVIDED AS IS AND WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED

#require 5.1001

string DIVersion = "1.0.7"; // 2010-07-27  reduce global variables
                            // 2010-08-12  1.0.1 alf@cadsoft.de
                            // 2010-08-15  1.0.2 alf@cadsoft.de
                            //             techpacvar() berichtigt bei benutzter Platzhalterkombination "*?".
                            // 2010-08-24  1.0.3 alf@cadsoft.de
                            // 2010-08-30  1.0.4 alf@cadsoft.de

string DILanguage = language();
string DICountry = country();

if (DICountry == "GB") {
   // According to ISO 3166 (http://www.iso.org/iso/english_country_names_and_code_elements)
   // there is no "en_UK". Computers that use "English/Great Britain" have "en_GB".
   // We set DICountry to the fake value of "UK" to work around this misconception in DesignLink:
   DICountry = "UK";
   }

// Debug stuff
int    DIDebug = 0;
int    DIDebugXfer = 0; // Debug data transfer
string DIDbgFile;
string DIDbgMsg;

// Configuration from eaglerc.usr
int    DIInStock = cfgget("ULP:designlink.InStock") == "1";
int    DIRoHS    = cfgget("ULP:designlink.RoHS")    == "1";
int    DIResultsPerPage = strtol(cfgget("ULP:designlink.ResultsPerPage"));

// Dummy credentials
string DICustomerID = "CadSoft2";
string DICustomerPW = "CadSoft2";

string DICompany = "";                 // Farnell or Newark
string DICurrency;
string DISearchURL;
string DIShoppingURL;

int    DInProducts;                    // number of products
string DIProducts[];                   // Products returned from PF server (XML strings)
numeric string DIProductList[];        // Products converted to a list for displaying (part of information)
int    DIProductSel = -1;              // Product currently selected
int    DIResultsPerPageIndex = 0;
string DIRecentSearch;
int    DIResultGroupIndex = 0;
string DIResultsPerPageValues[] = { "10", "25", "50" }; // Farnell's Design Link can't provide more than 50 items at a single blow
int    DIResultOffset = 0;
string DIHtml = "";
string DIAdvice = "";                  // Advice for user needed In some cases (shopping cart or library processing)
                                       // displayed when no match
string DISearchResult;                 // Total result returned from PF server (XML string)
string DIResultTotal;                  // Total nr. of search results
string DIResultGroupArray[];           // stringlist for CBox for selecting a page of search results
int    DIMode = 1;                     // to redisplay dialog
int    DISkipRem = 0;                  // Switch, to skip the rest of the search through schematic or library
int    DICancelSearch = 0;
int    DIShow = 0;                     // Show EAGLE part/device
string DIChangedOrValue = "";          // Zum Überprüfen, ob der Anwerder manuell gesucht hat,
                                       // und dafür den Suchparmater (Package-Varinate geändert hat.
                                       // Als Konsequenz muss im SCH die Packagevariante ebenfalls geändert werden!
// 2010-08-04 mit Walter Spermann besprochen : alf
string DIAttOCFarnell = "OC_FARNELL";  // Attribute name of Farnell order code
string DIAttOCNewark  = "OC_NEWARK";   // Attribute name of Newark order code
string DIAttOC        = "";            // Attribute name of order code (one of the above, set later)
string DIAttMF        = "MF";          // Attribute name of manufacturer name
string DIAttMPN       = "MPN";         // Attribute name of manufacturer part name
string DIUnknown      = "unknown";     // Attribute value if unknown order code
int DISearchOptPac    = 0;             // Add package name to search string
int DISearchType;

enum { DISearchByKeyword, DISearchByOC };

// The central search routine SingleSearch is used in 3 different ways
// with slightly different dialog and behaviour:
//  - Initial Search when walking through schematic
//  - Search again after order list is displayed
//  - Search through a library (library processing tool)
enum { DIModeInitSearch, DIModeNewSearch, DIModeLbrSearch };

// Language support for dialogs: German/English
// Please keep to alphabetic sorting for better maintainability !
string Dictionary[] = {
  "de\v"
  "en\v",
  "Abbrechen\v"
  "Cancel\v",
  "Aktualisieren\v"
  "Update\v",
  "allgemeine Suche\v"
  "general search\v",
  "Anzahl Leiterplatten:\v"
  "Number of PCBs:\v",
  "Anzahl Positionen: \v"
  "Number of list items: \v",
  "Anzahl\tValue\tPackage\tOrdercode\tHersteller\tHerstellercode\tVerfügbarkeit\tPreis (ab)\tBeschreibung\v"     // 2010-08-27 Value/Package "/" zu TAB geaendert
  "Quantity\tValue\tPackage\tOrder code\tManufacturer\tManuf. Code\tAvailability\tPrice (from)\tDescription\v",  //  2020-08-27 Value/Package "/" zu TAB geaendert
  "Attribute - Anzeigeoptionen\v"
  "Attributes - Display options\v",
  "Auf Lager: \v"
  "In stock: \v",
  "Auswählen\v"
  "Select\v",
  "Bauteilsuche für \v"
  "part search for \v",
  "Beschreibung\v"
  "Description\v",
  "Bestellliste exportieren\v"
  "Export order list\v",
  "Bestellliste für \v"
  "order list for \v",
  "Bitte geben Sie unten einen Suchstring ein !\v"
  "Please enter a search string below !\v",
  "Bitte manuell suchen oder Part überspringen !\v"
  "Please search manually or skip this part !\v",
  "Bitte starten Sie vom Bibliotheks-Editor aus !\v"
  "Please run this tool from the library editor !\v",
  "Bitte starten Sie vom Schaltplan aus !\v"
  "Please start from schematic !\v",
  "Bitte wälen Sie einen Listeneintrag oder überspringen Sie dieses Bauteil !\v"
  "Please select a list entry or skip this part !\v",
  "Beenden\v"
  "Close\v",
  "Besuchen Sie\v"
  "Visit\v",
  " DesignLink Devicesuche und -anzeige - \v"
  " DesignLink Device search and display - \v",
  "Deviceset\tPackage\tTechnologie\tDevicename\tOrdercode-Typ\tOrdercode\tHerstellercode\tBeschreibung\v"
  "Deviceset\tPackage\tTechnology\tDevice name\tOrder code type\tOrder code\tManuf. code\tDescription\v",
  "Direkt zur Bestelliste\v"
  "Directly to order list\v",
  "Erzeugung der Package-Bilder zunächst !\v"
  "Generating package images first !\v",
  "Exportieren\v"
  "Export\v",
  "Export als Textdatei\v"
  "Export as text file\v",
  "Fehler bei Zugriff auf\v"
  "Error while accessing\v",
  "Hilfe\v"
  "Help\v",
  "Keine Anzeige\v"
  "display none\v",
  "Keine Treffer.\v"
  "No matches.\v",
  "+Manuelle Suche\v"
  "+Manual Search\v",
  "Mehr als ein Artikel zu diesem Ordercode gefunden!\v"
  "Found more than one item fitting order code!\v",
  "Mit Ordercodes: \v"
  "With order codes: \v",
  "Name anzeigen\v"
  "only Name\v",
  "Neue Suche\v"
  "New search\v",
  "Ordercode\tHersteller\tHerstellercode\tVerfügbarkeit\tPreis (ab)\tBeschreibung\v"
  "Order code\tManufacturer\tManuf. code\tAvailability\tPrice (from)\tDescription\v",
  "Ordercode-Zuweisung - \v"
  "Order code embedding - \v",
  "Ordercodes speichern\v"
  "Save order codes\v",
  "Prüfe Preis und Verfügbarkeit: \v"
  "Check price and availability: \v",
  "Selektion zum Warenkorb hinzufügen\v"
  "Add selection to shopping cart\v",
  "Speichere Ordercodes\v"
  "Saving order codes\v",
  "Speichern + Beenden\v"
  "Save + Close\v",
  "Suche\v"
  "Searching\v",
  "+Suchen\v"
  "+Search\v",
  "Suche nach \v"
  "Searching for \v",
  "Treffer pro Seite\v"
  "Results per page\v",
  "und Include \v"
  "and include \v",
  "Überspringen\v"
  "Skip this\v",
  "Verschiedene Versionen von \v"
  "Different versions of \v",
  "Vorrätig\v"
  "In stock\v",
  "Wert anzeigen\v"
  "only Value\v",
  "Wert und Name anzeigen\v"
  "Value and Name\v",
  "Zum Warenkorb hinzufügen\v"
  "Add to shopping cart\v",
  "Zur Detailansicht oder erneuten Suche der Artikel bitte diese doppelklicken !\v"
  "For detail view or new search of items please doubleclick them !\v",
  "Zur Detailansicht oder Suche und Zuweisung eines Artikels bitte Device doppelklicken !\v"
  "For detail view or search and assignment of a product please doubleclick device !\v",
  "Zurück\v"
  "Back\v"
};

string DIDlgLang = DILanguage;
if (DIDlgLang != "de") DIDlgLang = "en";
int DILangIdx = strstr(Dictionary[0], DIDlgLang) / 3;

// Translate, based on Dictionary
string tr(string s) {
  string t = lookup(Dictionary, s, DILangIdx, '\v');
  return t ? t : s;
}
//-----------------------------------------------------------------------------

// product list head vor various representations
string DIProductListHead = tr("Ordercode\tHersteller\tHerstellercode\tVerfügbarkeit\tPreis (ab)\tBeschreibung");

//-----------------------------------------------------------------------------

if (DIResultsPerPage)
  while (DIResultsPerPage > strtol(DIResultsPerPageValues[DIResultsPerPageIndex]))
    if (!DIResultsPerPageValues[++DIResultsPerPageIndex]) {
      --DIResultsPerPageIndex;
      break;
    }

/************** Functions ****************************************************/
void InitCountryData(void)
{
  string CountryData[] = {
   "DE EUR 0 de.farnell.com",
   "AT EUR 0 at.farnell.com",
   "CH CHF 0 ch.farnell.com",
   "UK GBP 0 uk.farnell.com", // actually this should be GB, see above
   "BE EUR 0 be.farnell.com",
   "FI EUR 0 fi.farnell.com",
   "FR EUR 0 fr.farnell.com",
   "NL EUR 0 nl.farnell.com",
   "NO NOK 0 no.farnell.com",
   "SE SEK 0 se.farnell.com",
   "DK DKK 0 dk.farnell.com",
   "IL USD 0 il.farnell.com",
   "IT EUR 0 it.farnell.com",
   "ES EUR 0 es.farnell.com",
   "AU AUD 2 au.element14.com",
   "NZ NZD 2 nz.element14.com",
   "PT EUR 0 pt.farnell.com",
   "IE EUR 0 ie.farnell.com",
   "SI EUR 0 si.farnell.com",
   "PL PLZ 0 pl.farnell.com",
   "HU HUF 0 hu.farnell.com",
   "SK EUR 0 sk.farnell.com",
   "IN INR 0 in.element14.com",
   "BG EUR 0 bg.farnell.com",
   "RO RON 0 ro.farnell.com",
   "CZ CZK 0 cz.farnell.com",
   "EE EUR 0 ee.farnell.com",
   "LV EUR 0 lv.farnell.com",
   "TR EUR 0 tr.farnell.com",
   "LT EUR 0 lr.farnell.com",
   "RU RUB 0 ru.farnell.com",
   "CN CNY 1 cn.element14.com",
   "HK HKD 2 hk.element14.com",
   "SG SGD 2 sg.element14.com",
   "MY MYR 2 my.element14.com",
   "PH PHP 2 ph.element14.com",
   "KR KRW 2 kr.element14.com",
   "TW TWD 2 tw.element14.com",
   "TH THB 2 th.element14.com",
   "CA CAD 3 canada.newark.com",
   "MX MXN 3 mexico.newark.com",
   "US USD 3 www.newark.com",
   "AS USD 3 www.newark.com"
   };

  string URLs[] = {
    "uk.farnell.com",
    "cn.element14.com",
    "au.element14.com",
    "www.newark.com"
    };

  string rx = "^" + DICountry;
  for (int i = 0; ; i++) {
      string cd = CountryData[i];
      if (!cd)
         break;
      if (strxstr(cd, rx) == 0) {
         DICurrency = strsub(cd, 3, 3);
         int u = strtol(strsub(cd, 7, 1));
         DISearchURL = URLs[u];
         // In case the Newark server is the one we contact, we keep to Newark order codes
         // Set device/part attribute keyword accordingly
         DIAttOC = (u == 3) ? DIAttOCNewark : DIAttOCFarnell;
         DICompany = (u == 3) ? "Newark" : "Farnell";
         DIShoppingURL = strsub(cd, 9);
         break;
         }
      }
  if (!DICurrency || !DISearchURL) {
     string Msg;
     sprintf(Msg, "Country '%s' not supported.", DICountry);
     dlgMessageBox(Msg);
     exit(1);
     }
}

string GetDesignLinkURL(void)
{
  return "https://" + DISearchURL + "/pffind/services/SearchService";
}

string GetImageURL(string Image, int Large, string vrnt)
{
  return Image && vrnt ? "http://" + DISearchURL + "/productimages/" + vrnt + (Large ? "standard" : "thumbnail") + Image
                       : "";
}

string GetDataSheetURL(string Url)
{
  if (Url[0] == '/')
     Url = "http://" + DISearchURL + Url;
  return Url;
}

string BoolString(int b)
{
  return b ? "true" : "false";
}

void Clear(void)
{
  DInProducts = 0;
  DIProductList[DInProducts] = "";
  DIProductSel = -1;
}

int Search(string key, int search_type)
{
   string localKey = key, offset = "0";

   // If search is done by keyword:
   // Replace special characters with according XML notation.
   if (search_type == DISearchByKeyword) {
      string StrRepl[] = { // string replacements
         "&", "&amp;",     // has to be the first one!
         "<", "&lt;",
         ">", "&gt;"
      };
      int o = 0;
      for (int x = 0; StrRepl[x]; ) {
         o = strstr(localKey, StrRepl[x], o);
         if (o >= 0) {
            localKey = strsub(localKey, 0, o) + StrRepl[x + 1] + strsub(localKey, o + strlen(StrRepl[x]));
            o += strlen(StrRepl[x + 1]);
         }
         else {
            x += 2;
            o = 0;
         }
      }
      // Remember search settings
      string CurrentSearch;
      sprintf(CurrentSearch, "%d%d_%d_%s\n", DIInStock, DIRoHS, DIResultsPerPageIndex, localKey);
      if (DIRecentSearch != CurrentSearch) {
         DIRecentSearch = CurrentSearch;
         DIResultGroupIndex = 0;
      }
      // Store current user settings in Eaglerc
      cfgset("ULP:designlink.InStock", DIInStock ? "1" : "0");
      cfgset("ULP:designlink.RoHS",    DIRoHS    ? "1" : "0");
      cfgset("ULP:designlink.ResultsPerPage",  DIResultsPerPageValues[DIResultsPerPageIndex]);
      // Calculate offset for getting the matches beginning from offset
      DIResultsPerPage = strtol(DIResultsPerPageValues[DIResultsPerPageIndex]);
      DIResultOffset = DIResultGroupIndex * DIResultsPerPage;
      sprintf(offset, "%d", DIResultGroupIndex * DIResultsPerPage);
   }
   // Build the XML query string
   string TimeStamp = t2string(time(), "Uyyyy-MM-ddThh:mm:ss.000");
   string Signature = fdlsignature(((search_type == DISearchByKeyword) ? "searchByKeyword" : "searchByPremierFarnellPartNumber") +
                                   TimeStamp, DICustomerPW);
   string Query = "<soapenv:Envelope  xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\"  xmlns:v1=\"http://pf.com/soa/services/v1\">\n"
                  " <soapenv:Header>\n"
                  "   <v1:userInfo>\n"
                  "     <v1:signature>" + Signature + "</v1:signature>\n"
                  "     <v1:timestamp>" + TimeStamp + "</v1:timestamp>\n"
                  "     <v1:locale>" + DILanguage + "_" + DICountry + "</v1:locale>\n"
                  "   </v1:userInfo>\n"
                  "   <v1:accountInfo>\n"
                  "     <v1:customerId>" + DICustomerID + "</v1:customerId>\n"
                  "   </v1:accountInfo>\n"
                  " </soapenv:Header>\n"
                  " <soapenv:Body>\n";
   // Differences in the query for both cases. Rest remains the same.
   if (search_type == DISearchByKeyword)
      Query +=    "   <v1:keywordParameter>\n"
                  "     <v1:keyword>" + localKey + "</v1:keyword>\n"
                  "     <v1:offset>" + offset + "</v1:offset>\n"
                  "     <v1:numberOfResults>" + DIResultsPerPageValues[DIResultsPerPageIndex] + "</v1:numberOfResults>\n"
                  "      <v1:RefinementParameters>\n"
                  "        <v1:inStock>" + BoolString(DIInStock) + "</v1:inStock>\n"
                  "        <v1:rohsComplaint>" + BoolString(DIRoHS) + "</v1:rohsComplaint>\n"
                  "      </v1:RefinementParameters>\n"
                  "    </v1:keywordParameter>\n";
   else
      Query +=   "   <v1:premierFarnellPartNumbers>\n"    // search by premierFarnellPartNumbers
                 "     <v1:Sku>" + localKey + "</v1:Sku>\n"
                 "    </v1:premierFarnellPartNumbers>\n";
   Query +=      "  </soapenv:Body>\n"
                 "</soapenv:Envelope>\n";

   DIHtml = tr("Suche") + " <b>" + localKey + "</b>" + " ...";
   if (!DIMode) dlgRedisplay(); // Omit Redisplay if we are not really in a dialog context !
   if (DIDebugXfer)
      output("fdlQuery.html", "wb") printf("%s", Query);

   // Here we go !
   int rv = netpost(DISearchResult, GetDesignLinkURL(), Query);

   if (DIDebugXfer)
      output("fdlResponse.html", "wb") printf("%s", DISearchResult);

   DISearchType = search_type; // Set search type accordingly
   // Error handling
   if (rv < 0) {
      string FaultString = "";
      if (strlen(DISearchResult) > 1)
        FaultString = xmltext(DISearchResult, "soapenv:Envelope/soapenv:Body/soapenv:Fault/faultstring");
      // This indicates that a search by OC failed. Don't treat this as an error !
      // Accessing a record with an invalid key (from outside) must not throw an exception !
      if ((search_type == DISearchByOC) && (FaultString == "SearchServiceException")) {
        DISearchResult = "";
        return 1;
      }
      if (FaultString)
         DISearchResult = FaultString;
      sprintf(DIHtml, tr("Fehler bei Zugriff auf") + " '%s':\n\n%s\n\n%s", GetDesignLinkURL(), neterror(), DISearchResult);
      DISearchResult = "";
      return 0;
   }
   return 1;
}

void Result2List(void)
{
   if (strlen(DISearchResult) <= 1)
      return;
   DInProducts = xmlelements(DIProducts, DISearchResult, "soapenv:Envelope/soapenv:Body/ns1:" +
                             (DISearchType == DISearchByKeyword ? "keywordSearchReturn" : "premierFarnellPartNumberReturn") +
                             "/ns1:products");
   DIProductList[DInProducts] = "";
   for (int i = DInProducts; --i >= 0; ) {
      string Prices[];
      int nPrices = xmlelements(Prices, DIProducts[i], "ns1:products/ns1:prices");
      string Price;
      for (int j = nPrices; --j >= 0; ) {
         string p1 = xmltext(Prices[j], "ns1:prices/ns1:cost");
         if (p1 && (!Price || strtod(p1) < strtod(Price)))
            Price = p1;
      }
      DIProductList[i] =
          xmltext(DIProducts[i], "ns1:products/ns1:sku") + "\t"
        + xmltext(DIProducts[i], "ns1:products/ns1:vendorName") + "\t"
        + xmltext(DIProducts[i], "ns1:products/ns1:translatedManufacturerPartNumber") + "\t"
        + xmltext(DIProducts[i], "ns1:products/ns1:inv") + "\t"
        + Price + "\t"
        + xmltext(DIProducts[i], "ns1:products/ns1:displayName");
   }
   // Support for several pages of search results: Only for search by keyword
   if (DISearchType == DISearchByKeyword) {
      DIResultTotal = xmltext(DISearchResult, "soapenv:Envelope/soapenv:Body/ns1:keywordSearchReturn/ns1:numberOfResults");
      int Total = strtol(DIResultTotal);
      for (int i = 0; ; ) {
         int n1 = i * DIResultsPerPage + 1,
             n2 = n1 + DIResultsPerPage - 1;
         if (n2 > Total)
            n2 = Total;
         string s;
         sprintf(s, "%d - %d of %d", n1, n2, Total);
         if (Total)
            DIResultGroupArray[i++] = s;
         if (n2 == Total || n2 >= 500) {  // there are problems at Farnell's Design Link with offsets above 500
            DIResultGroupArray[i] = "";
            break;
         }
      }
   }
   else
      DIResultTotal = "1"; // There must be only one result otherwise something is fishy !
   DIProductSel = -2; // the first item after sort
}

void ShowSelection(void)
{
  int huge_quantity = 1000000; // Indicator for "or higher"...

  if (!DInProducts) {
     DIHtml = tr("Keine Treffer.") + "<p>\n";
     DIHtml += DIAdvice;
     return;
     }
  if (DIProductSel < 0)
     return;

  string p = DIProducts[DIProductSel];
  if (p) {
     string Attributes[];
     int nAttributes = xmlelements(Attributes, p, "ns1:products/ns1:attributes");

     string AttributeList;
     for (int i = 0; i < nAttributes; ++i)
         AttributeList += "    <li> " + xmltext(Attributes[i], "ns1:attributes/ns1:attributeLabel")
                               + ": " + xmltext(Attributes[i], "ns1:attributes/ns1:attributeValue")
                                      + xmltext(Attributes[i], "ns1:attributes/ns1:attributeUnit")
                                      + "</li>\n";
     string PricesList;
     {
       string Prices[];
       int nPrices = xmlelements(Prices, p, "ns1:products/ns1:prices");
       for (int j = 0; j < nPrices; ++j) {
           string Cost = xmltext(Prices[j], "ns1:prices/ns1:cost");
           if (Cost) {
              string n1 = xmltext(Prices[j], "ns1:prices/ns1:from"),
                     n2 = xmltext(Prices[j], "ns1:prices/ns1:to");
              if (strtol(n2) >= huge_quantity)
                 n2 = "+";
              else
                 n2 = "&nbsp;-&nbsp;" + n2 + "&nbsp;";
              PricesList += "       <tr>\n"
                            "        <td>" + n1 + n2 + "</td>\n"
                            "        <td>" + Cost + "</td>\n"
                            "       </tr>\n";
              }
           }
     }
     DIHtml = "<style type=\"text/css\">\n"
              " .tableblock {\n"
              "     background-color: #A0A0A0;\n"
              " }\n"
              " .tabledata {\n"
              "     background-color: #F0F0F0;\n"
              "     padding: 2;\n"
              " }\n"
              "</style>\n"
              ;

     DIHtml +=  "<h2>" + xmltext(p, "ns1:products/ns1:displayName") + "</h2>";
     string ImageUrl = GetImageURL(xmltext(p, "ns1:products/ns1:image/ns1:baseName"), 1, xmltext(p, "ns1:products/ns1:image/ns1:vrntPath")),
            DataSheet = xmlelement(p, "ns1:products/ns1:datasheets");
     DIHtml +=  "<table>\n"
                " <tr>\n"
                "  <td>\n";
     if (ImageUrl)
        DIHtml +=
              "   <img src=\"" + ImageUrl + "\">\n";
     if (DataSheet)
        DIHtml +=
              "   <p>\n"
              "    <a href=\"" + GetDataSheetURL(xmltext(DataSheet, "ns1:datasheets/ns1:url")) + "\">\n"
              "    " + xmltext(DataSheet, "ns1:datasheets/ns1:description") + "</a>\n"
              "   </p>\n";
     DIHtml +=  "  </td>\n"
              "  <td>\n"
              "   <dl>\n"
              "    <dt>Manufacturer:</dt>\n"
              "    <dd style=\"text-align: left;\">" + xmltext(p, "ns1:products/ns1:vendorName") + "</dd>\n"
              "    <dt>Order Code:</dt>\n"
              "    <dd>" + xmltext(p, "ns1:products/ns1:sku") + "</dd>\n"
              "    <dt>Manufacturer Part No:</dt>\n"
              "    <dd>" + xmltext(p, "ns1:products/ns1:translatedManufacturerPartNumber") + "</dd>\n"
              "   </dl>\n"
              "   <p>RoHS: <strong>  "  + xmltext(p, "ns1:products/ns1:rohsStatusCode") + "</strong></p>\n"
              "   <dl>\n"
              "    <dt>Description</dt>\n"
              "   </dl>\n"
              "   <ul>\n"
              "    <li>" + xmltext(p, "ns1:products/ns1:displayName") + "</li>\n"
                   + AttributeList +
              "   </ul>\n"
              "  </td>\n"
              "  <td>\n"
              "   <table class=\"tableblock\">\n"
              "    <tr>\n"
              "     <td class=\"tabledata\">\n"
              "      <h4>Availability</h4>\n"
              "      <table width=\"100\%\" style=\"background:#FFFFFF\">\n"
              "       <tr>\n"
              "        <td>\n"
              "         <p>\n"
              "          <b>Availability</b>: " + xmltext(p, "ns1:products/ns1:inv") + "<br>\n"
              "         </p>\n"
              "         <p>\n"
              "          <b>Price For:</b> " + xmltext(p, "ns1:products/ns1:packSize") + " " + xmltext(p, "ns1:products/ns1:unitOfMeasure") +
              "         </p>\n"
              "         <p>\n"
              "          <b>Minimum Order Quantity:</b> " + xmltext(p, "ns1:products/ns1:translatedMinimumOrderQuality") +
              "         </p>\n"
              "         <p>\n"
              "          <b>Order Multiple:</b> " + xmltext(p, "ns1:products/ns1:translatedMinimumOrderQuality") +
              "         </p>\n"
              "        </td>\n"
              "       </tr>\n"
              "      </table>\n"
              "     </td>\n"
              "    </tr>\n"
              "    <tr>\n"
              "     <td class=\"tabledata\">\n"
              "      <h4>Price (" + DICurrency + ")</h4>\n"
              "      <table width=\"100\%\" style=\"background:#FFFFFF\">\n"
              "       <tr>\n"
              "        <th width=\"44\%\">Qty</th>\n"
              "        <th>List Price</th>\n"
              "       </tr>\n"
                      + PricesList +
              "      </table>\n"
              "     </td>\n"
              "    </tr>\n"
              "   </table>\n"
              "  </td>\n"
              " </tr>\n"
              "</table>\n";
     }
}

void DoSearch(string keyword)
{
  if (Search(keyword, DISearchByKeyword)) {// 1. suchen
     Result2List();                        // 2. Ergebnis in die Liste eintragen
     ShowSelection();                      // 3. Ergebnisse anzeigen
  }
}

string SingleSearch(int dlg_mode, string dlg_head, string description, string image,
                    int start_search_mode, string singlekey, string displaykey) {
  // Without treating the help text here local maintenance of it is a nightmare !
  string help[] = {
    "<b>Bauteilsuche</b><br>"
    "Gehen Sie schrittweise durch die Bauteiltypen Ihres Schaltplans und suchen "
    "nach passenden Artikeln in Farnell's Produktkatalog.<br>"
    "Pro Bauteiltyp werden zunächst Suchergebnisse anhand des Bauteil-Value angezeigt. "
    "Befindet sich ein geeigneter Farnell-Artikel darunter, können Sie diesen "
    "per Button oder Doppelklick auswählen.<br>"
    "Wenn nicht, können Sie eine beliebige <i>manuelle Suche</i> starten und dann auswählen.<br>"
    "Nach der Auswahl gelangen Sie zum nächsten Bauteiltyp. "
    "Mit <i>Überspringen</i> kommen Sie ohne Auswahl weiter. "
    "Bauteiltypen, die anhand des Value kein Suchergebnis liefern, werden automatisch übersprungen. "
    "Wenn Sie alle Bauteiltypen durchgegangen sind oder auch mit dem Button <i>Direkt zur Bestelliste</i> "
    "gelangen Sie zur Bestellliste, von der aus Sie direkt bei Farnell ordern können. "
    "Sie können Ihre Auswahl vorher noch beliebig ändern. "
    "<p><b>Hersteller-Bibliotheken mit Ordercodes</b><br>"
    "Für Bauteile aus Hersteller-Bibliotheken, die bereits mit Farnell-Ordercodes versehen sind, "
    "erübrigt sich diese Suche und der entsprechende Farnell-Artikel wird automatisch in die "
    "Bestellliste übernommen. "
    "<p><b>Alternative Suche mit Value und Package</b><br>"
    "Anstatt nur mit dem Bauteil-Value können Sie die automatische Suche auch mit Value und Package "
    "durchführen. Sie erreichen dies, in dem Sie <br><i>" + filename(argv[0]) + "</i> mit der Option "
    "-sop (search option package) aufrufen."
    "<p><b>Hinweis zu Internet-Verbindungseinstellungen</b><br>"
    "Falls Sie über einen <b>Proxy</b> auf das Internet zugreifen, passen Sie bitte die Einstellungen "
    "im Control Panel unter Hilfe / Auf Update prüfen / Konfigurieren entsprechend an."
    ,
    "<b>Part search</b><br>"                                                                                             +
    "Step through the part types of your schematic and search for according articles in "                                +
    DICompany + "'s product catalog.<br>"                                                                                +
    "For each part type, search results fitting to the part value are displayed at first. "                              +
    "If there's an appropriate article from " + DICompany + " among them you can choose it by button or mouse click.<br>"+
    "If not, you can start an arbitrary <i>manual search</i> and select then.<br>"                                       +
    "After selection you get to the next part type. "                                                                    +
    "With <i>Skip this</i> you proceed without a choice. "                                                               +
    "Part types with no search results based on the part value are skipped automatically. "                              +
    "After going through all part types or by pressing <i>Directly to order list</i> "                                   +
    "you get to the order list where you can load it to a " + DICompany + " shopping cart. "                               +
    "You can still change your selection before."                                                                        +
    "<p><b>Manufacturer libraries with order codes</b><br>"                                                              +
    "This search is not necessary for parts from manufacturer libraries with " + DICompany + " order codes. "            +
    "The corresponding " + DICompany + " article is taken automatically to the order list."                              +
    "<p><b>Alternative search by value and package</b><br>"                                                              +
    "Instead of search by part value you can do the automatic search by value and package. "                             +
    "You achieve this by starting <i>" + filename(argv[0]) + "</i> with option -sop (search option package)."
    "<p><b>Hint on internet settings</b><br>"
    "If you access the internet by a <b>proxy</b>, please adjust the settings in the Control Panel under "
    "Help / Check for Update / Configure accordingly."
  };
  DIProductSel = -1;
  DIChangedOrValue = singlekey;  // Zum Überprüfen, ob der Anwerder manuell gesucht hat,
                                 // und dafür den Suchparameter (Package-Varinate geändert hat.
                                 // Als Konzequenz muss im SCH die Packagevariante ebenfalls geändert werden!
  string key = displaykey;
  int ok = 1;                    // Wether search worked or not

  if (!((start_search_mode == DISearchByOC) && (singlekey == DIUnknown))) { // Avoid search with an unknown OC !
    ok = Search(singlekey, start_search_mode);
    Result2List();
  }

  if (DInProducts > 0)
    DIProductSel = 0;
  // If it's the initial search and no products have been found don't display the dialog (and go to next item)
  // The same for initial search by OC: If OC is there, everything is clear.
  // Search must have worked (ok=1). Otherwise user needs to be informed !
  if (ok && (dlg_mode == DIModeInitSearch) && ((DInProducts == 0) || (start_search_mode == DISearchByOC)) )
      return (DInProducts == 0) ? "" : DIProductList[0];
  else {
    DIMode = 0;              // For displaying search message
    if (ok) ShowSelection(); // Prepares Html string for display
    int dlg_ret = dlgDialog(dlg_head) {
      dlgVBoxLayout {
        if (dlg_mode == DIModeLbrSearch) {
          dlgLabel("<b>" + tr("Suche nach ") + (DICountry == "US" ? "NEWARK" : "FARNELL") +
                   " Code </b>(" + GetDesignLinkURL() + ")");
        }
        dlgHBoxLayout dlgSpacing(800);
        dlgHBoxLayout {
          dlgTextView(DIHtml);  // Gleich anzeigen...
          dlgVBoxLayout dlgSpacing(400);  // flines
        }
        dlgListView(DIProductListHead ,DIProductList, DIProductSel)
          if (dlgSelectionChanged()) ShowSelection();
          else dlgAccept();
        dlgGroup(tr("Beschreibung") + " EAGLE Part/Device:") {
          dlgHBoxLayout {
            dlgLabel(description);
            if (image) dlgLabel(image);
          }
        }
        dlgHBoxLayout {
          dlgStringEdit(key);
          dlgPushButton(tr("+Manuelle Suche")) {
            DIChangedOrValue = key;         // zum Überprüfen, ob der Anwender den Suchwert geändert hat,
                                            // um evtl. die Package-Variante zu ändern.
            Clear();
            DoSearch(key);
          }
        }
        dlgHBoxLayout {
          dlgPushButton(tr("Auswählen")) {
            if (DIProductSel < 0) dlgMessageBox(
              tr("Bitte wählen Sie einen Listeneintrag oder überspringen Sie dieses Bauteil !"), "OK");
            else dlgAccept();
          }

          dlgPushButton(tr("Überspringen")) {
            DIProductSel = -1;
            dlgAccept();
          }
          dlgStretch(1);
          if (dlg_mode == DIModeLbrSearch) {
            dlgComboBox(DIResultGroupArray, DIResultGroupIndex) DoSearch(key);
            dlgStretch(1);
          }
          if (dlg_mode == DIModeInitSearch) // In case of initial search offer help
            dlgPushButton(tr("Hilfe") + "...")
              dlgMessageBox(help[DILangIdx]);
          string button_text;
          if (dlg_mode == DIModeInitSearch) button_text = tr("Direkt zur Bestelliste");
          else button_text = tr("Zurück");
          dlgPushButton(button_text) {
            dlgAccept();
            if (dlg_mode == DIModeInitSearch) DISkipRem = 1;
            else DICancelSearch = 1;
          }
        }
      }
    };
    DIMode = 1; // Reset
    // User pressed the "x" close button:
    // This means: No selection, no change of data in case of search from order list
    // In case of the initial search, assume/respect that the user wants to leave DL
    // Pro: He should have a possibility to leave. Otherwise he'd have to go directly to orderlist
    // first (involving unintended searches) before he can close.
    // Con: He may click without being aware that all his previous decesions are lost.
    // This is less likely as we are not providing a close button here.
    if (dlg_ret == -1) {
      if (dlg_mode == DIModeInitSearch)
        exit(EXIT_SUCCESS);
      DIProductSel = -1;
      DICancelSearch = 1;
    }
    return (DIProductSel < 0 ? "" : DIProductList[DIProductSel]);
  }
}

// --- replace package and technology text holder ----
string techpacvar(string devsetname, string devname, string tech, string pacname) {
  if (tech == "''") tech = "";
  if (pacname == "''") pacname = "";
  if (devname == "''") devname = "";
  string s[];
  int cnt;
  cnt = strsplit(s, devsetname, '*');
  if (cnt > 1) {
    if (cnt == 2) {
      if (!s[0]) {
        devsetname = tech + s[1];
      }
      else if (!s[1]) {
        devsetname = s[0] + tech;
      }
      else devsetname = s[0] + tech + s[1];
    }
  }
  else devsetname += tech;

  cnt = strsplit(s, devsetname, '?');
  if (cnt > 1) {
    if (cnt == 2) {
      if (!s[0]) {
        devsetname = devname + s[1];
      }
      else if (!s[1]) {
        devsetname = s[0] + devname;
      }
      else devsetname = s[0] + devname + s[1];
    }
  }
  else devsetname += devname;
  return devsetname;
}
