Chaturbate Thumbnails Zoom

Make thumbnails zoom on mouse hover

  1. // ==UserScript==
  2. // @name Chaturbate Thumbnails Zoom
  3. // @author nima-r
  4. // @namespace https://greasyfork.org/en/users/846327-nima-rahbar
  5. // @icon https://www.google.com/s2/favicons?sz=64&domain=chaturbate.org
  6. // @description Make thumbnails zoom on mouse hover
  7. // @copyright 2022, nima-r (https://greasyfork.org/en/users/846327-nima-rahbar)
  8. // @license MIT
  9. // @version 1.2.6
  10. // @homepageURL https://greasyfork.org/en/scripts/437111-chaturbate-animate-thumbnail-re-layout
  11. // @homepage https://greasyfork.org/en/scripts/437111-chaturbate-animate-thumbnail-re-layout
  12. // @supportURL https://greasyfork.org/en/scripts/437111-chaturbate-animate-thumbnail-re-layout/feedback
  13. // @require https://cdn.jsdelivr.net/npm/jquery@3.6.0
  14. // @require https://cdn.jsdelivr.net/npm/@violentmonkey/dom@1.0.9
  15. // @match *://*.chaturbate.com/*
  16. // @run-at document-idle
  17. // ==/UserScript==
  18.  
  19. /* global $, VM */
  20.  
  21. // Immediately Invoked Function Expression (IIFE) to avoid polluting the global scope
  22. (() => {
  23. // Create a <style> element to hold custom CSS rules
  24. const style = document.createElement('style');
  25.  
  26. // Define your CSS rules for the enlargement button
  27. const css = `
  28. .enlargementBtn {
  29. display: inline-block;
  30. width: 16px;
  31. height: 16px;
  32. background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE5LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPg0KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJDYXBhXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgNDIwLjggNDIwLjgiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyMC44IDQyMC44OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8Zz4NCgk8cGF0aCBmaWxsPSIjRkZGRkZGIiBkPSJNNDAyLjQ1LDBoLTczLjNjLTkuOCwwLTE3LjksOC0xNy45LDE3LjljMCw5LjgsOCwxNy45LDE3LjksMTcuOWgzMi4xbC04OC44LDg4bC00OS42LTQ5LjZjLTcuMS02LjMtMTguOC02LjMtMjUsMA0KCQlMNzMuNjUsMTk4LjNjLTcuMSw3LjEtNy4xLDE3LjksMCwyNWw0OS42LDQ5LjZMMzYuMTUsMzYwdi0zMC40YzAtOS44LTgtMTcuOS0xNy45LTE3LjljLTkuOCwwLTE3LjksOC0xNy45LDE3Ljl2NzMuMw0KCQljMCw5LjgsOCwxNy45LDE3LjksMTcuOWg3My4zYzguOSwwLDE3LjktOCwxNy45LTE3LjljMC05LjgtOC0xNy45LTE3LjktMTcuOWgtMzEuMmw4Ny45LTg3LjFsNDkuNiw0OS42YzcuNiw3LjYsMTguNCw2LjYsMjUsMA0KCQlsMTI0LjItMTI0LjFjNi45LTYuOSw3LjYtMTcuNSwwLTI1bC00OS41LTQ5LjVsODcuMS04Ni40djI4LjdjMCw5LjgsOCwxNy45LDE3LjksMTcuOWM5LjgsMCwxNy45LTgsMTcuOS0xNy45VjE3LjkNCgkJQzQyMC4yNSw4LjEsNDEyLjI1LDAsNDAyLjQ1LDB6IE0yMTAuMzUsMzEwbC05OS4yLTk5LjJsOTkuMi05OS4ybDk5LjIsOTkuMkwyMTAuMzUsMzEweiIvPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPC9zdmc+DQo=")
  33. }
  34.  
  35. .enlargementBtn.off {
  36. background-image: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pg0KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE5LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPg0KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJDYXBhXzEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KICAgICB2aWV3Qm94PSIwIDAgNDIwLjggNDIwLjgiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQyMC44IDQyMC44OyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8Zz4NCiAgICA8cGF0aCBmaWxsPSIjRkZGRkZGIiBkPSJNNDAyLjQ1LDBoLTczLjNjLTkuOCwwLTE3LjksOC0xNy45LDE3LjljMCw5LjgsOCwxNy45LDE3LjksMTcuOWgzMi4xbC04OC44LDg4bC00OS42LTQ5LjZjLTcuMS02LjMtMTguOC02LjMtMjUsMA0KICAgICAgICBMNzMuNjUsMTk4LjNjLTcuMSw3LjEtNy4xLDE3LjksMCwyNWw0OS42LDQ5LjZMMzYuMTUsMzYwdi0zMC40YzAtOS44LTgtMTcuOS0xNy45LTE3LjljLTkuOCwwLTE3LjksOC0xNy45LDE3Ljl2NzMuMw0KICAgICAgICBjMCw5LjgsOCwxNy45LDE3LjksMTcuOWg3My4zYzguOSwwLDE3LjktOCwxNy45LTE3LjljMC05LjgtOC0xNy45LTE3LjktMTcuOWgtMzEuMmw4Ny45LTg3LjFsNDkuNiw0OS42YzcuNiw3LjYsMTguNCw2LjYsMjUsMA0KICAgICAgICBsMTI0LjItMTI0LjFjNi45LTYuOSw3LjYtMTcuNSwwLTI1bC00OS41LTQ5LjVsODcuMS04Ni40djI4LjdjMCw5LjgsOCwxNy45LDE3LjksMTcuOWM5LjgsMCwxNy45LTgsMTcuOS0xNy45VjE3LjkNCiAgICAgICAgQzQyMC4yNSw4LjEsNDEyLjI1LDAsNDAyLjQ1LDB6IE0yMTAuMzUsMzEwbC05OS4yLTk5LjJsOTkuMi05OS4ybDk5LjIsOTkuMkwyMTAuMzUsMzEweiIvPg0KICAgIDxsaW5lIHgxPSIwIiB5MT0iMCIgeDI9IjQyMC44IiB5Mj0iNDIwLjgiIHN0cm9rZT0iI0ZGMDAwMCIgc3Ryb2tlLXdpZHRoPSI1MCIvPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPGc+DQo8L2c+DQo8Zz4NCjwvZz4NCjxnPg0KPC9nPg0KPC9zdmc+DQo=")
  37. }
  38. `;
  39.  
  40. // Set the innerHTML of the <style> element to the defined CSS rules
  41. style.innerHTML = css;
  42.  
  43. // Append the <style> element to the <head> of the document to apply the styles
  44. document.head.appendChild(style);
  45.  
  46. // Retrieve the enlargement state from localStorage or set it to "on" if not found
  47. var enlargement = localStorage.getItem('enlargement') || localStorage.setItem('enlargement', "on");
  48.  
  49. // When the document is ready
  50. $(document).ready(function () {
  51. // Add the enlargement button to the navigation bar after the "broadcast yourself" link
  52. $("#header #nav .broadcast-yourself").after(
  53. // Create the button with the current enlargement state
  54. `<li><a href="#" class="enlargementBtn ${enlargement}"></a></li>`
  55. );
  56. });
  57.  
  58. // Event handler for click events on the enlargement button
  59. $("body").on("click", ".enlargementBtn", function (e) {
  60. e.preventDefault(); // Prevent the default action of the link
  61. // Toggle the enlargement state
  62. if (enlargement == "on") {
  63. enlargement = "off"; // Set to 'off'
  64. $(".enlargementBtn").addClass("off"); // Add 'off' class to the button
  65. } else {
  66. enlargement = "on"; // Set to 'on'
  67. $(".enlargementBtn").removeClass("off"); // Remove 'off' class from the button
  68. }
  69. // Save the new enlargement state to localStorage
  70. localStorage.setItem('enlargement', enlargement);
  71. location.reload(); // Reload the page to apply changes
  72. });
  73.  
  74. // Observe changes in the document body
  75. VM.observe(document.body, () => {
  76. // Check if enlargement is enabled
  77. if (enlargement == "on") {
  78. // Select all room cards in the endless page template
  79. const rooms = $(".list.endless_page_template .roomCard");
  80.  
  81. // If there are room cards present
  82. if (rooms.length > 0) {
  83. // Apply a transition effect to room cards for smooth scaling
  84. $(".list.endless_page_template .roomCard").css("transition", "transform .1s ease-in-out");
  85. // Disable text selection on iPad for better user experience
  86. $(".isIpad .list.endless_page_template *")
  87. .css("user-select", "none")
  88. .css("-webkit-touch-callout", "none");
  89.  
  90. // Iterate over each room card
  91. $(rooms).each((index, element) => {
  92. // Get the room name from the data attribute or username
  93. const name = $(element).find("> a").data("room")
  94. ? $(element).find("> a").data("room")
  95. : $(element).find("> .user-info > .username > a").text().replace(/^\s/g, "");
  96. const thumbnail = $(element).find("> a img"); // Get the thumbnail image
  97.  
  98. // Bind pointer events to the room card element
  99. $(element)
  100. .bind("pointerdown", (event) => {
  101. // Release pointer capture on pointer down
  102. element.releasePointerCapture(event.pointerId);
  103. })
  104. .bind("pointerenter", (event) => {
  105. // Handle pointer enter event to scale the room card
  106. var firstQ = $("body .list.endless_page_template").innerWidth() / 5, // Calculate first quadrant
  107. lastQ = firstQ * 4, // Calculate last quadrant
  108. origin = "center center", // Default transform origin
  109. originX = "center", // Default horizontal origin
  110. originY = "center"; // Default vertical origin
  111.  
  112. // Determine the horizontal origin based on pointer position
  113. if (event.pageX < firstQ) {
  114. originX = "left"; // Set to left if in the first quadrant
  115. } else if (event.pageX > lastQ) {
  116. originX = "right"; // Set to right if in the last quadrant
  117. }
  118. if (event.pageY < $(document).innerHeight() / 4) {
  119. originY = "top"; // Set to top if in the upper quarter
  120. } else if (event.pageY > $(document).innerHeight() / 4) {
  121. originY = "bottom"; // Set to bottom if in the lower quarter
  122. }
  123. origin = originX + " " + originY; // Combine horizontal and vertical origins
  124.  
  125. if ($(element).parent(".list.endless_page_template").length > 0) {
  126. // Scale only on room list
  127. $(element)
  128. .css("transform-origin", origin) // Set the transform origin
  129. .css("transform", "translateX(0px) scale(1.5)") // Scale the room card
  130. .css("z-index", "999"); // Bring the room card to the front
  131. }
  132. })
  133. .bind("pointerup pointerleave", (event) => {
  134. // Reset the scaling when pointer is released or leaves the element
  135. if ($(element).parent(".list.endless_page_template").length > 0) {
  136. // Scale only on room list
  137. $(element)
  138. .css("transform-origin", "center center") // Reset transform origin
  139. .css("transform", "translateX(0px) scale(1)") // Reset scale to normal
  140. .css("z-index", "0"); // Reset z-index
  141. }
  142. });
  143. });
  144. return false; // Prevent default behavior
  145. }
  146. }
  147. });
  148. })();