Google Forms Unlocker

Stops Google Forms from being locked, consequently letting you do them without a chromebook.

  1. // ==UserScript==
  2. // @name Google Forms Unlocker
  3. // @namespace https://github.com/xNasuni/google-forms-unlocker
  4. // @description Stops Google Forms from being locked, consequently letting you do them without a chromebook.
  5. // @author Mia @ github.com/xNasuni
  6. // @match *://docs.google.com/forms/*
  7. // @grant GM_addStyle
  8. // @version 1.7
  9. // @run-at document-start
  10. // @license GPL-3.0
  11. // @supportURL https://github.com/xNasuni/google-forms-unlocker/issues
  12. // ==/UserScript==
  13.  
  14. const kAssessmentAssistantExtensionId = "gndmhdcefbhlchkhipcnnbkcmicncehk"
  15. const ERROR_USER_AGENT = "_useragenterror"
  16. const ERROR_UNKNOWN = "_unknown"
  17.  
  18. var shouldSpoof = location.hash === "#gfu"
  19.  
  20. // support for browsers other than chrome.
  21. unsafeWindow.chrome = unsafeWindow.chrome || {}
  22. unsafeWindow.chrome.runtime = unsafeWindow.chrome.runtime || {}
  23. unsafeWindow.chrome.runtime.sendMessage = unsafeWindow.chrome.runtime.sendMessage || function(extId, payload, callback){chrome.runtime.lastError = 1; callback()}
  24.  
  25. const oldSendMessage = unsafeWindow.chrome.runtime.sendMessage
  26.  
  27. if (GM_addStyle === undefined) {
  28. // https://stackoverflow.com/questions/23683439/gm-addstyle-equivalent-in-tampermonkey
  29. GM_addStyle = function (css) {
  30. const style = unsafeWindow.document.getElementById("GM_addStyleBy8626") || (function () {
  31. const style = unsafeWindow.document.createElement('style');
  32. style.type = 'text/css';
  33. style.id = "GM_addStyleBy8626";
  34. unsafeWindow.document.head.appendChild(style);
  35. return style;
  36. })();
  37. const sheet = style.sheet;
  38. sheet.insertRule(css, (sheet.rules || sheet.cssRules || []).length);
  39. }
  40. }
  41.  
  42. function ButtonAction() {
  43. location.hash = "gfu"
  44. location.reload()
  45. }
  46.  
  47. function MatchExtensionId(ExtensionId) {
  48. return ExtensionId === kAssessmentAssistantExtensionId
  49. }
  50.  
  51. function GetGoogleForm() {
  52. const Containers = unsafeWindow.document.querySelectorAll("div.RGiwf")
  53. var Form
  54.  
  55. for (const Container of Containers) {
  56. for (const Child of Container.childNodes) {
  57. if (Child.nodeName == "FORM") {
  58. Form = Child
  59. }
  60. }
  61. }
  62.  
  63. return Form
  64. }
  65.  
  66. function GetQuizHeader() {
  67. const QuizHeader = unsafeWindow.document.querySelector("div.mGzJpd")
  68. return QuizHeader
  69. }
  70.  
  71. function PageIsErrored() {
  72. const QuizHeader = GetQuizHeader()
  73. if (QuizHeader === null) { return false }
  74.  
  75. const ChildNodes = QuizHeader.childNodes
  76. if (ChildNodes[3].getAttribute("aria-live") === "assertive" && ChildNodes[4].getAttribute("aria-live") === "assertive") {
  77. return {title: ChildNodes[3].innerText, description: ChildNodes[4].innerText}
  78. }
  79. return false
  80. }
  81.  
  82. function MatchErrorType(error) {
  83. if (error.title === "You can't access this quiz." && error.description === "Locked mode is on. Only respondents using managed Chromebooks can open this quiz. Learn more") {
  84. return ERROR_USER_AGENT
  85. }
  86. return ERROR_UNKNOWN
  87. }
  88.  
  89. function MakeButton(Text, Callback, Color) {
  90. const Form = GetGoogleForm()
  91. if (Form === undefined) { return false }
  92.  
  93. const ButtonHolder = Form.childNodes[2]
  94.  
  95. const Button = unsafeWindow.document.createElement("div")
  96. Button.classList.value = "uArJ5e UQuaGc Y5sE8d TIHcue QvWxOd"
  97. Button.style.marginLeft = "10px"
  98. Button.style.backgroundColor = Color
  99. Button.setAttribute("role", "button")
  100. Button.setAttribute("tabindex", ButtonHolder.childNodes.length)
  101. Button.setAttribute("mia-gfu-state", "custom-button")
  102. ButtonHolder.appendChild(Button)
  103.  
  104. const Glow = unsafeWindow.document.createElement("div")
  105. Glow.classList.value = "Fvio9d MbhUzd"
  106. Glow.style.top = '21px'
  107. Glow.style.left = '9px'
  108. Glow.style.width = '110px'
  109. Glow.style.height = '110px'
  110. Button.appendChild(Glow)
  111.  
  112. const TextContainer = unsafeWindow.document.createElement("span")
  113. TextContainer.classList.value = "l4V7wb Fxmcue"
  114. Button.appendChild(TextContainer)
  115.  
  116. const TextSpan = unsafeWindow.document.createElement("span")
  117. TextSpan.classList.value = "NPEfkd RveJvd snByac"
  118. TextSpan.innerText = Text
  119. TextContainer.appendChild(TextSpan)
  120.  
  121. Button.addEventListener("click", Callback)
  122.  
  123. return {destroy: function(){Button.remove()}}
  124. }
  125.  
  126. async function IsOnChromebook() {
  127. return new Promise((resolve, _reject) => {
  128. oldSendMessage(kAssessmentAssistantExtensionId, {command: "isLocked"}, function(_response) {
  129. if (unsafeWindow.chrome.runtime.lastError) {
  130. resolve(false)
  131. }
  132. resolve(true)
  133. })
  134. })
  135. }
  136.  
  137. async function Initialize() {
  138. GM_addStyle(`
  139. .gfu-red {
  140. font-family: monospace;
  141. text-align: center;
  142. font-size: 11px;
  143. padding-top: 24px;
  144. color: red !important;
  145. }
  146. .EbMsme {
  147. transition: filter cubic-bezier(0.4, 0, 0.2, 1) 0.3s;
  148. filter: blur(8px) !important;
  149. }
  150. .EbMsme:hover {
  151. filter: blur(0px) !important;
  152. }
  153. `)
  154.  
  155. const Errored = PageIsErrored()
  156. if (Errored !== false) {
  157. switch (MatchErrorType(Errored)) {
  158. case ERROR_USER_AGENT:
  159. const QuizHeader = GetQuizHeader()
  160. const Error = unsafeWindow.document.createElement("div")
  161. Error.classList.value = "gfu-red"
  162. QuizHeader.appendChild(Error)
  163.  
  164. const ErrorSpan = unsafeWindow.document.createElement("span")
  165. ErrorSpan.innerText = "Google Forms Unlocker - In order to continue, you need a User Agent Spoofer. "
  166. Error.appendChild(ErrorSpan)
  167.  
  168. const AnchorSpan = unsafeWindow.document.createElement("a")
  169. AnchorSpan.classList.value = "gfu-red"
  170. AnchorSpan.innerText = "Install one here."
  171. AnchorSpan.target = "_blank"
  172. AnchorSpan.rel = "noopener"
  173. AnchorSpan.href = "https://github.com/xNasuni/google-forms-unlocker/blob/main/README.md#spoofing-your-user-agent"
  174. ErrorSpan.appendChild(AnchorSpan)
  175. break
  176. default:
  177. alert(`Unhandled error type: ${JSON.stringify(Errored)}`)
  178. }
  179. return
  180. }
  181.  
  182. const Form = GetGoogleForm()
  183. if (Form === undefined) { return false }
  184.  
  185. const IsRealManagedChromebook = await IsOnChromebook()
  186.  
  187. if (IsRealManagedChromebook === false) {
  188. const ButtonHolder = Form.childNodes[2]
  189. for (const Button of ButtonHolder.childNodes) {
  190. if (Button.getAttribute("mia-gfu-state") === "custom-button") { continue }
  191. Button.style.backgroundColor = "#ccc"
  192. Button.setAttribute("jsaction", "")
  193. }
  194. }
  195. MakeButton("Bypass", ButtonAction, "#ff90bf")
  196. }
  197.  
  198. var fakeIsLocked = shouldSpoof
  199. function InterceptCommand(Payload, Callback) {
  200. switch (Payload.command) {
  201. case "isLocked":
  202. Callback({locked: fakeIsLocked})
  203. return true
  204. case "lock":
  205. if (shouldSpoof) {
  206. return false
  207. }
  208. fakeIsLocked = false
  209. Callback({locked: fakeIsLocked})
  210. return true
  211. case "unlock":
  212. fakeIsLocked = false
  213. Callback({locked: fakeIsLocked})
  214. return true
  215. }
  216.  
  217. return false
  218. }
  219.  
  220. setInterval(() => {
  221. unsafeWindow.chrome.runtime.sendMessage = function() {
  222. const ExtensionId = (arguments)[0]
  223. const Payload = (arguments)[1]
  224. const Callback = (arguments)[2]
  225.  
  226. if (MatchExtensionId(ExtensionId)) {
  227. const Intercepted = InterceptCommand(Payload, Callback)
  228. if (Intercepted) { return null }
  229. }
  230. console.warn("Not intercepting", ExtensionId, Payload, Callback)
  231.  
  232. return oldSendMessage(ExtensionId, Payload, function() {
  233. if (unsafeWindow.chrome.runtime.lastError) {
  234. alert(`Google Forms Unlocker, please report this to the GitHub https://github.com/xNasuni/google-forms-unlocker/issues\nUnhandled error: ${JSON.stringify(chrome.runtime.lastError)}`)
  235. return
  236. }
  237. Callback.apply(this, arguments)
  238. })
  239. }
  240. })
  241.  
  242. unsafeWindow.document.addEventListener("DOMContentLoaded", () => {
  243. unsafeWindow.console.log("Initialized")
  244. Initialize()
  245. })
  246.  
  247. Object.defineProperty(unsafeWindow.document, 'hidden', {
  248. value: false,
  249. writable: false
  250. })
  251. Object.defineProperty(unsafeWindow.document, 'visibilityState', {
  252. value: "visible",
  253. writable: false
  254. })
  255. Object.defineProperty(unsafeWindow.document, 'webkitVisibilityState', {
  256. value: "visible",
  257. writable: false
  258. })
  259. Object.defineProperty(unsafeWindow.document, 'mozVisibilityState', {
  260. value: "visible",
  261. writable: false
  262. })
  263. Object.defineProperty(unsafeWindow.document, 'msVisibilityState', {
  264. value: "visible",
  265. writable: false
  266. })
  267. const BlacklistedEvents = ['mozvisibilitychange', 'webkitvisibilitychange', 'msvisibilitychange', 'visibilitychange']
  268. const oldAddEventListener = unsafeWindow.document.addEventListener;
  269. unsafeWindow.document.addEventListener = function() {
  270. const EventType = (arguments)[0]
  271. const Method = (arguments)[1]
  272. const Options = (arguments)[2]
  273.  
  274. if (BlacklistedEvents.indexOf(EventType) !== -1) {
  275. console.log(`type ${EventType} blocked from being registered with`, Method)
  276. return
  277. }
  278.  
  279. return oldAddEventListener.apply(this, arguments)
  280. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址