JavaScript OO Programming and passing variable to PHP - javascript

Using Joomla custom output module to display articles for a category, but have limited space.
Here is the test site: http://cshtest.camdendiocese.org/
In the footer you'll see the third of 4 columns has data. It scrolls forward and backwards using JavaScript. But as you see it has 4 columns, all of which work the same on different categories.
I'm having trouble with creating the other columns because they all will need to use separate data items, hence separate modules. I was trying to create an object but got stopped specifying a JavaScript object inside PHP.
Here is the JavaScript that is working:
// This script provides forward and backward paging for article intros in footer-1
// create an array of strings
// Each array element is initalized at this time to establish global scope
var mytexts = [];
mytexts[0] = "<h2>this is text string one</h2>";
mytexts[1] = "<h2>this is text string two</h2>";
mytexts[2] = "<h2>this is text string three</h2>";
// initialize variables
var txtNum = 0;
var txtLength = mytexts.length - 1;
// function to change string
function changeText(direction, col) {
// get next text number
txtNum = txtNum + direction;
// make sure we loop
if (txtNum > txtLength) {
txtNum = 0;
}
if (txtNum < 0) {
txtNum = txtLength;
}
// change the src attribute of the image
var colx = "col-" + col;
document.getElementById( colx ).innerHTML = mytexts[txtNum];
return false; // prevent default link
}
Here is the constructor I made to try to make an object:
function CatArticles(x0, x1, x2) {
// This script provides forward and backward paging for article intros in footer-1
// create an array of strings
// Each array element is initalized at this time to establish global scope
this.mytexts = [];
this.mytexts[0] = x0;
this.mytexts[1] = x1;
this.mytexts[2] = x2;
// initialize variables
this.Num = 0;
this.txtLength = this.mytexts.length - 1;
this.changeText = changeText;
}
// function to change string
function changeText (direction, col) {
// get next text number
this.Num = this.Num + direction;
// make sure we loop
if (this.Num > this.txtLength) {
this.Num = 0;
}
if (this.Num < 0) {
this.Num = this.txtLength;
}
// change the src attribute of the image
var colx = "col-" + col;
document.getElementById( colx ).innerHTML = this.mytexts[this.Num];
return false; // prevent default link
}
I can instantiate the object with script tags in PHP but can't think how to get the variable created in the html to be output. Do you have a suggestion or approach?

If I understand well, considering they are two different scripts, server side and client side another, the solution would be to use ajax for interaction, after the page has already been generated by php.

Related

Unable to pass value from C# to amcharts JavaScript function

I created a chart with the help of amCharts that shows temperature from the examples on their site. The chart displayed correctly.
Now I am getting the temperature from a database with C# and I am trying to pass the value to the function where temperature was hard coded, so I get dynamic values. However, I just get the chart and the needle is still at 0 and does not show the temperature.
I have tried 3 ways so far:
I used a hidden field, assigned the value to hidden field in C# and called the JavaScript function showing the charts. It only shows chart. The needle does not change.
I used script manager and Web API, (I don't know Web API and just used code on the Internet), it is the same result however the function in c# keeps on being continuously called.
I put the entire amCharts code in a JavaScript function . I got values in c# and then used
String script = "window.onload = function() { UpdateTemp('" + dt.Rows[0][0].ToString()+ "'); };";
ClientScript.RegisterStartupScript(this.GetType(), "UpdateTemp", script, true);
which again shows same result, map is shown needle stays on 0.
This is my code for the 3rd approach:
ASPX page, JavaScript function
<script>
function UpdateTemp(temp) {
am4core.ready(function() {
// Themes begin
am4core.useTheme(am4themes_animated);
// Themes end
// create chart
var chart = am4core.create("chartdiv", am4charts.GaugeChart);
chart.hiddenState.properties.opacity = 0; // this makes initial fade in effect
chart.innerRadius = -25;
var axis = chart.xAxes.push(new am4charts.ValueAxis());
axis.min = 0;
axis.max = 100;
axis.strictMinMax = true;
axis.renderer.grid.template.stroke = new am4core.InterfaceColorSet().getFor("background");
axis.renderer.grid.template.strokeOpacity = 0.3;
var colorSet = new am4core.ColorSet();
var range0 = axis.axisRanges.create();
range0.value = 0;
range0.endValue = 50;
range0.axisFill.fillOpacity = 1;
range0.axisFill.fill = colorSet.getIndex(0);
range0.axisFill.zIndex = - 1;
var range1 = axis.axisRanges.create();
range1.value = 50;
range1.endValue = 80;
range1.axisFill.fillOpacity = 1;
range1.axisFill.fill = colorSet.getIndex(2);
range1.axisFill.zIndex = -1;
var range2 = axis.axisRanges.create();
range2.value = 80;
range2.endValue = 100;
range2.axisFill.fillOpacity = 1;
range2.axisFill.fill = colorSet.getIndex(4);
range2.axisFill.zIndex = -1;
var hand = chart.hands.push(new am4charts.ClockHand());
// using chart.setTimeout method as the timeout will be disposed together with a chart
chart.setTimeout(randomValue, 2000);
function randomValue(temp) {
hand.showValue(temp, 1000, am4core.ease.cubicOut);
chart.setTimeout(randomValue, 2000);
}
}); // end am4core.ready()
};
</script>
C# function to get temperature and call JavaScript function
public void BindGrid(String charttype)
{
string constring = "Data Source=********.DOMAIN.ORG01;Initial Catalog=Temperature;Integrated Security=SSPI;";
using (SqlConnection con = new SqlConnection(constring))
{
using (SqlCommand cmd = new SqlCommand("SELECT Temperature,HighestPoint,LowestPoint FROM Temperature", con))
{
cmd.CommandType = CommandType.Text;
con.Open();
DataTable dt = new DataTable();
dt.Load(cmd.ExecuteReader());
temp1.Value = dt.Rows[0][0].ToString();
con.Close();
String script = "window.onload = function() { UpdateTemp('" + dt.Rows[0][0].ToString()+ "'); };";
ClientScript.RegisterStartupScript(this.GetType(), "UpdateTemp", script, true);
}
}
}
The quality code should be revised but I managed to make it work.
I made 2 changes:
1 : I used the method "PageLoad" instead of "BindGrid" (do you really need this one ?)
2 : I filled the raw temperature value directly as a parameter (as it is a float on my side), you englobed your datarow value with simple quote, that's interpreted as string by javascript and the method "showValue" from "hand" seems to not tolerate it.
Code:
Result:
Some ideas to improve your code:
Put your connection string in configuration file (never in code !)
If you use the "using" do not matter calling the method "close" of your resource, it's automatically called by it. (behind the using it's in fact a try-catch-finally, the close is called anyway in the finally ;) )
If possible separate the data access from the rendering page, create a separate class that manages it, it will improve readability and evolution of your code.
If the variable "temp1" is not used, just remove it.
When you get a data (no matter the source, could be a webservice or database or else), check always if it's null before access it and log it, it doesn't cost a lot and you avoid escalation of exceptions.
We are in 2019, you could use Dapper to get directly object as result from your queries instead of a generic datarow. (check the website if interested they have a lot of interesting tutorials).
Kr,
Ali

InDesign Export all TextFrames to JPG or PNG Individually

I'm struggling with a problem where I need to get JPGs of all the written questions in InDesign since I work at a school. To make the process faster, I decided to go the "Export Objects" way. But i couldn't find a suitable script to achieve what I need.
Basically,
Select All Text Frames in the given Layout(Alternates are present)
Export All Text frames as JPG with given DPI value and name variable.
Both actions are actually available with the Sample codes, but in a different way. For example: SelectObject script works, but when combined with Command+E, the export result is one big image that contains all the selected frames. I want them separately.
I've tried combining the scripts given as applescript or js with my little knowledge of coding but no cigar.
Select Text : Indesign CC 2018 - SelectObjects Sample Code.
Image export: Indesign CC 2018 - Export to JPG scripts are the codes I've tried mingling with.
Goal: Export all text frames individually as variable DPI, variable name and variable destination from separate alternate layouts.
Edit 1: Code that I'm trying to mingle with. I found it on reddit but even though it essentially does what i need, it does in a crooked way and unusable. IT exports ALL items and they are with the page background, I'd need only the stuff that are contained within the TextFrame.
// Set resolution options
var rp = [72,150,300];
// Set EXPORT presets
app.pngExportPreferences.exportResolution = 72;
app.pngExportPreferences.antiAlias = true;
app.pngExportPreferences.pngQuality = PNGQualityEnum.MAXIMUM;
app.pngExportPreferences.pngColorSpace = PNGColorSpaceEnum.RGB;
app.pngExportPreferences.pngExportRange = PNGExportRangeEnum.EXPORT_RANGE;
// Resolution Select Dialog
var w;
w = new Window('dialog', 'Export Presets');
w.orientation = 'row';
with (w)
{
w.sText = add('statictext', undefined, 'Select Export Resolution:');
w.dropdown = add ( 'DropDownList',undefined,undefined,{items:rp});
w.btnOK = add('button', undefined, 'OK');
};
w.dropdown.selection=0;
w.show();
// Set resolution
var x = 0;
x = x + w.dropdown.selection;
app.pngExportPreferences.exportResolution = rp[x];
// SElECT Folder locations
var myFileAmount = 0;
var myFileLocation = Folder.selectDialog("Please select path of InDesign Files");
myFolder = new Folder ([myFileLocation]);
myFolderContents = myFolder.getFiles("*.indd"); // array
myFileAmount = (myFolderContents.length - 1);
// CHECK IF FILES IN FOLDER
if (myFileAmount < 0){alert("No Files in selected folder");}
else
{
var mySaveLocation = Folder.selectDialog("Please select path to Save PNG");
mySaveFolder = new Folder ([mySaveLocation]);
// OPEN FILES One at a time
for (i = myFileAmount; i >= 0; i--)
{
app.open(File (myFolderContents[i]));
app.scriptPreferences.userInteractionLevel = UserInteractionLevels.NEVER_INTERACT;
// SET Active Document
var myDoc = app.activeDocument;
// EXPORT
//One Page at a time
for (var p=0; p < myDoc.pages.length; p++)
{//page loop
var pname = myDoc.pages.item(p).name;
//set all items in file hiden
for (var itemnum=0;itemnum < myDoc.pages.item(p).pageItems.length; itemnum++)
{
myDoc.pages.item(p).pageItems.item(itemnum).visible = false;
}
// set page to export
app.pngExportPreferences.pageString = pname;
//add a 0 in from on less than 10
if (p<9){var pnm=0;}else{var pnm=""}
//LOOP Through items Set Visibility, export a PNG, Set Visibility
for (var itemexport=0;itemexport < myDoc.pages.item(p).pageItems.length; itemexport++)
{
var itm = itemexport+1;//items start at 1 not 0
myDoc.pages.item(p).pageItems.item(itemexport).visible = true;
//ACTUALLY EXPORTING
app.activeDocument.exportFile(
ExportFormat.PNG_FORMAT,
File(mySaveFolder.fsName + "/" + app.activeDocument.name.split(".indd")[0] + "_"+ pnm + pname + "_" + itm +".png"),
false,
);
//SET VISIBILITY
myDoc.pages.item(p).pageItems.item(itemexport).visible = false;
}
}
app.activeDocument.close(SaveOptions.no);
}
}

JavaScript and conditional formatting based on localStorage contents

I'm writing a GreaseMonkey script to make some much-needed enhancements to my employer's internal job search website.
The loop I've included here iterates through all the rows in a table returned by a job search. I'm adding icons to the left side of each row to track communication, acceptance, and rejection. The data is logged in the browser localStorage as a JSON object converted to a string. An example localStorage entry looks like this:
job_12345 = '{"accept":"9/3/2017, 6:50 PM","reject":"9/3/2017, 6:50 PM"}'
In the below function, lstore is a localStorage key+value like the above example. The loop works fine until I try to add in a bit of conditional formatting that shows a "disabled" icon (really just 50% opacity) for job actions that don't exist in localStorage.
It's that three-line IF statement near the bottom of my loop that I just cannot figure out. It works on the first time in the array, and then the array simply breaks and the function ends.
function checkRows() {
if (document.getElementsByClassName("jstat") != null) {
clearInterval(startIt2);
var jobs2 = document.getElementById('results').getElementsByClassName('jrow');
for (var j = 0; j < jobs2.length; j++) {
// conditional formatting by status
var job_status = jobs2[j].getElementsByClassName("jstat")[0].innerText;
job_status = job_status.toLowerCase();
job_status = job_status.replace(/ /g, "_");
// apply conditional class to each row
jobs2[j].classList.add(job_status);
// get job id for shortcut buttons
var jid = jobs2[j].getElementsByTagName("input")[0].value;
// get the table cell where the buttons go
var job_box = jobs2[j].getElementsByClassName("col-chk")[0];
job_box.classList.add("icon");
// read localStorage for selected job
var lstore = JSON.parse(localStorage.getItem("job_" + jid));
// array for adding buttons
var arr_acts = ["mail", "accept", "reject"];
for (var a = 0; a < arr_acts.length; a++) {
// get the action from the array
var action = arr_acts[a];
// create a new span to hold the icon
var span = document.createElement("span");
span.setAttribute("data-jobid", jid);
span.setAttribute("data-action", action);
span.id = (action + jid);
span.classList.add(action);
span.onclick = toggleLocal;
//span.classList.add("disabled");
// set the icon for each action
var icon;
switch (action) {
case "mail":
icon = "📧";
break;
case "accept":
icon = "👍";
break;
case "reject":
icon = "👎";
break;
}
// if the action doesn't exist in localStorage, set the class to "disabled"
console.log(lstore);
// *** this is where the loop breaks *** //
if (lstore.hasOwnProperty(action)) {
span.classList.add("disabled");
}
span.innerHTML = icon;
// render the icon
job_box.appendChild(span);
}
}
}
}
Thanks for the help!
I believe I've figured this out - my variable calling to localStorage (lstore) is null if nothing exists for that job ID, and that appears to have been breaking my IF statement. This worked:
// if the action doesn't exist in localStorage, set the class to "disabled"
if ((lstore == null) || !(lstore.hasOwnProperty(action))) {
span.classList.add("disabled");
}

How to improve typeAhead performance on xPage?

Here is my inputText control with typeAhead enabled:
<xp:inputText id="inputNameEditBox">
<xp:typeAhead
mode="full"
minChars="3"
ignoreCase="true"
valueList="#{javascript:return mytypeAheadList();}"
var="searchValue"
valueMarkup="true"
id="typeAhead1">
</xp:typeAhead>
</xp:inputText>
SSJS mytypeAheadList() function calls custom Java userTools.userLookup() function to get a set of suggestions. (Our server cannot access corporate directory so we have to use LDAP HTTP Java API).
SSJS library:
function mytypeAheadList(){
var v=new userTools.userLookup(); //Java library
var usrList = v.getUserList(searchValue);
var lenList = usrList.length;
var retList = "<ul>";
if(lenList>0){
for (var i=0; i<lenList; i++) {
var matchDetails:string = ["<li>",#Name("[ABBREVIATE]", #Left(usrList[i], "#")),"</li>"].join("");
retList += matchDetails;
}
} else {
retList += ["<li>","None found","</li>"].join("");
}
retList += "</ul>";
return retList;
}
So that means userTools Java object is created each time user type a character. Is there a way to avoid it, e.g. make var v a global variable on page load? Seems scope variables cannot accept Java objects.
I would do the following:
Implement the Serializable interface to your POJO returned by getUserLookup. This allows to store the object in viewScope
Limit the max size of lenlist. E.g. 20 results would reduce the time of looping, the size of the HTTP response and the performance in the browser
Cache the result of the search (add searchValue and the resulting HTML string to a map). If a user hits backspace, the whole result must not be recomputed.
Drop SSJS. Use Java.
optional: If possible, precompute the results.
EDIT
Something like this:
function mytypeAheadList(){
// check if value is already cached
if( viewScope.containsKey("search~" + searchValue) ){
return viewScope.get("search~" + searchValue);
}
// get the userLookup object
var v = null;
if( viewScope.containsKey("userLookup") ){
v = viewScope.get("userLookup");
}else{
v = new userTools.userLookup();
viewScope.put("userLookup", v);
}
// if usrList is "unlimited", limit the max size
var usrList = v.getUserList(searchValue);
var lenList = usrList.length > 20 ? 20 : usrList.length;
// if getUserList has a restriction parameter
var usrList = v.getUserList(searchValue, 20);
var lenList = usrList.length;
// build the list
var retList = null;
// reuse a variable is up to 2 times faster
var matchDetails = null;
if(lenList>0){
retList = "<ul>";
for (var i=0; i<lenList; i++) {
// concatenating a string is up to 2 times faster then join
matchDetails = "<li>" + #Name("[ABBREVIATE]", #Left(usrList[i], "#")) + "</li>";
retList += matchDetails;
}
retList += "</ul>";
} else {
// why join a static string?
retList = "<ul><li>None found</li></ul>";
}
// put the result to the cache
viewScope.get("search~" + searchValue, retList);
return retList;
}
Yes you can do that.
Either you can put var v outside of your function to keep it loaded (and just lazy load it first time by checking if it is null).
Or you can put it all in a Java bean and let the scope of the bean determine how long you want to keep the data: ViewScope for just this page - ApplicationScope to allow all users to share it (and you can build in a check to force a maximum age of xx minutes - this could be relevant to consider if the data you look up could change).

How do I store html properties in an object?

I am experementing with javascript objects for the first time and need some help. I want to store generated user input in objects, push them into an array and later on reuse them. So far I have come to this:
function changeColors() {
//get the numbers from the html
var rd = parseInt(document.getElementById("red").value);
var gr = parseInt(document.getElementById("green").value);
var bl = parseInt(document.getElementById("blue").value);
var op = parseFloat(document.getElementById("opacity").value);
//convert the decimal into hexadecimal
var rdhex = (rd < 16) ? "0" + rd.toString(16) : rd.toString(16);
var grhex = (gr < 16) ? "0" + gr.toString(16) : gr.toString(16);
var blhex = (bl < 16) ? "0" + bl.toString(16) : bl.toString(16);
//concatenate all hex to generate a color
var hexcode = "#" + rdhex + grhex + blhex;
//view the change in the browser
document.getElementById("div").style.backgroundColor = hexcode;
document.getElementById("colordisplay").innerHTML = hexcode;
//change opacity
document.getElementById("div").style.opacity = op;
Here I get all the input that I need to store and in the next function I am trying to store it in an object and array:
function Save(){
var colors = {};
var nextColor = []
colors.nextColor = nextColor;
console.log(colors);
var rgb = document.getElementById("colordisplay").innerHTML;
var opacity = document.getElementById("div").style.opacity;
var name = document.getElementById("name").value;
var nextColor = {
"name": name,
"rgb": rgb,
"opacity": opacity
}
colors.nextColor.push(nextColor);
console.log(colors);
}
My question is: is how wrong is that and how it can be corrected?
Thank you!
I am unsure what your question exactly is, but looking at your code for Save I assume you're inquiring how to best store data in the context of an application. Looking at the Save-method body:
var colors = {};
var nextColor = [];
These variables are only available in the scope of the Save function. As such the "colors"-Object will only ever contain one single color Object, i.e. the "nextColor" Object created in the Save function. On top of this, the "colors"-Object is not accessible outside of the Save function, rendering it... well, useless.
Ideally you hold the contents of the "colors"-Object in a global variable (or reference it in another Object available to your application, i.e. a "Model") and fill the colors Object with the return of the Save-method, i.e.:
function Save() {
var rgb = document.getElementById("colordisplay").innerHTML;
var opacity = document.getElementById("div").style.opacity;
var name = document.getElementById("name").value;
var nextColor = {
"name": name,
"rgb": rgb,
"opacity": opacity
};
return nextColor;
}
// assume an event handler invoked after a form is submitted, this
// creates a nextColor and pushes it into the existing colors Object
function someEventHandler( e ) {
colors.nextColor.push( Save() );
}
This implies that the Save-methods sole function is to gather the values entered in the HTML document, and translate it into a new value Object. The Save-method now has no business knowing about any remaining data belonging to your application. (i.e. the creation of the "colors" Object and its "nextColor"-Array should be left to another function, ideally executed when your application launches).
I guess what I'm saying is you're on the right track, but you can get a lot of mileage by investing some time into creating separate functions to handle your data layer. After all, that's all JSON is, data.
If for instance you want to enter validation in your Save()-method (let's say to make sure that the "name" Input element actually contains a valid String), you just modify it there in that one function. If you additionally wish to make sure that the same color isn't added to the "nextColor"-Array twice, you can make another function that checks whether a color with the same values is already present in the data Object and either removes it or prevents pushing the duplicate value into the Array. This is logic that shouldn't be in the Save()-method, as such you can structure your program to organize your data neatly.
I hope this is the answer you were looking for.
Try this:
var colors = {
"nextColor": []
};
function Save() {
colors.nextColor.push({
"name": document.getElementById("name").value,
"rgb": document.getElementById("colordisplay").innerHTML,
"opacity": document.getElementById("div").style.opacity
});
console.log(colors);
}
Notice that the colors variable should be outside the scope of the function in order to retain the variable beyond individual runs of the Save() function.
I've also simplified the code quite a bit.

Categories