crfPortal.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. function drawForm(par){
  2. populateSourceTable(par);
  3. let tableId="entryTable";
  4. generateTable("formDiv",tableId);
  5. generateRow(tableId, par,"User");
  6. populateSelectTableEntry(par,"User");
  7. generateRow(tableId, par,"Site");
  8. generateRow(tableId, par, "FormStatus");
  9. generateRow(tableId, par,"Crf");
  10. populateSelectTableEntry(par,"Site");
  11. populateSelectTableEntry(par,"FormStatus");
  12. let formTableId="selectFormTable";
  13. generateTable("selectFormDiv",formTableId);
  14. generateRow(formTableId,par,"Form");
  15. generateButtonRow(formTableId,"Add new CRF","Add", par, addNewEntry);
  16. populateSelectTableEntry(par,"Form");
  17. generateTable("startDiv","startTable");
  18. generateButtonRow("startTable","Start filling the selected form","Start", par, startForm);
  19. }
  20. function sourceVar(crfEntryName,elementId,sourceName){
  21. let f=new Object();
  22. f.masterSelectVarName=crfEntryName;
  23. f.selectId=elementId;
  24. f.inputType="innerHTML";
  25. f.sourceSelectVarName=sourceName;
  26. return f;
  27. }
  28. function generateHead(headDivName,divName,title){
  29. document.getElementById("formStatus").value+="\ngenerateHead";
  30. let tb=document.createElement('table');
  31. tb.className='t2';
  32. let row=tb.insertRow();
  33. let cell=document.createElement('th');
  34. row.appendChild(cell);
  35. cell.setAttribute("colspan","4");
  36. cell.style.fontSize="20px";
  37. cell.style.textAlign="center";
  38. let cellData=document.createTextNode(title);
  39. cell.appendChild(cellData);
  40. cell=row.insertCell();
  41. cell.style.fontSize="20px";
  42. let input=document.createElement("input");
  43. input.type="button";
  44. input.value="Show";
  45. input.id="toggle"+divName+"VisbilityButton";
  46. input.onclick=function(){toggleVisibility(divName,input.id)};
  47. cell.appendChild(input);
  48. document.getElementById(headDivName).appendChild(tb);
  49. document.getElementById("formStatus").value+="\ngenerateHead: Done";
  50. }
  51. function toggleVisibility(divName,buttonName){
  52. let x = document.getElementById(divName);
  53. if (x.style.display === "none") {
  54. x.style.display = "block";
  55. document.getElementById(buttonName).value="Hide";
  56. } else {
  57. x.style.display = "none";
  58. document.getElementById(buttonName).value="Show";
  59. }
  60. }
  61. function populateSourceTable(par){
  62. let debug=true;
  63. if (debug){
  64. document.getElementById('formStatus').value +=
  65. "\npopulateSourceTable: Starting";
  66. }
  67. let config=new Object();
  68. if (!("source" in par)) return;
  69. if (debug){
  70. print("populateSourceTable ["+par.source.queryName+"]");
  71. }
  72. config.schemaName=par.source.schemaName;
  73. config.queryName=par.source.queryName;
  74. config.success=function(data){populateSourceTableData(data,par)};
  75. config.failure=function(errorTxt){print("populateSourceData:fail")};
  76. LABKEY.Query.selectRows(config);
  77. }
  78. function print(msg){
  79. document.getElementById('formStatus').value +="\n"+msg;
  80. }
  81. function populateSourceTableData(data,par){
  82. let debug=true;
  83. if (debug){
  84. document.getElementById('formStatus').value +=
  85. "\npopulateSourceTableData: nrow: "+data.rows.length;
  86. }
  87. let entry=data.rows[0];
  88. for (let i=0;i < par.source.vars.length;i++){
  89. let srcVarName=par.source.vars[i];
  90. if (debug){
  91. document.getElementById('formStatus').value +=
  92. "\npopulateSourceTable ["+srcVarName+"]";
  93. }
  94. let row=par.vars[srcVarName];
  95. let el=document.getElementById(row.selectId);
  96. if (debug){
  97. document.getElementById('formStatus').value +=
  98. "\nElement: "+el;
  99. }
  100. el.innerHTML=entry[row.sourceSelectVarName];
  101. }
  102. }
  103. function populateSelectNotLookup(data,parameters,rowId, entry){
  104. //selectId
  105. //masterSelectVarName
  106. let debug=true;
  107. if (debug)
  108. print("populateSelectNonLookup on "+data.queryName);
  109. let row=parameters.vars[rowId];
  110. let varName=row.masterSelectVarName;
  111. if (debug) print("var "+varName+" rows "+data.rows.length);
  112. if (debug) print("Getting element "+row.selectId);
  113. let el=document.getElementById(row.selectId);
  114. if (!el) {
  115. print("Element not found");
  116. return;
  117. }
  118. if (debug) print("Element "+el);
  119. if (debug) print("Clearing entries");
  120. //remove previous options
  121. for(i = el.options.length; i >= 0; i--) {
  122. el.remove(i);
  123. }
  124. if (debug)
  125. print("Adding entries");
  126. if ("addSelect" in row){
  127. if (debug)
  128. print("adding <Select>");
  129. let opt = document.createElement("option");
  130. opt.text = "<Select>";
  131. opt.value = -2;
  132. el.options[0] = opt;
  133. }
  134. if ("addNewFlag" in row){
  135. if (debug) print("adding Add new");
  136. let opt = document.createElement("option");
  137. opt.text = "Add New";
  138. opt.value = row.addNewFlag;
  139. el.options[el.options.length] = opt;
  140. }
  141. for (let i=0;i< data.rows.length;i++){
  142. let valEntry=data.rows[i];
  143. if (debug)
  144. print("adding "+valEntry[varName]);
  145. let opt = document.createElement("option");
  146. opt.text = valEntry[varName];
  147. opt.value = valEntry[varName];
  148. if (entry){
  149. if (opt.value==entry[varName])
  150. el.selectedIndex=el.options.length-1;
  151. }
  152. el.options[el.options.length] = opt;
  153. }
  154. if (entry)
  155. row.callback(parameters,rowId);
  156. }
  157. function populateSelect(data,selectedData, parameters, rowId){
  158. //data is the set of lookup entries, selectedData is a subset of entries valued at lookup.keyColumn
  159. let debug=true;
  160. if (debug)
  161. print("populateSelect Data: "+data.queryName+" selectedData:"+selectedData.queryName);
  162. let row=parameters.vars[rowId];
  163. let selectId=row.selectId;
  164. let varName=row.masterSelectVarName;
  165. if ("filter" in row){
  166. varName=row.filter.filterVarName;
  167. }
  168. let field=getField(selectedData,varName);
  169. let displayColumn=field.lookup.displayColumn;
  170. let keyColumn=field.lookup.keyColumn;
  171. if (debug){
  172. document.getElementById('formStatus').value+="\n Query: "+data.queryName;
  173. document.getElementById('formStatus').value+="\n ElementId: "+selectId;
  174. document.getElementById('formStatus').value+="\n keyColumn: "+keyColumn;
  175. document.getElementById('formStatus').value+="\n displayColumn: "+displayColumn;
  176. }
  177. let el = document.getElementById(selectId);
  178. if (debug)
  179. document.getElementById('formStatus').value+="\n Element: "+el;
  180. for(i = el.options.length; i >= 0; i--) {
  181. el.remove(i);
  182. }
  183. if ("addSelect" in row){
  184. let opt = document.createElement("option");
  185. opt.text = "<Select>";
  186. opt.value = -1;
  187. el.options[0] = opt;
  188. }
  189. if ("addNewFlag" in row){
  190. let opt = document.createElement("option");
  191. opt.text = "Add New"
  192. opt.value = row.addNewFlag;
  193. el.options[el.options.length] = opt;
  194. }
  195. for (var i = 0; i < data.rows.length; i++) {
  196. let key=data.rows[i][keyColumn];
  197. let skip=true;
  198. if (row.selectAll){
  199. skip=false;
  200. }
  201. else{
  202. if (debug)
  203. print("Selecting from: "+selectedData.rows.length);
  204. for (let j=0; j< selectedData.rows.length; j++){
  205. let entry=selectedData.rows[j];
  206. if (debug)
  207. print("Comparing: "+entry[varName]+"/"+key);
  208. if (key!=entry[varName]) continue;
  209. skip=false;
  210. break;
  211. }
  212. }
  213. if (skip) continue;
  214. let opt = document.createElement("option");
  215. opt.text = data.rows[i][displayColumn];
  216. opt.value = data.rows[i][keyColumn];
  217. if (debug)
  218. print("Adding: "+opt.value+" : "+opt.text);
  219. el.options[el.options.length] = opt;
  220. if ("selectedKey" in row){
  221. if (debug)
  222. print("Comparing: " + opt.value + "/" + row["selectedKey"]);
  223. if (opt.value==row["selectedKey"]){
  224. el.selectedIndex=el.options.length-1;
  225. if (debug)
  226. print("Equal; "+el.selectedIndex);
  227. }
  228. }
  229. }
  230. if (debug)
  231. print("Running callback");
  232. row.callback(parameters,rowId);
  233. }
  234. function saveData(queryName){
  235. document.getElementById('formStatus').value+="\n saveData: "+queryName;
  236. }
  237. function populateSelectTableEntry(parameters,rowId){
  238. //parameters should include
  239. //tableId - update this table (unused)
  240. //masterQuery - the master query that will collect al the data
  241. //masterSelectVarName - variable that is going to fill the content of select.
  242. // Typically, the variable is a lookup in the master query
  243. //masterUserVarName - variable that stores the user id in the master query
  244. // only entries matching current user in the master query will be selected
  245. //authorization - additional data to check:
  246. // * if the current user is authorized to perform action and/or
  247. // * limit number of different queryVariable values user is entitled to use
  248. //authorization should contain:
  249. // * queryName - query of user/queryVariable pairs to check for authorized access
  250. // * authUserVarName - name of the variable in authorization.queryName that contain usesId
  251. // * authSelectVarName - name of the selectVar in authorization query
  252. //callback - function that executes when value of the select changes (onchange).
  253. // argument to callback are the parameters
  254. //selectId - id of the DOM element where select is rendered - to check for value in callback
  255. //dataDiv - div element to render potential output of the callback
  256. let debug=true;
  257. let row=parameters.vars[rowId];
  258. if (debug)
  259. document.getElementById('formStatus').value+="\n populateSelectTableEntry:"
  260. +parameters.masterQuery+"/"+row.masterSelectVarName;
  261. let config=new Object();
  262. config.schemaName='lists';
  263. if ("filter" in row){
  264. //populateSelect on authorizationQuery with authSelectVarName
  265. let filter=row.filter;
  266. if (debug){
  267. print("Filter:"+filter.queryName);
  268. print("FilterVar "+filter.filterVarName);
  269. }
  270. config.queryName=filter.queryName;
  271. config.filterArray=[];
  272. for (f in filter.filters){
  273. if (debug) print("Adding filter: "+f+" val "+filter.filters[f]);
  274. config.filterArray.push(LABKEY.Filter.create(f,filter.filters[f]));
  275. }
  276. }
  277. else{
  278. config.queryName=parameters.masterQuery;
  279. }
  280. config.success=function(data){populateTableRow(data,parameters,rowId)};
  281. LABKEY.Query.selectRows(config);
  282. if (debug)
  283. print("generateSelect: End");
  284. return;
  285. }
  286. function getField(data, varName){
  287. let debug=false;
  288. if (debug) print("getField");
  289. let fields=data.metaData.fields;
  290. for (f in fields){
  291. if (debug) print("Checking "+f+": name "+fields[f].name+"/"+varName);
  292. if (fields[f].name!=varName) continue;
  293. return fields[f];
  294. }
  295. return null;
  296. }
  297. function getCaption(data, varName){
  298. let field=getField(data,varName);
  299. if (field) return field.shortCaption;
  300. return "NONE";
  301. }
  302. function generateTable(divName,elementId){
  303. let debug=true;
  304. if (debug)
  305. document.getElementById('formStatus').value+="\n generateTable";
  306. let tb=document.createElement('table');
  307. tb.className="t2";
  308. tb.id=elementId;
  309. document.getElementById(divName).appendChild(tb);
  310. if (debug)
  311. document.getElementById('formStatus').value+="\n generateTable: Done";
  312. }
  313. function generateRow(tableId, parameters,rowId){
  314. let debug=true;
  315. if (debug)
  316. document.getElementById('formStatus').value+="\n generateRow: Start";
  317. let config=new Object();
  318. config.schemaName='lists';
  319. config.queryName=parameters.masterQuery;
  320. config.success=function(data){generateTableRow(data,tableId, parameters,rowId)};
  321. LABKEY.Query.selectRows(config);
  322. if (debug)
  323. document.getElementById('formStatus').value+="\n generateRow: End";
  324. return;
  325. }
  326. function generateTableRow(data,tableId, parameters,rowId){
  327. let tb=document.getElementById(tableId);
  328. let row=parameters.vars[rowId];
  329. let field=getField(data,row.masterSelectVarName);
  330. let trow=tb.insertRow();
  331. let cell=document.createElement('th');
  332. trow.appendChild(cell);
  333. let text = document.createTextNode(field.shortCaption);
  334. cell.appendChild(text);
  335. cell=trow.insertCell();
  336. let input = document.createElement("select");
  337. input.id = row.selectId;
  338. input.onchange=function(){row.callback(parameters,rowId)};
  339. cell.appendChild(input);
  340. }
  341. function populateTableRow(data,parameters,rowId){
  342. //data is output of selectRows on either
  343. // * masterQuery looking at masterSelectVarName or
  344. // * authQuery looking at authSelectVarName
  345. //in both cases, query[varName] is a lookup variable, so do populateSelect with lookupData,queryData and parameters
  346. let debug=true;
  347. let row=parameters.vars[rowId];
  348. let varName=row.masterSelectVarName;
  349. if ("filter" in row){
  350. if (row.filter.queryName==data.queryName){
  351. varName=row.filter.filterVarName;
  352. }
  353. }
  354. if (debug)
  355. print("generateSelectVar: "+data.queryName+"/"+varName+" size "+data.rows.length);
  356. let field=getField(data,varName);
  357. if (!field) {
  358. print("Field "+varName+" not found");
  359. return;
  360. }
  361. if (!("lookup" in field)){
  362. let entry=data.rows[0];
  363. print("Field "+varName+" not a lookup");
  364. populateSelectNotLookup(data,parameters,rowId, entry);
  365. return;
  366. }
  367. let config=new Object();
  368. config.schemaName=field.lookup.schemaName;
  369. config.queryName=field.lookup.queryName;
  370. config.success=function(lookupData){populateSelect(lookupData,data,parameters,rowId)};
  371. LABKEY.Query.selectRows(config);
  372. if (debug)
  373. print("generateSelectVar: End");
  374. }
  375. function generateButtonRow(tableId,caption,label,parameters,callback){
  376. let tb=document.getElementById(tableId);
  377. let trow=tb.insertRow();
  378. let cell=document.createElement('th');
  379. trow.appendChild(cell);
  380. let text = document.createTextNode(caption);
  381. cell.appendChild(text);
  382. cell=trow.insertCell();
  383. let input = document.createElement("input");
  384. input.type="button";
  385. input.value=label;
  386. input.onclick=function(){callback(parameters)};
  387. cell.appendChild(input);
  388. }
  389. function generateListAndPopulateDaughterSelect(parameters,rowId){
  390. let debug=true;
  391. if (debug){
  392. print("generateListAndPopulateDaughter");
  393. }
  394. generateList(parameters,rowId);
  395. let row=parameters.vars[rowId];
  396. if ("daughterSelect" in row){
  397. populateDaughterSelect(parameters,rowId,row["daughterSelect"],null);
  398. }
  399. }
  400. function populateDaughterSelect(parameters,rowId,daughterRowId,entry){
  401. let debug=true;
  402. if (debug)
  403. print("populateDaughterSelect: "+rowId+" "+daughterRowId);
  404. let row=parameters.vars[rowId];
  405. if (debug)
  406. print("row["+rowId+"]:"+row);
  407. let daughterRow=parameters.vars[daughterRowId];
  408. if (debug)
  409. print("daughterRow["+daughterRowId+"]:"+daughterRow);
  410. let el=document.getElementById(row.selectId);
  411. if (debug)
  412. print("\n Element:"+el);
  413. let varValue=el.options[el.selectedIndex].value;
  414. if (debug)
  415. print("\nAdding filter ["+row.masterSelectVarName+"]:"+varValue);
  416. let config=new Object();
  417. config.schemaName='lists';
  418. config.queryName=parameters.masterQuery;
  419. config.filterArray=[];
  420. for (let i=0;i < parameters.filters.length;i++){
  421. let filterRowId=parameters.filters[i];
  422. let filterRow=parameters.vars[filterRowId];
  423. let filterValue=document.getElementById(filterRow.selectId).value;
  424. config.filterArray.push(LABKEY.Filter.create(filterRow.masterSelectVarName,filterValue));
  425. }
  426. config.success=function(data){populateSelectNotLookup(data,parameters,daughterRowId,entry)};
  427. LABKEY.Query.selectRows(config);
  428. }
  429. function generateList(parameters,rowId){
  430. let row=parameters.vars[rowId];
  431. let debug=true;
  432. if (debug)
  433. print("generateList: "+parameters.masterQuery);
  434. //ignore authorization, just select on select variable
  435. let el=document.getElementById(row.selectId);
  436. let varValue=el.options[el.selectedIndex].value;
  437. if (debug)
  438. print("Using value "+varValue+" from "+row.selectId);
  439. let iValue=parseInt(varValue);
  440. let div=document.getElementById(parameters.addDiv);
  441. div.style.display="none";
  442. //add new crf entry
  443. if ("addNewFlag" in row){
  444. if (debug)
  445. print("Comparing " + iValue + "/" + row.addNewFlag);
  446. if (iValue==row.addNewFlag) {
  447. addNew(parameters);
  448. return;
  449. }
  450. }
  451. //do filtering
  452. let filterArray=[];
  453. for (let i=0;i < parameters.filters.length;i++){
  454. let filterRowId=parameters.filters[i];
  455. let filterRow=parameters.vars[filterRowId];
  456. let filterValue=document.getElementById(filterRow.selectId).value;
  457. filterArray.push(LABKEY.Filter.create(filterRow.masterSelectVarName,filterValue));
  458. }
  459. //show all for system entries (Select, Add New)
  460. if (debug)
  461. print("Using iValue "+iValue);
  462. if (iValue<0) {
  463. if (debug)
  464. print("Ignoring ["+row.masterSelectVarName+ "]: "+varValue);
  465. }
  466. else{
  467. if (debug)
  468. print("Filtering ["+row.masterSelectVarName+"]: "+varValue);
  469. if (rowId==="Crf"){
  470. filterArray.push(LABKEY.Filter.create(row.masterSelectVarName,varValue));
  471. if (debug)
  472. print("Filtering ["+row.masterSelectVarName+"]: "+varValue);
  473. }
  474. }
  475. LABKEY.QueryWebPart({renderTo: parameters.dataDiv, schemaName: 'lists',
  476. queryName: parameters.masterQuery,
  477. buttonBarPosition: 'top',filters: filterArray, viewName:"sparseView",
  478. success:updateSuccess, failure:updateFailure });
  479. }
  480. function updateSuccess(){
  481. document.getElementById('formStatus').value+="\n Update success";
  482. }
  483. function updateFailure(json){
  484. document.getElementById('formStatus').value+="\n Update failed";
  485. }
  486. function addNewEntry(parameters){
  487. print("Add new, npar ");
  488. let entry=new Object();
  489. for (vv in parameters.vars){
  490. let f=parameters.vars[vv];
  491. print("New: Adding "+f.masterSelectVarName);
  492. setValue(entry,f);
  493. }
  494. for (f in entry){
  495. print("entry ["+f+"]="+entry[f]);
  496. }
  497. entry.entryId=Date.now();
  498. entry.Date=new Date();
  499. entry.formStatus=1;//In Progress
  500. let config=new Object();
  501. config.schemaName='lists';
  502. config.queryName=parameters.masterQuery;
  503. config.rows=[entry];
  504. config.success=function(data){
  505. populateDaughterSelect(parameters,"Site","Crf",data.rows[0]);
  506. selectEntry(data,parameters.vars["Crf"]);
  507. generateList(parameters,"Crf");};
  508. LABKEY.Query.insertRows(config);
  509. }
  510. function selectEntry(data,row){
  511. let entry=data.rows[0];
  512. let varName=row.masterSelectVarName;
  513. let el=document.getElementById(row.selectId);
  514. for (let i=0;i< el.options.length;i++){
  515. print("selectEntry: "+el.options[i].value+"/"+entry[varName]);
  516. if (el.options[i].value!=entry[varName]) continue;
  517. el.selectedIndex=i;
  518. return;
  519. }
  520. }
  521. function setValue(entry,f){
  522. let el=document.getElementById(f.selectId);
  523. print("setValue: Element: "+el);
  524. if (f.inputType=="select"){
  525. entry[f.masterSelectVarName]=el.value;
  526. }
  527. if (f.inputType=="innerHTML"){
  528. entry[f.masterSelectVarName]=el.innerHTML;
  529. }
  530. }
  531. function addNew(parameters){
  532. print("Show Add new");
  533. let div=document.getElementById(parameters.addDiv);
  534. div.style.display="block";
  535. }
  536. function startForm(parameters){
  537. let crfVar=parameters.vars["Crf"];
  538. let el=document.getElementById(crfVar.selectId);
  539. let config=new Object();
  540. config.schemaName='lists';
  541. config.queryName=parameters.masterQuery;
  542. config.filterArray=[LABKEY.Filter.create(crfVar.masterSelectVarName,el.value)]
  543. config.success=function(data){findURL(data,parameters)};
  544. LABKEY.Query.selectRows(config);
  545. // The set of URL parameters.
  546. }
  547. function findURL(data,parameters){
  548. let entry=data.rows[0];
  549. let fields=data.metaData.fields;
  550. let formVar=parameters.vars["Form"];
  551. let formVarName=formVar.masterSelectVarName;
  552. let lookup;
  553. for (f in fields){
  554. if (fields[f].name!=formVarName) continue;
  555. lookup=fields[f].lookup;
  556. break;
  557. }
  558. let config=new Object();
  559. config.schemaName=lookup.schemaName;
  560. config.queryName=lookup.queryName;
  561. config.filterArray=[LABKEY.Filter.create(lookup.keyColumn,entry[formVarName])];
  562. config.success=function(data){finalRedirect(data,entry,parameters)};
  563. LABKEY.Query.selectRows(config);
  564. }
  565. function finalRedirect(data,entry,parameters){
  566. let formEntry=data.rows[0];
  567. let formVar=parameters.vars["Form"];
  568. let formUrl=formEntry[formVar.urlName];
  569. var params = {
  570. "name": formUrl, // The destination wiki page. The name of this parameter is not arbitrary.
  571. "userid": entry[parameters.vars["User"].masterSelectVarName],
  572. "entryId": entry[parameters.vars["Crf"].masterSelectVarName]
  573. };
  574. // This changes the page after building the URL.
  575. //Note that the wiki page destination name is set in params.
  576. var wikiURL = LABKEY.ActionURL.buildURL("wiki", "page", LABKEY.ActionURL.getContainer(), params);
  577. print("Redirecting to "+wikiURL);
  578. window.location = wikiURL;
  579. }
  580. </script>