// ==UserScript== // @name FB: full timestamps // @match https://www.facebook.com/* // @match https://*.facebook.com/* // @run-at document-start // @grant GM_addStyle // @author wOxxOm & JZersche // @require https://greasyfork.org/scripts/12228/code/setMutationHandler.js // @ require http://momentjs.com/downloads/moment.min.js // @version 1.04 // @namespace https://greasyfork.org/users/95175 // @description Shows full timestamps on Facebook posts // @downloadURL none // ==/UserScript== var options = { weekday: 'long', year: 'numeric', month: 'numeric', day: '2-digit' }; GM_addStyle( '.full-timestamp { opacity: 0.65; color: #f00; }' + '.full-timestamp:hover { opacity: 1.0; }' + '.full-timestamp:before { content: " ("; }' + '.full-timestamp:after { content: ")"; }' ); // process the already loaded portion of the page if any expandDates(document.querySelectorAll('abbr[data-utime]')); // process the stuff added from now on setMutationHandler(document, 'abbr[data-utime]', expandDates); function expandDates(nodes) { for (var i = 0, abbr; (abbr = nodes[i++]); ) { if (abbr.querySelector('.full-timestamp')) { // already processed continue; } abbr.insertAdjacentHTML('beforeend', '' + (moment(new Date(abbr.dataset.utime * 1000)).format('dddd, M/DD/YYYY []')) + '' + abbr.title.substr(+15, abbr.title.substr.length0) .replace('2000','').replace('2001','').replace('2002','') .replace('2003','').replace('2004','').replace('2005','') .replace('2006','').replace('2007','').replace('2008','') .replace('2009','').replace('2010','').replace('2011','') .replace('2012','').replace('2013','').replace('2014','') .replace('2015','').replace('2016','').replace('2017','') .replace('000','').replace('001','').replace('002','') .replace('003','').replace('004','').replace('005','') .replace('006','').replace('007','').replace('008','') .replace('009','').replace('010','').replace('011','') .replace('012','').replace('013','').replace('014','') .replace('015','').replace('016','').replace('017','') .replace('January','').replace('February','').replace('March','') .replace('April','').replace('May','').replace('June','') .replace('July','').replace('August','').replace('September','') .replace('October','').replace('November','').replace('December','') .replace('at','').replace('am','AM').replace('pm','PM') .replace('ber','').replace('ary','').replace('ry','') .replace('t','') .replace('er 4','').replace('er','').replace(' 5','5') .replace(' 6','6').replace(' 7','').replace(' 8','') .replace(' 9','9').replace('y 10','').replace(' 11,','') .replace(' 12','12').replace('12,','').replace('13,','') .replace(' 13,','').replace(' 14','').replace(' 15','') .replace('15 ','').replace(' 16','').replace(' 18','') .replace(' 19','').replace(' 20','').replace('ry 21,','') .replace(' 22','').replace(' 23','').replace(' 24','') .replace(' 25','').replace(' 26','').replace(' 27','') .replace(' 28','').replace(' 29','').replace(' 30,','') .replace(' 31','').replace(' 32','').replace(' 27','') .replace('r 28','').replace('30, ','').replace('14 ','') .replace(',','') + //':' + ( moment(new Date(abbr.dataset.utime * 1000) ) .format(' · [UTC]Z')) + ''); } } /* Method #1 (moment(abbr.dataset.utime * 1000).format('dddd, M/DD/YYYY, h:mm:ss A · [UTC]-05:00')) + ''); ~ Problematic, because old posts end up showing 3 hours ahead, while recent posts show correct time for some unknown reason.*/ /* Method #2 (moment(abbr.dataset.utime * 1000).utcOffset.format('dddd, M/DD/YYYY, h:mm:ss A · [UTC]-05:00')) + ''); ~ Use this to subtract 3 hours, resulting in accurate older posts but newer posts show 3 hours behind. */ // new Date(abbr.dataset.utime * 1000).toLocaleString() + ''); //// document.getElementsByTagName("abbr")[i].title.toLocaleString() + ''); //// //// (moment(new Date()).format('dddd, M/DD/YYYY, h:mm:ss A · [GMT]ZZ')); ////// new Date(abbr.dataset.utime * 1000).toLocaleDateString('en-US', options) + ''); /* (moment(abbr.dataset.utime * 1000).subtract('hours', 3).format('dddd, M/DD/YYYY, h:mm:ss A · [UTC]Z')) + ''); */ /* EXAMPLE: setMutationHandler(document, '.container p.some-child', function(nodes) { // single node: nodes[0].remove(); // or multiple nodes: nodes.forEach(function(node) { node.style.display = 'none'; }); //this.disconnect(); // disconnect the observer, this is useful for one-time jobs }); */ // ==UserScript== // @name setMutationHandler // @description MutationObserver wrapper to wait for the specified CSS selector // @namespace wOxxOm.scripts // @author wOxxOm // @grant none // @version 2.0.9 // ==/UserScript== /* function setMutationHandler(baseNode, selector, cb, options) { var ob = new MutationObserver(function MOhandler(mutations) { if (mutations.length > 100 && !document.querySelector(selector)) return; var found = []; for (var i=0, ml=mutations.length; i < ml; i++) { var m = mutations[i]; switch (m.type) { case 'childList': var nodes = m.addedNodes, nl = nodes.length; var textNodesOnly = true; for (var j=0; j < nl; j++) { var n = nodes[j]; textNodesOnly &= n.nodeType == 3; // TEXT_NODE if (n.nodeType != 1) // ELEMENT_NODE continue; if (n.matches(selector)) found.push(n); else if (n.querySelector(selector)) { n = n.querySelectorAll(selector); if (n.length < 1000) found.push.apply(found, n); else found = found.concat(found.slice.call(n)); } } if (textNodesOnly && m.target.matches(selector)) found.push(m.target); break; case 'attributes': if (m.target.matches(selector)) found.push(m.target); break; case 'characterData': if (m.target.parentNode && m.target.parentNode.matches(selector)) found.push(m.target.parentNode); break; } } if (found.length) cb.call(ob, found); }); ob.observe(baseNode, options || {subtree:true, childList:true}); return ob; } */