WM Rules Manager Objects

This is the rules manager system which is created under the WM version 4.x script

As of 2014-12-09. See the latest version.

This script should not be not be installed directly. It is a library for other scripts to include with the meta directive // @require https://update.greatest.deepsurf.us/scripts/6896/27561/WM%20Rules%20Manager%20Objects.js

  1. // ==UserScript==
  2. // @name WM Rules Manager Objects
  3. // @namespace MerricksdadWMRulesManagerObjects
  4. // @description This is the rules manager system which is created under the WM version 4.x script
  5. // @license http://creativecommons.org/licenses/by-nc-nd/3.0/us/
  6. // @version 4.0.0.0
  7. // @copyright Charlie Ewing except where noted
  8. // ==/UserScript==
  9.  
  10. //this script requires some functions in the WM Common Library
  11. //this script needs access to a pre-defined JSON object
  12.  
  13.  
  14. (function(){
  15.  
  16. //***************************************************************************************************************************************
  17. //***** Rules Manager Object
  18. //***************************************************************************************************************************************
  19. WM.rulesManager = {
  20. rules:[],
  21. enabled:function(){return !WM.quickOpts.heartbeatDisabled;},
  22.  
  23. init:function(params){try{
  24. params=(params||{});
  25. // build a kidsNode getter
  26. WM.rulesManager.__defineGetter__("kidsNode",function(){try{
  27. return $("wmPriorityBuilder");
  28. }catch(e){log("WM.rulesManager.kidsNode: "+e);}});
  29.  
  30. //import rules
  31. WM.rulesManager.rules=[];
  32. var rulesIn=getOptJSON("priority3_"+WM.currentUser.profile)||{};
  33. var globalsIn=getOptJSON("priority3_global")||{};
  34. //detect early beta rule lists
  35. if (isObject(rulesIn)) for (var i in rulesIn){
  36. var rule=rulesIn[i];
  37. WM.rulesManager.rules.push( new WM.rulesManager.Rule(rule) );
  38. //don't bother with globals here
  39. //or use current version rule arrays
  40. } else if (isArrayAndNotEmpty(rulesIn)) for (var i=0,rule;(rule=rulesIn[i]);i++) {
  41. if (rule.isGlobal) {
  42. var glob=globalsIn[rule.uniqueID]||null;
  43. if (glob){
  44. var merge=mergeJSON(glob,rule);
  45. WM.rulesManager.rules.push( new WM.rulesManager.Rule(merge) );
  46. glob.alreadyUsed=true;
  47. } else {
  48. log("WM.rulesManager.init: Global rule missing, cannot merge");
  49. }
  50. } else {
  51. WM.rulesManager.rules.push( new WM.rulesManager.Rule(rule) );
  52. }
  53. }
  54. //import all globals not already accounted for
  55. for (var t in globalsIn) {
  56. var glob=globalsIn[t];
  57. //avoid already imported globals
  58. if (!glob.alreadyUsed){
  59. glob.uniqueID=t;
  60. glob.isGlobal=true;
  61. WM.rulesManager.rule.push( new WM.rulesManager.Rule(glob) );
  62. }
  63. }
  64. }catch(e){log("WM.rulesManager.init: "+e);}},
  65. //check to see if any rules match the post object
  66. doEvent:function(event,obj){
  67. //do nothing if disabled
  68. if (!WM.rulesManager.enabled) return;
  69. //log("WM.rulesManager.doEvent: event="+event+", post="+post.id);
  70. for (var r=0,rule;(rule=WM.rulesManager.rules[r]);r++){
  71. if (rule.enabled) (function(){rule.doEvent(event,obj);})();
  72. }
  73. },
  74. //convert a test (such as dynamic grab entry) to a rule
  75. ruleFromTest:function(test){
  76. //[{"id":"_h6qil21n","label":"new test","search":["body"],"find":["nothing"],"ret":"dynamic","kids":[{"id":"_h6qiw4zf","find":[]}],"appID":"102452128776","disabled":true}]
  77. //[{"enabled":true,"limit":0,"limitCount":0,"expanded":true,"validators":[{"search":["body"],"operand":"lessThan","find":"chipmunk"}],"actions":[{"event":"onIdentify","action":"setColor","params":"orange"}],"kids":[{"enabled":true,"limit":0,"limitCount":0,"expanded":true,"validators":[],"actions":[],"kids":[],"eggs":[]},{"enabled":true,"limit":0,"limitCount":0,"expanded":true,"validators":[],"actions":[],"kids":[],"eggs":[]}],"eggs":[{"enabled":true,"limit":0,"limitCount":0,"expanded":true,"validators":[],"actions":[],"kids":[],"eggs":[]},{"enabled":true,"limit":0,"limitCount":0,"expanded":true,"validators":[],"actions":[],"kids":[],"eggs":[]}]}]
  78. var ret={
  79. title:(test.label||test.title)||"Converted Dynamic Test",
  80. enabled:!(test.disabled||false),
  81. limit:0,
  82. limitCount:0,
  83. expanded:true,
  84. validators:function(){
  85. var ret=[];
  86. //add the initial validator
  87. ret.push({
  88. search:["appID"],
  89. operand:"equals",
  90. find:test.appID
  91. });
  92. //detect search/find method
  93. var method="basic";
  94. if (isArrayAndNotEmpty(test.subTests) && test.find.contains("{%1}")) method="subTests";
  95. if (exists(test.subNumRange) && test.find.contains("{%1}")) method="subNumRange";
  96. if (test.regex==true) method="regexp";
  97. if (method=="regexp") {
  98. //leave the expression just as it is
  99. ret.push({
  100. search:test.search||[],
  101. operand:"matchRegExp",
  102. find:test.find,
  103. });
  104. } else if (method=="basic") {
  105. //convert the test.find array into a regular espression
  106. ret.push({
  107. search:test.search||[],
  108. operand:"matchRegExp",
  109. find:arrayToRegExp(test.find),
  110. });
  111. } else if (method=="subTests") {
  112. //insert the subTests into the find insertion point as a regular expression
  113. //but make the rest of the find parameter not return if found
  114. var find=test.find;
  115. if (find.contains("{%1}")){
  116. find=find.split("{%1}");
  117. find=(find[0].length?("(?:"+find[0]+")"):"")+arrayToRegExp(test.subTests)+(find[1].length?("(?:"+find[1]+")"):"");
  118. }
  119. ret.push({
  120. search:test.search||[],
  121. operand:"matchRegExp",
  122. find:find
  123. });
  124. } else if (method=="subNumRange") {
  125. //insert the subNumRange into the find insertion point as a regular expression
  126. //but make the rest of the find parameter not return if found
  127. var numRange=("string"==typeof test.subNumRange)?test.subNumRange.split(","):[test.subNumRange.low,test.subNumRange.high];
  128. var find=test.find;
  129. if (find.contains("{%1}")){
  130. find=find.split("{%1}");
  131. find=(find[0].length?("(?:"+find[0]+")"):"")+integerRangeToRegExp({min:numRange[0],max:numRange[1]})+(find[1].length?("(?:"+find[1]+")"):"");
  132. }
  133. ret.push({
  134. search:test.search||[],
  135. operand:"matchRegExp",
  136. find:find
  137. });
  138. }
  139. return ret;
  140. }(),
  141. actions:[
  142. {
  143. event:"onIdentify",
  144. action:"setWhich",
  145. params:test.ret||"dynamic",
  146. }
  147. ],
  148. kids:[],
  149. eggs:[],
  150. };
  151. //convert children
  152. if (isArrayAndNotEmpty(test.kids)) {
  153. for (var k=0,kid;(kid=test.kids[k]);k++) {
  154. ret.kids.push(WM.rulesManager.ruleFromTest(kid));
  155. }
  156. }
  157. return ret;
  158. },
  159. //create a rule based on a specific post
  160. ruleFromPost:function(post){
  161. //create some data to get us started
  162. var rule={
  163. basedOn:post,
  164. title:"Based On: "+post.id,
  165. enabled:false, //start out not using this rule
  166. validators:[
  167. {search:["appID"],find:post.appID,operand:"equals"},
  168. {search:["title"],find:post.name,operand:"matchRegExp"},
  169. {search:["caption"],find:post.caption,operand:"matchRegExp"},
  170. {search:["desc"],find:post.description,operand:"matchRegExp"},
  171. {search:["link"],find:post.linkText,operand:"matchRegExp"},
  172. ],
  173. actions:[
  174. {event:"onIdentify",action:"setWhich",params:"dynamic"}
  175. ]
  176. };
  177. WM.rulesManager.rules.push(rule=new WM.rulesManager.Rule(rule));
  178.  
  179. if (WM.opts.rulesJumpToNewRule){
  180. //jump to rule view
  181. WM.console.tabContainer.selectTab(3);
  182. //scroll to new rule
  183. rule.node.scrollIntoView();
  184. }
  185. },
  186. //copy all dynamics to new rules
  187. //does not destroy dynamics as they are converted
  188. convertDynamics:function(){
  189. var tests=WM.grabber.tests;
  190. if (isArrayAndNotEmpty(tests)) {
  191. for (var t=0,test;(test=tests[t]);t++){
  192. WM.rulesManager.rules.push( new WM.rulesManager.Rule( WM.rulesManager.ruleFromTest(test) ) );
  193. }
  194. }
  195. },
  196.  
  197. //rest rule limits for all rules and their children
  198. resetAllLimits:function(params){
  199. params=params||{};
  200. var ask=WM.opts.rulesConfirmResetLimit;
  201. if (params.noConfirm || !ask || (ask && confirm("Reset Limit Counter?"))) {
  202. if (isArrayAndNotEmpty(WM.rulesManager.rules)) for (var r=0,rule;(rule=WM.rulesManager.rules[r]);r++) {
  203. rule.resetLimit({preventSave:true,resetChildren:true,noConfirm:true});
  204. }
  205. WM.rulesManager.saveRules();
  206. }
  207. },
  208. saveRules : function(){try{
  209. //pack rule objects
  210. var retRules=[];
  211. var retGlobal={};
  212. if (isArrayAndNotEmpty(WM.rulesManager.rules)) {
  213. for (var r=0,rule; (rule=WM.rulesManager.rules[r]);r++){
  214. if (!rule.isGlobal) {
  215. retRules.push(rule.saveableData);
  216. } else {
  217. //make a placeholder locally
  218. retRules.push({isGlobal:true, uniqueID:rule.uniqueID, enabled:rule.enabled, expanded:rule.expanded});
  219. //and save it globally
  220. var glob=rule.saveableData;
  221. glob.uniqueID=rule.uniqueID;
  222. retGlobal[rule.uniqueID]=glob;
  223. }
  224. }
  225. }
  226. //save rules
  227. setOptJSON("priority3_"+WM.currentUser.profile,retRules);
  228. setOptJSON("priority3_global",retGlobal);
  229. }catch(e){log("WM.rulesManager.saveRules: "+e);}},
  230. showData : function(){try{
  231. promptText(getOpt("priority3_"+WM.currentUser.profile),true);
  232. }catch(e){log("WM.rulesManager.showData: "+e);}},
  233.  
  234. newRule : function(p){try{
  235. var rule=new WM.rulesManager.Rule(p);
  236. WM.rulesManager.rules.push(rule);
  237. WM.rulesManager.saveRules();
  238. }catch(e){log("WM.rulesManager.newRule: "+e);}},
  239.  
  240. importRule: function(){try{
  241. var params=prompt("Input rule data",null);
  242. if (params) {
  243. var convertedInput=JSON.parse(params);
  244. if (isArray(convertedInput)){
  245. for (var i=0;i<convertedInput.length;i++){
  246. WM.rulesManager.newRule(convertedInput[i]);
  247. }
  248. } else {
  249. WM.rulesManager.newRule(convertedInput);
  250. }
  251. }
  252. }catch(e){log("WM.rulesManager.importRule: "+e);}},
  253.  
  254. toggleHeartbeat: function(){try{
  255. WM.quickOpts.heartbeatDisabled=!WM.quickOpts.heartbeatDisabled;
  256. with (WM.rulesManager.toggleHBNode) {
  257. className=className.swapWordB(WM.quickOpts.heartbeatDisabled,"oddOrange","oddGreen");
  258. }
  259. WM.saveQuickOpts();
  260. log(WM.quickOpts.heartbeatDisabled);
  261. }catch(e){log("WM.rulesManager.toggleHeartbeat: "+e);}},
  262. };
  263. //***************************************************************************************************************************************
  264. //***** Rules Manager Enums & Functions
  265. //***************************************************************************************************************************************
  266. WM.rulesManager.ruleActions = {
  267. "addToFeeds":{name:"Add Poster To Feeds",toolTip:"Add the post's creator to your feeds manager. Can also be used with onFriend* events."},
  268. "appendLink":{name:"Append To Link",toolTip:"Add specific code to the end of the collection link.",hasParam:true,paramType:"textBox","default":""},
  269. "birth":{name:"Birth Eggs",toolTip:"Clone the egg children to this rule's level, without destroying this rule."},
  270. "cancelInterval":{name:"Cancel Interval",toolTip:"Destroy the repeating timer on this rule."},
  271. "cancelTimer":{name:"Cancel Timer",toolTip:"Destroy the timer on this rule."} ,
  272. "cleanPost":{name:"Clean Post",toolTip:"Remove the calling post from the collector."},
  273. "commentPost":{name:"Comment Post",toolTip:"Create a comment on the calling post.",hasParam:true,paramLabel:"comment",paramType:"string","default":"Thanks!"},
  274. "createInterval":{name:"Create Interval",toolTip:"Create a repeating timer on this rule, where 1000 equals 1 second.",hasParam:true,paramType:"timePicker","default":1000} ,
  275. "createTimer":{name:"Create Timer",toolTip:"Create a timer on this rule, where 1000 equals 1 second.",hasParam:true,paramType:"timePicker","default":1000},
  276. "decrementCounter":{name:"Decrement Limit Counter",toolTip:"Decrement the rule limit counter.",hasParam:true,paramType:"number","default":1},
  277. "decrementParentCounter":{name:"Decrement Parent Limit Counter",toolTip:"Decrement the parent rule limit counter.",hasParam:true,paramType:"number","default":1},
  278. "destroyRule":{name:"Destroy Rule",toolTip:"Permanently removes this rule and all of its children."},
  279. "disableApp":{name:"Disable App",toolTip:"Disable the specified app. Leave blank to disable the app associated with the activating post.",hasParam:true,paramType:"textBox","default":""},
  280. "disableAppOption":{name:"Disable App Option",toolTip:"Disable an option in the related sidekick by internal name.",hasParam:true,paramType:"textBox","default":""},
  281. "disableAutocomment":{name:"Disable Autocomment",toolTip:"Disable the autocomment feature."},
  282. "disableAutolike":{name:"Disable Autolike",toolTip:"Disable the autolike feature."},
  283. "disableChildRules":{name:"Disable Child Rules",toolTip:"Disable the immediate children of this rule. Does not disable this rule."},
  284. "disableHostOption":{name:"Disable Host Option",toolTip:"Disable an option in the wm host by internal name.",hasParam:true},
  285. "disableRule":{name:"Disable Rule",toolTip:"Disable the current rule."},
  286. "emergencyOpen":{name:"Emergency Open",toolTip:"Move the calling post directly to a new processing window, no matter what your opened window limit is."},
  287. "emptyAutolikeQueue":{name:"emptyAutolikeQueue",toolTip:"Destroys the list of posts you intended to autolike or autocomment."},
  288. "enableApp":{name:"Enable App",toolTip:"Enable the specified app. Leave blank to enable the app associated with the activating post.",hasParam:true,paramType:"textBox","default":""},
  289. "enableAppOption":{name:"Enable App Option",toolTip:"Enable an option in the related sidekick by internal name.",hasParam:true,paramType:"textBox","default":""},
  290. "enableAutocomment":{name:"Enable Autocomment",toolTip:"Enable the autocomment feature."},
  291. "enableAutolike":{name:"Enable Autolike",toolTip:"Enable the autolike feature."},
  292. "enableChildRules":{name:"Enable Child Rules",toolTip:"Enable the immediate children of this rule."},
  293. "enableHostOption":{name:"Enable Host Option",toolTip:"Enable an option in the wm host by internal name.",hasParam:true},
  294. "enableRule":{name:"Enable Rule",toolTip:"Enable the current rule."},
  295. "fetchNewer":{name:"Fetch Newer Posts",toolTip:"Fetch some more posts for this app, feed or feed filter."},
  296. "fetchOlder":{name:"Fetch Older Posts",toolTip:"Fetch some more posts for this app, feed or feed filter."},
  297. "fetchHours":{name:"Fetch Hours of Posts",toolTip:"Fetch some more posts for this app, feed or feed filter.",hasParam:true,paramType:"number","default":24},
  298. "forceOpen":{name:"Force Open",toolTip:"Move the calling post directly to the collector queue."},
  299. "forceOpenFirst":{name:"Force Open First",toolTip:"Move the calling post directly to the collector queue AND cut in line to be next processed."},
  300. "hatch":{name:"Hatch Eggs",toolTip:"Hatch the egg-children of the current rule, and destroy this rule."},
  301. "incrementCounter":{name:"Increment Limit Counter",toolTip:"Increment the rule limit counter.",hasParam:true,paramType:"number","default":1},
  302. "incrementParentCounter":{name:"Increment Parent Limit Counter",toolTip:"Increment the parent rule limit counter.",hasParam:true,paramType:"number","default":1},
  303. "likePost":{name:"Like Post",toolTip:"Like the calling post."},
  304. "openPostSource":{name:"Open Post Source",toolTip:"Opens the post source in a separate window/tab."},
  305. "processLast":{name:"Move To Bottom",toolTip:"Move the post to the bottom of the collector window."},
  306. "processFirst":{name:"Move To Top",toolTip:"Move the post to the top of the collector window."},
  307. "pauseAllApps":{name:"Pause All Apps",toolTip:"Pause all apps currently associated with docked sidekicks."},
  308. "pauseApp":{name:"Pause App",toolTip:"Pauses processing anything by this app.",hasParam:true,paramType:"textBox","default":""},
  309. "pauseWM.collector":{name:"Pause WM.collector",toolTip:"Pauses collection of all posts."},
  310. "pauseFetch":{name:"Pause Fetching",toolTip:"Pauses fetching of all posts."},
  311. "pauseType":{name:"Pause Type",toolTip:"Pause collection of all bonuses of this type."},
  312. "pinPost":{name:"Pin Post",toolTip:"Pins the calling post."}, //pin the post
  313. "queueCommentPost":{name:"Queue Comment Post",toolTip:"Comment on the calling post by first using the autolike queue system to delay the autocomment.",hasParam:true,paramLabel:"comment",paramType:"string","default":"Thanks!"},
  314. "queueLikePost":{name:"Queue Like Post",toolTip:"Like the calling post by first using the autolike queue system to delay the autolike."},
  315. "refreshBrowser":{name:"Refresh Browser",toolTip:"Reloads the browser window."},
  316. "reIDAll":{name:"ReID All",toolTip:"Re-ID all posts in the collector."},
  317. "removePriority":{name:"Remove Priority",toolTip:"Sets the priority of the calling post to normal."},
  318. "removePriorityApp":{name:"Remove Priority (App)",toolTip:"Sets the priority of all posts of the calling or specified app to normal.",hasParam:true,paramType:"textBox","default":""},
  319. "removePriorityType":{name:"Remove Priority (Type)",toolTip:"Sets the priority of all posts of the calling app with specified or associated type to normal.",hasParam:true,paramType:"textBox","default":"dynamic"},
  320. "resetAllLimits":{name:"Reset All Limit Counters",toolTip:"Reset all limits in the rules manager."},
  321. "resetLimit":{name:"Reset Limit Counter",toolTip:"Reset the limit counter of the current rule."},
  322. "resetBranchLimits":{name:"Reset Branch Limit Counters",toolTip:"Reset the limit counter of ALL rules that are lower in this branch (children, grandchildren, etc.). Does not reset the limit on this rule."},
  323. "resetChildrenLimits":{name:"Reset Children Limit Counters",toolTip:"Reset the limit counter of immediate child rules of this rule. Does not reset the limit on this rule."},
  324. "resetParentLimit":{name:"Reset Parent Limit Counter",toolTip:"Reset the limit counter of the parent rule."},
  325. "setAppOption":{name:"Set App Option",toolTip:"Set an option in the related sidekick by internal name.",hasParam:true,paramCount:2,paramData:[{paramType:"textBox","default":"",paramLabel:"Name"},{paramType:"textBox","default":"",paramLabel:"Value"}]},
  326. "setAppTab":{name:"Set App Tab",toolTip:"Set the current collection tab by app ID.",hasParam:true,paramType:"textBox","default":"all"},
  327. "setAsAccepted":{name:"Set As Accepted",toolTip:"Set the calling post as accepted.",hasParam:true,paramType:"checkBox",paramLabel:"saveToHistory","default":false},
  328. "setAsExcluded":{name:"Set As Excluded",toolTip:"Set the calling post as excluded."},
  329. "setAsFailed":{name:"Set As Failed",toolTip:"Set the calling post as failed.",hasParam:true,paramType:"checkBox",paramLabel:"saveToHistory","default":false},
  330. "setColor":{name:"Set Post Color",toolTip:"Set the background color of the calling post.",hasParam:true,paramType:"colorPicker","default":"blue"},
  331. "setHostOption":{name:"Set Host Option",toolTip:"Set the value a host option by internal name.",hasParam:true,paramCount:2,paramData:[{paramType:"textBox","default":"",paramLabel:"Name"},{paramType:"textBox","default":"",paramLabel:"Value"}]},
  332. "setPriority":{name:"Set Priority",toolTip:"Set the priority of the calling post.",hasParam:true,paramType:"number","default":50},
  333. "setPriorityApp":{name:"Set Priority (App)",toolTip:"Set the priority of the calling app or specified app.",hasParam:true,paramCount:2,paramData:[{paramType:"textBox",paramLabel:"App","default":""},{paramType:"number",paramLabel:"Priority","default":50}]},
  334. "setPriorityType":{name:"Set Priority (Type)",toolTip:"Set the priority of the calling post type or specified type for the same app.",hasParam:true,paramCount:2,paramData:[{paramType:"textBox",paramLabel:"Type Code","default":""},{paramType:"number",paramLabel:"Priority","default":50}]},
  335. "setToCollect":{name:"Set To Collect",toolTip:"Set the calling post to be collected in normal order. Use Force Open to do more immediate collection, or Emergency Open to override your opened window limit."},
  336. "setToCollectPriority1":{name:"Set To Collect Top Priority",toolTip:"Set the calling post to be collected and also set its priority to 1. Use Force Open to do more immediate collection, or Emergency Open to override your opened window limit."},
  337. "setWhich":{name:"Set Type",toolTip:"Set the bonus type id of the calling post.",hasParam:true,paramType:"textBox","default":"dynamic"},
  338. "uncheckType":{name:"Uncheck Post Type",toolTip:"Unchecks option to collect this bonus in the options menu."},
  339. "unpauseAllApps":{name:"Unpause All Apps",toolTip:"Unpause all apps currently associated with docked sidekicks."},
  340. "unpauseAllTypesAllApps":{name:"Unpause All Types",toolTip:"Unpause all bonus types by all apps."},
  341. "unpauseAllTypesByApp":{name:"Unpause All Types By App",toolTip:"Unpause all bonus types associated with the given app, or the app associated with the activating post.",hasParam:true,paramType:"textBox","default":""},
  342. "unpauseApp":{name:"Unpause App",toolTip:"Starts processing anything by this app.",hasParam:true,paramType:"textBox","default":""},
  343. "unpauseWM.collector":{name:"Unpause WM.collector",toolTip:"Starts collection of posts."},
  344. "unpauseFetch":{name:"Unpause Fetching",toolTip:"Starts fetching of posts."},
  345. "unpauseType":{name:"Unpause Type",toolTip:"Unpause collection of all bonuses of this type."},
  346. };
  347. WM.rulesManager.ruleActionsCodes = {
  348. "addToFeeds":1,"appendLink":2,"birth":3,"cancelInterval":4,"cancelTimer":5,"cleanPost":6,"commentPost":7,"createInterval":8,"createTimer":9,
  349. "decrementCounter":10,"decrementParentCounter":11,"destroyRule":12,"disableApp":13,"disableAppOption":14,"disableAutolike":15,"disableChildRules":16,
  350. "disableHostOption":17,"disableRule":18,"emergencyOpen":19,"emptyAutolikeQueue":20,"enableApp":21,"enableAppOption":22,"enableAutolike":23,
  351. "enableChildRules":24,"enableHostOption":25,"enableRule":26,"fetchNewer":27,"fetchOlder":28,"forceOpen":29,"forceOpenFirst":30,"hatch":31,
  352. "incrementCounter":32,"incrementParentCounter":33,"likePost":34,"openPostSource":35,"processLast":36,"processFirst":37,"pauseAllApps":38,
  353. "pauseApp":39,"pauseWM.collector":40,"pauseFetch":41,"pauseType":42,"pinPost":43,"queueCommentPost":44,"queueLikePost":45,"refreshBrowser":46,
  354. "reIDAll":47,"removePriority":48,"removePriorityApp":49,"removePriorityType":50,"resetAllLimits":51,"resetLimit":52,"resetBranchLimits":53,
  355. "resetChildrenLimits":54,"resetParentLimit":55,"setAppOption":56,"setAppTab":57,"setAsAccepted":58,"setAsExcluded":59,"setAsFailed":60,"setColor":61,
  356. "setHostOption":62,"setPriority":63,"setPriorityApp":64,"setPriorityType":65,"setToCollect":66,"setToCollectPriority1":67,"setWhich":68,
  357. "uncheckType":69,"unpauseAllApps":70,"unpauseAllTypesAllApps":71,"unpauseAllTypesByApp":72,"unpauseApp":73,"unpauseWM.collector":74,"unpauseFetch":75,
  358. "unpauseType":76,"fetchHours":77,"enableAutocomment":78,"disableAutocomment":79
  359. };
  360. WM.rulesManager.ruleActionByCode = function(code){
  361. for (c in WM.rulesManager.ruleActionsCodes) {
  362. if (WM.rulesManager.ruleActionsCodes[c]==code) return c;
  363. }
  364. return null;
  365. };
  366. WM.rulesManager.ruleEvents = {
  367. //post events
  368. "onIdentify":"Called after a post is (re)identified. Posts are first identified as soon as they are fetched.",
  369. "onBeforeCollect":"Called before collection opens a sidekick window.",
  370. "onAfterCollect":"Called after collection is tried. Activates regardless of return status.",
  371. "onFailed":"Called when a post is marked failed. This could be actual or simulated by the user.",
  372. "onAccepted":"Called when a post is marked accepted. This could be actual or simulated by the user.",
  373. "onTimeout":"Called when a post is marked as timed out. This could be actual or simulated by the user.",
  374. "onValidate":"Called when a post is first fetched, but after its first identification. Not called on posts which fail identification.",
  375. //rule events
  376. "onLimit":"Called when this rule limit counter equals the rule's limit.",
  377. "onHatch":"Called when this rule's egg children are hatched.",
  378. "onTimer":"Called when the timer on this rule activates.",
  379. "onInterval":"Called when the repeating timer on this rule activates.",
  380. "onBirth":"Called when this rule's egg children are birthed.",
  381. "onRuleCreated":"Called when the rule is created (or loaded on startup).",
  382. "onRuleButtonClicked":"Called when the rule button is clicked. Available only for control rules.",
  383. //app events
  384. "onSidekickDock":"Called when the sidekick for this app docks.",
  385. "onSidekickReady":"Called when the sidekick for this app creates an app object, and after it appends the collection tab for that app.",
  386. /*
  387. paused/unpaused
  388. enabled/disabled
  389. failCountChanged
  390. acceptCountChanged
  391. */
  392. //console events
  393. "onHeartbeat":"Called when the global heartbeat interval ticks.",
  394. "onSetAppFilter":"Called when the collection panel app tab changes, including at startup if 'Show All' is selected as default",
  395. //feed events
  396. "onFeedFilterOlderLimitReached":"Called when a specific feed filter reaches its user-defined older limit.",
  397. };
  398. WM.rulesManager.ruleEventsCodes ={
  399. "onIdentify":1,"onBeforeCollect":2,"onAfterCollect":3,"onFailed":4,"onAccepted":5,"onTimeout":6,"onValidate":7,"onLimit":8,"onHatch":9,"onTimer":10,
  400. "onInterval":11,"onBirth":12,"onRuleCreated":13,"onSidekickDock":14,"onSidekickReady":15,"onHeartbeat":16,"onSetAppFilter":17,
  401. "onFeedFilterOlderLimitReached":18,"onRuleButtonClicked":19
  402. };
  403. WM.rulesManager.ruleEventByCode = function(code){
  404. for (c in WM.rulesManager.ruleEventsCodes) {
  405. if (WM.rulesManager.ruleEventsCodes[c]==code) return c;
  406. }
  407. return null;
  408. };
  409. WM.rulesManager.postParts = {
  410. "age":"The time between the current time and the post creation time (in ms).",
  411. "acceptCount":"An app's accept counter value. Friend objects also have an acceptCount.",
  412. "activatorType":"Returns the object type of the rule-activating object: app, post, rule, feed, feedfilter or unknown.",
  413. "alreadyProcessed":"Reports if a post has already created a history entry.",
  414. "appID":"The appID of the game for which a post belongs. You can read the appID from the following affected objects: app, post, and feedFilter.",
  415. "appName":"The appName of the game for which this post belongs, as reported by the FB database.",
  416. "body":"The body of a post is a compilation of the title, caption, and desc.",
  417. "canvas":"The canvas of a post is its namespace granted by FB, ie. FarmVille's namespace is 'onthefarm'.",
  418. "caption":"The caption of a post is one line just below its title (or 'name'). Not all posts have this field.",
  419. "commentorID":"The commentorID is a list of IDs of all commentors.",
  420. "commentorName":"The commentorName is a list of names of all commentors.",
  421. "comments":"The comments are list of all comments made to the post, excluding the initial msg.",
  422. "currentTime":"The current time (in ms) on your system, not localized. This global value can be referenced from any activating object type.",
  423. "currentAppTab":"The currently selected collection tab's appID, or the word 'all' if the 'Show All' tab is selected.",
  424. "date":"The date of a post is its creation time on FB, and is the 'created_time' parameter in fb data packets.",
  425. "desc":"The desc of a post is two lines below the title. This is the 'description' parameter in fb data packets. Not all posts have this field.",
  426. "either":"The either of a post is the compilation of the link and body.",
  427. "enabled":"The enabled state of an activating object.",
  428. "expanded":"The expanded state of an activating object.",
  429. "failCount":"An app's fail counter value. Friend objects also have a failCount.",
  430. "friendAcceptedCount":"Gets the accepted count from a FriendTracker friend object matching this post creator.",
  431. "friendFailedCount":"Gets the failed count from a FriendTracker friend object matching this post creator.",
  432. "fromID":"The fromID is the ID of the poster.",
  433. "fromName":"The fromName is the name of the poster.",
  434. "fromNameLastFirst":"The name of the poster, displayed as Lastname, Firstname",
  435. "html":"The html of a post is the compilation of ALL visible FB attributes.",
  436. "id":"Normally a post ID, which is usually the post creator's ID and a timestamp separated by an underscore. Alternately, you can ask for the id of an activating friend, feed or feed filter object.",
  437. "idText":"The identified link text of a post.",
  438. "img":"The img of a post is the url of the icon that displays with the post. This is the 'picture' parameter in fb data packets.",
  439. "isAccepted":"Reports if the post is set as having already been successfully collected.",
  440. "isAppPaused":"Reports if the associated app is paused.",
  441. "isCollect":"Reports if the post is set to be collected.",
  442. "isExcluded":"Reports if the post has been set as excluded.",
  443. "isFailed":"Reports if the post is set as having already failed.",
  444. "isForMe":"Reports if the W2W post targets the current user.",
  445. "isLiked":"Reports if the post has been identified as already being liked by the current user.",
  446. "isMyPost":"Reports if the post belongs to the current user.",
  447. "isPaused":"Reports if the calling object (post or app) is paused. Not specific!",
  448. "isPinned":"Reports if the post is marked as being pinned.",
  449. "isRemovable":"Reports if a feed is removeable. Your own profile wall and your home feed are not removeable, only disableable.",
  450. "isTimeout":"Reports if the post has been marked as a timed out collection attempt.",
  451. "isTypePaused":"Reports if the associated bonus type is paused.",
  452. "isScam":"Reports if a post is suspected of being a scam, usually when the canvas and appName do not match.",
  453. "isStale":"Reports if a post is older than the user-set older limit.",
  454. "isUndefined":"Reports if the post does not match any id given by the sidekick.",
  455. "isWishlist":"Reports if the post is deemed a whichlist request.",
  456. "isWorking":"Reports if the post is currently in the working state (being processed).",
  457. "isW2W":"Reports if the post is a Wall-To-Wall post, meaning that it was posted to a specific user's wall.",
  458. "lastKnownPostDate":"A friend object's last known post date (as unix time, no millisecond data).",
  459. "likeID":"The likeID is a list of IDs of users who liked the post.",
  460. "likeName":"The likeName is a list of names of users who liked this post.",
  461. "limit":"This rule's limit number.",
  462. "limitCount":"This rule's limit counter.",
  463. "link":"The 'link' of a post is the link text, not the url. This is the 'action.name' in fb data packets.",
  464. "linkHref":"The original url as it appeared from facebook. This SHOULD be exactly the same as 'url'.",
  465. "linkText":"The original link text as it appeared from facebook. You may want to NOT use 'link' and instead use this one.",
  466. "msg":"The msg of a post is the part the poster added as a comment during the post's creation.",
  467. "name":"With posts, this is the same as 'title', because its the FB name of a post object. With friend objects, this is the friend's text name.",
  468. "parentLimit":"The parent rule's limit number, or NULL if no parent exists.",
  469. "parentLimitCount":"The parent rule's limit counter, or NULL if no parent exists.",
  470. "postCount":"A friend object's count of posts it is tracking.",
  471. "postedDay":"A partial date-time value containing only the year/month/day portions, which corresponds to the post creation time.",
  472. "postedHour":"A partial date-time value containing only the year/month/day/hour portions, which corresponds to the post creation time.",
  473. "priority":"The priority of a post which could have been set by a rule, or by default of 50.",
  474. "status":"The status of a post is the return code given by a sidekick, or 0 if it has not been processed.",
  475. "targetID":"The targetID is a list of targets' IDs that the poster intended the post to display to.",
  476. "targetName":"The targetName is a list of targets the poster intended the post to display to.",
  477. "title":"The title of a post contains the bold text, usually including the poster's name, at the top of the post. This is the 'name' parameter in facebook data packets.",
  478. "totalCount":"An app's failcount and acceptcount combined. Friend objects also have a totalCount.",
  479. "typesPaused":"An app's list of paused bonus types. Only accessible from an activating post. Please stick to the contains/notContains operators because this is an array, not text.",
  480. "url":"The url of a post is the address to which the post redirects the user when clicked. This is the 'link' or 'action.link' parameter in fb data packets. This is the original url supplied by the app, not a modified url, such as WM's removal of https or a sidekick-modified url. Alternately, you can ask for the URL of a feed object.",
  481. "which":"The 'which' of a post is its identified codename that defines its bonus type and ties it to option menu entries. The codename starts with an appID and ends with something the sidekick developer uses to key the bonus type.",
  482. "whichText":"Text associated with this bonus type.",
  483. };
  484. WM.rulesManager.postPartsCodes = {
  485. "age":1,"acceptCount":2,"activatorType":3,"alreadyProcessed":4,"appID":5,"appName":6,"body":7,"canvas":8,"caption":9,"commentorID":10,
  486. "commentorName":11,"comments":12,"currentTime":13,"currentAppTab":14,"date":15,"desc":16,"either":17,"enabled":18,"expanded":19,"failCount":20,
  487. "fromID":21,"fromName":22,"fromNameLastFirst":23,"html":24,"id":25,"idText":26,"img":27,"isAccepted":28,"isAppPaused":29,"isCollect":30,
  488. "isExcluded":31,"isFailed":32,"isForMe":33,"isLiked":34,"isMyPost":35,"isPaused":36,"isPinned":37,"isRemovable":38,"isTimeout":39,"isTypePaused":40,
  489. "isScam":41,"isStale":42,"isUndefined":43,"isWishlist":44,"isWorking":45,"isW2W":46,"lastKnownPostDate":47,"likeID":48,"likeName":49,"limit":50,
  490. "limitCount":51,"link":52,"linkHref":53,"linkText":54,"msg":55,"name":56,"parentLimit":57,"parentLimitCount":58,"postCount":59,"postedDay":60,
  491. "postedHour":61,"priority":62,"status":63,"targetID":64,"targetName":65,"title":66,"totalCount":67,"typesPaused":68,"url":69,"which":70,
  492. "whichText":71,"friendAcceptedCount":72,"friendFailedCount":73
  493. };
  494. WM.rulesManager.postPartByCode = function(code){
  495. for (c in WM.rulesManager.postPartsCodes) {
  496. if (WM.rulesManager.postPartsCodes[c]==code) return c;
  497. }
  498. return null;
  499. };
  500. WM.rulesManager.ruleOperands = {
  501. "equals":"Property and query must match.",
  502. "notEquals":"Property and query must not match.",
  503. "startsWith":"Property must start with query value.",
  504. "notStartsWith":"Property cannot start with query value.",
  505. "endsWith":"Property must end with query value.",
  506. "notEndsWith":"Property cannot end with query value.",
  507. "contains":"Property contains anywhere the query value.",
  508. "notContains":"Property does not contain the query value.",
  509. "matchRegExp":"Property must match the registered expression.",
  510. "notMatchRegExp":"Property must not match the registered expression.",
  511. "greaterThan":"Property must be greater than query value.",
  512. "lessThan":"Property must be less than query value.",
  513. "greaterThanOrEquals":"Property must be greater than or equal to query value.",
  514. "lessThanOrEquals":"Property must be less than or equal to query value.",
  515.  
  516. "equalsExactly":"Property and query must match exactly via binary comparison.",
  517. "notEqualsExactly":"Property and query must not match exactly via binary comparison.",
  518. };
  519.  
  520. WM.rulesManager.ruleOperandsCodes = {
  521. "equals":1,
  522. "notEquals":2,
  523. "startsWith":3,
  524. "notStartsWith":4,
  525. "endsWith":5,
  526. "notEndsWith":6,
  527. "contains":7,
  528. "notContains":8,
  529. "matchRegExp":9,
  530. "notMatchRegExp":10,
  531. "greaterThan":11,
  532. "lessThan":12,
  533. "greaterThanOrEquals":13,
  534. "lessThanOrEquals":14,
  535. "equalsExactly":15,
  536. "notEqualsExactly":16,
  537. };
  538. WM.rulesManager.ruleOperandByCode = function(code){
  539. for (c in WM.rulesManager.ruleOperandsCodes) {
  540. if (WM.rulesManager.ruleOperandsCodes[c]==code) return c;
  541. }
  542. return null;
  543. };
  544. //***************************************************************************************************************************************
  545. //***** RuleValidator Class
  546. //***************************************************************************************************************************************
  547. WM.rulesManager.RuleValidator = function(params){try{
  548. var isNew=(!exists(params));
  549. var self=this;
  550. //return saveable data from this branch
  551. this.__defineGetter__("saveableData",function(){try{
  552. var s=self.search, modSearch=[]; //use a second array to avoid accidental overwrite of first byRef
  553. for (var c=0;c<s.length;c++){
  554. modSearch.push(WM.rulesManager.postPartsCodes[s[c]]);
  555. }
  556. var ret = {search:modSearch, operand:WM.rulesManager.ruleOperandsCodes[self.operand], find:self.find}
  557. return ret;
  558. }catch(e){log("WM.rulesManager.RuleValidator.saveableData: "+e);}});
  559.  
  560. //remove this from parent
  561. this.remove=function(){try{
  562. var ask=WM.opts.rulesConfirmDeleteValidator;
  563. if (!ask || (ask && confirm("Delete rule validator?"))){
  564. remove(this.node);
  565. this.parent.validators.removeByValue(this);
  566. doAction(WM.rulesManager.saveRules);
  567. }
  568. }catch(e){log("WM.rulesManager.RuleValidator.remove: "+e);}};
  569. this.moveUp=function(){try{
  570. //where is this
  571. var parentContainer = this.parent.validators;
  572. //only affects items not already the first in the list
  573. //and not the only child in the list
  574. if ((parentContainer.length>1) && (parentContainer[0]!=this)) {
  575. //which index is this?
  576. var myIndex=parentContainer.inArrayWhere(this);
  577. if (myIndex != -1) {
  578. //I have a proper index here
  579. //who is my sibling
  580. var sibling = parentContainer[myIndex-1];
  581. //swap me with my sibling
  582. parentContainer[myIndex-1]=this;
  583. parentContainer[myIndex]=sibling;
  584. //place my node before my sibling node
  585. sibling.node.parentNode.insertBefore(this.node,sibling.node);
  586. //save it
  587. WM.rulesManager.saveRules();
  588. }
  589. }
  590. }catch(e){log("WM.rulesManager.RuleValidator.moveUp: "+e);}};
  591.  
  592. //move down in the list
  593. this.moveDown=function(){try{
  594. //where is this
  595. var parentContainer = this.parent.validators;
  596. //only affects items not already the first in the list
  597. //and not the only child in the list
  598. if ((parentContainer.length>1) && (parentContainer.last()!=this)) {
  599. //which index is this?
  600. var myIndex=parentContainer.inArrayWhere(this);
  601. if (myIndex != -1) {
  602. //I have a proper index here
  603. //who is my sibling
  604. var sibling = parentContainer[myIndex+1];
  605. //swap me with my sibling
  606. parentContainer[myIndex+1]=this;
  607. parentContainer[myIndex]=sibling;
  608. //place my node before my sibling node
  609. sibling.node.parentNode.insertBefore(sibling.node,this.node);
  610. //save it
  611. WM.rulesManager.saveRules();
  612. }
  613. }
  614. }catch(e){log("WM.rulesManager.RuleValidator.moveDown: "+e);}};
  615.  
  616. //copy this validator on the parent
  617. this.clone=function(){try{
  618. this.parent.addValidator({search:this.search, operand:this.operand, find:this.find});
  619. WM.rulesManager.saveRules();
  620. }catch(e){log("WM.rulesManager.RuleValidator.clone: "+e);}};
  621.  
  622. //init
  623. //this.id=params.id||unique();
  624. this.parent=params.parent||null;
  625. if (!this.parent) {
  626. log("WM.rulesManager.RuleValidator: no parent specified: abort init");
  627. return null;
  628. }
  629. //this.validationNode=parent.validationNode;
  630. this.search=params.search||["appID"];
  631. if (!isArray(this.search)) this.search=[].push(this.search);
  632. //convert number codes to text commands
  633. for (var e in this.search) {
  634. //t=this.search[e];
  635. if (isNumber(this.search[e])) this.search[e]=WM.rulesManager.postPartByCode(this.search[e]);
  636. //log([this.search[e],t])
  637. }
  638. this.operand=params.operand||"matchRegExp";
  639. if (isNumber(this.operand)) this.operand=WM.rulesManager.ruleOperandByCode(this.operand);
  640. this.find=params.find||"";
  641. //draw it
  642. this.parent.validationNode.appendChild(this.node=createElement("div",{className:"validator"},[
  643. //search portion for this validator
  644. createElement("div",{className:"line"},[
  645. this.searchNode=(this.objSearch=new jsForms.comboBox({
  646. className:"jsfComboBox selectPostPart",
  647. onChange:function(){
  648. self.search=this.value;
  649. WM.rulesManager.saveRules();
  650. },
  651. items:(function(){
  652. var ret=[];
  653. for (var i in WM.rulesManager.postParts){
  654. ret.push(new jsForms.checkBox({
  655. text:i,
  656. value:i,
  657. toolTipText:WM.rulesManager.postParts[i],
  658. checked:(self.search.inArray(i)),
  659. size:{width:"200%"},
  660. }));
  661. }
  662. return ret;
  663. })(),
  664. borderStyle:"none",
  665. //borderRadius:{topLeft:"1px", bottomRight:"1px",topRight:"1px",bottomLeft:"1px"},
  666. //explicitClose:true,
  667. highlightSelected:true,
  668. dropDownSize:{height:"200px"},
  669. backColor:"#EEEEEE",
  670. })).node,
  671. //operator portion for this validator
  672. this.operandNode=createElement("select",{className:"selectOperand",onchange:function(){self.operand=this.value;WM.rulesManager.saveRules();}},(function(){
  673. var ret=[],elem;
  674. for (var i in WM.rulesManager.ruleOperands){
  675. ret.push(elem=createElement("option",{textContent:i,value:i,title:WM.rulesManager.ruleOperands[i]}));
  676. if (i==self.operand) elem.selected=true;
  677. }
  678. return ret;
  679. })()),
  680. //find portion for this validator
  681. /*
  682. right here we need to bring up an element based on
  683. the post part chosen
  684. for most cases, we just need an input box to accept string values
  685. for special case "which" we need a dropdown of bonus types
  686. for boolean flags we need a default value of true and maybe
  687. some kind of limitation to true and false in the box
  688. */
  689. this.findNode=createElement("input",{className:"findBox",value:this.find,onchange:function(){self.find=this.value;WM.rulesManager.saveRules();}}),
  690. //toolbox
  691. createElement("div",{className:"littleButton oddOrange",onclick:function(){self.remove();},title:"Delete Validator"},[
  692. createElement("img",{className:"resourceIcon trash"+WM.opts.littleButtonSize}),
  693. ]),
  694. createElement("div",{className:"littleButton oddBlue",onclick:function(){self.clone();},title:"Clone Validator"},[
  695. createElement("img",{className:"resourceIcon clone"+WM.opts.littleButtonSize}),
  696. ]),
  697. createElement("div",{className:"littleButton oddGreen",onclick:function(){self.moveUp();},title:"Move Up"},[
  698. createElement("img",{className:"resourceIcon arrowUp"+WM.opts.littleButtonSize}),
  699. ]),
  700. createElement("div",{className:"littleButton oddOrange",onclick:function(){self.moveDown();},title:"Move Down"},[
  701. createElement("img",{className:"resourceIcon arrowDown"+WM.opts.littleButtonSize}),
  702. ]),
  703. (self.parent.basedOn)?createElement("div",{className:"indent littleButton oddBlue",onclick:function(){
  704. //if a validator search array exists
  705. if (isArrayAndNotEmpty(self.search)){
  706. //fill the 'find' box with the post data linked to the search terms
  707. var f="";
  708. var post=self.parent.basedOn;
  709. for (var s=0;s<self.search.length;s++){
  710. if (s>0) f+=" ";
  711. f+=(post.testData[self.search[s]]||post[self.search[s]]||"");
  712. }
  713. self.findNode.value=f;
  714. self.find=f;
  715. WM.rulesManager.saveRules();
  716. }
  717. },title:"Capture Text From Linked Post"},[
  718. createElement("img",{className:"resourceIcon importData"+WM.opts.littleButtonSize}),
  719. ]):null,
  720. ]),
  721. ]));
  722. //if (isNew) WM.rulesManager.saveRules();
  723. return self;
  724. }catch(e){log("WM.rulesManager.RuleValidator.init(): "+e);}};
  725.  
  726. //***************************************************************************************************************************************
  727. //***** RuleAction Class
  728. //***************************************************************************************************************************************
  729. WM.rulesManager.RuleAction = function(params){try{
  730. var isNew=(!exists(params));
  731. var self=this;
  732. //return saveable data from this branch
  733. this.__defineGetter__("saveableData",function(){try{
  734. var a= {event:WM.rulesManager.ruleEventsCodes[this.event], action:WM.rulesManager.ruleActionsCodes[this.action]};
  735. if (this.hasParam) a.params=this.params;
  736. if (this.paramCount==2) a.params2=this.params2;
  737. return a;
  738. }catch(e){log("WM.rulesManager.RuleAction.saveableData: "+e);}});
  739.  
  740. //remove this from parent
  741. this.remove=function(){try{
  742. var ask=WM.opts.rulesConfirmDeleteAction;
  743. if (!ask || (ask && confirm("Delete rule action?"))){
  744. remove(this.node);
  745. this.parent.actions.removeByValue(this);
  746. doAction(WM.rulesManager.saveRules);
  747. }
  748. }catch(e){log("WM.rulesManager.RuleAction.remove: "+e);}};
  749. //move up in the list
  750. this.moveUp=function(){try{
  751. //where is this
  752. var parentContainer = this.parent.actions;
  753. //only affects items not already the first in the list
  754. //and not the only child in the list
  755. if ((parentContainer.length>1) && (parentContainer[0]!=this)) {
  756. //which index is this?
  757. var myIndex=parentContainer.inArrayWhere(this);
  758. if (myIndex != -1) {
  759. //I have a proper index here
  760. //who is my sibling
  761. var sibling = parentContainer[myIndex-1];
  762. //swap me with my sibling
  763. parentContainer[myIndex-1]=this;
  764. parentContainer[myIndex]=sibling;
  765. //place my node before my sibling node
  766. sibling.node.parentNode.insertBefore(this.node,sibling.node);
  767. //save it
  768. WM.rulesManager.saveRules();
  769. }
  770. }
  771. }catch(e){log("WM.rulesManager.RuleAction.moveUp: "+e);}};
  772.  
  773. //move down in the list
  774. this.moveDown=function(){try{
  775. //where is this
  776. var parentContainer = this.parent.actions;
  777. //only affects items not already the first in the list
  778. //and not the only child in the list
  779. if ((parentContainer.length>1) && (parentContainer.last()!=this)) {
  780. //which index is this?
  781. var myIndex=parentContainer.inArrayWhere(this);
  782. if (myIndex != -1) {
  783. //I have a proper index here
  784. //who is my sibling
  785. var sibling = parentContainer[myIndex+1];
  786. //swap me with my sibling
  787. parentContainer[myIndex+1]=this;
  788. parentContainer[myIndex]=sibling;
  789. //place my node before my sibling node
  790. sibling.node.parentNode.insertBefore(sibling.node,this.node);
  791. //save it
  792. WM.rulesManager.saveRules();
  793. }
  794. }
  795. }catch(e){log("WM.rulesManager.RuleAction.moveDown: "+e);}};
  796.  
  797. //copy this validator on the parent
  798. this.clone=function(){try{
  799. this.parent.addAction(this.saveableData());
  800. WM.rulesManager.saveRules();
  801. }catch(e){log("WM.rulesManager.RuleAction.clone: "+e);}};
  802.  
  803. //init
  804. //this.id=params.id||unique();
  805. this.parent=params.parent||null;
  806. if (!this.parent) {
  807. log("WM.rulesManager.RuleAction: no parent specified: abort init");
  808. return null;
  809. }
  810. //this.actionsNode=parent.actionsNode;
  811. this.action=params.action||"incrementCounter";
  812. //log(this.action);
  813. if (isNumber(this.action)) this.action=WM.rulesManager.ruleActionByCode(this.action);
  814. this.event=params.event||"onAccepted";
  815. if (isNumber(this.event)) this.event=WM.rulesManager.ruleEventByCode(this.event);
  816. //setup default values and param types
  817. //log(this.action);
  818. var def=WM.rulesManager.ruleActions[this.action];
  819. this.hasParam = def.hasParam;
  820. this.params = params.params||def["default"]||((def.paramData||null)?def.paramData[0]["default"]:"");
  821. this.params2 = params.params2||((def.paramData||null)?def.paramData[1]["default"]:"");
  822. this.paramCount = def.paramCount;
  823. //draw it
  824. this.parent.actionsNode.appendChild(this.node=createElement("div",{className:"action"},[
  825. //event for this action
  826. createElement("div",{className:"line"},[
  827. this.eventNode=createElement("select",{className:"selectEvent",onchange:function(){self.event=this.value; if (self.event=="onRuleButtonClicked") {self.parent.ruleButtonHousingNode.style.display="";} else {self.parent.ruleButtonHousingNode.style.display="none";}; WM.rulesManager.saveRules();}},(function(){
  828. var actioneventsret=[],elem;
  829. for (var i in WM.rulesManager.ruleEvents){
  830. actioneventsret.push(elem=createElement("option",{textContent:i,value:i,title:WM.rulesManager.ruleEvents[i]}));
  831. if (i==self.event) elem.selected=true;
  832. }
  833. return actioneventsret;
  834. })()),
  835. //function to call on the event
  836. this.actionNode=createElement("select",{className:"selectFunction",onchange:function(){
  837. self.action=this.value;
  838. WM.rulesManager.saveRules();
  839. //set the param type
  840. var action = WM.rulesManager.ruleActions[this.value];
  841. self.paramNode.style.display=((action.hasParam)?"":"none");
  842. self.param2Node.style.display=((action.hasParam && (action.paramCount==2))?"":"none");
  843.  
  844. }},(function(){
  845. var actionfuncsret=[],elem;
  846. for (var i in WM.rulesManager.ruleActions){
  847. entry=WM.rulesManager.ruleActions[i];
  848. actionfuncsret.push(elem=createElement("option",{textContent:entry.name,value:i,title:entry.toolTip}));
  849. if (i==self.action) elem.selected=true;
  850. }
  851. return actionfuncsret;
  852. })()),
  853. //this is for special cases only and should be hidden otherwise
  854. this.paramNode=createElement("input",{className:"paramBox",value:this.params,onchange:function(){self.params=this.value;WM.rulesManager.saveRules();}}),
  855. this.param2Node=createElement("input",{className:"paramBox",value:this.params2,onchange:function(){self.params2=this.value;WM.rulesManager.saveRules();}}),
  856. //toolbox
  857. createElement("div",{className:"littleButton oddOrange",onclick:function(){self.remove();},title:"Delete Action"},[
  858. createElement("img",{className:"resourceIcon trash"+WM.opts.littleButtonSize}),
  859. ]),
  860. createElement("div",{className:"littleButton oddBlue",onclick:function(){self.clone();},title:"Clone Action"},[
  861. createElement("img",{className:"resourceIcon clone"+WM.opts.littleButtonSize}),
  862. ]),
  863. createElement("div",{className:"littleButton oddGreen",onclick:function(){self.moveUp();},title:"Move Up"},[
  864. createElement("img",{className:"resourceIcon arrowUp"+WM.opts.littleButtonSize}),
  865. ]),
  866. createElement("div",{className:"littleButton oddOrange",onclick:function(){self.moveDown();},title:"Move Down"},[
  867. createElement("img",{className:"resourceIcon arrowDown"+WM.opts.littleButtonSize}),
  868. ]),
  869.  
  870. ]),
  871. ]));
  872. //hide param node when not used
  873. self.paramNode.style.display=((self.hasParam)?"":"none");
  874. self.param2Node.style.display=((self.hasParam && (self.paramCount==2))?"":"none");
  875.  
  876. //if (isNew) WM.rulesManager.saveRules();
  877. return self;
  878. }catch(e){log("WM.rulesManager.RuleAction.init(): "+e);}};
  879.  
  880. //***************************************************************************************************************************************
  881. //***** Rule Class
  882. //***************************************************************************************************************************************
  883. WM.rulesManager.Rule = function(params){try{
  884. this.objType="rule";
  885. var self=this;
  886. params=params||{};
  887.  
  888. //set defaults
  889. this.parent=null;
  890. this.enabled=true;
  891. this.kids=[]; //child nodes
  892. this.eggs=[]; //hatchable child nodes
  893. this.actions=[]; //events:actions list
  894. this.validators=[]; //search:find list
  895. this.limitCount=0;
  896. this.limit=0;
  897. this.actionsNode=null;
  898. this.validationNode=null;
  899. this.node=null;
  900. this.isChild=false;
  901. this.isEgg=false;
  902. this.expanded=true;
  903. this.timers={};
  904. this.intervals={};
  905. this._isGlobal=false;
  906. //return savable data from this branch
  907. this.__defineGetter__("saveableData",function(){try{
  908. var ret={};
  909. //ret.id=this.id;
  910. ret.title=this.title;
  911. ret.enabled=this.enabled;
  912. ret.limit=this.limit;
  913. ret.limitCount=this.limitCount;
  914. //ret.level=this.level;
  915. ret.expanded=this.expanded;
  916. ret.validators=[];
  917. if (isArrayAndNotEmpty(this.validators)) for (var i=0,validator;(validator=this.validators[i]);i++) {
  918. ret.validators.push(validator.saveableData);
  919. }
  920. ret.actions=[];
  921. if (isArrayAndNotEmpty(this.actions)) for (var i=0,action;(action=this.actions[i]);i++) {
  922. ret.actions.push(action.saveableData);
  923. }
  924. ret.kids=[];
  925. if (isArrayAndNotEmpty(this.kids)) for (var i=0,kid;(kid=this.kids[i]);i++) {
  926. ret.kids.push(kid.saveableData);
  927. }
  928. ret.eggs=[];
  929. if (isArrayAndNotEmpty(this.eggs)) for (var i=0,egg;(egg=this.eggs[i]);i++) {
  930. ret.eggs.push(egg.saveableData);
  931. }
  932. return ret;
  933. }catch(e){log("WM.rulesManager.Rule.saveableData: "+e);}});
  934.  
  935. //set/get wether this rule is saved as global or profile
  936. this.__defineGetter__("isGlobal",function(){try{
  937. return self._isGlobal;
  938. }catch(e){log("WM.rulesManager.Rule.isGlobal: "+e);}});
  939. this.__defineSetter__("isGlobal",function(v){try{
  940. //only top level rule can be global
  941. if (self.parent) {
  942. confirm("Only top level rule can be set to global.");
  943. return;
  944. }
  945. if (!v) {
  946. if (!confirm("Disabling profile sharing on this rule will prevent other users on this machine from loading it. Are you sure you wish to make this rule locally available only?")) return;
  947. }
  948. self._isGlobal=v;
  949. //make sure we have a uniqueID
  950. //but don't destroy one that already exists
  951. if (v && !exists(self.uniqueID)) self.uniqueID = unique();
  952. //change the color/icon of the isGlobal button
  953. if (self.toggleGlobalButton) {
  954. var s=WM.opts.littleButtonSize;
  955. with (self.toggleGlobalButton) className=className.swapWordB(v,"removeGlobal"+s,"addGlobal"+s);
  956. with (self.toggleGlobalButton.parentNode) {
  957. className=className.swapWordB(v,"oddOrange","oddGreen");
  958. title=(v)?"Disable Profile Sharing":"Share With Other Profiles";
  959. }
  960. }
  961. }catch(e){log("WM.rulesManager.Rule.isGlobal: "+e);}});
  962. this.__defineGetter__("parentLimit",function(){try{
  963. if (self.parent||null) return self.parent.limit;
  964. return null;
  965. }catch(e){log("WM.rulesManager.Rule.parentLimit: "+e);}});
  966.  
  967. this.__defineGetter__("isBranchDisabled",function(){try{
  968. var p=self.parent,ret=false;
  969. while(p) {
  970. if (!p.enabled) return true;
  971. p=p.parent;
  972. }
  973. return false;
  974. }catch(e){log("WM.rulesManager.Rule.isBranchDisabled: "+e);}});
  975.  
  976. this.__defineGetter__("parentLimitCount",function(){try{
  977. if (self.parent||null) return self.parent.limitCount;
  978. return null;
  979. }catch(e){log("WM.rulesManager.Rule.parentLimitCount: "+e);}});
  980.  
  981. //copy passed params to this object
  982. for (var p in params) {
  983. //omit specific params
  984. if (!(["actions","validators","kids","eggs"].inArray(p)) ) {
  985. //copy only params that make it past the checker
  986. this[p]=params[p];
  987. }
  988. }
  989. this.usesRuleButton=function(){
  990. for (var action in this.actions) {
  991. if (action.event=="onRuleButtonClicked") {return true;}
  992. }
  993. return false;
  994. };
  995. this.moveUp=function(){try{
  996. //where is this
  997. var parentContainer =
  998. (this.isChild)?this.parent.kids:
  999. (this.isEgg)?this.parent.eggs:
  1000. WM.rulesManager.rules;
  1001. //only affects items not already the first in the list
  1002. //and not the only child in the list
  1003. if ((parentContainer.length>1) && (parentContainer[0]!=this)) {
  1004. //which index is this?
  1005. var myIndex=parentContainer.inArrayWhere(this);
  1006. if (myIndex != -1) {
  1007. //I have a proper index here
  1008. //who is my sibling
  1009. var sibling = parentContainer[myIndex-1];
  1010. //swap me with my sibling
  1011. parentContainer[myIndex-1]=this;
  1012. parentContainer[myIndex]=sibling;
  1013. //place my node before my sibling node
  1014. sibling.node.parentNode.insertBefore(this.node,sibling.node);
  1015. //save it
  1016. WM.rulesManager.saveRules();
  1017. }
  1018. }
  1019. }catch(e){log("WM.rulesManager.Rule.moveUp: "+e);}};
  1020. this.moveDown=function(){try{
  1021. //where is this
  1022. var parentContainer =
  1023. (this.isChild)?this.parent.kids:
  1024. (this.isEgg)?this.parent.eggs:
  1025. WM.rulesManager.rules;
  1026. //only affects items not already the last in the list
  1027. //and not the only child in the list
  1028. if ((parentContainer.length>1) && (parentContainer.last()!=this)) {
  1029. //which index is this?
  1030. var myIndex=parentContainer.inArrayWhere(this);
  1031. if (myIndex != -1) {
  1032. //I have a proper index here
  1033. //who is my sibling
  1034. var sibling = parentContainer[myIndex+1];
  1035. //swap me with my sibling
  1036. parentContainer[myIndex+1]=this;
  1037. parentContainer[myIndex]=sibling;
  1038. //place my node before my sibling node
  1039. sibling.node.parentNode.insertBefore(sibling.node,this.node);
  1040. //save it
  1041. WM.rulesManager.saveRules();
  1042. }
  1043. }
  1044. }catch(e){log("WM.rulesManager.Rule.moveDown: "+e);}};
  1045.  
  1046. this.moveUpLevel=function(){try{
  1047. if (this.parent) {
  1048. //this is not a top level node, so we can move it
  1049. var targetContainer=((this.parent.parent)?this.parent.parent.kids:WM.rulesManager.rules);
  1050. //remove from parent
  1051. this.parent[(this.isChild)?"kids":(this.isEgg)?"eggs":null].removeByValue(this);
  1052. //set new parent
  1053. this.parent=(this.parent.parent||null); //never point to the top level
  1054. //set flags
  1055. this.isChild=(this.parent!=null);
  1056. this.isEgg=false;
  1057. //move the object
  1058. targetContainer.push(this);
  1059. //move the node
  1060. if (this.parent) this.parent.kidsNode.appendChild(this.node);
  1061. else WM.console.priorityBuild.appendChild(this.node);
  1062. //save it
  1063. WM.rulesManager.saveRules();
  1064. }
  1065. }catch(e){log("WM.rulesManager.Rule.moveUpLevel: "+e);}};
  1066. this.moveDownLevel=function(){try{
  1067. //where is this
  1068. var parentContainer =
  1069. (this.isChild)?this.parent.kids:
  1070. (this.isEgg)?this.parent.eggs:
  1071. WM.rulesManager.rules;
  1072. //create a new rule at my level
  1073. var newRule = new WM.rulesManager.Rule({
  1074. parent:this.parent||null,
  1075. isChild:this.isChild,
  1076. isEgg:this.isEgg,
  1077. });
  1078. parentContainer.push(newRule);
  1079. //remove me from my current parent
  1080. parentContainer.removeByValue(this);
  1081. //attach me to my new parent
  1082. this.parent=newRule;
  1083. this.isChild=true;
  1084. this.isEgg=false;
  1085. newRule.kids.push(this);
  1086. //move my node
  1087. newRule.kidsNode.appendChild(this.node);
  1088. //save it
  1089. WM.rulesManager.saveRules();
  1090. }catch(e){log("WM.rulesManager.Rule.moveDownLevel: "+e);}};
  1091.  
  1092. this.enable=function(){try{
  1093. this.enabled=true;
  1094. this.node.className=this.node.className.removeWord("disabled");
  1095. WM.rulesManager.saveRules();
  1096. }catch(e){log("WM.rulesManager.Rule.enable: "+e);}};
  1097.  
  1098. this.disable=function(){try{
  1099. this.enabled=false;
  1100. this.node.className=this.node.className.addWord("disabled");
  1101. WM.rulesManager.saveRules();
  1102. }catch(e){log("WM.rulesManager.Rule.disable: "+e);}};
  1103.  
  1104. this.disableChildren=function(){try{
  1105. if (isArrayAndNotEmpty(this.kids)) for (var k=0,kid;(kid=this.kids[k]);k++){
  1106. kid.disable();
  1107. }
  1108. }catch(e){log("WM.rulesManager.Rule.disableChildren: "+e);}};
  1109.  
  1110. this.enableChildren=function(){try{
  1111. if (isArrayAndNotEmpty(this.kids)) for (var k=0,kid;(kid=this.kids[k]);k++){
  1112. kid.enable();
  1113. }
  1114. }catch(e){log("WM.rulesManager.Rule.enableChildren: "+e);}};
  1115. this.toggle=function(){try{
  1116. //if(this.enabled)this.disable(); else this.enable();
  1117. //this.enabled=!this.enabled;
  1118. this.enabled=this.toggleNode.checked;
  1119. this.node.className=this.node.className.swapWordB(this.enabled,"enabled","disabled");
  1120. WM.rulesManager.saveRules();
  1121. //this.toggleNode.checked=();
  1122.  
  1123. }catch(e){log("WM.rulesManager.Rule.toggle: "+e);}};
  1124.  
  1125. this.clone=function(){try{
  1126. var cloneRule=this.saveableData;
  1127. //cloneRule.id=unique();
  1128. if (this.isChild) this.parent.addChild(cloneRule);
  1129. else if (this.isEgg) this.parent.addEgg(cloneRule);
  1130. else WM.rulesManager.newRule(cloneRule);
  1131. }catch(e){log("WM.rulesManager.RuleAction.clone: "+e);}};
  1132.  
  1133. this.resetLimit=function(params){try{
  1134. params=params||{};
  1135. var ask=WM.opts.rulesConfirmResetLimit;
  1136. if (params.noConfirm || !ask || (ask && confirm("Reset Limit Counter?"))) {
  1137. this.limitCount=0;
  1138. this.limitCounterNode.value=this.limitCount;
  1139. if (!(params.resetChildren||false)) {
  1140. if (isArrayAndNotEmpty(this.kids)) for (var k=0,kid;(kid=this.kids[k]);k++){
  1141. kid.resetLimit(params);
  1142. }
  1143. }
  1144. if (!(params.preventSave||false)) WM.rulesManager.saveRules();
  1145. }
  1146. }catch(e){log("WM.rulesManager.Rule.resetLimit: "+e);}};
  1147. this.resetBranchLimits=function(params){try{
  1148. params=params||{};
  1149. //resets the limits of entire branch rules, but not self limit
  1150. if (isArrayAndNotEmpty(this.kids)) for (var k=0,kid;(kid=this.kids[k]);k++){
  1151. kid.resetLimit({resetChildren:true,noConfirm:params.noConfirm||false});
  1152. }
  1153. }catch(e){log("WM.rulesManager.Rule.resetBranchLimits: "+e);}};
  1154.  
  1155. this.resetChildrenLimits=function(params){try{
  1156. params=params||{};
  1157. //resets the limits of all immediate children, but not self limit
  1158. if (isArrayAndNotEmpty(this.kids)) for (var k=0,kid;(kid=this.kids[k]);k++){
  1159. kid.resetLimit({noConfirm:params.noConfirm||false});
  1160. }
  1161. }catch(e){log("WM.rulesManager.Rule.resetChildrenLimits: "+e);}};
  1162.  
  1163. this.incrementLimitCounter=function(o,n){try{
  1164. this.limitCount=parseInt(parseInt(this.limitCount)+(exists(n)?parseInt(n):1));
  1165. this.limitCounterNode.value=this.limitCount;
  1166. WM.rulesManager.saveRules();
  1167. //for reaching of limit
  1168. if (this.limit && (this.limitCount>=this.limit)) this.onEvent("onLimit",o);
  1169. }catch(e){log("WM.rulesManager.Rule.incrementLimitCounter: "+e);}};
  1170.  
  1171. this.decrementLimitCounter=function(o,n){try{
  1172. this.limitCount=parseInt(parseInt(this.limitCount)-(exists(n)?parseInt(n):1));
  1173. //dont allow to drop below 0
  1174. if (this.limitCount<0) this.limitCount=0;
  1175. this.limitCounterNode.value=this.limitCount;
  1176. WM.rulesManager.saveRules();
  1177. }catch(e){log("WM.rulesManager.Rule.decrementLimitCounter: "+e);}};
  1178.  
  1179. this.remove=function(noConfirm){try{
  1180. var ask=WM.opts.rulesConfirmDeleteRule;
  1181. if (noConfirm || (this.isGlobal && confirm("This rule is shared with other profiles. Deleting it here will prevent it from loading for other users. Are you sure you wish to delete this rule and its children.")) || !ask || (!this.isGlobal && ask && confirm("Delete rule and all of its child nodes?"))){
  1182. //destroy intervals and timers
  1183. this.cleanup();
  1184. //remove my data
  1185. var parentContainer=((this.isChild)?this.parent.kids:(this.isEgg)?this.parent.eggs:WM.rulesManager.rules);
  1186. parentContainer.removeByValue(this);
  1187. //remove my node
  1188. remove(this.node);
  1189. doAction(WM.rulesManager.saveRules);
  1190. }
  1191. }catch(e){log("WM.rulesManager.Rule.remove: "+e);}};
  1192.  
  1193. this.cancelAllTimers=function(){try{
  1194. //find the correct timer by target
  1195. for (var t in this.timers){
  1196. if (this.timers[t]!=null) {
  1197. window.clearTimeout(this.timers[t]);
  1198. delete this.timers[t];
  1199. }
  1200. }
  1201. }catch(e){log("WM.rulesManager.Rule.cancelAllTimers: "+e);}};
  1202.  
  1203. this.cancelTimer=function(target){try{
  1204. //find the correct timer by target
  1205. var timer=null;
  1206. for (var t in this.timers){
  1207. if (this.timers[t].target==target) {
  1208. timer=this.timers[t];
  1209. break;
  1210. }
  1211. }
  1212. if (timer) {
  1213. window.clearTimeout(timer.timer);
  1214. delete this.timers[timer.id];
  1215. }
  1216. }catch(e){log("WM.rulesManager.Rule.cancelTimer: "+e);}};
  1217. this.createTimer=function(t,o){try{
  1218. this.cancelTimer(o);
  1219. var self=this;
  1220. var stamp=unique();
  1221. var timer=window.setTimeout(function(){
  1222. self.onEvent("onTimer",o);
  1223. },t);
  1224. this.timers[stamp]={timer:timer, target:o, id:stamp};
  1225. }catch(e){log("WM.rulesManager.Rule.createTimer: "+e);}};
  1226. this.cancelAllIntervals=function(){try{
  1227. //find the correct timer by target
  1228. for (var t in this.intervals){
  1229. if (this.intervals[t]!=null) {
  1230. window.clearInterval(this.intervals[t]);
  1231. delete this.intervals[t];
  1232. }
  1233. }
  1234. }catch(e){log("WM.rulesManager.Rule.cancelAllIntervals: "+e);}};
  1235. this.cancelInterval=function(target){try{
  1236. //find the correct timer by target
  1237. var interval=null;
  1238. for (var t in this.intervals){
  1239. if (this.intervals[t].target==target) {
  1240. interval=this.intervals[t];
  1241. break;
  1242. }
  1243. }
  1244. if (interval) {
  1245. window.clearInterval(interval.timer);
  1246. delete this.intervals[interval.id];
  1247. }
  1248. }catch(e){log("WM.rulesManager.Rule.cancelInterval: "+e);}};
  1249. this.createInterval=function(t,o){try{
  1250. this.cancelInterval(o);
  1251. var self=this;
  1252. var stamp=unique();
  1253. var interval=window.setInterval(function(){
  1254. self.onEvent("onInterval",o);
  1255. },t);
  1256. this.intervals[stamp]={timer:interval, target:o, id:stamp};
  1257. }catch(e){log("WM.rulesManager.Rule.createInterval: "+e);}};
  1258. this.doEvent=function(event,obj,logit){try{
  1259. //check to see if post matches this rule, if it does, perform actions
  1260. //if (this.validators){
  1261. //logit=logit||(obj.objType=="post");
  1262. var obj=(obj||{});
  1263. //var self=this;
  1264. var matchPost=true, found=[];
  1265. for (var vl=0,validator;(validator=this.validators[vl]);vl++) { try{
  1266. //within the search array, each result is handled as an OR
  1267. var result=false;
  1268. for (var s=0,search; (search=validator.search[s]); s++) {
  1269. var v =
  1270. //special request for object type of the object that activated this rule
  1271. (search=="activatorType")?(
  1272. (exists(obj))?(obj.objType||"unknown"):"unknown"
  1273. ):
  1274. //special specific app being paused test
  1275. (search=="isAppPaused")?(
  1276. (exists(obj) && exists(obj.app))?obj.app.paused:false
  1277. ):
  1278. //special specific bonus type being paused
  1279. (search=="isTypePaused")?(
  1280. (exists(obj) && exists(obj.which) && exists(obj.app))?obj.app.typesPaused.inArray(obj.which):false
  1281. ):
  1282. //read from post object test data
  1283. (exists(obj) && exists(obj.testData) && exists(obj.testData[search]))?obj.testData[search]:
  1284. //read from activating object standard parameters
  1285. (exists(obj) && exists(obj[search]))?obj[search]:
  1286. //read from this rule object
  1287. exists(self[search])?self[search]:
  1288. //read from parameters in the console/main object
  1289. exists(WM[search])?WM[search]:
  1290. //there is no known reference for this query
  1291. "undefined";
  1292.  
  1293. //fail validators that do not work with this obj
  1294. if (v=="undefined") {result=false; break;}
  1295. //convert functions to values
  1296. if (typeof v=="function") v=v();
  1297. var query = validator.find;
  1298. //make the query the same type as the value
  1299. if (!(typeof query == typeof v)) {
  1300. switch (typeof v) {
  1301. case "boolean":
  1302. //convert texts of false/true to actual booleans
  1303. query = cBool(query);
  1304. break;
  1305. case "number":
  1306. //convert text numbers to real numbers
  1307. query=(query=Number(query))?query:0;
  1308. //if (query==null) query=0;
  1309. break;
  1310. }
  1311. }
  1312. //if (logit) log([search, v, query]);
  1313.  
  1314. //compare
  1315. switch(validator.operand){
  1316. case "equals": result=result||(v==query); break;
  1317. case "notEquals": result=result||(v!=query); break;
  1318. case "startsWith": result=result||(v.startsWith(query)); break;
  1319. case "notStartsWith": result=result||(!v.startsWith(query)); break;
  1320. case "endsWith": result=result||(v.endsWith(query)); break;
  1321. case "notEndsWith": result=result||(!v.endsWith(query)); break;
  1322. case "contains": result=result||(v.contains(query)); break;
  1323. case "notContains": result=result||(!v.contains(query)); break;
  1324. case "greaterThan": result=result||(v>query); break;
  1325. case "lessThan": result=result||(v<query); break;
  1326. case "greaterThanOrEquals": result=result||(v>=query); break;
  1327. case "lessThanOrEquals": result=result||(v<=query); break;
  1328. case "matchRegExp":
  1329. var f; //storage space for match array
  1330. var regex = new RegExp(query,"gi");
  1331. f=regex.exec(v);
  1332. result=result||isArrayAndNotEmpty(f);
  1333. //result=result||((f=v.match(regex))!=null);
  1334. if (f) found=found.concat(f);
  1335. break;
  1336. case "notMatchRegExp":
  1337. var regex = new RegExp(query,"gi");
  1338. result=result||(v.match(regex)==null);
  1339. break;
  1340. case "equalsExactly": result=result||(v===query); break;
  1341. case "notEqualsExactly": result=result||!(v===query); break;
  1342. }
  1343. //any result of true inside this loop is an automatic success
  1344. if (result) break;
  1345. }
  1346. //evaluate our current result with the previous results
  1347. //outside the search array, each value is handled as an AND
  1348. //any one non-match is a complete failure
  1349. matchPost=(matchPost && result);
  1350. if (!matchPost) break;
  1351. }catch(e){
  1352. log("WM.rulesManager.Rule.doEvent: "+e+"{event:" +event+ ", search:"+search+", value:"+v+", query:"+query+", result:"+result+"}");
  1353. continue;
  1354. }}
  1355. //if after all testing we still matched the object
  1356. //then perform this rule's events and check children
  1357. if (matchPost) {
  1358. //log("post matches all validators");
  1359. //first do every child rule
  1360. for (var k=0,kid;(kid=this.kids[k]);k++){
  1361. kid.doEvent(event,obj,true);
  1362. }
  1363. //now finish up with this rule's actions
  1364. this.onEvent(event,obj,found||null);
  1365. }
  1366. //}
  1367. //log("WM.rulesManager.Rule.doEvent: {obj="+obj.id+", event="+event+", matchPost="+matchPost+"}");
  1368. }catch(e){log("WM.rulesManager.Rule.doEvent: "+e);}};
  1369.  
  1370. this.onEvent=function(event,obj,found){try{
  1371. var actionFilter=["*"];
  1372. /*
  1373. handle special events
  1374. */
  1375. if (event=="onRuleCreated") {
  1376. /*
  1377. we do want onRuleCreated events to fire even if the rule is disabled,
  1378. or intervals won't be set and ready for later, if the user does enable the rule
  1379. this session. But we want to filter which actions are available.
  1380. */
  1381. if (!this.enabled || this.isBranchDisabled) actionFilter=["createInterval","createTimer"];
  1382. } else if ((event=="onInterval")||(event=="onTimer")) {
  1383. //special case for intervals and timers
  1384. if (!this.enabled || this.isBranchDisabled) return;
  1385. } else {
  1386. //always break if this rule is disabled
  1387. if (!this.enabled || this.isBranchDisabled) return;
  1388. }
  1389. /*
  1390. end handle special events
  1391. */
  1392. obj=obj||null;
  1393. //var self=this;
  1394. var post=(self!=obj)?obj:null;
  1395. var app=post?(exists(obj.app)?obj.app:obj):null;
  1396. //some insertable post parts
  1397. var inserts=["appID","which","fromID"];
  1398. //perform an action based on an event call
  1399. //post object may be null if called from this
  1400. for (var a1=0,action;(action=this.actions[a1]);a1++){
  1401. //filter actions: allow only those in the filter list, or all actions if "*" is in the list
  1402. if (actionFilter.inArray("*") || actionFilter.inArray(action.action) ) if (action.event==event){
  1403. var param=action.params;
  1404. var param2=action.params2;
  1405. var hasParam=action.hasParam;
  1406. //format some post parts into the param
  1407. if (hasParam && param) {
  1408. for (var i=0;i<inserts.length;i++){
  1409. if (post && (post.replace||null)) {
  1410. param.replace(new RegExp("{%"+inserts[i]+"}","gi"), post.testData[inserts[i]] || post[inserts[i]]);
  1411. }
  1412. }
  1413. }
  1414. switch(action.action){
  1415. case "destroyRule":(function(){
  1416. doAction(function(){
  1417. self.remove(true);
  1418. });
  1419. })(); break;
  1420. case "pauseType":(function(){
  1421. var w=post.which, a=app;
  1422. doAction(function(){
  1423. WM.pauseByType(a,w);
  1424. });
  1425. })(); break;
  1426. case "unpauseType":(function(){
  1427. var w=post.which, a=app;
  1428. doAction(function(){
  1429. WM.unPauseByType(a,w);
  1430. });
  1431. })(); break;
  1432. case "uncheckType":(function(){
  1433. var w=post.which, a=app;
  1434. doAction(function(){
  1435. WM.disableOpt(w,a);
  1436. //WM.stopCollectionOf(post.which);
  1437. });
  1438. })(); break;
  1439.  
  1440. case "checkType":(function(){
  1441. var w=post.which, a=app;
  1442. doAction(function(){
  1443. WM.enableOpt(w,a);
  1444. //WM.startCollectionOf(post.which);
  1445. });
  1446. })(); break;
  1447. case "disableAppOption":(function(){
  1448. var c=param, f=found, a=app;
  1449. if (f) c=c.format2(f);
  1450. doAction(function(){
  1451. WM.disableOpt(c,a);
  1452. });
  1453. })(); break;
  1454. case "enableAppOption":(function(){
  1455. var c=param, f=found, a=app;
  1456. if (f) c=c.format2(f);
  1457. doAction(function(){
  1458. WM.enableOpt(c,a);
  1459. });
  1460. })(); break;
  1461. case "disableHostOption":(function(){
  1462. //debug.print("option disabled");
  1463. var c=param, f=found;
  1464. if (f) c=c.format2(f);
  1465. doAction(function(){
  1466. WM.disableOpt(c);
  1467. });
  1468. })(); break;
  1469. case "enableHostOption":(function(){
  1470. //debug.print("option enabled");
  1471. var c=param, f=found;
  1472. if (f) c=c.format2(f);
  1473. doAction(function(){
  1474. WM.enableOpt(c);
  1475. });
  1476. })(); break;
  1477. case "disableAutolike":(function(){
  1478. doAction(function(){
  1479. //debug.print("autolike disabled");
  1480. WM.disableOpt("useautolike");
  1481. });
  1482. })(); break;
  1483. case "enableAutolike":(function(){
  1484. doAction(function(){
  1485. //debug.print("autolike enabled");
  1486. WM.enableOpt("useautolike");
  1487. });
  1488. })(); break;
  1489. case "disableAutocomment":(function(){
  1490. doAction(function(){
  1491. WM.disableOpt("useautocomment");
  1492. });
  1493. })(); break;
  1494. case "enableAutocomment":(function(){
  1495. doAction(function(){
  1496. WM.enableOpt("useautocomment");
  1497. });
  1498. })(); break;
  1499. case "pauseApp":(function(){
  1500. var a = WM.apps[param]||app;
  1501. doAction(function(){
  1502. a.pause();
  1503. });
  1504. })(); break;
  1505. case "unpauseApp":(function(){
  1506. var a = WM.apps[param]||app;
  1507. doAction(function(){
  1508. a.unPause();
  1509. });
  1510. })(); break;
  1511. case "appendLink":(function(){
  1512. var p=post, v=param||"";
  1513. if (p) doAction(function(){
  1514. p.link=p.linkHref+v;
  1515. });
  1516. })(); break;
  1517. case "unpauseAllTypesByApp":(function(){
  1518. var a = WM.apps[param]||app;
  1519. doAction(function(){
  1520. a.unpauseAllTypes();
  1521. });
  1522. })(); break;
  1523. case "unpauseAllTypesAllApps":(function(){
  1524. doAction(function(){
  1525. for (var a in WM.apps){
  1526. a.unpauseAllTypes();
  1527. }
  1528. });
  1529. })(); break;
  1530. case "unpauseAllApps":(function(){
  1531. doAction(function(){
  1532. for (var a in WM.apps){
  1533. a.unpause();
  1534. }
  1535. });
  1536. })(); break;
  1537. case "pauseAllApps":(function(){
  1538. doAction(function(){
  1539. for (var a in WM.apps){
  1540. a.pause();
  1541. }
  1542. });
  1543. })(); break;
  1544. case "refreshBrowser":(function(){
  1545. doAction(function(){
  1546. window.location.reload();
  1547. });
  1548. })(); break;
  1549. case "pauseWM.collector":(function(){
  1550. doAction(function(){
  1551. WM.pauseCollecting(true);
  1552. });
  1553. })(); break;
  1554. case "unpauseWM.collector":(function(){
  1555. doAction(function(){
  1556. WM.pauseCollecting(false);
  1557. });
  1558. })(); break;
  1559. case "pauseFetch":(function(){
  1560. doAction(function(){
  1561. WM.pauseFetching(true);
  1562. });
  1563. })(); break;
  1564. case "unpauseFetch":(function(){
  1565. doAction(function(){
  1566. WM.pauseFetching(false);
  1567. });
  1568. })(); break;
  1569. case "likePost":(function(){
  1570. doAction(function(){
  1571. post.like();
  1572. });
  1573. })(); break;
  1574. case "queueLikePost":(function(){
  1575. doAction(function(){
  1576. WM.queAutoLike(post);
  1577. });
  1578. })(); break;
  1579. case "commentPost":(function(){
  1580. var p=param,f=found;
  1581. if (f) p=p.format2(f);
  1582. doAction(function(){
  1583. post.comment(p);
  1584. });
  1585. })(); break;
  1586. case "queueCommentPost":(function(){
  1587. var p=param,f=found;
  1588. if (f) p=p.format2(f);
  1589. //log(["queueCommentPost fired",p]);
  1590. doAction(function(){
  1591. WM.queAutoComment(post,p);
  1592. });
  1593. })(); break;
  1594. case "cleanPost":(function(){
  1595. doAction(function(){
  1596. post.remove();
  1597. });
  1598. })(); break;
  1599. case "incrementCounter":(function(){
  1600. var o=obj,p=param,f=found;
  1601. //if (f) p=p.format2(f);
  1602. doAction(function(){
  1603. self.incrementLimitCounter(o,p);
  1604. });
  1605. })(); break;
  1606. case "decrementCounter":(function(){
  1607. var o=obj,p=param,f=found;
  1608. //if (f) p=p.format2(f);
  1609. doAction(function(){
  1610. self.decrementLimitCounter(o,p);
  1611. });
  1612. })(); break;
  1613. case "incrementParentCounter":(function(){
  1614. var o=obj,p=param, f=found;
  1615. //if (f) p=p.format2(f);
  1616. if (this.parent) {
  1617. doAction(function(){
  1618. //passes the activating object, not this rule
  1619. self.parent.incrementLimitCounter(o,p);
  1620. });
  1621. }
  1622. })(); break;
  1623. case "decrementParentCounter":(function(){
  1624. var o=obj,p=param, f=found;
  1625. //if (f) p=p.format2(f);
  1626. if (this.parent){
  1627. doAction(function(){
  1628. //passes the activating object, not this rule
  1629. self.parent.decrementLimitCounter(o,p);
  1630. });
  1631. }
  1632. })(); break;
  1633. case "setColor":(function(){
  1634. var c=param;
  1635. var f=found;
  1636. if (f) c=c.format2(f);
  1637. doAction(function(){
  1638. post.setColor(c);
  1639. });
  1640. })(); break;
  1641. case "pinPost":(function(){
  1642. doAction(function(){
  1643. post.pin();
  1644. });
  1645. })(); break;
  1646. case "setAsAccepted":(function(){
  1647. var saveit=param;
  1648. doAction(function(){
  1649. post.accept(saveit);
  1650. });
  1651. })(); break;
  1652. case "setAsFailed":(function(){
  1653. var saveit=param;
  1654. doAction(function(){
  1655. post.fail(saveit);
  1656. });
  1657. })(); break;
  1658. case "setAsExcluded":(function(){
  1659. doAction(function(){
  1660. post.exclude();
  1661. });
  1662. })(); break;
  1663. case "processFirst":(function(){
  1664. doAction(function(){
  1665. post.moveToTop();
  1666. });
  1667. })(); break;
  1668. case "processLast":(function(){
  1669. doAction(function(){
  1670. post.moveToBottom();
  1671. });
  1672. })(); break;
  1673. case "setPriority":(function(){
  1674. var p=param, f=found;
  1675. if (f) p=p.format2(f);
  1676. doAction(function(){
  1677. post.setPriority(p);
  1678. });
  1679. })(); break;
  1680. case "setPriorityApp":(function(){
  1681. var p=param2, a=WM.apps[param]||app, f=found;
  1682. if (f) p=p.format2(f);
  1683. doAction(function(){
  1684. app.setPriority(p);
  1685. });
  1686. })(); break;
  1687. case "removePriorityApp":(function(){
  1688. var p=param2, a=WM.apps[param]||app, f=found;
  1689. if (f) p=p.format2(f);
  1690. doAction(function(){
  1691. app.setPriority(50);
  1692. });
  1693. })(); break;
  1694. case "setPriorityType":(function(){
  1695. var p=param2, a=app, f=found, w=param||post.which;
  1696. if (f) p=p.format2(f);
  1697. doAction(function(){
  1698. app.setPriorityByType(w,p);
  1699. });
  1700. })(); break;
  1701. case "removePriorityType":(function(){
  1702. var a=app, f=found, w=param||post.which;
  1703. if (f) p=p.format2(f);
  1704. doAction(function(){
  1705. app.setPriorityByType(w,50);
  1706. });
  1707. })(); break;
  1708. case "removePriority":(function(){
  1709. doAction(function(){
  1710. post.setPriority(50);
  1711. });
  1712. })(); break;
  1713. case "resetLimit":(function(){
  1714. doAction(function(){
  1715. self.resetLimit({noConfirm:true});
  1716. });
  1717. })(); break;
  1718. case "resetParentLimit":(function(){
  1719. if (this.parent) {
  1720. doAction(function(){
  1721. self.parent.resetLimit({noConfirm:true});
  1722. });
  1723. }
  1724. })(); break;
  1725. case "resetChildrenLimits":(function(){
  1726. doAction(function(){
  1727. self.resetChildrenLimits({noConfirm:true});
  1728. });
  1729. })(); break;
  1730. case "resetBranchLimits":(function(){
  1731. doAction(function(){
  1732. self.resetBranchLimits({noConfirm:true});
  1733. });
  1734. })(); break;
  1735. case "hatch":(function(){
  1736. var o=obj;
  1737. doAction(function(){
  1738. self.hatch(o);
  1739. });
  1740. })(); break;
  1741. case "birth":(function(){
  1742. var o=obj;
  1743. doAction(function(){
  1744. this.birth(o);
  1745. });
  1746. })(); break;
  1747. case "fetchNewer":(function(){
  1748. doAction(function(){
  1749. app.fetchPosts({newer:true,bypassPause:true});
  1750. });
  1751. })(); break;
  1752. case "fetchOlder":(function(){
  1753. doAction(function(){
  1754. app.fetchPosts({older:true,bypassPause:true});
  1755. });
  1756. })(); break;
  1757. case "fetchHours":(function(){
  1758. var p=param, f=found, a=app;
  1759. if (f) p=p.format2(f);
  1760. doAction(function(){
  1761. //var t0=timestamp()/1000; //let the fetch script calc it from the feed
  1762. var t1=Math.round((timeStamp()-(p*hour))/1000);
  1763. //t=t.substr(0,t.length-3);
  1764. log("fetchHours: "+p+" please wait...");
  1765. WM.fetch({bypassPause:true, older:true, targetEdge:t1, currentEdge:Math.round(timeStamp()/1000), apps:app});
  1766. });
  1767. })(); break;
  1768. case "disableRule":(function(){
  1769. doAction(function(){
  1770. self.disable();
  1771. });
  1772. })(); break;
  1773. case "enableRule":(function(){
  1774. doAction(function(){
  1775. self.enable();
  1776. });
  1777. })(); break;
  1778. case "disableChildRules":(function(){
  1779. doAction(function(){
  1780. self.disableChildren();
  1781. });
  1782. })(); break;
  1783. case "enableChildRules":(function(){
  1784. doAction(function(){
  1785. self.enableChildren();
  1786. });
  1787. })(); break;
  1788. case "disableApp":(function(){
  1789. //check for specified app
  1790. var a = WM.apps[param]||app;
  1791. doAction(function(){
  1792. a.disable();
  1793. });
  1794. })(); break;
  1795. case "enableApp":(function(){
  1796. var a = WM.apps[param]||app;
  1797. doAction(function(){
  1798. a.enable();
  1799. });
  1800. })(); break;
  1801. case "forceOpen":(function(){
  1802. doAction(function(){
  1803. post.forceOpen();
  1804. });
  1805. })(); break;
  1806. case "forceOpenFirst":(function(){
  1807. doAction(function(){
  1808. post.forceOpen({first:true});
  1809. });
  1810. })(); break;
  1811. case "emergencyOpen":(function(){
  1812. doAction(function(){
  1813. post.forceOpen({emergency:true});
  1814. });
  1815. })(); break;
  1816. case "setToCollect":(function(){
  1817. doAction(function(){
  1818. post.collect();
  1819. });
  1820. })(); break;
  1821. case "setToCollectPriority1":(function(){
  1822. doAction(function(){
  1823. post.collect();
  1824. post.setPriority(1);
  1825. });
  1826. })(); break;
  1827. case "createTimer":(function(){
  1828. var o=obj, p=param, f=found;
  1829. if (f) p=p.format2(f);
  1830. //allow new time format entry
  1831. //if the calculated time differs from the passed time, then use that calculated time, as long as it doesn't translate to 0
  1832. var t=calcTime(p);
  1833. if (t!=0 && t!=p) p=t;
  1834. //debug.print(["b",param,t,p]);
  1835. doAction(function(){
  1836. self.createTimer(p,o);
  1837. });
  1838. })(); break;
  1839. case "cancelTimer":(function(){
  1840. var o=obj;
  1841. doAction(function(){
  1842. if (o.objType=="rule") self.cancelAllTimers();
  1843. else self.cancelTimer(o);
  1844. });
  1845. })(); break;
  1846. case "createInterval":(function(){
  1847. var o=obj, p=param, f=found;
  1848. if (f) p=p.format2(f);
  1849. //allow new time format entry
  1850. //if the calculated time differs from the passed time, then use that calculated time, as long as it doesn't translate to 0
  1851. var t=calcTime(p);
  1852. if (t!=0 && t!=p) p=t;
  1853. //debug.print(["b",param,t,p]);
  1854. doAction(function(){
  1855. self.createInterval(p,o);
  1856. });
  1857. })(); break;
  1858. case "cancelInterval":(function(){
  1859. var o=obj;
  1860. doAction(function(){
  1861. if (o.objType=="rule") self.cancelAllIntervals();
  1862. else self.cancelInterval(o);
  1863. });
  1864. })(); break;
  1865. case "setWhich":(function(){
  1866. var w=param;
  1867. var f=found;
  1868. if (f) w=w.format2(f);
  1869. doAction(function(){
  1870. post.setWhich(w);
  1871. });
  1872. })(); break;
  1873. case "reIDAll": (function(){
  1874. doAction(function(){
  1875. WM.reIDAll();
  1876. });
  1877. })(); break;
  1878. case "resetAllLimits":(function(){
  1879. doAction(function(){
  1880. WM.rulesManager.resetAllLimits();
  1881. });
  1882. })(); break;
  1883. case "openPostSource":(function(){
  1884. doAction(function(){
  1885. post.openSource();
  1886. });
  1887. })(); break;
  1888. case "emptyAutolikeQueue":(function(){
  1889. doAction(function(){
  1890. WM.emptyAutoLikeQueue();
  1891. });
  1892. })(); break;
  1893. case "setHostOption":(function(){
  1894. var c=param, c2=param2, f=found;
  1895. if (f) c=c.format2(f); //format only param1
  1896. doAction(function(){
  1897. WM.setOpt(c,c2);
  1898. });
  1899. })(); break;
  1900. case "setAppOption":(function(){
  1901. var c=param, c2=param2, f=found, a=app;
  1902. if (f) c=c.format2(f); //format only param1
  1903. doAction(function(){
  1904. WM.setOpt(c,c2,a);
  1905. });
  1906. })(); break;
  1907. case "setAppTab":(function(){
  1908. if (param=="all") {
  1909. doAction(function(){
  1910. //switch to Show All
  1911. WM.console.collectTabControl.selectTab(0);
  1912. });
  1913. } else {
  1914. //check for specified app
  1915. var a = WM.apps[param]||app;
  1916. if (a||null) doAction(function(){
  1917. //switch to associated tab
  1918. click(a.collectionTabNode);
  1919. });
  1920. }})(); break;
  1921. }
  1922. }
  1923. }
  1924. }catch(e){log("WM.rulesManager.Rule.onEvent: "+e);}};
  1925. this.addAction=function(p){try{
  1926. var isNew=!exists(p);
  1927. p=p||{};
  1928. p.parent=this;
  1929. var ret=new WM.rulesManager.RuleAction(p);
  1930. this.actions.push(ret);
  1931. if (isNew) WM.rulesManager.saveRules();
  1932. }catch(e){log("WM.rulesManager.Rule.addAction: "+e);}};
  1933.  
  1934. this.addValidator=function(p){try{
  1935. var isNew=!exists(p);
  1936. p=p||{};
  1937. p.parent=this;
  1938. var ret=new WM.rulesManager.RuleValidator(p);
  1939. this.validators.push(ret);
  1940. if (isNew) WM.rulesManager.saveRules();
  1941. }catch(e){log("WM.rulesManager.Rule.addValidator: "+e);}};
  1942. this.addChild=function(p){try{
  1943. var isNew=!exists(p);
  1944. p=p||{};
  1945. p.parent=this;
  1946. p.isChild=true;
  1947. var rule=new WM.rulesManager.Rule(p);
  1948. this.kids.push(rule);
  1949. if (isNew) WM.rulesManager.saveRules();
  1950. }catch(e){log("WM.rulesManager.Rule.addChild: "+e);}};
  1951.  
  1952. this.addEgg=function(p){try{
  1953. var isNew=!exists(p);
  1954. p=p||{};
  1955. p.parent=this;
  1956. p.isEgg=true;
  1957. var rule=new WM.rulesManager.Rule(p);
  1958. this.eggs.push(rule);
  1959. if (isNew) WM.rulesManager.saveRules();
  1960. }catch(e){log("WM.rulesManager.Rule.addEgg: "+e);}};
  1961. //move eggs to parent node and destroy this node
  1962. this.hatch=function(obj){try{
  1963. var ask=WM.opts.rulesConfirmHatch
  1964. if (!ask || (ask && confirm("Hatch egg child and remove current rule and all its children?")) ) {
  1965. this.onEvent("onHatch",obj||this);
  1966. for (var e=0,egg; (egg=this.eggs[e]); e++){
  1967. egg.moveUpLevel();
  1968. }
  1969. this.remove(true); //with noConfirm
  1970. }
  1971. }catch(e){log("WM.rulesManager.Rule.hatch: "+e);}};
  1972.  
  1973. //clone eggs to parent node
  1974. this.birth=function(obj){try{
  1975. this.onEvent("onBirth",obj||this);
  1976. for (var e=0,egg; (egg=this.eggs[e]); e++){
  1977. var cloneRule=egg.saveableData;
  1978. if (this.isChild) this.parent.addChild(cloneRule);
  1979. else WM.rulesManager.newRule(cloneRule);
  1980. }
  1981. }catch(e){log("WM.rulesManager.Rule.birth: "+e);}};
  1982.  
  1983. //self rule button clicked
  1984. this.ruleButtonClicked=function(obj){try{
  1985. this.onEvent("onRuleButtonClicked",obj||this);
  1986. }catch(e){log("WM.rulesManager.Rule.ruleButtonClicked: "+e);}};
  1987.  
  1988. this.toggleContent=function(){try{
  1989. this.expanded=!this.expanded;
  1990. var btnSize=WM.opts.littleButtonSize;
  1991. with (this.contentNode)
  1992. className=className.swapWordB(this.expanded,"expanded","collapsed");
  1993. with (this.toggleImgNode)
  1994. className=className.swapWordB(this.expanded,"treeCollapse"+btnSize,"treeExpand"+btnSize);
  1995. WM.rulesManager.saveRules();
  1996. }catch(e){log("WM.rulesManager.Rule.toggleContent: "+e);}};
  1997.  
  1998. this.populateBonusList=function(){try{
  1999. var node=this.bonusDropDown;
  2000. var bonuses=[];
  2001. //get the list of accept texts for this app
  2002. if (this.appID!="") {
  2003. if (this.appID=="* All") {
  2004. //populate list with bonuses from ALL docked sidekicks
  2005. } else bonuses = mergeJSON(WM.apps[this.appID].accText,WM.apps[this.appID].userDefinedTypes);
  2006. }
  2007. bonuses["dynamic"]="* Dynamic grab";
  2008. bonuses["none"]="* None";
  2009. bonuses["wishlist"]="* Flaged as Wishlist";
  2010. bonuses["exclude"]="* Excluded types";
  2011. bonuses["send"]="* Send Unknown";
  2012. bonuses["doUnknown"]="* Get Unknown";
  2013. bonuses["*"]="* All"; //perform rule on ALL bonus types for this app
  2014.  
  2015. //sort by display text
  2016. bonuses=sortCollection(bonuses,"value");
  2017.  
  2018. //add each element to the dropdown
  2019. var elem;
  2020. node.innerHTML=""; //wipe previous list
  2021. for (var i in bonuses) {
  2022. var showI=i.removePrefix(this.appID);
  2023. node.appendChild(elem=createElement("option",{textContent:((bonuses[i].startsWith("*"))?"":((showI.startsWith("send"))?"Send ":"Get "))+bonuses[i], value:showI}));
  2024. if (this.bonus== showI) elem.selected = true;
  2025. }
  2026. }catch(e){log("WM.rulesManager.Rule.populateBonusList: "+e);}};
  2027.  
  2028. //draw to priority/rule manager or to the parent node's kids or eggs section
  2029. try{(((this.parent)?this.parent[(this.isChild)?"kidsNode":"eggsNode"]:null)||$("wmPriorityBuilder")).appendChild(
  2030. this.node=createElement("div",{className:"listItem "+((this.enabled)?"enabled":"disabled")},[
  2031. createElement("div",{className:"line"},[
  2032. createElement("div",{className:"littleButton",title:"Toggle Content",onclick:function(){self.toggleContent();}},[
  2033. this.toggleImgNode=createElement("img",{className:"resourceIcon "+(this.expanded?"treeCollapse"+WM.opts.littleButtonSize:"treeExpand"+WM.opts.littleButtonSize)}),
  2034. ]),
  2035. this.toggleNode=createElement("input",{type:"checkbox",checked:this.enabled,onchange:function(){
  2036. self.enabled=this.checked;
  2037. with (self.node) className=className.toggleWordB(!this.checked,"disabled");
  2038. WM.rulesManager.saveRules();
  2039. }}),
  2040. createElement("label",{textContent:"Title:"}),
  2041. this.titleNode=createElement("input",{className:"w400",value:(this.title||""), onchange:function(){self.title=this.value; WM.rulesManager.saveRules();}}),
  2042. //toolbox
  2043. createElement("div",{className:"littleButton oddOrange", title:"Remove Rule"},[
  2044. createElement("img",{className:"resourceIcon trash"+WM.opts.littleButtonSize,onclick:function(){self.remove();}})]),
  2045. createElement("div",{className:"littleButton oddBlue",title:"Hatch Egg Children"},[
  2046. createElement("img",{className:"resourceIcon hatch"+WM.opts.littleButtonSize,onclick:function(){self.hatch();}})]),
  2047. createElement("div",{className:"littleButton oddBlue", title:"Reset Limit Counter"},[
  2048. createElement("img",{className:"resourceIcon reset"+WM.opts.littleButtonSize,onclick:function(){self.resetLimit();}})]),
  2049. createElement("div",{className:"littleButton oddBlue", title:"Clone Rule"},[
  2050. createElement("img",{className:"resourceIcon clone"+WM.opts.littleButtonSize,onclick:function(){self.clone();}})]),
  2051. createElement("div",{className:"littleButton oddBlue", title:"Birth Egg Children"},[
  2052. createElement("img",{className:"resourceIcon birth"+WM.opts.littleButtonSize,onclick:function(){self.birth();}})]),
  2053. createElement("div",{className:"littleButton oddGreen", title:"Move Up"},[
  2054. createElement("img",{className:"resourceIcon arrowUp"+WM.opts.littleButtonSize,onclick:function(){self.moveUp();}})]),
  2055. createElement("div",{className:"littleButton oddOrange", title:"Move Down"},[
  2056. createElement("img",{className:"resourceIcon arrowDown"+WM.opts.littleButtonSize,onclick:function(){self.moveDown();}})]),
  2057. createElement("div",{className:"littleButton oddGreen", title:"Move Up Level"},[
  2058. createElement("img",{className:"resourceIcon moveUpLevelLeft"+WM.opts.littleButtonSize,onclick:function(){self.moveUpLevel();}})]),
  2059. createElement("div",{className:"littleButton oddOrange", title:"Move Down Level"},[
  2060. createElement("img",{className:"resourceIcon moveInLevel"+WM.opts.littleButtonSize,onclick:function(){self.moveDownLevel();}})]),
  2061. createElement("div",{className:"littleButton oddBlue", title:"Show Source"},[
  2062. createElement("img",{className:"resourceIcon object"+WM.opts.littleButtonSize,onclick:function(){promptText(JSON.stringify(self.saveableData),true);}})]),
  2063.  
  2064. createElement("div",{className:"indent littleButton "+((this.isGlobal)?"oddOrange":"oddGreen"), title:((this.isGlobal)?"Disable Profile Sharing":"Share With Other Profiles")},[
  2065. this.toggleGlobalButton=createElement("img",{className:"resourceIcon "+((this.isGlobal)?"removeGlobal":"addGlobal")+WM.opts.littleButtonSize,onclick:function(){self.isGlobal=!self.isGlobal; WM.rulesManager.saveRules();}})]),
  2066. ]),
  2067. this.contentNode=createElement("div",{className:"subsection "+(this.expanded?"expanded":"collapsed")},[
  2068. (this.basedOn)?createElement("div",{className:"line"},[
  2069. createElement("label",{textContent:"This rule is linked to a post: ",title:"This rule is linked to a post. Validators can draw information from that post so you can easily capture similar posts just by editing the captured texts to suit your needs. Post linking is not carried from session to session."}),
  2070. this.basedOnNode=createElement("span",{textContent:this.basedOn.id}),
  2071. ]):null,
  2072. createElement("div",{className:"line"},[
  2073. createElement("label",{textContent:"Limit:"}),
  2074. this.limitNode=createElement("input",{value:(this.limit||0), onchange:function(){self.limit=this.value;WM.rulesManager.saveRules();}}),
  2075. ]),
  2076. createElement("div",{className:"line"},[
  2077. createElement("label",{textContent:"Counter:"}),
  2078. this.limitCounterNode=createElement("input",{value:(this.limitCount||0), onchange:function(){self.limitCount=this.value;WM.rulesManager.saveRules();}}),
  2079. ]),
  2080. this.ruleButtonHousingNode=createElement("div",{className:"line", style:(this.usesRuleButton())?"":"display:none;"},[
  2081. createElement("label",{textContent:"Rule Button:"}),
  2082. this.ruleButtonNode=createElement("button",{type:"button", textContent:"onRuleButtonClicked()", onclick:function(){self.ruleButtonClicked();}}),
  2083. ]),
  2084. //validation subbox
  2085. createElement("div",{className:"line"},[
  2086. createElement("label",{textContent:"For Activating Objects:",title:"These validators attempt to match a post or other activating object, such as feed, feed filter, app, or this rule. All activators that match here then have the following actions performed at certain events."}),
  2087. createElement("div",{className:"littleButton oddGreen",onclick:function(){self.addValidator();},title:"Add Validator"},[
  2088. createElement("img",{className:"resourceIcon plus"+WM.opts.littleButtonSize}),
  2089. ]),
  2090. this.validationNode=createElement("div",{className:"subsection"}),
  2091. ]),
  2092. //actions subbox
  2093. createElement("div",{className:"line"},[
  2094. createElement("label",{textContent:"Do Actions:",title:"Actions to perform on matching posts."}),
  2095. createElement("div",{className:"littleButton oddGreen",onclick:function(){self.addAction();},title:"Add Action"},[
  2096. createElement("img",{className:"resourceIcon plus"+WM.opts.littleButtonSize}),
  2097. ]),
  2098. this.actionsNode=createElement("div",{className:"subsection"}),
  2099. ]),
  2100. //kids subbox
  2101. createElement("div",{className:"line"},[
  2102. createElement("label",{textContent:"Child Rules:",title:"Child rules are nested rules which are applied to matching posts at the same time the parent rule is applied. Child rules can have different validators, but will only activate if the parent validators have already matched a post."}),
  2103. createElement("div",{className:"littleButton oddGreen",onclick:function(){self.addChild();},title:"Add Child"},[
  2104. createElement("img",{className:"resourceIcon plus"+WM.opts.littleButtonSize}),
  2105. ]),
  2106. this.kidsNode=createElement("div",{className:"subsection"}),
  2107. ]),
  2108. //egg kids subbox
  2109. createElement("div",{className:"line"},[
  2110. createElement("label",{textContent:"Egg Rules:", title:"Eggs are potential future rules. When 'hatched', these eggs take the place of the parent rule. The parent rule and its normal children are destroyed."}),
  2111. createElement("div",{className:"littleButton oddGreen",onclick:function(){self.addEgg();},title:"Add Egg"},[
  2112. createElement("img",{className:"resourceIcon plus"+WM.opts.littleButtonSize}),
  2113. ]),
  2114. this.eggsNode=createElement("div",{className:"subsection"}),
  2115. ]),
  2116. ]),
  2117. ])
  2118. );}catch(e){log("WM.rulesManager.Rule.init.drawRule: "+e);}
  2119.  
  2120. //list the actions for this rule
  2121. if (isArrayAndNotEmpty(params.actions)) for (var i=0,action; (action=params.actions[i]); i++) {
  2122. this.addAction(action);
  2123. }
  2124. //list the validators for this rule
  2125. if (isArrayAndNotEmpty(params.validators)) for (var i=0,validator; (validator=params.validators[i]); i++) {
  2126. this.addValidator(validator);
  2127. }
  2128. //list the kids for this rule
  2129. if (isArrayAndNotEmpty(params.kids)) for (var i=0,kid; (kid=params.kids[i]); i++) {
  2130. this.addChild(kid);
  2131. }
  2132. //list the egg kids for this rule
  2133. if (isArrayAndNotEmpty(params.eggs)) for (var i=0,egg; (egg=params.eggs[i]); i++) {
  2134. this.addEgg(egg);
  2135. }
  2136. //create cleanup function
  2137. this.cleanup=function(){try{
  2138. for (var t in this.timers) {
  2139. window.clearTimeout(this.timers[t]);
  2140. }
  2141. for (var i in this.intervals) {
  2142. window.clearInterval(this.intervals[i]);
  2143. }
  2144. var self=this;
  2145. removeEventListener("beforeunload",self.cleanup,false);
  2146. }catch(e){log("WM.rulesManager.Rule.cleanup: "+e);}};
  2147. addEventListener("beforeunload",self.cleanup,false);
  2148. this.onEvent("onRuleCreated");
  2149. return self;
  2150.  
  2151. }catch(e){log("WM.rulesManager.Rule.init: "+e);}};
  2152.  
  2153. })();