-- PseudoXMLPS
-----------------------------------
-- CREATED:
-- 2002
--
-- DESCRIPTION:
--              Pseudo XML by Alex da Franca c2003
--              Convert a lingo list to a XML like string and back

--
-- REQUIRES:
--              only the functions mSaveList_2_XML() and mReadXML_2_List() require my system of library scripts
--              in order to write and read text with fileio. All other functions have no dependencies
--              Either get these scripts from my website, or replace the write/read stuff with your own fileIO handlers
--                    That is:
--                            * movie script "aleXtrasMovieScript"
--                            * parent script "commonMovieScript"
--                            * parent script "FileIOFunktionen"
--
-- USAGE:
--              ---------- please see the comments for each handler for a how-to.
--              ---------- nonetheless here are some fast start comments with the minimum amount of parameters
--              PseudoXMLPS = new(script "PseudoXMLPS")
--              -- Convert a list to an xml string:
--              xmlString = PseudoXMLPS.mGetXMLStringFromList(["one", "two", [#prop: 3]])
--              lingolist = PseudoXMLPS.mGetListFromXMLString(xmlString)
--              ---------- basically that's it
--
--              ---------- another "hack" to be aware of is the following:
--              ---------- linear lists get identified by having a nodename which starts with the exact string "item"
--              ---------- so if an xml node like <item1>one</item1> is encountered, it is treated as linear list: ["one"]
--              ---------- and NOT as [#item1:"one"]
--
--              ---------- the mGetXMLStringFromList() function escapes the reserved xml chars <> and & and ' and " by default
--              ---------- if for some reason you rather want to enclose the contents in a <![CDATA[ tag, use the dontReplaceGT flag.
--------------------------------------------- EXAMPLE for <dontReplaceGT = 1> (dontReplaceGT means don't replace "greater than" btw...):
--
-- myList = [#stringWithInvalidChars:"A string with invalid chars like <> and & and ' and " & QUOTE]
-- myList[#stringWithInvalidChars] = "<![CDATA[" & myList[#stringWithInvalidChars] & "]]>"
-- xmlString = PseudoXMLPS.mGetXMLStringFromList(myList, "myList", 1, 1)
-- ...
-- myList = PseudoXMLPS.mGetListFromXMLString(xmlString)
-- repeat with n = countmyList) down to 1
--   thisValue = myList[n]
--   if ilk(thisValue) = #string then
--     if thisValue starts "<![CDATA[" then
--       delete char 1 to 9 of thisValue
--       delete char length(thisValue) - 2 to length(thisValue) of thisValue
--       myList[n] = thisValue
--     end if
--   end if
-- end repeat
--------------------------------------------- please note, that the above example is ONLY needed, if you use dontReplaceGT = 1 !!
--
-- HISTORY:

--              alex am Freitag 21.April.2006 16:31:57
--              fixed a bug in "mBuildXMLString" many thanks to Olaf Schliesing for pointing this out
--
--              alex am 28.11.06 um 16:41:00
--              finally added a tag property, when writing xml, so the ilk of the object gets stored
--              so, when reading back the xml file, we do not convert a node by accident to a value
--              when a string evaluates to a value "by accident"
--
--              Scriptmarker: changes alex (04.04.2007 at 10:22 Uhr) // Scriptmarker
--              added  escaping of: 
--                                   & -> &amp;
--                                   ' -> &apos;
--                                   " -> &quot;
--              and vice versa. Until now I only escaped < and >, but ampersand, apostrophe and quote must also be escaped for proper xml
--              I als added "support" for the CData tag, which allows you to escape the whole contents of a node.
--              support is only done through ignoring esaping the special characters, wehn BUILDING the xml string.
--              So the user can decide on his own, which nodes s/he wants to be masked inside a CData section
--              (otherwise I'd have to mask EVERY string and try to unmask EVERY node. Now the user is on his own)
-- TODO:
-- -
-----------------------------------

on _____________________PROPERTY_DECLARATION me
end
-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
property ancestor
property pXMLParserXtra
property pXmlxtraversion, pVersionNumber

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
on ___________________STANDARD_EVENTS me
end
-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

on new me
  Script_Root_Object = member("Script_Root_Object")
  if ilk(Script_Root_Object) = #member then
    if Script_Root_Object.type = #script then
      ancestor = new(script "Script_Root_Object")
      mSetScriptName me, "PseudoXMLPS"
    end if
  end if
  return me
end

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
on ___________________ENGINE_EVENTS me
end
-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

on mDestroy me
  pXMLParserXtra = void
end

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
on ___________PUBLIC_EVENTS me
end
-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

-- on handler me
--end

-- on handlers me
--end

on interface me
  str = "Pseudo XML Script  by alex da franca c2003 -- alex@farbflash.de -- all rigths reserved"
  put RETURN & "version 1" after str
  
  put RETURN & "----- Translate lingoList to XML-ish string (docname and strict optional)" after str
  put RETURN & "on mGetXMLStringFromList me, list_propOrlinearList, string_docName, boolean_strict" after str
  put RETURN & "------------ RETURNS string" after str
  put RETURN after str
  
  put RETURN & "----- Parse XML-ish string to lingo list" after str
  put RETURN & "on mGetListFromXMLStringlingo me, string_XMLstring, integer_convertValues" after str
  put RETURN & "-- -- <convertValues> #integer" after str
  put RETURN & "-- -- -- 0 => don't convert (fast, all values are strings)" after str
  put RETURN & "-- -- -- 1 => convert only integer() and float() (slower)" after str
  put RETURN & "-- -- -- 2 => try to convert all data with value(), even parse for colors in hexstring format (slow)" after str
  put RETURN & "------------ RETURNS property list" after str
  put RETURN after str
  
  put RETURN & "----- Parse XML string to lingo list using XML xtra:" after str
  put RETURN & "on mGetListFromXMLString me, string_XMLstring, integer_convertValues" after str
  put RETURN & "-- -- <convertValues> #integer" after str
  put RETURN & "-- -- -- 0 => don't convert (fast, all values are strings)" after str
  put RETURN & "-- -- -- 1 => convert only integer() and float() (slower)" after str
  put RETURN & "-- -- -- 2 => try to convert all data with value(), even parse for colors in hexstring format (slow)" after str
  put RETURN & "------------ RETURNS property list" after str
  put RETURN after str
  
  put RETURN & "----- Read Apple-style plist file and convert it to a lingo property list:" after str
  put RETURN & "on mReadPList me, string_Filename" after str
  put RETURN & "-- -- -- <Filename> is optional, if not present a file selection dialog is displayed" after str
  put RETURN & "------------ RETURNS property list" after str
  put RETURN after str
  
  put RETURN & "----- Convert Apple-style plist file to lingo list:" after str
  put RETURN & "on mGetListFromPListString me, string_pListString" after str
  put RETURN & "------------ RETURNS property list" after str
  put RETURN after str
  
  --  put RETURN & "----- Convert Apple-style plist to lingo list (after converting the .plist file with mGetListFromXMLStringX):" after str
  --  put RETURN & "proplist mConvertKeyList object me, proplist inputlist" after str
  --  put RETURN after str
  
  put RETURN & "----- Parse Excel style XML data (for now it parses only the first worksheet with the XMLParser Xtra):" after str
  put RETURN & "on mParseExcelXML me, string_theText" after str
  put RETURN & "------------ RETURNS property list" after str
  
  return str
end


-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-- xxxxxxxxxxxxxxxxxx Convert lingo list (also nested lists) to XML stylish string

on mGetXMLStringFromList me, listref, docName, strict, dontReplaceGT, withParams
  -----------------------------------
  -- CREATED: 06.03.2008
  -- ACTION: Convert lingo list (also nested lists) to XML stylish string
  -- INPUT: 
  --              <listref> format: property list or linear list
  --              <docName> format: #string; optional. if omitted "Untitled" is used for the XML document name
  --              <strict>        => boolean; avoid spaces in tag names
  --              <dontReplaceGT> => boolean; dont replace < and >
  --              <withParams>    => boolean; write attribute in tag for the lingo ilk => bigger xml files and unfortunately it is slower to parse
  ----------------------------- (I thought avoiding value() would help, but in this case the additional text parsing of the attributes tag slows down)
  -- RETURNS: string
  -- EXAMPLE: saveString = mGetXMLStringFromList(me, lingo_list, "documentName")
  -----------------------------------
  
  --  ms = the milliseconds
  if voidP(strict) then strict = 0
  
  --  if the last char of the moviepath = ":" then
  --    str = "<?xml version="&QUOTE&"1.0"&QUOTE&&"encoding="&QUOTE&"macintosh"&QUOTE&&"?>"&RETURN
  --  else
  --    str = "<?xml version="&QUOTE&"1.0"&QUOTE&&"encoding="&QUOTE&"ISO-8859-1"&QUOTE&&"?>"&RETURN
  --  end if
  str = "<?xml version="&QUOTE&"1.0"&QUOTE&&"encoding="&QUOTE&"ISO-8859-1"&QUOTE&&"?>"&RETURN
  
  if not(string(docName).length) then docName = "Untitled"
  if not("abcdefghijklmnopqrstuvwxyz_" contains char 1 of docname) then put "a" before docname
  put "<" & docName & ">" & RETURN after str
  
  if the platform contains "mac" then
    put mMapCharCodes(me, mBuildXMLString(me, listref, 1, strict, dontReplaceGT, withParams), #mac2win) after str
  else
    put mBuildXMLString(me, listref, 1, strict, dontReplaceGT, withParams) after str
  end if
  
  put "</" & docName & ">" after str
  --  put the milliseconds - ms
  return str
end


-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-- xxxxxxxxxxxxxxxxxx Convert XML stylish string to lingo list


on mGetListFromXMLStringlingo me, str, convertValues, withParams
  -----------------------------------
  -- CREATED: 06.03.2008
  -- ACTION: Description
  -- INPUT:
  --          <str> format: #string; split a string using <> and </> tags into lingo list
  --          <convertValues> #integer 0=>don't convert (fast, all values are strings), 1 => convert only numbers (slower); 2 => try to convert all data, even colors (slow)
  --          <withParams> : #boolean : parse parameters too. new, not very well tested
  -- RETURNS: property list
  -- EXAMPLE: lingo_list = mGetListFromXMLStringlingo(me, saveString)
  -- CHANGES: implemented parameter parsing
  -----------------------------------
  
  ms = the milliseconds
  
  if ilk(str) <> #string then str = string(str)
  
  if voidP(convertValues) then convertValues = 2
  
  if the platform contains "mac" then
    if str.line[1] contains "encoding="&QUOTE&"ISO-8859-1"&QUOTE then
      str = mMapCharCodes(me, str, #win2mac)
    end if
  end if
  
  str = mRemoveXMLComments(me, str)
  
  
  offs = offset("<", str)
  if offs > 0 then
    if offs > 1 then delete char 1 to (offs - 1) of str
    offs = offset(">", str)
    if offs > 0 then
      selector = char 2 to (offs - 1) of str
      retlist = mParseXMLString(me, str, selector, convertValues)
      --      put the milliseconds - ms
      return retlist[1].getaprop(symbol(selector))
    end if
  end if
  --  put the milliseconds - ms
  return [:]
end


-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-- this is just backward compatibility with older scripts

on mGetListFromXMLStringX me, str, convertValues, withParams
  return mGetListFromXMLString(me, str, convertValues, withParams)
end

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

on mGetListFromXMLString me, str, convertValues, withParams
  -----------------------------------
  -- CREATED: 06.03.2008
  -- ACTION: Convert xml string to lingo list using the xmlparser xtra, if possible
  --         MUCH FASTER than the above: Use the XML xtra to parse the string
  --         BUT it must be a valid xml string, the above is slower but allows more malformed xml
  -- INPUT: 
  --         <str> format: #string; split a string using <> and </> tags into lingo list
  --         <convertValues> #integer
  --           -- -- 0 => don't convert (fast, all values are strings)
  --           -- -- 1 => convert only integer() and float() (slower)
  --           -- -- 2 => try to convert all data with value(), even parse for colors in hexstring format (slow)
  -- RETURNS: property list
  -- EXAMPLE: lingo_list = mGetListFromXMLString(me, saveString)
  -- CHANGES: resorts to the slower lingo function on xml parser error. So this handler can always be used.
  -----------------------------------
  
  if ilk(str) <> #string then str = string(str)
  
  ------------------ First check for the XML Parser xtra version 10
  if voidP(pXmlxtraversion) then
    
    listOfXtras = the xtraList
    pXmlxtraversion = ""
    repeat with currentXtra in listOfXtras
      theName = string(currentXtra.getaprop(#filename))
      if length(theName) < 1 then theName = string(currentXtra.getaprop(#name))
      if theName contains "XmlParser" then
        pXmlxtraversion =string(currentXtra.getaprop(#version))
        exit repeat
      end if
    end repeat
    
    if length(pXmlxtraversion) then
      offs = offset(".", pXmlxtraversion)
      if offs > 0 then
        intVers = char 1 to offs of pXmlxtraversion
        delete char 1 to offs of pXmlxtraversion
      else
        intVers = ""
      end if
      cnt = length(pXmlxtraversion)
      repeat with n = 1 to cnt
        c = pXmlxtraversion.char[n]
        if integerP(integer(c)) then
          put c after intVers
        else if c <> "." then
          exit repeat
        end if
      end repeat
      
      pXmlxtraversion = value(intVers)
    else
      pXmlxtraversion = 0
    end if
    
  end if
  
  if pXmlxtraversion < 10 then return mGetListFromXMLStringlingo(me, str, convertValues, withParams)
  ------------------ end XML Parser xtra version 10 check
  
  
  ms = the milliseconds
  
  if voidP(convertValues) then convertValues = 2
  
  offs = offset("<", str)
  if offs < 1 then return [:]
  if offs > 1 then delete char 1 to (offs - 1) of str
  offs = offset("<?xml", str)
  if offs <> 1 then
    offs = offset("<? xml", str)
    if offs <> 1 then
      put "<?xml version="&QUOTE&"1.0"&QUOTE&&"encoding="&QUOTE&"ISO-8859-1"&QUOTE&&"?>"&RETURN before str
    end if
  end if
  
  
  if not(objectP(pXMLParserXtra)) then pXMLParserXtra = new(xtra "XmlParser")
  pXMLParserXtra.parseString(str)
  
  if not(voidP(pXMLParserXtra.getError())) then
    put "Script: PseudoXMLPS; Handler: mGetListFromXMLString; error:" && pXMLParserXtra.getError()
    return mGetListFromXMLStringlingo(me, str, convertValues, withParams)
  end if
  
  xx = pXMLParserXtra.makePropList()
  dontEscapeSpecialChars = 1 -- we do not need to do this, as the xmlparser xtra already did it for us
  li = mConvertXMLPropList(me, [xx], convertValues, dontEscapeSpecialChars)
  
  -- put "PseudoXMLPS: mGetListFromXMLString:" && the milliseconds - ms
  
  if not(listP(li)) then return [:]
  if count(li) < 1 then return [:]
  return li[1]
  
end

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
on _______________READ_WRITE_EXTERNAL_FILES
end
-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

on mReadXML_2_List me, thePath
  -----------------------------------
  -- CREATED: 06.03.2008
  -- ACTION: read external xml file to lingo list
  -- INPUT: <thePath> : string ; optional pathname. if no pathname or "", then a file save dialog is shown
  -- RETURNS: property list
  -----------------------------------
  
  dertext = xscr(#FileIOFunktionen).mGetTextFromFile(thePath, void, "windows-1252")
  if length(dertext) > 0 then return mGetListFromXMLString(me, dertext)
  return [:]
end

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

on mSaveList_2_XML me, theList, thePath
  -----------------------------------
  -- CREATED: 06.03.2008
  -- ACTION: write liongo list to external xml file
  -- INPUT: 
  --          <theList> : linear or property list
  --          <thePath> : string ; optional pathname. if no pathname or "", then a file selection dialog is shown
  -- RETURNS: full pathname of newly created file, if successful, otherwise 0
  -----------------------------------
  
  if not(listP(theList)) then return 0
  theResult = mGetXMLStringFromList(me, theList)
  return xscr(#FileIOFunktionen).mSaveToTextFile(theResult, thePath)
end


-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-- xxxxxxxxxxxxxxxxxx Privat Handlers
-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-- (used recursively for mGetXMLStringFromList() and mGetListFromXMLString())


on mBuildXMLString me, eineListe, tiefe, strict, dontReplaceGT, withParams
  praefix = ""
  if ilk(tiefe) = #integer then
    t2 = tiefe + 1
    repeat with n = 1 to tiefe
      put TAB after praefix
    end repeat
  else
    t2 = void
  end if
  
  anz = count(eineListe)
  prop = (ilk(eineListe) = #propList)
  
  str = ""
  repeat with n = 1 to anz
    if prop then
      ident = eineListe.getPropAt(n)
      if strict then
        if [#symbol, #integer, #float].getPos(ilk(ident)) < 1 then
          ident = string(ident)
          offs = offset(SPACE, ident)
          repeat while offs
            put "_" into char offs of ident
            offs = offset(SPACE, ident)
          end repeat
        end if
      end if
    else
      ident = "item"&n
    end if
    
    val = eineListe[n]
    theIlk = ilk(val)
    
    if withParams then
      put praefix & "<" & ident && "ilk=" & QUOTE & theIlk & QUOTE & ">" after str
    else
      put praefix & "<" & ident & ">" after str
    end if
    
    suffix = ""
    case theIlk of
        -- alex am Freitag 21.April.2006 16:31:57
        --------------------------------------------------------
      #list:
        if count(val) then
          put RETURN after str
          put mBuildXMLString(me, val, t2, strict, dontReplaceGT, withParams) after str -- <- rekursiv
          suffix = praefix
        else
          put "[]" after str
        end if
      #proplist:
        if count(val) then
          put RETURN after str
          put mBuildXMLString(me, val, t2, strict, dontReplaceGT, withParams) after str -- <- rekursiv
          suffix = praefix
        else
          put "[:]" after str
        end if
        --------------------------------------------------------
        -- // alex am Freitag 21.April.2006 16:31:57
      #color:
        put val.hexstring() after str
      #symbol:
        put "#" & val after str
      otherwise
        if not dontReplaceGT then -- in this case I needed "<![CDATA[02]]>" and so do not convert < and >
          
          -- escape <>&'"
          val = mEscapeSpecialChars(me, val)
          
        end if
        put val after str
    end case
    put suffix & "</" & ident & ">" & RETURN after str
  end repeat
  
  return str
end


-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

on mParseXMLString me, str, selector, convertValues
  
  offs = offset("<", str)
  
  if offs > 0 then
    delete char 1 to offs of str
    offs = offset(">", str)
    if offs > 0 then
      sel = char 1 to (offs - 1) of str
      
      attr = ""
      theparams = sel.word[2 .. sel.word.count]
      if length(theparams) > 0 then
        poffs = offset("=", theparams)
        if poffs > 0 then
          if char 1 to poffs-1 of theparams = "ilk" then
            attr = char poffs+1 to length(theparams) of theparams
            if length(attr) > 0 then
              if char 1 of attr = QUOTE then delete char 1 of attr
              if length(attr) > 0 then
                if the last char of attr = QUOTE then delete the last char of attr
              end if
            end if
          end if
        end if
      end if
      
      sel = sel.word[1]
      
      if offset("item", sel) = 1 then retlist = []
      else retlist = [:]
      
      delete char 1 to offs of str
      
      offs = offset("<", str)
      repeat while offs > 0
        
        
        if the last char of sel = "/" then
          
          delete the last char of sel
          
          if ilk(retList) = #proplist then
            retList.addProp(symbol(sel), 1)
          else
            retList.add(1)
          end if
          
          if char (offs + 1) of str = "/" then
            delete char 1 to (offs - 1) of str
            offs = offset(">", str)
            if offs > 0 then
              delete char 1 to offs of str
            end if
            
            repeat while str.length
              if ([SPACE, TAB, RETURN].getPos(str.char[1]) > 0) then
                delete char 1 of str
              else
                exit repeat
              end if
            end repeat
            
            exit repeat
          else
            delete char 1 to offs of str
            offs = offset(">", str)
            if offs > 0 then
              sel = char 1 to (offs - 1) of str
              --      theparams = sel.word[2 .. sel.word.count]
              sel = sel.word[1]
              
              delete char 1 to offs of str
              
              offs = offset("<", str)
              
            else
              exit repeat 
            end if
          end if
          
          
          
        else if char (offs + 1) to (offs + 1 + sel.length) of str = "/"&sel then
          
          if offs > 1 then val = char 1 to (offs - 1) of str
          else val = ""
          
          delete char 1 to (offs + 2 + sel.length) of str
          repeat while str.length
            if ([SPACE, TAB, RETURN].getPos(str.char[1]) > 0) then
              delete char 1 of str
            else
              exit repeat
            end if
          end repeat
          
          
          
          
          if length(attr) > 0 then
            
            attr = symbol(attr)
            case attr of
              #string:
                
                -- unescape <>&'"
                val = mUnEscapeSpecialChars(me, val)
                
                
              #integer:
                val = integer(val)
              #float:
                val = float(val)
              otherwise
                v2 = value(val)
                if ilk(v2) <> attr then val = void
                
            end case
            
          else
            
            -- simple case, only check for integer and float values:
            if convertValues = 1 then
              
              v2 = mConvertToNumber(me, val)
              if voidP(v2) then
                
                -- unescape <>&'"
                val = mUnEscapeSpecialChars(me, val)
                
              else
                val = v2
              end if
              
              
              -- slower case, check for hex color values and value():
            else if convertValues = 2 then
              
              v2 = mFindIlk(me, val)
              if voidP(v2) then
                
                -- unescape <>&'"
                val = mUnEscapeSpecialChars(me, val)
                
              else
                val = v2
              end if
              
              
            else
              
              -- unescape <>&'"
              val = mUnEscapeSpecialChars(me, val)
              
              
            end if
            
          end if -- attr found
          
          
          if ilk(retList) = #proplist then
            retList.addProp(symbol(sel), val)
          else
            retList.add(val)
          end if
          
          offs = offset("<", str)
          
          if offs > 0 then
            if char (offs + 1) of str = "/" then
              delete char 1 to (offs - 1) of str
              offs = offset(">", str)
              if offs > 0 then
                delete char 1 to offs of str
              end if
              
              repeat while str.length
                if ([SPACE, TAB, RETURN].getPos(str.char[1]) > 0) then
                  delete char 1 of str
                else
                  exit repeat
                end if
              end repeat
              
              exit repeat
            else
              delete char 1 to offs of str
              offs = offset(">", str)
              if offs > 0 then
                sel = char 1 to (offs - 1) of str
                --      theparams = sel.word[2 .. sel.word.count]
                sel = sel.word[1]
                
                delete char 1 to offs of str
                
                offs = offset("<", str)
                
              else
                exit repeat 
              end if
            end if
          else
            exit repeat
          end if
          
        else if char (offs + 1) to (offs + 1 + selector.length) of str = "/"&selector then
          
          delete char 1 to (offs + 2 + selector.length) of str
          
          repeat while str.length
            if ([SPACE, TAB, RETURN].getPos(str.char[1]) > 0) then
              delete char 1 of str
            else
              exit repeat
            end if
          end repeat
          
          exit repeat
        else
          strLi = mParseXMLString(me, str, sel, convertValues)
          
          if ilk(retList) = #proplist then
            retList.addProp(symbol(sel), strLi[1])
          else
            retList.add(strLi[1])
          end if
          
          str = strLi[2]
          
          offs = offset("<", str)
          
          if offs > 0 then
            if char (offs + 1) of str = "/" then
              delete char 1 to (offs - 1) of str
              offs = offset(">", str)
              if offs > 0 then
                delete char 1 to offs of str
              end if
              
              repeat while str.length
                if ([SPACE, TAB, RETURN].getPos(str.char[1]) > 0) then
                  delete char 1 of str
                else
                  exit repeat
                end if
              end repeat
              
              exit repeat
            else
              delete char 1 to offs of str
              offs = offset(">", str)
              if offs > 0 then
                sel = char 1 to (offs - 1) of str
                --                theparams = sel.word[2 .. sel.word.count]
                sel = sel.word[1]
                
                delete char 1 to offs of str
                
                offs = offset("<", str)
                
              else
                exit repeat 
              end if
            end if
          else
            exit repeat
          end if
          
        end if
        
      end repeat
      
      return [retList, str]
    end if
    
  end if
  
  return [[], str]
end

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

on mConvertToNumber me, val
  if voidP(val) then return void
  
  val = word 1 to the number of words of val of val
  len = length(val)
  if len < 1 then return void
  
  v2 = integer(val)
  if integerP(v2) then
    
    if val = "-" then return void
    if length(string(v2)) <> len then return void
    
    return v2
  end if
  
  v2 = float(val)
  if floatP(v2) then
    
    --    if length(string(v2)) <> len then return void
    
    return v2
  end if
  
  return void
end

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

on mFindIlk me, val
  if voidP(val) then return void
  
  if val.length = 7 then
    if (val.char[1] = "#") then
      isColor = 1
      repeat with n = 2 to 7
        if (offset(val.char[n], "abcdef0123456789") < 1) then
          isColor = 0
          exit repeat
        end if
      end repeat
      if isColor = 1 then return rgb(val)
    end if
  end if 
  
  v2 = mConvertToNumber(me, val)
  if not(voidP(v2)) then return v2
  
  if val = "" then return val
  
  v2 = value(val)
  
  if [#vector, #rect, #point, #float].getPos(ilk(v2))  then return v2
  
  if abs(length(string(v2)) - length(val)) > 1 then return void -- in case of symbols the # gets stripped, when we use the string() function
  
  return v2
end


-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

on mRemoveXMLComments me, str
  -- remove comments:
  -- version:
  offs = offset("<?", str)
  repeat while offs
    ende = offset("?>", str)
    if ende > 0 then
      delete char offs to (ende + 1) of str
    else
      return [[], str]
    end if
    offs = offset("<?", str)
  end repeat
  
  -- doctype:
  offs = offset("<!Doctype", str)
  repeat while offs
    ende = offset(">", str.char[offs .. str.length])
    if ende > 0 then
      delete char offs to (ende + offs - 1) of str
    else
      return [[], str]
    end if
    offs = offset("<!Doctype", str)
  end repeat
  
  -- comments
  offs = offset("<!--", str)
  repeat while offs
    ende = offset("-->", str)
    if ende > 0 then
      delete char offs to (ende + 2) of str
    else
      return [[], str]
    end if
    offs = offset("<!--", str)
  end repeat
  
  return str
end

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

on mEscapeSpecialChars me, str
  
  val = mReplaceAllGT(me, string(str), "&", "&amp;")
  val = mReplaceAllGT(me, val, "<", "&lt;")
  val = mReplaceAllGT(me, val, ">", "&gt;")
  val = mReplaceAllGT(me, val, "'", "&apos;")
  val = mReplaceAllGT(me, val, QUOTE, "&quot;")
  
  return val
  
end

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

on mUnEscapeSpecialChars me, str
  
  val = mReplaceAllGT(me, string(str), "&lt;", "<")
  val = mReplaceAllGT(me, val, "&gt;", ">")
  val = mReplaceAllGT(me, val, "&apos;", "'")
  val = mReplaceAllGT(me, val, "&quot;", QUOTE)
  val = mReplaceAllGT(me, val, "&amp;", "&")
  
  return val
end

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

on mReplaceAllGT me, srcText, fromChunk, toChunk
  if the runmode contains "plugin" then return mReplaceChunkGT(me, srcText, fromChunk, toChunk)
  if mCheckForRegEx(me) = 0 then return mReplaceChunkGT(me, srcText, fromChunk, toChunk)
  
  -- mit pregEx()
  retlist = [srcText]
  PRegEx_Replace(retlist, PRegEx_QuoteMeta(fromChunk) , "ig", PRegEx_QuoteMeta(toChunk))
  
  return retlist[1]
end

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

-- mit offset()
on mReplaceChunkGT me, srcText, fromChunk, toChunk
  
  stelle = offset(fromChunk, srcText)
  
  offs = 0
  if stelle then
    
    newtext = ""
    repeat while stelle
      if stelle > 1 then put char 1 to (stelle - 1) of srcText after newtext
      put toChunk after newtext
      delete char 1 to (stelle + fromChunk.length - 1) of srcText
      stelle = offset(fromChunk, srcText)
      if stelle = 0 then put srcText after newtext
    end repeat
    
  else
    return srcText
  end if
  
  return newtext
end

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

property pRegExtra

on mCheckForRegEx me
  if voidP(pRegExtra) then
    pRegExtra = 0
    repeat with n = the number of xtras down to 1
      if (the name of xtra n = "PRegEx") then
        pRegExtra = 1
        exit repeat
      end if
    end repeat
  end if
  return pRegExtra
end


-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

on mConvertXMLPropList me, inputlist, convertValues, dontEscapeSpecialChars
  retlist = [:]
  anz = count(inputlist)
  repeat with n = 1 to anz
    val = inputlist[n].getaprop(#child)
    
    if listP(val) then
      
      if n = 1 then
        propname = inputlist[n].getaprop(#name)
        if offset("item", propname) = 1 then
          retlist = []
        end if
      end if
      
      if count(val) then
        if ilk(retlist) = #proplist then
          retlist.addProp(symbol(inputlist[n].getaprop(#name)), mConvertXMLPropList(me, val, convertValues, dontEscapeSpecialChars))
        else
          retlist.add(mConvertXMLPropList(me, val, convertValues, dontEscapeSpecialChars))
        end if
      else
        val = inputlist[n].getaprop(#chardata)
        
        attr = ""
        attrLi = inputlist[n].getaprop(#attributes)
        if ilk(attrLi) = #proplist then
          attr = string(attrLi.getaprop(#ilk))
        end if
        
        
        
        if length(attr) > 0 then
          
          attr = symbol(attr)
          case attr of
            #string:
              
              -- unescape <>&'"
              if dontEscapeSpecialChars <> 1 then val = mUnEscapeSpecialChars(me, val)
              
            #integer:
              val = integer(val)
            #float:
              val = float(val)
            otherwise
              v2 = value(val)
              if ilk(v2) <> attr then val = void
              
          end case
          
        else
          
          -- simple case, only check for integer and float values:
          if convertValues = 1 then
            
            v2 = mConvertToNumber(me, val)
            if voidP(v2) then
              
              -- unescape <>&'"
              if dontEscapeSpecialChars <> 1 then val = mUnEscapeSpecialChars(me, val)
              
            else
              val = v2
            end if
            
            
            -- slower case, check for hex color values and value():
          else if convertValues = 2 then
            
            v2 = mFindIlk(me, val)
            if voidP(v2) then
              
              -- unescape <>&'"
              if dontEscapeSpecialChars <> 1 then val = mUnEscapeSpecialChars(me, val)
              
            else
              val = v2
            end if
            
            
          else
            
            -- unescape <>&'"
            if dontEscapeSpecialChars <> 1 then val = mUnEscapeSpecialChars(me, val)
            
            
          end if
          
        end if -- attr found
        
        
        
        --      if ((not(voidP(v2))) and string(v2).length) then val = v2
        
        if ilk(retlist) = #proplist then
          retlist.addProp(symbol(inputlist[n].getaprop(#name)), val)
        else
          retlist.add(val)
        end if
        
      end if
      
    end if
    
  end repeat
  
  return retlist
end


-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

on mParseExcelXML me, theText
  -----------------------------------
  -- ACTION: Read and convert an excel xml file into a lingo property list
  -- INPUT: <theText> ; string ; required => xml formatted string
  -- RETURNS: property list
  -- EXAMPLE: plist = new(script "PseudoXMLPS").mParseExcelXML(xscr(#FileIOFunktionen).mGetTextFromFile())
  -----------------------------------
  
  ms = the milliseconds
  li = []
  xmlp = new(xtra "xmlparser")
  xmlp.parseString(theText)
  if voidP(xmlp.getError()) then
    xmlList = xmlp.makeList()
    if listP(xmlList) then
      if count(xmlList) > 0 then
        xmlList = xmlList[1]
        if ilk(xmlList) = #proplist then
          xmlList = xmlList.getaprop("Workbook")
          if ilk(xmlList) = #proplist then
            xmlList = xmlList.getaprop("Worksheet")
            if ilk(xmlList) = #proplist then
              xmlList = xmlList.getaprop("Table")
              if listP(xmlList) then
                rowCnt = count(xmlList)
                repeat with n = 2 to rowCnt
                  thisRow = []
                  li.add(thisRow)
                  currRow = xmlList[n]
                  if listP(currRow) then
                    cellCnt = count(currRow)
                    repeat with m = 2 to cellCnt
                      thisCell = currRow[m].getaprop("Data")
                      if ilk(thisCell) = #proplist then
                        dertyp = thisCell.getaprop("!ATTRIBUTES")
                        if ilk(dertyp) = #proplist then dertyp = dertyp.getaprop("ss:Type")
                        case dertyp of
                          "Number":
                            thisRow.add(float(thisCell.getaprop("!CHARDATA")))
                          otherwise
                            thisRow.add(thisCell.getaprop("!CHARDATA"))
                        end case
                        
                      else
                        put "mParseExcelXML: Row "&n&" Cell "&m&" is not a property list"
                      end if -- if ilk(thisCell) = #proplist then
                      
                    end repeat -- repeat through cells
                    
                  else
                    put "mParseExcelXML: Row "&n&" is not a list"
                  end if -- if listP(currRow) then
                  
                end repeat -- repeat through rows
                
              else
                put "mParseExcelXML: Table not found"
              end if -- if listP(xmlList) then
              
            else
              put "mParseExcelXML: Worksheet not found"
            end if -- if ilk(xmlList) = #proplist then
            
          else
            put "mParseExcelXML: Workbook not found"
          end if -- if ilk(xmlList) = #proplist then
          
        else
          put "mParseExcelXML: ROOT is not a property list"
        end if --if ilk(xmlList) = #proplist then
        
      else
        put "mParseExcelXML: Xml parser makelist() returned empty list"
      end if -- if count(xmlList) > 0 then
      
    else
      put "mParseExcelXML: Xml parser makelist() failed"
    end if -- if listP(xmlList) then
    
  else
    put "mParseExcelXML: Xml parser error: "&xmlp.getError()
  end if -- if voidP(xmlp.getError()) then
  
  xmlp = 0
  --  put the milliseconds - ms
  return li
end

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-- read apple style plist file

on mReadPList me, pfad
  -----------------------------------
  -- ACTION: Read and convrt an apple style plist into a lingo style property list from file
  -- INPUT: <pfad> ; string ; full pathname to plist file. This is optional, if it is void or "" a file selection dialog is displayed 
  -- RETURNS: property list
  -- EXAMPLE: plist = new(script "PseudoXMLPS").mReadPList()
  -----------------------------------
  
  str = ""
  
  if string(pfad) starts "/" then
    if mCheckShellXtra(me) <> 1 then return ["You need the Shell xtra to convert binary plist files"]
    str = string(shell_cmd("cat "&QUOTE&pfad&QUOTE))
  else
    fio = new(xtra "fileio")
    if (voidP(pfad) or (pfad = "")) then pfad = fio.displayOpen()
    
    if ilk(pfad) <> #string then return []
    if not(pfad.length) then return []
    
    fio.openFile(pfad, 1)
    if fio.status() <> 0 then
      retval = [fio.error(fio.status())]
      fio = void
      return retval
    end if
    
    str = fio.readFile()
    fio.closeFile()
    fio = void
  end if
  
  
  if not(str starts "<?xml") then -- since 10.4 plist files are no longer in raw text format, we need to convert the binary file
    
    if not(pfad starts "/") then
      -- convert to unix path
      offs = offset(":", pfad)
      repeat while offs > 0
        put "/" into char offs of pfad
        offs = offset(":", pfad)
      end repeat
      put "/Volumes/" before pfad
    end if
    
    if mCheckShellXtra(me) <> 1 then return ["You need the Shell xtra to convert binary plist files"]
    
    shell_cmd("plutil -convert xml1 -o /var/tmp/tempplist "&QUOTE&pfad&QUOTE)
    str = shell_cmd("cat /var/tmp/tempplist")
  end if
  
  
  return mGetListFromPListString(me, str)
  
end

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-- this is required to convert a 10.4 style binary plist file to xml:

property pShellXtraPresent

on mCheckShellXtra me
  if voidP(pShellXtraPresent) then
    pShellXtraPresent = 0
    repeat with n = the number of xtras down to 1
      if (the name of xtra n = "Shell") then
        pShellXtraPresent = 1
        exit repeat
      end if
    end repeat
  end if
  return pShellXtraPresent
end

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-- read apple style plist file

on mGetListFromPListString me, str
  return mConvertKeyList(me, mGetListFromXMLString(me, str, 0))
end


-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-- convert apple style plist

on mConvertKeyList me, inputlist
  if ilk(inputlist) <> #proplist then return inputlist
  retlist = [:]
  cnt = count(inputlist)
  
  repeat with n = 1 to cnt
    if (inputlist.getpropAt(n) = #key) then
      prop = inputlist[n]
      n = n + 1
      theIlk = inputlist.getpropAt(n)
      retlist.addProp(prop, mGetKeyListValue(me, theIlk, inputlist[n]))
    else
      retlist.addProp(#dict, mConvertKeyList(me, inputlist[n]))
    end if
  end repeat
  
  return retlist
  
end

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

on mGetKeyListValue me, theIlk, theValue
  case theIlk of
    #array:
      li = []
      if ilk(theValue) = #proplist then
        cnt = count(theValue)
        repeat with n = 1 to cnt
          li.add(mGetKeyListValue(me, theValue.getPropAt(n), theValue[n]))
        end repeat
      end if
      return li
    #dict:
      return mConvertKeyList(me, theValue)
    #real:
      return float(theValue)
    #integer:
      return integer(theValue)
    #false:
      return 0
    #true:
      return 1
    otherwise
      return string(theValue)
  end case
end


-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-- fontmapping
-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

on mMapCharCodes me, str, whichDirection
  
  if mCheckForRegEx(me) = 1 then
    return mMapCharsCodesP(me, str, whichDirection)
  end if
  
  replLi = mGetCharMapList(me)
  
  if whichDirection = #Win2Mac then
    indTo = 1
    indFrom = 2
  else
    indTo = 2
    indFrom = 1
  end if
  
  if getVersionNumber(me) > 10.99 then
    -- director 11 will have unicode numbers for the chars
    indFrom = 3
  end if
  
  
  newStr = str
  repeat with repl in replLi
    tstr = str
    vers = 0
    searchChar = numToChar(repl[indFrom])
    replaceChar = numToChar(repl[indTo])
    offs = offset(searchChar, tstr)
    repeat while offs > 0
      ctn = charToNum(char offs of tstr)
      delete char 1 to offs of tstr
      vers = vers + offs
      if ctn = repl[indFrom] then
        put replaceChar into char vers of newStr
      end if
      offs = offset(searchChar, tstr)
    end repeat
  end repeat
  
  zehn = numToChar(10)
  offs = offset(zehn, newStr)
  repeat while offs > 0
    delete char offs of newStr
    offs = offset(zehn, newStr)
  end repeat
  
  return newStr
end

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-- using pregex:

on mMapCharsCodesP me, str, whichDirection
  strli = [str]
  li = mGetPregTranslateStrings(me)
  
  if whichDirection = #Win2Mac then
    pregex_translate(strli, li[2], li[1])
    
    PRegEx_Replace(strli, "\" & numToChar(10), "g", "")
    
  else
    pregex_translate(strli, li[1], li[2])
    
  end if
  
  return strli[1]
end

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

on mGetPregTranslateStrings me
  
  replLi = mGetCharMapList(me)
  fromStr = ""
  toStr = ""
  
  if getVersionNumber(me) > 10.99 then
    -- director 11 will have unicode numbers for the chars
    fromIndex = 3
  else
    fromIndex = 1
  end if
  
  repeat with repl in replLi
    put numToChar(repl[fromIndex]) after fromStr
    put numToChar(repl[2]) after toStr
  end repeat
  
  return [fromStr, toStr]
  
end

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

on mGetCharMapList me
  li = []
  li.add([128, 196, 402])
  li.add([129, 197, 8776])
  li.add([130, 199, 171])
  li.add([131, 201, 8230])
  li.add([132, 209, 8212])
  li.add([133, 214, 247])
  li.add([134, 220, 8249])
  li.add([135, 225, 183])
  li.add([136, 224, 8225])
  li.add([137, 226, 8218])
  li.add([138, 228, 8240])
  li.add([139, 227, 8222])
  li.add([140, 229, 194])
  li.add([141, 231, 193])
  li.add([142, 233, 200])
  li.add([143, 232, 203])
  li.add([144, 234, 205])
  li.add([145, 235, 206])
  li.add([146, 237, 204])
  li.add([147, 236, 207])
  li.add([148, 238, 211])
  li.add([149, 239, 212])
  li.add([150, 241, 210])
  li.add([151, 243, 219])
  li.add([152, 242, 218])
  li.add([153, 244, 217])
  li.add([154, 246, 710])
  li.add([155, 245, 305])
  li.add([156, 250, 729])
  li.add([157, 249, 728])
  li.add([158, 251, 730])
  li.add([159, 252, 184])
  li.add([160, 134, 220])
  li.add([161, 176, 8734])
  li.add([164, 167, 223])
  li.add([165, 149, 239])
  li.add([166, 182, 8706])
  li.add([167, 223, 64258])
  li.add([168, 174, 198])
  li.add([170, 153, 244])
  li.add([171, 180, 165])
  li.add([172, 168, 174])
  li.add([173, 141, 231])
  li.add([174, 198, 8710])
  li.add([175, 216, 255])
  li.add([176, 144, 234])
  li.add([178, 143, 232])
  li.add([179, 142, 233])
  li.add([180, 165, 8226])
  li.add([182, 240, 63743])
  li.add([183, 221, 8250])
  li.add([184, 222, 64257])
  li.add([185, 254, 731])
  li.add([186, 138, 228])
  li.add([187, 170, 8482])
  li.add([188, 186, 8747])
  li.add([189, 253, 733])
  li.add([190, 230, 202])
  li.add([191, 248, 175])
  li.add([192, 191, 248])
  li.add([193, 161, 176])
  li.add([194, 172, 168])
  li.add([195, 175, 216])
  li.add([196, 131, 201])
  li.add([197, 188, 186])
  li.add([198, 208, 8211])
  li.add([199, 171, 180])
  li.add([200, 187, 170])
  li.add([201, 133, 214])
  li.add([202, 160, 8224])
  li.add([203, 192, 191])
  li.add([204, 195, 8730])
  li.add([205, 213, 8217])
  li.add([206, 140, 229])
  li.add([207, 156, 250])
  li.add([208, 173, 8800])
  li.add([209, 151, 243])
  li.add([210, 147, 236])
  li.add([211, 148, 238])
  li.add([212, 145, 235])
  li.add([213, 146, 237])
  li.add([214, 247, 732])
  li.add([216, 255, 711])
  li.add([217, 159, 252])
  li.add([218, 158, 251])
  li.add([219, 128, 196])
  li.add([220, 139, 227])
  li.add([221, 155, 245])
  li.add([222, 128, 196])
  li.add([223, 129, 197])
  li.add([224, 135, 225])
  li.add([225, 183, 8721])
  li.add([226, 130, 199])
  li.add([227, 132, 209])
  li.add([228, 137, 226])
  li.add([229, 194, 172])
  li.add([230, 202, 160])
  li.add([231, 193, 161])
  li.add([232, 203, 192])
  li.add([233, 200, 187])
  li.add([234, 205, 213])
  li.add([235, 206, 338])
  li.add([236, 207, 339])
  li.add([237, 204, 195])
  li.add([238, 211, 8221])
  li.add([239, 212, 8216])
  li.add([240, 157, 249])
  li.add([241, 210, 8220])
  li.add([242, 218, 8260])
  li.add([243, 219, 8364])
  li.add([244, 217, 376])
  li.add([245, 166, 182])
  li.add([246, 136, 224])
  li.add([247, 152, 242])
  li.add([248, 150, 241])
  li.add([249, 154, 246])
  li.add([250, 178, 8804])
  li.add([251, 190, 230])
  li.add([252, 184, 8719])
  li.add([253, 189, 937])
  li.add([254, 179, 8805])
  li.add([255, 185, 960])
  return li
end

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

on getVersionNumber me
  if voidP(pVersionNumber) then
    pVersionNumber = getFloatVersionNumber(me, the productVersion)
  end if
  return pVersionNumber
end

-- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

on getFloatVersionNumber me, prodVers
  offs = offset(".", prodVers)
  if offs > 0 then
    intVers = char 1 to offs of prodVers
    delete char 1 to offs of prodVers
  else
    intVers = ""
  end if
  cnt = length(prodVers)
  repeat with n = 1 to cnt
    c = prodVers.char[n]
    if integerP(integer(c)) then
      put c after intVers
    else if c <> "." then
      exit repeat
    end if
  end repeat
  return value(intVers)
end

