stream-router.js 36 KB

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