Textpattern CMS support forum
You are not logged in. Register | Login | Help
- Topics: Active | Unanswered
Pages: 1
all_sideview prototype
Side view is a few lines of prototype css and javascript that add a live article <iframe />
to the Write page. When you save your article, the iframe updates.
It works well when specified in Textpattern’s config.php define('admin_custom_js', 'all_sideview.js');
, but loses critical functionality when I convert it to a plugin for easy distribution.
<?php
if( @txpinterface === 'admin' ){
register_callback('all_sideview','admin_side','head_end');
}
function all_sideview(){
echo <<<INLINE_CODE
<style>
.txp-body:has(#sideview) {
max-width: 100%;
display: grid;
gap: 2em;
grid-template-columns: minmax(480px,1280px) minmax(360px,1280px);
}
#sideview {
height: 100%;
width: 100%;
overflow: auto;
}
.sideview__button {
.ui-icon:last-of-type {
margin-left: -.125em;
margin-right: .375em;
}
}
</style>
<script type="text/javascript">
$(document).ready(function() {
if ($('#page-article').length) {
const sectionExcluded = '';
const sectionSelect = $('#section')[0];
const articleSection = sectionSelect.value;
var currentUrl = $('#article_partial_article_view').attr('href');
if (articleSection != sectionExcluded && currentUrl) {
sideView();
} else {
$('#sideview').remove();
}
$("form").on("change", function() {
if ($('#sideview').length) {
document.getElementById('sideview').contentDocument.location.reload(true);
console.log('form changed');
}
});
if (currentUrl) {
$('.txp-save-zone .txp-actions').append('<a href="" title="View article alongside" class="sideview__button"><span class="ui-icon ui-icon-notice"></span> <span class="ui-icon ui-icon-copy"></span>Sideview</a>');
$('.sideview__button').click(function() {
if (localStorage.getItem('sideView') != 'true') {
localStorage.setItem('sideView', 'true');
} else {
localStorage.setItem('sideView', 'false');
this.removeClass('sideview--active')
}
sideView();
return false;
});
}
}
function sideView() {
if (localStorage.getItem('sideView') == 'true') {
$('.txp-body').append('<iframe id="sideview" src="' + currentUrl + '"></iframe>');
} else {
$('#sideview').remove();
}
}
});</script>
INLINE_CODE;
}
The $("form").on("change", function() {…
stops working in the plugin version; clicking ‘Save’ doesn’t reload the iframe. It looks like a script order thing to me, but changing it makes no difference. Type = Admin side, Order=5.
What am I doing rong?
Offline
Re: all_sideview prototype
Nice idea!
A couple of things spring to mind:
- Like with jcr_writenav_buttons the other day, you’ll need your plugin to be of Type 4 = admin + async to restore the iframe after clicking save. That’s only necessary for pages like the Write tab that save asynchronously.
- You can also leverage Textpattern’s in-built functionality to inject the css and js. That way they will support CSP if activated. For CSS that is
Txp::get('\Textpattern\UI\Style')->setContent($plugin_styles)
. See bot_wtc_css (called here). Injecting js functions the same way but usingTxp::get('\Textpattern\UI\Script')->setContent($plugin_js);
(see here for example). In your case, you’d split the inject css and inject js into two functions and register a callback at the top for each.
If setting the plugin type to 4 doesn’t help, maybe you also need to refresh the function on async save as bot_wtc does here.
TXP Builders – finely-crafted code, design and txp
Offline
Re: all_sideview prototype
Thank you jakob.
I already tried Type 4, so I’m off to investigate your tips…
The ajax/async aspect of Textpattern annoys me (likely ‘cos it rears its head whenever I need to shuffle UI elements around to better suit a clients workflow). From my perspective it offers more pain and complexity than gain. How do others feel?
Last edited by giz (2025-07-03 20:23:29)
Offline
Re: all_sideview prototype
I had a look and it does seem to need the textpattern.Relay.register("txpAsyncForm.success", yourRefreshFunction);
in the js to reinstate the button and reload the iframe.
This works for me with plugin type 4 = admin + async:
<?php
if (txpinterface === 'admin') {
register_callback('all_sideview_css', 'admin_side', 'head_end');
register_callback('all_sideview_js', 'admin_side', 'head_end');
}
function all_sideview_css()
{
$plugin_css = <<<EOCSS
.txp-body:has(#sideview) {
max-width: 100%;
display: grid;
gap: 2em;
grid-template-columns: minmax(480px, 1280px) minmax(360px, 1280px);
}
#sideview {
height: 100%;
width: 100%;
overflow: auto;
}
.sideview__button {
.ui-icon:last-of-type {
margin-left: -.125em;
margin-right: .375em;
}
EOCSS;
if (class_exists('\Textpattern\UI\Style')) {
echo Txp::get('\Textpattern\UI\Style')->setContent($plugin_css);
} else {
echo '<style>' . $plugin_css . '</style>';
}
}
function all_sideview_js()
{
$plugin_js = <<<EOJS
$(document).ready(function() {
if ($('#page-article').length) {
const sectionExcluded = '';
const sectionSelect = $('#section')[0];
const articleSection = sectionSelect.value;
var currentUrl = $('#article_partial_article_view').attr('href');
if (articleSection != sectionExcluded && currentUrl) {
sideView();
} else {
$('#sideview').remove();
}
$("form").on("change", function() {
sideViewReloadIframe();
});
if (currentUrl) {
sideViewAppendBtn();
}
textpattern.Relay.register("txpAsyncForm.success", sideViewAppendBtn);
textpattern.Relay.register("txpAsyncForm.success", sideViewReloadIframe);
}
function sideViewAppendBtn() {
$('.txp-save-zone .txp-actions').append('<a href="" title="View article alongside" class="sideview__button"><span class="ui-icon ui-icon-notice"></span> <span class="ui-icon ui-icon-copy"></span>Sideview</a>');
$('.sideview__button').click(function() {
if (localStorage.getItem('sideView') != 'true') {
localStorage.setItem('sideView', 'true');
} else {
localStorage.setItem('sideView', 'false');
this.removeClass('sideview--active')
}
sideView();
return false;
});
}
function sideViewReloadIframe() {
if ($('#sideview').length) {
document.getElementById('sideview').contentDocument.location.reload(true);
console.log('form changed');
}
}
function sideView() {
if (localStorage.getItem('sideView') == 'true') {
$('.txp-body').append('<iframe id="sideview" src="' + currentUrl + '"></iframe>');
} else {
$('#sideview').remove();
}
}
});
EOJS;
if (class_exists('\Textpattern\UI\Script')) {
echo Txp::get('\Textpattern\UI\Script')->setContent($plugin_js);
} else {
echo '<script>' . $plugin_js . '</script>';
}
}
The main differences are:
- php: Separate
admin_head
callback functions for the css and js (not absolutely necessary). - js: separate out the appendBtn and reloadIframe code sections into functions so that they can be called again on the js-event
txpAsyncForm.success
.
I’ve not tested any edge cases and if people do set CSP rules, it might require some special settings to permit the iframe.
TXP Builders – finely-crafted code, design and txp
Offline
Re: all_sideview prototype
Wow; thanks!
I got half way, was still futzing with txpAsyncForm.success
(with little success :).
I’ll finalise the code (mostly UI / viewport size considerations) and make it available…
Offline
Re: all_sideview prototype
all_sideview is ready for general testing.
Offline
Pages: 1