Griffon view - can I bind to to array element - view

In a model I have ..
#Bindable contentList = ['A','B','C','D','E','F','G','H','J','K','L','M']
In a view i have
def offset = 0
2.times { outer ->
6.times { inner ->
panel(background: Color.white,
border: BorderFactory.createLineBorder(Color.black),
constraints: inner.equals(5) ? 'grow,wrap' : 'grow') {
label(text: bind {model.contentList[offset++]},
font: new Font("Sans Serif",
Font.PLAIN, 18))
}
}
}
On initial loading this works fine but when I try and update an element in the array in a controller
it's not reflected in the screen .. Is there a way to bind to an array element ?
Thanks

You can use ObservableList for this purpose. For example, in the model, you can declare contentList as:
ObservableList contentList = new ObservableList(['A', 'B', 'C', 'D', 'E',
'F', 'G', 'H', 'J', 'K', 'L', 'M'])
If you bind to the content property of ObservableList, you will be notified when it is changed. You can then use a converter to retrieve the proper value based on certain index, for example:
def offset = 0
def c = { o, v -> v[o] }
2.times { outer ->
6.times { inner ->
label(text: bind('content', source: model.contentList,
converter: c.curry(offset++)))
}
}

Related

Dynamic SLD on Geoserver

Can somebody help me with this CSS sld on geoserver?
If someone have better option rather than my approach, please suggest.
[activity_group_id = 20] [application_status = 'PreSanction-Pending']{
mark:symbol('ttf://ESRI Business#39');
:symbol {
fill:#FF0000;
}
}
[activity_group_id = 20] [application_status = 'Payment-Done']{
mark:symbol('ttf://ESRI Business#39');
:symbol {
fill:#00FF00;
}
}
[activity_group_id = 20] [application_status = 'Work-Completed']{
mark:symbol('ttf://ESRI Business#39');
:symbol {
fill:#0000FF;
}
}
[activity_group_id = 20] [application_status = 'PreSanction-Received']{
mark:symbol('ttf://ESRI Business#39');
:symbol {
fill:#00FFFF;
}
}
this is working fine
but I want to simplify this using if-then-else
I'm stuck on this
as there are more than 30 unique 'activity_group_id' there
I have point data where each row have two variable 'activity_group_id' and 'activity_status',
I'm using ttf symbols to display as layer but the condition is,
each point needs to be assigned color (Fill) according to its status
there are total 4 unique value of it, as I'm new to sld referred docs but not finding any solution.
Edit :- adding some test data for point location layer
activity_group_id application_status desk4_longitude desk4_latitude
20 "Work-Completed" 77.8912643252645 20.7848792063826
20 "PreSanction-Pending" 77.8912791454753 20.7796634062134
20 "Payment-Done" 77.874307404545 20.7786504284761
20 "PreSanction-Pending" 77.8748653559629 20.7777572907007
20 "Payment-Done" 77.8935239518168 20.7742195299066
20 "PreSanction-Pending" 77.8887775696933 20.7848194877974
20 "PreSanction-Received" 77.8829004567405 20.7622202218188
Use the recode function to simplify the style.
I cannot test it, but should be something like this:
[activity_group_id = 20] {
mark:symbol('ttf://ESRI Business#39');
:symbol {
fill: recode(application_status,
'Payment-Done', #00FF00,
'Work-Completed', #0000FF,
'PreSanction-Received', #00FFFF);
}
}
If the colors are the same based on application_status, but shape changes based on the group id, then use cascading and set up separate rules:
[activity_group_id = 20] {
mark:symbol('ttf://ESRI Business#39');
}
[activity_group_id = anotherId] {
mark:symbol('ttf://anotherSymbol');
}
* {
:symbol {
fill: recode(application_status,
'Payment-Done', #00FF00,
'Work-Completed', #0000FF,
'PreSanction-Received', #00FFFF);
}
}
#Andrea Aime
as from your reference I checked docs, and made some changes in syntax of your answer
sharing my answer for other to help
check geoserver css doc
/* #title Apiculture */
[activity_group_id = 20] {
mark:symbol('ttf://ESRI Business#39');
mark-size:15;
:symbol {
fill: [recode(application_status,
'PreSanction-Pending','#FF0000',
'Payment-Done', '#00FF00',
'Work-Completed', '#0000FF',
'PreSanction-Received', '#00FFFF')];
}
}
/* #title Sprinkler Irrigation */
[activity_group_id = 19] {
mark:symbol('ttf://ESRI Business#40');
mark-size:15;
:symbol {
fill: [recode(application_status,
'PreSanction-Pending','#FF0000',
'Payment-Done', '#00FF00',
'Work-Completed', '#0000FF',
'PreSanction-Received', '#00FFFF')];
}
}

jsplumb.connect is not working for second time after adding new source element in angular 4

I am using Angular 4, and I am trying to connect two elements using jsplumb connect plugin.
But it is working fine only for first time,
means if I am having 3 source elements and want to connect to a single target, then it will work fine, it will get connected.
But now if I add the 4th source element in that sources list programmatically, using the child component and then calling connect method, it's not working.
Means once I used that jsplumb.connect function, and then added new source element to list and again calling to connect, it is not working.
parentCreateLine.component.ts
sourceIds = ['s1', 's2', 's3'];
/* this is step 1 intial call from ui */
showIntialConnection() {
this.connectSourceToTargetUsingJSPlumb(this.sourceIds);
}
/* this function will be called from UI to add new source and then show connection */
addNewSourceToListAndConnect(){
this.sourceIds.push('s4');
this.connectSourceToTargetUsingJSPlumb(this.sourceIds);
}
connectSourceToTargetUsingJSPlumb(sourceIds) {
console.log('create connection');
jsPlumb.reset();
let labelName;
for (let i = 0; i < sourceIds.length; i++) {
labelName = 'connection' + (i + 1);
jsPlumb.connect({
connector: ['Flowchart', {stub: [212, 67], cornerRadius: 1, alwaysRespectStubs: true}],
source: sourceIds[i],
target: 'target0',
anchor: ['Right', 'Left'],
endpoint: 'Blank',
paintStyle: {stroke: '#B8C5D6', strokeWidth: 4},
overlays: [
['Label', {label: labelName, location: 0, cssClass: 'connectingConnectorLabel'}]
],
});
}
}
Please help me.
I have tried with uuid also, but got the same output.
Please suggest me the correct way of doing it in Angular 4.
Finally, I got the solution,
I have created the instance of jsplumb in ngAfterViewInit, and then using it and
resetting in the proper order,
this.jsPlumbInstance.reset();
jsPlumb.reset();
this.jsPlumbInstance = jsPlumb.getInstance();
so that everytime will get the new instance.
parentCreateLine.component.ts
jsPlumbInstance;
ngAfterViewInit() {
jsPlumb.reset();
this.jsPlumbInstance = jsPlumb.getInstance();
}
connectSourceToTargetUsingJSPlumb(sourceIds) {
this.jsPlumbInstance.reset();
jsPlumb.reset();
this.jsPlumbInstance = jsPlumb.getInstance();
let labelName;
for (let i = 0; i < sourceIds.length; i++) {
labelName = 'connection' + (i + 1);
this.jsPlumbInstance.connect({
... above code ...
});
}
}
You can share the common jsPlumb instance, Find code below
these works for me
app.component.ts
import { jsPlumb } from 'jsplumb';
constructor(private customService: CustomService) {
customService.jsPlumbInstance = jsPlumb.getInstance();
}
child.component.ts
import {CustomService} from '...path to the service';
constructor(private customService: CustomService) {}
ngAfterViewInit() {
this.jsPlumbInstance = this.customService.jsPlumbInstance;
this.jsPlumbInstance.deleteEveryConnection();
this.jsPlumbInstance.deleteEveryEndpoint();
this.jsPlumbInstance.importDefaults({...})
}

SVG - Convert all shapes/primitives to <path>

I'm doing a number of D3.JS operations requiring that I work with SVG paths instead of primitives/shapes (polylines, recs, etc.).
This question is general, but I'd like to know if it is possible to convert any SVG primitive to a path, either with D3 or another script/library.
For reference, here is a link which does it for polylines: https://gist.github.com/andytlr/9283541
I'd like to do this for every primitive. Any ideas? Is this possible?
JavaScript solution
You can also convert all primitives using Jarek Foksa's path-data polyfill:
It's main purpose is to parse a path's d attribute to an array of commands.
But it also provides a normalize method to convert any element's geometry to path commands.
element.getPathData({normalize: true});
This method will convert all commands and coordinates to absolute values
and reduce the set of commands to these cubic bézier commands: M,L, C, Z.
Example usage: Convert all primitives
(and convert other commands A, S, Q etc.)
const svgWrp = document.querySelector('.svgWrp');
const svg = document.querySelector('svg');
const primitives = svg.querySelectorAll('path, line, polyline, polygon, circle, rect');
const svgMarkup = document.querySelector('#svgMarkup');
svgMarkup.value = svgWrp.innerHTML;
function convertPrimitives(svg, primitives) {
primitives.forEach(function(primitive, i) {
/**
* get normalized path data:
* all coordinates are absolute;
* reduced set of commands: M, L, C, Z
*/
let pathData = primitive.getPathData({
normalize: true
});
//get all attributes
let attributes = [...primitive.attributes];
let path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
//exclude attributes not needed for paths
let exclude = ['x', 'y', 'x1', 'y1', 'x2', 'y2', 'cx', 'cy', 'r', 'rx', 'ry', 'points', 'height',
'width'
];
setAttributes(path, attributes, exclude);
// set d attribute from rounded pathData
path.setPathData(roundPathData(pathData, 1));
svg.appendChild(path);
primitive.remove();
})
// optional: output new svg markup
let newSvgMarkup = svgWrp.innerHTML.
replaceAll("></path>", "/>").
replace(/([ |\n|\r|\t])/g, " ").
replace(/ +/g, ' ').trim().
replaceAll("> <", "><").
replaceAll("><", ">\n<");
svgMarkup.value = newSvgMarkup;
}
function roundPathData(pathData, decimals = 3) {
pathData.forEach(function(com, c) {
let values = com['values'];
values.forEach(function(val, v) {
pathData[c]['values'][v] = +val.toFixed(decimals);
})
})
return pathData;
}
function setAttributes(el, attributes, exclude = []) {
attributes.forEach(function(att, a) {
if (exclude.indexOf(att.nodeName) === -1) {
el.setAttribute(att.nodeName, att.nodeValue);
}
})
}
<script src="https://cdn.jsdelivr.net/npm/path-data-polyfill#1.0.3/path-data-polyfill.min.js"></script>
<p><button type="button" onclick="convertPrimitives(svg, primitives)">Convert Primitives</button></p>
<div class="svgWrp">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150 30">
<polygon id="polygon" fill="#ccc" stroke="green" points="9,22.4 4.1,14 9,5.5 18.8,5.5 23.7,14
18.8,22.4 " />
<polyline id="polyline" fill="none" stroke="red" points="43,22.4 33.3,22.4 28.4,14 33.3,5.5 43,5.5
47.9,14 " />
<rect id="rect" x="57.3" y="5.5" fill="none" stroke="orange" width="16.9" height="16.9" />
<line id="line" fill="none" stroke="purple" x1="52.6" y1="22.4" x2="52.6" y2="5.5" />
<circle class="circle" data-att="circle" id="circle" fill="none" stroke="magenta" cx="87.4" cy="14" r="8.5" />
<path transform="scale(0.9) translate(110,5)" d="M 10 0 A 10 10 0 1 1 1.34 15 L 10 10 z" fill="red" class="segment segment-1 segment-class" id="segment-01"/>
</svg>
</div>
<h3>Svg markup</h3>
<textarea name="svgMarkup" id="svgMarkup" style="width:100%; height:20em;"></textarea>
The above example script will also retain all attributes like class, id, fill etc.
But it will strip attributes like r, cx, rx specific to primitives.
Do we need this polyfill?
Unfortunately, the getPathData() and setPathData() methods are still a svg 2 drafts/proposals – intended to replace the deprecated pathSegList() methods.
Hopefully we will get native browser support in the near future.
Since this polyfill is still rather lightweight (~12.5 KB uncompressed) compared to more advanced svg libraries like (snap.svg, d3 etc.) it won't increase your loading times significantly.
Update: Standalone script (no polyfill dependency)
This is rather a proof of concept – you can convert svg primitives based on pretty basic value calculations – without the need of advanced frameworks/libraries – inspired by this post: Convert all shapes/primitives into path elements of SVG.
But as I fiddled around with my own clunky conversion script, I quickly realised that there were some challenges (that Jarek Foksa's normalizing implementations solves flawlessly) such as:
Relative i.e percentage based units
<circle cx="50%" cy="50%" r="25%" />
OK ... I guess we need to calculate these relative values to absolute coordinates according to the parent svg's boundaries as defined by viewBox property ... maybe no viewBox available at all ... or width/height values.
Or something like rx, ry properties to apply rounded borders to a <rect> element – for a decent conversion we'll need to add some curvy commands like a, c or s.
Paths vs. primitives
It's true that a <path> element can draw just any shape a primitive can offer via cubic or quadratic spline commands – even in a more efficient way due to it's concatenating abilities (combining multiple shapes) and furthermore its relative or shorthand commands.
But it doesn't support relative units – however the shapes you need to convert might heavily depend on relative dimensions (e.g. circular gauges pie charts etc.)
Conclusion
It's not too difficult to write your custom conversion script, but pay attention to some tricky details.
const svg = document.querySelector('svg');
const svgMarkup = document.querySelector('#svgMarkup');
svgMarkup.value = svg.outerHTML;
/**
* example script
**/
function getConvertedMarkup(svg, markupEl, decimals = 1) {
convertPrimitivesNative(svg, decimals);
//optimize output
let newSvgMarkup = svg.outerHTML.
replaceAll("></path>", "/>").
replace(/^\s+|\s+$|\s+(?=\s)/g, "").
replaceAll("> <", "><").
replaceAll("><", ">\n<");
markupEl.value = newSvgMarkup;
}
/**
* parse svg attributes and convert relative units
**/
function parseSvgAttributes(svg, atts) {
let calcW = 0;
let calcH = 0;
let calcR = 0;
//1. check viewBox
let viewBoxAtt = svg.getAttribute('viewBox');
let viewBox = viewBoxAtt ? viewBoxAtt.split(' ') : [];
[calcW, calcH] = [viewBox[2], viewBox[3]];
//2. check width attributes
if (!calcW || !calcH) {
widthAtt = svg.getAttribute('width') ? parseFloat(svg.getAttribute('width')) : '';
heightAtt = svg.getAttribute('height') ? parseFloat(svg.getAttribute('height')) : '';
[calcW, calcH] = [widthAtt, heightAtt];
}
//3. calculate by getBBox()
if (!calcW || !calcH) {
let bb = svg.getBBox();
[calcW, calcH] = [(calcW ? calcW : bb.width), (calcH ? calcH : bb.height)];
}
// calculate relative radius: needed for non square aspect ratios
calcR = Math.sqrt(Math.pow(calcW, 2) + Math.pow(calcH, 2)) / Math.sqrt(2);
let attArr = [...atts];
let attObj = {};
attArr.forEach(function(att) {
let attName = att.nodeName;
// convert percentages to absolute svg units
let val = att.nodeValue;
let percentAtts = ['x', 'y', 'x1', 'y1', 'x2', 'y2', 'r', 'rx', 'ry', 'cx', 'cy', 'width', 'height']
if (val.toString().indexOf('%') !== -1 && percentAtts.indexOf(attName) !== -1) {
// strip units
val = parseFloat(val);
switch (attName) {
case 'cx':
case 'rx':
case 'width':
case 'x':
case 'x1':
case 'x2':
val = 1 / 100 * val * calcW;
break;
case 'cy':
case 'ry':
case 'height':
case 'y':
case 'y1':
case 'y2':
val = 1 / 100 * val * calcH;
break;
case 'r':
val = 1 / 100 * val * calcR;
break;
}
}
attObj[att.nodeName] = val;
});
return attObj;
}
/**
* convert primitive attributes to relative path commands
*/
function convertPrimitivesNative(svg, decimals = 3) {
let primitives = svg.querySelectorAll('line, polyline, polygon, circle, ellipse, rect');
if (primitives.length) {
primitives.forEach(function(primitive) {
let pathData = [];
let type = primitive.nodeName;
let atts = parseSvgAttributes(svg, primitive.attributes, 2);
let path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
//exclude attributes not needed for paths
let exclude = ['x', 'y', 'x1', 'y1', 'x2', 'y2', 'cx', 'cy', 'r', 'rx', 'ry', 'points', 'height',
'width'
];
switch (type) {
case 'rect':
let [rx, ry] = [atts.rx, atts.ry];
rx = !rx && ry ? ry : rx;
ry = !ry && rx ? rx : ry;
let [x, y, width, height] = [atts.x, atts.y, atts.width, atts.height];
let [widthInner, heightInner] = [width - rx * 2, height - ry * 2];
if (rx) {
pathData.push({
type: 'M',
values: [x, (y + ry)]
}, {
type: 'a',
values: [rx, ry, 0, 0, 1, rx, -ry]
}, {
type: 'h',
values: [widthInner]
}, {
type: 'a',
values: [rx, ry, 0, 0, 1, rx, ry]
}, {
type: 'v',
values: [heightInner]
}, {
type: 'a',
values: [rx, ry, 0, 0, 1, -rx, ry]
}, {
type: 'h',
values: [-widthInner]
}, {
type: 'a',
values: [rx, ry, 0, 0, 1, -rx, -ry]
}, {
type: 'z',
values: []
});
} else {
pathData.push({
type: 'M',
values: [x, y]
}, {
type: 'h',
values: [width]
}, {
type: 'v',
values: [height]
}, {
type: 'h',
values: [-width]
}, {
type: 'z',
values: []
});
}
break;
case 'line':
let [x1, y1, x2, y2] = [atts.x1, atts.y1, atts.x2, atts.y2];
pathData.push({
type: 'M',
values: [x1, y1]
}, {
type: 'l',
values: [(x2 - x1), (y2 - y1)]
});
break;
case 'circle':
case 'ellipse':
if (type == 'circle') {
let r = atts.r;
let [cX, cY] = [atts.cx, atts.cy - atts.r];
pathData.push({
type: 'M',
values: [cX, cY]
}, {
type: 'a',
values: [r, r, 0, 0, 1, r, r]
}, {
type: 'a',
values: [r, r, 0, 0, 1, -r, r]
}, {
type: 'a',
values: [r, r, 0, 0, 1, -r, -r]
}, {
type: 'a',
values: [r, r, 0, 0, 1, r, -r]
}, {
type: 'z',
values: []
});
} else {
let rx = atts.rx;
let ry = atts.ry;
let [cX, cY] = [atts.cx, atts.cy - atts.ry];
pathData.push({
type: 'M',
values: [cX, cY]
}, {
type: 'a',
values: [rx, ry, 0, 0, 1, rx, ry]
}, {
type: 'a',
values: [rx, ry, 0, 0, 1, -rx, ry]
}, {
type: 'a',
values: [rx, ry, 0, 0, 1, -rx, -ry]
}, {
type: 'a',
values: [rx, ry, 0, 0, 1, rx, -ry]
}, {
type: 'z',
values: []
});
}
break;
case 'polygon':
case 'polyline':
let closePath = type == 'polygon' ? 'z' : '';
let points = atts.points.replace(/^\s+|\s+$|\s+(?=\s)/g, "").replaceAll(",", " ");
let pointArr = points.split(' ');
pathData.push({
type: 'M',
values: [+pointArr[0], +pointArr[1]]
});
for (let i = 2; i < pointArr.length; i += 2) {
let [x0, y0] = [+pointArr[i - 2], +pointArr[i - 1]];
let [x, y] = [+pointArr[i], +pointArr[i + 1]];
let com = {};
if (y == y0) {
com = {
type: 'h',
values: [x - x0]
}
} else if (x == x0) {
com = {
type: 'v',
values: [y - y0]
}
} else {
com = {
type: 'l',
values: [x - x0, y - y0]
}
}
pathData.push(com);
}
if (closePath) {
pathData.push({
type: 'z',
values: []
});
}
break;
//paths
default:
let dClean = atts.d.replace(/^\s+|\s+$|\s+(?=\s)/g, "").replaceAll(",", " ");
let dArr = dClean.replace(/([a-zA-Z])/g, " | $1").split(' | ');
dArr.shift();
for (let i = 0; i < dArr.length; i++) {
let command = dArr[i].trim().split(' ');
let type = command.shift();
command = command.map((x) => {
return parseFloat(x);
});
pathData.push({
type: type,
values: command
});
}
break;
}
// copy primitive's attributes to path
setAttributes(path, atts, exclude);
// round coordinates and replace primitive with path
path.setPathDataOpt(pathData, decimals);
primitive.replaceWith(path);
})
}
};
function setAttributes(el, attributes, exclude = []) {
for (key in attributes) {
if (exclude.indexOf(key) === -1) {
el.setAttribute(key, attributes[key]);
}
}
}
function getAttributes(el) {
let attArr = [...el.attributes];
let attObj = {};
attArr.forEach(function(att) {
attObj[att.nodeName] = att.nodeValue;
});
return attObj;
}
/**
* return rounded path data
* based on:
* https://github.com/jarek-foksa/path-data-polyfill/blob/master/path-data-polyfill.js
*/
if (!SVGPathElement.prototype.setPathDataOpt) {
SVGPathElement.prototype.setPathDataOpt = function(pathData, decimals = 3) {
let d = "";
if (pathData.length) {
for (let i = 0; i < pathData.length; i++) {
let seg = pathData[i];
let [type, values] = [seg.type, seg.values];
let valArr = [];
if (values.length) {
for (let v = 0; v < values.length; v++) {
val = parseFloat(values[v]);
valArr.push(+val.toFixed(decimals));
}
}
d += type;
if (valArr.length) {
d += valArr.join(" ").trim();
}
}
d = d.
replaceAll(' -', '-').
replaceAll(' 0.', ' .').
replaceAll(' z', 'z');
this.setAttribute("d", d);
}
};
}
<p><button type="button" onclick="getConvertedMarkup(svg, svgMarkup, 2)">Convert Primitives</button></p>
<svg xmlns="http://www.w3.org/2000/svg" data-width="150px" data-height="30px" viewBox="0 0 150 30">
<polygon id="polygon" fill="#CCCCCC" stroke="#E3000F" points="7.9,22.8 3,14.3 7.9,5.8 17.6,5.8 22.5,14.3
17.6,22.8 " />
<polyline id="polyline" fill="none" stroke="#E3000F" points="40.9,22.8 31.1,22.8 26.2,14.3 31.1,5.8
40.9,5.8 45.8,14.3 " />
<rect id="rect" x="37.5%" y="20%" rx="2%" ry="5%" fill="none" stroke="#E3000F" width="6%" height="56%" />
<line id="line" fill="none" stroke="#E3000F" x1="50.5" y1="22.8" x2="52.5" y2="5.8" />
<circle id="circle" fill="none" stroke="#E3000F" cx="52%" cy="49%" r="8%" />
<ellipse id="ellipse" fill="none" stroke="#E3000F" cx="68%" cy="49%" rx="7%" ry="25%" />
<path id="piechart" transform="scale(0.9) translate(130, 6)" d="M 10 0 A 10 10 0 1 1 1.34 15 L 10 10 z"
fill="red" class="segment segment-1 segment-class" id="segment-01" />
</svg>
<h3>Output</h3>
<textarea name="svgMarkup" id="svgMarkup" style="width:100%; height:20em;"></textarea>
Codepen converter example
I found this github site which has a set of java functions for converting shapes to paths: https://github.com/JFXtras/jfxtras-labs/blob/2.2/src/main/java/jfxtras/labs/util/ShapeConverter.java

How can I get hex color in hexadecimal to three.js lambertmaterial function

i have small problem in my project with getting hex color. I have simple variable with colors:
var colors = {
"C":0x000000,
"H":0xffffff,
"O":0xff0000,
...
}
I want to get my color by key in function below: (it is written in typescript)
getAtomColor(element: string) :number{
for (var key in colors) {
if (element == key) {
return colors[key];
}
}
}
Problem is with got atom color (below parameter clr), it is in integer form and function in three.js THREE.MeshLambertMaterial({color:clr}) has undefined parameters. How can I fix this problem?
You should be able to adjust your object to hold the hexadecimal strings - this would guarantee that you get back '0x000000' rather than 0.
var colors = {
'C': '0x000000',
'H': '0xffffff',
'O': '0xff0000',
}
Otherwise, you could store them as THREE.color objects...
var colors = {
'C': new THREE.Color(0x000000),
'H': new THREE.Color(0xffffff),
'O': new THREE.Color(0xff0000),
}

CKeditor stripping font tags instead of converting to span

I have a CKeditor instance (version 4.1.2) with font, size, text and background color buttons in its toolbar, all completely default.
It's created by replacing a <textarea> whose contents are loaded from a database.
When the loaded html contains elements such as:
<h3><font color="red">Big Red Heading</font></h3>
CKeditor is simply stripping away the tags, to leave:
<h3>Big Red Heading</h3>
Whereas, my expectations (according to the docs) were that it should convert this to:
<h3><span style="color:red">Big Red Heading</span></h3>
(It strips tags with size and face attributes also, just the same way).
I haven't changed allowedContent or colorButton_foreStyle, or any other config setting that ought to have any effect on this issue. I've tried removing all custom config (leaving an absolutely default instance of the editor), and it still happens.
Can anyone explain why this might be happening, and how to fix it?
Thanks.
EDIT: The default value of colorButton_foreStyle is set like this in the CKeditor source:
CKEDITOR.config.colorButton_foreStyle = {
element: 'span',
styles: { 'color': '#(color)' },
overrides: [ { element: 'font', attributes: { 'color': null } } ]
};
...which is why I expected it would automatically convert font tags..?
CKEditor hasn't got all possible transformations defined by default. There is a set of them and it will be enlarged in the future, but this specific one wasn't defined yet.
From Advanced Content Filter guide - content transformations:
Currently, we have defined content transformations for only a handful of editor features, but their number will increase in future releases.
So, there are two solutions:
If you want to keep your font tags, then extend the Advanced Content Filter settings by defining config.extraAllowedContent and change the font plugins settings like in HTML output sample.
If you want to automatically transform your font tags to their newer equivalents, then you can add a new transformations. Read more in filter#addTransformations doc.
I got same problem in CKeditor 4. i searched but i didnt get solution. but i need it so i created my own method in js. its working great.
i created ownFunctoin:
function ConvertFontTagToSpanTag(str) {
var startIndex = str.indexOf('<font');
while (startIndex >= 0) {
var endIndex = str.substring(startIndex).indexOf('>');
var subString1 = str.substring(startIndex, (startIndex + endIndex) + 1);
var subString2 = subString1;
var mapObj = {
size: "font-size:",
face: "font-family:",
color: "color:"
};
subString2 = subString2.replace(/size|face|color/gi, function (matched) {
return mapObj[matched];
});
subString2 = subString2.replace(/['"]/g, ';');
subString2 = subString2.replace(/=;/g, '');
subString2 = subString2.replace('font', 'span');
if (subString2.length > 6) {
subString2 = [subString2.slice(0, 6), 'style=\"', subString2.slice(6)].join('');
subString2 = [subString2.slice(0, subString2.length - 1), '\"', subString2.slice(subString2.length - 1)].join('');
}
//Converting Font-size
var sizeIndex = subString2.indexOf('font-size:');
if (sizeIndex >= 0) {
var sizeEndIndex = subString2.substring(sizeIndex).indexOf(';');
var size = subString2.substring(sizeIndex + 10, (sizeIndex + sizeEndIndex));
//Removing Font size
subString2 = subString2.slice(0, (sizeIndex + sizeEndIndex) - 1) + subString2.slice((sizeIndex + sizeEndIndex));
//Adding Font Size
subString2 = [subString2.slice(0, (sizeIndex + sizeEndIndex)-1), ConvertSpanFontSize(size), subString2.slice((sizeIndex + sizeEndIndex)-1)].join('');
}
//end
str = str.replace(subString1, subString2);
startIndex = str.indexOf('<font');
}
str = str.replace(/font>/g, 'span>');
return str;
}
function ConvertSpanFontSize(size) {
switch (size) {
case '1': return '0.63em';
case '2': return '0.82em';
case '3': return '1.0em';
case '4': return '1.13em';
case '5': return '1.5em';
case '6': return '2em';
case '7': return '3em';
default: return '4em';
}
Cheers...! Thank you

Resources