InstaSynchP Core

The core for a modular plugin system for InstaSync

  1. // ==UserScript==
  2. // @name InstaSynchP Core
  3. // @namespace InstaSynchP
  4. // @description The core for a modular plugin system for InstaSync
  5. // @version 1.4.9
  6. // @author Zod-
  7. // @source https://github.com/Zod-/InstaSynchP-Core
  8. // @license MIT
  9. // @require https://greasyfork.org/scripts/2859-video-url-parser/code/code.js?version=30624
  10. // @require https://greasyfork.org/scripts/2857-jquery-bind-first/code/code.js?version=26080
  11. // @require https://greasyfork.org/scripts/8159-log4javascript/code/code.js?version=37575
  12. // @require https://greasyfork.org/scripts/5647-instasynchp-library/code/code.js?version=51269
  13. // @require https://greasyfork.org/scripts/8177-instasynchp-logger/code/code.js?version=51298
  14. // @require https://greasyfork.org/scripts/5718-instasynchp-cssloader/code/code.js?version=43457
  15. // @require https://greasyfork.org/scripts/5719-instasynchp-settings/code/code.js?version=51309
  16. // @require https://greasyfork.org/scripts/6332-instasynchp-commands/code/code.js?version=49212
  17. // @require https://greasyfork.org/scripts/5651-instasynchp-event-hooks/code/code.js?version=52057
  18. // @include *://instasync.com/r/*
  19. // @include *://*.instasync.com/r/*
  20. // @grant none
  21. // @run-at document-end
  22. // ==/UserScript==
  23.  
  24. function Events() {
  25. 'use strict';
  26. this.listeners = {};
  27. }
  28.  
  29. Events.prototype.createListener = function (name) {
  30. 'use strict';
  31. this.listeners[name] = {
  32. 'preOld': [],
  33. 'postOld': []
  34. };
  35. };
  36.  
  37. Events.prototype.createListenerIfNotExists = function (name) {
  38. 'use strict';
  39. var _this = this;
  40. if (isUdef(_this.listeners[name])) {
  41. _this.createListener(name);
  42. }
  43. };
  44.  
  45. Events.prototype.log = function (opts) {
  46. 'use strict';
  47. var args = [];
  48. opts.type = opts.type || 'debug';
  49. args.push('Events');
  50. args.push(opts.event);
  51. if (opts.eventName || opts.eventNames) {
  52. args.push(opts.eventName || opts.eventNames);
  53. }
  54. if (opts.callback) {
  55. args.push(opts.callback.name);
  56. }
  57. if (!isUdef(opts.preOld)) {
  58. args.push('preOld');
  59. args.push(opts.preOld);
  60. }
  61. if (opts.ref) {
  62. args.push(opts.ref.name);
  63. }
  64. if (opts.parameters) {
  65. try {
  66. args.push(JSON.stringify(opts.parameters));
  67. } catch (ignore) {
  68. args.push(opts.parameters);
  69. }
  70. }
  71. if (opts.err) {
  72. args.push(opts.err.message);
  73. args.push(opts.err.stack);
  74. args.push(opts.err);
  75. }
  76. logger()[opts.type].apply(logger(), args);
  77. };
  78.  
  79. Events.prototype.pushListener = function (ref, eventName, callback, preOld) {
  80. 'use strict';
  81. var _this = this;
  82. var prepost = preOld ? 'preOld' : 'postOld';
  83. _this.createListenerIfNotExists(eventName);
  84. _this.listeners[eventName][prepost].push({
  85. ref: ref,
  86. callback: callback
  87. });
  88. };
  89.  
  90.  
  91. Events.prototype.on = function (ref, eventNames, callback, preOld) {
  92. 'use strict';
  93. if (isUdef(callback)) {
  94. return;
  95. }
  96. var _this = this;
  97. preOld = preOld || false;
  98. _this.log({
  99. event: 'on',
  100. callback: callback,
  101. ref: ref,
  102. eventNames: eventNames,
  103. preOld: preOld
  104. });
  105.  
  106. eventNames.split(/\s*,\s*/).forEach(function (eventName) {
  107. eventName = eventName.trim();
  108. _this.pushListener(ref, eventName, callback, preOld);
  109. });
  110. };
  111.  
  112. Events.prototype.once = function (ref, eventNames, callback, preOld) {
  113. 'use strict';
  114. this.unbind(eventNames, callback);
  115. this.on(ref, eventNames, callback, preOld);
  116. };
  117.  
  118. Events.prototype.removeListener = function (eventName, callback, prepost) {
  119. 'use strict';
  120. var _this = this;
  121. if (isUdef(_this.listeners[eventName])) {
  122. return;
  123. }
  124. var listeners = _this.listeners[eventName][prepost];
  125. for (var i = 0; i < listeners.length; i++) {
  126. if (listeners[i].callback === callback) {
  127. listeners.splice(i, 1);
  128. i -= 1;
  129. }
  130. }
  131. };
  132.  
  133. Events.prototype.removePreOldListener = function (eventName, callback) {
  134. 'use strict';
  135. this.removeListener(eventName, callback, 'preOld');
  136. };
  137.  
  138. Events.prototype.removePostOldListener = function (eventName, callback) {
  139. 'use strict';
  140. this.removeListener(eventName, callback, 'postOld');
  141. };
  142.  
  143. Events.prototype.unbind = function (eventNames, callback) {
  144. 'use strict';
  145. var _this = this;
  146. if (isUdef(callback)) {
  147. return;
  148. }
  149.  
  150. _this.log({
  151. event: 'unbind',
  152. callback: callback,
  153. eventNames: eventNames
  154. });
  155.  
  156. eventNames.split(/\s*,\s*/).forEach(function (eventName) {
  157. _this.removePreOldListener(eventName, callback);
  158. _this.removePostOldListener(eventName, callback);
  159. });
  160. };
  161.  
  162. Events.prototype.copyPreOldListeners = function (eventName) {
  163. 'use strict';
  164. return [].concat(this.listeners[eventName].preOld);
  165. };
  166.  
  167. Events.prototype.copyPostOldListeners = function (eventName) {
  168. 'use strict';
  169. return [].concat(this.listeners[eventName].postOld);
  170. };
  171.  
  172. Events.prototype.copyListeners = function (eventName, preOldFlag) {
  173. 'use strict';
  174. if (preOldFlag) {
  175. return this.copyPreOldListeners(eventName);
  176. } else {
  177. return this.copyPostOldListeners(eventName);
  178. }
  179. };
  180.  
  181. Events.prototype.fire = function (eventName, parameters, preOld) {
  182. 'use strict';
  183. var _this = this;
  184. preOld = preOld || false;
  185.  
  186. //Don't record every single keystroke
  187. //and PageMessages from youtube and others
  188. if (eventName !== 'PageMessage' && !eventName.startsWith('Input')) {
  189. _this.log({
  190. event: 'fire',
  191. eventName: eventName,
  192. preOld: preOld,
  193. parameters: parameters
  194. });
  195. }
  196.  
  197. if (isUdef(_this.listeners[eventName])) {
  198. return;
  199. }
  200.  
  201. //make a copy of the listeners since some handlers
  202. //could remove listeners, changing the array while iterating over it
  203. _this.copyListeners(eventName, preOld).forEach(function (listener) {
  204. try {
  205. listener.callback.apply(listener.ref, parameters);
  206. } catch (err) {
  207. _this.log({
  208. callback: listener.callback,
  209. ref: listener.ref,
  210. eventName: eventName,
  211. type: 'error',
  212. event: 'fire',
  213. err: err
  214. });
  215. }
  216. });
  217. };
  218.  
  219. function Core() {
  220. 'use strict';
  221. this.version = '1.4.9';
  222. this.name = 'InstaSynchP Core';
  223. this.connected = false;
  224. this.Events = Events;
  225. this.styles = [{
  226. name: 'core',
  227. url: 'https://cdn.rawgit.com/Zod-/InstaSynchP-Core/1eabab1beefc635d7a59e5bad3176de063da3331/dist/core.css',
  228. autoload: true
  229. }];
  230. this.isMainLoaded = false;
  231. }
  232.  
  233. Core.prototype.executeOnceCore = function () {
  234. 'use strict';
  235. window.events = new this.Events();
  236. };
  237.  
  238. Core.prototype.resetVariables = function () {
  239. 'use strict';
  240. this.connected = false;
  241. window.room.user.userinfo = undefined;
  242. };
  243.  
  244. Core.prototype.prepareFramework = function () {
  245. 'use strict';
  246. var _this = this;
  247. _this.executeOnceCore();
  248. plugins.logger.executeOnceCore();
  249. _this.log({
  250. event: 'Prepare Framework'
  251. });
  252. plugins.commands.executeOnceCore();
  253. plugins.settings.executeOnceCore();
  254. };
  255.  
  256. Core.prototype.finishUpFramework = function () {
  257. 'use strict';
  258. var _this = this;
  259. _this.log({
  260. event: 'Finish up Framework'
  261. });
  262. events.on(plugins.eventHooks, 'ExecuteOnce',
  263. plugins.eventHooks.executeOnceCore);
  264. events.on(_this, 'LoadUserlist', _this.onConnect);
  265. };
  266.  
  267. Core.prototype.log = function (opts) {
  268. 'use strict';
  269. var args = [];
  270. opts.type = opts.type || 'debug';
  271. args.push(this.name);
  272. args.push(opts.event);
  273. if (opts.plugin) {
  274. args.push(opts.plugin.name);
  275. args.push(opts.plugin.version);
  276. }
  277. logger()[opts.type].apply(logger(), args);
  278. };
  279.  
  280. Core.prototype.preparePlugin = function (plugin) {
  281. 'use strict';
  282. var _this = this;
  283. if (!plugin.enabled) {
  284. _this.log({
  285. event: 'Skipping disabled plugin',
  286. type: 'info',
  287. plugin: plugin
  288. });
  289. return;
  290. }
  291.  
  292. _this.log({
  293. event: 'Found plugin',
  294. type: 'info',
  295. plugin: plugin
  296. });
  297.  
  298. events.on(plugin, 'PreConnect', plugin.preConnect);
  299. events.on(plugin, 'PostConnect', plugin.postConnect);
  300. events.on(plugin, 'ExecuteOnce', plugin.executeOnce);
  301. events.on(plugin, 'ResetVariables', plugin.resetVariables);
  302.  
  303. commands.bind(plugin.commands);
  304. };
  305.  
  306. Core.prototype.preparePlugins = function () {
  307. 'use strict';
  308. var _this = this;
  309. _this.log({
  310. event: 'Prepare plugins'
  311. });
  312. for (var pluginName in plugins) {
  313. if (plugins.hasOwnProperty(pluginName)) {
  314. _this.preparePlugin(plugins[pluginName]);
  315. }
  316. }
  317. };
  318.  
  319. Core.prototype.slowLoadingCompensate = function () {
  320. 'use strict';
  321. this.connected = true;
  322. events.fire('Connected');
  323. events.fire('Joining');
  324. events.fire('Joined');
  325. window.room.playlist.load(window.room.playlist.videos);
  326. window.room.userlist.load(window.room.userlist.users);
  327. reloadPlayer();
  328. };
  329.  
  330. Core.prototype.fireConnect = function () {
  331. 'use strict';
  332. var _this = this;
  333. events.fire('PreConnect');
  334. if (thisUser()) {
  335. _this.log({
  336. event: 'Script was loading slowly'
  337. });
  338. _this.slowLoadingCompensate();
  339. }
  340. };
  341.  
  342. Core.prototype.fireExecuteOnce = function () {
  343. 'use strict';
  344. this.isMainLoaded = true;
  345. events.fire('ExecuteOnce');
  346. };
  347.  
  348. Core.prototype.onConnect = function () {
  349. 'use strict';
  350. this.connected = true;
  351. events.fire('PostConnect');
  352. };
  353.  
  354. Core.prototype.executeOnce = function () {
  355. 'use strict';
  356. logger().info(this.name, navigator.userAgent);
  357. };
  358.  
  359. Core.prototype.start = function () {
  360. 'use strict';
  361. var _this = this;
  362. _this.log({
  363. event: 'Start'
  364. });
  365. _this.fireExecuteOnce();
  366. _this.fireConnect();
  367. events.on(_this, 'PreConnect, Disconnect', function fireResetVariables() {
  368. events.fire('ResetVariables');
  369. });
  370. };
  371.  
  372. Core.prototype.main = function () {
  373. 'use strict';
  374. var _this = this;
  375.  
  376. _this.prepareFramework();
  377. _this.preparePlugins();
  378. _this.finishUpFramework();
  379.  
  380. _this.start();
  381. };
  382.  
  383. window.plugins = window.plugins || {};
  384. window.plugins.core = new Core();
  385. if (window.document.readyState === 'complete') {
  386. window.plugins.core.main();
  387. window.plugins.core.log({
  388. event: 'Page was already loaded'
  389. });
  390. } else {
  391. window.addEventListener('load', function () {
  392. 'use strict';
  393. if (!window.plugins.core.isMainLoaded) {
  394. window.plugins.core.main();
  395. }
  396. window.plugins.core.log({
  397. event: 'Page load'
  398. });
  399. }, false);
  400. }