stream-router.js 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864
  1. /*· · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · ·
  2. · ·
  3. · ·
  4. · Q V I T T E R ·
  5. · ·
  6. · ·
  7. · <o) ·
  8. · /_//// ·
  9. · (____/ ·
  10. · (o< ·
  11. · o> \\\\_\ ·
  12. · \\) \____) ·
  13. · ·
  14. · ·
  15. · @licstart The following is the entire license notice for the ·
  16. · JavaScript code in this page. ·
  17. · ·
  18. · Copyright (C) 2015 Hannes Mannerheim and other contributors ·
  19. · ·
  20. · ·
  21. · This program is free software: you can redistribute it and/or modify ·
  22. · it under the terms of the GNU Affero General Public License as ·
  23. · published by the Free Software Foundation, either version 3 of the ·
  24. · License, or (at your option) any later version. ·
  25. · ·
  26. · This program is distributed in the hope that it will be useful, ·
  27. · but WITHOUT ANY WARRANTY; without even the implied warranty of ·
  28. · MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ·
  29. · GNU Affero General Public License for more details. ·
  30. · ·
  31. · You should have received a copy of the GNU Affero General Public License ·
  32. · along with this program. If not, see <http://www.gnu.org/licenses/>. ·
  33. · ·
  34. · @licend The above is the entire license notice ·
  35. · for the JavaScript code in this page. ·
  36. · ·
  37. · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · · */
  38. /* ·
  39. ·
  40. · Other plugins can add streams to Qvitter, by pushing streamObjects to
  41. · this array. See the structure in pathToStreamRouter()
  42. ·
  43. · · · · · · · · · */
  44. window.pluginStreamObjects = [];
  45. /* ·
  46. ·
  47. · Sets the location bar in the browser to correspond with given stream
  48. ·
  49. · @param streamObject: stream object returned by pathToStreamRouter
  50. ·
  51. · · · · · · · · · */
  52. function setUrlFromStream(streamObject) {
  53. // if we know the nickname for profiles, go with that instead of the id
  54. if(streamObject.name == 'profile by id' && streamObject.nickname !== false) {
  55. history.pushState({strm:streamObject.nickname},'','/' + streamObject.nickname);
  56. }
  57. else {
  58. history.pushState({strm:streamObject.path},'','/' + streamObject.path);
  59. }
  60. }
  61. /* ·
  62. ·
  63. · Local URL to stream router
  64. ·
  65. · @param url: any URL
  66. ·
  67. · · · · · · · · · */
  68. function URLtoStreamRouter(url) {
  69. // we don't expect protocol to matter
  70. url = removeProtocolFromUrl(url);
  71. // not a local URL
  72. if(url != window.siteRootDomain && url.indexOf(window.siteRootDomain + '/') != 0) {
  73. // console.log('not a local url: ' + url);
  74. return false;
  75. }
  76. // remove server
  77. var path = url.substring(window.siteRootDomain.length);
  78. return pathToStreamRouter(path);
  79. }
  80. /* ·
  81. ·
  82. · Path to stream router
  83. ·
  84. · @param path: path, with or without starting slash
  85. ·
  86. · · · · · · · · · */
  87. function pathToStreamRouter(path) {
  88. // remove and remember anchor tags
  89. var anchor = false;
  90. if(path.indexOf('#')>-1) {
  91. anchor = path.substring(path.indexOf('#'));
  92. path = path.substring(0,path.indexOf('#'));
  93. }
  94. // remove starting slash
  95. if(path.indexOf('/') == 0) {
  96. path = path.substring(1);
  97. }
  98. // remove ending slash
  99. if(path.length>0 && path.lastIndexOf('/') == (path.length-1)) {
  100. path = path.substring(0,path.length-1);
  101. }
  102. // if we're on the instance base url and logged in, route to {nickname}/all
  103. if(window.loggedIn && path.length == 0) {
  104. path = window.loggedIn.screen_name + '/all';
  105. }
  106. // structure of the returned object
  107. var streamObject = {
  108. path: path, // this path
  109. name: false, // human readable name
  110. streamHeader: false, // short header, e.g. links and buttons – no html!
  111. streamSubHeader: false, // a longer header, that can include html and links
  112. streamDescription: false, // description of the stream
  113. parentPath: false, // a parent path can e.g. be "group/qvitter" for "group/qvitter/members"
  114. stream: false, // the API path
  115. nickname: false, // if we can read a nickname/screen_name from the path, add it to this property
  116. id: false, // if we can read a id number string from the path, add it to this property
  117. maxIdOrPage: 'maxId', // whether this stream uses 'maxId' or 'page' for paging (maxId is default)
  118. menu: false, // optional menu in the header
  119. callbacks: false, // functions to run after this timeline is loaded to feed-body
  120. type: 'notices' // notices, notifications, users, groups, lists etc. notices is default
  121. };
  122. // instance's public timeline
  123. if((path.length == 0 && window.siteLocalOnlyDefaultPath) || path == 'main/public') {
  124. streamObject.path = 'main/public';
  125. streamObject.name = 'public timeline';
  126. streamObject.streamHeader = window.sL.publicTimeline;
  127. streamObject.stream = 'statuses/public_timeline.json';
  128. if(window.loggedIn !== false) {
  129. streamObject.menu = [
  130. {
  131. type: 'profile-prefs-toggle',
  132. namespace: 'qvitter',
  133. topic: 'hide_embedded_in_timeline:' + streamObject.path,
  134. label: window.sL.hideEmbeddedInTimeline,
  135. callback: 'showOrHideEmbeddedContentInTimelineFromProfilePref'
  136. },
  137. {
  138. type: 'profile-prefs-toggle',
  139. namespace: 'qvitter',
  140. topic: 'hide_quotes_in_timeline:' + streamObject.path,
  141. label: window.sL.hideQuotesInTimeline,
  142. callback: 'showOrHideQuotesInTimelineFromProfilePref'
  143. },
  144. {
  145. type: 'divider'
  146. },
  147. {
  148. type: 'link',
  149. label: window.sL.silencedPlural,
  150. href: window.siteInstanceURL + 'main/silenced'
  151. },
  152. {
  153. type: 'link',
  154. label: window.sL.sandboxedPlural,
  155. href: window.siteInstanceURL + 'main/sandboxed'
  156. }
  157. ];
  158. streamObject.callbacks = [
  159. 'showOrHideEmbeddedContentInTimelineFromProfilePref',
  160. 'showOrHideQuotesInTimelineFromProfilePref'
  161. ];
  162. }
  163. return streamObject;
  164. }
  165. // the whole known network
  166. if(path.length == 0 || path == 'main/all') {
  167. streamObject.path = 'main/all';
  168. streamObject.name = 'public and external timeline';
  169. streamObject.streamHeader = window.sL.publicAndExtTimeline;
  170. streamObject.stream = 'statuses/public_and_external_timeline.json';
  171. if(window.loggedIn !== false) {
  172. streamObject.menu = [
  173. {
  174. type: 'profile-prefs-toggle',
  175. namespace: 'qvitter',
  176. topic: 'hide_embedded_in_timeline:' + streamObject.path,
  177. label: window.sL.hideEmbeddedInTimeline,
  178. callback: 'showOrHideEmbeddedContentInTimelineFromProfilePref'
  179. },
  180. {
  181. type: 'profile-prefs-toggle',
  182. namespace: 'qvitter',
  183. topic: 'hide_quotes_in_timeline:' + streamObject.path,
  184. label: window.sL.hideQuotesInTimeline,
  185. callback: 'showOrHideQuotesInTimelineFromProfilePref'
  186. },
  187. {
  188. type: 'divider'
  189. },
  190. {
  191. type: 'link',
  192. label: window.sL.silencedPlural,
  193. href: window.siteInstanceURL + 'main/silenced'
  194. },
  195. {
  196. type: 'link',
  197. label: window.sL.sandboxedPlural,
  198. href: window.siteInstanceURL + 'main/sandboxed'
  199. }
  200. ];
  201. streamObject.callbacks = [
  202. 'showOrHideEmbeddedContentInTimelineFromProfilePref',
  203. 'showOrHideQuotesInTimelineFromProfilePref'
  204. ];
  205. }
  206. return streamObject;
  207. }
  208. // groups directory, qvitter can't handle that yet
  209. if(path == 'groups') {
  210. streamObject.name = 'group directory';
  211. return streamObject;
  212. }
  213. // search/notice?q={urlencoded search terms}
  214. if(path.indexOf('search/notice?q=') == 0) {
  215. var searchQuery = replaceHtmlSpecialChars(path.replace('search/notice?q=',''));
  216. if(searchQuery.length>0) {
  217. streamObject.name = 'search';
  218. streamObject.streamHeader = window.sL.searchVerb + ': ' + replaceHtmlSpecialChars(decodeURIComponent(searchQuery));
  219. streamObject.stream = 'search.json?q=' + searchQuery;
  220. streamObject.id = searchQuery;
  221. streamObject.maxIdOrPage = 'page';
  222. if(window.loggedIn !== false) {
  223. streamObject.menu = [
  224. {
  225. type: 'profile-prefs-toggle',
  226. namespace: 'qvitter',
  227. topic: 'hide_embedded_in_timeline:' + streamObject.path,
  228. label: window.sL.hideEmbeddedInTimeline,
  229. callback: 'showOrHideEmbeddedContentInTimelineFromProfilePref'
  230. },
  231. {
  232. type: 'profile-prefs-toggle',
  233. namespace: 'qvitter',
  234. topic: 'hide_quotes_in_timeline:' + streamObject.path,
  235. label: window.sL.hideQuotesInTimeline,
  236. callback: 'showOrHideQuotesInTimelineFromProfilePref'
  237. }
  238. ];
  239. streamObject.callbacks = [
  240. 'showOrHideEmbeddedContentInTimelineFromProfilePref',
  241. 'showOrHideQuotesInTimelineFromProfilePref'
  242. ];
  243. }
  244. return streamObject;
  245. }
  246. }
  247. // main/silenced
  248. if(path == 'main/silenced') {
  249. streamObject.name = 'silenced profiles';
  250. streamObject.streamHeader = window.sL.silencedPlural;
  251. streamObject.streamSubHeader = window.sL.silencedUsersOnThisInstance;
  252. streamObject.streamDescription = window.sL.silencedStreamDescription;
  253. streamObject.stream = 'qvitter/silenced.json?count=20';
  254. streamObject.maxIdOrPage = 'page';
  255. streamObject.type = 'users';
  256. streamObject.menu = [
  257. {
  258. type: 'link',
  259. label: window.sL.sandboxedPlural,
  260. href: window.siteInstanceURL + 'main/sandboxed'
  261. }
  262. ];
  263. return streamObject;
  264. }
  265. // main/sandboxed
  266. if(path == 'main/sandboxed') {
  267. streamObject.name = 'sandboxed profiles';
  268. streamObject.streamHeader = window.sL.sandboxedPlural;
  269. streamObject.streamSubHeader = window.sL.sandboxedUsersOnThisInstance;
  270. streamObject.streamDescription = window.sL.sandboxedStreamDescription;
  271. streamObject.stream = 'qvitter/sandboxed.json?count=20';
  272. streamObject.maxIdOrPage = 'page';
  273. streamObject.type = 'users';
  274. streamObject.menu = [
  275. {
  276. type: 'link',
  277. label: window.sL.silencedPlural,
  278. href: window.siteInstanceURL + 'main/silenced'
  279. }
  280. ];
  281. return streamObject;
  282. }
  283. // {screen_name}
  284. if(/^[a-zA-Z0-9]+$/.test(path)) {
  285. streamObject.name = 'profile';
  286. if(window.loggedIn.screen_name == path) {
  287. streamObject.name = 'my profile';
  288. }
  289. streamObject.nickname = path;
  290. streamObject.streamHeader = '@' + replaceHtmlSpecialChars(streamObject.nickname);
  291. streamObject.streamSubHeader = window.sL.notices + '<div class="queet-streams">/ <a class="queet-stream mentions" href="' + window.siteInstanceURL + streamObject.nickname + '/replies">' + window.sL.mentions + '</a> / <a class="queet-stream favorites" href="' + window.siteInstanceURL + streamObject.nickname + '/favorites">' + window.sL.favoritesNoun +'</a></div>';
  292. streamObject.stream = 'statuses/user_timeline.json?screen_name=' + streamObject.nickname + '&withuserarray=1';
  293. return streamObject;
  294. }
  295. var pathSplit = path.split('/');
  296. // tag/{tag}
  297. if(pathSplit.length == 2 && pathSplit[0] == 'tag') {
  298. streamObject.name = 'tag stream';
  299. streamObject.streamHeader = '#' + replaceHtmlSpecialChars(pathSplit[1]);
  300. streamObject.id = pathSplit[1];
  301. streamObject.stream = 'statusnet/tags/timeline/' + streamObject.id + '.json';
  302. if(window.loggedIn !== false) {
  303. streamObject.menu = [
  304. {
  305. type: 'profile-prefs-toggle',
  306. namespace: 'qvitter',
  307. topic: 'hide_embedded_in_timeline:' + streamObject.path,
  308. label: window.sL.hideEmbeddedInTimeline,
  309. callback: 'showOrHideEmbeddedContentInTimelineFromProfilePref'
  310. },
  311. {
  312. type: 'profile-prefs-toggle',
  313. namespace: 'qvitter',
  314. topic: 'hide_quotes_in_timeline:' + streamObject.path,
  315. label: window.sL.hideQuotesInTimeline,
  316. callback: 'showOrHideQuotesInTimelineFromProfilePref'
  317. }
  318. ];
  319. streamObject.callbacks = [
  320. 'showOrHideEmbeddedContentInTimelineFromProfilePref',
  321. 'showOrHideQuotesInTimelineFromProfilePref'
  322. ];
  323. }
  324. return streamObject;
  325. }
  326. // notice/{id}
  327. if(pathSplit.length == 2 && pathSplit[0] == 'notice' && /^[0-9]+$/.test(pathSplit[1])) {
  328. streamObject.name = 'notice';
  329. streamObject.streamHeader = replaceHtmlSpecialChars(path);
  330. streamObject.id = pathSplit[1];
  331. streamObject.stream = 'statuses/show/' + streamObject.id + '.json';
  332. return streamObject;
  333. }
  334. // conversation/{id}
  335. if(pathSplit.length == 2 && pathSplit[0] == 'conversation' && /^[0-9]+$/.test(pathSplit[1])) {
  336. streamObject.name = 'notice';
  337. streamObject.id = pathSplit[1];
  338. // conversation links are redirected to notice page, if the link is to a
  339. // non root notice, then the notice we want to link to and expand is in the hash
  340. if(anchor && anchor.indexOf('#notice-') == 0) {
  341. streamObject.id = anchor.substring(8);
  342. }
  343. streamObject.path = 'notice/' + streamObject.id;
  344. streamObject.streamHeader = replaceHtmlSpecialChars(streamObject.path);
  345. streamObject.stream = 'statuses/show/' + streamObject.id + '.json';
  346. return streamObject;
  347. }
  348. // user/{id}
  349. if(pathSplit.length == 2 && pathSplit[0] == 'user' && /^[0-9]+$/.test(pathSplit[1])) {
  350. streamObject.name = 'profile by id';
  351. streamObject.nickname = userArrayCacheGetUserNicknameById(pathSplit[1]);
  352. if(streamObject.nickname === false) {
  353. streamObject.streamHeader = replaceHtmlSpecialChars(path);
  354. }
  355. else {
  356. streamObject.streamHeader = '@' + streamObject.nickname;
  357. streamObject.parentPath = streamObject.nickname;
  358. streamObject.streamSubHeader = window.sL.notices + '<div class="queet-streams">/ <a class="queet-stream mentions" href="' + window.siteInstanceURL + streamObject.nickname + '/replies">' + window.sL.mentions + '</a> / <a class="queet-stream favorites" href="' + window.siteInstanceURL + streamObject.nickname + '/favorites">' + window.sL.favoritesNoun +'</a></div>';
  359. }
  360. streamObject.id = pathSplit[1];
  361. streamObject.stream = 'qvitter/statuses/user_timeline.json?id=' + streamObject.id + '&withuserarray=1';
  362. return streamObject;
  363. }
  364. // group/{group_nickname}
  365. if(pathSplit.length == 2 && pathSplit[0] == 'group' && /^[a-zA-Z0-9]+$/.test(pathSplit[1])) {
  366. streamObject.name = 'group notice stream';
  367. streamObject.nickname = pathSplit[1];
  368. streamObject.streamHeader = '!' + replaceHtmlSpecialChars(pathSplit[1]);
  369. streamObject.stream = 'statusnet/groups/timeline/' + streamObject.nickname + '.json';
  370. if(window.loggedIn !== false) {
  371. streamObject.menu = [
  372. {
  373. type: 'profile-prefs-toggle',
  374. namespace: 'qvitter',
  375. topic: 'hide_embedded_in_timeline:' + streamObject.path,
  376. label: window.sL.hideEmbeddedInTimeline,
  377. callback: 'showOrHideEmbeddedContentInTimelineFromProfilePref'
  378. },
  379. {
  380. type: 'profile-prefs-toggle',
  381. namespace: 'qvitter',
  382. topic: 'hide_quotes_in_timeline:' + streamObject.path,
  383. label: window.sL.hideQuotesInTimeline,
  384. callback: 'showOrHideQuotesInTimelineFromProfilePref'
  385. }
  386. ];
  387. streamObject.callbacks = [
  388. 'showOrHideEmbeddedContentInTimelineFromProfilePref',
  389. 'showOrHideQuotesInTimelineFromProfilePref'
  390. ];
  391. }
  392. return streamObject;
  393. }
  394. // group/{id}/id
  395. if(pathSplit.length == 3 && pathSplit[0] == 'group' && /^[0-9]+$/.test(pathSplit[1]) && pathSplit[2] == 'id') {
  396. streamObject.name = 'group notice stream by id';
  397. streamObject.id = pathSplit[1];
  398. streamObject.streamHeader = replaceHtmlSpecialChars(path);
  399. streamObject.stream = 'statusnet/groups/timeline/' + streamObject.id + '.json';
  400. return streamObject;
  401. }
  402. // group/{group_nickname}/members
  403. if(pathSplit.length == 3 && pathSplit[0] == 'group' && /^[a-zA-Z0-9]+$/.test(pathSplit[1]) && pathSplit[2] == 'members') {
  404. streamObject.name = 'group member list';
  405. streamObject.nickname = pathSplit[1];
  406. streamObject.parentPath = 'group/' + streamObject.nickname;
  407. streamObject.streamHeader = '!' + replaceHtmlSpecialChars(pathSplit[1]);
  408. streamObject.streamSubHeader = window.sL.memberCount;
  409. streamObject.stream = 'statusnet/groups/membership/' + streamObject.nickname + '.json?count=20';
  410. streamObject.maxIdOrPage = 'page';
  411. streamObject.type = 'users';
  412. return streamObject;
  413. }
  414. // group/{group_nickname}/admins
  415. if(pathSplit.length == 3 && pathSplit[0] == 'group' && /^[a-zA-Z0-9]+$/.test(pathSplit[1]) && pathSplit[2] == 'admins') {
  416. streamObject.name = 'group admin list';
  417. streamObject.nickname = pathSplit[1];
  418. streamObject.parentPath = 'group/' + streamObject.nickname;
  419. streamObject.streamHeader = '!' + replaceHtmlSpecialChars(pathSplit[1]);
  420. streamObject.streamSubHeader = window.sL.adminCount;
  421. streamObject.stream = 'statusnet/groups/admins/' + streamObject.nickname + '.json?count=20';
  422. streamObject.maxIdOrPage = 'page';
  423. streamObject.type = 'users';
  424. return streamObject;
  425. }
  426. // {screen_name}/all
  427. if(pathSplit.length == 2 && /^[a-zA-Z0-9]+$/.test(pathSplit[0]) && pathSplit[1] == 'all') {
  428. streamObject.name = 'friends timeline';
  429. streamObject.nickname = pathSplit[0];
  430. streamObject.streamHeader = replaceHtmlSpecialChars(path);
  431. if(window.loggedIn.screen_name == streamObject.nickname) {
  432. streamObject.stream = 'statuses/friends_timeline.json';
  433. streamObject.streamSubHeader = window.sL.timeline;
  434. streamObject.menu = [
  435. {
  436. type: 'profile-prefs-toggle',
  437. namespace: 'qvitter',
  438. topic: 'hide_replies',
  439. label: window.sL.hideRepliesToPeopleIDoNotFollow
  440. },
  441. {
  442. type: 'divider'
  443. },
  444. {
  445. type: 'profile-prefs-toggle',
  446. namespace: 'qvitter',
  447. topic: 'hide_embedded_in_timeline:' + streamObject.path,
  448. label: window.sL.hideEmbeddedInTimeline,
  449. callback: 'showOrHideEmbeddedContentInTimelineFromProfilePref'
  450. },
  451. {
  452. type: 'profile-prefs-toggle',
  453. namespace: 'qvitter',
  454. topic: 'hide_quotes_in_timeline:' + streamObject.path,
  455. label: window.sL.hideQuotesInTimeline,
  456. callback: 'showOrHideQuotesInTimelineFromProfilePref'
  457. }
  458. ];
  459. streamObject.callbacks = [
  460. 'showOrHideEmbeddedContentInTimelineFromProfilePref',
  461. 'showOrHideQuotesInTimelineFromProfilePref'
  462. ];
  463. }
  464. else {
  465. streamObject.stream = 'statuses/friends_timeline.json?screen_name=' + streamObject.nickname + '&withuserarray=1';
  466. streamObject.parentPath = streamObject.nickname;
  467. }
  468. return streamObject;
  469. }
  470. // {screen_name}/replies
  471. if(pathSplit.length == 2 && /^[a-zA-Z0-9]+$/.test(pathSplit[0]) && pathSplit[1] == 'replies') {
  472. streamObject.name = 'mentions';
  473. streamObject.nickname = pathSplit[0];
  474. if(window.loggedIn.screen_name == streamObject.nickname) {
  475. streamObject.stream = 'statuses/mentions.json';
  476. streamObject.streamHeader = window.sL.mentions;
  477. }
  478. else {
  479. streamObject.parentPath = streamObject.nickname;
  480. streamObject.stream = 'statuses/mentions.json?screen_name=' + streamObject.nickname + '&withuserarray=1';
  481. streamObject.streamSubHeader = '<div class="queet-streams"><a class="queet-stream queets" href="' + window.siteInstanceURL + streamObject.nickname + '">' + window.sL.notices + '</a> /</div>' + window.sL.mentions + '<div class="queet-streams">/ <a class="queet-stream favorites" href="' + window.siteInstanceURL + streamObject.nickname + '/favorites">' + window.sL.favoritesNoun + '</a></div>';
  482. }
  483. if(window.loggedIn !== false) {
  484. streamObject.menu = [
  485. {
  486. type: 'profile-prefs-toggle',
  487. namespace: 'qvitter',
  488. topic: 'hide_embedded_in_timeline:' + streamObject.path,
  489. label: window.sL.hideEmbeddedInTimeline,
  490. callback: 'showOrHideEmbeddedContentInTimelineFromProfilePref'
  491. },
  492. {
  493. type: 'profile-prefs-toggle',
  494. namespace: 'qvitter',
  495. topic: 'hide_quotes_in_timeline:' + streamObject.path,
  496. label: window.sL.hideQuotesInTimeline,
  497. callback: 'showOrHideQuotesInTimelineFromProfilePref'
  498. }
  499. ];
  500. streamObject.callbacks = [
  501. 'showOrHideEmbeddedContentInTimelineFromProfilePref',
  502. 'showOrHideQuotesInTimelineFromProfilePref'
  503. ];
  504. }
  505. return streamObject;
  506. }
  507. // {screen_name}/notifications
  508. if(pathSplit.length == 2 && /^[a-zA-Z0-9]+$/.test(pathSplit[0]) && pathSplit[1] == 'notifications') {
  509. streamObject.name = 'notifications';
  510. streamObject.nickname = pathSplit[0];
  511. // only accessible to the logged in user
  512. if(window.loggedIn.screen_name == streamObject.nickname) {
  513. streamObject.stream = 'qvitter/statuses/notifications.json';
  514. streamObject.streamHeader = '@' + replaceHtmlSpecialChars(streamObject.nickname);
  515. streamObject.streamSubHeader = window.sL.notifications;
  516. streamObject.type = 'notifications';
  517. streamObject.menu = [
  518. {
  519. type: 'function',
  520. functionName: 'markAllNotificationsAsSeen',
  521. label: window.sL.markAllNotificationsAsSeen
  522. },
  523. {
  524. type: 'divider'
  525. },
  526. {
  527. type: 'profile-prefs-toggle',
  528. namespace: 'qvitter',
  529. topic: 'hide_embedded_in_timeline:' + streamObject.path,
  530. label: window.sL.hideEmbeddedInTimeline,
  531. callback: 'showOrHideEmbeddedContentInTimelineFromProfilePref'
  532. },
  533. {
  534. type: 'profile-prefs-toggle',
  535. namespace: 'qvitter',
  536. topic: 'hide_quotes_in_timeline:' + streamObject.path,
  537. label: window.sL.hideQuotesInTimeline,
  538. callback: 'showOrHideQuotesInTimelineFromProfilePref'
  539. },
  540. {
  541. type: 'divider'
  542. },
  543. {
  544. type: 'profile-prefs-toggle',
  545. namespace: 'qvitter',
  546. topic: 'only_show_notifications_from_users_i_follow',
  547. label: window.sL.onlyShowNotificationsFromUsersIFollow,
  548. callback: 'reloadCurrentStreamAndClearCache'
  549. },
  550. {
  551. type: 'profile-prefs-toggle',
  552. namespace: 'qvitter',
  553. topic: 'hide_notifications_from_muted_users',
  554. label: window.sL.hideNotificationsFromMutedUsers,
  555. callback: 'showOrHideNoticesFromMutedUsersInNotifications'
  556. },
  557. {
  558. type: 'divider'
  559. },
  560. {
  561. type: 'profile-prefs-toggle',
  562. namespace: 'qvitter',
  563. topic: 'disable_notify_replies_and_mentions',
  564. label: window.sL.notifyRepliesAndMentions,
  565. callback: 'reloadCurrentStreamAndClearCache'
  566. },
  567. {
  568. type: 'profile-prefs-toggle',
  569. namespace: 'qvitter',
  570. topic: 'disable_notify_favs',
  571. label: window.sL.notifyFavs,
  572. callback: 'reloadCurrentStreamAndClearCache'
  573. },
  574. {
  575. type: 'profile-prefs-toggle',
  576. namespace: 'qvitter',
  577. topic: 'disable_notify_repeats',
  578. label: window.sL.notifyRepeats,
  579. callback: 'reloadCurrentStreamAndClearCache'
  580. },
  581. {
  582. type: 'profile-prefs-toggle',
  583. namespace: 'qvitter',
  584. topic: 'disable_notify_follows',
  585. label: window.sL.notifyFollows,
  586. callback: 'reloadCurrentStreamAndClearCache'
  587. }
  588. ];
  589. streamObject.callbacks = [
  590. 'showOrHideEmbeddedContentInTimelineFromProfilePref',
  591. 'showOrHideQuotesInTimelineFromProfilePref',
  592. 'showOrHideNoticesFromMutedUsersInNotifications'
  593. ];
  594. }
  595. return streamObject;
  596. }
  597. // {screen_name}/favorites
  598. if(pathSplit.length == 2 && /^[a-zA-Z0-9]+$/.test(pathSplit[0]) && pathSplit[1] == 'favorites') {
  599. streamObject.name = 'favorites';
  600. streamObject.nickname = pathSplit[0];
  601. if(window.loggedIn.screen_name == streamObject.nickname) {
  602. streamObject.stream = 'favorites.json';
  603. streamObject.streamSubHeader = window.sL.favoritesNoun;
  604. }
  605. else {
  606. streamObject.parentPath = streamObject.nickname;
  607. streamObject.stream = 'favorites.json?screen_name=' + streamObject.nickname + '&withuserarray=1';
  608. streamObject.streamHeader = '@' + replaceHtmlSpecialChars(streamObject.nickname);
  609. streamObject.streamSubHeader = '<div class="queet-streams"><a class="queet-stream queets" href="' + window.siteInstanceURL + streamObject.nickname + '">' + window.sL.notices + '</a> / <a class="queet-stream mentions" href="' + window.siteInstanceURL + streamObject.nickname + '/replies">' + window.sL.mentions + '</a> /</div>' + window.sL.favoritesNoun;
  610. }
  611. if(window.loggedIn !== false) {
  612. streamObject.menu = [
  613. {
  614. type: 'profile-prefs-toggle',
  615. namespace: 'qvitter',
  616. topic: 'hide_embedded_in_timeline:' + streamObject.path,
  617. label: window.sL.hideEmbeddedInTimeline,
  618. callback: 'showOrHideEmbeddedContentInTimelineFromProfilePref'
  619. },
  620. {
  621. type: 'profile-prefs-toggle',
  622. namespace: 'qvitter',
  623. topic: 'hide_quotes_in_timeline:' + streamObject.path,
  624. label: window.sL.hideQuotesInTimeline,
  625. callback: 'showOrHideQuotesInTimelineFromProfilePref'
  626. }
  627. ];
  628. streamObject.callbacks = [
  629. 'showOrHideEmbeddedContentInTimelineFromProfilePref',
  630. 'showOrHideQuotesInTimelineFromProfilePref'
  631. ];
  632. }
  633. return streamObject;
  634. }
  635. // {screen_name}/subscribers
  636. if(pathSplit.length == 2 && /^[a-zA-Z0-9]+$/.test(pathSplit[0]) && pathSplit[1] == 'subscribers') {
  637. streamObject.name = 'subscribers';
  638. streamObject.nickname = pathSplit[0];
  639. streamObject.parentPath = streamObject.nickname;
  640. streamObject.streamHeader = '@' + replaceHtmlSpecialChars(streamObject.nickname);
  641. streamObject.streamSubHeader = '<div class="queet-streams"><a class="queet-stream following" href="' + window.siteInstanceURL + streamObject.nickname + '/subscriptions">' + window.sL.following + '</a> / </div>' + window.sL.followers;
  642. streamObject.stream = 'statuses/followers.json?count=20&screen_name=' + streamObject.nickname + '&withuserarray=1';
  643. streamObject.maxIdOrPage = 'page';
  644. streamObject.type = 'users';
  645. return streamObject;
  646. }
  647. // {screen_name}/subscriptions
  648. if(pathSplit.length == 2 && /^[a-zA-Z0-9]+$/.test(pathSplit[0]) && pathSplit[1] == 'subscriptions') {
  649. streamObject.name = 'subscriptions';
  650. streamObject.nickname = pathSplit[0];
  651. streamObject.parentPath = streamObject.nickname;
  652. streamObject.streamHeader = '@' + replaceHtmlSpecialChars(streamObject.nickname);
  653. streamObject.streamSubHeader = window.sL.following + '<div class="queet-streams">/ <a class="queet-stream followers" href="' + window.siteInstanceURL + streamObject.nickname + '/subscribers">' + window.sL.followers + '</a></div>';
  654. streamObject.stream = 'statuses/friends.json?count=20&screen_name=' + streamObject.nickname + '&withuserarray=1';
  655. streamObject.maxIdOrPage = 'page';
  656. streamObject.type = 'users';
  657. return streamObject;
  658. }
  659. // {screen_name}/blocks
  660. if(pathSplit.length == 2 && /^[a-zA-Z0-9]+$/.test(pathSplit[0]) && pathSplit[1] == 'blocks') {
  661. streamObject.name = 'user blocks';
  662. streamObject.nickname = pathSplit[0];
  663. streamObject.streamHeader = window.sL.userBlocked;
  664. streamObject.streamSubHeader = window.sL.userBlocks;
  665. streamObject.stream = 'qvitter/blocks.json?count=20&id=' + streamObject.nickname + '&withuserarray=1';
  666. streamObject.maxIdOrPage = 'page';
  667. streamObject.type = 'users';
  668. return streamObject;
  669. }
  670. // {screen_name}/mutes
  671. if(pathSplit.length == 2 && /^[a-zA-Z0-9]+$/.test(pathSplit[0]) && pathSplit[1] == 'mutes') {
  672. streamObject.name = 'user mutes';
  673. streamObject.nickname = pathSplit[0];
  674. streamObject.streamHeader = window.sL.userMuted;
  675. streamObject.streamSubHeader = window.sL.userMutes;
  676. streamObject.streamDescription = window.sL.mutedStreamDescription;
  677. streamObject.stream = 'qvitter/mutes.json?count=20&withuserarray=1';
  678. streamObject.maxIdOrPage = 'page';
  679. streamObject.type = 'users';
  680. return streamObject;
  681. }
  682. // {screen_name}/groups
  683. if(pathSplit.length == 2 && /^[a-zA-Z0-9]+$/.test(pathSplit[0]) && pathSplit[1] == 'groups') {
  684. streamObject.name = 'user group list';
  685. streamObject.nickname = pathSplit[0];
  686. streamObject.parentPath = streamObject.nickname;
  687. streamObject.streamHeader = '@' + replaceHtmlSpecialChars(streamObject.nickname);
  688. streamObject.streamSubHeader = window.sL.groups;
  689. streamObject.stream = 'statusnet/groups/list.json?count=10&screen_name=' + streamObject.nickname + '&withuserarray=1';
  690. streamObject.maxIdOrPage = 'page';
  691. streamObject.type = 'groups';
  692. return streamObject;
  693. }
  694. // {screen_name}/all/{listname}
  695. if(pathSplit.length == 3 && /^[a-zA-Z0-9]+$/.test(pathSplit[0]) && pathSplit[1] == 'all' && /^[a-zA-Z0-9]+$/.test(pathSplit[2])) {
  696. streamObject.name = 'list notice stream';
  697. streamObject.nickname = pathSplit[2];
  698. streamObject.stream = 'qvitter/' + pathSplit[0] + '/lists/' + pathSplit[2] + '/statuses.json';
  699. streamObject.type = 'notices';
  700. if(window.loggedIn.screen_name == pathSplit[0]) {
  701. streamObject.streamHeader = window.sL.myListWithListName.replace('{list-name}',streamObject.nickname);
  702. streamObject.streamSubHeader = window.sL.myListWithListName.replace('{list-name}',streamObject.nickname) + '<div class="queet-streams">/ <a class="queet-stream list-members" href="' + window.siteInstanceURL + pathSplit[0] + '/all/' + streamObject.nickname + '/tagged">' + window.sL.listMembers + '</a> / <a class="queet-stream list-subscribers" href="' + window.siteInstanceURL + pathSplit[0] + '/all/' + streamObject.nickname + '/subscribers">' + window.sL.listSubscribers + '</a></div>';
  703. }
  704. else {
  705. streamObject.streamHeader = window.sL.nicknamesListWithListName.replace('{list-name}',streamObject.nickname).replace('{nickname}',pathSplit[0]);
  706. streamObject.streamSubHeader = window.sL.nicknamesListWithListName.replace('{list-name}',streamObject.nickname).replace('{nickname}',pathSplit[0]) + '<div class="queet-streams">/ <a class="queet-stream list-members" href="' + window.siteInstanceURL + pathSplit[0] + '/all/' + streamObject.nickname + '/tagged">' + window.sL.listMembers + '</a> / <a class="queet-stream list-subscribers" href="' + window.siteInstanceURL + pathSplit[0] + '/all/' + streamObject.nickname + '/subscribers">' + window.sL.listSubscribers + '</a></div>';
  707. }
  708. if(window.loggedIn !== false) {
  709. streamObject.menu = [
  710. {
  711. type: 'profile-prefs-toggle',
  712. namespace: 'qvitter',
  713. topic: 'hide_embedded_in_timeline:' + streamObject.path,
  714. label: window.sL.hideEmbeddedInTimeline,
  715. callback: 'showOrHideEmbeddedContentInTimelineFromProfilePref'
  716. },
  717. {
  718. type: 'profile-prefs-toggle',
  719. namespace: 'qvitter',
  720. topic: 'hide_quotes_in_timeline:' + streamObject.path,
  721. label: window.sL.hideQuotesInTimeline,
  722. callback: 'showOrHideQuotesInTimelineFromProfilePref'
  723. }
  724. ];
  725. streamObject.callbacks = [
  726. 'showOrHideEmbeddedContentInTimelineFromProfilePref',
  727. 'showOrHideQuotesInTimelineFromProfilePref'
  728. ];
  729. }
  730. return streamObject;
  731. }
  732. // {screen_name}/all/{listname}/members
  733. if(pathSplit.length == 4 && /^[a-zA-Z0-9]+$/.test(pathSplit[0]) && pathSplit[1] == 'all' && /^[a-zA-Z0-9]+$/.test(pathSplit[2]) && pathSplit[3] == 'tagged') {
  734. streamObject.name = 'list members';
  735. streamObject.nickname = pathSplit[2];
  736. streamObject.parentPath = pathSplit[0] + '/all/' + pathSplit[2];
  737. streamObject.stream = 'qvitter/' + pathSplit[0] + '/lists/' + pathSplit[2] + '/members.json';
  738. streamObject.maxIdOrPage = 'page';
  739. streamObject.type = 'users';
  740. if(window.loggedIn.screen_name == pathSplit[0]) {
  741. streamObject.streamHeader = window.sL.myListWithListName.replace('{list-name}',streamObject.nickname);
  742. streamObject.streamSubHeader = '<div class="queet-streams"><a class="queet-stream list-notice-stream" href="' + window.siteInstanceURL + pathSplit[0] + '/all/' + streamObject.nickname + '">' + window.sL.myListWithListName.replace('{list-name}',streamObject.nickname) + '</a> /</div>' + window.sL.listMembers + '<div class="queet-streams">/ <a class="queet-stream list-subscribers" href="' + window.siteInstanceURL + pathSplit[0] + '/all/' + streamObject.nickname + '/subscribers">' + window.sL.listSubscribers + '</a></div>';
  743. }
  744. else {
  745. streamObject.streamHeader = window.sL.nicknamesListWithListName.replace('{list-name}',streamObject.nickname).replace('{nickname}',pathSplit[0]);
  746. streamObject.streamSubHeader = '<div class="queet-streams"><a class="queet-stream list-notice-stream" href="' + window.siteInstanceURL + pathSplit[0] + '/all/' + streamObject.nickname + '">' + window.sL.nicknamesListWithListName.replace('{list-name}',streamObject.nickname).replace('{nickname}',pathSplit[0]) + '</a> /</div>' + window.sL.listMembers + '<div class="queet-streams">/ <a class="queet-stream list-subscribers" href="' + window.siteInstanceURL + pathSplit[0] + '/all/' + streamObject.nickname + '/subscribers">' + window.sL.listSubscribers + '</a></div>';
  747. }
  748. return streamObject;
  749. }
  750. // {screen_name}/all/{listname}/subscribers
  751. if(pathSplit.length == 4 && /^[a-zA-Z0-9]+$/.test(pathSplit[0]) && pathSplit[1] == 'all' && /^[a-zA-Z0-9]+$/.test(pathSplit[2]) && pathSplit[3] == 'subscribers') {
  752. streamObject.name = 'list subscribers';
  753. streamObject.nickname = pathSplit[2];
  754. streamObject.parentPath = pathSplit[0] + '/all/' + pathSplit[2];
  755. streamObject.stream = 'qvitter/' + pathSplit[0] + '/lists/' + pathSplit[2] + '/subscribers.json';
  756. streamObject.maxIdOrPage = 'page';
  757. streamObject.type = 'users';
  758. if(window.loggedIn.screen_name == pathSplit[0]) {
  759. streamObject.streamHeader = window.sL.myListWithListName.replace('{list-name}',streamObject.nickname);
  760. streamObject.streamSubHeader = '<div class="queet-streams"><a class="queet-stream list-notice-stream" href="' + window.siteInstanceURL + pathSplit[0] + '/all/' + streamObject.nickname + '">' + window.sL.myListWithListName.replace('{list-name}',streamObject.nickname) + '</a> / <a class="queet-stream list-members" href="' + window.siteInstanceURL + pathSplit[0] + '/all/' + streamObject.nickname + '/tagged">' + window.sL.listMembers + '</a> /</div>' + window.sL.listSubscribers;
  761. }
  762. else {
  763. streamObject.streamHeader = window.sL.nicknamesListWithListName.replace('{list-name}',streamObject.nickname).replace('{nickname}',pathSplit[0]);
  764. streamObject.streamSubHeader = '<div class="queet-streams"><a class="queet-stream list-notice-stream" href="' + window.siteInstanceURL + pathSplit[0] + '/all/' + streamObject.nickname + '">' + window.sL.nicknamesListWithListName.replace('{list-name}',streamObject.nickname).replace('{nickname}',pathSplit[0]) + '</a> / <a class="queet-stream list-members" href="' + window.siteInstanceURL + pathSplit[0] + '/all/' + streamObject.nickname + '/tagged">' + window.sL.listMembers + '</a> /</div>' + window.sL.listSubscribers;
  765. }
  766. return streamObject;
  767. }
  768. // other plugins can add streams to Qvitter
  769. if(window.pluginStreamObjects.length > 0) {
  770. $.each(window.pluginStreamObjects,function(k,pluginStreamObject) {
  771. if(typeof pluginStreamObject.pathRegExp != 'undefined' && pluginStreamObject.pathRegExp.test(path)){
  772. $.extend(streamObject,pluginStreamObject);
  773. return false;
  774. }
  775. });
  776. return streamObject;
  777. }
  778. return false;
  779. }
  780. /* ·
  781. ·
  782. · Get stream from location bar
  783. ·
  784. · · · · · · · · · */
  785. function getStreamFromUrl() {
  786. var streamObject = URLtoStreamRouter(window.location.href);
  787. if(streamObject.stream) {
  788. return streamObject;
  789. }
  790. // fallback to friends timeline or public timeline if URLtoStreamRouter can't find a stream
  791. else if(window.loggedIn) {
  792. return pathToStreamRouter(window.loggedIn.screen_name + '/all');
  793. }
  794. else if(window.siteLocalOnlyDefaultPath) {
  795. return pathToStreamRouter('main/public');
  796. }
  797. else {
  798. return pathToStreamRouter('main/all');
  799. }
  800. }