I am currently developing a drag and drop interface for a course builder. Courses are split into lessons and lessons into slides. While switching between slides, I wanted to have a small thumbnail representing the current slide content.
One option would have been to use something like PhantomJS / CasperJS. A headless browser , to take a screenshot of the slide. I have done that in the past and it works perfectly.
This time, I decided to go with another solution, that allows me to convert html to a canvas element.
The project is called html2canvas (GitHub).
„This script allows you to take screenshots of webpages or parts of it, directly on the users browser. The screenshot is based on the DOM and as such may not be 100% accurate to the real representation as it does not make an actual screenshot, but builds the screenshot based on the information available on the page.“
1 2 3 4 5 |
html2canvas(document.body, { onrendered: function(canvas) { document.body.appendChild(canvas); } }); |
I have a slide container (#slideContainer), that holds the active slide for editing and a thumb navigation to switch between slides (.slide.thumb).
1 2 3 4 5 6 7 8 9 10 11 12 |
var current = $(".slide.thumb.active"); html2canvas($("#slideContainer").get(0), { onrendered: function(canvas) { var dataURL = canvas.toDataURL(); current.css({"background" :"url("+dataURL+")", "background-size":"contain", "background-position": "center", "background-repeat":"no-repeat"}); } }); |
When switching between slides, I am doing a snapshot of the current slideContainer and add it as a new background image to the thumb navigation item for that slide. This is done with canvas.toDataURL() and by embedding the resulting image using a data: url.
Works perfectly. html2canvas has no full css support, so the output will not always match 100%, but its close enough to get a glimpse of what is stored per slide ;)
html2canvas
PhantomJS
CasperJS
„Gisto is a code snippet manager that runs on GitHub Gists and adds additional features such as searching, tagging and sharing gists while including a rich code editor. All your data is stored on GitHub and you can access it from GitHub Gists at any time with changes carrying over to Gisto.“
The application is multi-platform.
„Mosaico is a JavaScript library (or maybe a single page application) supporting the editing of email templates.
The great thing is that Mosaico itself does not define what you can edit or what styles you can change: this is defined by the template.
This makes Mosaico very flexible.“
Nested is a jQuery plugin which allows you to create multi-column and dynamic grid layouts. All completely gap-free.
okayNav aims to progressively collapse navigation links into an off-screen navigation, instead of doing it for all the links at the same time.
1 2 3 4 5 6 7 8 9 |
.no-selection { -webkit-touch-callout: none; /* iOS Safari */ -webkit-user-select: none; /* Chrome/Safari/Opera */ -khtml-user-select: none; /* Konqueror */ -moz-user-select: none; /* Firefox */ -ms-user-select: none; /* IE/Edge */ user-select: none; /* non-prefixed version, currently not supported by any browser */ } |
Had to use this for one of my latest Syntax Highlighting tweaks, so that I can use an ordered list, line numbers and still be able to copy & paste the code :)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
ol.amp_code { list-style:none; } ol.amp_code li { counter-increment:lineNumber; } ol.code li:before { position: absolute; -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; color: #ccc; font-size: 0.8em; line-height:0.8em; content:counter(lineNumber); margin-left: -2.0em; } |
Again another Google AMP article, this time dealing with Syntax Highlighting. If you have a code centric website, this is important.
In one of my last articles I talked about a Generic Syntax Highlighter. This time I want to show you, how to add Syntax Highlighting with GeSHi to a custom template in AMP-WP.
Please read up on documentation, as I am not diving into every detail.
1 2 3 4 5 6 7 8 |
add_filter( 'amp_post_template_file', 'xyz_amp_set_custom_template', 10, 3 ); function xyz_amp_set_custom_template( $file, $type, $post ) { if ( 'single' === $type ) { $file = dirname( __FILE__ ) . '/templates/my-amp-template.php'; } return $file; } |
1 2 |
add_action( 'pre_amp_render_post', 'xyz_amp_add_custom_actions' ); add_action( 'amp_post_template_css', 'xyz_amp_my_additional_css_styles' ); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
// Load phpQuery require ('my-libs/phpQuery/phpQuery.php'); // Load GesSHi require ('my-libs/geshi/geshi.php'); // Get AMP content and add it to phpQuery $doc = phpQuery::newDocumentHTML($this->get('post_amp_content')); $highlight = array(); // Loop through the code snippets (pre tags) foreach(pq('pre') as $snippet) { // Get classes of tag $class = pq($snippet)->attr("class"); // Check for Crayon Syntax Highlighter Class if (strpos($class, "lang:") !== false && strpos($class, "lang:default") === false) { preg_match("/lang:(?P<lang>\w+)/", $class, $catch); } else { // Fallback, if no language is detected $catch['lang'] = "php"; } // New Syntax Highlighter for each code snippet, with the correct language set // Storing for later stylesheet output. This makes also sure that stylesheets are only loaded once. $highlight[$catch['lang']] = new GeSHi(pq($snippet)->text() , $catch['lang']); // Uses Classes and no inlien styles $highlight[$catch['lang']]->enable_classes(); // Update code snippet $html = pq($snippet)->html($highlight[$catch['lang']]->parse_code()); } |
1 2 3 4 5 6 7 8 9 10 11 12 |
<style amp-custom> <?php $this->load_parts( array( 'style' ) ); ?><?php do_action( 'amp_post_template_css', $this ); // Load Stylesheets for highlighted languages foreach($highlight as $store){ echo $store->get_stylesheet(); } ?> </style> |
1 2 3 4 5 6 7 |
<div class="amp-wp-content"> <h1 class="amp-wp-title"><?php echo wp_kses_data( $this->get( 'post_title' ) ); ?></h1> <ul class="amp-wp-meta"> <?php $this->load_parts( apply_filters( 'amp_post_template_meta_parts', array( 'meta-author', 'meta-time', 'meta-taxonomy' ) ) ); ?> </ul> <?php echo $doc->html(); // amphtml content; no kses ?> </div> |
Due to Google AMP (Accelerated Mobile Pages) , I have been looking for a way to effectively do Syntax Highlighting without Javascript in pure PHP.
I was about to write my own, when I found an older article from phoboslab. Thanks Dominic for saving me some time ;) Its not perfect, but close enough.
A simple Syntax Highlighting Class that does just that. The class was not working with PHP 5.4.x+, as it uses preg_replace() with the /e modifier.
It will not cover all, but its better than nothing :) I will also add a section to my my AMP tweaks article to showcase the integration of Geshi.
Here an updated version using the preg_replace_callback() function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
class SyntaxHighlight { static $tokens = array();// This array will be filled from the regexp-callback public static function process($s) { $s = htmlspecialchars($s); // Workaround for escaped backslashes $s = str_replace('\\\\','\\\\<e>', $s); $regexp = array( // Punctuations '/([\-\!\%\^\*\(\)\+\|\~\=`\{\}\[\]\:\"\'<>\?\,\.\/]+)/' => '<span class="P">$1</span>', // Numbers (also look for Hex) '/(?<!\w)( (0x|\#)[\da-f]+| \d+| \d+(px|em|cm|mm|rem|s|\%) )(?!\w)/ix' => '<span class="N">$1</span>', // Make the bold assumption that an // all uppercase word has a special meaning '/(?<!\w|>|\#)( [A-Z_0-9]{2,} )(?!\w)/x' => '<span class="D">$1</span>', // Keywords '/(?<!\w|\$|\%|\@|>)( and|or|xor|for|do|while|foreach|as|return|die|exit|if|then|else| elseif|new|delete|try|throw|catch|finally|class|function|string| array|object|resource|var|bool|boolean|int|integer|float|double| real|string|array|global|const|static|public|private|protected| published|extends|switch|true|false|null|void|this|self|struct| char|signed|unsigned|short|long )(?!\w|=")/ix' => '<span class="K">$1</span>', // PHP/Perl-Style Vars: $var, %var, @var '/(?<!\w)( (\$|\%|\@)(\->|\w)+ )(?!\w)/ix' => '<span class="V">$1</span>' ); $s = preg_replace_callback( '/( \/\*.*?\*\/| \/\/.*?\n| \#.[^a-fA-F0-9]+?\n| \<\!\-\-[\s\S]+\-\-\>| (?<!\\\)".*?(?<!\\\)"| (?<!\\\)\'(.*?)(?<!\\\)\' )/isx' , array('SyntaxHighlight', 'replaceId'),$s); $s = preg_replace(array_keys($regexp), array_values($regexp), $s); // Paste the comments and strings back in again $s = str_replace(array_keys(SyntaxHighlight::$tokens), array_values(SyntaxHighlight::$tokens), $s); // Delete the "Escaped Backslash Workaround Token" (TM) // and replace tabs with four spaces. $s = str_replace(array('<e>', "\t"), array('', ' '), $s); return '<pre>'.$s.'</pre>' ; } // Regexp-Callback to replace every comment or string with a uniqid and save // the matched text in an array // This way, strings and comments will be stripped out and wont be processed // by the other expressions searching for keywords etc. static function replaceId($match) { $id = "##r" . uniqid() . "##"; // String or Comment? if(substr($match[1], 0, 2) == '//' || substr($match[1], 0, 2) == '/*' || substr($match[1], 0, 2) == '##' || substr($match[1], 0, 7) == '<!--') { SyntaxHighlight::$tokens[$id] = '<span class="C">' . $match[1] . '</span>'; } else { SyntaxHighlight::$tokens[$id] = '<span class="S">' . $match[1] . '</span>'; } return $id; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
pre { font-family: 'Courier New', 'Bitstream Vera Sans Mono', 'monospace'; font-size: 9pt; border-top: 1px solid #333; border-bottom: 1px solid #333; padding: 0.4em; color: #fff; } pre span.N{ color:#f2c47f; } /* Numbers */ pre span.S{ color:#42ff00; } /* Strings */ pre span.C{ color:#838383; } /* Comments */ pre span.K{ color:#ff0078; } /* Keywords */ pre span.V{ color:#70d6ff; } /* Vars */ pre span.D{ color:#ff9a5d; } /* Defines */ |
1 |
echo SyntaxHighlight::process( $your_code ); |
@GitHub portalzine/UtilityBelt/SyntaxHighlight