spa.js 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819
  1. require('./polyfill');
  2. import Vue from 'vue';
  3. window.Vue = Vue;
  4. import VueRouter from "vue-router";
  5. import Vuex from "vuex";
  6. import { sync } from "vuex-router-sync";
  7. import BootstrapVue from 'bootstrap-vue'
  8. import InfiniteLoading from 'vue-infinite-loading';
  9. import Loading from 'vue-loading-overlay';
  10. import VueTimeago from 'vue-timeago';
  11. import VueCarousel from 'vue-carousel';
  12. import VueBlurHash from 'vue-blurhash';
  13. import VueMasonry from 'vue-masonry-css';
  14. import VueI18n from 'vue-i18n';
  15. window.pftxt = require('twitter-text');
  16. import 'vue-blurhash/dist/vue-blurhash.css'
  17. window.filesize = require('filesize');
  18. import swal from 'sweetalert';
  19. window._ = require('lodash');
  20. window.Popper = require('popper.js').default;
  21. window.pixelfed = window.pixelfed || {};
  22. window.$ = window.jQuery = require('jquery');
  23. require('bootstrap');
  24. window.axios = require('axios');
  25. window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
  26. require('readmore-js');
  27. window.blurhash = require("blurhash");
  28. $('[data-toggle="tooltip"]').tooltip()
  29. let token = document.head.querySelector('meta[name="csrf-token"]');
  30. if (token) {
  31. window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
  32. } else {
  33. console.error('CSRF token not found.');
  34. }
  35. Vue.use(VueRouter);
  36. Vue.use(Vuex);
  37. Vue.use(VueBlurHash);
  38. Vue.use(VueCarousel);
  39. Vue.use(BootstrapVue);
  40. Vue.use(InfiniteLoading);
  41. Vue.use(Loading);
  42. Vue.use(VueMasonry);
  43. Vue.use(VueI18n);
  44. Vue.use(VueTimeago, {
  45. name: 'Timeago',
  46. locale: 'en'
  47. });
  48. Vue.component(
  49. 'navbar',
  50. require('./../components/partials/navbar.vue').default
  51. );
  52. Vue.component(
  53. 'notification-card',
  54. require('./components/NotificationCard.vue').default
  55. );
  56. Vue.component(
  57. 'photo-presenter',
  58. require('./components/presenter/PhotoPresenter.vue').default
  59. );
  60. Vue.component(
  61. 'video-presenter',
  62. require('./components/presenter/VideoPresenter.vue').default
  63. );
  64. Vue.component(
  65. 'photo-album-presenter',
  66. require('./components/presenter/PhotoAlbumPresenter.vue').default
  67. );
  68. Vue.component(
  69. 'video-album-presenter',
  70. require('./components/presenter/VideoAlbumPresenter.vue').default
  71. );
  72. Vue.component(
  73. 'mixed-album-presenter',
  74. require('./components/presenter/MixedAlbumPresenter.vue').default
  75. );
  76. Vue.component(
  77. 'post-menu',
  78. require('./components/PostMenu.vue').default
  79. );
  80. // Vue.component(
  81. // 'announcements-card',
  82. // require('./components/AnnouncementsCard.vue').default
  83. // );
  84. Vue.component(
  85. 'story-component',
  86. require('./components/StoryTimelineComponent.vue').default
  87. );
  88. const HomeComponent = () => import(/* webpackChunkName: "home.chunk" */ "./../components/Home.vue");
  89. const ComposeComponent = () => import(/* webpackChunkName: "compose.chunk" */ "./../components/Compose.vue");
  90. const PostComponent = () => import(/* webpackChunkName: "post.chunk" */ "./../components/Post.vue");
  91. const ProfileComponent = () => import(/* webpackChunkName: "profile.chunk" */ "./../components/Profile.vue");
  92. const MemoriesComponent = () => import(/* webpackChunkName: "discover~memories.chunk" */ "./../components/discover/Memories.vue");
  93. const MyHashtagComponent = () => import(/* webpackChunkName: "discover~myhashtags.chunk" */ "./../components/discover/Hashtags.vue");
  94. const AccountInsightsComponent = () => import(/* webpackChunkName: "daci.chunk" */ "./../components/discover/Insights.vue");
  95. const DiscoverFindFriendsComponent = () => import(/* webpackChunkName: "discover~findfriends.chunk" */ "./../components/discover/FindFriends.vue");
  96. const DiscoverServerFeedComponent = () => import(/* webpackChunkName: "discover~serverfeed.chunk" */ "./../components/discover/ServerFeed.vue");
  97. const DiscoverSettingsComponent = () => import(/* webpackChunkName: "discover~settings.chunk" */ "./../components/discover/Settings.vue");
  98. const DiscoverComponent = () => import(/* webpackChunkName: "discover.chunk" */ "./../components/Discover.vue");
  99. const NotificationsComponent = () => import(/* webpackChunkName: "notifications.chunk" */ "./../components/Notifications.vue");
  100. const DirectComponent = () => import(/* webpackChunkName: "dms.chunk" */ "./../components/Direct.vue");
  101. const DirectMessageComponent = () => import(/* webpackChunkName: "dms~message.chunk" */ "./../components/DirectMessage.vue");
  102. const ProfileFollowersComponent = () => import(/* webpackChunkName: "profile~followers.bundle" */ "./../components/ProfileFollowers.vue");
  103. const ProfileFollowingComponent = () => import(/* webpackChunkName: "profile~following.bundle" */ "./../components/ProfileFollowing.vue");
  104. const HashtagComponent = () => import(/* webpackChunkName: "discover~hashtag.bundle" */ "./../components/Hashtag.vue");
  105. const NotFoundComponent = () => import(/* webpackChunkName: "error404.bundle" */ "./../components/NotFound.vue");
  106. // const HelpComponent = () => import(/* webpackChunkName: "help.bundle" */ "./../components/HelpComponent.vue");
  107. // const KnowledgebaseComponent = () => import(/* webpackChunkName: "kb.bundle" */ "./../components/Knowledgebase.vue");
  108. // const AboutComponent = () => import(/* webpackChunkName: "about.bundle" */ "./../components/About.vue");
  109. // const ContactComponent = () => import(/* webpackChunkName: "contact.bundle" */ "./../components/Contact.vue");
  110. const LanguageComponent = () => import(/* webpackChunkName: "i18n.bundle" */ "./../components/Language.vue");
  111. // const PrivacyComponent = () => import(/* webpackChunkName: "static~privacy.bundle" */ "./../components/Privacy.vue");
  112. // const TermsComponent = () => import(/* webpackChunkName: "static~tos.bundle" */ "./../components/Terms.vue");
  113. const ChangelogComponent = () => import(/* webpackChunkName: "changelog.bundle" */ "./../components/Changelog.vue");
  114. // import LiveComponent from "./../components/Live.vue";
  115. // import LivestreamsComponent from "./../components/Livestreams.vue";
  116. // import LivePlayerComponent from "./../components/LivePlayer.vue";
  117. // import LiveHelpComponent from "./../components/LiveHelp.vue";
  118. // import DriveComponent from "./../components/Drive.vue";
  119. // import SettingsComponent from "./../components/Settings.vue";
  120. // import ProfileComponent from "./components/ProfileNext.vue";
  121. // import VideosComponent from "./../components/Videos.vue";
  122. // import GroupsComponent from "./../components/Groups.vue";
  123. const router = new VueRouter({
  124. mode: "history",
  125. linkActiveClass: "active",
  126. routes: [
  127. {
  128. path: "/i/web/timeline/:scope",
  129. name: 'timeline',
  130. component: HomeComponent,
  131. props: true
  132. },
  133. // {
  134. // path: "/i/web/timeline/local",
  135. // component: LocalTimeline
  136. // },
  137. // {
  138. // path: "/i/web/timeline/global",
  139. // component: GlobalTimeline
  140. // },
  141. // {
  142. // path: "/i/web/drive",
  143. // name: 'drive',
  144. // component: DriveComponent,
  145. // props: true
  146. // },
  147. // {
  148. // path: "/i/web/groups",
  149. // name: 'groups',
  150. // component: GroupsComponent,
  151. // props: true
  152. // },
  153. {
  154. path: "/i/web/post/:id",
  155. name: 'post',
  156. component: PostComponent,
  157. props: true
  158. },
  159. // {
  160. // path: "/i/web/profile/:id/live",
  161. // component: LivePlayerComponent,
  162. // props: true
  163. // },
  164. {
  165. path: "/i/web/profile/:id/followers",
  166. name: 'profile-followers',
  167. component: ProfileFollowersComponent,
  168. props: true
  169. },
  170. {
  171. path: "/i/web/profile/:id/following",
  172. name: 'profile-following',
  173. component: ProfileFollowingComponent,
  174. props: true
  175. },
  176. {
  177. path: "/i/web/profile/:id",
  178. name: 'profile',
  179. component: ProfileComponent,
  180. props: true
  181. },
  182. // {
  183. // path: "/i/web/videos",
  184. // component: VideosComponent
  185. // },
  186. {
  187. path: "/i/web/discover",
  188. component: DiscoverComponent
  189. },
  190. // {
  191. // path: "/i/web/stories",
  192. // component: HomeComponent
  193. // },
  194. // {
  195. // path: "/i/web/settings/*",
  196. // component: SettingsComponent,
  197. // props: true
  198. // },
  199. // {
  200. // path: "/i/web/settings",
  201. // component: SettingsComponent
  202. // },
  203. {
  204. path: "/i/web/compose",
  205. component: ComposeComponent
  206. },
  207. {
  208. path: "/i/web/notifications",
  209. component: NotificationsComponent
  210. },
  211. {
  212. path: "/i/web/direct/thread/:accountId",
  213. component: DirectMessageComponent,
  214. props: true
  215. },
  216. {
  217. path: "/i/web/direct",
  218. component: DirectComponent
  219. },
  220. // {
  221. // path: "/i/web/kb/:id",
  222. // name: "kb",
  223. // component: KnowledgebaseComponent,
  224. // props: true
  225. // },
  226. {
  227. path: "/i/web/hashtag/:id",
  228. name: "hashtag",
  229. component: HashtagComponent,
  230. props: true
  231. },
  232. // {
  233. // path: "/i/web/help",
  234. // component: HelpComponent
  235. // },
  236. // {
  237. // path: "/i/web/about",
  238. // component: AboutComponent
  239. // },
  240. // {
  241. // path: "/i/web/contact",
  242. // component: ContactComponent
  243. // },
  244. {
  245. path: "/i/web/language",
  246. component: LanguageComponent
  247. },
  248. // {
  249. // path: "/i/web/privacy",
  250. // component: PrivacyComponent
  251. // },
  252. // {
  253. // path: "/i/web/terms",
  254. // component: TermsComponent
  255. // },
  256. {
  257. path: "/i/web/whats-new",
  258. component: ChangelogComponent
  259. },
  260. {
  261. path: "/i/web/discover/my-memories",
  262. component: MemoriesComponent
  263. },
  264. {
  265. path: "/i/web/discover/my-hashtags",
  266. component: MyHashtagComponent
  267. },
  268. {
  269. path: "/i/web/discover/account-insights",
  270. component: AccountInsightsComponent
  271. },
  272. {
  273. path: "/i/web/discover/find-friends",
  274. component: DiscoverFindFriendsComponent
  275. },
  276. {
  277. path: "/i/web/discover/server-timelines",
  278. component: DiscoverServerFeedComponent
  279. },
  280. {
  281. path: "/i/web/discover/settings",
  282. component: DiscoverSettingsComponent
  283. },
  284. // {
  285. // path: "/i/web/livestreams",
  286. // component: LivestreamsComponent
  287. // },
  288. // {
  289. // path: "/i/web/live/help",
  290. // component: LiveHelpComponent
  291. // },
  292. // {
  293. // path: "/i/web/live/player",
  294. // component: LivePlayerComponent
  295. // },
  296. // {
  297. // path: "/i/web/live",
  298. // component: LiveComponent
  299. // },
  300. {
  301. path: "/i/web",
  302. component: HomeComponent,
  303. props: true
  304. },
  305. {
  306. path: "/i/web/*",
  307. component: NotFoundComponent,
  308. props: true
  309. },
  310. ],
  311. scrollBehavior(to, from, savedPosition) {
  312. if (to.hash) {
  313. return {
  314. selector: `[id='${to.hash.slice(1)}']`
  315. };
  316. } else {
  317. return { x: 0, y: 0 };
  318. }
  319. }
  320. });
  321. function lss(name, def) {
  322. let key = 'pf_m2s.' + name;
  323. let ls = window.localStorage;
  324. if(ls.getItem(key)) {
  325. let val = ls.getItem(key);
  326. if(['pl', 'color-scheme'].includes(name)) {
  327. return val;
  328. }
  329. return ['true', true].includes(val);
  330. }
  331. return def;
  332. }
  333. const store = new Vuex.Store({
  334. state: {
  335. version: 1,
  336. hideCounts: lss('hc', false),
  337. autoloadComments: lss('ac', true),
  338. newReactions: lss('nr', true),
  339. fixedHeight: lss('fh', false),
  340. profileLayout: lss('pl', 'grid'),
  341. showDMPrivacyWarning: lss('dmpwarn', true),
  342. relationships: {},
  343. emoji: [],
  344. colorScheme: lss('color-scheme', 'system'),
  345. },
  346. getters: {
  347. getVersion: state => {
  348. return state.version;
  349. },
  350. getHideCounts: state => {
  351. return state.hideCounts;
  352. },
  353. getAutoloadComments: state => {
  354. return state.autoloadComments;
  355. },
  356. getNewReactions: state => {
  357. return state.newReactions;
  358. },
  359. getFixedHeight: state => {
  360. return state.fixedHeight;
  361. },
  362. getProfileLayout: state => {
  363. return state.profileLayout;
  364. },
  365. getRelationship: (state) => (id) => {
  366. // let rel = state.relationships[id];
  367. // if(!rel || !rel.hasOwnProperty('id')) {
  368. // return axios.get('/api/pixelfed/v1/accounts/relationships', {
  369. // params: {
  370. // 'id[]': id
  371. // }
  372. // })
  373. // .then(res => {
  374. // let relationship = res.data;
  375. // // Vue.set(state.relationships, relationship.id, relationship);
  376. // state.commit('updateRelationship', res.data[0]);
  377. // return res.data[0];
  378. // })
  379. // .catch(err => {
  380. // return {};
  381. // })
  382. // } else {
  383. // return state.relationships[id];
  384. // }
  385. return state.relationships[id];
  386. },
  387. getCustomEmoji: state => {
  388. return state.emoji;
  389. },
  390. getColorScheme: state => {
  391. return state.colorScheme;
  392. },
  393. getShowDMPrivacyWarning: state => {
  394. return state.showDMPrivacyWarning;
  395. }
  396. },
  397. mutations: {
  398. setVersion(state, value) {
  399. state.version = value;
  400. },
  401. setHideCounts(state, value) {
  402. localStorage.setItem('pf_m2s.hc', value);
  403. state.hideCounts = value;
  404. },
  405. setAutoloadComments(state, value) {
  406. localStorage.setItem('pf_m2s.ac', value);
  407. state.autoloadComments = value;
  408. },
  409. setNewReactions(state, value) {
  410. localStorage.setItem('pf_m2s.nr', value);
  411. state.newReactions = value;
  412. },
  413. setFixedHeight(state, value) {
  414. localStorage.setItem('pf_m2s.fh', value);
  415. state.fixedHeight = value;
  416. },
  417. setProfileLayout(state, value) {
  418. localStorage.setItem('pf_m2s.pl', value);
  419. state.profileLayout = value;
  420. },
  421. updateRelationship(state, relationships) {
  422. relationships.forEach((relationship) => {
  423. Vue.set(state.relationships, relationship.id, relationship)
  424. })
  425. },
  426. updateCustomEmoji(state, emojis) {
  427. state.emoji = emojis;
  428. },
  429. setColorScheme(state, value) {
  430. if(state.colorScheme == value) {
  431. return;
  432. }
  433. localStorage.setItem('pf_m2s.color-scheme', value);
  434. state.colorScheme = value;
  435. const name = value == 'system' ? '' : (value == 'light' ? 'force-light-mode' : 'force-dark-mode');
  436. document.querySelector("body").className = name;
  437. if(name != 'system') {
  438. const payload = name == 'force-dark-mode' ? { dark_mode: 'on' } : {};
  439. axios.post('/settings/labs', payload);
  440. }
  441. },
  442. setShowDMPrivacyWarning(state, value) {
  443. localStorage.setItem('pf_m2s.dmpwarn', value);
  444. state.showDMPrivacyWarning = value;
  445. }
  446. },
  447. });
  448. let i18nMessages = {
  449. en: require('./i18n/en.json'),
  450. ar: require('./i18n/ar.json'),
  451. ca: require('./i18n/ca.json'),
  452. de: require('./i18n/de.json'),
  453. el: require('./i18n/el.json'),
  454. es: require('./i18n/es.json'),
  455. eu: require('./i18n/eu.json'),
  456. fr: require('./i18n/fr.json'),
  457. he: require('./i18n/he.json'),
  458. gd: require('./i18n/gd.json'),
  459. gl: require('./i18n/gl.json'),
  460. id: require('./i18n/id.json'),
  461. it: require('./i18n/it.json'),
  462. ja: require('./i18n/ja.json'),
  463. nl: require('./i18n/nl.json'),
  464. pl: require('./i18n/pl.json'),
  465. pt: require('./i18n/pt.json'),
  466. ru: require('./i18n/ru.json'),
  467. uk: require('./i18n/uk.json'),
  468. vi: require('./i18n/vi.json'),
  469. };
  470. let locale = document.querySelector('html').getAttribute('lang');
  471. const i18n = new VueI18n({
  472. locale: locale, // set locale
  473. fallbackLocale: 'en',
  474. messages: i18nMessages
  475. });
  476. sync(store, router);
  477. const App = new Vue({
  478. el: '#content',
  479. i18n,
  480. router,
  481. store
  482. });
  483. axios.get('/api/v1/custom_emojis')
  484. .then(res => {
  485. if(res && res.data && res.data.length) {
  486. store.commit('updateCustomEmoji', res.data);
  487. }
  488. });
  489. if(store.state.colorScheme) {
  490. const name = store.state.colorScheme == 'system' ? '' : (store.state.colorScheme == 'light' ? 'force-light-mode' : 'force-dark-mode');
  491. if(name != 'system') {
  492. document.querySelector("body").className = name;
  493. }
  494. }
  495. pixelfed.readmore = () => {
  496. $('.read-more').each(function(k,v) {
  497. let el = $(this);
  498. let attr = el.attr('data-readmore');
  499. if(typeof attr !== typeof undefined && attr !== false) {
  500. return;
  501. }
  502. el.readmore({
  503. collapsedHeight: 45,
  504. heightMargin: 48,
  505. moreLink: '<a href="#" class="d-block small font-weight-bold text-dark text-center">Show more</a>',
  506. lessLink: '<a href="#" class="d-block small font-weight-bold text-dark text-center">Show less</a>',
  507. });
  508. });
  509. };
  510. try {
  511. document.createEvent("TouchEvent");
  512. $('body').addClass('touch');
  513. } catch (e) {
  514. }
  515. window.App = window.App || {};
  516. // window.App.redirect = function() {
  517. // document.querySelectorAll('a').forEach(function(i,k) {
  518. // let a = i.getAttribute('href');
  519. // if(a && a.length > 5 && a.startsWith('https://')) {
  520. // let url = new URL(a);
  521. // if(url.host !== window.location.host && url.pathname !== '/i/redirect') {
  522. // i.setAttribute('href', '/i/redirect?url=' + encodeURIComponent(a));
  523. // }
  524. // }
  525. // });
  526. // }
  527. // window.App.boot = function() {
  528. // new Vue({ el: '#content'});
  529. // }
  530. // window.addEventListener("load", () => {
  531. // if ("serviceWorker" in navigator) {
  532. // navigator.serviceWorker.register("/sw.js");
  533. // }
  534. // });
  535. window.App.util = {
  536. compose: {
  537. post: (function() {
  538. let path = window.location.pathname;
  539. let whitelist = [
  540. '/',
  541. '/timeline/public'
  542. ];
  543. if(whitelist.includes(path)) {
  544. $('#composeModal').modal('show');
  545. } else {
  546. window.location.href = '/?a=co';
  547. }
  548. }),
  549. circle: (function() {
  550. console.log('Unsupported method.');
  551. }),
  552. collection: (function() {
  553. console.log('Unsupported method.');
  554. }),
  555. loop: (function() {
  556. console.log('Unsupported method.');
  557. }),
  558. story: (function() {
  559. console.log('Unsupported method.');
  560. }),
  561. },
  562. time: (function() {
  563. return new Date;
  564. }),
  565. version: 1,
  566. format: {
  567. count: (function(count = 0, locale = 'en-GB', notation = 'compact') {
  568. if(count < 1) {
  569. return 0;
  570. }
  571. return new Intl.NumberFormat(locale, { notation: notation , compactDisplay: "short" }).format(count);
  572. }),
  573. timeAgo: (function(ts) {
  574. let date = Date.parse(ts);
  575. let seconds = Math.floor((new Date() - date) / 1000);
  576. let interval = Math.floor(seconds / 63072000);
  577. if (interval < 0) {
  578. return "0s";
  579. }
  580. if (interval >= 1) {
  581. return interval + "y";
  582. }
  583. interval = Math.floor(seconds / 604800);
  584. if (interval >= 1) {
  585. return interval + "w";
  586. }
  587. interval = Math.floor(seconds / 86400);
  588. if (interval >= 1) {
  589. return interval + "d";
  590. }
  591. interval = Math.floor(seconds / 3600);
  592. if (interval >= 1) {
  593. return interval + "h";
  594. }
  595. interval = Math.floor(seconds / 60);
  596. if (interval >= 1) {
  597. return interval + "m";
  598. }
  599. return Math.floor(seconds) + "s";
  600. }),
  601. timeAhead: (function(ts, short = true) {
  602. let date = Date.parse(ts);
  603. let diff = date - Date.parse(new Date());
  604. let seconds = Math.floor((diff) / 1000);
  605. let interval = Math.floor(seconds / 63072000);
  606. if (interval >= 1) {
  607. return interval + (short ? "y" : " years");
  608. }
  609. interval = Math.floor(seconds / 604800);
  610. if (interval >= 1) {
  611. return interval + (short ? "w" : " weeks");
  612. }
  613. interval = Math.floor(seconds / 86400);
  614. if (interval >= 1) {
  615. return interval + (short ? "d" : " days");
  616. }
  617. interval = Math.floor(seconds / 3600);
  618. if (interval >= 1) {
  619. return interval + (short ? "h" : " hours");
  620. }
  621. interval = Math.floor(seconds / 60);
  622. if (interval >= 1) {
  623. return interval + (short ? "m" : " minutes");
  624. }
  625. return Math.floor(seconds) + (short ? "s" : " seconds");
  626. }),
  627. rewriteLinks: (function(i) {
  628. let tag = i.innerText;
  629. if(i.href.startsWith(window.location.origin)) {
  630. return i.href;
  631. }
  632. if(tag.startsWith('#') == true) {
  633. tag = '/discover/tags/' + tag.substr(1) +'?src=rph';
  634. } else if(tag.startsWith('@') == true) {
  635. tag = '/' + i.innerText + '?src=rpp';
  636. } else {
  637. tag = '/i/redirect?url=' + encodeURIComponent(tag);
  638. }
  639. return tag;
  640. })
  641. },
  642. filters: [
  643. ['1984','filter-1977'],
  644. ['Azen','filter-aden'],
  645. ['Astairo','filter-amaro'],
  646. ['Grassbee','filter-ashby'],
  647. ['Bookrun','filter-brannan'],
  648. ['Borough','filter-brooklyn'],
  649. ['Farms','filter-charmes'],
  650. ['Hairsadone','filter-clarendon'],
  651. ['Cleana ','filter-crema'],
  652. ['Catpatch','filter-dogpatch'],
  653. ['Earlyworm','filter-earlybird'],
  654. ['Plaid','filter-gingham'],
  655. ['Kyo','filter-ginza'],
  656. ['Yefe','filter-hefe'],
  657. ['Goddess','filter-helena'],
  658. ['Yards','filter-hudson'],
  659. ['Quill','filter-inkwell'],
  660. ['Rankine','filter-kelvin'],
  661. ['Juno','filter-juno'],
  662. ['Mark','filter-lark'],
  663. ['Chill','filter-lofi'],
  664. ['Van','filter-ludwig'],
  665. ['Apache','filter-maven'],
  666. ['May','filter-mayfair'],
  667. ['Ceres','filter-moon'],
  668. ['Knoxville','filter-nashville'],
  669. ['Felicity','filter-perpetua'],
  670. ['Sandblast','filter-poprocket'],
  671. ['Daisy','filter-reyes'],
  672. ['Elevate','filter-rise'],
  673. ['Nevada','filter-sierra'],
  674. ['Futura','filter-skyline'],
  675. ['Sleepy','filter-slumber'],
  676. ['Steward','filter-stinson'],
  677. ['Savoy','filter-sutro'],
  678. ['Blaze','filter-toaster'],
  679. ['Apricot','filter-valencia'],
  680. ['Gloming','filter-vesper'],
  681. ['Walter','filter-walden'],
  682. ['Poplar','filter-willow'],
  683. ['Xenon','filter-xpro-ii']
  684. ],
  685. filterCss: {
  686. 'filter-1977': 'sepia(.5) hue-rotate(-30deg) saturate(1.4)',
  687. 'filter-aden': 'sepia(.2) brightness(1.15) saturate(1.4)',
  688. 'filter-amaro': 'sepia(.35) contrast(1.1) brightness(1.2) saturate(1.3)',
  689. 'filter-ashby': 'sepia(.5) contrast(1.2) saturate(1.8)',
  690. 'filter-brannan': 'sepia(.4) contrast(1.25) brightness(1.1) saturate(.9) hue-rotate(-2deg)',
  691. 'filter-brooklyn': 'sepia(.25) contrast(1.25) brightness(1.25) hue-rotate(5deg)',
  692. 'filter-charmes': 'sepia(.25) contrast(1.25) brightness(1.25) saturate(1.35) hue-rotate(-5deg)',
  693. 'filter-clarendon': 'sepia(.15) contrast(1.25) brightness(1.25) hue-rotate(5deg)',
  694. 'filter-crema': 'sepia(.5) contrast(1.25) brightness(1.15) saturate(.9) hue-rotate(-2deg)',
  695. 'filter-dogpatch': 'sepia(.35) saturate(1.1) contrast(1.5)',
  696. 'filter-earlybird': 'sepia(.25) contrast(1.25) brightness(1.15) saturate(.9) hue-rotate(-5deg)',
  697. 'filter-gingham': 'contrast(1.1) brightness(1.1)',
  698. 'filter-ginza': 'sepia(.25) contrast(1.15) brightness(1.2) saturate(1.35) hue-rotate(-5deg)',
  699. 'filter-hefe': 'sepia(.4) contrast(1.5) brightness(1.2) saturate(1.4) hue-rotate(-10deg)',
  700. 'filter-helena': 'sepia(.5) contrast(1.05) brightness(1.05) saturate(1.35)',
  701. 'filter-hudson': 'sepia(.25) contrast(1.2) brightness(1.2) saturate(1.05) hue-rotate(-15deg)',
  702. 'filter-inkwell': 'brightness(1.25) contrast(.85) grayscale(1)',
  703. 'filter-kelvin': 'sepia(.15) contrast(1.5) brightness(1.1) hue-rotate(-10deg)',
  704. 'filter-juno': 'sepia(.35) contrast(1.15) brightness(1.15) saturate(1.8)',
  705. 'filter-lark': 'sepia(.25) contrast(1.2) brightness(1.3) saturate(1.25)',
  706. 'filter-lofi': 'saturate(1.1) contrast(1.5)',
  707. 'filter-ludwig': 'sepia(.25) contrast(1.05) brightness(1.05) saturate(2)',
  708. 'filter-maven': 'sepia(.35) contrast(1.05) brightness(1.05) saturate(1.75)',
  709. 'filter-mayfair': 'contrast(1.1) brightness(1.15) saturate(1.1)',
  710. 'filter-moon': 'brightness(1.4) contrast(.95) saturate(0) sepia(.35)',
  711. 'filter-nashville': 'sepia(.25) contrast(1.5) brightness(.9) hue-rotate(-15deg)',
  712. 'filter-perpetua': 'contrast(1.1) brightness(1.25) saturate(1.1)',
  713. 'filter-poprocket': 'sepia(.15) brightness(1.2)',
  714. 'filter-reyes': 'sepia(.75) contrast(.75) brightness(1.25) saturate(1.4)',
  715. 'filter-rise': 'sepia(.25) contrast(1.25) brightness(1.2) saturate(.9)',
  716. 'filter-sierra': 'sepia(.25) contrast(1.5) brightness(.9) hue-rotate(-15deg)',
  717. 'filter-skyline': 'sepia(.15) contrast(1.25) brightness(1.25) saturate(1.2)',
  718. 'filter-slumber': 'sepia(.35) contrast(1.25) saturate(1.25)',
  719. 'filter-stinson': 'sepia(.35) contrast(1.25) brightness(1.1) saturate(1.25)',
  720. 'filter-sutro': 'sepia(.4) contrast(1.2) brightness(.9) saturate(1.4) hue-rotate(-10deg)',
  721. 'filter-toaster': 'sepia(.25) contrast(1.5) brightness(.95) hue-rotate(-15deg)',
  722. 'filter-valencia': 'sepia(.25) contrast(1.1) brightness(1.1)',
  723. 'filter-vesper': 'sepia(.35) contrast(1.15) brightness(1.2) saturate(1.3)',
  724. 'filter-walden': 'sepia(.35) contrast(.8) brightness(1.25) saturate(1.4)',
  725. 'filter-willow': 'brightness(1.2) contrast(.85) saturate(.05) sepia(.2)',
  726. 'filter-xpro-ii': 'sepia(.45) contrast(1.25) brightness(1.75) saturate(1.3) hue-rotate(-5deg)'
  727. },
  728. emoji: ['๐Ÿ˜‚','๐Ÿ’ฏ','โค๏ธ','๐Ÿ™Œ','๐Ÿ‘','๐Ÿ‘Œ','๐Ÿ˜','๐Ÿ˜ฏ','๐Ÿ˜ข','๐Ÿ˜…','๐Ÿ˜','๐Ÿ™‚','๐Ÿ˜Ž','๐Ÿ˜€','๐Ÿคฃ','๐Ÿ˜ƒ','๐Ÿ˜„','๐Ÿ˜†','๐Ÿ˜‰','๐Ÿ˜Š','๐Ÿ˜‹','๐Ÿ˜˜','๐Ÿ˜—','๐Ÿ˜™','๐Ÿ˜š','๐Ÿค—','๐Ÿคฉ','๐Ÿค”','๐Ÿคจ','๐Ÿ˜','๐Ÿ˜‘','๐Ÿ˜ถ','๐Ÿ™„','๐Ÿ˜','๐Ÿ˜ฃ','๐Ÿ˜ฅ','๐Ÿ˜ฎ','๐Ÿค','๐Ÿ˜ช','๐Ÿ˜ซ','๐Ÿ˜ด','๐Ÿ˜Œ','๐Ÿ˜›','๐Ÿ˜œ','๐Ÿ˜','๐Ÿคค','๐Ÿ˜’','๐Ÿ˜“','๐Ÿ˜”','๐Ÿ˜•','๐Ÿ™ƒ','๐Ÿค‘','๐Ÿ˜ฒ','๐Ÿ™','๐Ÿ˜–','๐Ÿ˜ž','๐Ÿ˜Ÿ','๐Ÿ˜ค','๐Ÿ˜ญ','๐Ÿ˜ฆ','๐Ÿ˜ง','๐Ÿ˜จ','๐Ÿ˜ฉ','๐Ÿคฏ','๐Ÿ˜ฌ','๐Ÿ˜ฐ','๐Ÿ˜ฑ','๐Ÿ˜ณ','๐Ÿคช','๐Ÿ˜ต','๐Ÿ˜ก','๐Ÿ˜ ','๐Ÿคฌ','๐Ÿ˜ท','๐Ÿค’','๐Ÿค•','๐Ÿคข','๐Ÿคฎ','๐Ÿคง','๐Ÿ˜‡','๐Ÿค ','๐Ÿคก','๐Ÿคฅ','๐Ÿคซ','๐Ÿคญ','๐Ÿง','๐Ÿค“','๐Ÿ˜ˆ','๐Ÿ‘ฟ','๐Ÿ‘น','๐Ÿ‘บ','๐Ÿ’€','๐Ÿ‘ป','๐Ÿ‘ฝ','๐Ÿค–','๐Ÿ’ฉ','๐Ÿ˜บ','๐Ÿ˜ธ','๐Ÿ˜น','๐Ÿ˜ป','๐Ÿ˜ผ','๐Ÿ˜ฝ','๐Ÿ™€','๐Ÿ˜ฟ','๐Ÿ˜พ','๐Ÿคฒ','๐Ÿ‘','๐Ÿค','๐Ÿ‘','๐Ÿ‘Ž','๐Ÿ‘Š','โœŠ','๐Ÿค›','๐Ÿคœ','๐Ÿคž','โœŒ๏ธ','๐ŸคŸ','๐Ÿค˜','๐Ÿ‘ˆ','๐Ÿ‘‰','๐Ÿ‘†','๐Ÿ‘‡','โ˜๏ธ','โœ‹','๐Ÿคš','๐Ÿ–','๐Ÿ––','๐Ÿ‘‹','๐Ÿค™','๐Ÿ’ช','๐Ÿ–•','โœ๏ธ','๐Ÿ™','๐Ÿ’','๐Ÿ’„','๐Ÿ’‹','๐Ÿ‘„','๐Ÿ‘…','๐Ÿ‘‚','๐Ÿ‘ƒ','๐Ÿ‘ฃ','๐Ÿ‘','๐Ÿ‘€','๐Ÿง ','๐Ÿ—ฃ','๐Ÿ‘ค','๐Ÿ‘ฅ'
  729. ],
  730. embed: {
  731. post: (function(url, caption = true, likes = false, layout = 'full') {
  732. let u = url + '/embed?';
  733. u += caption ? 'caption=true&' : 'caption=false&';
  734. u += likes ? 'likes=true&' : 'likes=false&';
  735. u += layout == 'compact' ? 'layout=compact' : 'layout=full';
  736. return '<iframe title="Pixelfed Post Embed" src="'+u+'" class="pixelfed__embed" style="max-width: 100%; border: 0" width="400" allowfullscreen="allowfullscreen"></iframe><script async defer src="'+window.location.origin +'/embed.js"><\/script>';
  737. }),
  738. profile: (function(url) {
  739. let u = url + '/embed';
  740. return '<iframe title="Pixelfed Profile Embed" src="'+u+'" class="pixelfed__embed" style="max-width: 100%; border: 0" width="400" allowfullscreen="allowfullscreen"></iframe><script async defer src="'+window.location.origin +'/embed.js"><\/script>';
  741. })
  742. },
  743. clipboard: (function(data) {
  744. return navigator.clipboard.writeText(data);
  745. }),
  746. navatar: (function() {
  747. $('#navbarDropdown .far').addClass('d-none');
  748. $('#navbarDropdown img').attr('src',window._sharedData.curUser.avatar)
  749. .removeClass('d-none')
  750. .addClass('rounded-circle border shadow')
  751. .attr('width', 34).attr('height', 34);
  752. })
  753. };
  754. const warningTitleCSS = 'color:red; font-size:60px; font-weight: bold; -webkit-text-stroke: 1px black;';
  755. const warningDescCSS = 'font-size: 18px;';
  756. console.log('%cStop!', warningTitleCSS);
  757. console.log("%cThis is a browser feature intended for developers. If someone told you to copy and paste something here to enable a Pixelfed feature or \"hack\" someone's account, it is a scam and will give them access to your Pixelfed account.", warningDescCSS);