CKEdtior allow <html>, <head>, <body> without using fullPage: true - configuration

CKEdtior strips <html>, <head> and <body> with the default configuration setting fullPage = false. This is most noticable when you paste HTML into the 'source' view, then turn the source view off and on again.
However, if you make fullPage = true then the editor automatically adds these tags to each textarea.
How can I edit the config so that these tags are allowed (not stripped out), but not added to each editor by default?

It isn't possible (without hacking). If you want to be able to use these tags, then the full page mode must be true. The reason is that CKEditor places the content that you load into it in a <body> element (or different element depending on the editor type).
The hack that came to my mind is using source protection to find content before <body> and after it and secure it. See a demo: http://jsfiddle.net/hkx8g8b8/1/
CKEDITOR.replace( 'editor', {
protectedSource: [
/^[\s\S]*<body>\s*/i,
/\s*<\/body>[\s\S]*$/i
]
} );
If you want to allow using some attributes in <body> tag you need to extend this regexp of course.

Related

How can I get fragment links to work in a page with a <base href="">?

This seems like a very basic HTML question, but I cannot find an answer here or elsewhere that actually works.
What I want to do is jump to an id link on the same document without reloading the document.
Here's my setup. The document is http://www.example.com/mydocument.htm/.
<head>
.
<base href="http://www.example.com">
.
.
</head>
<body>
<!-- Jump from ... -->
<div>
Jump to here.
</div>
<!-- Jump to ... -->
<div id="myid">
<Do stuff>
<Do more stuff>
</div>
</body>
This syntax, according to everything I have read on this site and elsewhere, is supposed to result in a jump within the current document without a page reload.
Doesn't work. My browsers (Firefox, Chrome) automatically stick the base href in front of the bookmark, viz: http://www.example.com/#myid, which opens my home page.
Not what I want.
If I change the href from "#myid" to /mydocument.htm#myid, then the jump completes, but the page reloads. Ditto if I use the absolute address: http://www.example.com/mydocument.htm/#myid.
I'm stuck. Any guidance?
The <base> element instructs the browser to append the URL in the href to all relative URLs on the page. So having:
<base href="http://www.example.com" />
Means that for :
here.
The href is handled as :
http://www.example.com/#myid
Instead of
<current_page>/#myid
You almost certainly don't need that <base> element in the head section, especially based on your further point that using the full URL (which also has http://www.example.com in it) works, meaning your page is already at http://www.example.com and thus doesn't need to make it explicit with <base>.
Alternatively (and I don't actually recommend this, because your use of base seems incorrect), you could change the href of your link to be the current page plus the id hash, like:
here.
As the browser will render the URL (when applying the base href) to :
http://www.example.com/mydocument.htm/#myid
and thus not try to leave the current page as it will treat it the same as if the base weren't set. (Note that this would only work when you have the base href set to the URL of the actual page's base, and as I mentioned earlier, that would make the base element unnecessary).
https://jsfiddle.net/ouLmvd3g/
If you are considering a javascript solution, since the <base> is apparently never necessary, I would recommend an event listener that removes the base element from the DOM rather than your suggested :
a fix using an event listener to remove the base URL for local links
A simple solution would be:
window.onload=function(){
var baseElement = document.getElementsByTagName("base")[0];
baseElement.parentNode.removeChild(baseElement);
}
https://jsfiddle.net/vLa0zgmc/
You could even add a bit of logic to check if the base element's href matches the current page's actual URL base, and only remove when it does. Something like:
var baseElements = document.getElementsByTagName("base");
if (baseElements.length > 0) {
var baseElement = baseElements[0];
var current_url = window.location.toString();
var base_url = baseElement.getAttribute("href");
// If the base url and current url overlap, remove base:
if (current_url.indexOf(base_url) === 0) {
baseElement.parentNode.removeChild(baseElement);
}
}
Example here : https://jsfiddle.net/gLeper25/2/
Thanks to all who responded.
In the end it turns out I was asking the wrong questions. What I needed was a means of jumping to an anchor on the same document without the document reloading. Unfortunately I got fixated on the problem with <base> interfering with the normal <a href....> process.
The actual answer was to use onClick instead, and the code was provided by #Davide Bubz in "Make anchor links refer to the current page when using <base>", and it's simple and elegant, using document.location.hash instead of <a href...>:
Anchor
where "test" is the ID identifying the item to be jumped to.
Several responders pointed to this thread as answering my issues, but I was not smart enough to understand its import until I had read it for the third time. Had I been smarter, I would have saved 6-1/2 hours of wasting my time on trying to fix the <base> problem.
Anyway, problem solved. Thanks to all and especially to Mr. Bubz.

Will any modern browsers *not* move DIV elements from the HEAD to the BODY?

I am looking for an easy way to insert content at the top of the <body> section in a WordPress plugin, and discovered that there is no core hook to do this, and I can't rely on modifying the theme. However there is a hook to insert in to the <head> section, and I discovered that if I insert the <div> into the <head> section rather than the <body>, all browsers I've tested will in fact move that content from the <head> to the top of the <body> section which is just what I want.
I know this fails W3C validation which is bad, on the other hand most sites have W3C validation errors. And more importantly, this trick works on Chrome, FF, IE, Safari, Android, and iPhone, Chrome mobile, Brave mobile, Ghostery mobile, to name a few places I've checked.
Will this kind of code cause problems in practice? Should I avoid doing this and find another way?
Run this HTML code and you'll see both DIVs in the body after the page renders:
<html>
<head>
<div>This is the head</div>
</head>
<body>
<div>This is the body</div>
</body>
</html>
And here's what you'll see in your browser:
Updated Answer
So you have some pretty strict requirements:
No theme editing (initiated via plugin)
No JavaScript insert (needs to be in initial DOM)
If you just need to have the element high on the page, you could stick it on a relatively early hook, and then move it after <body> with JavaScript.
If it needs to be one of the first things loaded, you probably could just stick it in the wp_head - making sure it's just got a really low priority so it makes quasi-semantic sense.
However you could also insert it with PHP using a real-time find and replace. You shouldn't suffer too much of a performance hit, but something like this would get you started:
function Danger_insert_iframe( $buffer ){
// Define your iframe
$iframe = '<iframe src="https://xhynk.com" style="width: 100%; height: 150px;"></iframe>';
// Match the first `<body>` tag
preg_match( '/(?:<body.*>)/U', $buffer, $matches );
// Return our buffer with iframe appended to the body match
return str_replace( $matches[0], $matches[0].$iframe, $buffer );
}
// An early hook with the DOM ready to fire on
add_action( 'template_redirect', function(){
ob_start();
ob_start( 'Danger_insert_iframe' );
});
What this does is matches the
<body [any class/js/data attributes here]>
and will then replace it with
<body [any class/js/data attributes here]><iframe></iframe>
Here's a Working Demo
It's not exactly elegant, but it will get the job done. The first thing that came to mind was a regex match for the body, but you may be better with another manipulation method like DOMDocument. For now though, this will get you started.
Original Answer:
While there's not really a *perfect* way to go about this, I would avoid _hacking_ a solution together. I've seen all sorts of strange things, including hooking in a `</head>` to `wp_head`, and directly inserting content like you've mentioned.
Your best best, and the most "accepted" way to do this, is to use a [Child Theme](https://codex.wordpress.org/Child_Themes). Ultimately, it will use the parent theme like the regular theme, but you can override parts of it with the child theme. This is *mostly* future-proof, unless the parent theme structure *radically* changes.
This gist of these would be to set up a blank Child Theme, and copy the `header.php` (or equivalent file) from the parent theme, and find the opening body tag, usually `<body <?php body_class(); ?>>`
Then, insert your own [`do_action()`](https://developer.wordpress.org/reference/functions/do_action/), so the new file will start with:
<body <?php body_class(); ?>>
<?php do_action('Danger_after_body'); ?>
Then create a `functions.php` file in the Child Theme and use that to deal with content on that hook.
add_action( 'Danger_after_body', 'Danger_insert_content' );
function Danger_insert_content(){
echo 'This Content is always after the <body> tag';
}
If this won't work for you (truthfully, it should except in a few instances), and you are just inserting simple content - you could just target the `body` with JavaScript and insert content that way.
You could also find the first hook that fires in your current theme and add high priority functions to that, but depending on the theme layout that can get messy and falls under the strange hack solutions I mentioned earlier.

Can CSS3 access the browsing context name?

I have an HTML file that wants to be viewed standalone, so it needs an h1.
But I want to embed it in another page too, where it does not need an h1, using object.
What I've come up with is
<head>
<style>
#foo h1 {display: none;}
</style>
</head>
<body>
<script>
document.body.id = window.name;
</script>
<h1>title</h1>
Content.
</body>
The style has no effect when the document is loaded by itself, because the window.name is null. But, in the including file, I use:
<object data="that-file-up-there.html" name="foo">
which gives the nested browsing context the name foo, and then the javascript copies it as the id of the nested document , thus causing the style to trigger, and suppress the h1. This works, but am I overlooking a way to not need the javascript?
No. You cannot do this without javascript because HTML and CSS are not programming languages that can copy attributes, elements or properties from other pages.

How can I have a Blank Title Page?

I'm using
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<script>document.title=" ";</script>
</head>
</html>
It works in IE and Firefox, but Chrome doesn't work.
Well, in W3C standard. It seems that blank title is not allowed?!
User agents should use the document's title when referring to the document in their user interface. When the contents of a title element are used in this way, the directionality of that title element should be used to set the directionality of the document's title in the user interface.
According to HTML5 CR, the title element is required, unless “the document is an iframe srcdoc document or if title information is available from a higher-level protocol” (this is specified in the description of the head element).
It is possible to set the title in JavaScript by an assignment to document.title, in practice and according to HTML5 CR. Of course, this is ineffective for many important uses of the title, since this only takes place in a browser.
HTML5 CR also specifies that the title element must not be empty or contain just a space. More exactly, the content must be text that is not inter-element whitespace.
There is no corresponding requirement for the value of document.title. However, HTML5 specifies that on assignment, leading and trailing whitespace is stripped off. If you test the value of document.title after the assignment document.title = " ", you will see that the value is the empty string, not a space.
Browsers differ in what they do with the document title (set with a <title> element or via assignment to document.title). They may display it in various ways in their user interface. If the title is set to the empty string or is not set at all, browsers typicallty use the file name of the document, possibly somehow modified, in place of a title. This is what happens e.g. in Chrome, Firefox, and IE (on Windows) when they show document title as the name of a tab. (I don’t know what the question means by saying that setting title blank “works” in IE and Chrome.)
In most cases, an empty title for a document makes no sense, any more than it would make sense to publish a book without a name. But if you have a use case where you want to make the title (as shown by browsers in some contexts), you can deploy various tricks.
For example, NO-BREAK SPACE U+00A0 is by definition not a whitespace character in HTML, so you could use <title> </title> in HTML or document.title='\u00A0' in JavaScript. The title will then look like blank, but it’s technically not empty, so it will be used browsers.
Since NO-BREAK still occupies space (it’s really shown with a glyph, just a completely blank glyph), you might use LEFT-TO-RIGHT MARK U+200E instead; it is an invisible (zero-width) control character. In HTML, you could use <title>‎</title>. Alternatively, you could use document.title='\u200E' in JavaScript.
chrome 76, macos 13 may 2019:
<title>‎</title>
works, and here is a full example for an empty jekyll page with no tab title:
---
layout: null
---
<link href="http://localhostdotdev.com/favicon.ico" rel="icon" type="image/x-icon" />
<title>‎</title>
<style>body { background-color: rgb(40, 44, 47) }</style>
(also has an empty favicon and a dark background that matches chrome's dark mode)

Prevent CKEditor from formatting code in source mode

How can you prevent any automatic formatting when in CKEditor when viewing in source mode?
I like to edit HTML source code directly instead of using the WYSIWYG interface, but whenever I write new lines, or layout tags how I would indent them, it all gets formatted when I switch to WYSIWYG mode and then back to source mode again.
I stumbled upon a CKEditor dev ticket, Preserve formatting of ProtectedSource elements, that alluded to a setting which may have existed once upon a time which would be exactly what I'm after. I just want to know how I can completely turn off all automatic formatting when editing in source mode.
I came up with a solution I thought would be foolproof (albeit not a pleasant one).
I learned about the protectedSource setting, so I thought, well maybe I can just use that and create an HTML comment tag before all my HTML and another after it and then push a regular expression finding the comment tags into the protectedSource array, but even that (believe it or not) doesn't work.
I've tried my expression straight up in the browser outside of CKEditor and it is working, but CKEditor doesn't protect the code as expected (which I suspect is a bug involving comment tags, since I can get it to work with other strings). In case you are wondering, this is what I had hoped would work as a work-around, but doesn't:
config.protectedSource.push( /<!-- src -->[\s\S]*<!-- end src-->/gi );
and what I planned on doing (for what appears to be the lack of a setting to disable formatting in source mode) was to nest all my HTML within the commented tags like this:
<!-- src -->
<div>some code that shouldn't be messed with (but is)</div>
<!-- end src -->
I'd love to hear if anyone has any suggestions for this scenario, or knows of a setting which I have described, or even if someone can just fill me in as to why I can't get protectedSource to work properly with two comment tags.
I really think it's gotta be a bug because I can get so many other expressions to work fine, and I can even protect HTML within the area of a single comment tag, but I simply cannot get HTML within two different comment tags to stay untouched.
My solution to this was to use comments in my system, but before feeding the page content to CKEditor, convert them to custom HTML tags. Then, upon save, convert them back to my comment tags.
For your syntax that would be something like this in PHP. Before printing the page content to the textarea:
$content = str_replace(array('<!-- src -->','<!-- end src -->'),array('<protected>','</protected>'),$content);
Before saving the resulting content:
$content = str_replace(array('<protected>','</protected>'),array('<!-- src -->','<!-- end src -->'),$content);
In the CKEditor configuration:
protectedSource:[/<protected>[\s\S]*<\/protected>/g]
Hope that helps!
I wanted to preserve newlines in my source, and the protectedSource feature works well for that. I added this to my config.js:
config.protectedSource = [/\r|\n/g];
config.allowedContent=true; will do the trick
Here is the full HTML code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CKEditor</title>
<script src="http://cdn.ckeditor.com/4.5.10/standard/ckeditor.js"></script>
</head>
<body>
<textarea name="editor1"></textarea>
<script>
CKEDITOR.config.allowedContent=true;
CKEDITOR.replace( 'editor1' );
</script>
</body>
</html>
I solved this problem by simply surrounding the back-end output of edit form page with a conditional on a $_GET variable - when you click on "Expert Mode" it loads a plain textarea instead of the ckeditor system. Your invocation of the ckeditor object will vary depending on your setup. ( I have a custom class that calls/builds the editor object )
<div id="postdivrich" class="postarea">
<?php
if( isset( $_GET['expert'] ) )
{
print "<div style=\"text-align:right;\">Editor mode</div>\n";
print "<textarea name=\"content\" style=\"height:400px;width:{$nEwidth}px;\">{$aDoc['content']}</textarea>\n";
}
else
{
print "<div style=\"text-align:right;\">Expert mode</div>\n";
require_once( 'admin/editor.class.php' );
$aDoc['content'] = str_replace( "\r", '', str_replace( "\n", '', nl2br( $aDoc['content'] ) ) );
$oEditor = new setEditor( $aDoc['content'], $nEwidth, "400", 'content' );
$oEditor->ShowEditor();
}
?>
</div>
Does this answer help? Basically you can turn off the options adding a javascript, it looks like.