diff --git a/CHANGELOG.md b/CHANGELOG.md
index da045abf..69ef83f3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,9 @@
## [Unreleased](https://github.com/pixelfed/pixelfed/compare/v0.11.4...dev)
+### New Features
+- Portfolios ([#3705](https://github.com/pixelfed/pixelfed/pull/3705))
+
### Updates
- Update ApiV1Controller, include self likes in favourited_by endpoint ([58b331d2](https://github.com/pixelfed/pixelfed/commit/58b331d2))
- Update PublicApiController, remove expensive and unused relationships ([2ecc3144](https://github.com/pixelfed/pixelfed/commit/2ecc3144))
diff --git a/app/Http/Controllers/PortfolioController.php b/app/Http/Controllers/PortfolioController.php
new file mode 100644
index 00000000..5890f2d0
--- /dev/null
+++ b/app/Http/Controllers/PortfolioController.php
@@ -0,0 +1,318 @@
+first();
+
+ if(!$user) {
+ return view('portfolio.404');
+ }
+
+ $portfolio = Portfolio::whereUserId($user->id)->firstOrFail();
+ $user = AccountService::get($user->profile_id);
+
+ if($user['locked']) {
+ return view('portfolio.404');
+ }
+
+ if($portfolio->active != true) {
+ if(!$request->user()) {
+ return view('portfolio.404');
+ }
+
+ if($request->user()->profile_id == $user['id']) {
+ return redirect(config('portfolio.path') . '/settings');
+ }
+
+ return view('portfolio.404');
+ }
+
+ return view('portfolio.show', compact('user', 'portfolio'));
+ }
+
+ public function showPost(Request $request, $username, $id)
+ {
+ $authed = $request->user();
+ $post = StatusService::get($id);
+
+ if(!$post) {
+ return view('portfolio.404');
+ }
+
+ $user = AccountService::get($post['account']['id']);
+ $portfolio = Portfolio::whereProfileId($user['id'])->first();
+
+ if($user['locked'] || $portfolio->active != true) {
+ return view('portfolio.404');
+ }
+
+ if(!$post || $post['visibility'] != 'public' || $post['pf_type'] != 'photo' || $user['id'] != $post['account']['id']) {
+ return view('portfolio.404');
+ }
+
+ return view('portfolio.show_post', compact('user', 'post', 'authed'));
+ }
+
+ public function myRedirect(Request $request)
+ {
+ abort_if(!$request->user(), 404);
+
+ $user = $request->user();
+
+ if(Portfolio::whereProfileId($user->profile_id)->exists() === false) {
+ $portfolio = new Portfolio;
+ $portfolio->profile_id = $user->profile_id;
+ $portfolio->user_id = $user->id;
+ $portfolio->active = false;
+ $portfolio->save();
+ }
+
+ $domain = config('portfolio.domain');
+ $path = config('portfolio.path');
+ $url = 'https://' . $domain . $path;
+
+ return redirect($url);
+ }
+
+ public function settings(Request $request)
+ {
+ if(!$request->user()) {
+ return redirect(route('home'));
+ }
+
+ $portfolio = Portfolio::whereUserId($request->user()->id)->first();
+
+ if(!$portfolio) {
+ $portfolio = new Portfolio;
+ $portfolio->user_id = $request->user()->id;
+ $portfolio->profile_id = $request->user()->profile_id;
+ $portfolio->save();
+ }
+
+ return view('portfolio.settings', compact('portfolio'));
+ }
+
+ public function store(Request $request)
+ {
+ abort_unless($request->user(), 404);
+
+ $this->validate($request, [
+ 'profile_source' => 'required|in:recent,custom',
+ 'layout' => 'required|in:grid,masonry',
+ 'layout_container' => 'required|in:fixed,fluid'
+ ]);
+
+ $portfolio = Portfolio::whereUserId($request->user()->id)->first();
+
+ if(!$portfolio) {
+ $portfolio = new Portfolio;
+ $portfolio->user_id = $request->user()->id;
+ $portfolio->profile_id = $request->user()->profile_id;
+ $portfolio->save();
+ }
+
+ $portfolio->active = $request->input('enabled') === 'on';
+ $portfolio->show_captions = $request->input('show_captions') === 'on';
+ $portfolio->show_license = $request->input('show_license') === 'on';
+ $portfolio->show_location = $request->input('show_location') === 'on';
+ $portfolio->show_timestamp = $request->input('show_timestamp') === 'on';
+ $portfolio->show_link = $request->input('show_link') === 'on';
+ $portfolio->profile_source = $request->input('profile_source');
+ $portfolio->show_avatar = $request->input('show_avatar') === 'on';
+ $portfolio->show_bio = $request->input('show_bio') === 'on';
+ $portfolio->profile_layout = $request->input('layout');
+ $portfolio->profile_container = $request->input('layout_container');
+ $portfolio->save();
+
+ return redirect('/' . $request->user()->username);
+ }
+
+ public function getFeed(Request $request, $id)
+ {
+ $user = AccountService::get($id, true);
+
+ if(!$user || !isset($user['id'])) {
+ return response()->json([], 404);
+ }
+
+ $portfolio = Portfolio::whereProfileId($user['id'])->first();
+
+ if(!$portfolio || !$portfolio->active) {
+ return response()->json([], 404);
+ }
+
+ if($portfolio->profile_source === 'custom' && $portfolio->metadata) {
+ return $this->getCustomFeed($portfolio);
+ }
+
+ return $this->getRecentFeed($user['id']);
+ }
+
+ protected function getCustomFeed($portfolio) {
+ if(!$portfolio->metadata['posts']) {
+ return response()->json([], 400);
+ }
+
+ return collect($portfolio->metadata['posts'])->map(function($p) {
+ return StatusService::get($p);
+ })
+ ->filter(function($p) {
+ return $p && isset($p['account']);
+ })->values();
+ }
+
+ protected function getRecentFeed($id) {
+ $media = Cache::remember('portfolio:recent-feed:' . $id, 3600, function() use($id) {
+ return DB::table('media')
+ ->whereProfileId($id)
+ ->whereNotNull('status_id')
+ ->groupBy('status_id')
+ ->orderByDesc('id')
+ ->take(50)
+ ->pluck('status_id');
+ });
+
+ return $media->map(function($sid) use($id) {
+ return StatusService::get($sid);
+ })
+ ->filter(function($post) {
+ return $post &&
+ isset($post['media_attachments']) &&
+ !empty($post['media_attachments']) &&
+ $post['pf_type'] === 'photo' &&
+ $post['visibility'] === 'public';
+ })
+ ->take(24)
+ ->values();
+ }
+
+ public function getSettings(Request $request)
+ {
+ abort_if(!$request->user(), 403);
+
+ $res = Portfolio::whereUserId($request->user()->id)->get();
+
+ if(!$res) {
+ return [];
+ }
+
+ return $res->map(function($p) {
+ return [
+ 'url' => $p->url(),
+ 'pid' => (string) $p->profile_id,
+ 'active' => (bool) $p->active,
+ 'show_captions' => (bool) $p->show_captions,
+ 'show_license' => (bool) $p->show_license,
+ 'show_location' => (bool) $p->show_location,
+ 'show_timestamp' => (bool) $p->show_timestamp,
+ 'show_link' => (bool) $p->show_link,
+ 'show_avatar' => (bool) $p->show_avatar,
+ 'show_bio' => (bool) $p->show_bio,
+ 'profile_layout' => $p->profile_layout,
+ 'profile_source' => $p->profile_source,
+ 'metadata' => $p->metadata
+ ];
+ })->first();
+ }
+
+ public function getAccountSettings(Request $request)
+ {
+ $this->validate($request, [
+ 'id' => 'required|integer'
+ ]);
+
+ $account = AccountService::get($request->input('id'));
+
+ abort_if(!$account, 404);
+
+ $p = Portfolio::whereProfileId($request->input('id'))->whereActive(1)->firstOrFail();
+
+ if(!$p) {
+ return [];
+ }
+
+ return [
+ 'url' => $p->url(),
+ 'show_captions' => (bool) $p->show_captions,
+ 'show_license' => (bool) $p->show_license,
+ 'show_location' => (bool) $p->show_location,
+ 'show_timestamp' => (bool) $p->show_timestamp,
+ 'show_link' => (bool) $p->show_link,
+ 'show_avatar' => (bool) $p->show_avatar,
+ 'show_bio' => (bool) $p->show_bio,
+ 'profile_layout' => $p->profile_layout,
+ 'profile_source' => $p->profile_source
+ ];
+ }
+
+ public function storeSettings(Request $request)
+ {
+ abort_if(!$request->user(), 403);
+
+ $this->validate($request, [
+ 'profile_layout' => 'sometimes|in:grid,masonry,album'
+ ]);
+
+ $res = Portfolio::whereUserId($request->user()->id)
+ ->update($request->only([
+ 'active',
+ 'show_captions',
+ 'show_license',
+ 'show_location',
+ 'show_timestamp',
+ 'show_link',
+ 'show_avatar',
+ 'show_bio',
+ 'profile_layout',
+ 'profile_source'
+ ]));
+
+ Cache::forget('portfolio:recent-feed:' . $request->user()->profile_id);
+
+ return 200;
+ }
+
+ public function storeCurated(Request $request)
+ {
+ abort_if(!$request->user(), 403);
+
+ $this->validate($request, [
+ 'ids' => 'required|array|max:24'
+ ]);
+
+ $pid = $request->user()->profile_id;
+
+ $ids = $request->input('ids');
+
+ Status::whereProfileId($pid)
+ ->whereScope('public')
+ ->whereIn('type', ['photo', 'photo:album'])
+ ->findOrFail($ids);
+
+ $p = Portfolio::whereProfileId($pid)->firstOrFail();
+ $p->metadata = ['posts' => $ids];
+ $p->save();
+
+ Cache::forget('portfolio:recent-feed:' . $pid);
+
+ return $request->ids;
+ }
+}
diff --git a/app/Models/Portfolio.php b/app/Models/Portfolio.php
new file mode 100644
index 00000000..758e8db4
--- /dev/null
+++ b/app/Models/Portfolio.php
@@ -0,0 +1,39 @@
+ 'json'
+ ];
+
+ public function url()
+ {
+ $account = AccountService::get($this->profile_id);
+ if(!$account) {
+ return null;
+ }
+
+ return 'https://' . config('portfolio.domain') . config('portfolio.path') . '/' . $account['username'];
+ }
+}
diff --git a/config/portfolio.php b/config/portfolio.php
new file mode 100644
index 00000000..9b4cd8f4
--- /dev/null
+++ b/config/portfolio.php
@@ -0,0 +1,31 @@
+ env('PORTFOLIO_DOMAIN', config('pixelfed.domain.app')),
+
+ /*
+ |--------------------------------------------------------------------------
+ | Portfolio Path
+ |--------------------------------------------------------------------------
+ |
+ | This value is the path used for the portfolio feature. Only change
+ | the default value if you have a subdomain configured. If you want
+ | to use the root path of the subdomain, leave this value empty.
+ |
+ | WARNING: SETTING THIS VALUE WITHOUT A SUBDOMAIN COULD BREAK YOUR
+ | INSTANCE, SO ONLY CHANGE THIS IF YOU KNOW WHAT YOU'RE DOING.
+ |
+ */
+ 'path' => env('PORTFOLIO_PATH', '/i/portfolio'),
+];
diff --git a/database/migrations/2022_01_16_060052_create_portfolios_table.php b/database/migrations/2022_01_16_060052_create_portfolios_table.php
new file mode 100644
index 00000000..73d639cc
--- /dev/null
+++ b/database/migrations/2022_01_16_060052_create_portfolios_table.php
@@ -0,0 +1,45 @@
+id();
+ $table->unsignedInteger('user_id')->nullable()->unique()->index();
+ $table->bigInteger('profile_id')->unsigned()->unique()->index();
+ $table->boolean('active')->nullable()->index();
+ $table->boolean('show_captions')->default(true)->nullable();
+ $table->boolean('show_license')->default(true)->nullable();
+ $table->boolean('show_location')->default(true)->nullable();
+ $table->boolean('show_timestamp')->default(true)->nullable();
+ $table->boolean('show_link')->default(true)->nullable();
+ $table->string('profile_source')->default('recent')->nullable();
+ $table->boolean('show_avatar')->default(true)->nullable();
+ $table->boolean('show_bio')->default(true)->nullable();
+ $table->string('profile_layout')->default('grid')->nullable();
+ $table->string('profile_container')->default('fixed')->nullable();
+ $table->json('metadata')->nullable();
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::dropIfExists('portfolios');
+ }
+}
diff --git a/public/css/portfolio.css b/public/css/portfolio.css
new file mode 100644
index 00000000..13ce2964
--- /dev/null
+++ b/public/css/portfolio.css
@@ -0,0 +1 @@
+@font-face{font-display:swap;font-family:Inter;font-style:normal;font-weight:100;src:url(/fonts/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0100-024f,U+0259,U+1e??,U+2020,U+20a0-20ab,U+20ad-20cf,U+2113,U+2c60-2c7f,U+a720-a7ff}@font-face{font-display:swap;font-family:Inter;font-style:normal;font-weight:100;src:url(/fonts/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) format("woff2");unicode-range:U+00??,U+0131,U+0152-0153,U+02bb-02bc,U+02c6,U+02da,U+02dc,U+2000-206f,U+2074,U+20ac,U+2122,U+2191,U+2193,U+2212,U+2215,U+feff,U+fffd}@font-face{font-display:swap;font-family:Inter;font-style:normal;font-weight:400;src:url(/fonts/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0100-024f,U+0259,U+1e??,U+2020,U+20a0-20ab,U+20ad-20cf,U+2113,U+2c60-2c7f,U+a720-a7ff}@font-face{font-display:swap;font-family:Inter;font-style:normal;font-weight:400;src:url(/fonts/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) format("woff2");unicode-range:U+00??,U+0131,U+0152-0153,U+02bb-02bc,U+02c6,U+02da,U+02dc,U+2000-206f,U+2074,U+20ac,U+2122,U+2191,U+2193,U+2212,U+2215,U+feff,U+fffd}@font-face{font-display:swap;font-family:Inter;font-style:normal;font-weight:700;src:url(/fonts/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2) format("woff2");unicode-range:U+0100-024f,U+0259,U+1e??,U+2020,U+20a0-20ab,U+20ad-20cf,U+2113,U+2c60-2c7f,U+a720-a7ff}@font-face{font-display:swap;font-family:Inter;font-style:normal;font-weight:700;src:url(/fonts/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2) format("woff2");unicode-range:U+00??,U+0131,U+0152-0153,U+02bb-02bc,U+02c6,U+02da,U+02dc,U+2000-206f,U+2074,U+20ac,U+2122,U+2191,U+2193,U+2212,U+2215,U+feff,U+fffd}body{background:#000;color:#d4d4d8;font-family:Inter,sans-serif;font-weight:400!important}.text-primary{color:#3b82f6!important}.font-weight-light,.lead{font-weight:400!important}a{color:#3b82f6;text-decoration:none}.text-gradient-primary{-webkit-text-fill-color:transparent;background:linear-gradient(90deg,#6366f1,#8b5cf6,#d946ef);-webkit-background-clip:text}.logo-mark{background:#212529;border:6px solid #212529;border-radius:1rem;color:#fff!important;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif!important;font-size:2.5rem;font-weight:700!important;letter-spacing:-1.5px;line-height:1.2;text-decoration:none!important;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}@media(min-width:768px){.logo-mark{font-size:4.5rem}}.logo-mark-sm{background:#212529;border-radius:10px;border-width:3px;font-size:16px!important;letter-spacing:-1px}.display-4.font-weight-bold{letter-spacing:-.3px;text-transform:uppercase}@media(min-width:768px){.display-4.font-weight-bold{letter-spacing:-3px}}.display-4.font-weight-bold a{color:#d1d5db;text-decoration:underline}.display-4{font-size:1.5rem}@media(min-width:768px){.display-4{font-size:3.5rem}}.btn-primary{background-color:#3b82f6}.card-columns{-moz-column-count:3;column-count:3;-moz-column-gap:0;column-gap:0;orphans:1;widows:1}.portfolio-settings .nav-pills .nav-item.disabled span{color:#3f3f46;pointer-events:none}.portfolio-settings .nav-pills .nav-link{color:#9ca3af;font-size:15px;font-weight:400}.portfolio-settings .nav-pills .nav-link.active{background-image:linear-gradient(90deg,#4f46e5 0,#2f80ed 51%,#4f46e5);background-size:200% auto;color:#fff;font-weight:100;transition:.5s}.portfolio-settings .nav-pills .nav-link.active:hover{background-position:100%}.portfolio-settings .card-header{background-color:#000;border:1px solid var(--dark);color:var(--muted);font-size:14px;font-weight:400;text-transform:uppercase}.portfolio-settings .card .list-group-item{background:transparent}.portfolio-settings .custom-select{background:#000 url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='4' height='5'%3E%3Cpath fill='%23343a40' d='M2 0 0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") right .75rem center/8px 10px no-repeat;border-color:var(--dark);border-radius:10px;color:#fff;font-weight:700;padding-left:20px}.portfolio-settings .selected-badge{align-items:center;background-color:#0284c7;border:2px solid #fff;border-radius:26px;color:#fff;display:flex;font-size:14px;font-weight:700;height:26px;justify-content:center;width:26px}.slide-fade-enter-active{transition:all .3s ease}.slide-fade-leave-active{transition:all .3s cubic-bezier(1,1)}.slide-fade-enter,.slide-fade-leave-to{opacity:0;transform:translateX(10px)}
diff --git a/public/fonts/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2 b/public/fonts/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2
new file mode 100644
index 00000000..980853fe
Binary files /dev/null and b/public/fonts/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2 differ
diff --git a/public/fonts/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2 b/public/fonts/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2
new file mode 100644
index 00000000..edd83468
Binary files /dev/null and b/public/fonts/UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2 differ
diff --git a/public/js/app.js b/public/js/app.js
index 7194d17e..d0a7b5ae 100644
--- a/public/js/app.js
+++ b/public/js/app.js
@@ -1,2 +1,2 @@
/*! For license information please see app.js.LICENSE.txt */
-(self.webpackChunkpixelfed=self.webpackChunkpixelfed||[]).push([[773],{50313:(e,t,r)=>{r(19755);var o=r(19755);r(86807),window._=r(96486),window.Popper=r(28981).default,window.pixelfed=window.pixelfed||{},window.$=r(19755),r(43734),window.axios=r(9669),window.axios.defaults.headers.common["X-Requested-With"]="XMLHttpRequest",r(90717),window.blurhash=r(43985);var n=document.head.querySelector('meta[name="csrf-token"]');n?window.axios.defaults.headers.common["X-CSRF-TOKEN"]=n.content:console.error("CSRF token not found."),window.App=window.App||{},window.App.redirect=function(){document.querySelectorAll("a").forEach((function(e,t){var r=e.getAttribute("href");if(r&&r.length>5&&r.startsWith("https://")){var o=new URL(r);o.host!==window.location.host&&"/i/redirect"!==o.pathname&&e.setAttribute("href","/i/redirect?url="+encodeURIComponent(r))}}))},window.App.boot=function(){new Vue({el:"#content"})},window.App.util={compose:{post:function(){var e=window.location.pathname;["/","/timeline/public"].includes(e)?o("#composeModal").modal("show"):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(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"en-GB",r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"compact";return e<1?0:new Intl.NumberFormat(t,{notation:r,compactDisplay:"short"}).format(e)},timeAgo:function(e){var t=Date.parse(e),r=Math.floor((new Date-t)/1e3),o=Math.floor(r/63072e3);return o>=1?o+"y":(o=Math.floor(r/604800))>=1?o+"w":(o=Math.floor(r/86400))>=1?o+"d":(o=Math.floor(r/3600))>=1?o+"h":(o=Math.floor(r/60))>=1?o+"m":Math.floor(r)+"s"},timeAhead:function(e){var t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],r=Date.parse(e),o=r-Date.parse(new Date),n=Math.floor(o/1e3),i=Math.floor(n/63072e3);return i>=1?i+(t?"y":" years"):(i=Math.floor(n/604800))>=1?i+(t?"w":" weeks"):(i=Math.floor(n/86400))>=1?i+(t?"d":" days"):(i=Math.floor(n/3600))>=1?i+(t?"h":" hours"):(i=Math.floor(n/60))>=1?i+(t?"m":" minutes"):Math.floor(n)+(t?"s":" seconds")},rewriteLinks:function(e){var t=e.innerText;return e.href.startsWith(window.location.origin)?e.href:t=1==t.startsWith("#")?"/discover/tags/"+t.substr(1)+"?src=rph":1==t.startsWith("@")?"/"+e.innerText+"?src=rpp":"/i/redirect?url="+encodeURIComponent(t)}},filters:[["1977","filter-1977"],["Aden","filter-aden"],["Amaro","filter-amaro"],["Ashby","filter-ashby"],["Brannan","filter-brannan"],["Brooklyn","filter-brooklyn"],["Charmes","filter-charmes"],["Clarendon","filter-clarendon"],["Crema","filter-crema"],["Dogpatch","filter-dogpatch"],["Earlybird","filter-earlybird"],["Gingham","filter-gingham"],["Ginza","filter-ginza"],["Hefe","filter-hefe"],["Helena","filter-helena"],["Hudson","filter-hudson"],["Inkwell","filter-inkwell"],["Kelvin","filter-kelvin"],["Kuno","filter-juno"],["Lark","filter-lark"],["Lo-Fi","filter-lofi"],["Ludwig","filter-ludwig"],["Maven","filter-maven"],["Mayfair","filter-mayfair"],["Moon","filter-moon"],["Nashville","filter-nashville"],["Perpetua","filter-perpetua"],["Poprocket","filter-poprocket"],["Reyes","filter-reyes"],["Rise","filter-rise"],["Sierra","filter-sierra"],["Skyline","filter-skyline"],["Slumber","filter-slumber"],["Stinson","filter-stinson"],["Sutro","filter-sutro"],["Toaster","filter-toaster"],["Valencia","filter-valencia"],["Vesper","filter-vesper"],["Walden","filter-walden"],["Willow","filter-willow"],["X-Pro II","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(e){var t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],r=arguments.length>2&&void 0!==arguments[2]&&arguments[2],o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"full",n=e+"/embed?";return n+=t?"caption=true&":"caption=false&",n+=r?"likes=true&":"likes=false&",'