wiring

DW screening

Sep 19th, 2012
97
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*
  2.  * Thanks to Foxfirefey for Dreamwidth Dynamic Reading Page Expand/Collapse
  3.  * on which this is somewhat based.
  4.  */
  5.  
  6. // ==UserScript==
  7. // @name       DW Entry Screening
  8. // @namespace  wiring.io
  9. // @version    0.1
  10. // @description  Cuts entries based on keywords/journals (present or absent). Works with Core2 styles.
  11. // @include    http://*.dreamwidth.org/*
  12. // @exclude    http://www.dreamwidth.org/*
  13. // @exclude    http://*.dreamwidth.org/manage*
  14. // @exclude    http://*.dreamwidth.org/inbox*
  15. // @exclude    http://*.dreamwidth.org/update*
  16. // @exclude    http://*.dreamwidth.org/profile*
  17. // @exclude    http://*.dreamwidth.org/calendar*
  18. // @exclude    http://*.dreamwidth.org/tag*
  19. // @exclude    http://*.dreamwidth.org/*.html*
  20. // @copyright  2011+, Lian (http://wiring.io/)
  21. // @require    http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js
  22. // ==/UserScript==
  23.  
  24. /**
  25.  * TODO/Known Bugs:
  26.  * - UI/UX improvements?
  27.  * - multiple filtering-- order of action: whitelist > blacklist, don't cut/collapse if already hidden, etc
  28.  * - BUG: fix cut text display so doesn't say entry contains if mode is set to include
  29.  */
  30.  
  31. (function(){
  32.     /* ENUMS ---------------*/
  33.  
  34.     var NodeState = {
  35.         EXPANDED : { name: "expanded", value: 0 },
  36.         COLLAPSED : { name: "collapsed", value: 1 },
  37.         HIDDEN : { name: "hidden", value: 2 },
  38.         CUT : { name: "cut", value: 3 }
  39.     };
  40.  
  41.     var ListType = {
  42.         KEYWORDS : { name: "Keywords", value: 0 },
  43.         USERS : { name: "Users", value: 1 },
  44.         COMMUNITIES : { name: "Communities", value: 2 }
  45.     };
  46.  
  47.     var FilterType = {
  48.         INCLUDE : { name: "Include", value: 0 },
  49.         EXCLUDE : { name: "Exclude", value: 1 }
  50.     };
  51.  
  52.     /* SETTINGS -----------*/
  53.  
  54.     // default state of all entries-- if you want to change this, you can start
  55.     // with all entries collapsed/hidden/cut and whitelist the ones you want visible/open
  56.     var globalState = NodeState.EXPANDED;
  57.  
  58.     // put classes of things you'd like to be hidden in 'collapsed' state here
  59.     var collapseables = new Array("entry-content", "metadata", "userpic", "entry-management-links");
  60.  
  61.     // fill in items with words/usernames you want to filter by
  62.     // if you use EXCLUDE, the state will apply to whatever you list in items
  63.     // if you use INCLUDE, the state will apply to everything BUT those items
  64.     // you can add more filters or remove the examples you're not using
  65.     var filters = [
  66.         {
  67.             items: ["anything_you", "want_warning_of"],
  68.             listType: ListType.KEYWORDS,
  69.             filterType: FilterType.EXCLUDE,
  70.             state: NodeState.CUT
  71.         },
  72.         {
  73.             items: ["anything_you", "don't_want_to_see"],
  74.             listType: ListType.KEYWORDS,
  75.             filterType: FilterType.EXCLUDE,
  76.             state: NodeState.COLLAPSE
  77.         },
  78.         {
  79.             items: ["individual_name"],
  80.             listType: ListType.USERS,
  81.             filterType: FilterType.EXCLUDE,
  82.             state: NodeState.COLLAPSED
  83.         },
  84.         {
  85.             items: ["community_name"],
  86.             listType: ListType.COMMUNITIES,
  87.             filterType: FilterType.EXCLUDE,
  88.             state: NodeState.COLLAPSED
  89.         }
  90.     ];
  91.  
  92.     /* MAIN ------------*/
  93.  
  94.     var Utilities = function() {
  95.     };
  96.  
  97.     Utilities.prototype = {
  98.  
  99.         getNodeState:
  100.         function (value) {
  101.             switch(value) {
  102.                 case 0:
  103.                     return NodeState.EXPANDED;
  104.                 case 1:
  105.                     return NodeState.COLLAPSED;
  106.                 case 2:
  107.                     return NodeState.HIDDEN;
  108.                 case 3:
  109.                     return NodeState.CUT;
  110.             }
  111.         }
  112.     };
  113.  
  114.     // XPath Constructor
  115.     var XPath = function() {
  116.         this._path = "//div[@class='entry-content']";
  117.         this._resultType = XPathResult.UNORDERED_NODE_ITERATOR_TYPE;
  118.         this.init();
  119.     };
  120.  
  121.     XPath.prototype = {
  122.  
  123.         _path: null,
  124.         _resultType: null,
  125.         _result: null,
  126.         _resultArray: null,
  127.        
  128.         init:
  129.         function () {
  130.             this._result = this.getResult(this._path, document, this._resultType);
  131.             this._resultArray = this.getResultArray(this._result);
  132.         },
  133.  
  134.         getResult:
  135.         function (path, scope, resultType) {
  136.             return document.evaluate(path, scope, null, resultType, null);
  137.         },
  138.  
  139.         getResultArray:
  140.         function (result) {
  141.             var temp = result.iterateNext(),
  142.                 array = [];
  143.             while(temp) {
  144.                 array.push(temp);
  145.                 temp = result.iterateNext();
  146.             }
  147.             return array;
  148.         }
  149.     };
  150.  
  151.     // Generic Filter object
  152.     var Filter = function() {
  153.         this._xpath = new XPath();
  154.         this._utils = new Utilities();
  155.         this.createEntryList();
  156.     };
  157.  
  158.     Filter.prototype = {
  159.      
  160.         _xpath: null,
  161.         _entryList: null,
  162.         _savedEntryList: null,
  163.  
  164.         createEntryList:
  165.         function () {
  166.             this._entryList = {};
  167.             this._savedEntryList = {};
  168.  
  169.             for (var i = 0; i < this._xpath._resultArray.length; i++) {
  170.                 var entry = new Entry(this._xpath._resultArray[i]);
  171.                 var storedValue = GM_getValue(entry._uid);
  172.  
  173.                 if (storedValue) {
  174.                     entry.setState(this._utils.getNodeState(storedValue.value));
  175.                     this._savedEntryList[entry._uid] = entry;
  176.                 } else {
  177.                     entry.setState(globalState);
  178.                 }
  179.                 this._entryList[entry._uid] = entry;
  180.             }
  181.         },
  182.  
  183.         match:
  184.         function (listType, type, nodeState, items) {
  185.             var matches = {};
  186.  
  187.             for (var x in this._entryList) {
  188.                 var entry = this._entryList[x];
  189.                 var matchPattern = null;
  190.                 var entryMatches = [];
  191.                
  192.                 for (var j = 0, len = items.length; j < len; j++) {    
  193.                    
  194.                     switch(listType)
  195.                     {
  196.                         case "Keywords":
  197.                             matchPattern = entry._innerHtml.match(new RegExp(items[j], "i"));
  198.                             if (matchPattern) { console.log("keyword: " + items[j]+ " (" + entry._uid + ")");}
  199.                             break;
  200.  
  201.                         case "Users":
  202.                             matchPattern = entry.matchPoster(items[j]);
  203.                             if (matchPattern) { console.log("user: " + items[j]+ " (" + entry._uid + ")");}
  204.                             break;
  205.  
  206.                         case "Communities":
  207.                             matchPattern = entry.matchJournal(items[j]);
  208.                             if (matchPattern) { console.log("comm: " + items[j] + " (" + entry._uid + ")");}
  209.                             break;
  210.                     }
  211.                     matchPattern = (type == FilterType.EXCLUDE) ? matchPattern : !matchPattern;
  212.  
  213.                     if(matchPattern) {
  214.                         entry._matchedArray.push(items[j]);
  215.                         entryMatches.push(items[j]); // todo: do I need both of these?
  216.                     }
  217.                        
  218.                     if ((entryMatches.length > 0) && (j == len-1) && !matches[entry._uid]) {
  219.                         entry.setState(nodeState);
  220.                         matches[entry._uid] = entry;
  221.                         console.log(entry);
  222.                     }      
  223.                 }
  224.             }
  225.  
  226.             return matches;
  227.         }
  228.     };
  229.  
  230.     // Entry constructor
  231.     var Entry = function(content, state) {
  232.         this._content = content;
  233.         this._state = state;
  234.  
  235.         this._node = this.getEntry();
  236.         this._innerHtml = this._content.innerHTML;
  237.         this._url = this.getEntryLink();
  238.  
  239.         this._matchedArray = [];
  240.         this._matchedString = "";
  241.  
  242.         this._poster = this.getPoster();
  243.         this._journal = this.getJournal();
  244.         this._uid = this.getEntryId();
  245.  
  246.         this._hasLinks = this.insertInteraction();
  247.     };
  248.  
  249.     Entry.prototype = {
  250.  
  251.         _content: null,
  252.         _node: null,
  253.         _state: null,
  254.         _url: null,
  255.         _matchedArray: null,
  256.         _matchedString: null,
  257.         _uid: null,
  258.         _innerHtml: null,
  259.         _poster: null,
  260.         _journal: null,
  261.         _hasLinks: false,
  262.  
  263.         setMatchedString:
  264.         function() {
  265.             for (var i = 0; i < this._matchedArray.length; i++) {
  266.                 var connector = "";
  267.                 if (i !== 0) { connector = ", "; }
  268.                 this._matchedString += connector + this._matchedArray[i];
  269.             }
  270.         },
  271.  
  272.         getEntry:
  273.         function() {
  274.             return $(this._content).parents(".entry-wrapper");
  275.         },
  276.  
  277.         matchPoster:
  278.         function(poster) {
  279.             return this._poster == poster;
  280.             //return this._poster.match(new RegExp(poster + "$", "i"));
  281.         },
  282.  
  283.         matchJournal:
  284.         function(journal) {
  285.             return this._journal == journal;
  286.             //return this._journal.match(new RegExp(journal + "$", "i"));
  287.         },
  288.  
  289.         getPoster:
  290.         function() {
  291.             var classes = this._node.attr("class");
  292.             var regex = new RegExp("poster-[a-z]*", "i");
  293.  
  294.             return classes.match(regex)[0].split("-")[1];
  295.         },
  296.  
  297.         getJournal:
  298.         function() {
  299.             var classes = this._node.attr("class");
  300.             console.log(this._journal);
  301.             var regex = new RegExp(/journal-[a-z0-9\_]+/g);
  302.             var jMatches = classes.match(regex);
  303.            
  304.             for (var i = 0, len = jMatches.length; i < len; i++) {
  305.                 if (jMatches[i] !== "journal-type") {
  306.                     return jMatches[i].split("-")[1];
  307.                 }
  308.             }
  309.  
  310.             return false;
  311.         },
  312.  
  313.         getEntryLink:
  314.         function() {
  315.             return this._node.find(".entry-permalink a").attr("href");
  316.         },
  317.  
  318.         getEntryId:
  319.         function() {
  320.             return this._poster + this._node.attr("id").match(new RegExp("[0-9]*$"))[0];
  321.         },
  322.  
  323.         setState:
  324.         function(state) {
  325.             switch(state){
  326.                 case NodeState.CUT:
  327.                     this.cut();
  328.                     break;
  329.  
  330.                 case NodeState.COLLAPSED:
  331.                     this.collapse();
  332.                     break;
  333.  
  334.                 case NodeState.HIDDEN:
  335.                     this.hide();
  336.                     break;
  337.  
  338.                 default:
  339.                     this.expand();
  340.                     break;
  341.             }
  342.         },
  343.  
  344.         // sets up the links on the title of the entry to manually collapse/expand/hide an entry: mostly from foxfirefey's code
  345.         insertInteraction:
  346.         function() {
  347.             var that = this;
  348.             if (!this._hasLinks) {
  349.                 // This is so people can customize it to be [] or {}
  350.                 var link_left_bracket = "[";
  351.                 var link_right_bracket = "]";
  352.  
  353.                 // This is so people can customize to say "collapse" or "expand" or "delete"
  354.                 var link_collapse = "-";
  355.                 var link_expand = "+";
  356.                 var link_hide = "x";
  357.  
  358.                 // Create a span containing the collapse and hide link
  359.                 // foxfirefey's code:
  360.                 var span, collapse_link, collapse_link_text, hide_link, state_link_text;
  361.                 var link_span = document.createElement("span");
  362.                 link_span.className = "entryCollapseState";
  363.                 link_span.id = "entryCollapseState-" + this._uid;
  364.                
  365.                 var state_link = document.createElement("a");
  366.                 state_link.href = "#";
  367.                
  368.                 // Figure out which action link to create -- collapse or expand
  369.                 if ( this._state == NodeState.COLLAPSED ) {
  370.                     state_link.addEventListener('click', function(event) {
  371.                         event.stopPropagation(); event.preventDefault();
  372.                         GM_deleteValue(that._uid);
  373.                         that.expand();
  374.                         }, true);
  375.                     state_link_text = link_expand;
  376.                     state_link.className = "entry-expand-link";
  377.                     state_link.title = "Expand entry";
  378.                 }
  379.                 else {
  380.                     state_link.addEventListener('click', function(event) {
  381.                         GM_setValue(that._uid, NodeState.COLLAPSED);
  382.                         that.collapse(); event.stopPropagation(); event.preventDefault(); }, true);
  383.                     state_link_text = link_collapse;
  384.                     state_link.className = "entry-collapse-link";
  385.                     state_link.title = "Collapse entry";
  386.                 }
  387.                
  388.                 state_link.appendChild( document.createTextNode(state_link_text) );
  389.                
  390.                 link_span.appendChild( document.createTextNode(" "));
  391.                 link_span.appendChild( document.createTextNode(link_left_bracket) );
  392.                 link_span.appendChild( state_link );
  393.                 link_span.appendChild( document.createTextNode(link_right_bracket ));
  394.                
  395.                 hide_link = document.createElement("a");
  396.                 hide_link.href = "#";
  397.                 hide_link.addEventListener('click', function(event) {
  398.                     GM_setValue(that._uid, NodeState.HIDDEN);
  399.                     that.hide(); event.stopPropagation(); event.preventDefault(); }, true);
  400.                 hide_link.title = "Hide entry";
  401.                 hide_link.appendChild( document.createTextNode(link_hide) );
  402.                
  403.                 link_span.appendChild( document.createTextNode(" "));
  404.                 link_span.appendChild( document.createTextNode(link_left_bracket) );
  405.                 link_span.appendChild( hide_link );
  406.                 link_span.appendChild( document.createTextNode(link_right_bracket ));
  407.                
  408.                 this._node.find(".entry-title").append( link_span );
  409.                 this._hasLinks = true;
  410.             } else {
  411.                 this._node.find("span.entryCollapseState").remove();
  412.                 this._hasLinks = false;
  413.                 this.insertInteraction();
  414.             }
  415.  
  416.             return this._hasLinks;
  417.         },
  418.  
  419.         // uses a DW-esque cut to cut an entry
  420.         // can't use an actual dw cut atm ($.dw.cuttag)
  421.         cut:
  422.         function() {
  423.             this._state = NodeState.CUT;
  424.             this.setMatchedString();
  425.             this._content.innerHTML = '<span class="cuttag_container"><span style="display: inline;" id="span-cuttag_autogen_'+this._uid+'" class="cuttag"><a href="#" id="cuttag_autogen_'+this._uid+'" class="cuttag-action cuttag-action-before"><img style="border:0;" aria-controls="div-cuttag_autogen_'+this._uid+'" src="http://www.dreamwidth.org/img/collapse.gif" alt="Expand" title="Expand"></a></span><b>( <a href="'+this._url+'">entry text contains: '+ this._matchedString +'</a> )</b><div class="keyword-hidden-entry" style="display: none;">' + this._content.innerHTML + '</div><div style="display: none;" id="div-cuttag_autogen_'+this._uid+'" aria-live="assertive"></div></span>';
  426.  
  427.             this.initCut();
  428.         },
  429.  
  430.         // make the button clickable
  431.         initCut:
  432.         function() {
  433.             var linkButton = $(this._content).find("a.cuttag-action-before");
  434.             var hiddenContent = $(this._content).find("div.keyword-hidden-entry");
  435.  
  436.             linkButton.bind("click", function(e) {
  437.                 e.preventDefault();
  438.                 var isHidden = ($(this._content).find(".keyword-hidden-entry:hidden").length > 0);
  439.                 var buttonState = isHidden ? "expand" : "collapse";
  440.                 linkButton.find("img").attr("src", "http://www.dreamwidth.org/img/" + buttonState + ".gif");
  441.                 hiddenContent.toggle();
  442.             });
  443.         },
  444.  
  445.         // completely hides an entry from view on the page
  446.         hide:
  447.         function() {
  448.             this._state = NodeState.HIDDEN;
  449.             this._node.hide();
  450.         },
  451.  
  452.         // hides "collapseable" values in an entry instead of making a cut-- smaller footprint
  453.         collapse:
  454.         function() {
  455.             this._state = NodeState.COLLAPSED;
  456.  
  457.             for ( var collapse_index in collapseables ) {
  458.                 var collapse = this._node.find("." + collapseables[collapse_index]);
  459.  
  460.                 if (collapse.length > 0) {
  461.                     collapse.hide();
  462.                 }
  463.             }
  464.             this.insertInteraction();
  465.         },
  466.  
  467.         // expand collapsed entries
  468.         expand:
  469.         function() {
  470.             this._state = NodeState.EXPANDED;
  471.  
  472.             for ( var expand_index in collapseables ) {
  473.                 var expand = this._node.find("." + collapseables[expand_index]);
  474.  
  475.                 if( expand.length > 0 ) {
  476.                     expand[0].style.display = '';
  477.                 }
  478.             }
  479.  
  480.             this.insertInteraction();
  481.         }
  482.     };
  483.  
  484.     function clearCollapsed() {
  485.         var listVals = GM_listValues();
  486.  
  487.         for (var i = 0, len = listVals.length; i < len; i++) {
  488.             var val = listVals[i];
  489.             var state = GM_getValue(val);
  490.  
  491.             if (state.value == NodeState.COLLAPSED.value) {
  492.                 GM_deleteValue(val);
  493.             }
  494.         }
  495.         GM_notification("Cleared collapsed. Please refresh to see changes.");
  496.     }
  497.  
  498.     function clearHidden() {
  499.         var listVals = GM_listValues();
  500.  
  501.         for (var i = 0, len = listVals.length; i < len; i++) {
  502.             var val = listVals[i];
  503.             var state = GM_getValue(val);
  504.  
  505.             if (state.value == NodeState.HIDDEN.value) {
  506.                 GM_deleteValue(val);
  507.             }
  508.         }
  509.         GM_notification("Cleared hidden. Please refresh to see changes.");
  510.     }
  511.  
  512.     function clearAll() {
  513.         var listVals = GM_listValues();
  514.  
  515.         for (var i = 0, len = listVals.length; i < len; i++) {
  516.             var val = listVals[i];
  517.             GM_deleteValue(val);
  518.         }
  519.         GM_notification("Cleared all. Please refresh to see changes.");
  520.     }
  521.  
  522.     // Object to actually instantiate and pull everything together
  523.     var Init = function() {
  524.        
  525.         var filter = new Filter();
  526.  
  527.         GM_registerMenuCommand("Clear stored hidden entries.", clearHidden);
  528.         GM_registerMenuCommand("Clear stored collapsed entries.", clearCollapsed);
  529.         GM_registerMenuCommand("Clear all stored entries.", clearAll);
  530.  
  531.         for (var f = 0, len = filters.length; f < len; f++) {
  532.             var current = filters[f];
  533.             filter.match(current.listType.name, current.filterType, current.state, current.items);
  534.         }
  535.     };
  536.  
  537.     Init();
  538. })();
Advertisement
Add Comment
Please, Sign In to add comment