branding.js 17 KB

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