mirror of
https://github.com/smogon/pokemon-showdown-client.git
synced 2026-10-02 07:42:12 -05:00
Migrate replay management to New Replays
This involved redesigning the left-right scroll system on low-height displays a little bit
This commit is contained in:
@@ -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]
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,223 +0,0 @@
|
||||
<?php
|
||||
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', TRUE);
|
||||
ini_set('display_startup_errors', TRUE);
|
||||
|
||||
require_once __DIR__ . '/../config/config.inc.php';
|
||||
require_once __DIR__ . '/theme/panels.lib.php';
|
||||
|
||||
$id = $_REQUEST['name'] ?? '';
|
||||
$password = '';
|
||||
|
||||
if (!$id) {
|
||||
include '404.php';
|
||||
die();
|
||||
}
|
||||
$manage = false;
|
||||
$csrfOk = false;
|
||||
|
||||
// this no longer needs to be in an if block, since the only reason to access
|
||||
// this at /manage is for managing. just assume they're always doing that
|
||||
require_once '../lib/ntbb-session.lib.php';
|
||||
if (!$users->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 '<p>This private replay now has a new harder-to-guess URL:</p>';
|
||||
echo '<p><a href="' . $url . '" data-target="replace">https://' . $psconfig['routes']['replays'] . $url . '</a></p>';
|
||||
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', '<?php $replay = ' . var_export($replay, true) . ';');
|
||||
}
|
||||
|
||||
function userid($username) {
|
||||
if (!$username) $username = '';
|
||||
$username = strtr($username, "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "abcdefghijklmnopqrstuvwxyz");
|
||||
return preg_replace('/[^A-Za-z0-9]+/','',$username);
|
||||
}
|
||||
|
||||
$replay['log'] = str_replace("\r","",$replay['log']);
|
||||
|
||||
// $matchSuccess = preg_match('/\\|player\\|p1\\|([^|]*)(\\|[^|]*)?\\n\\|player\\|p2\\|([^|]+)(\\|[^|]*)?\\n(\\|gametype\\|[^|]*\\n)?\\|tier\\|([^|]*)\\n/', $replay['log'], $matches);
|
||||
$matchSuccess = preg_match('/\\n\\|tier\\|([^|]*)\\n/', $replay['log'], $matches);
|
||||
$format = $replay['format'];
|
||||
if ($matchSuccess) $format = $matches[1];
|
||||
$p1 = $replay['players'][0];
|
||||
$p2 = $replay['players'][1];
|
||||
$p1id = $users->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();
|
||||
|
||||
?>
|
||||
<div class="pfx-panel"><div class="pfx-body" style="max-width:1180px">
|
||||
<div class="wrapper replay-wrapper">
|
||||
|
||||
<div class="battle"><div class="playbutton"><button disabled>Loading...</button></div></div>
|
||||
<div class="battle-log"></div>
|
||||
<div class="replay-controls">
|
||||
<button data-action="start"><i class="fa fa-play"></i> Play</button>
|
||||
</div>
|
||||
<div class="replay-controls-2">
|
||||
<div class="chooser leftchooser speedchooser">
|
||||
<em>Speed:</em>
|
||||
<div><button value="hyperfast">Hyperfast</button><button value="fast">Fast</button><button value="normal" class="sel">Normal</button><button value="slow">Slow</button><button value="reallyslow">Really Slow</button></div>
|
||||
</div>
|
||||
<div class="chooser colorchooser">
|
||||
<em>Color scheme:</em>
|
||||
<div><button class="sel" value="light">Light</button><button value="dark">Dark</button></div>
|
||||
</div>
|
||||
<div class="chooser soundchooser" style="display:none">
|
||||
<em>Music:</em>
|
||||
<div><button class="sel" value="on">On</button><button value="off">Off</button></div>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if lte IE 8]>
|
||||
<div class="error"><p>ಠ_ಠ <strong>You're using an old version of Internet Explorer.</strong></p>
|
||||
<p>We use some transparent backgrounds, rounded corners, and other effects that your old version of IE doesn't support.</p>
|
||||
<p>Please install <em>one</em> of these: <a href="http://www.google.com/chrome">Chrome</a> | <a href="http://www.mozilla.org/en-US/firefox/">Firefox</a> | <a href="http://windows.microsoft.com/en-US/internet-explorer/products/ie/home">Internet Explorer 9</a></p></div>
|
||||
<![endif]-->
|
||||
|
||||
<?php if (@$replay['private']) echo '<strong>THIS REPLAY IS PRIVATE</strong> - make sure you have the owner\'s permission to share<br />'; ?>
|
||||
|
||||
<pre class="urlbox" style="word-wrap: break-word;"><?php echo htmlspecialchars('https://'.$psconfig['routes']['replays'].'/'.$fullid); ?></pre>
|
||||
|
||||
<h1 style="font-weight:normal;text-align:left"><strong><?= htmlspecialchars($format) ?></strong>: <a href="//<?= $psconfig['routes']['users'] ?>/<?= userid($p1) ?>" class="subtle"><?= htmlspecialchars($p1) ?></a> vs. <a href="//<?= $psconfig['routes']['users'] ?>/<?= userid($p2) ?>" class="subtle"><?= htmlspecialchars($p2) ?></a></h1>
|
||||
<p style="padding:0 1em;margin-top:0">
|
||||
<small class="uploaddate" data-timestamp="<?= @$replay['uploadtime'] ?? @$replay['date'] ?>"><em>Uploaded:</em> <?php echo date("M j, Y", @$replay['uploadtime'] ?? @$replay['date']); ?><?= @$replay['rating'] ? ' | <em>Rating:</em> ' . $replay['rating'] : '' ?></small>
|
||||
</p>
|
||||
|
||||
<div id="loopcount"></div>
|
||||
<?php
|
||||
if ($manage) {
|
||||
if ($csrfOk && isset($_POST['private'])) {
|
||||
$replay['private'] = intval($_POST['private']);
|
||||
$Replays->edit($replay);
|
||||
echo '<p>Edited.</p>';
|
||||
}
|
||||
?>
|
||||
Change privacy: <form action="/<?= $replay['id'] ?>/manage" method="post" style="display: inline" data-target="replace">
|
||||
<?php $users->csrfData(); ?>
|
||||
<input type="hidden" name="private" value="3" />
|
||||
<button type="submit" name="private" value="3"<?= @$replay['private'] === 3 ? ' disabled' : '' ?>>Deleted</button>
|
||||
</form>
|
||||
<form action="/<?= $replay['id'] ?>/manage" method="post" style="display: inline" data-target="replace">
|
||||
<?php $users->csrfData(); ?>
|
||||
<input type="hidden" name="private" value="1" />
|
||||
<button type="submit" name="private" value="1"<?= @$replay['private'] === 1 && $replay['password'] ? ' disabled' : '' ?>>Private</button>
|
||||
</form>
|
||||
<form action="/<?= $replay['id'] ?>/manage" method="post" style="display: inline" data-target="replace">
|
||||
<?php $users->csrfData(); ?>
|
||||
<input type="hidden" name="private" value="2" />
|
||||
<button type="submit" name="private" value="2"<?= @$replay['private'] === 1 && !$replay['password'] ? ' disabled' : '' ?>>Private (no password)</button>
|
||||
</form>
|
||||
<form action="/<?= $replay['id'] ?>/manage" method="post" style="display: inline" data-target="replace">
|
||||
<?php $users->csrfData(); ?>
|
||||
<input type="hidden" name="private" value="0" />
|
||||
<button type="submit" name="private" value="0"<?= !@$replay['private'] ? ' disabled' : '' ?>>Public</button>
|
||||
</form>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="replayid" value="<?php echo htmlspecialchars($replay['id']); ?>" />
|
||||
<!--
|
||||
|
||||
You can get this log directly at https://<?php echo $psconfig['routes']['replays']; ?>/<?php echo $replay['id']; ?>.log
|
||||
|
||||
Or with metadata at https://<?php echo $psconfig['routes']['replays']; ?>/<?php echo $replay['id']; ?>.json
|
||||
|
||||
Most PS pages you'd want to scrape will have a .json version!
|
||||
|
||||
-->
|
||||
<script type="text/plain" class="log"><?php if ($replay['id'] === 'smogtours-ou-509') readfile('js/smogtours-ou-509.log'); else if ($replay['id'] === 'ou-305002749') readfile('js/ou-305002749.log'); else echo str_replace('/','\\/',$replay['log']); ?></script>
|
||||
<?php
|
||||
if (substr($replay['formatid'], -12) === 'randombattle' || substr($replay['formatid'], -19) === 'randomdoublesbattle' || $replay['formatid'] === 'gen7challengecup' || $replay['formatid'] === 'gen7challengecup1v1' || $replay['formatid'] === 'gen7battlefactory' || $replay['formatid'] === 'gen7bssfactory' || $replay['formatid'] === 'gen7hackmonscup' || $manage) {
|
||||
?>
|
||||
|
||||
<script type="text/plain" class="inputlog"><?php echo str_replace('</','<\\/',$replay['inputlog']); ?></script>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
<?php
|
||||
if ($panels->output === 'normal') {
|
||||
?>
|
||||
<div><script>
|
||||
google_ad_client = "ca-pub-6535472412829264";
|
||||
/* PS replay */
|
||||
google_ad_slot = "6865298132";
|
||||
google_ad_width = 728;
|
||||
google_ad_height = 90;
|
||||
</script>
|
||||
<script src="//pagead2.googlesyndication.com/pagead/show_ads.js"></script>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
|
||||
<a href="/" class="pfx-backbutton" data-target="back"><i class="fa fa-chevron-left"></i> <?= $cached ? 'Other' : 'More' ?> replays</a>
|
||||
|
||||
</div></div>
|
||||
<?php
|
||||
|
||||
$panels->end();
|
||||
|
||||
?>
|
||||
@@ -1,243 +0,0 @@
|
||||
// preserved for replay management
|
||||
|
||||
/* function updateProgress(done, a, b)
|
||||
{
|
||||
if (!battle.paused) return;
|
||||
//$('#battle').html('<div class="playbutton"><button data-action="start">Play</button>'+(done?'':'<br />Buffering: '+parseInt(100*a/b)+'%')+'</div>');
|
||||
} */
|
||||
//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...<br /><i class="fa fa-caret-down"></i>'
|
||||
$.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<br /><i class="fa fa-caret-down"></i>'
|
||||
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('<a class="button replayDownloadButton" style="float:right;margin-top:7px;margin-right:7px" href="#"><i class="fa fa-download"></i> Download</a>');
|
||||
|
||||
// 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('<hr /><div class="chat">This replay was uploaded from a third-party server (<code>' + BattleLog.escapeHTML(m[1]) + '</code>). It contains errors.</div><div class="chat">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.</div>', 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('<button data-action="play"><i class="fa fa-play"></i> Play</button><button data-action="reset"' + resetDisabled + '><i class="fa fa-undo"></i> Reset</button> <button data-action="rewind"><i class="fa fa-step-backward"></i> Last turn</button><button data-action="ff"><i class="fa fa-step-forward"></i> Next turn</button> <button data-action="ffto"><i class="fa fa-fast-forward"></i> Go to turn...</button> <button data-action="switchViewpoint"><i class="fa fa-random"></i> Switch sides</button>');
|
||||
} else {
|
||||
this.$('.replay-controls').html('<button data-action="pause"><i class="fa fa-pause"></i> Pause</button><button data-action="reset"><i class="fa fa-undo"></i> Reset</button> <button data-action="rewind"><i class="fa fa-step-backward"></i> Last turn</button><button data-action="ff"><i class="fa fa-step-forward"></i> Next turn</button> <button data-action="ffto"><i class="fa fa-fast-forward"></i> Go to turn...</button> <button data-action="switchViewpoint"><i class="fa fa-random"></i> Switch sides</button>');
|
||||
}
|
||||
},
|
||||
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();
|
||||
@@ -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 <p class="section message-error">You do not have permission to manage this replay.</p>;
|
||||
}
|
||||
const privacy = this.currentPrivacy();
|
||||
return <section class="section" style={{ clear: 'right', marginTop: '12px', marginRight: '0', maxWidth: '500px' }}>
|
||||
<button type="button" class="button" style="float:right" onClick={this.toggleManage}>
|
||||
<i class="fa fa-times"></i> Close
|
||||
</button>
|
||||
<h2 style="margin-top:0">Manage replay</h2>
|
||||
<p>
|
||||
Privacy: {}
|
||||
<button class="button button-first" disabled={privacy === 0} value={0} onClick={this.changePrivacy}>
|
||||
Public
|
||||
</button>
|
||||
<button class="button button-middle" disabled={privacy === 1} value={1} onClick={this.changePrivacy}>
|
||||
Private
|
||||
</button>
|
||||
<button class="button button-middle" disabled={privacy === 2} value={2} onClick={this.changePrivacy}>
|
||||
Private (no password)
|
||||
</button>
|
||||
<button class="button button-last" disabled={privacy === 3} value={3} onClick={this.changePrivacy}>
|
||||
Deleted
|
||||
</button> {}
|
||||
{this.privacySaving !== null && <em class="button cur">Saving...</em>}
|
||||
</p>
|
||||
{this.manageError && <p class="message-error">{this.manageError}</p>}
|
||||
<p>
|
||||
<a class="button" href={this.manageURL('json')}>JSON</a> {}
|
||||
<a class="button" href={this.manageURL('log')}>Log</a> {}
|
||||
<a class="button" href={this.manageURL('inputlog')}>Input log</a>
|
||||
</p>
|
||||
</section>;
|
||||
}
|
||||
renderError() {
|
||||
if (this.resultError) {
|
||||
return <div class={PSRouter.showingLeft() ? 'mainbar has-sidebar' : 'mainbar'} style={position}>
|
||||
return <div class={PSRouter.showingLeft() ? 'mainbar has-sidebar' : 'mainbar'}>
|
||||
<section class="section">
|
||||
<h1>Error</h1>
|
||||
<p>
|
||||
@@ -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 <div class={PSRouter.showingLeft() ? 'mainbar has-sidebar' : 'mainbar'} style={position}>
|
||||
return <div class={PSRouter.showingLeft() ? 'mainbar has-sidebar' : 'mainbar'}>
|
||||
<section class="section" style={{ maxWidth: '200px' }}>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<img src="//play.pokemonshowdown.com/sprites/gen5ani/unown-n.gif" alt="" style={{ imageRendering: 'pixelated' }} />
|
||||
@@ -506,36 +608,48 @@ export class BattlePanel extends preact.Component<{ id: string }> {
|
||||
</label>
|
||||
</p>
|
||||
{this.result ? <h1>
|
||||
<strong>{this.result.format}</strong>: {this.result.players.join(' vs. ')}
|
||||
<strong>{this.result.format}</strong>: {}
|
||||
{!!this.result.private && <i class="fa fa-lock" aria-hidden></i>} {this.result.players.join(' vs. ')}
|
||||
</h1> : <h1>
|
||||
<em>Loading...</em>
|
||||
</h1>}
|
||||
{!!this.result?.private && <p>
|
||||
<strong><i class="fa fa-lock" aria-hidden></i> PRIVATE</strong> - make sure you have the owner's permission to share
|
||||
</p>}
|
||||
<p>
|
||||
<label>
|
||||
Short URL: <input
|
||||
name="shareurl" type="text" class="textbox" readOnly size={60}
|
||||
style="max-width:99%;box-sizing:border-box;field-sizing:content;padding-right:20px"
|
||||
value={this.shareURL()} onFocus={this.selectShareURL}
|
||||
/>
|
||||
</label>
|
||||
</p>
|
||||
{this.result ? <p>
|
||||
<a class="button" href="/download" onClick={this.clickDownload} style={{ float: 'right' }}>
|
||||
<i class="fa fa-download" aria-hidden></i> Download
|
||||
</a>
|
||||
<span style={{ float: 'right' }}>
|
||||
{this.props.user?.isLeader && <button
|
||||
type="button" class={`button${this.manageOpen ? ' cur' : ''}`} onClick={this.toggleManage}
|
||||
>
|
||||
<i class="fa fa-wrench" aria-hidden></i> Manage
|
||||
</button>} {}
|
||||
<a class="button" href="/download" onClick={this.clickDownload}>
|
||||
<i class="fa fa-download" aria-hidden></i> Download
|
||||
</a>
|
||||
</span>
|
||||
{this.result.uploadtime ? new Date(this.result.uploadtime * 1000).toDateString() : "Unknown upload date"}
|
||||
{this.result.rating ? [` | `, <em>Rating:</em>, ` ${this.result.rating}`] : ''}
|
||||
{/* {} <code>{this.keyCode}</code> */}
|
||||
</p> : <p> </p>}
|
||||
{this.renderManagement()}
|
||||
{!PSRouter.showingLeft() && <p>
|
||||
<a href={PSRouter.href(PSRouter.leftLoc)} class="button"><i class="fa fa-caret-left" aria-hidden></i> More replays</a>
|
||||
</p>}
|
||||
</div>;
|
||||
}
|
||||
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 <div class={PSRouter.showingLeft() ? 'mainbar has-sidebar' : 'mainbar'} style={position}>
|
||||
return <div class={PSRouter.showingLeft() ? 'mainbar has-sidebar' : 'mainbar'}>
|
||||
<div style={{ position: 'relative' }}>
|
||||
<BattleDiv />
|
||||
<BattleLogDiv />
|
||||
|
||||
@@ -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<string>;
|
||||
@@ -37,7 +37,7 @@ function ReplayLink(props: {
|
||||
</a>;
|
||||
}
|
||||
|
||||
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<HTMLInputElement>('input[name=user]')!.value = this.loggedInUser;
|
||||
if (!this.props.user) return; // shouldn't happen
|
||||
this.base!.querySelector<HTMLInputElement>('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 }> {
|
||||
<label>
|
||||
Username: <small class="gray">(separate multiple usernames by commas)</small><br />
|
||||
<input type="search" class="textbox" name="user" placeholder="(blank = any user)" size={20} /> {}
|
||||
{this.loggedInUser &&
|
||||
<button type="button" class="button" onClick={this.searchLoggedIn}>{this.loggedInUser}'s replays</button>}
|
||||
{this.props.user &&
|
||||
<button type="button" class="button" onClick={this.searchLoggedIn}>
|
||||
{this.props.user.id}'s replays
|
||||
</button>}
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
|
||||
@@ -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 <div
|
||||
class={'bar-wrapper' + (PSRouter.showingLeft() && PSRouter.showingRight() ? ' has-sidebar' : '')} style={position}
|
||||
>
|
||||
{PSRouter.showingLeft() && <SearchPanel id={PSRouter.leftLoc!} />}
|
||||
{PSRouter.showingRight() && <BattlePanel id={PSRouter.rightLoc!} />}
|
||||
const twoColumn = PSRouter.showingLeft() && PSRouter.showingRight();
|
||||
const className = `bar-wrapper${twoColumn ? ' has-sidebar' : ''}${
|
||||
twoColumn && PSRouter.shortViewport ? ' short-viewport' : ''
|
||||
}`;
|
||||
return <div class={className}>
|
||||
{PSRouter.showingLeft() && <SearchPanel id={PSRouter.leftLoc!} user={this.user} />}
|
||||
{PSRouter.showingRight() && <BattlePanel id={PSRouter.rightLoc!} user={this.user} />}
|
||||
<div style={{ clear: 'both' }}></div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
<?php
|
||||
|
||||
include_once dirname(__FILE__).'/wrapper.inc.php';
|
||||
|
||||
class Panels {
|
||||
var $output = 'normal';
|
||||
var $tab = false;
|
||||
|
||||
var $root;
|
||||
var $name;
|
||||
|
||||
function __construct() {
|
||||
global $psconfig;
|
||||
|
||||
$this->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();
|
||||
@@ -1,110 +0,0 @@
|
||||
<?php
|
||||
|
||||
// NOTE: this is Old Replays. Mostly unused except for `/manage`
|
||||
|
||||
if ((substr($_SERVER['REMOTE_ADDR'],0,11) === '69.164.163.') ||
|
||||
(substr(@$_SERVER['HTTP_X_FORWARDED_FOR'],0,11) === '69.164.163.')) {
|
||||
die('website disabled');
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Header
|
||||
********************************************************************/
|
||||
|
||||
function ThemeHeaderTemplate() {
|
||||
global $panels;
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
|
||||
<meta charset="utf-8" />
|
||||
|
||||
<title><?php if ($panels->pagetitle) echo htmlspecialchars($panels->pagetitle).' - '; ?>Pokémon Showdown</title>
|
||||
|
||||
<?php if ($panels->pagedescription) { ?>
|
||||
<meta name="description" content="<?php echo htmlspecialchars($panels->pagedescription); ?>" />
|
||||
<?php } ?>
|
||||
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=IE8" />
|
||||
<link rel="stylesheet" href="//play.pokemonshowdown.com/style/font-awesome.css" />
|
||||
<link rel="stylesheet" href="//pokemonshowdown.com/theme/panels.css" />
|
||||
<link rel="stylesheet" href="//pokemonshowdown.com/theme/main.css" />
|
||||
<link rel="stylesheet" href="//play.pokemonshowdown.com/style/battle.css?" />
|
||||
<link rel="stylesheet" href="//play.pokemonshowdown.com/style/replay.css?" />
|
||||
<link rel="stylesheet" href="//play.pokemonshowdown.com/style/utilichart.css?" />
|
||||
|
||||
<!-- Workarounds for IE bugs to display trees correctly. -->
|
||||
<!--[if lte IE 6]><style> li.tree { height: 1px; } </style><![endif]-->
|
||||
<!--[if IE 7]><style> li.tree { zoom: 1; } </style><![endif]-->
|
||||
|
||||
<script type="text/javascript">
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-26211653-1']);
|
||||
_gaq.push(['_setDomainName', 'pokemonshowdown.com']);
|
||||
_gaq.push(['_setAllowLinker', true]);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
</script>
|
||||
</head><body>
|
||||
|
||||
<div class="pfx-topbar">
|
||||
<div class="header">
|
||||
<ul class="nav">
|
||||
<li><a class="button nav-first<?php if ($panels->tab === 'home') echo ' cur'; ?>" href="//pokemonshowdown.com/"><img src="//pokemonshowdown.com/images/pokemonshowdownbeta.png" alt="Pokémon Showdown! (beta)" /> Home</a></li>
|
||||
<li><a class="button<?php if ($panels->tab === 'pokedex') echo ' cur'; ?>" href="//dex.pokemonshowdown.com/">Pokédex</a></li>
|
||||
<li><a class="button<?php if ($panels->tab === 'replay') echo ' cur'; ?>" href="/">Replay</a></li>
|
||||
<li><a class="button purplebutton" href="//smogon.com/dex/" target="_blank">Strategy</a></li>
|
||||
<li><a class="button nav-last purplebutton" href="//smogon.com/forums/" target="_blank">Forum</a></li>
|
||||
</ul>
|
||||
<ul class="nav nav-play">
|
||||
<li><a class="button greenbutton nav-first nav-last" href="http://play.pokemonshowdown.com/">Play</a></li>
|
||||
</ul>
|
||||
<div style="clear:both"></div>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* Footer
|
||||
********************************************************************/
|
||||
|
||||
function ThemeScriptsTemplate() {
|
||||
?>
|
||||
<script src="//play.pokemonshowdown.com/js/lib/jquery-1.11.0.min.js"></script>
|
||||
<script src="//play.pokemonshowdown.com/js/lib/lodash.core.js"></script>
|
||||
<script src="//play.pokemonshowdown.com/js/lib/backbone.js"></script>
|
||||
<script src="//dex.pokemonshowdown.com/js/panels.js"></script>
|
||||
<?php
|
||||
}
|
||||
|
||||
function ThemeFooterTemplate() {
|
||||
global $panels;
|
||||
?>
|
||||
<?php $panels->scripts(); ?>
|
||||
|
||||
<script src="//play.pokemonshowdown.com/js/lib/jquery-cookie.js"></script>
|
||||
<script src="//play.pokemonshowdown.com/js/lib/html-sanitizer-minified.js"></script>
|
||||
<script src="//play.pokemonshowdown.com/js/battle-sound.js?"></script>
|
||||
<script src="//play.pokemonshowdown.com/config/config.js?"></script>
|
||||
<script src="//play.pokemonshowdown.com/js/battledata.js?"></script>
|
||||
<script src="//play.pokemonshowdown.com/data/pokedex-mini.js?"></script>
|
||||
<script src="//play.pokemonshowdown.com/data/pokedex-mini-bw.js?"></script>
|
||||
<script src="//play.pokemonshowdown.com/data/graphics.js?"></script>
|
||||
<script src="//play.pokemonshowdown.com/data/pokedex.js?"></script>
|
||||
<script src="//play.pokemonshowdown.com/data/items.js?"></script>
|
||||
<script src="//play.pokemonshowdown.com/data/moves.js?"></script>
|
||||
<script src="//play.pokemonshowdown.com/data/abilities.js?"></script>
|
||||
<script src="//play.pokemonshowdown.com/data/teambuilder-tables.js?"></script>
|
||||
<script src="//play.pokemonshowdown.com/js/battle-tooltips.js?"></script>
|
||||
<script src="//play.pokemonshowdown.com/js/battle.js?"></script>
|
||||
<script src="/js/replay.js?"></script>
|
||||
|
||||
</body></html>
|
||||
<?php
|
||||
}
|
||||
Reference in New Issue
Block a user