Find/Replace Inside Texareas WYSIWYG - javascript

I believe the problem with finding/replacing text is when a wysiwyg steps into the mix.
However, I'm sure someone has figured out how to do the magic.
Here's the textarea, with no editor attached:
<textarea id="template1" style="width:400px; height:200px;">
<div>Dear {{companyname}},</div>
<div>A new Support Case has been opened for you.</div>
<div>Support Case description:</div>
.........more html text follows
</textarea>
<script language="javascript" type="text/javascript">
$(document).ready(function(){
var str = $("#template1").val();
var newstr = str.replace("{{companyname}}", "ACME Inc");
$("#template1").val(newstr);
});
</script>
Without the editor, I'm good to go.
As soon as I apply my editor of choice (TinyMCE), the replace() method gets ignored.
Does anyone know why?

There are two reasons why your replace function doesn't work.
1. The contents of the former textarea get replaced, but you cannot see the result because tinymce hides the textarea and uses a contenteditable iframe to load and style the former contents. The editor content gets written back to the textarea onSave and onSubmit
2. $(document).ready all ressources of your page have been loaded, but the tinymce editor is not ready.
This will lead to the question on how to replace the code in the tinymce editor. $(document).ready won't help here. You will need use the tinymce configuration paramter onInit
// Adds an observer to the onInit event using tinyMCE.init
tinyMCE.init({
...
setup : function(ed) {
ed.onInit.add(function(ed) {
//console.debug('Editor is done: ' + ed.id);
// replace the editor content
var str = ed.getContent();
var newstr = str.replace("{{companyname}}", "ACME Inc");
ed.setContent(newstr);
});
}
});

If you look at the actual Html generated by tinymce in your favourite page inspector (firebug, dev tools, dragonfly etc), you will see that you are actually editing an iframe with contentEditable enabled. As far as i can tell it will use the textarea/input to set the initial content, and do some other sort of voodoo to set it back when you post the form back to the server.
If you want to do it manually there are methods to do so:
http://www.tinymce.com/wiki.php/API3:method.tinymce.Editor.getContent
http://www.tinymce.com/wiki.php/API3:method.tinymce.Editor.setContent
You could do your text processing via those.

Related

Configure prism.js to recognize <pre> tags (without <code> tag)

The prism.js documentation states
Prism forces you to use the correct element for marking up code: <code>. On its own for inline code, or inside a <pre> for blocks of code - https://prismjs.com/#features-full
We're using a document management system, that does not allow any HTML code inside a <pre> tag
<pre>some code</pre> - formatting is correct, but no syntax highlighing
<code>some code</code> - syntax highlighing works, but all line breaks/indentations are removed by the CMS
<pre><code>some code</code></pre> - transformed to <pre><code>some code</pre> by the CMS
Is there a way to have prism.js add syntax highlighting to a <pre> tag, like this:
<pre class="language-javascript">
if (test) {
someCode();
}
</pre>
Maybe there's a plugin or a JS configuration to tell prism.js to highlight those <pre> tags.
I was able to do it. Here is the code, I don't think you need the language-js how I do it later...
<pre class="language-js">
var cheese = sandwich;
function(){
return "hello!";
}
</pre>
First I init prism and follow the Manual Highlighting from the docs:
<script>
window.Prism = window.Prism || {};
window.Prism.manual = true;
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/prism.min.js"></script>
Now nothing happens by default. Further down the docs, they show an example under Usage with Node
// The code snippet you want to highlight, as a string
const code = `var data = 1;`;
// Returns a highlighted HTML string
const html = Prism.highlight(code, Prism.languages.javascript, 'javascript');
So in my example I do the following:
<script>
// Get the pre element
let pre = document.querySelector("pre");
// Grab the text out of it
let code = pre.innerText;
// Highlight it
let highlighted = Prism.highlight(code, Prism.languages.javascript, 'javascript');
// Now put that back in, but as HTML
pre.innerHTML = highlighted
</script>
Example here:
https://codepen.io/EightArmsHQ/pen/f9023daaa6499786e25899cb62f4d6c2?editors=1010
I'm sure you can figure out how to querySelectorAll the pre tags and loop through each formatting them : )
Solution 1 (original answer)
Here's the code that I came up with, based on the reply by Djave:
<script>
// Enable the "manual" mode to prevent prism from instantly firing.
window.Prism = window.Prism || {};
window.Prism.manual = true;
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/prism.min.js" defer></script>
<script>
// Use the hook "before-highlightall" to register the custom CSS selectors:
Prism.hooks.add('before-highlightall', function (env) {
env.selector += ', pre[class*="language-"], pre[class*="lang-"]';
});
// Highlight code, when the page finished loading (using jQuery here)
jQuery(Prism.highlightAll);
</script>
Notes:
Maybe the code can be written even shorter (I'm not sure if the first block is really required). However, this solution seems to be very reliable and works stable on all our documents.
In my tests, the code also works when "prism.min.js" is loaded using the defer or async flags.
Solution 2 (recommended)
I've found, that the missing code tag has some other (minor) problems with prism.js plugins, such as the line-number plugin.
We now use the following snippet in our CMS to automatically insert the code tag where it's missing:
<script>
// Enable the "manual" mode to prevent prism from instantly firing.
window.Prism = window.Prism || {};
window.Prism.manual = true;
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/prism.min.js" defer></script>
<script>
// THE FOLLOWING BLOCK CHANGED:
jQuery(function() {
// Wrap the code inside the required <code> tag, when needed:
jQuery('pre[class*="language-"], pre[class*="lang-"]').each(function() {
if (1 !== jQuery(this).children('code').length) {
jQuery(this).wrapInner('<code>');
}
});
// Highlight code, when the page finished loading (using jQuery here)
Prism.highlightAll()
});
</script>

Access TinyMCE current input within iframe

I am using TinyMCE and I am trying to output what the user is currently typing to a div below the TinyMCE editor. I want the user to see how there post would look like rendered.
The script I am using is this:
<script type="text/javascript">
$(function () {
$('#tinymce').keyup(function () {
$('#myDIVTag').html('<b>' + $(this).val() + '</b>');
});
});
</script>
I have placed the corresponding div in my begin form in the view:
<div id="myDIVTag">
</div>
However nothing is being rendered as I type.
There is no fiddle, which makes thing harder - but it got pointed out in the comment that TinyMCE editor itself is hidden within <iframe> element. This seems to be the root of the issue.
To be able to access anything within iframe element with JavaScript, we must remember about Cross-Domain Policy. In short - if iframe has src attribute set to another domain, and website under this source doesn't explicitly let us access its contents within iframe - we won't be able to do it, end of the story. More: http://blog.cakemail.com/the-iframe-cross-domain-policy-problem/ .
Luckily, I'm pretty sure it won't be of any problem here - unless TinyMCE within our iframe gets served from another domain.
Given all that, here's the snippet that should do the trick. Please alter selectors for your needs (I pointed them out with comments):
$('iframe') // iframe element
.contents()
.find('textarea') // textarea/other input *within* iframe
.keyup(function () {
$('#myDIVTag').html('<b>' + $(this).val() + '</b>');
});
EDIT:
It got suggested in the comment (thanks #charlietfl!) that the proper way to approach this problem is to use TinyMCE API event, not generic binding to textarea element within iframe. Here's a quick answer taking that into account:
HTML:
<textarea id="tinymce-textarea"></textarea>
<div class="text-mirror"></div>
JS:
tinymce.init({
selector: "#tinymce-textarea",
setup: function (editor) {
editor.on('change', function (e) {
var newVal = tinymce.get('tinymce-textarea').getContent();
$('.text-mirror').html(newVal);
});
}
});
JSFiddle: http://jsfiddle.net/c1zncmt8/1/ .
Basically: we're binding to TinyMCE editor "change" event, and whenever it gets triggered we get value of our input (var newVal = ...) and put it into separate div (.text-mirror).

Javascript to image fallback

I am working on a web widget that can be embedded on 3rd party websites.
Since a lot of content management systems do not allow users to post/execute scripts, I want my widget to show an image instead of JS-generated content if such situation occurs.
<script type="text/javascript">
(function(){var s = document.createElement('script');s.src = '//example.com/file.js';s.async = "async";document.body.appendChild(s);}());
</script>
<img src="//example.com/image.svg?param1=value1" src="" id="my_fallback">
For now I am using the code above. Is there any way to show the image only if the script did not load? The goal is to reduce transfer usage and provide better user experience.
The first line of my widget script is removing #my_fallback, but it is not fast enough - sometimes I can see the image for a second before the actual widget content replaces it.
The only thing I came up with is to delay creation of the image by including something like sleep() in the beginning of my image generator.
EDIT
No, <noscript> won't work here. I do not want to fallback if user has disabled javascript. I want to fallback when a script has not loaded - for any reason, especially if some security mechanism cut off the <script> section.
Use html tag Noscript
<noscript>Your browser does not support JavaScript! or a image here</noscript>
Remember
In HTML 4.01, the tag can only be used inside the element.
In HTML5, the tag can be used both inside and .
Edit : -
add one html tag
<span class="noscript">script is loading.....or put image</span>
inside your script tag
now in your scripts which has to be load add one code like
add this line at the end
$('.noscript').hide();
This is the other way which you can handle the same!
One quick fix is to create a global variable from that script, visible to the window object.Also the image must be hidden. Then, on a main.js script check for that variable. If it exists then run your widget code from there. If it doesnt exist then fadeIn the fallback image.
Heres a demo
The default img is an image 272x178 size and the widget image is an image 300x400 size.
To simulate the action when the script is unavailable, just name the variable myWidgetIsEnabled with a different name so the condition fails.
Here is some code:
// Code goes here
var widget = (function(){
window.myWidgetIsEnabled = true;
return {
init: function(){
var s = document.createElement('script');s.src = 'file.js';s.async = "async";
document.body.appendChild(s);}
}
}());
$(document).ready(function(){
if(window.myWidgetIsEnabled){
widget.init();
}else{
console.log('not enabled, the default behavior');
$('.fallback').fadeIn();
}
})

Does this not work because I can't use a script in a div?

I am making a website with a program editor, for users to make HTML programs.
I usually test this out by inputting <script> alert("Hi!"); </script> into the body input area, but no alert comes. As you can see from the code, the result is the script ending up inside the div.
So do scripts work in divs? If they do, here's my code (the important part, at least):
This is my script to run when I press the "Run!" button:
<script>
function onclick(){
document.getElementsByTagName("head")["0"].innerHTML += eval(document.getElementById("editorHead").value);
document.getElementById("result").innerHTML = eval(document.getElementById("editorBody").value);
}
</script>
This is the code for the input areas & run button (omitting the text between them & class attributes):
<textarea id="editorHead" rows="20"></textarea>
<textarea id="editorBody" rows="20"></textarea>
<div id="result"></div>
<button onclick="onclick();">Run!</button>
I tried changing the onclick's name to run, and that made the clicking work (before that the button wouldn't turn blue when you clicked it), but that was it.
The asked question is do scripts work in div's. Yes <script></script> tag content executes inline where it appears in the document.
However, it always executes in the document context. Meaning this === document. So to bind to the div's onclick method (the way you handle ui events in javascript) you need to find the div :
document.findElementById("my-div-id").onclick = function(e) {
// do something
};
Note this clobbers the default behavior for the element if there is a default click behavior (like on an a tag)
Also to be more clear on the expected behavior. Do this
<script>
function doSomething() {
alert();
}
</script>
<button onclick="doSomething()">Button</button>

How can I dynamically add an <object> tag with JavaScript in IE?

I have to add either an embed tag for Firefox or an object tag for Internet Explorer with JavaScript to address the appropriate ActiveX / Plugin depending on the browser. The plugin could be missing and needs to get downloaded in this case. The dynamically added embed tag for Firefox works as expected. The dynamically added object tag for Internet Explorer seems to do nothing at all. The object tag needs the following attributes to function properly.
id ="SomeId"
classid = "CLSID:{GUID}"
codebase = "http://www.MyActicexSource.com/MyCuteActivex.CAB#Version=2,0,0,1"
Even a general working idea or method would be nice.
Thanks!
I needed to do this same thing and simply place all of the HTML needed for the OBJECT tag in a string in JavaScript and simply replace the innerHTML of a div tag with the OBJECT HTML and it works in IE just fine.
// something akin to this:
document.getElementById(myDivId).innerHTML = "<OBJECT id='foo' classid='CLSID:22d6f312-b0f6-11d0-94ab-0080c74c7e95'.....etc";
That should work, it does just fine for me - I use it to embed Windows Media Player in a page.
UPDATE: You would run the above code after the page loads via an event handler that either runs on the page's load event or maybe in response to a user's click. The only thing you need to do is have an empty DIV tag or some other type of tag that would allow us to inject the HTML code via that element's innerHTML property.
UPDATE: Apparently you need more help than I thought you needed? Maybe this will help:
Have your BODY tag look like this: <body onload="loadAppropriatePlugin()">
Have somewhere in your page, where you want this thing to load, an empty DIV tag with an id attribute of something like "Foo" or whatever.
Have code like this in a <script> tag in your <head> section:
function getIEVersion() { // or something like this
var ua = window.navigator.userAgent;
var msie = ua.indexOf("MSIE ");
return ((msie > 0) ? parseInt(ua.substring(msie+5, ua.indexOf(".", msie))) : 0);
}
function loadAppropriatePlugin() {
if(getIEVersion() != 0) { // this means we are in IE
document.getElementById("Foo").innerHTML = "<OBJECT id='foo' classid='CLSID:22d6f312-b0f6-11d0-94ab-0080c74c7e95'.....etc";
} else {
// if you want to maybe do the same for FF and load that stuff...
}
}
Does that help?
var object = document.createelement('object')
object.setAttribute('id','name')
object.setAttribute('clssid','CLSID:{}')
And the same for other parameters.
Two ways.
1) Just do a document.write where ever you want it
<script type="text/javascript">
<!--
document.write("<object id=\"SomeId\" classid=\"CLSID:{GUID}\" codebase=\"http://www.MyActicexSource.com/MyCuteActivex.CAB#Version=2,0,0,1\"></object>");
-->
</script>
2) Edit a tag's innerHTML property.
<div id="my-div"></div>
<script type="text/javascript">
<!--
document.getElementById("my-div").innerHTML = "<object id=\"SomeId\" classid=\"CLSID:{GUID}\" codebase=\"http://www.MyActicexSource.com/MyCuteActivex.CAB#Version=2,0,0,1\"></object>";
-->
</script>
EDIT: Just a note, it is best to not use JavaScript to do this, since people with JavaScript enabled will never see the object. It would be better to just place it in your HTML.

Categories