You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

625 lines
20 KiB

5 years ago
  1. if(typeof __wm==="undefined") __wm={};
  2. (function(){
  3. var _JSON = typeof __wbhack != 'undefined' ? __wbhack.JSON : JSON;
  4. var prettyMonths = [
  5. "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];
  6. var $D=document,$=function(n){return document.getElementById(n)};
  7. function formatNumber(n) {
  8. return (''+n).replace(/\B(?=(\d{3})+$)/g, ',');
  9. }
  10. var ajax=__wm.ajax=function ajax(method, url, callback, headers, data) {
  11. var xmlhttp;
  12. if (window.XMLHttpRequest) {
  13. xmlhttp = new XMLHttpRequest();
  14. } else {
  15. xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
  16. }
  17. xmlhttp.onreadystatechange = function() {
  18. if (this.readyState == 4) {
  19. callback(xmlhttp);
  20. }
  21. };
  22. xmlhttp.open(method, url, true);
  23. if (headers) {
  24. for (var header in headers) {
  25. if (headers.hasOwnProperty(header)) {
  26. xmlhttp.setRequestHeader(header, headers[header]);
  27. }
  28. }
  29. }
  30. // pass cookies for user authorization
  31. xmlhttp.withCredentials = true;
  32. xmlhttp.send(data);
  33. }
  34. __wm.h=function hideToolbar(ev) {
  35. $("wm-ipp").style.display="none";
  36. ev.stopPropagation();
  37. }
  38. var $expand, $capinfo;
  39. __wm.bt=function bootstrap(imgWidth,imgHeight,yearImgWidth,monthImgWidth,
  40. coll,wbCurrentUrl,captureDate,firstYear) {
  41. var wbPrefix='/'+(coll||'web')+'/';
  42. captureDate = captureDate.split('-');
  43. var displayDay = captureDate[2];
  44. var displayMonth = captureDate[1];
  45. var displayYear = captureDate[0];
  46. var trackerVal,curYear = -1,curMonth = -1;
  47. var yearTracker,monthTracker;
  48. var $spk=$('wm-ipp-sparkline')
  49. $expand=$('wm-expand');
  50. $capinfo=$('wm-capinfo');
  51. function showTrackers(event) {
  52. var val = event.type=="mouseenter"?1:0;
  53. if (val===trackerVal) return;
  54. var $ipp=$("wm-ipp");
  55. var $y=$("displayYearEl"),$m=$("displayMonthEl"),$d=$("displayDayEl");
  56. if (val) {
  57. $ipp.className="hi";
  58. } else {
  59. $ipp.className="";
  60. $y.innerHTML=displayYear;$m.innerHTML=prettyMonths[displayMonth-1];$d.innerHTML=displayDay;
  61. }
  62. yearTracker.style.display=val?"inline":"none";
  63. monthTracker.style.display=val?"inline":"none";
  64. trackerVal = val;
  65. }
  66. function getElementX2(el) {
  67. var de = $D.documentElement;
  68. var box = (typeof el.getBoundingClientRect!=='undefied')?
  69. el.getBoundingClientRect():{top:0,left:0};
  70. return box.left + (window.pageXOffset||de.scrollLeft)-(de.clientLeft||0);
  71. }
  72. function navCaptures(captures) {
  73. var $e = $("wm-nav-captures");
  74. var count = 0;
  75. var years = captures.years;
  76. var first_ts = captures.first_ts, last_ts = captures.last_ts;
  77. for (var j = 0; j < years.length; j++) {
  78. var months = years[j][1];
  79. for (var i = 0; i < months.length; i++) {
  80. count += months[i];
  81. }
  82. }
  83. var html = '<a class="t" href="' + wbPrefix + '*/' + wbCurrentUrl +
  84. '" title="See a list of every capture for this URL">' +
  85. formatNumber(count) + ' ' +
  86. (count > 1 ? "captures" : "capture") + '</a>';
  87. var timespan = __wbTs.format(first_ts, '%d %b %Y');
  88. if (last_ts != first_ts) {
  89. timespan += ' - ' + __wbTs.format(last_ts, '%d %b %Y');
  90. }
  91. html += '<div class="r" title="Timespan for captures of this URL">' +
  92. timespan + '</div>';
  93. $e.innerHTML = html;
  94. }
  95. function trackMouseMove(event) {
  96. //var element = event.target;
  97. var element = $spk;
  98. var eventX = getEventX(event);
  99. var elementX = getElementX2(element);
  100. var xOff = Math.min(Math.max(0, eventX - elementX),imgWidth);
  101. var monthOff = xOff % yearImgWidth;
  102. var year = Math.floor(xOff / yearImgWidth);
  103. var monthOfYear = Math.min(11,Math.floor(monthOff / monthImgWidth));
  104. // 1 extra border pixel at the left edge of the year:
  105. var month = (year * 12) + monthOfYear;
  106. var day = monthOff % 2==1?15:1;
  107. var dateString = zeroPad(year + firstYear) + zeroPad(monthOfYear+1,2) +
  108. zeroPad(day,2) + "000000";
  109. $("displayYearEl").innerHTML=year+firstYear;
  110. $("displayMonthEl").innerHTML=prettyMonths[monthOfYear];
  111. // looks too jarring when it changes..
  112. //$("displayDayEl").innerHTML=zeroPad(day,2);
  113. var url = wbPrefix + dateString + '/' + wbCurrentUrl;
  114. $("wm-graph-anchor").href=url;
  115. if(curYear != year) {
  116. var yrOff = year * yearImgWidth;
  117. yearTracker.style.left = yrOff + "px";
  118. curYear = year;
  119. }
  120. if(curMonth != month) {
  121. var mtOff = year + (month * monthImgWidth) + 1;
  122. monthTracker.style.left = mtOff + "px";
  123. curMonth = month;
  124. }
  125. }
  126. function disclaimElement(element) {
  127. if (window.top == window.self) {
  128. element.style.display = "block";
  129. $D.body.insertBefore(element, $D.body.firstChild);
  130. }
  131. }
  132. yearTracker=$D.createElement('div');
  133. yearTracker.className='yt';
  134. with(yearTracker.style){
  135. display='none';width=yearImgWidth+"px";height=imgHeight+"px";
  136. }
  137. monthTracker=$D.createElement('div');
  138. monthTracker.className='mt';
  139. with(monthTracker.style){
  140. display='none';width=monthImgWidth+"px";height=imgHeight+"px";
  141. }
  142. $spk.appendChild(yearTracker);
  143. $spk.appendChild(monthTracker);
  144. var $cv=$('wm-sparkline-canvas');
  145. $spk.onmouseenter=showTrackers;
  146. $spk.onmouseleave=showTrackers;
  147. $spk.onmousemove=trackMouseMove;
  148. var $ipp=$("wm-ipp");
  149. $ipp&&disclaimElement($ipp);
  150. var testCanvas = document.createElement('canvas');
  151. if(!!(testCanvas.getContext && testCanvas.getContext('2d'))) {
  152. var sparkline_url = "/__wb/sparkline?output=json&url=" +
  153. encodeURIComponent(wbCurrentUrl) +
  154. (coll && "&collection=" + coll || '');
  155. ajax("GET", sparkline_url, function(response) {
  156. if(response.status == 200) {
  157. var capnav=_JSON.parse(response.responseText);
  158. var yearsobj = capnav.years;
  159. var ykeys = Object.getOwnPropertyNames(yearsobj);
  160. var years = (capnav.years = []);
  161. for (var i = 0; i < ykeys.length; i++) {
  162. var y = ykeys[i];
  163. if (yearsobj[y]) {
  164. years.push([y, yearsobj[y]]);
  165. }
  166. }
  167. navCaptures(capnav);
  168. sparkline(capnav,imgWidth,imgHeight,'wm-sparkline-canvas',
  169. firstYear, displayYear, displayMonth);
  170. }
  171. });
  172. } else {
  173. var sparklineImg = new Image();
  174. sparklineImg.src = "/__wb/sparkline?url=" +
  175. encodeURIComponent(wbCurrentUrl) +
  176. "&width=" + imgWidth + "&height=" + imgHeight +
  177. "&selected_year=" + displayYear + "&selected_month=" + displayMonth +
  178. (coll && "&collection=" + coll || '');
  179. sparklineImg.alt= "sparkline";
  180. sparklineImg.width=imgWidth;
  181. sparklineImg.height=imgHeight;
  182. sparklineImg.id="sparklineImgId";
  183. sparklineImg.border="0";
  184. $cv.parentNode.replaceChild(sparklineImg, $cv);
  185. }
  186. function process_autocomplete(data) {
  187. var out = []
  188. var len = data.length;
  189. for(var i=0; i<len; i++) {
  190. if(typeof data[i].excluded === 'undefined') {
  191. out.push(data[i].display_name);
  192. }
  193. }
  194. return out;
  195. }
  196. new wbAutoComplete({
  197. selector: 'input#wmtbURL',
  198. delay: 400,
  199. source: function(query, suggest) {
  200. ajax("GET", '/__wb/search/host?q=' + encodeURIComponent(query),
  201. function(data) {
  202. var data = _JSON.parse(data.response);
  203. if (typeof data.hosts!=='undefined' && data.hosts.length>0) {
  204. var output = process_autocomplete(data.hosts);
  205. suggest(output);
  206. } else if (typeof data.isUrl!=='undefined' && data.isUrl===true && typeof data.excluded==='undefined') {
  207. suggest([query]);
  208. } else {
  209. ajax("GET", '/__wb/search/anchor?q='+encodeURIComponent(query),
  210. function(data) {
  211. var data = _JSON.parse(data.response);
  212. if (typeof data!=='undefined' && data.length>0) {
  213. var output = process_autocomplete(data.slice(0,5));
  214. suggest(output);
  215. }
  216. });
  217. }
  218. });
  219. },
  220. onSelect: function(e, term, item) {
  221. $("wmtb").submit();
  222. }
  223. });
  224. $("wmtb").onsubmit = function(e) {
  225. var query = $("wmtbURL").value;
  226. // if textbox value is not a URL, redirect to search
  227. if (!(query.indexOf('http://') === 0 || query.indexOf('https://') === 0 ||
  228. query.match(/[\w\.]{2,256}\.[a-z]{2,4}/gi))) {
  229. document.location.href="/web/*/" + $("wmtbURL").value;
  230. e.preventDefault();
  231. return false;
  232. }
  233. };
  234. };
  235. function show_timestamps() {
  236. // Populate capinfo with capture resources if empty. If not empty, it has
  237. // already run before so avoid redoing AJAX.
  238. var $capresources=$('wm-capresources');
  239. $capresources.innerHTML = '';
  240. //disable caching to be able to reload list when browsing frames.
  241. //if($capresources.innerHTML.length !== 0) {
  242. // return;
  243. //}
  244. var $wmloading=$("wm-capresources-loading");
  245. $wmloading.style.display='block';
  246. // calculate datetime difference with capture datetime and return relative
  247. // value such as "-5 hours, 10 minutes".
  248. var capture_ts = document.getElementById('wmtb').elements.date.value;
  249. var capture_msec = __wbTs.timestamp2datetime(capture_ts).getTime();
  250. function datetime_diff(dt_str) {
  251. var dt_msec = Date.parse(dt_str);
  252. var diff = dt_msec - capture_msec;
  253. var prefix = "";
  254. if(diff < 0) {
  255. prefix += "-";
  256. diff = Math.abs(diff);
  257. } else {
  258. prefix += "+";
  259. }
  260. var highlight = false;
  261. if(diff < 1000) {
  262. // equal to the page datetime
  263. return {delta: diff, text:"", highlight: highlight};
  264. }
  265. var total_diff = diff;
  266. var years_d = Math.floor(diff/1000/60/60/24/30/12);
  267. diff -= years_d*1000*60*60*24*30*12;
  268. var months_d = Math.floor(diff/1000/60/60/24/30);
  269. diff -= months_d*1000*60*60*24*30;
  270. var days_d = Math.floor(diff/1000/60/60/24);
  271. diff -= days_d*1000*60*60*24;
  272. var hours_d = Math.floor(diff/1000/60/60);
  273. diff -= hours_d*1000*60*60;
  274. var minutes_d = Math.floor(diff/1000/60);
  275. diff -= minutes_d*1000*60;
  276. var seconds_d = Math.floor(diff/1000);
  277. var parts = [];
  278. if(years_d > 1) {
  279. parts.push(years_d + " years");
  280. highlight = true;
  281. } else if(years_d == 1) {
  282. parts.push(years_d + " year");
  283. highlight = true;
  284. }
  285. if(months_d > 1) {
  286. parts.push(months_d + " months");
  287. highlight = true;
  288. } else if(months_d == 1) {
  289. parts.push(months_d + " month");
  290. highlight = true;
  291. }
  292. if(days_d > 1) {
  293. parts.push(days_d + " days");
  294. } else if(days_d == 1) {
  295. parts.push(days_d + " day");
  296. }
  297. if(hours_d > 1) {
  298. parts.push(hours_d + " hours");
  299. } else if(hours_d == 1) {
  300. parts.push(hours_d + " hour");
  301. }
  302. if(minutes_d > 1) {
  303. parts.push(minutes_d + " minutes");
  304. } else if (minutes_d == 1) {
  305. parts.push(minutes_d + " minute");
  306. }
  307. if(seconds_d > 1) {
  308. parts.push(seconds_d + " seconds");
  309. } else if(seconds_d == 1) {
  310. parts.push(seconds_d + " second");
  311. }
  312. if(parts.length > 2) {
  313. parts = parts.slice(0, 2);
  314. }
  315. return {delta: total_diff, text: prefix + parts.join(" "),
  316. highlight: highlight};
  317. }
  318. // Utility method to find elements in dom (currently only img) using URL.
  319. // Also look into embedded frames recursively
  320. // Captured resources urls may have timestamps different from DOM URL
  321. // so it is not possible to search with original path
  322. // /web/20120407141544/http://example.com
  323. // we must search for URLS ENDING WITH http://example.com
  324. function find_elements_by_url(current_window, url) {
  325. var orig_url = url.split("/").splice(6).join("/");
  326. var els=current_window.document.querySelectorAll(
  327. "img[src$='" + orig_url + "'], iframe[src$='" + orig_url + "'], frame[src$='" + orig_url + "']"
  328. );
  329. var els_array=Array.prototype.slice.call(els);
  330. for(var i=0; i<current_window.frames.length; i++) {
  331. try {
  332. var frame_els_array=find_elements_by_url(current_window.frames[i].window, url);
  333. els_array = els_array.concat(frame_els_array);
  334. } catch(err) {
  335. // pass
  336. }
  337. }
  338. return els_array;
  339. }
  340. // invoked onmouseover of link to add highlight
  341. function highlight_elm(e){
  342. if(e.tagName=='FRAME'||e.tagName=='IFRAME')
  343. return e.contentWindow.document.documentElement;
  344. else
  345. return e;
  346. }
  347. function highlight_on(ev) {
  348. var elements = find_elements_by_url(window, ev.target.href);
  349. if(elements.length > 0) {
  350. for(var i=0; i<elements.length; i++) {
  351. highlight_elm(elements[i]).classList.add("wb-highlight");
  352. }
  353. }
  354. }
  355. // invoked onmouseout of link to remove highlight
  356. function highlight_off(ev) {
  357. var elements = find_elements_by_url(window, ev.target.href);
  358. if(elements.length > 0) {
  359. for(var i=0; i<elements.length; i++) {
  360. highlight_elm(elements[i]).classList.remove("wb-highlight");
  361. }
  362. }
  363. }
  364. // Utility method to show capture elements link, datetime and content-type.
  365. // AJAX follows redirects automatically, only status=200 responses are handled.
  366. function get_resource_info(url) {
  367. ajax("HEAD", url, function(response) {
  368. $wmloading.style.display='none';
  369. if(response.status==200) {
  370. var dt=response.getResponseHeader('Memento-Datetime');
  371. var dt_span=document.createElement('span');
  372. var dt_result = datetime_diff(dt);
  373. var style = dt_result.highlight ? "color:red;" : "";
  374. dt_span.innerHTML=" " + dt_result.text;
  375. dt_span.title=dt;
  376. dt_span.setAttribute('style', style);
  377. var ct=response.getResponseHeader('Content-Type');
  378. var url=response.responseURL.replace(window.location.origin, "");
  379. var link=document.createElement('a');
  380. // remove /web/timestamp/ from appearance
  381. link.innerHTML=url.split("/").splice(3).join("/");
  382. link.href=url;
  383. link.title=ct;
  384. link.onmouseover=highlight_on;
  385. link.onmouseout=highlight_off;
  386. var el=document.createElement('div');
  387. el.setAttribute('data-delta', dt_result.delta);
  388. el.appendChild(link);
  389. el.append(dt_span);
  390. $capresources.appendChild(el);
  391. // sort elements by delta in a descending order and update container
  392. var items = Array.prototype.slice.call($capresources.childNodes, 0);
  393. items.sort(function(a, b) {
  394. return b.getAttribute('data-delta') - a.getAttribute('data-delta');
  395. });
  396. $capresources.innerHTML = "";
  397. for(var i=0, len=items.length; i<len; i++) {
  398. $capresources.appendChild(items[i]);
  399. }
  400. }
  401. });
  402. }
  403. // utility method to traverse the document and frames recursively to find
  404. // element with specific tag. Always convert selector result (NodeList)
  405. // to Array to be able to concat.
  406. function find_elements_by_tag_name(current_window, tag) {
  407. var els=current_window.document.getElementsByTagName(tag);
  408. var els_array=Array.prototype.slice.call(els);
  409. for(var i=0; i<current_window.frames.length; i++) {
  410. try {
  411. var frame_els_array=find_elements_by_tag_name(current_window.frames[i].window, tag);
  412. els_array = els_array.concat(frame_els_array);
  413. } catch(err) {
  414. // pass
  415. }
  416. }
  417. return els_array;
  418. }
  419. // images
  420. var static_prefix=window.location.origin + "/static/";
  421. var srcList=[];
  422. var imgs=find_elements_by_tag_name(window, 'img');
  423. for(var i=0, len=imgs.length; i<len; i++) {
  424. // exclude WBM /static/images, leaked images and embedded data URIs
  425. if(!imgs[i].src || imgs[i].src.startsWith(static_prefix) ||
  426. !imgs[i].src.startsWith(window.location.origin) ||
  427. imgs[i].src.startsWith("data:")) {
  428. continue;
  429. }
  430. srcList.push(imgs[i].src);
  431. }
  432. // frames
  433. var frames=find_elements_by_tag_name(window, 'frame');
  434. for(i=0, len=frames.length; i<len; i++) {
  435. if(!frames[i].src) {
  436. continue;
  437. }
  438. srcList.push(frames[i].src);
  439. }
  440. var iframes=find_elements_by_tag_name(window, 'iframe');
  441. for(i=0, len=iframes.length; i<len; i++) {
  442. if(!iframes[i].src || (iframes[i].id && iframes[i].id === 'playback')) {
  443. continue;
  444. }
  445. srcList.push(iframes[i].src);
  446. }
  447. var scripts=find_elements_by_tag_name(window, 'script');
  448. for(i=0, len=scripts.length; i<len; i++) {
  449. if(!scripts[i].src || scripts[i].src.startsWith(static_prefix) ||
  450. !scripts[i].src.startsWith(window.location.origin)) {
  451. continue;
  452. }
  453. srcList.push(scripts[i].src);
  454. }
  455. // link.href (CSS, RSS, etc)
  456. var links=find_elements_by_tag_name(window, 'link');
  457. for(i=0, len=links.length; i<len; i++) {
  458. if(!links[i].href || links[i].href.startsWith(static_prefix) ||
  459. !links[i].href.startsWith(window.location.origin)) {
  460. continue;
  461. }
  462. if(links[i].rel && links[i].rel=="stylesheet") {
  463. srcList.push(links[i].href);
  464. }
  465. }
  466. // deduplicate
  467. var deduped = srcList.filter(function(el, i, arr) {
  468. return arr.indexOf(el) === i;
  469. });
  470. if(deduped.length > 0) {
  471. deduped.map(get_resource_info);
  472. } else {
  473. $capresources.innerHTML = "There are no sub-resources in the page.";
  474. $wmloading.style.display='none';
  475. }
  476. }
  477. __wm.ex=function expand(ev) {
  478. ev.stopPropagation();
  479. var c=$expand.className;
  480. if (c.match(/wm-closed/)) { // closed
  481. $expand.className=c.replace(/wm-closed/,'wm-open');
  482. $capinfo.style.display='block';
  483. show_timestamps();
  484. } else {
  485. $expand.className=c.replace(/wm-open/,'wm-closed');
  486. $capinfo.style.display='none';
  487. }
  488. };
  489. function isArray(obj) {
  490. return (typeof obj !== 'undefined' && obj && obj.constructor === Array);
  491. }
  492. function setDisplayStyle(id, display) {
  493. var el = $(id);
  494. if (el) {
  495. el.style.display = display;
  496. }
  497. }
  498. function show(ids) {
  499. if (!isArray(ids)) {
  500. ids = [ids];
  501. }
  502. for (var i = 0; i < ids.length; i++) {
  503. setDisplayStyle(ids[i], 'inline-block');
  504. }
  505. }
  506. function hide(ids) {
  507. if (!isArray(ids)) {
  508. ids = [ids];
  509. }
  510. for (var i = 0; i < ids.length; i++) {
  511. setDisplayStyle(ids[i], 'none');
  512. }
  513. }
  514. function userIsLoggedIn() {
  515. show('wm-save-snapshot-open');
  516. hide('wm-sign-in');
  517. }
  518. function userIsNotLoggedIn() {
  519. hide([
  520. 'wm-save-snapshot-open',
  521. 'wm-save-snapshot-in-progress',
  522. ]);
  523. show('wm-sign-in');
  524. }
  525. function startSnapShotSaving() {
  526. hide([
  527. 'wm-save-snapshot-fail',
  528. 'wm-save-snapshot-open',
  529. 'wm-save-snapshot-success',
  530. ]);
  531. show([
  532. 'wm-save-snapshot-in-progress',
  533. ]);
  534. }
  535. function successSnapshotSaving() {
  536. hide([
  537. 'wm-save-snapshot-fail',
  538. 'wm-save-snapshot-in-progress',
  539. ]);
  540. show([
  541. 'wm-save-snapshot-open',
  542. 'wm-save-snapshot-success',
  543. ]);
  544. }
  545. function failSnapshotSaving(err) {
  546. hide([
  547. 'wm-save-snapshot-in-progress',
  548. 'wm-save-snapshot-success',
  549. ]);
  550. show([
  551. 'wm-save-snapshot-fail',
  552. 'wm-save-snapshot-open',
  553. ]);
  554. }
  555. /**
  556. * check whether cookie has field
  557. *
  558. * @param name
  559. * @return boolean
  560. */
  561. function hasCookie(name) {
  562. return document.cookie.search(name) >= 0;
  563. }
  564. __wm.saveSnapshot = function (url, timestamp, tags) {
  565. startSnapShotSaving();
  566. ajax('POST', '/__wb/web-archive/', function (res) {
  567. if (res.status === 401) {
  568. // it seems that user is not logged in
  569. userIsNotLoggedIn();
  570. } else if (res.status >= 400) {
  571. failSnapshotSaving(res.responseText);
  572. console.log('You have got an error.');
  573. console.log('If you think something wrong here please send it to support.');
  574. console.log('Response: "' + res.responseText + '"');
  575. console.log('status: "' + res.status + '"');
  576. } else {
  577. successSnapshotSaving(res);
  578. }
  579. }, {
  580. 'Content-Type': 'application/json'
  581. }, _JSON.stringify({
  582. url: url,
  583. snapshot: timestamp,
  584. tags: tags || [],
  585. }));
  586. return false;
  587. };
  588. document.addEventListener('DOMContentLoaded', function () {
  589. if (hasCookie('logged-in-user') && hasCookie('logged-in-sig')) {
  590. userIsLoggedIn();
  591. } else {
  592. userIsNotLoggedIn();
  593. }
  594. });
  595. })();