1// Licensed under the Apache License, Version 2.0 (the "License"); you may not
2// use this file except in compliance with the License. You may obtain a copy of
3// the License at
4//
5//   http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10// License for the specific language governing permissions and limitations under
11// the License.
12
13function LoadData ()
14{
15    gTypeNames = [];
16    ComplexTypeNames = [];
17    SimpleTypeNames = [];
18    for (var sName in Data)
19    {
20        gTypeNames.push(sName);
21        switch(Data[sName].type)
22        {
23            case "complex-type":
24                ComplexTypeNames.push(sName);
25                break;
26
27            case "simple-type":
28                SimpleTypeNames.push(sName);
29                break;
30        }
31    }
32    document.getElementById("message").innerHTML = "there are " + ComplexTypeNames.length + " complex types and "
33        + SimpleTypeNames.length +" simple types";
34}
35
36
37
38
39function InitializeSearch ()
40{
41	CurrentTypeName = "";
42	TypeHistory = Array();
43    LoadData();
44    ShowType("A:ST_Overlap");
45}
46
47
48
49
50function CheckInput (aField, aEvent)
51{
52    switch(aEvent.keyCode)
53    {
54        case 38:
55            --selection_index;
56            if (selection_index < 0)
57                selection_index = matches.length-1;
58            break;
59        case 40:
60            ++selection_index;
61            if (selection_index >= matches.length)
62                selection_index = 0;
63            break;
64        default:
65            matches = GetMatches(aField.value);
66            selection_index = 0;
67   }
68
69    ShowMatches(matches, selection_index);
70}
71
72
73
74
75function GetMatches (sPattern)
76{
77    var aLcPatterns = sPattern.toLowerCase().split(/\s+/);
78
79    var nTypeCount = gTypeNames.length;
80
81    var aMatches = [];
82    for (index=0; index<nTypeCount; ++index)
83    {
84        var sTypeName = gTypeNames[index];
85        var aParts = new Array(sTypeName);
86        var sLcTypeName = sTypeName.toLowerCase();
87        var bIsMatch = true;
88        var nSearchStart = 0;
89        for (nPatternIndex=0; nPatternIndex<aLcPatterns.length && bIsMatch; ++nPatternIndex)
90        {
91            var sPattern = aLcPatterns[nPatternIndex];
92            var nMatchStart = sLcTypeName.indexOf(sPattern, nSearchStart);
93            if (nMatchStart >= 0)
94            {
95                var nMatchEnd = nMatchStart + sPattern.length;
96                aParts.push(sTypeName.substring(nSearchStart, nMatchStart));
97                aParts.push(sTypeName.substring(nMatchStart, nMatchEnd));
98                nSearchStart = nMatchEnd;
99            }
100            else
101            {
102                // Only some patterns are matched.
103                bIsMatch = false;
104            }
105        }
106        if (bIsMatch)
107        {
108            if (nMatchEnd < sTypeName.length-1)
109                aParts.push(sTypeName.substring(nMatchEnd));
110            aMatches.push(aParts);
111        }
112    }
113    return aMatches;
114}
115
116
117
118
119/** Show the matching types.
120 *  As there can be a great many matching types, some sort of abbreviation is necessary.
121 *  Format all matches into lines of n entries.
122 *  Show the line containing the selected match and the ones before and after.
123 *  Show also the number of ommited matches.
124 */
125function ShowMatches (aMatches, nSelectionIndex)
126{
127    var sText = "";
128
129    var nHalfRange = 10;
130    var nMatchesPerLine = 5;
131    var nLineCount = Math.floor((aMatches.length+nMatchesPerLine-1) / nMatchesPerLine);
132    var nLineOfSelection = Math.floor(nSelectionIndex / nMatchesPerLine);
133    var nFirstDisplayedLine = nLineOfSelection>0 ? nLineOfSelection-1 : 0;
134    var nLastDisplayedLine = nLineOfSelection<nLineCount-1 ? nLineOfSelection+1 : nLineCount-1;
135
136    for (nLineIndex=nFirstDisplayedLine; nLineIndex<=nLastDisplayedLine; ++nLineIndex)
137    {
138        var nLineStartIndex = nLineIndex * nMatchesPerLine;
139        var nLineEndIndex = nLineStartIndex + nMatchesPerLine - 1;
140        if (nLineEndIndex >= aMatches.length)
141            nLineEndIndex = aMatches.length-1;
142
143        sText += "<tr>"
144        for (nIndex=nLineStartIndex; nIndex<=nLineEndIndex; ++nIndex)
145        {
146            var aMatch = aMatches[nIndex];
147            var sTypeName = aMatch[0];
148            var sMatch = "";
149            for (nPartIndex=1; nPartIndex<aMatch.length; ++nPartIndex)
150            {
151                if ((nPartIndex%2)==0)
152                    sMatch += "<span class=\"match-highlight\">"+aMatch[nPartIndex]+"</span>";
153                else
154                    sMatch += aMatch[nPartIndex];
155            }
156            sText += "<td>"
157            if (nIndex == nSelectionIndex)
158            {
159                sText += " <span class=\"typelink current-match\">" + sMatch + "</span>";
160            }
161            else
162            {
163                sText += " " + GetTypeLink(sMatch, sTypeName, -1);
164            }
165            sText += "</td>"
166        }
167
168        sText += "</tr>";
169    }
170    if (nFirstDisplayedLine > 0)
171        sText = "<tr><td>["+(nFirstDisplayedLine*nMatchesPerLine)+" matches] ...</td><tr>" + sText;
172    if (nLastDisplayedLine+1 < nLineCount)
173        sText += "<tr><td>... ["+((nLineCount-nLastDisplayedLine-1)*nMatchesPerLine)+" matches]</td></tr>";
174
175    document.getElementById('matches').innerHTML = "<table>"+sText+"</table>";
176    if (aMatches.length == 0)
177    {
178        document.getElementById('match-count').innerHTML = "no match:";
179    }
180    else
181    {
182        if (aMatches.length == 1)
183            document.getElementById('match-count').innerHTML = "single match:";
184        else
185            document.getElementById('match-count').innerHTML = aMatches.length+" matches:";
186
187        ShowType(aMatches[nSelectionIndex][0]);
188    }
189}
190
191
192
193
194function GetTopLevelNodeForTypeName(sTypeName)
195{
196    if (sTypeName in Data)
197    	return Data[sTypeName];
198    else
199    	alert(sTypeName +" is not a known complex type, simple type, or group");
200}
201
202
203
204
205/** Show the specified type.
206 *  When nHistoryIndex is not -1 then the history is shortened to that (before that) index.
207 */
208function ShowType (sTypeName, nHistoryIndex)
209{
210	if (nHistoryIndex == -1)
211	{
212		if (CurrentTypeName != "")
213		{
214			TypeHistory.push(CurrentTypeName);
215			ShowHistory();
216		}
217	}
218	else
219	{
220		TypeHistory = TypeHistory.slice(0,nHistoryIndex);
221		ShowHistory();
222	}
223	CurrentTypeName = sTypeName;
224
225	var aElement = document.getElementById('result');
226
227    // Remove the old content.
228    while(aElement.childNodes.length > 0)
229    	aElement.removeChild(aElement.firstChild);
230
231    // Create the new content.
232    var list = CreateDomTreeForType(GetTopLevelNodeForTypeName(sTypeName), "ul");
233
234    // Show the new content.
235    aElement.appendChild(list);
236}
237
238
239
240
241/** Create a dom sub tree for the given OOXML type that is ready for insertion into the global DOM tree.
242 */
243function CreateDomTreeForType (aNode, sType)
244{
245    var aEntry = document.createElement(sType);
246
247    if (typeof aNode==='undefined')
248    {
249    	aEntry.innerHTML = "undefined node";
250    }
251    else if (typeof aNode.type==='undefined')
252    {
253    	aEntry.innerHTML = "unknown type";
254    }
255    else
256    {
257	    switch(aNode.type)
258	    {
259	    	case "attribute":
260	    		aEntry.innerHTML = CreateValueTable([
261	    				"attribute",
262	    				"type:", GetTypeLink(aNode["value-type"], aNode["value-type"], -1),
263	    				"use:", aNode.use]);
264	        	break;
265
266            case "attribute-reference":
267                aEntry.innerHTML = CreateReference(aNode["referenced-attribute"]);
268                break;
269
270	    	case "builtin":
271	    		aEntry.innerHTML = CreateValueTable(
272	    		        ["builtin",
273	    		         "name:", aNode['builtin-type']
274	    		        ]);
275	    		break;
276
277            case "complex-type":
278                aEntry.innerHTML = aNode.type + " " + aNode.name;
279                break;
280
281            case "complex-type-reference":
282                aEntry.innerHTML = CreateReference(aNode["referenced-complex-type"]);
283                break;
284
285	        case "group":
286	        	aEntry.innerHTML = aNode.type + " " + aNode.name;
287	        	break;
288
289            case "group-reference":
290                aEntry.innerHTML = CreateReference("group", aNode["referenced-group"]);
291                break;
292
293	        case "element":
294	        	aEntry.innerHTML = "element <b>" + aNode["tag"] + "</b> -> " + GetTypeLink(aNode["result-type"], aNode["result-type"], -1);
295	        	break;
296
297            case "occurrence":
298                aEntry.innerHTML = aNode.minimum +" -> " + aNode.maximum;
299                break;
300
301            case "restriction":
302                aEntry.innerHTML = CreateRestrictionRepresentation(aNode);
303                break;
304
305            case "sequence":
306                aEntry.innerHTML = "sequence";
307                break;
308
309            case "simple-type":
310                aEntry.innerHTML = aNode.type + " " + aNode.name;
311                break;
312
313            case "simple-type-reference":
314                aEntry.innerHTML = CreateReference("simple-type", aNode["referenced-simple-type"]);
315                break;
316
317	        default:
318	        	aEntry.innerHTML = aNode.type;
319		    	break;
320	    }
321
322	    // Add nodes for attributes.
323	    var aAttributes= aNode["attributes"];
324	    if ( ! (typeof aAttributes==='undefined' || aAttributes.length == 0))
325	    {
326			var aAttributeList = document.createElement("ul");
327			aEntry.appendChild(aAttributeList);
328
329			for (var nIndex=0; nIndex<aAttributes.length; ++nIndex)
330			{
331			    var aAttributeEntry = CreateDomTreeForType(aAttributes[nIndex], "li");
332			    aAttributeList.appendChild(aAttributeEntry);
333			}
334	    }
335
336	    // Add nodes for children.
337	    var aChildren = aNode["children"];
338	    if ( ! (typeof aChildren==='undefined' || aChildren.length == 0))
339	    {
340			var aChildrenList = document.createElement("ul");
341			aEntry.appendChild(aChildrenList);
342
343			for (var nIndex=0; nIndex<aChildren.length; ++nIndex)
344			{
345			    var aChildrenEntry = CreateDomTreeForType(aChildren[nIndex], "li");
346			    aChildrenList.appendChild(aChildrenEntry);
347			}
348	    }
349    }
350    return aEntry;
351}
352
353
354
355
356function GetTypeLink (sText, sTarget, nIndex)
357{
358    return "<span class=\"typelink\" id=\""+sTarget+"\" onclick=\"ShowType('"+sTarget+"',"+nIndex+")\">"+sText+"</span>";
359}
360
361
362
363
364function CreateValueTable (aValues)
365{
366	var sResult = "<table class=\"value-table\"><tr><td>"+aValues[0]+"</td><td>"+aValues[1]+"</td><td>"+aValues[2]+"</td></tr>";
367	for (nIndex=3; nIndex<aValues.length; nIndex+=2)
368	{
369		sResult += "<tr><td></td><td>"+aValues[nIndex]+"</td><td>"+aValues[nIndex+1]+"</td></tr>";
370	}
371	sResult += "</table>";
372	return sResult;
373}
374
375
376
377
378function CreateReference (sWhat, sTypeName)
379{
380    return "reference to "+sWhat+" "+GetTypeLink(sTypeName, sTypeName, -1) + " "
381        +CreateButton(
382            sTypeName,
383            "show",
384            "ToggleTypeReferenceExpansion('"+sTypeName+"')")
385            +"<br><span id=\"expansion-"+sTypeName+"\"></span>";
386}
387
388
389
390
391function CreateButton (sId, sText, sExpandAction)
392{
393    return "<span class=\"button\" id=\"button-"+sId+"\" onClick=\""+sExpandAction+"\">"+sText+"</span>";
394}
395
396
397
398
399function ToggleTypeReferenceExpansion(sTypeName)
400{
401    var aButton = document.getElementById("button-"+sTypeName);
402    var aExpansion = document.getElementById("expansion-"+sTypeName);
403    if (aButton.innerHTML == "show")
404    {
405        aExpansion.appendChild(CreateDomTreeForType(Data[sTypeName], "span"));
406        aButton.innerHTML = "hide";
407    }
408    else
409    {
410        aExpansion.innerHTML = "";
411        aButton.innerHTML = "show";
412    }
413}
414
415
416
417
418function ShowHistory ()
419{
420	var aElement = document.getElementById('history');
421	var sContent = "History:";
422	for (nIndex=0; nIndex<TypeHistory.length; ++nIndex)
423	{
424		if (nIndex == 0)
425			sContent += " ";
426		else
427			sContent += ", ";
428		sContent += GetTypeLink(TypeHistory[nIndex], TypeHistory[nIndex], nIndex);
429	}
430	aElement.innerHTML = sContent;
431}
432
433
434
435
436function CreateRestrictionRepresentation (aNode)
437{
438    var aTableData = ["restriction", "based on:", GetTypeLink(aNode['base-type'], aNode['base-type'], -1)];
439    AddValue(aNode, "enumeration", aTableData);
440    AddValue(aNode, "pattern", aTableData);
441    AddValue(aNode, "length", aTableData);
442    return CreateValueTable(aTableData);
443}
444
445
446
447function AddValue (aMap, sKey, aTableData)
448{
449    if (sKey in aMap)
450    {
451        aTableData.push(sKey+":");
452        aTableData.push(aMap[sKey]);
453    }
454}
455