Feedly Search

Add search box on Feedly

  1. // ==UserScript==
  2. // @name Feedly Search
  3. // @namespace http://nodaguti.usamimi.info/
  4. // @description Add search box on Feedly
  5. // @include http://feedly.com/*
  6. // @include https://feedly.com/*
  7. // @version 0.1
  8. // @author nodaguti
  9. // @license MIT License
  10. // @grant GM_log
  11. // @grant GM_addStyle
  12. // @grant unsafeWindow
  13. // ==/UserScript==
  14.  
  15. (function(window, document){
  16.  
  17. var DB_NAME = 'feedly-search-entries';
  18. var DB_VERSION = 1;
  19. var DB_STORE_NAME = 'entries';
  20. var DB = null;
  21.  
  22. var timeline = document.getElementById('box');
  23.  
  24. var SEARCH_ICON = "data:image/png;base64,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";
  25.  
  26. var STYLE = "\
  27. .hidden{\
  28. display: none !important;\
  29. }\
  30. \
  31. .invisible{\
  32. visibility: hidden !important;\
  33. }\
  34. \
  35. #feedlySearchBoxContainer{\
  36. position: absolute;\
  37. top: 0;\
  38. right: 0;\
  39. z-index: 99999;\
  40. color: rgb(102, 102, 102);\
  41. background-color: rgb(245, 245, 245);\
  42. box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.2);\
  43. border: 1px solid rgb(190, 190, 190);\
  44. padding: 1em;\
  45. }\
  46. \
  47. #feedlySearchBoxContainer input[type='text']{\
  48. border: 1px #bfbfbf solid;\
  49. border-radius: 3px;\
  50. color: #444;\
  51. padding: 3px;\
  52. }\
  53. #feedlySearchBoxContainer button,\
  54. #feedlySearchBoxContainer input[type='checkbox'],\
  55. #feedlySearchBoxContainer select{\
  56. background-image: linear-gradient(to bottom, #ededed, #ededed 38%, #dedede);\
  57. border: 1px #ccc solid;\
  58. border-radius: 3px;\
  59. box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1);\
  60. color: #444;\
  61. text-shadow: 0 1px 0 #f0f0f0;\
  62. padding: 2px 10px;\
  63. margin: 0 5px;\
  64. }\
  65. #feedlySearchBoxContainer button::-moz-focus-inner{\
  66. border: 0 !important;\
  67. padding: 0 !important;\
  68. }\
  69. #feedlySearchBoxContainer form{\
  70. margin: 0;\
  71. }\
  72. \
  73. #feedlySearchOptions{\
  74. margin-top: 5px;\
  75. font-size: 90%;\
  76. color: #333;\
  77. }\
  78. \
  79. #feedlySearchLoading{\
  80. width: 12px;\
  81. height: 12px;\
  82. border-radius: 50%;\
  83. border: 3px solid #333;\
  84. border-right-color: transparent;\
  85. animation: spin 1s linear infinite;\
  86. display: inline-block;\
  87. vertical-align: middle;\
  88. margin-left: 0.5em;\
  89. }\
  90. \
  91. @keyframes spin{\
  92. 0% { transform: rotate(0deg); opacity: 0.2; }\
  93. 50% { transform: rotate(180deg); opacity: 1.0; }\
  94. 100% { transform: rotate(360deg); opacity: 0.2; }\
  95. }\
  96. \
  97. #feedlySearchHitList{\
  98. list-style: none;\
  99. margin-top: 3em;\
  100. }\
  101. #feedlySearchHitList > li{\
  102. margin: 1em 0;\
  103. list-style: none;\
  104. }\
  105. #feedlySearchHitList a{\
  106. color: #1122CC !important;\
  107. }\
  108. .feedlySearchResultTitle{\
  109. font-size: 130%;\
  110. }\
  111. .feedlySearchResultSource{\
  112. display: inline-block;\
  113. margin-left: 0.5em;\
  114. font-size: 80%;\
  115. color: #888;\
  116. }\
  117. .feedlySearchResultSource::before{\
  118. content: '(';\
  119. }\
  120. .feedlySearchResultSource::after{\
  121. content: ')';\
  122. }\
  123. .feedlySearchResultBody{\
  124. padding: 1em 0 0 2.5em;\
  125. width: 80%;\
  126. font-size: 110%;\
  127. max-height: 200px;\
  128. overflow: hidden;\
  129. }\
  130. .feedlySearchResultBody:hover{\
  131. max-height: auto;\
  132. }";
  133.  
  134.  
  135.  
  136. var FeedlySearch = {
  137.  
  138. init: function(){
  139. //Add observer
  140. var observer = new MutationObserver(function(mutations){
  141. this.addEntries();
  142. }.bind(this));
  143.  
  144. observer.observe(timeline, { childList: true, subtree: true });
  145.  
  146. //Open database
  147. this.openDatabase();
  148.  
  149. //Add search button after waiting for building the page
  150. setTimeout(this.addSearchButton, 3000);
  151.  
  152. GM_addStyle(STYLE);
  153. },
  154.  
  155.  
  156. addSearchButton: function(){
  157. //Search Button
  158. var img = document.createElement("img");
  159. img.id = "pageActionSearch";
  160. img.className = "pageAction";
  161. img.width = "20";
  162. img.height = "20";
  163. img.border = "0";
  164. img.src = SEARCH_ICON;
  165. img.dataset.appAction = "search";
  166. img.title = "Search";
  167.  
  168. var parent = document.querySelector("#feedlyPageHeader > .pageActionBar");
  169. if(!parent) return;
  170.  
  171. parent.appendChild(img);
  172.  
  173. img.addEventListener('click', function(){
  174. var rect = document.getElementById('feedlyPageHeader').getBoundingClientRect();
  175. var searchbox = document.getElementById('feedlySearchBoxContainer');
  176.  
  177. //Adjust position of search box
  178. searchbox.style.top = (rect.bottom + 5) + 'px';
  179. searchbox.style.right = (document.documentElement.clientWidth - rect.right) + 'px';
  180.  
  181. //Show search box
  182. searchbox.classList.toggle('hidden');
  183.  
  184. //Focus search box
  185. document.getElementById('feedlySearchBox').focus();
  186. }, false);
  187.  
  188.  
  189. //Search Box
  190. var searchBoxTag = '' +
  191. '<form action="" onsubmit="FeedlySearch.search(document.getElementById(\'feedlySearchBox\').value);return false;">'+
  192. '<input type="text" id="feedlySearchBox" title="Split by whitepace to AND search" />'+
  193. '<button type="submit">Search</button>'+
  194. '<div id="feedlySearchLoading" class="invisible" title="Click to abort searching"></div>'+
  195. '<div id="feedlySearchOptions">'+
  196. '<label><input type="checkbox" id="feedlySearchTitle" checked="checked" /> Title</label> '+
  197. '<label><input type="checkbox" id="feedlySearchURL" checked="checked" /> URL</label> '+
  198. '<label><input type="checkbox" id="feedlySearchBody" checked="checked" /> Body</label> '+
  199. '<label><input type="checkbox" id="feedlySearchRegExp" /> RegExp</label>'+
  200. '</div>'+
  201. '</form>';
  202.  
  203. var container = document.createElement('div');
  204. container.id = 'feedlySearchBoxContainer';
  205. container.classList.add('hidden');
  206. document.body.appendChild(container);
  207. container.innerHTML = searchBoxTag;
  208.  
  209. setTimeout(function(){
  210. document.getElementById('feedlySearchLoading').addEventListener('click', function(){
  211. FeedlySearch.abortSearch();
  212. }, false);
  213. });
  214. },
  215.  
  216.  
  217. openDatabase: function(){
  218. var req = window.indexedDB.open(DB_NAME, DB_VERSION);
  219.  
  220. req.onerror = this.onError;
  221. req.onupgradeneeded = this.createDatabase;
  222. req.onsuccess = function(event){
  223. GM_log('Success: Opening the database.');
  224. DB = event.target.result;
  225. };
  226. },
  227.  
  228.  
  229. createDatabase: function(event){
  230. var objectStore = event.target.result.createObjectStore(DB_STORE_NAME, { keyPath: "id" });
  231.  
  232. GM_log("Success: Creating objectStore.");
  233. },
  234.  
  235.  
  236. resetDatabase: function(){
  237. DB.transaction([DB_STORE_NAME], "readwrite").objectStore(DB_STORE_NAME).clear();
  238. },
  239.  
  240.  
  241. addEntries: function(){
  242. GM_log("Adding new entries...");
  243.  
  244. var transaction = DB.transaction([DB_STORE_NAME], "readwrite");
  245. transaction.onerror = this.onError;
  246. transaction.oncomplete = this.onAllEntriesAdded;
  247.  
  248. var objectStore = transaction.objectStore(DB_STORE_NAME);
  249.  
  250. //unread articles
  251. var unreadEntries = Array.slice(timeline.getElementsByClassName('u0Entry')).filter(function(item){
  252. return item.getElementsByClassName('unread').length > 0;
  253. });
  254.  
  255. unreadEntries.forEach(function(entry){
  256. var id = entry.dataset.inlineentryid;
  257. var title = entry.dataset.title;
  258. var url = entry.dataset.alternateLink;
  259. var sourceTitle = entry.querySelector('.sourceTitle > a');
  260. var summary = entry.getElementsByClassName('u0Summary')[0].innerHTML;
  261.  
  262. var request = objectStore.put({
  263. id: id,
  264. title: title,
  265. url: url,
  266. sourceTitle: sourceTitle.firstChild.nodeValue,
  267. sourceURL: sourceTitle.href,
  268. body: summary,
  269. });
  270. request.onsuccess = FeedlySearch.onEntryAdded;
  271. request.onerror = FeedlySearch.onError;
  272. });
  273.  
  274.  
  275. //opened articles
  276. var selectedEntry = timeline.querySelector('.inlineFrame[data-uninlineentryid] .u100Entry');
  277. if(selectedEntry){
  278. var id = selectedEntry.dataset.selectentryid;
  279. var title = selectedEntry.dataset.title;
  280. var url = selectedEntry.dataset.alternateLink;
  281. var sourceTitle = selectedEntry.getElementsByClassName('sourceTitle')[0];
  282. var body = selectedEntry.getElementsByClassName('entryBody')[0];
  283.  
  284. var fullFeedLoaded = body.classList.contains('gm_fullfeed_loaded');
  285.  
  286. var content = fullFeedLoaded ? body : body.querySelector('.content');
  287.  
  288. var request = objectStore.put({
  289. id: id,
  290. title: title,
  291. url: url,
  292. sourceTitle: sourceTitle.firstChild.nodeValue,
  293. sourceURL: sourceTitle.href,
  294. body: content.innerHTML,
  295. });
  296. request.onsuccess = this.onEntryAdded;
  297. request.onerror = this.onError;
  298. }
  299.  
  300. //If remove the following code, this script doesn't work well. (I don't know why)
  301. if(objectStore.mozGetAll)
  302. objectStore.mozGetAll().onsuceess = function(event){};
  303. },
  304.  
  305.  
  306. onEntryAdded: function(event){
  307. GM_log("Entry Saved: " + event.target.result);
  308. },
  309.  
  310. onAllEntriesAdded: function(event){
  311. GM_log("Finish Saving All Entries.");
  312. },
  313.  
  314.  
  315. search: function(key){
  316. GM_log("Searching...");
  317. this._abortSearch = false;
  318.  
  319. var count = 0;
  320. var objectStore = DB.transaction([DB_STORE_NAME]).objectStore(DB_STORE_NAME);
  321.  
  322. //Get search options
  323. var optionTags = Array.slice(document.getElementById('feedlySearchOptions').querySelectorAll('input[type="checkbox"]'));
  324. var options = {};
  325. var keys;
  326.  
  327. optionTags.forEach(function(optionTag){
  328. options[optionTag.id.replace('feedlySearch', '').toLowerCase()] = optionTag.checked;
  329. });
  330.  
  331.  
  332. //Create RegExp Object if RegExp option selected
  333. if(options.regexp){
  334. keys = [new RegExp(key)];
  335. }else{
  336. keys = key.split(/[\s ]+/);
  337. }
  338.  
  339.  
  340. //Create Search Display
  341. var titleBar = document.getElementById('feedlyTitleBar');
  342. var hhint = titleBar.getElementsByClassName('hhint')[0];
  343.  
  344. //Change Title to "Search"
  345. titleBar.firstChild.nodeValue = 'Search';
  346. hhint.innerHTML = '';
  347.  
  348. //Show Loading icon
  349. var loadingIcon = document.getElementById('feedlySearchLoading');
  350. loadingIcon.classList.remove('invisible');
  351.  
  352. //Clear timeline
  353. var entriesArea = document.getElementById('mainArea');
  354. while(entriesArea.hasChildNodes()){
  355. entriesArea.removeChild(entriesArea.firstChild);
  356. }
  357.  
  358. //Create List
  359. var hitEntriesList = document.createElement('ul');
  360. hitEntriesList.id = "feedlySearchHitList";
  361. entriesArea.appendChild(hitEntriesList);
  362.  
  363. var startTime = Date.now();
  364.  
  365.  
  366. //Emphasize every hit term
  367. function emphasizeTerm(str, keys){
  368. var _str = str;
  369.  
  370. keys.forEach(function(key){
  371. _str = _str.replace(key, "<strong>$&</strong>", "g");
  372. });
  373.  
  374. return _str;
  375. }
  376.  
  377.  
  378. //Search
  379. objectStore.openCursor().onsuccess = function(event){
  380. var cursor = event.target.result;
  381.  
  382. if(cursor){
  383. var entry = cursor.value;
  384.  
  385. if(
  386. keys.every(function(key){
  387. return options.regexp ?
  388. (options.title && key.test(entry.title)) ||
  389. (options.url && key.test(entry.url)) ||
  390. (options.body && key.test(entry.body))
  391. :
  392. (options.title && entry.title.indexOf(key) > -1) ||
  393. (options.url && entry.url.indexOf(key) > -1) ||
  394. (options.body && entry.body.indexOf(key) > -1)
  395. })
  396. ){
  397. count++;
  398. hitEntriesList.insertAdjacentHTML("beforeend", "" +
  399. "<li>"+
  400. '<div class="feedlySearchResultTitle">' +
  401. '<a href="' + entry.url + '" target="_blank">' + emphasizeTerm(entry.title, keys) + "</a>" +
  402. '<div class="feedlySearchResultSource">' +
  403. '<a href="' + entry.sourceURL + '">' + entry.sourceTitle + "</a>" +
  404. "</div>" +
  405. "</div>" +
  406. '<div class="feedlySearchResultBody">' +
  407. emphasizeTerm(entry.body, keys) +
  408. '</div>' +
  409. "</li>");
  410. }
  411.  
  412. if(!FeedlySearch._abortSearch) return cursor.continue();
  413. }
  414.  
  415. loadingIcon.classList.add('invisible');
  416. GM_log("Search finished.");
  417.  
  418. if(count == 0){
  419. entriesArea.innerHTML = "No Entries Found.";
  420. }else{
  421. hhint.innerHTML = count + ' results (' + ((Date.now() - startTime) / 1000) + ' seconds)';
  422. }
  423. }
  424. },
  425.  
  426.  
  427. abortSearch: function(){
  428. this._abortSearch = true;
  429. },
  430.  
  431.  
  432.  
  433. onError: function(event){
  434. GM_log('Error has occurred.\n\nType: ' + event.type + '\nValue: ' + event.value);
  435. }
  436.  
  437. };
  438.  
  439.  
  440. window.FeedlySearch = FeedlySearch;
  441. FeedlySearch.init();
  442.  
  443. })(unsafeWindow, unsafeWindow.document);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址