TinyMCE removing valid tags - iframe

I'm using tinyMCE for small site, that is used by people to write simple articles. Usualy they write in MS word and copy text to tinyMCE and submit this.
That's why I only allowed few tags:
valid_elements: "a[href|target],strong/b,em/i,div[align],br,p[style|align],ul,li,ol,table,tr,td,iframe[*],img[*]",
But despite allowing img[*] after inserting image by 'Insert/edit image' only:
<img alt=""/>
appears in code. Same goes for iframe (which is complitly removed)
I've already tried every combination of valid_elements with full list of img and iframe attributes and with extended_valid_elements.
When I remove valid_elements clause everything works fine, but then word formatting which is not allowed (h1, h2, etc) is messing up styles.
TinyMCE version is 3.4.2.

I am using the paste_preprocess setting with the tinymce paste plugin and i filter out unwanted tags there. Here is an example:
in your tinymce init:
paste_preprocess : function(pl, o) {
//if(console) console.log('Object', o);
//if(console) console.log('Content:', o.content);
// usage param1 = the string to strip out tags from, param2 = tags to keep in the string
o.content = ir.im.strip_tags( o.content,'<p><div><br><br/>' );
},
Help function to strip out tags:
strip_tags = function (str, allowed_tags) {
var key = '', allowed = false;
var matches = []; var allowed_array = [];
var allowed_tag = '';
var i = 0;
var k = '';
var html = '';
var replacer = function (search, replace, str) {
return str.split(search).join(replace);
};
// Build allowes tags associative array
if (allowed_tags) {
allowed_array = allowed_tags.match(/([a-zA-Z0-9]+)/gi);
}
str += '';
// Match tags
matches = str.match(/(<\/?[\S][^>]*>)/gi);
// Go through all HTML tags
for (key in matches) {
if (isNaN(key)) {
// IE7 Hack
continue; }
// Save HTML tag
html = matches[key].toString();
// Is tag not in allowed list? Remove from str!
allowed = false;
// Go through all allowed tags
for (k in allowed_array) { // Init
allowed_tag = allowed_array[k];
i = -1;
if (i != 0) { i = html.toLowerCase().indexOf('<'+allowed_tag+'>');}
if (i != 0) { i = html.toLowerCase().indexOf('<'+allowed_tag+' ');}
if (i != 0) { i = html.toLowerCase().indexOf('</'+allowed_tag) ;}
// Determine
if (i == 0) { allowed = true;
break;
}
}
if (!allowed) {
str = replacer(html, "", str); // Custom replace. No regexing
}
}
return str;
};

Related

Defining custom style rules for unordered/ordered lists

I have a use case in CKEditor where a user may need to insert a Unordered or Ordered list, but due to the site's brand guidelines, we need to provide the option to color the bullets or numbers. I have looked at the List Style plugin (http://ckeditor.com/addon/liststyle) but it does not provide that featureset nor does it provide any insight on how to add that kind of setting in the plugin itself. What are my best options to add this functionality to CKEditor?
You can create a plugin or modify an existing plugin to color the list items with this code:
var colorStyleLi = {
element: 'li',
styles: { 'color': '#(color)' }
};
var sel = editor.getSelection();
var ranges = sel.getRanges();
var st = new CKEDITOR.style(colorStyleLi, { color: color } );
for (var i = 0, len = ranges.length; i < len; ++i) {
var walker = new CKEDITOR.dom.walker(ranges[i]),
node;
while((node = walker.next())) {
if(node.type==CKEDITOR.NODE_ELEMENT) {
st.applyToObject(node, editor);
} else {
var p = node.getParent();
st.applyToObject(p, editor);
}
}
}

creating a Placemarks that can be hidden

I have been trying to create a Placemark that I can hide and show (like turning visibility on and off) on demand (on click)... I am using this to make the placemark:
function placemark(lat, long, name, url, iconsrc){
var placemark = ge.createPlacemark(name);
ge.getFeatures().appendChild(placemark);
placemark.setName(name);
// Create style map for placemark
var icon = ge.createIcon('');
if(iconsrc == "0")
icon.setHref('http://maps.google.com/mapfiles/kml/paddle/red-circle.png');
else{
icon.setHref(iconsrc);
}
var style = ge.createStyle('');
style.getIconStyle().setIcon(icon);
if(iconsrc != "0")
style.getIconStyle().setScale(2.5);
placemark.setStyleSelector(style);
// Create point
var point = ge.createPoint('');
point.setLatitude(lat);
point.setLongitude(long);
//point.setAltitudeMode(1500);
placemark.setGeometry(point);
google.earth.addEventListener(placemark, 'click', function(event) {
// Prevent the default balloon from popping up.
event.preventDefault();
var balloon = ge.createHtmlStringBalloon('');
balloon.setFeature(placemark); // optional
balloon.setContentString(
'<iframe src="'+ url +'" frameborder="0"></iframe>');
ge.setBalloon(balloon);
});
}
I have tried everything... from this:
function hidePlacemark(name){
var children = ge.getFeatures().getChildNodes();
for(var i = 0; i < children.getLength(); i++) {
var child = children.item(i);
if(child.getType() == 'KmlPlacemark') {
if(child.getId()== name)
child.setVisibility(false);
}
}
}
to using this ge.getFeatures().removeChild(child);
can anyone point me to the right direction on creating a function that will allow me to turn the visibility on/off on demand please.
Your hidePlacemark function is missing some {} in your final IF statement
if(child.getId()== name)
you have
function hidePlacemark(name){
var children = ge.getFeatures().getChildNodes();
for(var i = 0; i < children.getLength(); i++) {
var child = children.item(i);
if(child.getType() == 'KmlPlacemark') {
if(child.getId()== name)
child.setVisibility(false);
}
}
}
make it
function hidePlacemark(name){
var children = ge.getFeatures().getChildNodes();
for(var i = 0; i < children.getLength(); i++) {
var child = children.item(i);
if(child.getType() == 'KmlPlacemark') {
if(child.getId()== name) {
child.setVisibility(false);
}
}
}
}
HOWEVER ------- you are better off doing this as it is much faster as you don't need to loop through ALL your placemarks
function hidePlacemark(name) {
var placemark = ge.getElementById(name);
placemark.setVisibility(false);
}
I think the plain ge.getFeatures().removeChild(placemark); works.
I played with this GooglePlayground, and just added the following code to line 8 (that is empty in this GooglePlayground Sample):
addSampleButton('Hide Placemark', function(){
ge.getFeatures().removeChild(placemark);
});
Clicking the button Hide Placemark hides the placemark like a charm here. Any chances your problem is somewhere else in your code?

Show static non-clickable heading in AutoCompleteExtender list

I have an AutoCompleteExtender from the Ajax Control Toolkit. I need to have a heading in the dropdown list that shows how many items found, but it should not be selectable as an item.
I have tried this using jQuery, but even when I just add as a div, it is still selected as an item into the text box when I click on it:
function clientPopulated(sender, e) {
var completionList = $find("AutoCompleteEx").get_completionList();
var completionListNodes = completionList.childNodes;
for (i = 0; i < completionListNodes.length; i++) {
completionListNodes[i].title = completionListNodes[i]._value.split(':')[2];
}
var resultsHeader;
if(completionListNodes.length==1000)
resultsHeader = 'Max count of 1000 reached.<br/>Please refine your search.';
else if(completionListNodes.length>0)
resultsHeader = completionListNodes.length + ' hits.';
else
resultsHeader = msg_NoObjectsFound ;
jQuery(completionListNodes[0]).before('<div>' + resultsHeader + '</div>');
}
Add OnClientItemSelected and OnClientShowing events handlers and try script below:
function itemSelected(sender, args) {
if (args.get_value() == null) {
sender._element.value = "";
}
}
function clientShowing() {
var extender = $find("AutoCompleteEx");
var optionsCount = extender.get_completionSetCount();
var message = "";
if (optionsCount == 1000) {
message = 'Max count of 1000 reached.<br/>Please refine your search.';
}
else if (optionsCount > 0) {
message = optionsCount + " hits."
}
else {
message = "oops."
}
jQuery(extender.get_completionList()).prepend("<li style='background-color:#ccc !important;'>" + message + "</li>");
}
Added:
you even can do this without OnClientItemSelected handler:
function clientShowing() {
var extender = $find("AutoCompleteEx");
var oldSetText = extender._setText;
extender._setText = function (item) {
if (item.rel == "header") {
extender._element.value = "";
return;
}
oldSetText.call(extender, item);
};
var optionsCount = extender.get_completionSetCount();
var message = "";
if (optionsCount == 1000) {
message = 'Max count of 1000 reached.<br/>Please refine your search.';
}
else if (optionsCount > 0) {
message = optionsCount + " hits."
}
else {
message = "oops."
}
jQuery(extender.get_completionList()).prepend("<li rel='header' style='background-color:#ccc !important;'>" + message + "</li>");
}
We can give a better answer if you post the output html of your autocomplete control. Anyway if its a dropdown control;
jQuery(completionListNodes[0]).before('
<option value="-99" disabled="disabled">your message here</option>'
);
The answer by Yuriy helped me in solving it so I give him credit although his sollution needed some changes to work.
First of all, the clientShowing event (mapped by setting OnClientShowing = "clientShowing" in the AutoExtender control) is executed on initialization. Here we override the _setText method to make sure nothing happens when clicking on the header element. I have used the overriding idea from Yuriy's answer that really did the trick for me. I only changed to check on css class instead of a ref attribute value.
function clientShowing(sender, e) {
var extender = sender;
var oldSetText = extender._setText;
extender._setText = function (item) {
if (jQuery(item).hasClass('listHeader')) {
// Do nothing. The original version sets the item text to the search
// textbox here, but I just want to keep the current search text.
return;
}
// Call the original version of the _setText method
oldSetText.call(extender, item);
};
}
So then we need to add the header element to the top of the list. This has to be done in the clientPopulated event (mapped by setting OnClientPopulated = "clientPopulated" in the AutoExtender control). This event is executed each time the search results have been finished populated, so here we have the correct search count available.
function clientPopulated(sender, e) {
var extender = sender;
var completionList = extender.get_completionList();
var completionListCount = completionList.childNodes.length;
var maxCount = extender.get_completionSetCount();
var resultsHeader;
if(completionListCount == maxCount)
resultsHeader = 'Max count of ' + maxCount + ' reached.<br/>'
+ 'Please refine your search.';
else if(completionListCount > 0)
resultsHeader = completionListCount + ' hits.';
else
resultsHeader = 'No objects found';
jQuery(completionList).prepend(
'<li class="listHeader">' + resultsHeader + '</li>');
}
I have also created a new css class to display this properly. I have used !important to make sure this overrides the mousover style added from the AutoExtender control.
.listHeader
{
background-color : #fafffa !important;
color : #061069 !important;
cursor : default !important;
}

auto Focus (Hit Enter) Javascript function is working good in IE7 but not working in IE8

I used a javascript FocusChange() in my aspx page. I have couple of controls and I need Hit enter key need to move next control based on tab index. It is working good in IE7 but not working in IE8... Please help me on this..
Thanks for your help in advance. The java script is given below.
function FocusChange() {
if (window.event.keyCode == 13) {
var formLength = document.form1.length; // Get number of elements in the form
var src = window.event.srcElement; // Gets the field having focus
var currentTabIndex = src.getAttribute('tabindex'); // Gets its tabindex
// scroll through all form elements and set focus in field having next tabindex
for (var i = 0; i < formLength; i++) {
if (document.form1.elements[i].getAttribute('tabindex') == currentTabIndex + 1) {
for (var j = i; j <= formLength; j++) {
if (document.form1.elements[j].disabled == false) {
document.form1.elements[j].focus();
event.returnValue = false;
event.cancel = true;
return;
}
}
}
}
}
}
I've got the same request as you, but solved it in a different manner, just replacing the Enter for Tab
<script language="JavaScript">
document.onkeydown = myOnkeydown;
function myOnkeydown()
{
var key = window.event.keyCode;
if (key == 13) //13 is the keycode of the 'Enter' key
{window.event.keyCode = 9; //9 is the code for the 'Tab' key. Essentially replacing the Enter with the Tab.
}
}
</script>
Warning: Works in IE only.

Displaying CSS images in div updated by Ajax

I wonder if anyone can help. An HTML div in a page of mine contains a tree control which is shown or hidden depending upon a button pressed by a user. The button triggers an Ajax event which sets a variable on the server to show or hide the tree so that the state is persisted.
But here's the problem; when the tree is re-displayed, the icons for expanding / collapsing brances are not present. So far, I've not been able to work out why this is the case.
The tree is shown below: the first graphic shows the tree as it should be, the second shows it after it has been hidden and re-displayed.
alt text http://www.dcs.bbk.ac.uk/~martin/Tree_with_icons.png
alt text http://www.dcs.bbk.ac.uk/~martin/Tree_without_icons.png
The tree's HTML is built on the server as a list and each list item has a class reference to CSS as follows:
ul.tree li.liOpen .bullet {
background: url(myApp_Minus.png) center left no-repeat;
cursor: pointer;
}
ul.tree li.liClosed .bullet {
background: url(myApp_Plus.png) center left no-repeat;
cursor: pointer;
}
ul.tree li.liBullet .bullet {
background: url(myApp_Hyphen.png) center left no-repeat;
cursor: pointer;
}
Can anyone advise a method of showing the icons when the tree is re-displayed?
I've tried putting a link to the CSS file in the div, inline CSS elements and so on but without success.
Any help would be welcome.
I attach an extract of the tree's HTML at runtime:
<td align = "left">
<div id = "tree"><ul class = "tree" id = "navTree">
<li class = "liOpen">
<a href = "/myDataSharer/aboutConcept#communities">
<img alt = "Community" src = "/myDataSharer/images/myDataSharer_Community_Small.png">
</a>&nbsp
Martin
<ul>
<li class = "liOpen">
<a href = "/myDataSharer/aboutConcept#datasets">
<img alt = "Tabular dataset" src = "/myDataSharer/images/myDataSharer_TabularDataset_Small.png">
</a>&nbsp
Planets
</li>
<ul>
<li>
<a href = "/myDataSharer/aboutConcept#QAV">
<img alt = "Visualisation" src = "/myDataSharer/images/myDataSharer_Visualisation_Small.png">
</a>&nbsp
Test QAV
</li>
<li>
<a href
The tree itself is in a div called 'tree' which is updated from Javascript method as follows:
document.getElementById("tree").style.visibility = "visible";
document.getElementById("tree").innerHTML = str;
The Javascript for the tree is:
/* WRITTEN BY: Martin O'Shea for myDataSharerAlpha.
*
* This program has been inherited verbatim from the original author's sample code as mentioned
* below. No changes have been made other than a rename of a variable on line 121 from 'mktree' to 'tree'.
* ===================================================================
* Author: Matt Kruse <matt#mattkruse.com>
* WWW: http://www.mattkruse.com/
*
* NOTICE: You may use this code for any purpose, commercial or
* private, without any further permission from the author. You may
* remove this notice from your final code if you wish, however it is
* appreciated by the author if at least my web site address is kept.
*
* You may *NOT* re-distribute this code in any way except through its
* use. That means, you can include it in your product, or your web
* site, or any other form where the code is actually being used. You
* may not put the plain javascript up on your site for download or
* include it in your javascript libraries for download.
* If you wish to share this code with others, please just point them
* to the URL instead.
* Please DO NOT link directly to my .js files from your site. Copy
* the files to your server and use them there. Thank you.
* =====================================================================
* HISTORY
* ------------------------------------------------------------------
* December 9, 2003: Added script to the Javascript Toolbox
* December 10, 2003: Added the preProcessTrees variable to allow user
* to turn off automatic conversion of UL's onLoad
* March 1, 2004: Changed it so if a <li> has a class already attached
* to it, that class won't be erased when initialized. This allows
* you to set the state of the tree when painting the page simply
* by setting some <li>'s class name as being "liOpen" (see example)
*
* This code is inspired by and extended from Stuart Langridge's aqlist code:
* http://www.kryogenix.org/code/browser/aqlists/
* Stuart Langridge, November 2002
* sil#kryogenix.org
* Inspired by Aaron's labels.js (http://youngpup.net/demos/labels/)
* and Dave Lindquist's menuDropDown.js (http://www.gazingus.org/dhtml/?id=109)
*/
// Automatically attach a listener to the window onload, to convert the trees
addEvent(window,"load",convertTrees);
// Utility function to add an event listener
function addEvent(o,e,f){
if (o.addEventListener){ o.addEventListener(e,f,true); return true; }
else if (o.attachEvent){ return o.attachEvent("on"+e,f); }
else { return false; }
}
// utility function to set a global variable if it is not already set
function setDefault(name,val) {
if (typeof(window[name])=="undefined" || window[name]==null) {
window[name]=val;
}
}
// Full expands a tree with a given ID
function expandTree(treeId) {
var ul = document.getElementById(treeId);
if (ul == null) { return false; }
expandCollapseList(ul,nodeOpenClass);
}
// Fully collapses a tree with a given ID
function collapseTree(treeId) {
var ul = document.getElementById(treeId);
if (ul == null) { return false; }
expandCollapseList(ul,nodeClosedClass);
}
// Expands enough nodes to expose an LI with a given ID
function expandToItem(treeId,itemId) {
var ul = document.getElementById(treeId);
if (ul == null) { return false; }
var ret = expandCollapseList(ul,nodeOpenClass,itemId);
if (ret) {
var o = document.getElementById(itemId);
if (o.scrollIntoView) {
o.scrollIntoView(false);
}
}
}
// Performs 3 functions:
// a) Expand all nodes
// b) Collapse all nodes
// c) Expand all nodes to reach a certain ID
function expandCollapseList(ul,cName,itemId) {
if (!ul.childNodes || ul.childNodes.length==0) { return false; }
// Iterate LIs
for (var itemi=0;itemi<ul.childNodes.length;itemi++) {
var item = ul.childNodes[itemi];
if (itemId!=null && item.id==itemId) { return true; }
if (item.nodeName == "LI") {
// Iterate things in this LI
var subLists = false;
for (var sitemi=0;sitemi<item.childNodes.length;sitemi++) {
var sitem = item.childNodes[sitemi];
if (sitem.nodeName=="UL") {
subLists = true;
var ret = expandCollapseList(sitem,cName,itemId);
if (itemId!=null && ret) {
item.className=cName;
return true;
}
}
}
if (subLists && itemId==null) {
item.className = cName;
}
}
}
}
// Search the document for UL elements with the correct CLASS name, then process them
function convertTrees() {
setDefault("treeClass","tree");
setDefault("nodeClosedClass","liClosed");
setDefault("nodeOpenClass","liOpen");
setDefault("nodeBulletClass","liBullet");
setDefault("nodeLinkClass","bullet");
setDefault("preProcessTrees",true);
if (preProcessTrees) {
if (!document.createElement) { return; } // Without createElement, we can't do anything
uls = document.getElementsByTagName("ul");
for (var uli=0;uli<uls.length;uli++) {
var ul=uls[uli];
if (ul.nodeName=="UL" && ul.className==treeClass) {
processList(ul);
}
}
}
}
// Process a UL tag and all its children, to convert to a tree
function processList(ul) {
if (!ul.childNodes || ul.childNodes.length==0) { return; }
// Iterate LIs
for (var itemi=0;itemi<ul.childNodes.length;itemi++) {
var item = ul.childNodes[itemi];
if (item.nodeName == "LI") {
// Iterate things in this LI
var subLists = false;
for (var sitemi=0;sitemi<item.childNodes.length;sitemi++) {
var sitem = item.childNodes[sitemi];
if (sitem.nodeName=="UL") {
subLists = true;
processList(sitem);
}
}
var s= document.createElement("SPAN");
var t= '\u00A0'; //
s.className = nodeLinkClass;
if (subLists) {
// This LI has UL's in it, so it's a +/- node
if (item.className==null || item.className=="") {
item.className = nodeClosedClass;
}
// If it's just text, make the text work as the link also
if (item.firstChild.nodeName=="#text") {
t = t+item.firstChild.nodeValue;
item.removeChild(item.firstChild);
}
s.onclick = function () {
this.parentNode.className = (this.parentNode.className==nodeOpenClass) ? nodeClosedClass : nodeOpenClass;
return false;
}
}
else {
// No sublists, so it's just a bullet node
item.className = nodeBulletClass;
s.onclick = function () { return false; }
}
s.appendChild(document.createTextNode(t));
item.insertBefore(s,item.firstChild);
}
}
}
Thanks.
The Ajax of the web page is shown below:
<script language="Javascript">
function xmlhttpPost(strURL) {
var xmlHttpReq = false;
var self = this;
// Mozilla / Safari.
if (window.XMLHttpRequest) {
self.xmlHttpReq = new XMLHttpRequest();
}
// IE.
else if (window.ActiveXObject) {
self.xmlHttpReq = new ActiveXObject("Microsoft.XMLHTTP");
}
self.xmlHttpReq.open('POST', strURL, true);
self.xmlHttpReq.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
self.xmlHttpReq.onreadystatechange = function() {
if (self.xmlHttpReq.readyState == 4) {
updatePage(self.xmlHttpReq.responseText);
}
}
self.xmlHttpReq.send(getQueryStr());
}
function getQueryStr() {
queryStr = "action=toggleTree";
return queryStr;
}
function updatePage(str) {
if (str == "false") {
// Hide tree buttons and tree.
document.getElementById("tree").style.visibility = "hidden";
document.getElementById("expColTreeButtons").style.visibility = "hidden";
}
else {
// Show tree buttons.
document.getElementById("expColTreeButtons").style.visibility = "visible";
// Show tree.
document.getElementById("tree").style.visibility = "visible";
document.getElementById("tree").innerHTML = str;
}
}
function toggleTree() {
// Make call to server to toggle tree.
document.getElementById("tree").innerHTML = "<img src='/myDataSharer/images/myDataSharer_Wait.gif' alt='Growing tree' />"
xmlhttpPost("/myDataSharer/toggleTree");
}
The Ajax above is triggered from a form which has three buttons. The 'Show / hide' button sees to things; the other two of the buttons are also enclosed within a div but they are alright.
<form>
<input class = "treeButton" type="submit" value="Show / hide" onClick = "toggleTree(); return false;">
<div id = "expColTreeButtons">
<input class = "treeButton" type="submit" value="Expand all" onClick = "expandTree('navTree'); return false;">
<br />
<input class = "treeButton" type="submit" value="Collapse all" onClick = "collapseTree('navTree'); return false;">
<br />
</div>
</form>
Your CSS looks fine ad like something that could produce the example on the left, so it must be the HTML or the JavaScript that does the showing and hiding. How does the JavaScript work?
It's not likely a CSS problem, since it's working the first time. I'd bet the problem lies in how your server is generating content - i.e. not assigning the proper attributes to each node.
This question has now resolved. Thanks those who contributed.
The solution was to re-process the Javascript tree after the div had been updated.

Resources