blob: 9be90f3d4784f0c7433b33ce97ea52222c626961 [file] [log] [blame]
Jan Tattermuschf209c582015-08-24 17:47:35 -07001//===============================================================================================================
2// System : Sandcastle Help File Builder
3// File : branding.js
4// Author : Eric Woodruff (Eric@EWoodruff.us)
5// Updated : 05/15/2014
6// Note : Copyright 2014, Eric Woodruff, All rights reserved
7// Portions Copyright 2010-2014 Microsoft, All rights reserved
8//
9// This file contains the methods necessary to implement the language filtering, collapsible section, and
10// copy to clipboard options.
11//
12// This code is published under the Microsoft Public License (Ms-PL). A copy of the license should be
13// distributed with the code. It can also be found at the project website: https://GitHub.com/EWSoftware/SHFB. This
14// notice, the author's name, and all copyright notices must remain intact in all applications, documentation,
15// and source files.
16//
17// Date Who Comments
18// ==============================================================================================================
19// 05/04/2014 EFW Created the code based on the MS Help Viewer script
20//===============================================================================================================
21
22// The IDs of all code snippet sets on the same page are stored so that we can keep them in synch when a tab is
23// selected.
24var allTabSetIds = new Array();
25
26// The IDs of language-specific text (LST) spans are used as dictionary keys so that we can get access to the
27// spans and update them when the user changes to a different language tab. The values of the dictionary
28// objects are pipe separated language-specific attributes (lang1=value|lang2=value|lang3=value). The language
29// ID can be specific (cs, vb, cpp, etc.) or may be a neutral entry (nu) which specifies text common to multiple
30// languages. If a language is not present and there is no neutral entry, the span is hidden for all languages
31// to which it does not apply.
32var allLSTSetIds = new Object();
33
34// Help 1 persistence support. This code must appear inline.
35var isHelp1;
36
37var curLoc = document.location + ".";
38
39if(curLoc.indexOf("mk:@MSITStore") == 0)
40{
41 isHelp1 = true;
42 curLoc = "ms-its:" + curLoc.substring(14, curLoc.length - 1);
43 document.location.replace(curLoc);
44}
45else
46 if(curLoc.indexOf("ms-its:") == 0)
47 isHelp1 = true;
48 else
49 isHelp1 = false;
50
51// The OnLoad method
52function OnLoad(defaultLanguage)
53{
54 var defLang;
55
56 if(typeof (defaultLanguage) == "undefined" || defaultLanguage == null || defaultLanguage == "")
57 defLang = "vb";
58 else
59 defLang = defaultLanguage;
60
61 // In MS Help Viewer, the transform the topic is ran through can move the footer. Move it back where it
62 // belongs if necessary.
63 try
64 {
65 var footer = document.getElementById("pageFooter")
66
67 if(footer)
68 {
69 var footerParent = document.body;
70
71 if(footer.parentElement != footerParent)
72 {
73 footer.parentElement.removeChild(footer);
74 footerParent.appendChild(footer);
75 }
76 }
77 }
78 catch(e)
79 {
80 }
81
82 var language = GetCookie("CodeSnippetContainerLanguage", defLang);
83
84 // If LST exists on the page, set the LST to show the user selected programming language
85 UpdateLST(language);
86
87 // If code snippet groups exist, set the current language for them
88 if(allTabSetIds.length > 0)
89 {
90 var i = 0;
91
92 while(i < allTabSetIds.length)
93 {
94 var tabCount = 1;
95
96 // The tab count may vary so find the last one in this set
97 while(document.getElementById(allTabSetIds[i] + "_tab" + tabCount) != null)
98 tabCount++;
99
100 tabCount--;
101
102 // If not grouped, skip it
103 if(tabCount < 2)
104 {
105 // Disable the Copy Code link if in Chrome
106 if(navigator.userAgent.toLowerCase().indexOf("chrome") != -1)
107 document.getElementById(allTabSetIds[i] + "_copyCode").style.display = "none";
108 }
109 else
110 SetCurrentLanguage(allTabSetIds[i], language, tabCount);
111
112 i++;
113 }
114 }
115
116 InitializeToc();
117}
118
119// This is just a place holder. The website script implements this function to initialize it's in-page TOC pane
120function InitializeToc()
121{
122}
123
124// This function executes in the OnLoad event and ChangeTab action on code snippets. The function parameter
125// is the user chosen programming language. This function iterates through the "allLSTSetIds" dictionary object
126// to update the node value of the LST span tag per the user's chosen programming language.
127function UpdateLST(language)
128{
129 for(var lstMember in allLSTSetIds)
130 {
131 var devLangSpan = document.getElementById(lstMember);
132
133 if(devLangSpan != null)
134 {
135 // There may be a carriage return before the LST span in the content so the replace function below
136 // is used to trim the whitespace at the end of the previous node of the current LST node.
137 if(devLangSpan.previousSibling != null && devLangSpan.previousSibling.nodeValue != null)
138 devLangSpan.previousSibling.nodeValue = devLangSpan.previousSibling.nodeValue.replace(/\s+$/, "");
139
140 var langs = allLSTSetIds[lstMember].split("|");
141 var k = 0;
142 var keyValue;
143
144 while(k < langs.length)
145 {
146 keyValue = langs[k].split("=");
147
148 if(keyValue[0] == language)
149 {
150 devLangSpan.innerHTML = keyValue[1];
151
152 // Help 1 and MS Help Viewer workaround. Add a space if the following text element starts
153 // with a space to prevent things running together.
154 if(devLangSpan.parentNode != null && devLangSpan.parentNode.nextSibling != null)
155 {
156 if (devLangSpan.parentNode.nextSibling.nodeValue != null &&
157 !devLangSpan.parentNode.nextSibling.nodeValue.substring(0, 1).match(/[.,);:!/?]/))
158 {
159 devLangSpan.innerHTML = keyValue[1] + " ";
160 }
161 }
162 break;
163 }
164
165 k++;
166 }
167
168 // If not found, default to the neutral language. If there is no neutral language entry, clear the
169 // content to hide it.
170 if(k >= langs.length)
171 {
172 if(language != "nu")
173 {
174 k = 0;
175
176 while(k < langs.length)
177 {
178 keyValue = langs[k].split("=");
179
180 if(keyValue[0] == "nu")
181 {
182 devLangSpan.innerHTML = keyValue[1];
183
184 // Help 1 and MS Help Viewer workaround. Add a space if the following text element
185 // starts with a space to prevent things running together.
186 if(devLangSpan.parentNode != null && devLangSpan.parentNode.nextSibling != null)
187 {
188 if(devLangSpan.parentNode.nextSibling.nodeValue != null &&
189 !devLangSpan.parentNode.nextSibling.nodeValue.substring(0, 1).match(/[.,);:!/?]/))
190 {
191 devLangSpan.innerHTML = keyValue[1] + " ";
192 }
193 }
194 break;
195 }
196
197 k++;
198 }
199 }
200
201 if(k >= langs.length)
202 devLangSpan.innerHTML = "";
203 }
204 }
205 }
206}
207
208// Get the specified cookie. If not found, return the specified default value.
209function GetCookie(cookieName, defaultValue)
210{
211 if(isHelp1)
212 {
213 try
214 {
215 var globals = Help1Globals;
216
217 var value = globals.Load(cookieName);
218
219 if(value == null)
220 value = defaultValue;
221
222 return value;
223 }
224 catch(e)
225 {
226 return defaultValue;
227 }
228 }
229
230 var cookie = document.cookie.split("; ");
231
232 for(var i = 0; i < cookie.length; i++)
233 {
234 var crumb = cookie[i].split("=");
235
236 if(cookieName == crumb[0])
237 return unescape(crumb[1])
238 }
239
240 return defaultValue;
241}
242
243// Set the specified cookie to the specified value
244function SetCookie(name, value)
245{
246 if(isHelp1)
247 {
248 try
249 {
250 var globals = Help1Globals;
251
252 globals.Save(name, value);
253 }
254 catch(e)
255 {
256 }
257
258 return;
259 }
260
261 var today = new Date();
262
263 today.setTime(today.getTime());
264
265 // Set the expiration time to be 60 days from now (in milliseconds)
266 var expires_date = new Date(today.getTime() + (60 * 1000 * 60 * 60 * 24));
267
268 document.cookie = name + "=" + escape(value) + ";expires=" + expires_date.toGMTString() + ";path=/";
269}
270
271// Add a language-specific text ID
272function AddLanguageSpecificTextSet(lstId)
273{
274 var keyValue = lstId.split("?")
275
276 allLSTSetIds[keyValue[0]] = keyValue[1];
277}
278
279// Add a language tab set ID
280function AddLanguageTabSet(tabSetId)
281{
282 allTabSetIds.push(tabSetId);
283}
284
285// Switch the active tab for all of other code snippets
286function ChangeTab(tabSetId, language, snippetIdx, snippetCount)
287{
288 SetCookie("CodeSnippetContainerLanguage", language);
289
290 SetActiveTab(tabSetId, snippetIdx, snippetCount);
291
292 // If LST exists on the page, set the LST to show the user selected programming language
293 UpdateLST(language);
294
295 var i = 0;
296
297 while(i < allTabSetIds.length)
298 {
299 // We just care about other snippets
300 if(allTabSetIds[i] != tabSetId)
301 {
302 // Other tab sets may not have the same number of tabs
303 var tabCount = 1;
304
305 while(document.getElementById(allTabSetIds[i] + "_tab" + tabCount) != null)
306 tabCount++;
307
308 tabCount--;
309
310 // If not grouped, skip it
311 if(tabCount > 1)
312 SetCurrentLanguage(allTabSetIds[i], language, tabCount);
313 }
314
315 i++;
316 }
317}
318
319// Sets the current language in the specified tab set
320function SetCurrentLanguage(tabSetId, language, tabCount)
321{
322 var tabIndex = 1;
323
324 while(tabIndex <= tabCount)
325 {
326 var tabTemp = document.getElementById(tabSetId + "_tab" + tabIndex);
327
328 if(tabTemp != null && tabTemp.innerHTML.indexOf("'" + language + "'") != -1)
329 break;
330
331 tabIndex++;
332 }
333
334 if(tabIndex > tabCount)
335 {
336 // Select the first non-disabled tab
337 tabIndex = 1;
338
339 if(document.getElementById(tabSetId + "_tab1").className == "codeSnippetContainerTabPhantom")
340 {
341 tabIndex++;
342
343 while(tabIndex <= tabCount)
344 {
345 var tab = document.getElementById(tabSetId + "_tab" + tabIndex);
346
347 if(tab.className != "codeSnippetContainerTabPhantom")
348 {
349 tab.className = "codeSnippetContainerTabActive";
350 document.getElementById(tabSetId + "_code_Div" + j).style.display = "block";
351 break;
352 }
353
354 tabIndex++;
355 }
356 }
357 }
358
359 SetActiveTab(tabSetId, tabIndex, tabCount);
360}
361
362// Set the active tab within a tab set
363function SetActiveTab(tabSetId, tabIndex, tabCount)
364{
365 var i = 1;
366
367 while(i <= tabCount)
368 {
369 var tabTemp = document.getElementById(tabSetId + "_tab" + i);
370
371 if(tabTemp.className == "codeSnippetContainerTabActive")
372 tabTemp.className = "codeSnippetContainerTab";
373 else
374 if(tabTemp.className == "codeSnippetContainerTabPhantom")
375 tabTemp.style.display = "none";
376
377 var codeTemp = document.getElementById(tabSetId + "_code_Div" + i);
378
379 if(codeTemp.style.display != "none")
380 codeTemp.style.display = "none";
381
382 i++;
383 }
384
385 // Phantom tabs are shown or hidden as needed
386 if(document.getElementById(tabSetId + "_tab" + tabIndex).className != "codeSnippetContainerTabPhantom")
387 document.getElementById(tabSetId + "_tab" + tabIndex).className = "codeSnippetContainerTabActive";
388 else
389 document.getElementById(tabSetId + "_tab" + tabIndex).style.display = "block";
390
391 document.getElementById(tabSetId + "_code_Div" + tabIndex).style.display = "block";
392
393 // Show copy code button if not in Chrome
394 if(navigator.userAgent.toLowerCase().indexOf("chrome") == -1)
395 document.getElementById(tabSetId + "_copyCode").style.display = "inline";
396 else
397 document.getElementById(tabSetId + "_copyCode").style.display = "none";
398}
399
400// Copy the code from the active tab of the given tab set to the clipboard
401function CopyToClipboard(tabSetId)
402{
403 var tabTemp, contentId;
404 var i = 1;
405
406 do
407 {
408 contentId = tabSetId + "_code_Div" + i;
409 tabTemp = document.getElementById(contentId);
410
411 if(tabTemp != null && tabTemp.style.display != "none")
412 break;
413
414 i++;
415
416 } while(tabTemp != null);
417
418 if(tabTemp == null)
419 return;
420
421 if(window.clipboardData)
422 {
423 try
424 {
425 window.clipboardData.setData("Text", document.getElementById(contentId).innerText);
426 }
427 catch(e)
428 {
429 alert("Permission denied. Enable copying to the clipboard.");
430 }
431 }
432 else if(window.netscape)
433 {
434 try
435 {
436 netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
437
438 var clip = Components.classes["@mozilla.org/widget/clipboard;1"].createInstance(
439 Components.interfaces.nsIClipboard);
440
441 if(!clip)
442 return;
443
444 var trans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(
445 Components.interfaces.nsITransferable);
446
447 if(!trans)
448 return;
449
450 trans.addDataFlavor("text/unicode");
451
452 var str = new Object();
453 var len = new Object();
454 var str = Components.classes["@mozilla.org/supports-string;1"].createInstance(
455 Components.interfaces.nsISupportsString);
456
457 var copytext = document.getElementById(contentId).textContent;
458
459 str.data = copytext;
460 trans.setTransferData("text/unicode", str, copytext.length * 2);
461
462 var clipid = Components.interfaces.nsIClipboard;
463
464 clip.setData(trans, null, clipid.kGlobalClipboard);
465 }
466 catch(e)
467 {
468 alert("Permission denied. Enter \"about:config\" in the address bar and double-click the \"signed.applets.codebase_principal_support\" setting to enable copying to the clipboard.");
469 }
470 }
471}
472
473// Expand or collapse a section
474function SectionExpandCollapse(togglePrefix)
475{
476 var image = document.getElementById(togglePrefix + "Toggle");
477 var section = document.getElementById(togglePrefix + "Section");
478
479 if(image != null && section != null)
480 if(section.style.display == "")
481 {
482 image.src = image.src.replace("SectionExpanded.png", "SectionCollapsed.png");
483 section.style.display = "none";
484 }
485 else
486 {
487 image.src = image.src.replace("SectionCollapsed.png", "SectionExpanded.png");
488 section.style.display = "";
489 }
490}
491
492// Expand or collapse a section when it has the focus and Enter is hit
493function SectionExpandCollapse_CheckKey(togglePrefix, eventArgs)
494{
495 if(eventArgs.keyCode == 13)
496 SectionExpandCollapse(togglePrefix);
497}
498
499// Help 1 persistence object. This requires a hidden input element on the page with a class of "userDataStyle"
500// defined in the style sheet that implements the user data binary behavior:
501// <input type="hidden" id="userDataCache" class="userDataStyle" />
502var Help1Globals =
503{
504 UserDataCache: function()
505 {
506 var userData = document.getElementById("userDataCache");
507
508 return userData;
509 },
510
511 Load: function(key)
512 {
513 var userData = this.UserDataCache();
514
515 userData.load("userDataSettings");
516
517 var value = userData.getAttribute(key);
518
519 return value;
520 },
521
522 Save: function(key, value)
523 {
524 var userData = this.UserDataCache();
525 userData.setAttribute(key, value);
526 userData.save("userDataSettings");
527 }
528};