diff --git a/resources/assets/js/spa.js b/resources/assets/js/spa.js new file mode 100644 index 000000000..fc96e1a08 --- /dev/null +++ b/resources/assets/js/spa.js @@ -0,0 +1,819 @@ +require('./polyfill'); +import Vue from 'vue'; +window.Vue = Vue; +import VueRouter from "vue-router"; +import Vuex from "vuex"; +import { sync } from "vuex-router-sync"; +import BootstrapVue from 'bootstrap-vue' +import InfiniteLoading from 'vue-infinite-loading'; +import Loading from 'vue-loading-overlay'; +import VueTimeago from 'vue-timeago'; +import VueCarousel from 'vue-carousel'; +import VueBlurHash from 'vue-blurhash'; +import VueMasonry from 'vue-masonry-css'; +import VueI18n from 'vue-i18n'; +window.pftxt = require('twitter-text'); +import 'vue-blurhash/dist/vue-blurhash.css' +window.filesize = require('filesize'); +import swal from 'sweetalert'; +window._ = require('lodash'); +window.Popper = require('popper.js').default; +window.pixelfed = window.pixelfed || {}; +window.$ = window.jQuery = require('jquery'); +require('bootstrap'); +window.axios = require('axios'); +window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; +require('readmore-js'); +window.blurhash = require("blurhash"); + +$('[data-toggle="tooltip"]').tooltip() +let token = document.head.querySelector('meta[name="csrf-token"]'); +if (token) { + window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content; +} else { + console.error('CSRF token not found.'); +} + +Vue.use(VueRouter); +Vue.use(Vuex); +Vue.use(VueBlurHash); +Vue.use(VueCarousel); +Vue.use(BootstrapVue); +Vue.use(InfiniteLoading); +Vue.use(Loading); +Vue.use(VueMasonry); +Vue.use(VueI18n); +Vue.use(VueTimeago, { + name: 'Timeago', + locale: 'en' +}); + +Vue.component( + 'navbar', + require('./../components/partials/navbar.vue').default +); + +Vue.component( + 'notification-card', + require('./components/NotificationCard.vue').default +); + +Vue.component( + 'photo-presenter', + require('./components/presenter/PhotoPresenter.vue').default +); + +Vue.component( + 'video-presenter', + require('./components/presenter/VideoPresenter.vue').default +); + +Vue.component( + 'photo-album-presenter', + require('./components/presenter/PhotoAlbumPresenter.vue').default +); + +Vue.component( + 'video-album-presenter', + require('./components/presenter/VideoAlbumPresenter.vue').default +); + +Vue.component( + 'mixed-album-presenter', + require('./components/presenter/MixedAlbumPresenter.vue').default +); + +Vue.component( + 'post-menu', + require('./components/PostMenu.vue').default +); + +// Vue.component( +// 'announcements-card', +// require('./components/AnnouncementsCard.vue').default +// ); + +Vue.component( + 'story-component', + require('./components/StoryTimelineComponent.vue').default +); + +const HomeComponent = () => import(/* webpackChunkName: "home.chunk" */ "./../components/Home.vue"); +const ComposeComponent = () => import(/* webpackChunkName: "compose.chunk" */ "./../components/Compose.vue"); +const PostComponent = () => import(/* webpackChunkName: "post.chunk" */ "./../components/Post.vue"); +const ProfileComponent = () => import(/* webpackChunkName: "profile.chunk" */ "./../components/Profile.vue"); +const MemoriesComponent = () => import(/* webpackChunkName: "discover~memories.chunk" */ "./../components/discover/Memories.vue"); +const MyHashtagComponent = () => import(/* webpackChunkName: "discover~myhashtags.chunk" */ "./../components/discover/Hashtags.vue"); +const AccountInsightsComponent = () => import(/* webpackChunkName: "daci.chunk" */ "./../components/discover/Insights.vue"); +const DiscoverFindFriendsComponent = () => import(/* webpackChunkName: "discover~findfriends.chunk" */ "./../components/discover/FindFriends.vue"); +const DiscoverServerFeedComponent = () => import(/* webpackChunkName: "discover~serverfeed.chunk" */ "./../components/discover/ServerFeed.vue"); +const DiscoverSettingsComponent = () => import(/* webpackChunkName: "discover~settings.chunk" */ "./../components/discover/Settings.vue"); +const DiscoverComponent = () => import(/* webpackChunkName: "discover.chunk" */ "./../components/Discover.vue"); +const NotificationsComponent = () => import(/* webpackChunkName: "notifications.chunk" */ "./../components/Notifications.vue"); +const DirectComponent = () => import(/* webpackChunkName: "dms.chunk" */ "./../components/Direct.vue"); +const DirectMessageComponent = () => import(/* webpackChunkName: "dms~message.chunk" */ "./../components/DirectMessage.vue"); +const ProfileFollowersComponent = () => import(/* webpackChunkName: "profile~followers.bundle" */ "./../components/ProfileFollowers.vue"); +const ProfileFollowingComponent = () => import(/* webpackChunkName: "profile~following.bundle" */ "./../components/ProfileFollowing.vue"); +const HashtagComponent = () => import(/* webpackChunkName: "discover~hashtag.bundle" */ "./../components/Hashtag.vue"); +const NotFoundComponent = () => import(/* webpackChunkName: "error404.bundle" */ "./../components/NotFound.vue"); +const HelpComponent = () => import(/* webpackChunkName: "help.bundle" */ "./../components/HelpComponent.vue"); +const KnowledgebaseComponent = () => import(/* webpackChunkName: "kb.bundle" */ "./../components/Knowledgebase.vue"); +const AboutComponent = () => import(/* webpackChunkName: "about.bundle" */ "./../components/About.vue"); +const ContactComponent = () => import(/* webpackChunkName: "contact.bundle" */ "./../components/Contact.vue"); +const LanguageComponent = () => import(/* webpackChunkName: "i18n.bundle" */ "./../components/Language.vue"); +const PrivacyComponent = () => import(/* webpackChunkName: "static~privacy.bundle" */ "./../components/Privacy.vue"); +const TermsComponent = () => import(/* webpackChunkName: "static~tos.bundle" */ "./../components/Terms.vue"); +const ChangelogComponent = () => import(/* webpackChunkName: "changelog.bundle" */ "./../components/Changelog.vue"); + +// import LiveComponent from "./../components/Live.vue"; +// import LivestreamsComponent from "./../components/Livestreams.vue"; +// import LivePlayerComponent from "./../components/LivePlayer.vue"; +// import LiveHelpComponent from "./../components/LiveHelp.vue"; + +// import DriveComponent from "./../components/Drive.vue"; +// import SettingsComponent from "./../components/Settings.vue"; +// import ProfileComponent from "./components/ProfileNext.vue"; +// import VideosComponent from "./../components/Videos.vue"; +// import GroupsComponent from "./../components/Groups.vue"; + +const router = new VueRouter({ + mode: "history", + linkActiveClass: "active", + + routes: [ + { + path: "/i/web/timeline/:scope", + name: 'timeline', + component: HomeComponent, + props: true + }, + // { + // path: "/i/web/timeline/local", + // component: LocalTimeline + // }, + // { + // path: "/i/web/timeline/global", + // component: GlobalTimeline + // }, + // { + // path: "/i/web/drive", + // name: 'drive', + // component: DriveComponent, + // props: true + // }, + // { + // path: "/i/web/groups", + // name: 'groups', + // component: GroupsComponent, + // props: true + // }, + { + path: "/i/web/post/:id", + name: 'post', + component: PostComponent, + props: true + }, + // { + // path: "/i/web/profile/:id/live", + // component: LivePlayerComponent, + // props: true + // }, + { + path: "/i/web/profile/:id/followers", + name: 'profile-followers', + component: ProfileFollowersComponent, + props: true + }, + { + path: "/i/web/profile/:id/following", + name: 'profile-following', + component: ProfileFollowingComponent, + props: true + }, + { + path: "/i/web/profile/:id", + name: 'profile', + component: ProfileComponent, + props: true + }, + // { + // path: "/i/web/videos", + // component: VideosComponent + // }, + { + path: "/i/web/discover", + component: DiscoverComponent + }, + // { + // path: "/i/web/stories", + // component: HomeComponent + // }, + // { + // path: "/i/web/settings/*", + // component: SettingsComponent, + // props: true + // }, + // { + // path: "/i/web/settings", + // component: SettingsComponent + // }, + { + path: "/i/web/compose", + component: ComposeComponent + }, + { + path: "/i/web/notifications", + component: NotificationsComponent + }, + { + path: "/i/web/direct/thread/:accountId", + component: DirectMessageComponent, + props: true + }, + { + path: "/i/web/direct", + component: DirectComponent + }, + { + path: "/i/web/kb/:id", + name: "kb", + component: KnowledgebaseComponent, + props: true + }, + { + path: "/i/web/hashtag/:id", + name: "hashtag", + component: HashtagComponent, + props: true + }, + { + path: "/i/web/help", + component: HelpComponent + }, + { + path: "/i/web/about", + component: AboutComponent + }, + { + path: "/i/web/contact", + component: ContactComponent + }, + { + path: "/i/web/language", + component: LanguageComponent + }, + { + path: "/i/web/privacy", + component: PrivacyComponent + }, + { + path: "/i/web/terms", + component: TermsComponent + }, + { + path: "/i/web/whats-new", + component: ChangelogComponent + }, + { + path: "/i/web/discover/my-memories", + component: MemoriesComponent + }, + { + path: "/i/web/discover/my-hashtags", + component: MyHashtagComponent + }, + { + path: "/i/web/discover/account-insights", + component: AccountInsightsComponent + }, + { + path: "/i/web/discover/find-friends", + component: DiscoverFindFriendsComponent + }, + { + path: "/i/web/discover/server-timelines", + component: DiscoverServerFeedComponent + }, + { + path: "/i/web/discover/settings", + component: DiscoverSettingsComponent + }, + // { + // path: "/i/web/livestreams", + // component: LivestreamsComponent + // }, + // { + // path: "/i/web/live/help", + // component: LiveHelpComponent + // }, + // { + // path: "/i/web/live/player", + // component: LivePlayerComponent + // }, + // { + // path: "/i/web/live", + // component: LiveComponent + // }, + + { + path: "/i/web", + component: HomeComponent, + props: true + }, + { + path: "/i/web/*", + component: NotFoundComponent, + props: true + }, + ], + + scrollBehavior(to, from, savedPosition) { + if (to.hash) { + return { + selector: `[id='${to.hash.slice(1)}']` + }; + } else { + return { x: 0, y: 0 }; + } + } +}); + +function lss(name, def) { + let key = 'pf_m2s.' + name; + let ls = window.localStorage; + if(ls.getItem(key)) { + let val = ls.getItem(key); + if(['pl', 'color-scheme'].includes(name)) { + return val; + } + return ['true', true].includes(val); + } + return def; +} + +const store = new Vuex.Store({ + state: { + version: 1, + hideCounts: lss('hc', false), + autoloadComments: lss('ac', true), + newReactions: lss('nr', true), + fixedHeight: lss('fh', false), + profileLayout: lss('pl', 'grid'), + showDMPrivacyWarning: lss('dmpwarn', true), + relationships: {}, + emoji: [], + colorScheme: lss('color-scheme', 'system'), + }, + + getters: { + getVersion: state => { + return state.version; + }, + + getHideCounts: state => { + return state.hideCounts; + }, + + getAutoloadComments: state => { + return state.autoloadComments; + }, + + getNewReactions: state => { + return state.newReactions; + }, + + getFixedHeight: state => { + return state.fixedHeight; + }, + + getProfileLayout: state => { + return state.profileLayout; + }, + + getRelationship: (state) => (id) => { + // let rel = state.relationships[id]; + // if(!rel || !rel.hasOwnProperty('id')) { + // return axios.get('/api/pixelfed/v1/accounts/relationships', { + // params: { + // 'id[]': id + // } + // }) + // .then(res => { + // let relationship = res.data; + // // Vue.set(state.relationships, relationship.id, relationship); + // state.commit('updateRelationship', res.data[0]); + // return res.data[0]; + // }) + // .catch(err => { + // return {}; + // }) + // } else { + // return state.relationships[id]; + // } + return state.relationships[id]; + }, + + getCustomEmoji: state => { + return state.emoji; + }, + + getColorScheme: state => { + return state.colorScheme; + }, + + getShowDMPrivacyWarning: state => { + return state.showDMPrivacyWarning; + } + }, + + mutations: { + setVersion(state, value) { + state.version = value; + }, + + setHideCounts(state, value) { + localStorage.setItem('pf_m2s.hc', value); + state.hideCounts = value; + }, + + setAutoloadComments(state, value) { + localStorage.setItem('pf_m2s.ac', value); + state.autoloadComments = value; + }, + + setNewReactions(state, value) { + localStorage.setItem('pf_m2s.nr', value); + state.newReactions = value; + }, + + setFixedHeight(state, value) { + localStorage.setItem('pf_m2s.fh', value); + state.fixedHeight = value; + }, + + setProfileLayout(state, value) { + localStorage.setItem('pf_m2s.pl', value); + state.profileLayout = value; + }, + + updateRelationship(state, relationships) { + relationships.forEach((relationship) => { + Vue.set(state.relationships, relationship.id, relationship) + }) + }, + + updateCustomEmoji(state, emojis) { + state.emoji = emojis; + }, + + setColorScheme(state, value) { + if(state.colorScheme == value) { + return; + } + localStorage.setItem('pf_m2s.color-scheme', value); + state.colorScheme = value; + const name = value == 'system' ? '' : (value == 'light' ? 'force-light-mode' : 'force-dark-mode'); + document.querySelector("body").className = name; + if(name != 'system') { + const payload = name == 'force-dark-mode' ? { dark_mode: 'on' } : {}; + axios.post('/settings/labs', payload); + } + }, + + setShowDMPrivacyWarning(state, value) { + localStorage.setItem('pf_m2s.dmpwarn', value); + state.showDMPrivacyWarning = value; + } + }, +}); + +let i18nMessages = { + en: require('./i18n/en.json'), + ar: require('./i18n/ar.json'), + ca: require('./i18n/ca.json'), + de: require('./i18n/de.json'), + el: require('./i18n/el.json'), + es: require('./i18n/es.json'), + eu: require('./i18n/eu.json'), + fr: require('./i18n/fr.json'), + he: require('./i18n/he.json'), + gd: require('./i18n/gd.json'), + gl: require('./i18n/gl.json'), + id: require('./i18n/id.json'), + it: require('./i18n/it.json'), + ja: require('./i18n/ja.json'), + nl: require('./i18n/nl.json'), + pl: require('./i18n/pl.json'), + pt: require('./i18n/pt.json'), + ru: require('./i18n/ru.json'), + uk: require('./i18n/uk.json'), + vi: require('./i18n/vi.json'), +}; + +let locale = document.querySelector('html').getAttribute('lang'); + +const i18n = new VueI18n({ + locale: locale, // set locale + fallbackLocale: 'en', + messages: i18nMessages +}); + +sync(store, router); + +const App = new Vue({ + el: '#content', + i18n, + router, + store +}); + +axios.get('/api/v1/custom_emojis') +.then(res => { + if(res && res.data && res.data.length) { + store.commit('updateCustomEmoji', res.data); + } +}); + +if(store.state.colorScheme) { + const name = store.state.colorScheme == 'system' ? '' : (store.state.colorScheme == 'light' ? 'force-light-mode' : 'force-dark-mode'); + if(name != 'system') { + document.querySelector("body").className = name; + } +} + +pixelfed.readmore = () => { + $('.read-more').each(function(k,v) { + let el = $(this); + let attr = el.attr('data-readmore'); + if(typeof attr !== typeof undefined && attr !== false) { + return; + } + el.readmore({ + collapsedHeight: 45, + heightMargin: 48, + moreLink: 'Show more', + lessLink: 'Show less', + }); + }); +}; + +try { + document.createEvent("TouchEvent"); + $('body').addClass('touch'); +} catch (e) { +} + +window.App = window.App || {}; + +// window.App.redirect = function() { +// document.querySelectorAll('a').forEach(function(i,k) { +// let a = i.getAttribute('href'); +// if(a && a.length > 5 && a.startsWith('https://')) { +// let url = new URL(a); +// if(url.host !== window.location.host && url.pathname !== '/i/redirect') { +// i.setAttribute('href', '/i/redirect?url=' + encodeURIComponent(a)); +// } +// } +// }); +// } + +// window.App.boot = function() { +// new Vue({ el: '#content'}); +// } + +// window.addEventListener("load", () => { +// if ("serviceWorker" in navigator) { +// navigator.serviceWorker.register("/sw.js"); +// } +// }); + +window.App.util = { + compose: { + post: (function() { + let path = window.location.pathname; + let whitelist = [ + '/', + '/timeline/public' + ]; + if(whitelist.includes(path)) { + $('#composeModal').modal('show'); + } else { + window.location.href = '/?a=co'; + } + }), + circle: (function() { + console.log('Unsupported method.'); + }), + collection: (function() { + console.log('Unsupported method.'); + }), + loop: (function() { + console.log('Unsupported method.'); + }), + story: (function() { + console.log('Unsupported method.'); + }), + }, + time: (function() { + return new Date; + }), + version: 1, + format: { + count: (function(count = 0, locale = 'en-GB', notation = 'compact') { + if(count < 1) { + return 0; + } + return new Intl.NumberFormat(locale, { notation: notation , compactDisplay: "short" }).format(count); + }), + timeAgo: (function(ts) { + let date = Date.parse(ts); + let seconds = Math.floor((new Date() - date) / 1000); + let interval = Math.floor(seconds / 63072000); + if (interval < 0) { + return "0s"; + } + if (interval >= 1) { + return interval + "y"; + } + interval = Math.floor(seconds / 604800); + if (interval >= 1) { + return interval + "w"; + } + interval = Math.floor(seconds / 86400); + if (interval >= 1) { + return interval + "d"; + } + interval = Math.floor(seconds / 3600); + if (interval >= 1) { + return interval + "h"; + } + interval = Math.floor(seconds / 60); + if (interval >= 1) { + return interval + "m"; + } + return Math.floor(seconds) + "s"; + }), + timeAhead: (function(ts, short = true) { + let date = Date.parse(ts); + let diff = date - Date.parse(new Date()); + let seconds = Math.floor((diff) / 1000); + let interval = Math.floor(seconds / 63072000); + if (interval >= 1) { + return interval + (short ? "y" : " years"); + } + interval = Math.floor(seconds / 604800); + if (interval >= 1) { + return interval + (short ? "w" : " weeks"); + } + interval = Math.floor(seconds / 86400); + if (interval >= 1) { + return interval + (short ? "d" : " days"); + } + interval = Math.floor(seconds / 3600); + if (interval >= 1) { + return interval + (short ? "h" : " hours"); + } + interval = Math.floor(seconds / 60); + if (interval >= 1) { + return interval + (short ? "m" : " minutes"); + } + return Math.floor(seconds) + (short ? "s" : " seconds"); + }), + rewriteLinks: (function(i) { + + let tag = i.innerText; + + if(i.href.startsWith(window.location.origin)) { + return i.href; + } + + if(tag.startsWith('#') == true) { + tag = '/discover/tags/' + tag.substr(1) +'?src=rph'; + } else if(tag.startsWith('@') == true) { + tag = '/' + i.innerText + '?src=rpp'; + } else { + tag = '/i/redirect?url=' + encodeURIComponent(tag); + } + + return tag; + }) + }, + filters: [ + ['1984','filter-1977'], + ['Azen','filter-aden'], + ['Astairo','filter-amaro'], + ['Grassbee','filter-ashby'], + ['Bookrun','filter-brannan'], + ['Borough','filter-brooklyn'], + ['Farms','filter-charmes'], + ['Hairsadone','filter-clarendon'], + ['Cleana ','filter-crema'], + ['Catpatch','filter-dogpatch'], + ['Earlyworm','filter-earlybird'], + ['Plaid','filter-gingham'], + ['Kyo','filter-ginza'], + ['Yefe','filter-hefe'], + ['Goddess','filter-helena'], + ['Yards','filter-hudson'], + ['Quill','filter-inkwell'], + ['Rankine','filter-kelvin'], + ['Juno','filter-juno'], + ['Mark','filter-lark'], + ['Chill','filter-lofi'], + ['Van','filter-ludwig'], + ['Apache','filter-maven'], + ['May','filter-mayfair'], + ['Ceres','filter-moon'], + ['Knoxville','filter-nashville'], + ['Felicity','filter-perpetua'], + ['Sandblast','filter-poprocket'], + ['Daisy','filter-reyes'], + ['Elevate','filter-rise'], + ['Nevada','filter-sierra'], + ['Futura','filter-skyline'], + ['Sleepy','filter-slumber'], + ['Steward','filter-stinson'], + ['Savoy','filter-sutro'], + ['Blaze','filter-toaster'], + ['Apricot','filter-valencia'], + ['Gloming','filter-vesper'], + ['Walter','filter-walden'], + ['Poplar','filter-willow'], + ['Xenon','filter-xpro-ii'] + ], + filterCss: { + 'filter-1977': 'sepia(.5) hue-rotate(-30deg) saturate(1.4)', + 'filter-aden': 'sepia(.2) brightness(1.15) saturate(1.4)', + 'filter-amaro': 'sepia(.35) contrast(1.1) brightness(1.2) saturate(1.3)', + 'filter-ashby': 'sepia(.5) contrast(1.2) saturate(1.8)', + 'filter-brannan': 'sepia(.4) contrast(1.25) brightness(1.1) saturate(.9) hue-rotate(-2deg)', + 'filter-brooklyn': 'sepia(.25) contrast(1.25) brightness(1.25) hue-rotate(5deg)', + 'filter-charmes': 'sepia(.25) contrast(1.25) brightness(1.25) saturate(1.35) hue-rotate(-5deg)', + 'filter-clarendon': 'sepia(.15) contrast(1.25) brightness(1.25) hue-rotate(5deg)', + 'filter-crema': 'sepia(.5) contrast(1.25) brightness(1.15) saturate(.9) hue-rotate(-2deg)', + 'filter-dogpatch': 'sepia(.35) saturate(1.1) contrast(1.5)', + 'filter-earlybird': 'sepia(.25) contrast(1.25) brightness(1.15) saturate(.9) hue-rotate(-5deg)', + 'filter-gingham': 'contrast(1.1) brightness(1.1)', + 'filter-ginza': 'sepia(.25) contrast(1.15) brightness(1.2) saturate(1.35) hue-rotate(-5deg)', + 'filter-hefe': 'sepia(.4) contrast(1.5) brightness(1.2) saturate(1.4) hue-rotate(-10deg)', + 'filter-helena': 'sepia(.5) contrast(1.05) brightness(1.05) saturate(1.35)', + 'filter-hudson': 'sepia(.25) contrast(1.2) brightness(1.2) saturate(1.05) hue-rotate(-15deg)', + 'filter-inkwell': 'brightness(1.25) contrast(.85) grayscale(1)', + 'filter-kelvin': 'sepia(.15) contrast(1.5) brightness(1.1) hue-rotate(-10deg)', + 'filter-juno': 'sepia(.35) contrast(1.15) brightness(1.15) saturate(1.8)', + 'filter-lark': 'sepia(.25) contrast(1.2) brightness(1.3) saturate(1.25)', + 'filter-lofi': 'saturate(1.1) contrast(1.5)', + 'filter-ludwig': 'sepia(.25) contrast(1.05) brightness(1.05) saturate(2)', + 'filter-maven': 'sepia(.35) contrast(1.05) brightness(1.05) saturate(1.75)', + 'filter-mayfair': 'contrast(1.1) brightness(1.15) saturate(1.1)', + 'filter-moon': 'brightness(1.4) contrast(.95) saturate(0) sepia(.35)', + 'filter-nashville': 'sepia(.25) contrast(1.5) brightness(.9) hue-rotate(-15deg)', + 'filter-perpetua': 'contrast(1.1) brightness(1.25) saturate(1.1)', + 'filter-poprocket': 'sepia(.15) brightness(1.2)', + 'filter-reyes': 'sepia(.75) contrast(.75) brightness(1.25) saturate(1.4)', + 'filter-rise': 'sepia(.25) contrast(1.25) brightness(1.2) saturate(.9)', + 'filter-sierra': 'sepia(.25) contrast(1.5) brightness(.9) hue-rotate(-15deg)', + 'filter-skyline': 'sepia(.15) contrast(1.25) brightness(1.25) saturate(1.2)', + 'filter-slumber': 'sepia(.35) contrast(1.25) saturate(1.25)', + 'filter-stinson': 'sepia(.35) contrast(1.25) brightness(1.1) saturate(1.25)', + 'filter-sutro': 'sepia(.4) contrast(1.2) brightness(.9) saturate(1.4) hue-rotate(-10deg)', + 'filter-toaster': 'sepia(.25) contrast(1.5) brightness(.95) hue-rotate(-15deg)', + 'filter-valencia': 'sepia(.25) contrast(1.1) brightness(1.1)', + 'filter-vesper': 'sepia(.35) contrast(1.15) brightness(1.2) saturate(1.3)', + 'filter-walden': 'sepia(.35) contrast(.8) brightness(1.25) saturate(1.4)', + 'filter-willow': 'brightness(1.2) contrast(.85) saturate(.05) sepia(.2)', + 'filter-xpro-ii': 'sepia(.45) contrast(1.25) brightness(1.75) saturate(1.3) hue-rotate(-5deg)' + }, + emoji: ['๐Ÿ˜‚','๐Ÿ’ฏ','โค๏ธ','๐Ÿ™Œ','๐Ÿ‘','๐Ÿ‘Œ','๐Ÿ˜','๐Ÿ˜ฏ','๐Ÿ˜ข','๐Ÿ˜…','๐Ÿ˜','๐Ÿ™‚','๐Ÿ˜Ž','๐Ÿ˜€','๐Ÿคฃ','๐Ÿ˜ƒ','๐Ÿ˜„','๐Ÿ˜†','๐Ÿ˜‰','๐Ÿ˜Š','๐Ÿ˜‹','๐Ÿ˜˜','๐Ÿ˜—','๐Ÿ˜™','๐Ÿ˜š','๐Ÿค—','๐Ÿคฉ','๐Ÿค”','๐Ÿคจ','๐Ÿ˜','๐Ÿ˜‘','๐Ÿ˜ถ','๐Ÿ™„','๐Ÿ˜','๐Ÿ˜ฃ','๐Ÿ˜ฅ','๐Ÿ˜ฎ','๐Ÿค','๐Ÿ˜ช','๐Ÿ˜ซ','๐Ÿ˜ด','๐Ÿ˜Œ','๐Ÿ˜›','๐Ÿ˜œ','๐Ÿ˜','๐Ÿคค','๐Ÿ˜’','๐Ÿ˜“','๐Ÿ˜”','๐Ÿ˜•','๐Ÿ™ƒ','๐Ÿค‘','๐Ÿ˜ฒ','๐Ÿ™','๐Ÿ˜–','๐Ÿ˜ž','๐Ÿ˜Ÿ','๐Ÿ˜ค','๐Ÿ˜ญ','๐Ÿ˜ฆ','๐Ÿ˜ง','๐Ÿ˜จ','๐Ÿ˜ฉ','๐Ÿคฏ','๐Ÿ˜ฌ','๐Ÿ˜ฐ','๐Ÿ˜ฑ','๐Ÿ˜ณ','๐Ÿคช','๐Ÿ˜ต','๐Ÿ˜ก','๐Ÿ˜ ','๐Ÿคฌ','๐Ÿ˜ท','๐Ÿค’','๐Ÿค•','๐Ÿคข','๐Ÿคฎ','๐Ÿคง','๐Ÿ˜‡','๐Ÿค ','๐Ÿคก','๐Ÿคฅ','๐Ÿคซ','๐Ÿคญ','๐Ÿง','๐Ÿค“','๐Ÿ˜ˆ','๐Ÿ‘ฟ','๐Ÿ‘น','๐Ÿ‘บ','๐Ÿ’€','๐Ÿ‘ป','๐Ÿ‘ฝ','๐Ÿค–','๐Ÿ’ฉ','๐Ÿ˜บ','๐Ÿ˜ธ','๐Ÿ˜น','๐Ÿ˜ป','๐Ÿ˜ผ','๐Ÿ˜ฝ','๐Ÿ™€','๐Ÿ˜ฟ','๐Ÿ˜พ','๐Ÿคฒ','๐Ÿ‘','๐Ÿค','๐Ÿ‘','๐Ÿ‘Ž','๐Ÿ‘Š','โœŠ','๐Ÿค›','๐Ÿคœ','๐Ÿคž','โœŒ๏ธ','๐ŸคŸ','๐Ÿค˜','๐Ÿ‘ˆ','๐Ÿ‘‰','๐Ÿ‘†','๐Ÿ‘‡','โ˜๏ธ','โœ‹','๐Ÿคš','๐Ÿ–','๐Ÿ––','๐Ÿ‘‹','๐Ÿค™','๐Ÿ’ช','๐Ÿ–•','โœ๏ธ','๐Ÿ™','๐Ÿ’','๐Ÿ’„','๐Ÿ’‹','๐Ÿ‘„','๐Ÿ‘…','๐Ÿ‘‚','๐Ÿ‘ƒ','๐Ÿ‘ฃ','๐Ÿ‘','๐Ÿ‘€','๐Ÿง ','๐Ÿ—ฃ','๐Ÿ‘ค','๐Ÿ‘ฅ' + ], + embed: { + post: (function(url, caption = true, likes = false, layout = 'full') { + let u = url + '/embed?'; + u += caption ? 'caption=true&' : 'caption=false&'; + u += likes ? 'likes=true&' : 'likes=false&'; + u += layout == 'compact' ? 'layout=compact' : 'layout=full'; + return '