I tried to apply PasswordRenderer to a specific row and column based on the value of that row.
So my custom renderer looked like this -
function firstRowRenderer(instance, td, row, col, prop, value, cellProperties) {
Handsontable.renderers.PasswordRenderer.apply(this, arguments); }
However the problem I face is, while editing the cells, the field is no longer masked. Here is an example - http://jsfiddle.net/whxbv6xh/
If you try and edit the first row of that table, the values are not masked. Any ideas?
This "unmasking" of the field is what the PasswordRenderer is essentially all about. If you want to not be able to see the original values at all you have at least two options:
1.Convert the header row's values beforehand:
function renderAsPassword(password) {
return new Array(password.length + 1).join('*');
}
alert(['', 'Kia', 'Nissan', 'Toyota', 'Honda'].map(renderAsPassword));
2.Create a custom editor, extending the built-in PasswordEditor:
var PasswordEditor = Handsontable.editors.PasswordEditor.prototype.extend();
PasswordEditor.prototype.getValue = function() {
return renderAsPassword(this.TEXTAREA.value);
};
PasswordEditor.prototype.setValue = function(newValue){
this.TEXTAREA.value = renderAsPassword(newValue);
};
Related
I am using amcharts 4 to display temperature lines. At times there are many stations so I would like to have just one tooltip and just for the value the cursor is at instead of one tooltip for every line (because then they overlap and some are unreadable).
And there might be several stations with the same temperature so I would have to list all of them in the tooltip.
Anyone knows how to achieve that?
In amcharts 3 I used a balloonFunction attached to the graphs to create my own tooltip. But yet I couldn't find how to do it with the series in amcharts 4.
Thanks for a hint!
So as David Liang mentioned, since all the data items converge along their x axis value (a datetime in this case), you can limit tooltips down to one by only setting one series' tooltipText, and it will have access to the rest of the data fields via data placeholders. E.g. even though series1's value field is E852_t4m, it can use series30's value by just putting "{median_tBel}".
But if you want to have a tooltip based on which line you're hovering over, how to do that depends whether or not you require the Chart Cursor.
If you don't need it, simply set the tooltipText on the line's bullets, e.g.
series1.bullets.getIndex(0).tooltipText = "{name} {valueY}°C";
Here's a demo of your fiddle with that:
https://codepen.io/team/amcharts/pen/803515896cf9df42310ecb7d8d7a2fb7
But if you require Chart Cursor, unfortunately there isn't a supported option at the moment. There's a kind of workaround but it's not the best experience. You start with doing the above. The Chart Cursor will trigger hover effects on all lines and their bullets, including triggering their tooltips. A bullet's tooltip is actually its series' (series1.bulletsContainer.children.getIndex(0).tooltip === series1.tooltip). If we remove the reference to the bullet's tooltip, e.g. series1.bullets.getIndex(0).tooltip = undefined;, the chart will check up the chain and refer to series' anyway. If we do the same to the series' tooltip, it'll go up the chain to chart.tooltip, if we do this to all series, we basically turn chart.tooltip into a singleton behavior of sorts. But it's not as responsive to mouseovers.
You'll see what I mean with this demo:
https://codepen.io/team/amcharts/pen/244ced223fe647ad6df889836da695a8
Oh, also in the above, you'll have to adjust the chart's tooltip to appear on the left/right of bullets with this:
chart.tooltip.pointerOrientation = "horizontal";
Edit:
Since the first method sufficed, I've updated it with an adapter that checks for other fields in range. In the adapter, the target will be the CircleBullet, target.dataItem.valueY is the currently hovered value, and target.dataItem.dataContext are the other fields at the same date.
This is how I modified tooltipText to show other series within +/-0.5C range of the currently-hovered bullet:
// Provide a range of values for determining what you'll consider to be an "overlap"
// (instead of checking neighboring x/y coords.)
function inRange(valueA, rangeA, rangeB) {
return valueA >= rangeA && valueA <= rangeB;
}
// Provide adapters for tooltipText so we can modify them on the fly
chart.series.each(function(series) {
series.bullets
.getIndex(0)
.adapter.add("tooltipText", function(tooltipText, target) {
// the other data fields will already match on the date/x axis, so skip
// the date and this bullet's data fields.
// (target.dataItem.component is the target's series.)
var skipFields = ["date", target.dataItem.component.dataFields.valueY];
// this bullet's value
var hoveredValue = target.dataItem.valueY;
// all the other data fields at this date
var data = target.dataItem.dataContext;
// flag for adding additional text before listing other nearby bullet values
var otherPoints = false;
Object.keys(target.dataItem.dataContext).forEach(function(field) {
// if the field is neither date, nor bullet's
if (!~skipFields.indexOf(field)) {
if (inRange(data[field], hoveredValue - 0.5, hoveredValue + 0.5)) {
if (!otherPoints) {
tooltipText += "\n\nOthers:";
otherPoints = true;
}
// Keep {data placeholder} notation to retain chart formatting features
tooltipText += "\n" + field + ": {" + field + "}°C";
}
}
});
return tooltipText;
});
});
If your series' data points have different x values, it's impossible to combine all the information into one tooltip.
But if they do have same x values, you can just turn on the tooltip for just one of the series:
...,
series: [{
type: "LineSeries",
tooltipHTML: `xxx`,
...
}, {
type: "LineSeries",
...
}, {
type: "LineSeries",
...
}],
...
And within the tooltip HTML, you have access to the data:
...,
tooltipHTML: `
<strong>Year: </strong>{year}<br />
<strong>Cars: </strong>{cars}<br />
<strong>Motorcycles: </strong>{motorcycles}<br />
<strong>Bicycles: </strong>{bicycles}
`,
...
demo: http://jsfiddle.net/davidliang2008/aq9Laaew/286519/
I have a chart that looks like this:
Data comes from a web service and I want all the data to remain intact as it comes through to my model. The bars on the chart represent timespans (just a number column that displays the timespan.TotalHours).
Occasionally, as shown in the image, a time span will be negative - meaning it was ahead of schedule. When this is the case, I do not want to display that segment at all and in fact would like to subtract that time from the green segment. From what I've looked up, something that could help me with the actual subtraction of the negative number could be post processing. But how would I be able to modify my chart creation after that's done?
In case it's relavent, the code for the chart's creation is below:
<script type="text/javascript">
google.charts.load('current', { 'packages': ['corechart'] });
google.charts.setOnLoadCallback(drawChart);
function drawChart() {
var data = new google.visualization.DataTable();
// Since every row will alwyas have this as the first column, we do not need to iterate. The first column will always be this title.
data.addColumn("string", "Category");
// This iterates through each stage (timespan) and creates a column for it.
// And display only the relavent time spans on the chart.
#foreach (var stage in Model.selectedForm.rows[0].stages)
{
#Html.Raw("data.addColumn('number', '" + Regex.Replace(stage.label, "(\\B[A-Z])", " $1") + "');") // Adding a numeric column for each timespan (stage) in that row. Using Regex to make the labels more friendly to the user.
}
#foreach (var row in Model.selectedForm.rows)
{
#Html.Raw("data.addRow(['" + row.label + "'") //The first part of our row is the label - each subsequent stage will be filled in at each iteration below
foreach (var stage in row.stages) //For each stage in the row being created ...
{
if (stage.timespan.TotalHours <= 0)
{
#Html.Raw(", " + "null") // replace the column with a null value
}
else
{
#Html.Raw(", " + stage.timespan.TotalHours) //...Continue the HTML builder adding additional timespans to eventually...
}
}
#Html.Raw("]);\n\n") //...close it here
}
var options = {
titlePosition: 'none',
width: 1200,
height: 400,
legend: { position: 'top', maxLines: 3 },
bar: { groupWidth: '75%' },
isStacked: true
}
var chart = new google.visualization.BarChart(document.getElementById('SOPChart'));
chart.draw(data, options);
}
</script>
Edit: I've added a check in my code that generates the table to account for the negative value and not drawing the purple segment. However there is still the matter of subtracting that time from the previous segment which I am unclear on how to accomplish.
Edit: Added the updated code with comments. Also added an updated image of what this produces. Now that the actual chart is being handled, I still need a way to subtract the value off of the previous (green) segment. After this edit, here is what the chart looks like now:
if you want to try javascript on the client to correct the issue...
remove the last edit and allow the negative value to come thru
then use following snippet to correct, add just above --> var options = {
for (var i = 0; i < data.getNumberOfColumns(); i++) {
if (data.getValue(i, data.getNumberOfColumns() - 1) < 0) {
// subtract from next to last column
data.setValue(i, data.getNumberOfColumns() - 2, data.getValue(i, data.getNumberOfColumns() - 2) + data.getValue(i, data.getNumberOfColumns() - 1));
// remove negative from last column
data.setValue(i, data.getNumberOfColumns() - 1, null);
}
}
Using OL3, I set the text on a style dynamically:
var myLayer = new ol.layer.Vector({
source: mySource,
style: function (feature, resolution) {
var style = new ol.style.Style({
text: new ol.style.Text({
text: setText(feature)
})
});
return [style];
}
});
I am trying to later read what is stored in text:
text: setText(feature)
I am trying to retrieve the text on a click event but not sure how to access that property under the feature style (feature is the variable from the event containing the clicked feature):
// Get current display text
var currentFeatureStyle = feature.getStyle();
But when I do that, I get a null currentFeatureStyle.
Also tried looping through the feature:
for (var fid in feature)
{
//what to look for to extract the feature text?
But not sure what to look for to extract the feature text. Any help getting back the feature text from a feature would be appreciated.
Thanks
If someone can come up with a more correct answer please by all means post below. I have created a "workaround" since I could not retrieve the text from the feature. The workaround involves the following:
Inside the method the text is originally set, add the following:
function setText(feature)
{
// First do your normal stuff (set the text of the feature)
var output = "myFeatureText";
// Here is the workaround step 1:
// Create a property and set it to the text
feature.displayText = output;
// Return back value for the setting of the style text
return output;
}
Now the text is also saved in a property called displayText. Note: displayText is a made up property, it does not exist in openlayers.
Step 2 is to retrieve the property you created in step 1 to get the display text. In this example we are retrieving the feature from singleclick but it can be from anywhere else you are using the feature:
map.on('singleclick', function (evt) {
var feature = map.forEachFeatureAtPixel(evt.pixel,
function (feature, layer) {
return feature;
});
// Here is the workaround step 2:
// Get "display text" custom field for this feature
var displayText = feature.displayText;
.
And thats all there is to it. There must be a "correct" way to retrieve the text from the style of the feature but I am not aware of it. If someone know how to by all means post your answer.
I am attempting to create a bar graph that when independent sliders are moved they change two bar graph svg heights at the same time and they are stacked, they are different colors show it shows two separate values in the same graph, basically showing growth vs the current. I am using jquery-ui and D3.js. Currently it only moves the one svg elements instead of both at the same time, Id like them both to move at the same time.
HTML
<div id="slider" class="slider">
<label for="amount">Age</label>
<input type="text" id="amount1" style="border:0; font-weight:bold;">
</div>
<div id="slider1" class="slider">
<label for="amount2">Retirement Age</label>
<input type="text" id="amount2" style="border:0; font-weight:bold;">
</div>
JS
//initialize sliders
jQuery(document).ready(function($) {
$("#slider").slider({
max: 100
});
$("#slider").slider({
min: 18
});
$("#slider1").slider({
max: 100
});
$("#slider1").slider({
min: 18
});
//slider actions
$("#slider, #slider1").slider({
value: 10,
animate: "fast" ,
slide: function (event, ui) {
//capture the value of the specified slider
var selection = $("#slider").slider("value");
var selection1 = $("#slider1").slider("value");
//fill the input box with the slider value
$( "#amount1" ).val( selection );
$( "#amount2" ).val( selection1 );
//set width and height, actually I'm a little confused what this is for
var w = 200;
var h = 200;
//data arrays for svgs
var dataset = [];
var dataset1 = [];
//fill the data arrays with slider values
dataset.push(selection);
dataset.push(selection1 + selection);
//draw rectangle on the page
var rectangle = svg.selectAll("rect")
.classed("collapse", true)
.data(dataset);
**
THIS IS WHERE IT CONFUSES ME
**
//I draw the second rectangle here, however I choose the same svg element,
//Im not sure what other way to get it to appear in the same space but
//I am sure this is what is causing my issues
var rectangle1 = svg.selectAll("rect")
.classed("collapse", true)
.data(dataset1);
//not sure what this does
rectangle.enter().append("rect");
rectangle1.enter().append("rect");
rectangle.attr("width", 200).transition().attr("fill", "#A02222").attr("height", function (d) { console.log('d is ' + d);
return d;
}).attr("x", function (d) {
return 40; //I dont know why I return 40?
}).attr("y", function (d) {
return 40; //Same here dont know why I return 40?
});
rectangle1.attr("width", 200).transition().attr("height", function (d) { console.log('d is ' + d);
return d;
}).attr("x", function (d) {
return 40; //I dont know why I return 40?
}).attr("y", function (d) {
return 40; //Same here dont know why I return 40?
});
}
// slider actions ends here
});
//Create SVG element
var svg = d3.select(".svgContain").append("svg").attr("width", 125).attr("height", 300);
});
For starters, you may want to follow this tutorial: http://bl.ocks.org/mbostock/3886208
The "return 40;" that you are wondering about are actually what will specify the position and dimensions of the rect's you're appending to the svg. Those shouldn't just be 40, they should be bound to values in the data set, or based on the index of the bar's series in the set of series or something more meaningful than 40.
There is a stacked bar chart data processor that will take a set of series and spit out a new set of series coordinate definitions that make it easier to calculate how rect's will stack in svg coordinate space: https://github.com/mbostock/d3/wiki/Stack-Layout
Then, there's the more general issue of how to deal with these "nested" data sets where you have series, and in the series there are values and you don't want to have to manually track and select individual series. There are several ways to handle this sort of situation. If you know you will only ever have two series, and you really want fine-grained control over each independently, you could assign the top level object an id and then start the data join for each of the plots by selecting that top level object by id... eg:
var container1 = d3.select("#myContainer1);
container1.selectAll("rect").data(myData1).append("rect");
var container2 = d3.select("#myContainer2);
container2.selectAll("rect").data(myData2).append("rect");
If you do something like that, the first select basically sets the context of the subsequent selects. So, only the rects inside of the "#myContainer1" or "#myContainer2" will get selected by each "selectAll" based on which context you're in.
The other approach is to use nested selections. Nested selections are a little more complicated to wrap your head around, but 90% of the time, this is the approach I use. With nested selections, you would restructure your data slightly and then apply nested selects/joins to bind each series to a dom element and then the values of each series to subelements of each of the series dom elements.
First, read this: http://bost.ocks.org/mike/nest/
and then try making your data something more like this:
data = [
{ key: "series1", values: [...]},
{ key: "series2", values: [...]}
];
Then, you will want to do a nested selection where you start with a selection of the "data" array and bind it to whatever svg or html element you have that wraps each of the two series.
var series = d3.select("svg").selectAll("g.series")
.data(data, function(d){return d.key; });
series.enter().append("g").attr("class", "series");
At this point, d3 will have added a "g" element to your svg element for each series and bound the series object (including the key and values array) to the appended elements. Next, you can make a nested selection to add series-specific elements to the g element... ie:
var rect = series.selectAll("rect").data(function(d) { return d.values });
rect.enter().append("rect");
Note that we used a function in our ".data(...)" call. That's because the values we want passed to the join actually depend on which specific series is being processed by D3.
Now, you'd have a rect added to the g element for each value in each series. Since you used d3 to do the data binding and you used the key function in the first select (".data(data, function(d){return d.key;}"), future selects done in the same nested/keyed manner will update the right g and rect elements.
Here's a Fiddle that demonstrates the concept:
http://jsfiddle.net/reblace/bWp8L/2/
A key takeaway is that you can update the data (including adding additional series) and the whole thing will redraw correctly according to the new nested join.
I have a splitter. In this splitter, I am implementing drag and drop functionality for the list view. Each list view is loaded in "table" format.
I want to drag my list view from left to right.
For the 1st list view, I have this code :
var listViewOptions = {
template: kendo.template(
$("#Firsttemplate").html()
),
dataSource: listdatSource,
};
var sourceListView = $("#First").kendoListView(listViewOptions).data("kendoListView");
var draggableOptions = {
filter: "table",
hint: function (e) {
return $('<div class="new">' + e.html() + '</div>');
}
}
sourceListView.element.kendoDraggable(draggableOptions);
In the filter, if I give "table" / "tbody" the whole content of list view is dragging in a row format and after dropping it is in the right side of splitter and is displaying in a single row. I want to display in the same format as in the leftside.
Can you tell me how to do this?
Thanks
somejQueryElement.html() gives you the inner HTML code of your element. Thus, if you filter on table elements, your argument e of the hint function is the table and e.html() is the content of the table.
If you want to have your table, you have to add reencapsulate the element in table tags :
var draggableOptions = {
filter: "table",
hint: function (e) {
return $('<div class="new"><table>' + e.html() + '</table></div>');
}
}