execCommand without a selection? (set font, size etc) - wysiwyg

I can get my custom WYSIWYG editor to apply styling to selected text no problem:
pnlDocumentEditor_IFrame.document.execCommand(cmd, ui, opt)
.. but what I need to be able to do is allow the user to set a font, or font size, or bold etc so that text typed AFTER this command is issued will have that style applied.
Is this possible? All execCommands I've tried seem to only work on selected text.

Appling execCommand on certain element, WITHOUT selecting it with mouse, can be done with this function:
jsFiddle example
function execCommandOnElement(el, commandName, value) {
if (typeof value == "undefined") {
value = null;
}
if (typeof window.getSelection != "undefined") {
// Non-IE case
var sel = window.getSelection();
// Save the current selection
var savedRanges = [];
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
savedRanges[i] = sel.getRangeAt(i).cloneRange();
}
// Temporarily enable designMode so that
// document.execCommand() will work
document.designMode = "on";
// Select the element's content
sel = window.getSelection();
var range = document.createRange();
range.selectNodeContents(el);
sel.removeAllRanges();
sel.addRange(range);
// Execute the command
document.execCommand(commandName, false, value);
// Disable designMode
document.designMode = "off";
// Restore the previous selection
sel = window.getSelection();
sel.removeAllRanges();
for (var i = 0, len = savedRanges.length; i < len; ++i) {
sel.addRange(savedRanges[i]);
}
} else if (typeof document.body.createTextRange != "undefined") {
// IE case
var textRange = document.body.createTextRange();
textRange.moveToElementText(el);
textRange.execCommand(commandName, false, value);
}
}
And example usage:
var testDiv = document.getElementById("test");
execCommandOnElement(testDiv, "Bold");
execCommandOnElement(testDiv, "ForeColor", "red");
Source: set execcommand just for a div

Related

Word Addin is getting crashed when trying to delete a row which contains a content control in a table

I'm developing a Word Add-in (Word API + Office.js) where i am working with content controls, I am trying to read the table content inside a content control where I need remove the empty rows
Sample: I have this table inside a content control I have to remove the blank rows
i am able to achieve this functionality with this code, but if the table contains a content control which is blank then when i try to delete that row the addin itself is getting crashed.
function checktable(element) {
Word.run(function (context) {
// Queue a command to get the current selection and then
// create a proxy range object with the results.
var contentControl = context.document.contentControls.getByTag('control1').getFirst();
var table = contentControl.tables.getFirst();
context.load(contentControl, 'tables');
table.load('values');
return context.sync()
.then(function () {
// Get the longest word from the selection.
if (contentControl.tables.items.length === 0) {
document.getElementById('lblstatus').innerText += "No Tables found";
}
else {
document.getElementById('lblstatus').innerText += " Tables found";
var Tablevaules = table.values;
for (var i = 0, len = Tablevaules.length; i < len; i++)
{
var nullcheck = "";
var inner=Tablevaules[i];
// inner loop applies to sub-arrays
for (var j = 0, len2 = inner.length; j < len2; j++) {
// accesses each element of each sub-array in turn
if (inner[j] == "") {
if (nullcheck != "False") {
nullcheck = "True";
}
}
else {
nullcheck = "False";
}
}
if (nullcheck == "True") {
table.deleteRows(i);
}
}
}
})
.then(context.sync)
.then(function () {
// Queue a command to highlight the search results.
document.getElementById('lblstatus').innerText += element + ":" + "Successs";
});
})
.catch(errorHandler);
}
Please let me know whether i am missing something or its a known bug!!

Chart.js click on labels, using bar chart

i need help with my Chart.js interactivity. When I click on the label, I need to return the column(index) number at which I clicked.
I tried to use getElementsAtEvent but it only work if I click directly at chart.
This http://jsfiddle.net/yxz2sjam/ is pretty much what I am looking for but getPointsAtEvent is no longer available in the new versions.
canvas.onclick = function (evt) {
var points = chart.getPointsAtEvent(evt);
alert(chart.datasets[0].points.indexOf(points[0]));
};
I also found this http://jsfiddle.net/1Lngmtz7/ but it isn't working with bar chart.
var ctx = document.getElementById("myChart").getContext("2d");
var myRadarChart = new Chart(ctx, {
type: 'radar',
data: data
})
$('#myChart').click(function (e) {
var helpers = Chart.helpers;
var eventPosition = helpers.getRelativePosition(e, myRadarChart.chart);
var mouseX = eventPosition.x;
var mouseY = eventPosition.y;
var activePoints = [];
helpers.each(myRadarChart.scale.ticks, function (label, index) {
for (var i = this.getValueCount() - 1; i >= 0; i--) {
var pointLabelPosition = this.getPointPosition(i, this.getDistanceFromCenterForValue(this.options.reverse ? this.min : this.max) + 5);
var pointLabelFontSize = helpers.getValueOrDefault(this.options.pointLabels.fontSize, Chart.defaults.global.defaultFontSize);
var pointLabeFontStyle = helpers.getValueOrDefault(this.options.pointLabels.fontStyle, Chart.defaults.global.defaultFontStyle);
var pointLabeFontFamily = helpers.getValueOrDefault(this.options.pointLabels.fontFamily, Chart.defaults.global.defaultFontFamily);
var pointLabeFont = helpers.fontString(pointLabelFontSize, pointLabeFontStyle, pointLabeFontFamily);
ctx.font = pointLabeFont;
var labelsCount = this.pointLabels.length,
halfLabelsCount = this.pointLabels.length / 2,
quarterLabelsCount = halfLabelsCount / 2,
upperHalf = (i < quarterLabelsCount || i > labelsCount - quarterLabelsCount),
exactQuarter = (i === quarterLabelsCount || i === labelsCount - quarterLabelsCount);
var width = ctx.measureText(this.pointLabels[i]).width;
var height = pointLabelFontSize;
var x, y;
if (i === 0 || i === halfLabelsCount)
x = pointLabelPosition.x - width / 2;
else if (i < halfLabelsCount)
x = pointLabelPosition.x;
else
x = pointLabelPosition.x - width;
if (exactQuarter)
y = pointLabelPosition.y - height / 2;
else if (upperHalf)
y = pointLabelPosition.y - height;
else
y = pointLabelPosition.y
if ((mouseY >= y && mouseY <= y + height) && (mouseX >= x && mouseX <= x + width))
activePoints.push({ index: i, label: this.pointLabels[i] });
}
}, myRadarChart.scale);
var firstPoint = activePoints[0];
if (firstPoint !== undefined) {
alert(firstPoint.index + ': ' + firstPoint.label);
}
});
Thank for response.
I solve the problem with
document.getElementById("chart").onclick = function(e)
{
var activeElement = weatherMainChart.lastTooltipActive;
console.log(activeElement[0]._index);
};
this solution register clicks on chart and label, then I restricted it with e.layerY to register only clicks on label section.
document.getElementById("chart").onclick = function(e)
{
var activeElement = weatherMainChart.lastTooltipActive;
if(e.layerY > 843 && e.layerY < 866 && activeElement[0] !== undefined)
console.log(activeElement[0]._index);
};
If you add a click handler through the onClick option you can use the following code using the getElementsAtEventForMode() call:
function handleClick(evt) {
var col;
switch(chartType) {
case "horizontalBar":
this.getElementsAtEventForMode(evt, "y", 1).forEach(function(item) { col = item._index });
break;
case "bar":
this.getElementsAtEventForMode(evt, "x", 1).forEach(function(item) { col = item._index });
break;
}
if (!col) {
return;
}
alert("Column " + col + " was selected");
};
You'll probably need to add extra switch checks for other chart types but I'm sure you get the idea.
Using version 2.4.0, i created an onClick Event, and inside it
var activeIndex = localChart.tooltip._lastActive[0]._index;
var clickCoordinates = Chart.helpers.getRelativePosition(e, localChart.chart);
if (clickCoordinates.y >= 530) { //custom value, depends on chart style,size, etc
alert("clicked on " + localChart.data.labels[activeIndex]);
}
I Solved this problem with single or multiple label click you will be find using true/false
First you need to set your chartJs Id click
below code SessionChart = Your ChartJs ID e.g. ("myChart") I was replace it for my Id
document.getElementById("SessionChart").onclick = function (evt) {
var meta = SubscriberSessionChart.getDatasetMeta(0);
if (meta.$filler.chart.legend.legendItems[0].text.toLowerCase() "sessions")
{
if (meta.$filler.chart.legend.legendItems[0].hidden) {
sessionHidden = true;
}
}
}
here "sessions" = first label text
meta.$filler.chart.legend.legendItems[0].text.toLowerCase() = is your first label
from Array so you can get multiple label's click here true / false
if (meta.$filler.chart.legend.legendItems[0].hidden) = your label is not active then
you will get hidden true otherwise you will get false if not tick on label
by default label tick hidden is false in chart js

Chart js - avoid overlapping of tooltips in pie chart

I've used chart js library for make pie chart. I want to display tooltips always. I've done this. I've attached screenshot.
But now the tooltips are overlapped . How to solve this?
This is my code
myPieChart = new Chart(pie_chart).Pie(data_results.comp.pie, {
tooltipTemplate: "<%= value %> %",
scaleFontSize: 14,
scaleFontColor: "#333",
tooltipFillColor: "rgba(0,0,0,0)",
onAnimationComplete: function()
{
this.showTooltip(this.segments, true);
},
tooltipEvents: [],
tooltipFontColor: "#000",
});
I want to change tooltip position if already one present in that position.
Actually to detect overlapping tooltips is very difficult.
I solved it in the end by deactivating the color in the toolbox, reducing the size of the tooltip, moving the tooltip closer to the outer border and hiding all tooltips, which represent less than 2%. Example looks like that:
I used for that the following code:
Chart.Tooltip.positioners.outer = function(elements) {
if (!elements.length) {
return false;
}
var i, len;
var x = 0;
var y = 0;
for (i = 0, len = elements.length; i < len; ++i) {
var el = elements[i];
if (el && el.hasValue()) {
var elPosX = el._view.x+0.95*el._view.outerRadius*Math.cos((el._view.endAngle-el._view.startAngle)/2+el._view.startAngle);
var elPosY = el._view.y+0.95*el._view.outerRadius*Math.sin((el._view.endAngle-el._view.startAngle)/2+el._view.startAngle);
if (x < elPosX) {
x = elPosX;
}
if (y < elPosY) {
y = elPosY;
}
}
}
return {
x: Math.round(x),
y: Math.round(y)
};
},
Chart.pluginService.register({
beforeRender: function (chart) {
if (chart.config.options.showAllTooltips) {
// create an array of tooltips
// we can't use the chart tooltip because there is only one tooltip per chart
chart.pluginTooltips = [];
chart.config.data.datasets.forEach(function (dataset, i) {
chart.getDatasetMeta(i).data.forEach(function (sector, j) {
if ((sector._view.endAngle-sector._view.startAngle) > 2*Math.PI*0.02) {
chart.pluginTooltips.push(
new Chart.Tooltip({
_chart: chart.chart,
_chartInstance: chart,
_data: chart.data,
_options: chart.options.tooltips,
_active: [sector]
}, chart)
);
}
});
});
// turn off normal tooltips
chart.options.tooltips.enabled = false;
}
},
afterDraw: function (chart, easing) {
if (chart.config.options.showAllTooltips) {
// we don't want the permanent tooltips to animate, so don't do anything till the animation runs atleast once
if (!chart.allTooltipsOnce) {
if (easing !== 1)
return;
chart.allTooltipsOnce = true;
}
// turn on tooltips
chart.options.tooltips.enabled = true;
Chart.helpers.each(chart.pluginTooltips, function (tooltip) {
tooltip.initialize();
tooltip._options.position = "outer";
tooltip._options.displayColors = false;
tooltip._options.bodyFontSize = tooltip._chart.height*0.025;
tooltip._options.yPadding = tooltip._options.bodyFontSize*0.30;
tooltip._options.xPadding = tooltip._options.bodyFontSize*0.30;
tooltip._options.caretSize = tooltip._options.bodyFontSize*0.5;
tooltip._options.cornerRadius = tooltip._options.bodyFontSize*0.50;
tooltip.update();
// we don't actually need this since we are not animating tooltips
tooltip.pivot();
tooltip.transition(easing).draw();
});
chart.options.tooltips.enabled = false;
}
}
});

Google Spreadsheet - How to avoid sending email duplicates?

I am having an issue with a script. I used the following script from Google Developers Website in order to do a simple merge mail. See https://developers.google.com/apps-script/articles/mail_merge
I modified a bit the script so to prevent email duplicates. However, even if the script seems to work as it marks 'EMAIL_SENT' in each row every time an email is sent. It does not pay attention if the mail as already been marked and still send the mail.
I believe there is an error at line 16 "var emailSent = rowData[6];"
I would really appreciate if someone could help me. Whoever you are thanks in advance.
Here is the modified script :
var EMAIL_SENT = "EMAIL_SENT";
function sendEmails() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataSheet = ss.getSheets()[0];
var dataRange = dataSheet.getRange(2, 1, dataSheet.getMaxRows() - 1, 7);
var templateSheet = ss.getSheets()[1];
var emailTemplate = templateSheet.getRange("A2").getValue();
var objects = getRowsData(dataSheet, dataRange);
for (var i = 0; i < objects.length; ++i) {
var Resume = DriveApp.getFilesByName('Resume.pdf') var Portfolio = DriveApp.getFilesByName('Portfolio.pdf') var rowData = objects[i];
var emailText = fillInTemplateFromObject(emailTemplate, rowData);
var emailSubject = "Architectural Internship";
var emailSent = rowData[6];
if (emailSent != EMAIL_SENT) {
MailApp.sendEmail(rowData.emailAddress, emailSubject, emailText, {
attachments: [Resume.next(), Portfolio.next()]
});
dataSheet.getRange(2 + i, 7).setValue(EMAIL_SENT);
SpreadsheetApp.flush();
}
}
}
function fillInTemplateFromObject(template, data) {
var email = template;
var templateVars = template.match(/\${\"[^\"]+\"}/g);
for (var i = 0; i < templateVars.length; ++i) {
var variableData = data[normalizeHeader(templateVars[i])];
email = email.replace(templateVars[i], variableData || "");
}
return email;
}
function getRowsData(sheet, range, columnHeadersRowIndex) {
columnHeadersRowIndex = columnHeadersRowIndex || range.getRowIndex() - 1;
var numColumns = range.getEndColumn() - range.getColumn() + 1;
var headersRange = sheet.getRange(columnHeadersRowIndex, range.getColumn(), 1, numColumns);
var headers = headersRange.getValues()[0];
return getObjects(range.getValues(), normalizeHeaders(headers));
}
function getObjects(data, keys) {
var objects = [];
for (var i = 0; i < data.length; ++i) {
var object = {};
var hasData = false;
for (var j = 0; j < data[i].length; ++j) {
var cellData = data[i][j];
if (isCellEmpty(cellData)) {
continue;
}
object[keys[j]] = cellData;
hasData = true;
}
if (hasData) {
objects.push(object);
}
}
return objects;
}
function normalizeHeaders(headers) {
var keys = [];
for (var i = 0; i < headers.length; ++i) {
var key = normalizeHeader(headers[i]);
if (key.length > 0) {
keys.push(key);
}
}
return keys;
}
function normalizeHeader(header) {
var key = "";
var upperCase = false;
for (var i = 0; i < header.length; ++i) {
var letter = header[i];
if (letter == " " && key.length > 0) {
upperCase = true;
continue;
}
if (!isAlnum(letter)) {
continue;
}
if (key.length == 0 && isDigit(letter)) {
continue;
}
if (upperCase) {
upperCase = false;
key += letter.toUpperCase();
} else {
key += letter.toLowerCase();
}
}
return key;
}
// Returns true if the cell where cellData was read from is empty. // Arguments: // - cellData: string function isCellEmpty(cellData) {
return typeof(cellData) == "string" && cellData == "";
}
// Returns true if the character char is alphabetical, false otherwise. function isAlnum(char) { return char >= 'A' && char <= 'Z' || char >= 'a' && char <= 'z' || isDigit(char); }
// Returns true if the character char is a digit, false otherwise. function isDigit(char) { return char >= '0' && char <= '9'; }
Your code is really hard to read and the functions that return 2 or more objects make it even harder...you are using variable names that are also a bit confusing.... but that is probably a personal pov :-)
Anyway, I think I've found the issue: when you write var rowData = objects[i];
This "object" is actually the result of the getRowData function but if you look at this function, you'll see that it returns 2 objects, the first one being itself the result of another function (getObjects) ...
You are checking the value is the 6th element of the array which is actually an object and compare it to a string. The equality will never be true.
I didn't go further in the analyse since I found it really confusing ( as I already said) but at least you have a first element to check .
I would suggest you rewrite this code in a more simple way and use more appropriate variable names to help you while debugging.
I would recommend logging both values before executing to make sure they are the same. I would also guess that the email_sent and EMAIL_SENT are different data types. Can also try forcing the value to string for comparison.
To clarify:
logger.Log(emailSent);
logger.Log(EMAIL_SENT);
if (emailSent.toString() != EMAIL_SENT.toString())
{...
Error is in this line of code -
var dataRange = sheet.getRange(startRow, 1, numRows, 2)
It's considering only 2 columns in the range. Changed 2 to 3 and it worked fine.

Reactively counting the number of form inputs completed in Meteor.js

I've been struggling with this implementation so I figure I'd give the good ol' community here at Stack Overflow a shot at solving my problem.
So i got a standard personal information form. The form id is "personal-form".
I have a helper method called {{numFormsCompleted}} from the template.
Here is my implementation of the method --
numFormsCompleted : function(){
var count = 0;
var form = document.getElementById("quote-form");
console.log(form);
if(form == null)
return 0;
else{
for(i = 0; i < form.length; i++){
value = form.elements[i].value;
if(value != "" || value != null)
count++;
}
console.log(count);
return count;
}
}
The form is showing up as null. Anyone know why?
Your template may or may not have been rendered when your code runs, therefore instead of providing it as a helper of your template, you can run your function at your template's rendered callback, and then assign the return value to a reactive session variable.
Template.myFormTemplate.rendered = function() {
Session.setDefault("formElementCount", 0);
var count = 0;
var form = this.find("#quote-form");
console.log(form);
if(!!form){
for(i = 0; i < form.length; i++){
value = form.elements[i].value;
if(value != "" || value != null)
count++;
}
console.log(count);
Session.set("formElementCount", count);
}
}
and then as your helper in your template
Template.myFormTemplate.numFormsCompleted = function() {
var count = Session.get("formElementCount");
return count;
}
This way, whenever your form template gets rendered, it will reactively give you the element count in your helper and as an added bonus, you can use it from within other templates as well.