Voat Enhancement

A simple script to provide some basic enhancement for voat. Includes auto-pagination, info bar detachment, and extra embedding for comments.

  1. // ==UserScript==
  2. // @name Voat Enhancement
  3. // @namespace septus.info
  4. // @include http://voat.co/*
  5. // @include http://*.voat.co/*
  6. // @include https://voat.co/*
  7. // @include https://*.voat.co/*
  8. // @version 1.0.5
  9. // @grant GM_getValue
  10. // @grant GM_setValue
  11. // @require https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js
  12. // @description:en A simple script to provide some basic enhancement for voat. Includes auto-pagination, info bar detachment, and extra embedding for comments.
  13. // @description A simple script to provide some basic enhancement for voat. Includes auto-pagination, info bar detachment, and extra embedding for comments.
  14. // ==/UserScript==
  15.  
  16. var PostProcessSubmissions = function(submissions){
  17. /*
  18. var toolTipsterData = {
  19. content: "Loading user info...",
  20. contentAsHTML: "true",
  21. functionBefore: function(n, t) {
  22. t();
  23. n.data("ajax") !== "cached" && $.ajax({
  24. type: "GET",
  25. url: "/ajaxhelpers/userinfo/" + n.attr("data-username"),
  26. success: function(t) {
  27. n.tooltipster("content", t).data("ajax", "cached");
  28. }
  29. });
  30. }
  31. };
  32. for(var i = 0; i < submissions.length; i++){
  33. $(submissions[i]).find(".userinfo").tooltipster(toolTipsterData);
  34. }
  35. */
  36. };
  37.  
  38.  
  39. function Plugin_UserTag(framework){
  40. var _this = this;
  41. var defaults = {
  42. tags: {
  43. pathogenxd: "VES Creator"
  44. }
  45. }
  46. if(GM_getValue('Plugin_UserTag') === null){
  47. GM_setValue('Plugin_UserTag', JSON.stringify(defaults));
  48. }
  49. this.settings = JSON.parse(GM_getValue('Plugin_UserTag'));
  50. //console.log(this.settings);
  51. $(document).on("comments-loaded", function(event){
  52. for(var i = 0; i < event.comments.length; i++){
  53. var username = $($(event.comments[i]).find("p.tagline a.author")[0]).text().toLowerCase();
  54. //console.log(username);
  55. if(_this.settings.tags[username] !== undefined)
  56. {
  57. var userattrsElement = $(event.comments[i]).find("p.tagline span.userattrs")[0];
  58. $(userattrsElement).after("&nbsp;<span style='color: blue;'>" + _this.settings.tags[username] + "</span>");
  59. }
  60. var buttons = $(event.comments[i]).find("ul.buttons")[0];
  61. var button = $('<li><a>tag user</a></li>').on('click', function(event){
  62. var usernameToTag = $(event.currentTarget).attr('data-username');
  63. var newTag = prompt('Custom tag for '+usernameToTag);
  64. if(newTag !== null){
  65. _this.settings.tags[usernameToTag] = newTag;
  66. GM_setValue('Plugin_UserTag', JSON.stringify(_this.settings));
  67. }
  68. });
  69. button.attr('data-username', username);
  70. //console.log(button);
  71. $(buttons).append(button);
  72. }
  73. });
  74. }
  75.  
  76. function Plugin_AutoPaginate(framework){
  77. this.framework = framework;
  78. this.currentPage = framework.page.currentPage;
  79. this.contentLoading = false;
  80. this.morePages = true;
  81. this.postIdLoaded = {};
  82. var _this = this;
  83. if(this.framework.page.type === "subverse"){
  84. this.HandleSubverse();
  85. }
  86. }
  87. Plugin_AutoPaginate.prototype.HandleSubverse = function(){
  88. var _this = this;
  89. if(! ($("li.btn-whoaverse-paging > a[rel^='next']")[0])){
  90. this.morePages = false;
  91. }
  92. //console.log(this.morePages);
  93. var url = "";
  94. if(this.framework.page.subverse === ""){
  95. url = "/"+this.framework.page.sorting;
  96. }
  97. else{
  98. url = "/v/" + this.framework.page.subverse +"/" + this.framework.page.sorting;
  99. }
  100. //console.log(url);
  101. $(document).on("submissions-loaded", function(event){
  102. // Hide duplicate posts as they appear
  103. for(var i = 0; i < event.submissions.length; i++){
  104. var postId = $(event.submissions[i]).attr('data-fullname');
  105. if(!_this.postIdLoaded[postId]){
  106. _this.postIdLoaded[postId] = true;
  107. }
  108. else{
  109. $(event.submissions[i]).css({display: "none"});
  110. //console.log("post id " + postId + " hidden");
  111. }
  112. }
  113. });
  114. $(document).on("scroll", function(event){
  115. var distanceToNewPage = $("div.pagination-container").offset().top -
  116. (window.pageYOffset + $(window).height()) - 600;
  117. //console.log(distanceToNewPage + " " + _this.contentLoading + " " + _this.morePages);
  118. if(distanceToNewPage < 0 && !_this.contentLoading && _this.morePages){
  119. _this.contentLoading = true;
  120. _this.currentPage++;
  121. //console.log("Loading page " + _this.currentPage);
  122. $.get( url, {page: _this.currentPage} )
  123. .done(function( data ) {
  124. var loadedDocument = $(data);
  125. var loadedSubmissions = null;
  126. if(_this.framework.page.subverse === ""){
  127. loadedSubmissions = loadedDocument.find("div.sitetable > div.submission");
  128. var paginationContainer = $("div.pagination-container");
  129. for(var i = 0; i < loadedSubmissions.length; i++){
  130. paginationContainer.before(loadedSubmissions[i]);
  131. }
  132. }
  133. else{
  134. loadedSubmissions = loadedDocument.find("div.linklisting > div.submission");
  135. var linklisting = $("div.linklisting");
  136. for(var i = 0; i < loadedSubmissions.length; i++){
  137. linklisting.append(loadedSubmissions[i]);
  138. }
  139. }
  140. if(!loadedDocument.find("li.btn-whoaverse-paging > a[rel^='next']")[0]){
  141. _this.morePages = false;
  142. }
  143. _this.contentLoading = false;
  144. PostProcessSubmissions(loadedSubmissions);
  145. $.event.trigger({
  146. type: "submissions-loaded",
  147. submissions: loadedSubmissions
  148. });
  149. })
  150. .fail(function(error) {
  151. //console.log( error );
  152. });
  153. }
  154. });
  155. };
  156.  
  157. var SaveStyles = function(domObject, styleObject){
  158. for (var property in styleObject) {
  159. styleObject[property] = $(domObject).css(property);
  160. }
  161. };
  162.  
  163. function Plugin_DetachedInfoBar(framework){
  164. var infoBar = $("div#header-account > div:nth-child(1)");
  165. var infoBarTop = infoBar.offset().top;
  166. var infoBarRight = $(window).width() - (infoBar.offset().left + infoBar.width());
  167. var infoBarDetached = false;
  168. var detachedStyle = {"position": "fixed", "top": "0px", "right": "0px"};
  169. var originalStyle = {"position": "", "top": "", "right": ""};
  170. SaveStyles(infoBar, originalStyle);
  171. //console.log(originalStyle);
  172. var changingState = false;
  173. $(document).on("scroll", function(event){
  174. if(!changingState){
  175. if(!infoBarDetached && window.pageYOffset - infoBarTop > 0){
  176. changingState = true;
  177. var animateBeginStyle = jQuery.extend(true, {}, detachedStyle);
  178. animateBeginStyle["right"] = infoBarRight + "px";
  179. infoBar.css(animateBeginStyle);
  180. infoBar.animate(detachedStyle, 200, function(){
  181. infoBarDetached = true;
  182. changingState = false;
  183. });
  184. //console.log("Info bar detached");
  185. }
  186. else if(infoBarDetached && window.pageYOffset - infoBarTop <= 0){
  187. changingState = true;
  188. var animateEndStyle = jQuery.extend(true, {}, detachedStyle);
  189. animateEndStyle["right"] = infoBarRight + "px";
  190. //console.log(animateEndStyle);
  191. infoBar.animate(animateEndStyle, 200, function(){
  192. infoBar.css(originalStyle);
  193. infoBarDetached = false;
  194. changingState = false;
  195. //console.log("complete");
  196. });
  197. }
  198. }
  199. });
  200. }
  201.  
  202. function Plugin_LinkEmbedder(framework){
  203. var collapsedButtonStyle = {
  204. "background": "transparent url(\"/Graphics/Light-SpriteSheet.png\") repeat scroll 0px -44px"
  205. };
  206. var uncollapsedButtonStyle = {
  207. "background": "transparent url(\"/Graphics/Light-SpriteSheet.png\") repeat scroll 0px -60px"
  208. };
  209. var EmbedRules = [
  210. //Imgur Album
  211. function(href){
  212. var result = {handled: false, embedHtml: ""};
  213. var match = null;
  214. match = href.match(/^https?:\/\/([^\.]*\.)?imgur\.com\/(gallery\/)?([a-zA-Z0-9]+)$/);
  215. if(match){
  216. result.handled = true;
  217. result.embedHtml = "<blockquote class=\"imgur-embed-pub\" lang=\"en\" data-id=\"a/"+ match[3] +"\"></blockquote><script async src=\"//s.imgur.com/min/embed.js\" charset=\"utf-8\"></script>";
  218. }
  219. return result;
  220. },
  221. //Daily motion
  222. function(href){
  223. var result = {handled: false, embedHtml: ""};
  224. var match = null;
  225. match = href.match(/^https?:\/\/www\.dailymotion\.com\/video\/([a-zA-Z0-9]+)_.*$/);
  226. if(match){
  227. result.handled = true;
  228. result.embedHtml = "<iframe frameborder=\"0\" width=\"480\" height=\"270\" src=\"//www.dailymotion.com/embed/video/" + match[1] + "\" allowfullscreen></iframe>";
  229. }
  230. return result;
  231. }
  232. ];
  233. $(document).on("submissions-loaded", function(event){
  234. //console.log("We need to process loaded submissions to attach embedded content");
  235. for(var i = 0; i < event.submissions.length; i++){
  236. }
  237. });
  238. $(document).on("comments-loaded", function(event){
  239. for(var i = 0; i < event.comments.length; i++){
  240. var links = $($(event.comments[i]).find("div.entry")[0]).find("div.md a");
  241. links.each(function(){
  242. for(var i = 0; i < EmbedRules.length; i++){
  243. var result = EmbedRules[i](this["href"]);
  244. if(result.handled === true){
  245. var embedDiv = $("<div class=\"embed-container\" style=\"display: block;\">" + result.embedHtml + "</div>");
  246. var button = $("<div></div>").css({
  247. "width": "16px",
  248. "height": "16px",
  249. "display": "inline-block"
  250. });
  251. button.css(collapsedButtonStyle);
  252. var buttonCollapsed = true;
  253. button.on("click", function(event){
  254. if(buttonCollapsed){
  255. buttonCollapsed = false;
  256. if(button.next().length === 0 || button.next()[0].tagName != 'DIV'){
  257. button.after(embedDiv);
  258. }
  259. else{
  260. button.next().css({"display": "block"});
  261. }
  262. button.css(uncollapsedButtonStyle);
  263. }
  264. else{
  265. buttonCollapsed = true;
  266. button.next().css({"display": "none"});
  267. button.css(collapsedButtonStyle);
  268. }
  269. });
  270. $(this).after(button);
  271. break;
  272. }
  273. }
  274. });
  275. }
  276. });
  277. }
  278.  
  279. function Framework(){
  280. function SubversePage(){
  281. this.type = "subverse";
  282. this.subverse = null;
  283. this.currentPage = 0;
  284. this.sorting = null;
  285. }
  286. SubversePage.prototype.TriggerInitialEvents = function(){
  287. loadedSubmission = null;
  288. if(this.subverse === ""){
  289. loadedSubmissions = $("div.sitetable > div.submission");
  290. }
  291. else{
  292. loadedSubmissions = $("div.linklisting > div.submission");
  293. }
  294. $.event.trigger({
  295. type: "submissions-loaded",
  296. submissions: loadedSubmissions
  297. });
  298. };
  299.  
  300. function SubmissionPage(){
  301. this.type = "submission";
  302. this.subverse = null;
  303. this.id = null;
  304. this.loadedParentComments = null;
  305. }
  306. SubmissionPage.prototype.TriggerInitialEvents = function(){
  307. var _this = this;
  308. $.event.trigger({
  309. type: "comments-loaded",
  310. comments: $("div.commentarea div.child")
  311. });
  312. this.loadedParentComments = $("div.commentarea > div.sitetable > div.child").length;
  313. //console.log(this.loadedParentComments);
  314. window.setInterval(function(){
  315. var temp = $("div.commentarea > div.sitetable > div.child");
  316. if(temp.length > _this.loadedParentComments){
  317. //("More comments loaded..." + _this.loadedParentComments + " " + temp.length);
  318. var event = {
  319. type: "comments-loaded",
  320. comments: []
  321. };
  322. for(var i = _this.loadedParentComments; i < temp.length; i++){
  323. event.comments.push(temp[i]);
  324. var temp2 = $(temp[i]).find("div.child");
  325. for(var j = 0; j < temp2.length; j++){
  326. event.comments.push(temp2[j]);
  327. }
  328. }
  329. $.event.trigger(event);
  330. //console.log(event);
  331. _this.loadedParentComments = temp.length;
  332. }
  333. }, 16);
  334. };
  335.  
  336. function PermalinkPage(){
  337. this.type = "permalink";
  338. this.subverse = null;
  339. this.submissionId = null;
  340. this.id = null;
  341. }
  342.  
  343. function UserSubmissionsPage(){
  344. this.type = "user-submissions";
  345. this.username = null;
  346. this.currentPage = 0;
  347. }
  348.  
  349. function UserCommentsPage(){
  350. this.type = "user-comments";
  351. this.username = null;
  352. this.currentPage = 0;
  353. }
  354.  
  355. function OtherPage(){
  356. this.type = "other";
  357. }
  358. //Find out what our page type is plus any useful information
  359. this.page = null;
  360. var urlHandled = false;
  361. var match = window.location.href.match(/^https?:\/\/(www\.)?voat\.co(\/(new))?\/?$/);
  362. if(!urlHandled && match !== null){
  363. this.page = new SubversePage();
  364. this.page.subverse = "";
  365. if(match[3]){
  366. this.page.sorting = match[3];
  367. }
  368. else{
  369. this.page.sorting = "";
  370. }
  371. urlHandled = true;
  372. }
  373. match = window.location.href.match(/^https?:\/\/(www\.)?voat\.co\/v\/([a-zA-Z0-9_]+)(\/(new|top))?$/);
  374. if(!urlHandled && match !== null){
  375. this.page = new SubversePage();
  376. this.page.subverse = match[2];
  377. if(match[4]){
  378. this.page.sorting = match[4];
  379. }
  380. else{
  381. this.page.sorting = "";
  382. }
  383. urlHandled = true;
  384. }
  385. match = window.location.href.match(/^https?:\/\/(www\.)?voat\.co\/v\/([a-zA-Z0-9_]+)\/comments\/([0-9]+)$/);
  386. if(!urlHandled && match !== null){
  387. this.page = new SubmissionPage();
  388. this.page.subverse = match[2];
  389. this.page.id = match[3];
  390. urlHandled = true;
  391. }
  392. match = window.location.href.match(/^https?:\/\/(www\.)?voat\.co\/v\/([a-zA-Z0-9_]+)\/comments\/([0-9]+)\/([0-9]+)$/);
  393. if(!urlHandled && match !== null){
  394. this.page = new PermalinkPage();
  395. this.page.subverse = match[2];
  396. this.page.submissionId = match[3];
  397. this.page.id = match[4];
  398. urlHandled = true;
  399. }
  400. match = window.location.href.match(/^https?:\/\/(www\.)?voat\.co\/user\/([a-zA-Z0-9_]+)\/submissions$/);
  401. if(!urlHandled && match !== null){
  402. this.page = new UserSubmissionsPage();
  403. this.page.username = match[2];
  404. urlHandled = true;
  405. }
  406. match = window.location.href.match(/^https?:\/\/(www\.)?voat\.co\/user\/([a-zA-Z0-9_]+)\/comments$/);
  407. if(!urlHandled && match !== null){
  408. this.page = new UserCommentsPage();
  409. this.page.username = match[2];
  410. urlHandled = true;
  411. }
  412. if(!urlHandled){
  413. this.page = new OtherPage();
  414. }
  415. console.log(this.page);
  416. new Plugin_AutoPaginate(this);
  417. new Plugin_DetachedInfoBar(this);
  418. new Plugin_LinkEmbedder(this);
  419. new Plugin_UserTag(this);
  420. if(this.page.TriggerInitialEvents){
  421. this.page.TriggerInitialEvents();
  422. }
  423. // $.event.trigger({
  424. // type: "submissions-loaded",
  425. // submissions: []
  426. // });
  427. }
  428. Framework.EventTypes = ["submissions-loaded", "comments-loaded"];
  429.  
  430. $(document).ready(function(){
  431. //console.log("test");
  432. var framework = new Framework();
  433. //InitializeAutoPaginate();
  434. //InitializeDetachedInfoBar();
  435. //InitializeLinkEmbedder();
  436. });