InstaSynchP Event Hooks

Attaches itself to the functions on InstaSync to fire events

  1. // ==UserScript==
  2. // @name InstaSynchP Event Hooks
  3. // @namespace InstaSynchP
  4. // @description Attaches itself to the functions on InstaSync to fire events
  5. // @version 1.1.9
  6. // @author Zod-
  7. // @source https://github.com/Zod-/InstaSynchP-Event-Hooks
  8. // @license MIT
  9. // @require https://greasyfork.org/scripts/5647-instasynchp-library/code/code.js?version=41059
  10. // @require https://greasyfork.org/scripts/2857-jquery-bind-first/code/code.js?version=26080
  11. // @include *://instasync.com/r/*
  12. // @include *://*.instasync.com/r/*
  13. // @grant none
  14. // @run-at document-start
  15. // ==/UserScript==
  16.  
  17. function EventHooks() {
  18. 'use strict';
  19. this.version = '1.1.9';
  20. this.name = 'InstaSynchP Event Hooks';
  21. this.resetVariables();
  22. this.isPlaylistLoaded = false;
  23. this.isShuffle = false;
  24. this.hooks = {};
  25. this.hookBinds = {
  26. events: {
  27. connected: 'Connected',
  28. joining: 'Joining',
  29. joined: 'Joined',
  30. reconnecting: 'Reconnecting',
  31. reconnect: 'Reconnect',
  32. disconnect: 'Disconnect'
  33. },
  34. playlist: {
  35. addVideo: 'AddVideo',
  36. removeVideo: 'RemoveVideo',
  37. moveVideo: 'MoveVideo',
  38. load: 'LoadPlaylist',
  39. purge: 'Purge',
  40. },
  41. userlist: {
  42. addUser: 'AddUser',
  43. removeUser: 'RemoveUser',
  44. load: 'LoadUserlist',
  45. renameUser: 'RenameUser',
  46. },
  47. poll: {
  48. create: 'CreatePoll',
  49. addVote: 'AddPollVote',
  50. removeVote: 'RemovePollVote',
  51. end: 'EndPoll',
  52. },
  53. room: {
  54. addMessage: 'AddMessage',
  55. makeLead: 'MakeLeader',
  56. playVideo: 'PlayVideo',
  57. resume: 'Resume',
  58. pause: 'Pause',
  59. seekTo: 'SeekTo',
  60. setSkips: 'Skips',
  61. sendcmd: 'SendCMD',
  62. }
  63. };
  64. }
  65.  
  66. EventHooks.prototype.bindLinkifySuppress = function () {
  67. 'use strict';
  68. var oldLinkify = window.linkify;
  69.  
  70. window.linkify = function (str, buildHashtagUrl, includeW3, target) {
  71. var tags = [];
  72. var index = -1;
  73. //remove image urls so they wont get linkified
  74. str = str.replace(/(src|href)=\"([^\"]*)\"/gi, function ($0, $1, $2) {
  75. tags.push({
  76. 'tagName': $1,
  77. 'url': $2
  78. });
  79. return '{0}=\"\"'.format($1);
  80. });
  81. str = oldLinkify(str, buildHashtagUrl, includeW3, target);
  82. //put them back in
  83. str = str.replace(/(src|href)=\"\"/gi, function () {
  84. index += 1;
  85. return '{0}="{1}"'.format(tags[index].tagName, tags[index].url);
  86. });
  87. return str;
  88. };
  89. };
  90.  
  91. EventHooks.prototype.countUser = function (user, addRemove) {
  92. 'use strict';
  93. var _this = this;
  94. var inc = (typeof addRemove === 'boolean' && addRemove) ? 1 : -1;
  95. if (user.permissions > 0) {
  96. _this.mods += inc;
  97. }
  98. if (user.loggedin) {
  99. _this.blacknames += inc;
  100. } else {
  101. _this.greynames += inc;
  102. }
  103. };
  104.  
  105. EventHooks.prototype.addUser = function (user) {
  106. 'use strict';
  107. this.countUser(user, true);
  108. };
  109.  
  110. EventHooks.prototype.removeUser = function (user) {
  111. 'use strict';
  112. this.countUser(user, false);
  113. };
  114.  
  115. EventHooks.prototype.bindUserCount = function () {
  116. 'use strict';
  117. var _this = this;
  118.  
  119. events.on(_this, 'RemoveUser', function (user) {
  120. _this.removeUser(user);
  121. }, true);
  122.  
  123. events.on(_this, 'AddUser', function (user) {
  124. _this.addUser(user);
  125. }, true);
  126. };
  127.  
  128. EventHooks.prototype.createHook = function (ev) {
  129. 'use strict';
  130. var _this = this;
  131.  
  132. function defaultFunction() {
  133. var instasyncArgs = arguments[0];
  134. var frameworkArgs = arguments[1] || instasyncArgs;
  135. events.fire(ev.name, frameworkArgs, true);
  136. ev.oldFn.apply(undefined, instasyncArgs);
  137. events.fire(ev.name, frameworkArgs, false);
  138. }
  139.  
  140. function arrayFunction() {
  141. var instasyncArgs = arguments[0];
  142. var frameworkArgs = arguments[1] || instasyncArgs;
  143. frameworkArgs = frameworkArgs[0];
  144. frameworkArgs.forEach(function () {
  145. var args = [].slice.call(arguments);
  146. args = args.slice(0, args.length - 2);
  147. events.fire(ev.name, args, true);
  148. });
  149. ev.oldFn.apply(undefined, instasyncArgs);
  150. frameworkArgs.forEach(function () {
  151. var args = [].slice.call(arguments);
  152. args = args.slice(0, args.length - 2);
  153. events.fire(ev.name, args, false);
  154. });
  155. }
  156.  
  157. if (ev.location === 'events') {
  158. window.room.e.on(ev.hook, function () {
  159. events.fire(ev.name, arguments, false);
  160. });
  161. return;
  162. }
  163. //custom hooks
  164. switch (ev.name) {
  165. case 'LoadPlaylist':
  166. return function () {
  167. if (!_this.isPlaylistLoaded) {
  168. defaultFunction.apply(undefined, [arguments]);
  169. _this.isPlaylistLoaded = true;
  170. } else {
  171. events.fire('Shuffle', arguments, true);
  172. ev.oldFn.apply(undefined, arguments);
  173. events.fire('Shuffle', arguments, false);
  174. }
  175. };
  176. case 'AddUser':
  177. return function () {
  178. if (Array.isArray(arguments[0])) {
  179. arrayFunction.apply(undefined, [arguments]);
  180. } else {
  181. defaultFunction.apply(undefined, [arguments]);
  182. }
  183. };
  184. case 'AddVideo':
  185. return function () {
  186. if (_this.isShuffle) {
  187. ev.oldFn.apply(undefined, arguments);
  188. return;
  189. }
  190. if (Array.isArray(arguments[0])) {
  191. arrayFunction.apply(undefined, [arguments]);
  192. } else {
  193. defaultFunction.apply(undefined, [arguments]);
  194. }
  195. };
  196. case 'RemoveUser':
  197. case 'MakeLeader':
  198. case 'RenameUser':
  199. return function () {
  200. var user = findUserId(arguments[0]);
  201. if (ev.name === 'RenameUser') {
  202. user.username = arguments[1];
  203. }
  204. defaultFunction.apply(undefined, [
  205. arguments, [user]
  206. ]);
  207. };
  208. case 'PlayVideo':
  209. case 'RemoveVideo':
  210. case 'MoveVideo':
  211. return function () {
  212. var indexOfVid = window.room.playlist.indexOf(arguments[0]);
  213. var video = window.room.playlist.videos[indexOfVid];
  214. var args = [].slice.call(arguments);
  215. args[0] = video;
  216. if (ev.name === 'MoveVideo') {
  217. args.push(indexOfVid);
  218. }
  219. defaultFunction.apply(undefined, [arguments, args]);
  220. };
  221. case 'Skips':
  222. return function () {
  223. var args = [].slice.call(arguments);
  224. args.push((args[1] / _this.blacknames) * 100); //skip percentage
  225. defaultFunction.apply(undefined, [arguments, args]);
  226. };
  227. }
  228. return function () {
  229. defaultFunction.apply(undefined, [arguments]);
  230. };
  231. };
  232.  
  233. EventHooks.prototype.forEachHookBind = function (callback) {
  234. 'use strict';
  235. var _this = this;
  236. Object.keys(_this.hookBinds).forEach(function (locationName) {
  237. var location = _this.hookBinds[locationName];
  238. Object.keys(location).forEach(function (hookName) {
  239. var hook = location[hookName];
  240. callback.apply(_this, [{
  241. name: locationName,
  242. value: location
  243. }, {
  244. name: hookName,
  245. value: hook
  246. }]);
  247. });
  248. });
  249. };
  250.  
  251. EventHooks.prototype.bindHooks = function () {
  252. 'use strict';
  253. var _this = this;
  254. _this.forEachHookBind(function (locationPair, hookPair) {
  255. var ev = {
  256. hook: hookPair.name,
  257. name: hookPair.value,
  258. location: locationPair.name
  259. };
  260. if (locationPair.name === 'events') {
  261. _this.createHook(ev);
  262. } else if (locationPair.name === 'room') {
  263. ev.oldFn = window.room[ev.hook];
  264. window.room[ev.hook] = _this.createHook(ev);
  265. } else {
  266. if (locationPair.name === 'room') {
  267. ev.context = window.room;
  268. } else {
  269. ev.context = window.room[ev.location];
  270. }
  271. ev.oldFn = ev.context[ev.hook];
  272. ev.context[ev.hook] = _this.createHook(ev);
  273. }
  274. });
  275. };
  276.  
  277. EventHooks.prototype.bindPageMessages = function () {
  278. 'use strict';
  279. window.addEventListener('message', function (event) {
  280. try {
  281. var parsed = JSON.parse(event.data);
  282. if (parsed.action) {
  283. //own events
  284. events.fire(parsed.action, [parsed.data], false);
  285. }
  286. //all
  287. events.fire('PageMessage', [parsed], false);
  288. } catch (ignore) {}
  289. }, false);
  290. };
  291.  
  292. EventHooks.prototype.bindShuffle = function () {
  293. 'use strict';
  294. var _this = this;
  295.  
  296. events.on(_this, 'Shuffle', function () {
  297. _this.isShuffle = true;
  298. }, true);
  299.  
  300. events.on(_this, 'Shuffle', function () {
  301. _this.isShuffle = false;
  302. }, false);
  303. };
  304.  
  305. EventHooks.prototype.bindPlayerDestroy = function () {
  306. 'use strict';
  307. var oldPlayerDestroy = window.room.video.destroy;
  308. window.room.video.destroy = function () {
  309. events.fire('PlayerDestroy', arguments, true);
  310. oldPlayerDestroy.apply(window.room.video, arguments);
  311. events.fire('PlayerDestroy', arguments, false);
  312. };
  313. };
  314.  
  315. EventHooks.prototype.bindKeyInput = function () {
  316. 'use strict';
  317. var csel = '#cin';
  318. $(csel).bindFirst('keypress', function (event) {
  319. events.fire('InputKeypress[{0}]'.format(event.keyCode), [event, $(
  320. csel).val()], false);
  321. events.fire('InputKeypress', [event, $(csel).val()], false);
  322. if (event.keyCode === 13 && $(csel).val() !== '') {
  323. events.fire('SendChat', [$(csel).val()], false);
  324. }
  325. });
  326. $(csel).bindFirst('keydown', function (event) {
  327. //prevent loosing focus on tab
  328. if (event.keyCode === 9) {
  329. event.preventDefault();
  330. }
  331. events.fire('InputKeydown[{0}]'.format(event.keyCode), [event,
  332. $(csel).val()
  333. ], false);
  334. events.fire('InputKeydown', [event, $(csel).val()], false);
  335. });
  336. $(csel).bindFirst('keyup', function (event) {
  337. events.fire('InputKeyup[{0}]'.format(event.keyCode), [event, $(csel).val()],
  338. false);
  339. events.fire('InputKeyup', [event, $(csel).val()], false);
  340. });
  341. };
  342.  
  343. EventHooks.prototype.executeOnceCore = function () {
  344. 'use strict';
  345. var _this = this;
  346. _this.bindLinkifySuppress();
  347. _this.bindUserCount();
  348. _this.bindHooks();
  349. _this.bindPageMessages();
  350. _this.bindShuffle();
  351. };
  352.  
  353. EventHooks.prototype.preConnect = function () {
  354. 'use strict';
  355. var _this = this;
  356. _this.bindPlayerDestroy();
  357. _this.bindKeyInput();
  358. };
  359.  
  360. EventHooks.prototype.resetVariables = function () {
  361. 'use strict';
  362. this.mods = 0;
  363. this.blacknames = 0;
  364. this.greynames = 0;
  365. this.isPlaylistLoaded = false;
  366. };
  367.  
  368. window.plugins = window.plugins || {};
  369. window.plugins.eventHooks = new EventHooks();