InstaSynchP Core

The core for a modular plugin system for InstaSynch

当前为 2015-02-28 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name InstaSynchP Core
  3. // @namespace InstaSynchP
  4. // @description The core for a modular plugin system for InstaSynch
  5.  
  6. // @version 1.3.9
  7. // @author Zod-
  8. // @source https://github.com/Zod-/InstaSynchP-Core
  9. // @license MIT
  10.  
  11. // @include *://instasync.com/r/*
  12. // @include *://*.instasync.com/r/*
  13. // @grant none
  14. // @run-at document-end
  15.  
  16. // @require https://greasyfork.org/scripts/2859-video-url-parser/code/Video%20Url%20Parser.js?version=30624
  17. // @require https://greasyfork.org/scripts/2855-gm-config/code/GM_config.js?version=33973
  18. // @require https://greasyfork.org/scripts/2857-jquery-bind-first/code/jquerybind-first.js?version=26080
  19. // @require https://greasyfork.org/scripts/8159-log4javascript/code/log4javascript.js?version=37575
  20.  
  21. // @require https://greasyfork.org/scripts/5647-instasynchp-library/code/code.js?version=37716
  22. // @require https://greasyfork.org/scripts/8177-instasynchp-logger/code/code.js?version=37872
  23. // @require https://greasyfork.org/scripts/6573-instasynchp-plugin-manager/code/code.js?version=37974
  24. // @require https://greasyfork.org/scripts/5718-instasynchp-cssloader/code/code.js?version=37736
  25. // @require https://greasyfork.org/scripts/5719-instasynchp-settings/code/code.js?version=37737
  26. // @require https://greasyfork.org/scripts/6332-instasynchp-commands/code/code.js?version=37738
  27. // @require https://greasyfork.org/scripts/5651-instasynchp-event-hooks/code/code.js?version=38337
  28. // ==/UserScript==
  29.  
  30. function Core(version) {
  31. "use strict";
  32. this.version = version;
  33. this.name = 'InstaSynchP Core';
  34. this.listeners = {};
  35. this.connected = false;
  36. }
  37.  
  38. Core.prototype.executeOnceCore = function () {
  39. "use strict";
  40. var th = this;
  41. window.events = (function () {
  42. return {
  43. //bind event handlers
  44. 'on': function (ref, eventNames, callback, preOld) {
  45. if (typeof callback === 'undefined') {
  46. return;
  47. }
  48. logger().debug(th.name, "On", eventNames, !isUdef(callback) ? callback.name : undefined,
  49. "preOld", preOld, !isUdef(ref) ? ref.name : undefined);
  50.  
  51. var arr = eventNames.split(','),
  52. eventName,
  53. i;
  54. for (i = 0; i < arr.length; i += 1) {
  55. eventName = arr[i].trim();
  56. if (typeof th.listeners[eventName] === 'undefined') {
  57. th.listeners[eventName] = {
  58. 'preOld': [],
  59. 'postOld': []
  60. };
  61. }
  62. //execute it before are after the overwritten function
  63. if (preOld) {
  64. th.listeners[eventName].preOld.push({
  65. ref: ref,
  66. callback: callback
  67. });
  68. } else {
  69. th.listeners[eventName].postOld.push({
  70. ref: ref,
  71. callback: callback
  72. });
  73. }
  74. }
  75. },
  76. //bind event handler and remove any previous once
  77. 'once': function (ref, eventNames, callback, preOld) {
  78. this.unbind(eventNames, callback);
  79. this.on(ref, eventNames, callback, preOld);
  80. },
  81. //unbind event handlers
  82. 'unbind': function (eventNames, callback) {
  83. if (isUdef(callback)) {
  84. return;
  85. }
  86. logger().debug(th.name, "Unbind", eventNames, callback.name);
  87. var arr = eventNames.split(','),
  88. i, temp;
  89.  
  90. //search all occurences of callback and remove it
  91. function removeCallback(eventName, old) {
  92. if (typeof th.listeners[eventName] === 'undefined') {
  93. return;
  94. }
  95. for (var j = 0; j < th.listeners[eventName][old].length; j += 1) {
  96. if (th.listeners[eventName][old][j].callback === callback) {
  97. th.listeners[eventName][old].splice(j, 1);
  98. j -= 1;
  99. }
  100. }
  101. }
  102. for (i = 0; i < arr.length; i += 1) {
  103. temp = arr[i].trim();
  104. removeCallback(temp, 'preOld');
  105. removeCallback(temp, 'postOld');
  106. }
  107. },
  108. //fire the event with the given parameters
  109. 'fire': function (eventName, parameters, preOld) {
  110. var i,
  111. listenersCopy;
  112.  
  113. if (eventName !== 'PageMessage' && !eventName.startsWith('Input')) {
  114. try {
  115. logger().debug(th.name, "Fire", eventName, "preOld", preOld, JSON.stringify(parameters));
  116. } catch (ignore) {
  117. logger().debug(th.name, "Fire", eventName, "preOld", preOld, parameters);
  118. }
  119. }
  120.  
  121. if (typeof th.listeners[eventName] === 'undefined') {
  122. return;
  123. }
  124.  
  125. //make a copy of the listener list since some handlers
  126. //could remove listeners changing the length of the array
  127. //while iterating over it
  128. if (preOld) {
  129. listenersCopy = [].concat(th.listeners[eventName].preOld);
  130. } else {
  131. listenersCopy = [].concat(th.listeners[eventName].postOld);
  132. }
  133. //fire the events and catch possible errors
  134. for (i = 0; i < listenersCopy.length; i += 1) {
  135. try {
  136. listenersCopy[i].callback.apply(listenersCopy[i].ref, parameters);
  137. } catch (err) {
  138. logger().error(th.name, eventName, err.message,
  139. listenersCopy[i].callback.name,
  140. listenersCopy[i].ref ? listenersCopy[i].ref.name : undefined,
  141. err.stack
  142. );
  143. }
  144. }
  145. }
  146. };
  147. }());
  148.  
  149. //create Plugins button
  150. var clone = $('#user_dropdown').clone();
  151. clone.attr('id', 'plugin_dropdown');
  152. $('a', clone).attr('href', '#').attr('onClick', '');
  153. $('.fa-user', clone).toggleClass('fa-user').toggleClass('fa-plug').before(
  154. $('#tabs_chat > a > span').clone().css('margin-right', '2px').toggleClass('updates')
  155. .css('animation', 'unread-msg-count 3s infinite')
  156. .css('-webkit-animation', 'unread-msg-count 3s infinite')
  157. .css('-moz-animation', 'unread-msg-count 3s infinite')
  158. .css('border-radius', '4px')
  159. );
  160. $('#my_room_link', clone).parent().remove();
  161. $('#logged_in_as', clone).attr('id', 'plugins_settings_title').text('Plugins');
  162. $('.dropdown-menu > li:first-child > a', clone).attr('id', 'plugin_settings');
  163. $('#logout', clone).attr('id', 'plugin_manager').text('').append(
  164. $('#tabs_chat > a > span').clone().css('margin-right', '3px').toggleClass('updates')
  165. .css('animation', 'unread-msg-count 3s infinite')
  166. .css('-webkit-animation', 'unread-msg-count 3s infinite')
  167. .css('-moz-animation', 'unread-msg-count 3s infinite')
  168. .css('border-radius', '4px')
  169. ).append(
  170. $('<i>', {
  171. 'class': 'fa fa-database'
  172. })
  173. ).append(' Manager');
  174. $('.fa-cog', clone).toggleClass('fa-cogs').toggleClass('fa-cog');
  175. $('#user_dropdown').before(clone);
  176. };
  177.  
  178. Core.prototype.postConnect = function () {
  179. "use strict";
  180. var th = this;
  181. $('#plugin_dropdown').css('display', 'initial');
  182. };
  183.  
  184. Core.prototype.resetVariables = function () {
  185. "use strict";
  186. this.connected = false;
  187. window.userInfo = undefined;
  188. };
  189.  
  190. Core.prototype.main = function () {
  191. "use strict";
  192. var th = this,
  193. plugins = window.plugins;
  194. th.executeOnceCore();
  195. plugins.logger.executeOnceCore();
  196. plugins.commands.executeOnceCore();
  197. //don't want to have ask if its firefox or chrome everytime
  198. logger().info(th.name, navigator.userAgent);
  199. plugins.pluginManager.executeOnceCore();
  200. events.on(plugins.cssLoader, 'ExecuteOnce', plugins.cssLoader.executeOnceCore);
  201. events.on(plugins.settings, 'ExecuteOnce', plugins.settings.executeOnceCore);
  202. events.on(th, 'PreConnect,Disconnect', function () {
  203. events.fire('ResetVariables');
  204. });
  205. //prepare plugins
  206. for (var pluginName in plugins) {
  207. if (!plugins.hasOwnProperty(pluginName)) {
  208. continue;
  209. }
  210. var plugin = plugins[pluginName];
  211. if (!plugin.enabled) {
  212. logger().info(th.name, "Skipping disabled plugin", plugin.name, plugin.version);
  213. continue;
  214. }
  215. logger().info(th.name, "Found plugin", plugin.name, plugin.version);
  216. events.on(plugin, 'PreConnect', plugin.preConnect);
  217. events.on(plugin, 'PostConnect', plugin.postConnect);
  218. events.on(plugin, 'ExecuteOnce', plugin.executeOnce);
  219. events.on(plugin, 'ResetVariables', plugin.resetVariables);
  220. commands.bind(plugin.commands);
  221. if (Array.isArray(plugin.settings)) {
  222. plugins.settings.fields = plugins.settings.fields.concat(plugin.settings);
  223. }
  224. }
  225.  
  226. function load() {
  227. //we are not connected yet
  228. events.fire('PreConnect');
  229. //if the script was loading slow and we are already connected
  230. if (window.userInfo) {
  231. th.connected = true;
  232. events.fire('PostConnect');
  233. }
  234. }
  235. //these need to be executed last
  236. events.on(plugins.eventHooks, 'ExecuteOnce', plugins.eventHooks.executeOnceCore);
  237. events.on(th, 'LoadUserlist', function () {
  238. th.connected = true;
  239. events.fire('PostConnect');
  240. });
  241. //execute one time only scripts
  242. events.fire('ExecuteOnce');
  243.  
  244. load();
  245. };
  246.  
  247. window.plugins = window.plugins || {};
  248. window.plugins.core = new Core('1.3.9');
  249. if (window.document.readyState === 'complete') {
  250. window.plugins.core.main();
  251. } else {
  252. window.addEventListener('load', function () {
  253. window.plugins.core.main();
  254. }, false);
  255. }