Skip to content

Instantly share code, notes, and snippets.

@JanCK
Last active September 12, 2016 18:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JanCK/be468cf07ef8dd105f438ec3c3059d50 to your computer and use it in GitHub Desktop.
Save JanCK/be468cf07ef8dd105f438ec3c3059d50 to your computer and use it in GitHub Desktop.
syncview getkirby plugin / field
<?php
/*
Append to your config.php
*/
c::set('routes', array(
// SYNCVIEW Route for JSON POST request calls helper to check target url for last modification
array(
'pattern' => 'syncview',
'action' => function() {
// check if a request happend
if(kirby()->request()->ajax())
{
// check if timestamp is integer / set url
if( v::integer( kirby()->request()->data()['timestamp'] ) )
{
$t = kirby()->request()->data()['timestamp'];
$url = kirby()->request()->data()['route'];
}
// validate url
if( isset($t) && isset($url) && !filter_var($url, FILTER_VALIDATE_URL) === false)
{
return response::json( Helpers::pageHasChanged( $url , $t) );
}
}
// error response
return response::json( array('error'=>'404') );
},'method' => 'POST'
)
));
<?php if(!defined('KIRBY')) exit;
/*
Place the syncview field within your blueprint to use the splitview for specific panel forms only.
*/
?>
title: Test
fields:
syncview:
help: Use shortcuts to toggle live preview (expand = alt + left arrow / collapse = alt + right arrow / close = escape)
type: syncview
width: 1
title:
label: Title
type: text
text:
label: Text
type: textarea
/*
Place in fields/syncview/assets/css and remove fields.syncview.assets.css. frome file name
*/
body.hasSyncView .section form > *,
body.hasSyncView #syncview-wrapper
{
-webkit-user-select: none; /* Chrome all / Safari all */
-moz-user-select: none; /* Firefox all */
-ms-user-select: none; /* IE 10+ */
user-select: none;
}
body.hasSyncView .syncview{
position: relative;
float:right;
width: 50%;
}
body.hasSyncView .syncview * {
box-sizing: border-box;
}
body.hasSyncView .mainbar {
left: 50%;
width: calc(50% + 15px);
right: none;
}
body.hasSyncView .mainbar .section{
left: 0;
}
body.hasSyncView .fieldset.buttons.buttons-centered{
position: relative;
left: 0 !important;
width: auto;
max-width: inherit !important;
}
body.hasSyncView #syncview{
float:right;
position: relative;
top: 0;
width: calc(100% - 20px);
height: 100%;
margin: 0;
padding: 0;
z-index: 0;
}
body.hasSyncView .mainbar,
body.hasSyncView #syncview iframe{
overflow:auto;
}
body.hasSyncView .mainbar.syncview-noscroll,
body.hasSyncView #syncview.syncview-noscroll iframe{
overflow:hidden !important;
}
body.hasSyncView.syncview-fullsize,
body.hasSyncView.syncview-half,
body.hasSyncView.syncview-hidden,
body.hasSyncView #syncview-wrapper,
body.hasSyncView .mainbar{
-webkit-transition: all 0.2s linear;
-moz-transition: all 0.2s linear;
-ms-transition: all 0.2s linear;
-o-transition: all 0.2s linear;
transition: all 0.2s linear;
}
body.hasSyncView.syncview-dragging #syncview-wrapper,
body.hasSyncView.syncview-dragging .mainbar {
-webkit-transition: none;
-moz-transition: none;
-ms-transition: none;
-o-transition: none;
transition: none;
}
body.hasSyncView.syncview-fullsize #syncview-wrapper{
width: 100vw !important;
}
body.hasSyncView.syncview-hidden #syncview-wrapper{
width: 0px !important;
display:none;
}
body.hasSyncView.syncview-fullsize .mainbar{
width: 100%;
}
body.hasSyncView.syncview-half #syncview-wrapper{
width: 50vw !important;
}
body.hasSyncView.syncview-half .mainbar{
width: 100%;
}
body.hasSyncView.syncview-hidden .mainbar{
width: 100%;
}
@media screen and (min-width: 820px) {
body.hasSyncView.syncview-fullsize .mainbar{
width: calc(33.33vw + 15px) !important;
}
body.hasSyncView.syncview-half #syncview-wrapper{
width: 33.33vw !important;
}
body.hasSyncView.syncview-half .mainbar{
width: calc(33.33vw + 40px) !important;
}
body.hasSyncView.syncview-hidden .mainbar{
width: calc(66.66vw) !important;
}
body.hasSyncView #syncview-wrapper{
top: 48px;
}
body.hasSyncView .mainbar {
left: 33vw;
min-width: calc(33vw + 15px) !important;
width: calc(33vw + 15px);
right: none;
}
}
body.hasSyncView #syncview-wrapper{
position: fixed;
right: 0;
top: 0;
bottom: 0;
z-index: 999 !important;
background: #FFF;
resize: both;
overflow: auto;
margin: 0;
padding: 0;
border:none;
width: 33%;
max-width:100% !important;
}
body.hasSyncView #syncview-handler{
border:none;
z-index: 1;
position: relative;
left: 0;
top: 0;
float:left;
width: 20px;
height: 100%;
margin: 0;
padding: 0;
background-color: #444;
-webkit-transition: background-color 0.2s ease-in-out;
-moz-transition: background-color 0.2s ease-in-out;
-ms-transition: background-color 0.2s ease-in-out;
-o-transition: background-color 0.2s ease-in-out;
transition: background-color 0.2s ease-in-out;
}
body.hasSyncView #syncview-handler:hover{
cursor: ew-resize;
background-color: #888;
-webkit-transition: background-color 0.2s ease-in-out;
-moz-transition: background-color 0.2s ease-in-out;
-ms-transition: background-color 0.2s ease-in-out;
-o-transition: background-color 0.2s ease-in-out;
transition: background-color 0.2s ease-in-out;
}
body.hasSyncView #syncview-handler:after{
content:"";
position: absolute;
background: transparent;
left: 0;
top: 0;
bottom: 0;
width:100px;
}
/*
Place in fields/syncview/assets/js and remove fields.syncview.assets.js. frome file name
*/
(function()
{
jQuery(document).ready(function(event)
{
var alt = false; // alt shortcut
var classes = ['syncview-hidden','syncview-half','syncview-fullsize']; // breakpoint classes defined in style.css
var position = 0; // breakpoint pointer
var len = classes.length - 1; // number of breakpoints
var polled = true; // longpolling active
function init()
{
// inject within scope of mainbar
jQuery('.mainbar').append(jQuery('.syncview'));
if(jQuery('#syncview').length <= 0) jQuery('body').removeClass('hasSyncView');
else jQuery('body').addClass('hasSyncView');
// resize handle behavior
jQuery('#syncview-handler').on('mousedown',function(e)
{
var w; // position x
jQuery('body').removeClass('syncview-half').removeClass('syncview-hidden').removeClass('syncview-fullsize').addClass('syncview-dragging');
jQuery('body').on( 'mousemove' ,
function( e2 )
{
if(!jQuery('.hasSyncView').length < 0) return false;
w = window.innerWidth - e2.pageX + 10;
jQuery('#syncview-wrapper').width( w );
if( window.innerWidth > 820 )
jQuery('.mainbar').width( window.innerWidth - w - window.innerWidth * 0.3 - 20 );
else
jQuery('.mainbar').width( window.innerWidth - 40 );
}
);
jQuery('body').on('mouseup',
function()
{
if(!jQuery('.hasSyncView').length < 0) return false;
jQuery(this).off('mousemove');
jQuery(this).removeClass('syncview-dragging');
}
);
});
// enable scrolling in iframe
jQuery('#syncview').on('mouseover',
function( event )
{
if(!jQuery('.hasSyncView').length < 0) return false;
jQuery('#syncview').removeClass('syncview-noscroll');
jQuery('.mainbar').addClass('syncview-noscroll');
}
);
jQuery('#syncview').on('mouseout',
function( event )
{
if(!jQuery('.hasSyncView').length < 0) return false;
jQuery('#syncview').addClass('syncview-noscroll');
jQuery('.mainbar').removeClass('syncview-noscroll');
}
);
// long polling refresh: timestamp from static helper class compares Unix last modified stamp with json submitted looped
var timestamp = 0;
var server = 'http://127.0.0.1/___projekte___/getkirby/kirby-2.2.3/';
var route = jQuery('#syncview').attr('src');
var longPoll = function()
{
var data = new Object();
data.timestamp = timestamp;
data.route = route + '?time=' + new Date().getTime();
jQuery.ajax({
url: server + 'syncview/',
dataType: 'json',
type: 'post',
data: data,
contentType: 'application/x-www-form-urlencoded',
success: function( res, textStatus, jQxhr )
{
if(!jQuery('body').hasClass('hasSyncView'))
return false;
console.log(res)
// replace last timestamp
timestamp = res.timestamp;
if(res.changed === true)
{
// refresh iframe
jQuery('#syncview').attr('src', route);
}
setTimeout(function()
{
longPoll(data.route);
}, 1000);
},
error: function( jqXhr, textStatus, errorThrown ){
console.log( jqXhr, textStatus, errorThrown );
}
});
}
longPoll();
}
// shortcuts
jQuery(window).keydown(
// start alt
function(event)
{
if(!jQuery('.hasSyncView').length < 0 ) return false;
if( event.keyCode == 18 ){
alt = true;
}
if(jQuery(event.target).is('input') || jQuery(event.target).is('textarea'))
alt = false;
}
);
jQuery(window).keyup(
function(event)
{
if(jQuery('#syncview').length <= 0 || !jQuery('.hasSyncView').length < 0 ) return false;
switch( event.keyCode ){
// alt + arrow Left
case 37:
if(alt == true && position >= 0 && position < len)
{
position++;
}
break;
// alt + arrow right
case 39:
if(alt == true && position > 0 && position <= len)
{
position--;
}
break;
// escape
case 27:
position = 0;
break;
// dissolve alt
case 18:
alt = false;
}
jQuery('body').removeClass('syncview-fullsize').removeClass('syncview-half').removeClass('syncview-hidden').addClass(classes[position]);
}
);
// dom observation to kill layout in abscense of field syncview
if( jQuery(event.target).find('#syncview').length == 1 )
{
jQuery('body').addClass('hasSyncView');
}
// unregistrate dom elements to toggle events
jQuery('body').on('DOMNodeRemoved', function(event)
{
if(polled == true) return false;
if( jQuery(event.target).find('.syncview').length == 1 )
{
polled = true;
if(jQuery('body').hasClass('hasSyncView'))
{
jQuery('body').removeClass('hasSyncView');
}
}
});
// registrate dom elements to toggle events
jQuery('body').on('DOMNodeInserted', function(event)
{
if(polled == false) return false;
if( jQuery(event.target).find('.syncview').length == 1 )
{
polled = false;
if(!jQuery('body').hasClass('hasSyncView'))
{
jQuery('body').addClass('hasSyncView');
init();
}
}
});
init();
});
})();
<?php
/*
Place in fields/syncview and remove fields.syncview. frome file name
*/
class SyncviewField extends BaseField {
static public $assets = array(
'js' => array(
'script.js'
),
'css' => array(
'style.css'
)
);
public function icon() {
if(empty($this->icon)) {
return null;
} else if($this->readonly()) {
$this->icon = 'refresh';
}
return 'icon fa fa-' . $this->icon;
}
public function result() {
return null;
}
public function content()
{
$wrapper = new Brick('div');
$wrapper->addClass('syncview');
$wrapper->html(tpl::load(__DIR__ . DS . 'template.php', array('page' => $this->page())));
return $wrapper;
}
public function element() {
$element = parent::element();
return $element;
}
}
<?php
/*
Place in fields/syncview and remove fields.syncview frome file name
*/
?>
<div id="syncview-wrapper">
<iframe id="syncview" height="100%" width="100%" src="<?php echo $page->url(); ?>" seamless="seamless"
frameborder="0" marginheight="0" marginwidth="0" noresize="noresize">
</iframe>
<div id="syncview-handler"></div>
</div>
<?php
/*
Place in plugins and remove plugins. frome file name
*/
class Helpers {
// Syncview
public static function pageHasChanged($path, $timestamp)
{
// make url a uri path
$modified = str_replace( url::index(),'',$path);
foreach ( site()->languages() as $lang ){
// clear path from language codes
$modified = str_replace( '/'.$lang->code().'/' , '/' , $modified);
}
// get last mod from folder
/*$p = site()->find( strtok( $modified , '?' ) )->root();
$modified = file_exists($p) ? filemtime( $p ) : '';*/
$modified = site()->find( strtok( $modified , '?' ) )->modified();
// append bool to avoid doubled timestamp comparision so JS success handler passes new timestamp and uses bool to take action
$is = ($modified > $timestamp) ? TRUE : FALSE;
return array('timestamp' => $modified , 'changed' => $is);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment