BDG Scenario 3

Convert (Form4.frm)

VERSION 5.00
Object = "{831FDD16-0C5C-11D2-A9FC-0000F8754DA1}#2.0#0"; "MSCOMCTL.OCX"
Begin VB.Form Convert 
   Caption         =   "Please Wait"
   ClientHeight    =   1185
   ClientLeft      =   60
   ClientTop       =   345
   ClientWidth     =   4680
   LinkTopic       =   "Form4"
   ScaleHeight     =   1185
   ScaleWidth      =   4680
   StartUpPosition =   3  'Windows Default
   Begin VB.CommandButton Command1 
      Caption         =   "Cancel"
      Height          =   375
      Left            =   1440
      TabIndex        =   1
      Top             =   720
      Width           =   1695
   End
   Begin MSComctlLib.ProgressBar ProgressBar1 
      Height          =   255
      Left            =   120
      TabIndex        =   0
      Top             =   360
      Width           =   4455
      _ExtentX        =   7858
      _ExtentY        =   450
      _Version        =   393216
      Appearance      =   1
   End
   Begin VB.Label Label1 
      Caption         =   "Traversing HTMLDOM element tree..."
      Height          =   255
      Left            =   120
      TabIndex        =   2
      Top             =   120
      Width           =   4455
   End
End
Attribute VB_Name = "Convert"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit

Const N_MAX_PROGRESS = 200
Const N_INDENT_INCREMENT = 3

Public m_rootElem As Object   ' Set to root of HTMLDOM before invoking
Public m_strHTML As String    ' Converted HTML/XML text
Public m_bKill As Boolean     ' True, if dialog was canceled

Private m_sCloseTag As String
Private m_bCompleteURL As Boolean
Private m_xmlDoc As New MSXML.DOMDocument

Private Sub Form_Load()
   ProgressBar1.Max = N_MAX_PROGRESS
End Sub

Private Sub Form_Activate()
   Dim xelem As MSXML.IXMLDOMElement
   m_strHTML = ""
   m_bCompleteURL = g_Options.CompleteURL
   m_sCloseTag = IIf(g_Options.ConvertToXML, " />", ">")
   If g_Options.UseLingoFile Then
      m_xmlDoc.async = False
      m_xmlDoc.preserveWhiteSpace = True
      m_bKill = Not m_xmlDoc.Load(g_Options.LingoFilePath)
      If m_bKill Then
         ' If file not found, then create a default version
         If m_xmlDoc.ParseError.errorCode = &H800C0006 Then
            Set xelem = m_xmlDoc.createElement("UIElement")
            ' BUG: Should change values according to source language
            xelem.setAttribute "CHARSET", "ISO-8859-1"
            xelem.setAttribute "CODEPAGE", "1252"
            xelem.setAttribute "xmlns", "x-schema:LingoSchema.xml"
            Set m_xmlDoc.documentElement = xelem
            m_bKill = False
         ' Otherwise, report error and allow fail
         Else
            ParseError m_xmlDoc.ParseError
         End If
      End If
   Else
      m_bKill = False
   End If
   TraverseTree m_rootElem
   If m_bKill Then
      UpdateUser "Conversion canceled."
   ElseIf g_Options.UseLingoFile Then
      On Error GoTo SaveFailed ' will fail if read-only
      m_xmlDoc.save g_Options.LingoFilePath
   End If
   Unload Me
   Exit Sub
   
SaveFailed:
   MsgBox Err.Description & " (" & Hex(Err.Number) & ")", vbCritical, "Cannot Save Lingo Updates"
   Resume Next
End Sub

' Cancel
Private Sub Command1_Click()
   m_bKill = True
End Sub

Private Sub Tick()
   Static Progress As Integer
   Progress = Progress + 1
   ProgressBar1.value = (Progress Mod N_MAX_PROGRESS)
   DoEvents
End Sub

'==== Methods for HTMLDOM traversal

Private Sub TraverseTree(ByRef elem As Object, Optional ByVal indents As Integer = 0)
   Dim Child As Object, sText As String, bIsAlways As Boolean
   
   Debug.Assert elem.nodeType = 1
   
   If m_bKill Then Exit Sub
   
   Tick   ' show progress

   On Error Resume Next
   
   ' Skip elements that are only ending tags
   If Left(elem.tagName, 1) = "/" Then Exit Sub
   
   ' Special handling for HTML comments (no begin/end tags)
   If elem.tagName = "!" Then
      m_strHTML = m_strHTML & Space(indents) & elem.innerHTML
   
   ' For elements with children: use afterBegin and beforeEnd text, recursion
   ElseIf elem.canHaveChildren And elem.tagName <> "LABEL" Then
      
      m_strHTML = m_strHTML & Space(indents) & "<" & elem.tagName
      Call showAttributes(elem)
      
      sText = elem.getAdjacentText("afterBegin") ' may cause error...
      
      If elem.children.Length > 0 Then
         
         m_strHTML = m_strHTML & ">"
         If sText <> "" And g_Options.UseLingoFile Then
            Call addLingoText(elem, sText, "afterBegin")
         Else
            m_strHTML = m_strHTML & sText
         End If
         m_strHTML = m_strHTML & vbCrLf
         
         ' Recursive call for each child element
         For Each Child In elem.children
            Call TraverseTree(Child, indents + N_INDENT_INCREMENT)
         Next
         
         sText = ""
         sText = elem.getAdjacentText("beforeEnd") ' may cause error...
         If sText <> "" And g_Options.UseLingoFile Then
            Call addLingoText(elem, sText, "beforeEnd")
         Else
            m_strHTML = m_strHTML & sText
         End If
         m_strHTML = m_strHTML & Space(indents) & "</" & elem.tagName & ">"
      
      ' Child-less: use afterBegin as complete text
      Else
         
         bIsAlways = IIf(Not g_Options.ConvertToXML Or g_Options.UseAlwaysTags, _
                         g_Options.IsAlwaysTag(elem.tagName), False)
      
         If sText <> "" Or bIsAlways Then
            m_strHTML = m_strHTML & ">"
            If g_Options.UseLingoFile Then
               Call addLingoText(elem, sText, "innerText")
            Else
               m_strHTML = m_strHTML & sText
            End If
            m_strHTML = m_strHTML & "</" & elem.tagName & ">"
         Else
            m_strHTML = m_strHTML & m_sCloseTag
         End If
      End If
   
   ' No children allowed: use beforeBegin and innerHTML as text
   Else
      sText = Trim(elem.getAdjacentText("beforeBegin")) ' may cause error...
      If sText <> "" And g_Options.UseLingoFile Then
         Call addLingoText(elem, sText, "beforeBegin")
      Else
         m_strHTML = m_strHTML & sText
      End If
      
      sText = elem.innerHTML
      ' If innerHTML exists, we may need to force assignment of LID
      If sText <> "" And g_Options.UseLingoFile Then Call Lid(elem)
      
      m_strHTML = m_strHTML & Space(indents) & "<" & elem.tagName
      Call showAttributes(elem)
      
      bIsAlways = IIf(Not g_Options.ConvertToXML Or g_Options.UseAlwaysTags, _
                      g_Options.IsAlwaysTag(elem.tagName), False)
      
      
      If sText <> "" Or bIsAlways Then
         m_strHTML = m_strHTML & ">"
         If g_Options.UseLingoFile Then
            Call addLingoText(elem, sText, "innerHTML")
         Else
            m_strHTML = m_strHTML & sText
         End If
         m_strHTML = m_strHTML & "</" & elem.tagName & ">"
      Else
         m_strHTML = m_strHTML & m_sCloseTag
      End If
   End If
   
   ' Newline
   m_strHTML = m_strHTML & vbCrLf
End Sub

Private Sub showAttributes(ByRef elem As Object)
   Dim attrib As Object, bAdded As Boolean
   For Each attrib In elem.Attributes
      If attrib.specified Then
         bAdded = False
         If g_Options.UseLingoFile Then
            If g_Options.IsLingoTag(attrib.nodeName) Then
               bAdded = addLingoAttribute(elem, attrib.nodeName, attrib.nodeValue)
            End If
         End If
         If Not bAdded Then
            Select Case attrib.nodeName
            Case "bgColor", "color", "text", "link", "aLink", "vLink":
               Call addAttribute(attrib.nodeName, ColorVal(attrib.nodeValue))
            Case "height", "width", "border", "cellPadding", "cellSpacing", "SIZE", _
                 "leftMargin", "rightMargin", "topMargin", "bottomMargin":
                Call addAttribute(attrib.nodeName, SizeVal(attrib.nodeValue))
            'Case "size":   ' used by FONT tags
               'Call addAttribute(attrib.nodeName, FontSizeVal(attrib.nodeValue))
            Case "src", "href", "action":
               Call addAttribute(attrib.nodeName, URLVal(attrib.nodeValue, m_bCompleteURL))
            Case Else:
               Call addAttribute(attrib.nodeName, attrib.nodeValue)
            End Select
         End If
      End If
   Next
   ' some important INPUT fields don't seem to be included in collection
   If elem.tagName = "INPUT" Then
      If elem.value <> "" Then
         ' NOTE: Always try to add VALUE to lingo attributes
         If g_Options.UseLingoFile And LCase(elem.Type) <> "hidden" Then
            bAdded = addLingoAttribute(elem, "value", elem.value)
         Else
            bAdded = False
         End If
         If Not bAdded Then Call addAttribute("value", elem.value)
      End If
      If elem.Size <> 0 Then
         Call addAttribute("size", elem.Size)
      End If
   ElseIf elem.tagName = "FONT" Then
     If elem.Size <> 0 Then
         Call addAttribute("size", elem.Size)
     End If
   End If
   ' STYLE text is not included in Attribute collection either
   If elem.Style.cssText <> "" Then
      Call addAttribute("style", elem.Style.cssText)
   End If
   ' Now parse through the custom (expando) attributes
   Call showCustomAttrib(elem)
End Sub

' CUSTOM ATTRIBUTES aren't found in the Attributes collection,
' but they can be accessed if you know what you're looking for...
Private Sub showCustomAttrib(ByRef elem As Object)
   Dim value As String, bAdded As Boolean
   Dim i As Long, attrib As String
   On Error Resume Next
   For i = 0 To g_Options.CustomAttributeCount  ' 0 = Lingo ID attrib
      attrib = g_Options.GetCustomAttribute(i)
      value = elem.getAttribute(attrib, 1)  ' 1 = case-sensitive
      If Err Or IsNull(value) Then
         ' attribute doesn't exist
         Err.Clear
      Else
         bAdded = False
         If g_Options.UseLingoFile Then
            If g_Options.IsLingoTag(attrib) Then
               bAdded = addLingoAttribute(elem, attrib, value)
            End If
         End If
         If Not bAdded Then Call addAttribute(attrib, value)
      End If
   Next
End Sub

Private Sub addAttribute(ByRef name As String, ByRef value As String)
   Dim q As String
   If InStr(value, """") Then q = "'" Else q = """"
   m_strHTML = m_strHTML & " " & name & "=" & q & value & q
End Sub

Private Function addLingoAttribute(ByRef elem As Object, ByRef name As String, ByRef value As String)
   Dim xelem As MSXML.IXMLDOMElement
   
   If Not g_Options.UseLingoFile Then Exit Function
   
   Set xelem = GetLingoTag(elem.tagName, Lid(elem))
   If Not xelem Is Nothing Then
      ' set the element attribute
      xelem.setAttribute name, value
      If Not g_Options.StripLingoText Then
         Call addAttribute(name, "LINGO--" & value & "--LINGO")
      End If
      addLingoAttribute = True
   Else
      ' just add the attribute
      addLingoAttribute = False
   End If
End Function

Private Sub addLingoText(ByRef elem As Object, ByRef text As String, ByVal placement As String)
   Dim xelem As MSXML.IXMLDOMElement
   Dim nRet As Integer, prevPlacement As String
   
   If Not g_Options.UseLingoFile Then Exit Sub
   If Trim(text) = "" Then Exit Sub
   
   Set xelem = GetLingoTag(elem.tagName, Lid(elem))
   If Not xelem Is Nothing Then
      If xelem.text <> "" And xelem.text <> text Then
         prevPlacement = xelem.getAttribute("placement")
         nRet = MsgBox("Overwriting " & prevPlacement & " text of " & Lid(elem) & ":" & vbCrLf & _
                xelem.text & vbCrLf & vbCrLf & _
                "with the following " & placement & " text:" & vbCrLf & _
                text & vbCrLf & vbCrLf & _
                "OK to continue?", vbQuestion + vbYesNo, "Overwrite Lingo Text")
      Else
         nRet = vbYes
      End If
      
      If nRet = vbYes Then
         ' set the element text
         xelem.text = text
         xelem.setAttribute "placement", placement
         If Not g_Options.StripLingoText Then
            m_strHTML = m_strHTML & "LINGO--" & text & "--LINGO"
         End If
      End If
   Else
      ' just output the text
      m_strHTML = m_strHTML & text
   End If
End Sub

Private Function GetLingoTag(ByVal name As String, Id As String) As MSXML.IXMLDOMNode
   Dim xnode As MSXML.IXMLDOMNode
   Dim xelem As MSXML.IXMLDOMElement
   Dim nodeName As String
   
   ' We must have an ID to locate the right lingo tag
   If Id = "" Then
      Set GetLingoTag = Nothing
      Exit Function
   End If
   
   On Error GoTo ErrHandler
   
   ' First attempt: find node by LID value
   Set xelem = m_xmlDoc.nodeFromID(Id)
   If xelem Is Nothing Then
      ' If not found: find element by traversal, create elements if necessary
      Set xnode = m_xmlDoc.documentElement.selectSingleNode("//" & name & "S")
      If xnode Is Nothing Then
         Set xnode = m_xmlDoc.createElement(name & "S")
         Set xnode = m_xmlDoc.documentElement.appendChild(xnode)
      End If
      nodeName = "./" & name & "[@" & g_Options.LingoIDAttrib & "='" & Id & "']"
      Set xelem = xnode.selectSingleNode(nodeName)
      If xelem Is Nothing Then
         Set xelem = m_xmlDoc.createElement(name)
         xelem.setAttribute g_Options.LingoIDAttrib, Id
         xnode.appendChild xelem
      End If
   End If
   Set GetLingoTag = xelem
   Exit Function
   
ErrHandler:
   ParseError m_xmlDoc.ParseError
   MsgBox "FAIL: " & Err.Description & " (" & Hex(Err.Number) & ")", vbCritical
End Function

Private Function Lid(ByRef elem As Object) As String
   Dim newLID As Variant ' must be Variant for setAttribute call
   On Error Resume Next
   ' use predefined LID, if available
   newLID = elem.getAttribute(g_Options.LingoIDAttrib, 1)
   If IsNull(newLID) And g_Options.AssignLIDs Then
      newLID = Left(elem.tagName, 3) & Format((Rnd * 9999) + 1, "0000")
      ' Debug.Print elem.uniqueID & " setting LID: " & newLID
      Call elem.setAttribute(g_Options.LingoIDAttrib, newLID, True) ' 1 = respect case
   End If
   Lid = newLID & ""
End Function