diff --git a/replay.pokemonshowdown.com/.htaccess b/replay.pokemonshowdown.com/.htaccess index 9d85282fe..f160c89a1 100644 --- a/replay.pokemonshowdown.com/.htaccess +++ b/replay.pokemonshowdown.com/.htaccess @@ -11,7 +11,7 @@ RewriteRule ^([A-Za-z0-9-]+)$ caches/index.php?name=$1 [L,QSA] RewriteRule ^search\.json$ /api/replays/search.json [L,QSA] RewriteRule ^recent\.json$ /api/replays/search.json [L,QSA] RewriteRule ^download$ download.html [L,QSA] -RewriteRule ^([A-Za-z0-9-]+)/manage$ replay-manage.php?name=$1&manage [L,QSA] +RewriteRule ^([A-Za-z0-9-]+)/manage$ /$1?manage [R=302,L,QSA] # RewriteRule ^([A-Za-z0-9-]+)\.log$ replay.log.php?name=$1 [L,QSA] # RewriteRule ^([A-Za-z0-9-]+)\.inputlog$ replay.log.php?inputlog&name=$1 [L,QSA] diff --git a/replay.pokemonshowdown.com/index.php b/replay.pokemonshowdown.com/index.php index 041674b24..73b290c7d 100644 --- a/replay.pokemonshowdown.com/index.php +++ b/replay.pokemonshowdown.com/index.php @@ -180,6 +180,30 @@ https://replay.pokemonshowdown.com/gen7randomdoublesbattle-865046831.log .mainbar.has-sidebar { margin-left: 330px; } + .bar-wrapper.has-sidebar:not(.short-viewport) .mainbar { + position: sticky; + top: 0; + } + .bar-wrapper.has-sidebar.short-viewport { + max-width: none; + } + .bar-wrapper.has-sidebar.short-viewport .sidebar { + box-sizing: border-box; + position: sticky; + top: 0; + max-height: 100vh; + overflow-y: auto; + } + @media (min-width: 1431px) and (max-width: 1510px) { + .bar-wrapper.has-sidebar.short-viewport .sidebar { + width: calc(50% - 395px); + padding-left: calc(50% - 715px); + } + .bar-wrapper.has-sidebar.short-viewport .mainbar { + margin-right: calc(50% - 715px); + margin-left: calc(50% - 385px); + } + } @media (min-width: 1511px) { .sidebar { width: 400px; @@ -190,6 +214,14 @@ https://replay.pokemonshowdown.com/gen7randomdoublesbattle-865046831.log .mainbar.has-sidebar { margin-left: 410px; } + .bar-wrapper.has-sidebar.short-viewport .sidebar { + width: calc(50% - 355px); + padding-left: calc(50% - 755px); + } + .bar-wrapper.has-sidebar.short-viewport .mainbar { + margin-right: calc(50% - 755px); + margin-left: calc(50% - 345px); + } } .section.first-section { margin-top: 9px; diff --git a/replay.pokemonshowdown.com/replay-manage.php b/replay.pokemonshowdown.com/replay-manage.php deleted file mode 100644 index 5cd111492..000000000 --- a/replay.pokemonshowdown.com/replay-manage.php +++ /dev/null @@ -1,223 +0,0 @@ -isLeader()) die("access denied"); -$csrfOk = !!$users->csrfCheck(); -$manage = true; -header('Cache-Control: max-age=0, no-cache, no-store, must-revalidate'); - -if (preg_match('/[^A-Za-z0-9-]/', $id)) die("access denied"); - -$fullid = $id; -if (substr($id, -2) === 'pw') { - $dashpos = strrpos($id, '-'); - $password = substr($id, $dashpos + 1, -2); - $id = substr($id, 0, $dashpos); - // die($id . ' ' . $password); -} - -$replay = null; -$cached = false; - -// $forcecache = isset($_REQUEST['forcecache8723']); -$forcecache = false; - -if (file_exists('caches/' . $id . '.inc.php')) { - include 'caches/' . $id . '.inc.php'; - $replay['formatid'] = ''; - $cached = true; -} else { - require_once 'replays.lib.php'; - if (!$Replays->db && !$forcecache) { - include '503.php'; - die(); - } - $replay = $Replays->get($id, $forcecache); -} -if (!$replay || (@$replay['private'] === 3 && !$manage)) { - include '404.php'; - die(); -} -$fullid = $id . (@$replay['password'] ? '-' . $replay['password'] . 'pw' : ''); - -if (@$replay['private']) { - header('X-Robots-Tag: noindex'); -} -if (@$replay['password']) { - if (!$password && !$manage) { - header('Cache-Control: max-age=0, no-cache, no-store, must-revalidate'); - require_once '../lib/ntbb-session.lib.php'; - if ($curuser['userid'] !== $p1id && $curuser['userid'] !== $p2id) { - die("Access denied (you must be logged into " . $p1id . " or " . $p2id . ")"); - } - $url = '/' . $id . '-' . $replay['password'] . 'pw'; - echo '

This private replay now has a new harder-to-guess URL:

'; - echo '

https://' . $psconfig['routes']['replays'] . $url . '

'; - die(); - } - if ($password !== $replay['password'] && !$manage) { - die("Access denied (please ask " . $p1id . " or " . $p2id . " for the password)"); - } -} - -if ($forcecache) { - file_put_contents('caches/' . $id . '.inc.php', 'userid($p1); -$p2id = $users->userid($p2); - -$panels->setPageTitle($format.' replay: '.$p1.' vs. '.$p2); -$panels->setPageDescription('Watch a replay of a Pokémon battle between ' . $p1 . ' and ' . $p2 . ' (' . $format . ')'); -$panels->setTab('replay'); -$panels->start(); - -?> -
-
- -
-
-
- -
-
-
- Speed: -
-
-
- Color scheme: -
-
- -
- - - THIS REPLAY IS PRIVATE - make sure you have the owner\'s permission to share
'; ?> - -
- -

: vs.

-

- Uploaded: Rating: ' . $replay['rating'] : '' ?> -

- -
-edit($replay); - echo '

Edited.

'; - } -?> - Change privacy:
- csrfData(); ?> - - -
-
- csrfData(); ?> - - -
-
- csrfData(); ?> - - -
-
- csrfData(); ?> - - -
- -
- - - - - - - - - -output === 'normal') { -?> -
- -
- - - replays - -
-end(); - -?> diff --git a/replay.pokemonshowdown.com/src/replay.js b/replay.pokemonshowdown.com/src/replay.js deleted file mode 100644 index 8f9be18b0..000000000 --- a/replay.pokemonshowdown.com/src/replay.js +++ /dev/null @@ -1,243 +0,0 @@ -// preserved for replay management - -/* function updateProgress(done, a, b) -{ - if (!battle.paused) return; - //$('#battle').html('
'+(done?'':'
Buffering: '+parseInt(100*a/b)+'%')+'
'); -} */ -//setTimeout(function(){updateProgress(true)}, 10000); - -// Panels - -var Topbar = Panels.Topbar.extend({ - height: 51 -}); - -var ReplaySidebarPanel = Panels.StaticPanel.extend({ - minWidth: 300, - maxWidth: 600, - events: { - 'click button[name=moreResults]': 'moreResults' - }, - moreResults: function(e) { - var elem = e.currentTarget; - elem.disabled = true; - // var offset = elem.value; - var page = Number(elem.value); - if (!page) page = 2; - var user = elem.dataset.user; - var format = elem.dataset.format; - var priv = !!elem.dataset.private; - var self = this; - elem.innerHTML = 'Loading...
' - $.get('/search', Object.assign({ - user: user, - format: format, - page: page, - output: 'html' - }, priv ? {private: 1} : {}), function (data) { - self.$('ul.linklist').append(data); - // var $nextOffset = self.$('input.offset'); - // var val = $nextOffset.val(); - // $nextOffset.remove(); - var $more = self.$('input.more'); - if (!$more.length) { - $(elem).remove(); - return; - } else { - $more.remove(); - } - elem.innerHTML = 'More
' - elem.disabled = false; - elem.value = page + 1; - }); - } -}); - -var ReplayPanel = Panels.StaticPanel.extend({ - minWidth: 960, - maxWidth: 1180, - moveTo: function() { - Panels.Panel.prototype.moveTo.apply(this, arguments); - this.$el.css('top', $(window).height() > 495 ? '51px' : '0'); - }, - events: { - 'click .chooser button': 'clickChangeSetting', - 'click .replayDownloadButton': 'clickReplayDownloadButton' - }, - clickChangeSetting: function(e) { - e.preventDefault(); - var $chooser = $(e.currentTarget).closest('.chooser'); - var value = e.currentTarget.value; - this.changeSetting($chooser, value, $(e.currentTarget)); - }, - changeSetting: function(type, value, valueElem) { - var $chooser; - if (typeof type === 'string') { - $chooser = this.$('.'+type+'chooser'); - } else { - $chooser = type; - type = ''; - if ($chooser.hasClass('colorchooser')) { - type = 'color'; - } else if ($chooser.hasClass('soundchooser')) { - type = 'sound'; - } else if ($chooser.hasClass('speedchooser')) { - type = 'speed'; - } - } - if (!valueElem) valueElem = $chooser.find('button[value='+value+']'); - - $chooser.find('button').removeClass('sel'); - valueElem.addClass('sel'); - - switch (type) { - case 'color': - if (value === 'dark') { - $(document.body).addClass('dark'); - } else { - $(document.body).removeClass('dark'); - } - break; - - case 'sound': - // remember this is reversed: sound[off] === muted[true] - this.muted = (value === 'off'); - this.battle.setMute(this.muted); - this.$('.startsoundchooser').remove(); - break; - - case 'speed': - var fadeTable = { - hyperfast: 40, - fast: 50, - normal: 300, - slow: 500, - reallyslow: 1000 - }; - var delayTable = { - hyperfast: 1, - fast: 1, - normal: 1, - slow: 1000, - reallyslow: 3000 - }; - this.battle.messageShownTime = delayTable[value]; - this.battle.messageFadeTime = fadeTable[value]; - this.battle.scene.updateAcceleration(); - break; - } - }, - battle: null, - muted: null, - updateContent: function() { - this.$el.css('overflow-x', 'hidden'); - var $battle = this.$('.battle'); - if (!$battle.length) return; - - var replayid = this.$('input[name=replayid]').val() || ''; - var log = (this.$('script.log').text() || '').replace(/\\\//g,'/'); - - var self = this; - this.battle = new Battle({ - id: replayid, - $frame: $battle, - $logFrame: this.$('.battle-log'), - log: log.split('\n'), - isReplay: true, - paused: true - }) - - this.$('.urlbox').css('margin-right', 120).before(' Download'); - - // this works around a WebKit/Blink bug relating to float layout - var rc2 = this.$('.replay-controls-2')[0]; - if (rc2) rc2.innerHTML = rc2.innerHTML; - - if (window.HTMLAudioElement) this.$('.soundchooser, .startsoundchooser').show(); - - this.battle.subscribe(function (state) { self.update(state); }); - this.update(); - }, - update: function (state) { - if (state === 'error') { - var m = /^([a-z0-9]+)-[a-z0-9]+-[0-9]+$/.exec(this.battle.id); - if (m) { - this.battle.log('
This replay was uploaded from a third-party server (' + BattleLog.escapeHTML(m[1]) + '). It contains errors.
Replays uploaded from third-party servers can contain errors if the server is running custom code, or the server operator has otherwise incorrectly configured their server.
', true); - } - return; - } - - if (BattleSound.muted && !this.muted) this.changeSetting('sound', 'off'); - - if (this.battle.paused) { - var resetDisabled = !this.battle.started ? ' disabled' : ''; - this.$('.replay-controls').html(' '); - } else { - this.$('.replay-controls').html(' '); - } - }, - clickReplayDownloadButton: function (e) { - var filename = (this.battle.tier || 'Battle').replace(/[^A-Za-z0-9]/g, ''); - - // ladies and gentlemen, JavaScript dates - var timestamp = parseInt(this.$('.uploaddate').data('timestamp'), 10) * 1000; - var date = new Date(timestamp); - filename += '-' + date.getFullYear(); - filename += (date.getMonth() >= 9 ? '-' : '-0') + (date.getMonth() + 1); - filename += (date.getDate() >= 10 ? '-' : '-0') + date.getDate(); - - filename += '-' + toID(this.battle.p1.name); - filename += '-' + toID(this.battle.p2.name); - - e.currentTarget.href = BattleLog.createReplayFileHref(this); - e.currentTarget.download = filename + '.html'; - - e.stopPropagation(); - }, - pause: function() { - this.battle.pause(); - }, - play: function() { - this.battle.play(); - }, - reset: function() { - this.battle.reset(); - }, - ff: function() { - this.battle.skipTurn(); - }, - rewind: function() { - this.battle.seekBy(-1); - }, - ffto: function() { - var turn = prompt('Turn?'); - if (!turn.trim()) return; - if (turn === 'e' || turn === 'end' || turn === 'f' || turn === 'finish') turn = Infinity; - turn = Number(turn); - if (isNaN(turn) || turn < 0) alert("Invalid turn"); - this.battle.seekTurn(turn); - }, - switchViewpoint: function() { - this.battle.switchViewpoint(); - }, - remove: function() { - this.battle.destroy(); - Panels.StaticPanel.prototype.remove.call(this); - }, -}); - -var App = Panels.App.extend({ - topbarView: Topbar, - states: { - '*path': ReplaySidebarPanel, // catch-all default - - ':replay': ReplayPanel, - ':replay/manage': ReplayPanel, - 'search': ReplaySidebarPanel, - 'search/': ReplaySidebarPanel, - 'search:query': ReplaySidebarPanel, - 'search/:query': ReplaySidebarPanel - }, -}); -var app = new App(); diff --git a/replay.pokemonshowdown.com/src/replays-battle.tsx b/replay.pokemonshowdown.com/src/replays-battle.tsx index aa5f1cf35..93ebac422 100644 --- a/replay.pokemonshowdown.com/src/replays-battle.tsx +++ b/replay.pokemonshowdown.com/src/replays-battle.tsx @@ -56,7 +56,7 @@ class BattleLogDiv extends preact.Component { } } -export class BattlePanel extends preact.Component<{ id: string }> { +export class BattlePanel extends preact.Component<{ id: string, user: PSReplays['user'] }> { result: { uploadtime: number, id: string, @@ -66,9 +66,12 @@ export class BattlePanel extends preact.Component<{ id: string }> { views: number, rating: number, private: number, - password: string, + password: string | null, } | null | undefined = undefined; resultError = ''; + privacySaving: number | null = null; + manageError = ''; + manageOpen = false; battle!: Battle | null; /** debug purposes */ lastUsedKeyCode = '0'; @@ -81,17 +84,28 @@ export class BattlePanel extends preact.Component<{ id: string }> { } override componentWillReceiveProps(nextProps: this['props']) { if (this.stripQuery(this.props.id) !== this.stripQuery(nextProps.id)) { + if (this.replayID(this.props.id) !== this.replayID(nextProps.id)) { + this.manageOpen = false; + } this.loadBattle(nextProps.id); } } stripQuery(id: string) { return id.includes('?') ? id.slice(0, id.indexOf('?')) : id; } + replayID(id: string) { + const fullid = this.stripQuery(id); + if (!fullid.endsWith('pw')) return fullid; + const passwordIndex = fullid.lastIndexOf('-'); + return passwordIndex > 0 ? fullid.slice(0, passwordIndex) : fullid; + } loadBattle(id: string) { if (this.battle) this.battle.destroy(); this.battle = null; this.result = undefined; this.resultError = ''; + this.privacySaving = null; + this.manageError = ''; this.forceUpdate(); const elem = document.getElementById(`replaydata-${id}`); @@ -367,9 +381,97 @@ export class BattlePanel extends preact.Component<{ id: string }> { e?.preventDefault(); this.forceUpdate(); }; - renderError(position: any) { + manageURL(extension: 'json' | 'log' | 'inputlog') { + return `${Net.defaultRoute}/${this.stripQuery(this.props.id)}.${extension}?manage`; + } + toggleManage = () => { + this.manageOpen = !this.manageOpen; + this.forceUpdate(); + }; + currentPrivacy() { + if (!this.result) return 0; + if (this.result.private === 1 && !this.result.password) return 2; + return this.result.private; + } + shareURL() { + if (!this.result) return ''; + const fullid = this.result.id + (this.result.password ? `-${this.result.password}pw` : ''); + return `https://psim.us/r/${fullid}`; + } + selectShareURL = (e: Event) => { + (e.currentTarget as HTMLInputElement).select(); + }; + changePrivacy = (e: Event) => { + const privateValue = Number((e.target as HTMLSelectElement).value); + if (!this.result || this.privacySaving !== null) return; + if (privateValue === 3 && !confirm("Delete this replay?")) return; + + const replay = this.result; + this.privacySaving = privateValue; + this.manageError = ''; + this.forceUpdate(); + + Net(`/api/replays/edit`).post({}, { + id: replay.id, + private: privateValue, + }).then(resultText => { + if (resultText.startsWith(']')) resultText = resultText.slice(1); + const result = JSON.parse(resultText); + if (result.actionerror) throw new Error(result.actionerror); + if (this.result !== replay) return; + + replay.private = privateValue; + replay.password = result.password || null; + this.privacySaving = null; + + const fullid = replay.id + (replay.password ? `-${replay.password}pw` : ''); + PSRouter.replace(fullid); + PSRouter.update(); + }).catch(error => { + if (this.result !== replay) return; + this.privacySaving = null; + this.manageError = error instanceof Error ? error.message : String(error); + this.forceUpdate(); + }); + }; + renderManagement() { + if (!this.manageOpen) return null; + if (!this.props.user?.isLeader) { + return

You do not have permission to manage this replay.

; + } + const privacy = this.currentPrivacy(); + return
+ +

Manage replay

+

+ Privacy: {} + + + + {} + {this.privacySaving !== null && Saving...} +

+ {this.manageError &&

{this.manageError}

} +

+ JSON {} + Log {} + Input log +

+
; + } + renderError() { if (this.resultError) { - return
+ return

Error

@@ -383,7 +485,7 @@ export class BattlePanel extends preact.Component<{ id: string }> { // never link to a nonexistent replay, but this might happen if e.g. // a replay gets deleted or made private after you searched for it // but before you clicked it. - return

+ return
@@ -506,36 +608,48 @@ export class BattlePanel extends preact.Component<{ id: string }> {

{this.result ?

- {this.result.format}: {this.result.players.join(' vs. ')} + {this.result.format}: {} + {!!this.result.private && } {this.result.players.join(' vs. ')}

:

Loading...

} + {!!this.result?.private &&

+ PRIVATE - make sure you have the owner's permission to share +

} +

+ +

{this.result ?

- - Download - + + {this.props.user?.isLeader && } {} + + Download + + {this.result.uploadtime ? new Date(this.result.uploadtime * 1000).toDateString() : "Unknown upload date"} {this.result.rating ? [` | `, Rating:, ` ${this.result.rating}`] : ''} {/* {} {this.keyCode} */}

:

 

} + {this.renderManagement()} {!PSRouter.showingLeft() &&

More replays

}
; } override render() { - let position: any = {}; - if (PSRouter.showingLeft()) { - if (PSRouter.stickyRight) { - position = { position: 'sticky', top: '0' }; - } else { - position = { position: 'sticky', bottom: '0' }; - } - } + if (this.result === null) return this.renderError(); - if (this.result === null) return this.renderError(position); - - return
+ return
diff --git a/replay.pokemonshowdown.com/src/replays-index.tsx b/replay.pokemonshowdown.com/src/replays-index.tsx index 4a7e37e72..778b35fa8 100644 --- a/replay.pokemonshowdown.com/src/replays-index.tsx +++ b/replay.pokemonshowdown.com/src/replays-index.tsx @@ -1,6 +1,6 @@ /** @jsx preact.h */ import preact from '../../play.pokemonshowdown.com/js/lib/preact'; -import { PSRouter } from "./replays"; +import { type PSReplays, PSRouter } from "./replays"; import { Net } from './utils'; type ID = Lowercase; @@ -37,7 +37,7 @@ function ReplayLink(props: { ; } -export class SearchPanel extends preact.Component<{ id: string }> { +export class SearchPanel extends preact.Component<{ id: string, user: PSReplays['user'] }> { results: ReplayResult[] | null = null; resultError: string | null = null; format = ''; @@ -45,17 +45,8 @@ export class SearchPanel extends preact.Component<{ id: string }> { isPrivate = false; byRating = false; page = 1; - loggedInUser: string | null = null; - loggedInUserIsSysop = false; sort = 'date'; override componentDidMount() { - if (!Net.defaultRoute) Net(`/api/replays/check-login`).get().then(result => { - if (!result.startsWith(']')) return; - const [userid, sysop] = result.slice(1).split(','); - this.loggedInUser = userid; - this.loggedInUserIsSysop = !!sysop; - this.forceUpdate(); - }); this.updateSearch(Net.decodeQuery(this.props.id)); } override componentDidUpdate(previousProps: this['props']) { @@ -171,8 +162,8 @@ export class SearchPanel extends preact.Component<{ id: string }> { this.search('', ''); }; searchLoggedIn = (e: Event) => { - if (!this.loggedInUser) return; // shouldn't happen - this.base!.querySelector('input[name=user]')!.value = this.loggedInUser; + if (!this.props.user) return; // shouldn't happen + this.base!.querySelector('input[name=user]')!.value = this.props.user.id; this.submitForm(e); }; url(replay: ReplayResult) { @@ -214,8 +205,10 @@ export class SearchPanel extends preact.Component<{ id: string }> {

diff --git a/replay.pokemonshowdown.com/src/replays.tsx b/replay.pokemonshowdown.com/src/replays.tsx index 0a4983852..966b7e818 100644 --- a/replay.pokemonshowdown.com/src/replays.tsx +++ b/replay.pokemonshowdown.com/src/replays.tsx @@ -3,6 +3,7 @@ import preact from '../../play.pokemonshowdown.com/js/lib/preact'; import { Net, PSModel } from './utils'; import { BattlePanel } from './replays-battle'; import { SearchPanel } from './replays-index'; +import type { ID } from '../../play.pokemonshowdown.com/src/battle-dex'; declare const Config: any; export const PSRouter = new class extends PSModel { @@ -10,7 +11,7 @@ export const PSRouter = new class extends PSModel { leftLoc: string | null = null; rightLoc: string | null = null; forceSinglePanel = false; - stickyRight = true; + shortViewport = false; constructor() { super(); const baseLocSlashIndex = document.location.href.lastIndexOf('/'); @@ -39,15 +40,16 @@ export const PSRouter = new class extends PSModel { } setSinglePanel(init?: boolean) { const singlePanel = window.innerWidth < 1300; - const stickyRight = (window.innerHeight > 614); - if (this.forceSinglePanel !== singlePanel || this.stickyRight !== stickyRight) { + const shortViewport = window.innerHeight <= 660; + if (this.forceSinglePanel !== singlePanel || this.shortViewport !== shortViewport) { this.forceSinglePanel = singlePanel; - this.stickyRight = stickyRight; + this.shortViewport = shortViewport; if (!init) this.update(); } } push(href: string): boolean { if (!href.startsWith(this.baseLoc)) return false; + if (href.includes('.json') || href.includes('.log') || href.includes('.inputlog')) return false; if (this.go(href)) { window.history?.pushState([this.leftLoc, this.rightLoc], '', href); @@ -95,6 +97,7 @@ export const PSRouter = new class extends PSModel { export class PSReplays extends preact.Component { static darkMode: 'dark' | 'light' | 'auto' = 'auto'; + user: { id: ID, isLeader: boolean } | null = null; static updateDarkMode() { let dark = this.darkMode === 'dark' ? 'dark' : ''; if (this.darkMode === 'auto') { @@ -122,15 +125,21 @@ export class PSReplays extends preact.Component { const data = JSON.parse(response); Object.assign(Config.customcolors, data); }); + if (!Net.defaultRoute) Net(`/api/replays/check-login`).get().then(result => { + if (!result.startsWith(']')) return; + const [userid, isLeader] = result.slice(1).split(','); + this.user = { id: userid as ID, isLeader: !!isLeader }; + this.forceUpdate(); + }); } override render() { - const position = PSRouter.showingLeft() && PSRouter.showingRight() && !PSRouter.stickyRight ? - { display: 'flex', flexDirection: 'column', justifyContent: 'flex-end' } : {}; - return

- {PSRouter.showingLeft() && } - {PSRouter.showingRight() && } + const twoColumn = PSRouter.showingLeft() && PSRouter.showingRight(); + const className = `bar-wrapper${twoColumn ? ' has-sidebar' : ''}${ + twoColumn && PSRouter.shortViewport ? ' short-viewport' : '' + }`; + return
+ {PSRouter.showingLeft() && } + {PSRouter.showingRight() && }
; } diff --git a/replay.pokemonshowdown.com/testclient.html b/replay.pokemonshowdown.com/testclient.html index ddb9f3282..2d0d59254 100644 --- a/replay.pokemonshowdown.com/testclient.html +++ b/replay.pokemonshowdown.com/testclient.html @@ -62,6 +62,30 @@ .mainbar.has-sidebar { margin-left: 330px; } + .bar-wrapper.has-sidebar:not(.short-viewport) .mainbar { + position: sticky; + top: 0; + } + .bar-wrapper.has-sidebar.short-viewport { + max-width: none; + } + .bar-wrapper.has-sidebar.short-viewport .sidebar { + box-sizing: border-box; + position: sticky; + top: 0; + max-height: 100vh; + overflow-y: auto; + } + @media (min-width: 1431px) and (max-width: 1510px) { + .bar-wrapper.has-sidebar.short-viewport .sidebar { + width: calc(50% - 395px); + padding-left: calc(50% - 715px); + } + .bar-wrapper.has-sidebar.short-viewport .mainbar { + margin-right: calc(50% - 715px); + margin-left: calc(50% - 385px); + } + } @media (min-width: 1511px) { .sidebar { width: 400px; @@ -72,6 +96,14 @@ .mainbar.has-sidebar { margin-left: 410px; } + .bar-wrapper.has-sidebar.short-viewport .sidebar { + width: calc(50% - 355px); + padding-left: calc(50% - 755px); + } + .bar-wrapper.has-sidebar.short-viewport .mainbar { + margin-right: calc(50% - 755px); + margin-left: calc(50% - 345px); + } } .section.first-section { margin-top: 9px; diff --git a/replay.pokemonshowdown.com/theme/panels.lib.php b/replay.pokemonshowdown.com/theme/panels.lib.php deleted file mode 100644 index f1731eb73..000000000 --- a/replay.pokemonshowdown.com/theme/panels.lib.php +++ /dev/null @@ -1,77 +0,0 @@ -root = '/'; - $this->name = 'Showdown'; - - switch (@$_REQUEST['output']) { - case 'html': - case 'json': - $this->output = $_REQUEST['output']; - break; - } - } - - var $pagetitle = false; - function setPageTitle($title) { - if ($this->pagetitle === false) { - $this->pagetitle = $title; - } - } - - var $pagedescription = false; - function setPageDescription($description) { - if ($this->pagedescription === false) { - $this->pagedescription = $description; - } - } - - function setTab($tab) { - if ($this->tab === false) { - $this->tab = $tab; - } - } - - var $depth = 0; - var $scriptsIncluded = false; - function start() { - $this->depth++; - if ($this->depth != 1) { - return; - } - if ($this->output === 'normal') { - ThemeHeaderTemplate(); - } - } - function noScripts() { - $this->scriptsIncluded = true; - } - function scripts() { - if ($this->output !== 'normal') return; - if ($this->scriptsIncluded) return; - $this->scriptsIncluded = true; - ThemeScriptsTemplate(); - } - function end() { - $this->depth--; - if ($this->depth != 0) { - return; - } - if ($this->output === 'normal') { - ThemeFooterTemplate(); - } - } -} - -$panels = new Panels(); diff --git a/replay.pokemonshowdown.com/theme/wrapper.inc.template.php b/replay.pokemonshowdown.com/theme/wrapper.inc.template.php deleted file mode 100644 index 876f722e9..000000000 --- a/replay.pokemonshowdown.com/theme/wrapper.inc.template.php +++ /dev/null @@ -1,110 +0,0 @@ - - - - - - - <?php if ($panels->pagetitle) echo htmlspecialchars($panels->pagetitle).' - '; ?>Pokémon Showdown - -pagedescription) { ?> - - - - - - - - - - - - - - - - - - -
-
- - -
-
-
- - - - - - -scripts(); ?> - - - - - - - - - - - - - - - - - - - -