Prevent CKEditor filtering Radiant Tags (non-valid HTML tags) - ruby-on-rails

I'm using CKEditor with refinerycms (rails CMS) I've also added basic support for radius tags (they are the tags used in Radiant, another rails CMS) so I'm able to list some elements from the model in the page just inserting a code.
The problem is that the radius tags mimic html:
<r:product_listing category="products" list_as="grid"/>
When using CKEditor to modify the page contents it thinks the radius tags are invalid HTML, which is correct and the expected behaviour, but I can't find the way to tell CKEditor to just ignore those tags.
Any ideas?
Thanks in advance
EDIT: Turned out that the tag was being filtered by the sanitize method in rails being called by RefineryCMS.

What kind of issues do you have with custom tags? And on which browsers?
I checked that CKEditor preserves this tag, but wraps entire content with it. To avoid that you have to edit CKEDITOR.dtd, namely:
CKEDITOR.dtd.$empty[ 'r:product_listing' ] = 1;
But that still may not be enough. To have better support you'd need to make more changes in this object - especially important is to define what can be its parents and that it's an inline tag. For example:
CKEDITOR.dtd.p[ 'r:product_listing' ] = 1; // it is allowed in <p> tag
CKEDITOR.dtd.$inline[ 'r:product_listing' ] = 1;
This still may not be enough - for example you'll most likely won't have copy&paste support.
So, if you need more reliable support I'd try a little bit different way. Using CKEDITOR.dataProcessor you can transform this tag into some normal one when data are loaded into editor and when data are retrieved transform it back to that tag.
Example solution:
// We still need this, because this tag has to be parsed correctly.
CKEDITOR.dtd.p[ 'r:product_listing' ] = 1;
CKEDITOR.dtd.$inline[ 'r:product_listing' ] = 1;
CKEDITOR.dtd.$empty[ 'r:product_listing' ] = 1;
CKEDITOR.replace( 'editor1', {
on: {
instanceReady: function( evt ) {
var editor = evt.editor;
// Add filter for html->data transformation.
editor.dataProcessor.dataFilter.addRules( {
elements: {
'r:product_listing': function( element ) {
// Span isn't self closing element - change that.
element.isEmpty = false;
// Save original element name in data-saved-name attribute.
element.attributes[ 'data-saved-name' ] = element.name;
// Change name to span.
element.name = 'span';
// Push zero width space, because empty span would be removed.
element.children.push( new CKEDITOR.htmlParser.text( '\u200b' ) );
}
}
} );
// Add filter for data->html transformation.
editor.dataProcessor.htmlFilter.addRules( {
elements: {
span: function( element ) {
// Restore everything.
if ( element.attributes[ 'data-saved-name' ] ) {
element.isEmpty = true;
element.children = [];
element.name = element.attributes[ 'data-saved-name' ];
delete element.attributes[ 'data-saved-name' ]
}
}
}
} );
}
}
} );
Now r:product_listing element will be transformed into span with zero-width space inside. Inside editor there will be a normal span, but in source mode and in data got by editor#getData() method you'll see original r:product_listing tag.
I think that this solution should be the safest one. E.g. copy and pasting works.

u can also add as protected source, so no filtering or parsing will be done.
config.protectedSource.push(/<r:product_listing[\s\S]*?\/>/g);
just add these line to your config.js ([\s\S]*? is for any random content)
check out http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-protectedSource

Related

onFullSync action, show updated info - DHTMLX Grid + RoR

I have a Ruby on Rails project where I use a DHTMLX Grid.
Is there a way of showing, using the event handler "onFullSync" provided by the grid API, to show updated data?
Let me explain a little better... I know I can do something like:
dp.attachEvent("onFullSync", function(){
alert("update complete");
})
But what I want is something more complex. I want to, after each completed update, alter a div adding the information like this:
Field 2 was updated to XYZ and field 3 was updated to XER on line X
Field 1 was updated to 123 and field 3 was updated to XSD on line Y
Is this possible?
Thanks
There is a onAfterUpdate event that can be used similar to onFullSync
http://docs.dhtmlx.com/api__dataprocessor_onafterupdate_event.html
It will fire after each data saving operation ( if you are saving 5 rows - it will fire 5 times )
Still, info about updated columns will not be available here.
Also, you can try onEditCell event of grid. It fires after changing data in db, but before real saving in database. Here you can get all necessary info - row, column, old value and new value.
http://docs.dhtmlx.com/api__link__dhtmlxtreegrid_oneditcell_event.html
So, what I end up doing was:
After creating the grid I created an array:
var samples = [];
Then, as per #Aquatic suggestion, I added to "onEditCell" event the following line:
samples[samples.length] = grid.cells(rId, 5).getValue();
This allowed me to add to the array the value present on column 5 of the row changed. Then, on "onFullSync" event I hide or show the div created on the view with the messages (I distinguish if it's on row or more changed on the grid).
//Deals with messages after update
dp.attachEvent("onFullSync", function(){
unique_samples = uniq_fast(samples.sort());
if (unique_samples.length == 1){
$('#updated-samples').text("");
$(".messages").show();
$('#updated-samples').text("A seguinte amostra foi actualizada: " + unique_samples[0]);
//to clear the array
samples = [];
} else if (unique_samples.length > 1){
$('#updated-samples').text("");
$(".messages").show();
$('#updated-samples').text("As seguintes amostras foram actualizadas: " + unique_samples.sort().join(", "));
//to clear the array
samples = [];
} else {
$('#updated-samples').text("");
$(".messages").hide();
//to clear the array
samples = [];
}
})
The problem with using "onEditCell" is that everytime a field is changed on that row I get a repeated value on my "samples" array, I I had to remove duplicate from that array. For that I used one of the suggestions at this answer
// remove duplicates in array
function uniq_fast(a) {
var seen = {};
var out = [];
var len = a.length;
var j = 0;
for(var i = 0; i < len; i++) {
var item = a[i];
if(seen[item] !== 1) {
seen[item] = 1;
out[j++] = item;
}
}
return out;
}
Then I have on the beginning of the view, to show the messages:
<div class="alert alert-success messages" id="updated-samples">
And that's it. I could be not the most efficient way but it works for what I wanted. I will leave the question open a few more days to see if any other option appears.

set maxlength in textarea

I want to set maxlength in textarea. I have defined maxlength property in textarea but it seems to be of no use. Pls help. My code:
<html:textarea styleClass="textarea" maxlength="2100" cols="60" rows="3">
Try this javascript function:
$(function(){
$("#id").keypress(function() {
var maxlen = 100; //length as you desire
if ($(this).val().length > maxlen) {
return false;
}
})
});
You have to write code for two events, keyup and copy paste so try this:
onKeyPress = "return ( this.value.length < 2100 );", onPaste = "return onTextAreaPaste(this,2100)"
Here is Js:
function onTextAreaPaste(textArea,size) {
var length = textArea.value.length;
if(window.clipboardData!=undefined)
length = textArea.value.length + window.clipboardData.getData('Text').length;
return length < size;
}
I recently came across a problem with textarea maxlength attribute because the newlines where considered as 1 in Firefox, and 2 in IE and Chrome. So I decide to go for JavaScript for handling this.
Here is how I handle the max length, its a jquery plugin, very easy to set and there's no problem with the different behaviors of browsers, plus give you a feedback of the characters used.

how to copy link name to title

i wanna ask how to change title in
name
so i want to make link name copy to title automatic
so if i make this code
title link
to
title link
how to do that in php or javascript
i know some in php
but need to make all words in link at database or make for every link variable $
can some one help me in that?
I'd suggest:
function textToTitle(elem, attr) {
if (!elem || !attr) {
// if function's called without an element/node,
// or without a string (an attribute such as 'title',
// 'data-customAttribute', etc...) then returns false and quits
return false;
}
else {
// if elem is a node use that node, otherwise assume it's a
// a string containing the id of an element, search for that element
// and use that
elem = elem.nodeType == 1 ? elem : document.getElementById(elem);
// gets the text of the element (innerText for IE)
var text = elem.textContent || elem.innerText;
// sets the attribute
elem.setAttribute(attr, text);
}
}
var link = document.getElementsByTagName('a');
for (var i = 0, len = link.length; i < len; i++) {
textToTitle(link[i], 'title');
}
JS Fiddle demo.
And since it seems traditional to offer a concise jQuery option:
$('a').attr('title', function() { return $(this).text(); });
JS Fiddle demo.
If you don't want to use a library:
var allLinks = document.getElementsByTagName('a');
for(var i = 0; i < allLinks.length; i++){
allLinks[i].title = allLinks[i].innerHTML;
}
Since you wanted to do all this to one element on the page, consider using something like this:
var allLinks = document.getElementById('myelement').getElementsByTagName('a'); // gets all the link elements out of #myelement
for ( int i = 0; i < allLinks.length; i++ ){
allLinks[i].title = allLinks[i].innerHTML;
}
Actually, this is roughly the same as before but we are changing the input elements.
Or, assuming you use jQuery, you could do something like this:
$('a').each(function(){ // runs through each link element on the page
$(this).attr('title', $(this).html()); // and changes the title to the text within itself ($(this).html())
});
In JQuery you can change an attribute by knowing the current tag and using the .attr() feature. Something like $('a').attr('title', 'new_title'); http://api.jquery.com/attr/

ASP.NET MVC Razor extra whitespace rendered

In Asp.net MVC, Razor inserts extra space between text blocks. I want to render a list this way: "1, 2, 3" but get "1 , 2 , 3".
#for (int i = 1; i < 3; i++)
{
<text>#i</text>
if (i != 2)
{
<text>, </text>
}
}
Is there any ways to remove extra whitespace ?
I want to render a list this way: "1, 2, 3"
Quick and dirty:
#string.Join(", ", Enumerable.Range(1, 3))
Obviously a custom helper seems more appropriate to the job of formatting something in the view:
public static class HtmlExtensions
{
public static IHtmlString FormatList(this HtmlHelper html, IEnumerable<int> list)
{
return MvcHtmlString.Create(string.Join(", ", list));
}
}
and then simply:
#Html.FormatList(Model.MyList)
You are seeing the extra whitespace between the number and the comma because your razor template includes a line break (which displays as whitespace in the browser) between the number and the comma:
#for (int i = 1; i < 3; i++)
{
<text>#i</text> >LINE BREAK HERE<
if (i != 2)
{
<text>, </text>
}
}
I like Darin's answer, and I would suggest removing your loop and replacing it with a more declarative statement, but if you don't want to go that far, try at least removing the line break:
#for (int i = 1; i < 3; i++)
{
<text>#i</text>if (i != 2){<text>, </text>}
}
Instead of writing out bits of text in different places each time round the loop, you could accumulate all the text in a StringBuilder, then outside the loop do #stringBuilderObject.ToString().
The problem is with the source that is generated. When you look at the actual source you get:
1
,
2
,
3
As you can see there is a lot of white space in there which browsers collapse down to a single space (look at the definition for normal).
Using a StringBuilder or string.Join is the way to fix this if all you're doing is outputting these three numbers. But if you're trying to do something else and this is a simplified example then see this blog post for a way of doing it using ol/ul and lis.
I might assume that it is not issue of Razor, but rather element is rendered with some margings.
Open FireBug (or Chrome or whatever) and see that it is really markup issue.
In you css file try to add
text { margin: 0 }

Open Source Projects for i18n à la Facebook

Facebook has this unique and clever approach to localization of their site: translators (in their case users that help to translate the site voluntarily) can simply click on the not-yet-translated strings – which are marked with a green bottom border – in their natural context on the site. See http://www.facebook.com/translations/.
Now, if you ever had to deal with the translation of a website, you'll be well aware of how odd and funny some of these translations can be when using tools like poedit where the translator isn't fully aware of the spot the translated string will lated appear in on the website.
Example: Please translate "Home". In German, for instance, the start page of a website would be "Home" while the house you live in is "Heim". Now, you as the translator basically have to guess which context this term is likely to appear in on the website and translate accordingly. Chances are, you're new website on home furniture now translates as "Home-Einrichtung" which sounds ridiculous to any German.
So, my question boils down to:
Do you know any open source PHP projects that work on something like this? I'm basically looking for a framework that allows you to put your internationalized website in "translation mode" and make strings clickable and translatable e.g. through a Javascript modal.
I'm not so much looking for a full-fledged and ready-made solution, but would love to know about similar projects that I can contribute code to.
Thanks in advance!
If you want to roll your own with jquery & jquery browserLanguage, this might get you going.
Tag all translatable text's contain elements with class="i18n", and include jquery, jquery browserLanguage, and your i18n script.
1. the internationalization javascript
— this needs to accept translations via ajax from your server, like:
var i18n = {};
i18n.bank = new Array();
i18n.t = function ( text, tl=$.browserLanguage ) {
var r = false;
$.ajax({
url: "/i18n_t.php?type=request&from="+ escape(text) +"&tl="+ tl,
success: function(){ i18n.bank[text] = this; r = true; }
});
return r;
};
2. php i18n translation service
— now we need to serve up translations, and accept them
the database will look like a bunch of tables, one for each language.
// SCHEMA for each language:
CREATE TABLE `en`
(
`id` INT PRIMARY KEY AUTO INCREMENT NOT NULL,
`from` VARCHAR(500) NOT NULL,
`to` VARCHAR(500) NOT NULL
)
the php will need some connection and db manipulation.. for now this may do:
//Connect to the database
$connection = mysql_connect('host (usually localhost)', 'mysql_username' , 'mysql_password');
$selection = mysql_select_db('mysql_database', $connection);
function table_exists($tablename, $database = false) {
if(!$database) {
$res = mysql_query("SELECT DATABASE()");
$database = mysql_result($res, 0);
}
$res = mysql_query("SELECT COUNT(*) AS count FROM information_schema.tables WHERE table_schema = '$database' AND table_name = '$tablename'
");
return mysql_result($res, 0) == 1;
}
the code is simply:
<?php
// .. database stuff from above goes here ..
$type=$_GET["type"];
$from=$_GET["from"];
$to=$_GET["to"];
$tl=$_GET["tl"];
if (! table_exists($tl)) {
...
}
if ($type == "request") { // might want to set $tl="en" when ! table_exists($tl)
$find = mysql_query("SELECT to FROM `'$tl'` WHERE from='$from'");
$row = mysql_fetch_array($find);
echo $row['to'];
} elsif ($type == "suggest") {
$find = mysql_query("SELECT COUNT(*) AS count FROM `'$tl'` WHERE from='$from'");
if ( !(mysql_result($res, 0)) == 0 ) {
$ins = mysql_query("INSERT INTO `'$tl'` (from, to) VALUES ('$from','$to')");
}
}
?>
3. page translation mechanics
— finally we can tie them together in your webpages with some further jquery:
i18n.suggest = function (from) { // post user translation to our php
$.ajax({
url: "/i18n_t.php?type=suggest&from='+from+'&to="+ escape( $('#i18n_s').contents() ) +"&tl="+ $.browserLanguage,
success: function(){ $('#i18n_t_div').html('<em>Thanks!</em>').delay(334).fadeOut().remove(); }
});
};
$(document).ready(function() {
i18n.t("submit");
i18n.t("Thanks!");
$('.i18n').click( function(event) { //add an onClick event for all i18n spans
$('#i18n_t_div').remove;
$(this).parent().append(
'<div id="i18n_t_div"><form class="i18n_t_form">
<input type="text" id="i18n_s" name="suggestion" value="+$(this).contents()+" />
<input type="button" value="'+ i18n.bank[ "submit" ] +'" onclick="i18n.suggest( '+$(this).contents()+' )" />
</form></div>'
);
}).each(function(){
var c = $(this).contents(); //now load initial translations for browser language for all the internationalized content on the page
if ( i18n.t(c) ){
$(this).html(i18n.bank[c]);
}
});
});
Mind you I don't have a server to test this on... and I don't actually code php. :D It will take some debugging but the scaffolding should be correct.

Resources