Passing variable between javascript functions not working? - javascript

I tried using this method to pass a variable to the next function:
function a(form, ctr) {
var a = form;
var b = ctr;
b(ctr);
}
function b(ctr) {
var b = ctr;
}
The exact code is a lot more complicated as i'm using the POST method with ajax: function a begins upon a click and uses both the form and ctr parameters - it then goes to function b which only needs ctr - however this method of passing the variable hasn't worked. Any better solution?
function updateQuestion(form, ctr) {
console.log("Called updateQuestion");
var str1 = "toggleDiv";
var str2 = ctr;
var id = str1.concat(str2);
var divVar = document.getElementById(id);
console.log(divVar);
var getdate = new Date(); //Used to prevent caching during ajax call
if(XMLHttpRequestObject) {
console.log("XMLHttpRequestObject = TRUE");
var myVar = form.create_mcq_question.value;
console.log("MyVar = " + myVar);
var formQuestion = document.getElementById("formQuestion");
console.log("1");
XMLHttpRequestObject.open("POST","hiddent",true);
console.log("2");
XMLHttpRequestObject.onreadystatechange = handleServerResponse;
console.log("3");
XMLHttpRequestObject.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
console.log("4" + document.getElementById("create_mcq_question").value);
var mcqid;
var mcqQuestion;
var mcqAnswerCorrect;
var mcqAnswerWrong1;
var mcqAnswerWrong2;
var mcqAnswerWrong3;
var mcqExplanation;
var error = 0;
if (form.create_mcq_question.value == "" || form.create_mcq_question.value == null ) {
error = 1;
}
else {
mcqQuestion = form.create_mcq_question.value
}
if (form.create_mcq_answer_correct.value == "" || form.create_mcq_answer_correct.value == null) {
mcqAnswerCorrect = "";
error = 1;
}
else {
mcqAnswerCorrect = form.create_mcq_answer_correct.value
}
if ((form.create_mcq_answer_wrong1.value == "" || form.create_mcq_answer_wrong1.value == null) && (form.create_mcq_answer_wrong2.value == "" || form.create_mcq_answer_wrong2.value == null) && (form.create_mcq_answer_wrong3.value == "" || form.create_mcq_answer_wrong3.value == null)) {
error = 1;
}
if (form.create_mcq_answer_wrong1.value == "" || form.create_mcq_answer_wrong1.value == null) {
mcqAnswerWrong1 = "";
}
else {
mcqAnswerWrong1 = form.create_mcq_answer_wrong1.value
}
if (form.create_mcq_answer_wrong2.value == "" || form.create_mcq_answer_wrong2.value == null) {
mcqAnswerWrong2 = "";
}
else {
mcqAnswerWrong2 = form.create_mcq_answer_wrong2.value
}
if (form.create_mcq_answer_wrong3.value == "" || form.create_mcq_answer_wrong3.value == null) {
mcqAnswerWrong3 = "";
}
else {
mcqAnswerWrong3 = form.create_mcq_answer_wrong3.value
}
if (form.create_mcq_explanation.value == "" || form.create_mcq_explanation.value == null) {
mcqExplanation = "";
}
else {
mcqExplanation = form.create_mcq_explanation.value
}
if (error == 0) {
XMLHttpRequestObject.send("create_mcq_question=" + mcqQuestion +
"&create_mcq_correct_answer=" + mcqAnswerCorrect +
"&create_mcq_wrong_answer1=" + mcqAnswerWrong1 +
"&create_mcq_answer_wrong2=" + mcqAnswerWrong2 +
"&create_mcq_answer_wrong3=" + mcqAnswerWrong3 +
"&create_mcq_explanation=" + mcqExplanation +
"&mcqid=" + mcqid );
console.log("5");
handleServerResponse(ctr);
}
else {
document.getElementById("divVar").innerHTML="Cannot update question - please ensure all required fields are filled!";
}
}
}
function handleServerResponse(ctr) {
var str1 = "toggleDiv";
var str2 = ctr;
var id = str1.concat(str2);
var divVar = document.getElementById(id);
console.log("Handle server response called");
if (XMLHttpRequestObject.readyState == 1) {
console.log("Loading");
document.getElementById(divVar).innerHTML="<img src=\"hidden">";
}
if (XMLHttpRequestObject.readyState == 4) {
console.log("4");
if(XMLHttpRequestObject.status == 200) {
document.getElementById(divVar).innerHTML=XMLHttpRequestObject.responseText; //Update the HTML Form element
console.log("divVar found");
console.log(divVar);
}
else {
document.getElementById(divVar).innerHTML="There was a problem updating your question - please try again!"; //Update the HTML Form element
console.log("divVar not found");
console.log(divVar);
}
}
}
And the button which starts the whole thing off:
<input type="button" value="Update My Question!" onclick="updateQuestion(this.form,<?php echo" $ctr"; ?>)">
Firebug showing the first function working, and calling the second one, which doesn't get the variable:
[02:21:42.106] Called updateQuestion
[02:21:42.106] [object HTMLDivElement]
[02:21:42.106] XMLHttpRequestObject = TRUE
[02:21:42.106] MyVar = Gram- bacteria are stained purple with gram staining, while gram+ bacteria are stained pink.
[02:21:42.106] 1
[02:21:42.107] 2
[02:21:42.107] 3
[02:21:42.107] 4Gram- bacteria are stained purple with gram staining, while gram+ bacteria are stained pink.
[02:21:42.108] Handle server response called
[02:21:42.108] Loading
[02:21:42.108] 5
[02:21:42.108] Handle server response called
[02:21:42.109] Loading
[02:21:42.649] Empty string passed to getElementById(). # hidden
[02:21:42.649] TypeError: document.getElementById(...) is null # hidden
[02:21:42.647] Handle server response called
[02:21:42.648] 4

There is already a variable called 'b' inside your function scope. That's why you get the error. If they are global functions, you can use:
function a(form, ctr) {
var a = form;
var b = ctr;
window.b(ctr);
}
function b(ctr) {
var b = ctr;
}

The following makes no sense really:
var b = ctr;
b(ctr);
Here the variable b is treated and invoked as function, and you pass a reference to itself as the parameter. Is that really our intention?
Edit: Now with the additional info, the problem is easy to explain. If you look at your log, you'll notice that the handleServerResponse is invoked several times. The first time "ctr" is passed as expected.
The problem is here:
XMLHttpRequestObject.onreadystatechange = handleServerResponse;
This sets a callback, and the callback will invoke your function without the "ctr" of course, which is why your code runs as it does. You could use an anonymous function so that a closure is used:
XMLHttpRequestObject.onreadystatechange = function() { handleServerResponse(ctr); };
You may want to read JavaScript: The Good Parts by Crockford, I think it would help to improve your JS coding significantly.

Related

value is not added to multidimensional array

The first time the function fires I get this result:
output1:test
The output2 2 alert is not firing. I know something is probably undefined in alert Does anyone know why the value won't be added in the multidimensional array?
I expect this to display after the false1:
output2:test2
Also if you want to fiddle with the code here it is:
https://jsfiddle.net/ndf0sjgf/1/
var carSelectedArray = [
[null]
];
addRow(carSelectedArray);
addRow(carSelectedArray);
function addRow(carSelectedArray) {
var arrayempty = false;
if (carSelectedArray[0][0] == null || carSelectedArray.length == 0) {
arrayempty = true;
} else {
arrayempty = false;
}
if (arrayempty == true) {
carSelectedArray[0][0] = "test";
alert("output1:" + carSelectedArray[0][0]);
} else {
carSelectedArray[1][0] = "test2";
alert("output2:" + carSelectedArray[1][0]);
}
}
Your loop works well, however your didn't define your array well.
there is only 1 dimension here :
var carSelectedArray = [[null]];
So replace with this :
var carSelectedArray = [[],[]];
PS : null is not required
and at the beginning in your function, you define arrayempty to false, so you can remove this :
else {
arrayempty = false;
}
Solution here : https://plnkr.co/edit/Qikalr0jc54R3MRSea4G?p=preview
var carSelectedArray = [[],[]];
addRow(carSelectedArray);
addRow(carSelectedArray);
function addRow(carSelectedArray) {
var arrayempty = false;
if (carSelectedArray[0][0] == null || carSelectedArray.length == 0) {
arrayempty = true;
}
if (arrayempty == true) {
carSelectedArray[0][0] = "test";
alert("output1:" + carSelectedArray[0][0]);
} else {
carSelectedArray[1][0] = "test2";
alert("output2:" + carSelectedArray[1][0]);
}
}

How to optimise RadComboBox clearItems function

I have a MultiSelectDropDown, that is, several RadComboBox controls are used in a combined way. For example, I can have a dropdown for regions, another for depots and another for user. The idea is to change the content of lower levels dynamically whenever items are selected or unselected on a higher level. The problem is that in the case when many items are selected, this becomes brutally slow due to some Telerik functions, but I do not understand why. This is a chunk from the client-side of the MultiSelectDropDown prototype:
changeLowerLevels: function (valueIndex, values, value) {
if (!this.canChange) return;
//Get selected values from combobox
var combo = $find(this.ddlIDs[valueIndex - 1]);
var cbItems = combo.get_checkedItems();
var selectedItems = [];
var change = null;
var counter = 0;
if (cbItems.length) this.filterString = "";
for (var i = 0; i < cbItems.length; i++) {
counter++;
if (this.filterString == "") this.filterString = cbItems[i].get_text();
selectedItems.push(cbItems[i].get_value());
}
if (counter > 1) this.filterString += " with " + (counter - 1) + " other" + ((counter > 2) ? "s" : "");
if (JSON.stringify(selectedItems) === JSON.stringify(this.selectedItems[valueIndex - 1]) || selectedItems == [])
return;
this.selectedItems[valueIndex - 1] = selectedItems;
var controlObject = this;
var combo = $find(this.ddlIDs[valueIndex]);
var comboItems = combo.get_items();
if(!this.disabled) combo.enable();
combo.clearItems();
if (valueIndex == 1) this.twoLevelCache = values;
var val = values;
//break if all items are found
var nrOfSelectedItems = this.selectedItems[valueIndex - 1].length;
var nrOfFoundItems = 0;
var index = 0;
var indexes = [];
var found = false;
while (nrOfFoundItems < nrOfSelectedItems && val[index] !== undefined) {
found = (this.selectedItems[valueIndex - 1].indexOf(val[index].Value) != -1);
if (!(found))
index++;
else {
indexes.push(index)
nrOfFoundItems++;
index++;
}
}
//separators from valuesIndex - 1 level
var controlObject = this;
for (var i = 0; i < indexes.length; i++) {
var separator = new Telerik.Web.UI.RadComboBoxItem();
separator.set_text("<span><a class=\"checkAll tt-multi-uncheck-icon\" index=\"" + index + "\">U</a>" + $find(this.ddlIDs[valueIndex - 1]).findItemByValue(val[indexes[i]].Value).get_text() + "</span>");
separator.set_value("");
separator.set_isSeparator(true);
comboItems.add(separator);
this.twoLevelCache.push(val[indexes[i]].Levels);
//valuesIndex level
var valuesArray = val;
var comboItem = new Telerik.Web.UI.RadComboBoxItem();
for (var depot in valuesArray[indexes[i]].Levels) {
comboItem = new Telerik.Web.UI.RadComboBoxItem();
comboItem.set_text(valuesArray[indexes[i]].Levels[depot].Name);
comboItem.set_value(valuesArray[indexes[i]].Levels[depot].Value);
comboItems.add(comboItem);
comboItem = null;
}
$('#' + this.ddlIDs[valueIndex] + '_DropDown a.checkAll').unbind().on("click", function () {
checkAllLowerItems(this, controlObject.ddlIDs[valueIndex]);
});
}
combo.set_emptyMessage(this.allText);
//$("#" + this.ddlIDs[valueIndex]).html(returnValue);
if (this.ddlIDs.length > valueIndex + 1) {
var paramToPass = (((val == undefined) || (val[index] === undefined)) ? ("") : (val[index]));
if (this.allText.length > 0)
this.changeLowerLevels(valueIndex + 1, paramToPass, "");
else {
if (paramToPass !== "")
paramToPass = paramToPass.Levels;
if ((val[index] == undefined) || (val[index].Levels[0] === undefined) || (val[index].Levels[0].Value === "")) {
this.changeLowerLevels(valueIndex + 1, paramToPass, "");
}
else {
this.changeLowerLevels(valueIndex + 1, paramToPass, val[index].Levels[0].Value);
}
}
}
else {
if (this.allText.length > 0)
this.selectedItems[valueIndex] = "";
else
if ((val[index] == undefined) || (val[index].Levels[0] === undefined) || (val[index].Levels[0].Value === "")) {
this.selectedItems[valueIndex] = "";
}
else {
this.selectedItems[valueIndex] = val[index].Levels[0].Value;
}
}
this.setText();
}
combo.clearItems() is extremeley slow. I have take a look on how it is implemented:
function (){var f=this._parent._getControl();?if(f._checkBoxes){f._checkedIndicesJson="[]";?f._checkedIndices=[];?var g=f.get_items();?for(var d=0,e=g.get_count();?d<e;?d++){var c=f.get_items().getItem(d);?c.set_checked(false);?}f.updateClientState();?}a.RadComboBoxItemCollection.callBaseMethod(this,"clear");?}
How can I make sure that this Javascript function speeds up?
I have finally solved the problem by rewriting Telerik client-side functionalities. It was a long and difficult debugging, but it yielded a large performance boost in the most difficult circumstances. From ~30 000 milliseconds, to ~300. Let's see the parts of the optimization:
The actual rewrite
/* Overriding Telerik functions Start */
var overridenTelerikControls = false;
function overrideTelerikFunctionalities() {
if (!overridenTelerikControls) {
overridenTelerikControls = true;
Telerik.Web.UI.RadComboBox.prototype.clearItems = function (isMultiSelectDropDown) {
this.get_items().clear(isMultiSelectDropDown);
this._itemData = null;
};
Telerik.Web.UI.RadComboBoxItemCollection.prototype.clear = function (isMultiSelectDropDown){
var f=this._parent._getControl();
if(f._checkBoxes){
f._checkedIndicesJson="[]";
f._checkedIndices=[];
var g = f.get_items();
for(var d=0,e=g.get_count();d<e;d++){
var c=f.get_items().getItem(d);
c.set_checked(false, isMultiSelectDropDown);
}
if (isMultiSelectDropDown) {
f._updateComboBoxText();
if (f._checkAllCheckBoxElement != null) {
f._updateCheckAllState();
}
}
f.updateClientState();
}
Telerik.Web.UI.RadComboBoxItemCollection.callBaseMethod(this, "clear");
};
Telerik.Web.UI.RadComboBoxItem.prototype.set_checked = function (d, isMultiSelectDropDown){
if(!this.get_enabled()){
return;
}
this._setChecked(d);
var c=this.get_comboBox();
if(c){
if(d){
c._registerCheckedIndex(this.get_index());
}else{
c._unregisterCheckedIndex(this.get_index());
}
if (!isMultiSelectDropDown) {
c._updateComboBoxText();
}
if((!isMultiSelectDropDown) && (c._checkAllCheckBoxElement!=null)){
c._updateCheckAllState();
}
}
};
}
}
/* Overriding Telerik functions End*/
My approach was to keep the old way of their working by default, but if an isMultiSelectDropDown parameter is passed, then work in the optimized manners. So we have a switch materialized as a parameter and we can turn it on/off. The main difference was that the old way was to change the label text showing the selected elements each time a checkbox is checked/unchecked. The main improvement was to do this change after all the checkboxes were checked/unchecked. This extremely simple idea was the driving force behind the boost of performance.
Actual usage
overrideTelerikFunctionalities();
combo.clearItems(true);
This was the functionalities were overriden if they were not already and the parameter was true, therefore the new approach was chosen.
Test, test, test

Javascript Callback in for Loop

My problem is that I'm having a Function A which calls at one point another function, let's call it Function B (getChildContent) and needs the return value of Function B in order to proceed. I know that it's because of Javascripts Asynchronous Nature, and i tried to solve it with a callback. But i can't get it work properly.
FunctionA(){
//some Code.....
else {
for(i in clustertitles) {
if(S(text).contains(clustertitles[i])) {
var parent = {};
parent.ClusterName = clustertitles[i];
parent.Functions = [];
var str = '== ' + clustertitles[i] + ' ==\n* ';
str = S(text).between(str,'.').s;
var caps = parseFunctions(str);
for(y in caps) {
//var content = getChildContent(caps[y]);
getChildContent(caps[y], function(content) { //Function call
var child = {};
child.FunctionName = caps[y];
child.Content = [];
child.Content.push(content);
parent.Functions.push(child);
console.log(content);
});
}}}
}
function getChildContent (capname, callback) {
t = capname.replace(' ', '_');
bot.page(t).complete(function (title, text, date) {
var str = S(text).between('== Kurzbeschreibung ==\n* ', '.').s;
if(str === undefined || str === null || str === '') {
throw new Error('Undefined, Null or Empty!');
}
else {
var content = {};
str = parseTitles(str);
content.Owner = str[0];
content.Aim = str[1];
content.What = str[2];
content.Who = str[3];
content.Steps = str[4];
content.Page = 'some URL';
callback(content);
}
});
}
So in Function A I'm trying to call getChildContent from a for-Loop and pass the current string from caps-array. For each String in caps-array getChildContent() makes a http request over a node.js module and retrieves a string. With this string i'm building an object (content) which is needed in Function A to continue. However the 'console.log(content)' in Function A just prints out the object which is created with the last string in caps-array, but for many times. E.G. if caps-array has 5 entries, i get 5 times the object which is created with the last entry of caps-array.
How can i manage the loop/callback to get every time the right object on my console?
Your loop should call another function that preserves the value of y, something like this:
FunctionA(){
//some Code.....
else {
for(i in clustertitles) {
if(S(text).contains(clustertitles[i])) {
var parent = {};
parent.ClusterName = clustertitles[i];
parent.Functions = [];
var str = '== ' + clustertitles[i] + ' ==\n* ';
str = S(text).between(str,'.').s;
var caps = parseFunctions(str);
for(y in caps) {
yourNewFunction (y, caps, parent);
}}}
}
function yourNewFunction (y, caps, parent) {
getChildContent(caps[y], function(content) { //Function call
var child = {};
child.FunctionName = caps[y];
child.Content = [];
child.Content.push(content);
parent.Functions.push(child);
console.log(content);
});
}
function getChildContent (capname, callback) {
t = capname.replace(' ', '_');
bot.page(t).complete(function (title, text, date) {
var str = S(text).between('== Kurzbeschreibung ==\n* ', '.').s;
if(str === undefined || str === null || str === '') {
throw new Error('Undefined, Null or Empty!');
}
else {
var content = {};
str = parseTitles(str);
content.Owner = str[0];
content.Aim = str[1];
content.What = str[2];
content.Who = str[3];
content.Steps = str[4];
content.Page = 'some URL';
callback(content);
}
});
}
There are 2 ways to do so.
Put the loop inside a function, execute your callback after the loop is done. (Problematic if you are doing async call inside the loop.
function doLoopdiloopStuff() {
for() {
}
callback();
}
The other way, the way i prefer looks like this:
for(var i = 0; i < stuff || function(){ /* here's the callback */ }(), false; i++) {
/* do your loop-di-loop */
}
In another example:
for (var index = 0; index < caps.length || function(){ callbackFunction(); /* This is the callback you are calling */ return false;}(); index++) {
var element = caps[index];
// here comes the code of what you want to do with a single element
}

Google Closure introducing errors

EDIT
The lesson, learned with the help of #Alex, is that you should never put function declarations in block scope. Not that I intended to do this, but if you slip up, it can cause big problems.
I have a script file that seems to be getting compressed via Google Closure incorrectly. When I run my app with the original code, all works fine. But when I try to compress it with Google Closure, some errors get introduced.
I am NOT using the advanced option; I'm using the basic, default mode
Obviously I can't expect anyone to debug the compressed file, but I'm hoping someone can look at the uncompressed code and let me know if I'm somehow doing something insanely stupid that would trick Closure.
Some notes on the minified code:
Closure is inlining BEFramework.prototype.hstreamLoad and BEFramework.prototype.hstreamEvalJson, and seems to be utterly removing the helper functions getDeleteValue, getValueToDisplay, getDisplayForLabel and likely others.
Uncompressed file is below.
This code can manually be compiled by closure here, which should reproduce the symptoms described above.
(function() {
var $ = jQuery;
// Load and display the messages ("healthstream") for a given module.
// This requires that the module's HTML have specific features, see
// dashboard.htm and contactsManager/details/default.htm for examples.
// This also requires that the `request` support `pageIndex` and `pageSize`,
// so we can handle paging.
//
// Args: `options` An options object with these keys:
// `channelId` The channel ID of the module (for transmitRequest)
// `translationId` Optional alternate ID for translation (if not given,
// `channelId` is used).
// `action` The action (for transmitRequest)
// - Must support `pageIndex` and `pageSize`
// `request` The request (for transmitRequest)
// - Must include `pageIndex` and `pageSize`
// `complete` Optional callback triggered when the load is complete.
// `showOptions` Optional callback if an options menu is supported
// by the calling module. Receives a raw event instance
// and the item on which the options were triggered:
// function showOptions(event, item)
// `context` Optional context (`this` value) for the call to
// `complete` and/or `showOptions`
BEFramework.prototype.hstreamLoad = hstreamLoad;
function hstreamLoad(options) {
var inst = this;
var channelId, translationId, action, request, complete, showOptions, context,
pageIndex, pageCount, pageSize, pageCount,
btnPrevious, btnNext,
dataShownFlags;
// Get our arguments (with defaults)
channelId = options.channelId;
translationId = options.translationId || options.channelId;
action = options.action;
request = $.extend({}, options.request); // Create a *copy*, because we modify it when doing paging
complete = options.complete;
if (typeof complete !== "function") {
complete = undefined;
}
showOptions = options.showOptions;
if (typeof showOptions !== "function") {
showOptions = undefined;
}
context = options.context; // (undefined will automatically become the global object)
// Grab the initial pageIndex and pageSize
pageIndex = request.pageIndex || 1;
pageSize = request.pageSize || 100;
// Disable the button and show "searching" label
$('#healthStreamSearchButton')
.button("disable")
.button("option", "label", BETranslate(translationId, 'HealthStreamSearching'));
// Hook up the buttons; be a bit paranoid that they've been hooked before and clear previous handlers
btnPrevious = $('#healthStreamPagePrevious');
btnNext = $('#healthStreamPageNext');
btnPrevious.hide().unbind("click.paging").bind("click.paging", goToPreviousPage);
btnNext.hide().unbind("click.paging").bind("click.paging", goToNextPage);
// Do it
doLoad();
// === Support functions
// Trigger a load request
function doLoad() {
request.pageIndex = pageIndex;
request.pageSize = pageSize;
inst._transport.transmitRequest(channelId, action, request, hstreamLoaded);
}
// Hndle the load response
function hstreamLoaded(objResponse) {
var healthStream = objResponse.items;
var total = objResponse.total;
var tbody = $('#healthStreamList');
// Need to make this update optional
$('#pageHeaderName').html(BETranslate(translationId, 'HeaderActivity') + ' (' + String(total) + ')');
$('#healthStreamSearchButton')
.button("enable")
.button("option", "label", BETranslate(translationId, 'HealthStreamSearch'));
tbody.empty();
btnPrevious.hide();
btnNext.hide();
if (healthStream.length > 0) {
pageCount = Math.ceil(total / pageSize);
if (pageCount > 1) {
if (pageIndex > 1) {
btnPrevious.show();
}
if (pageIndex < pageCount) {
btnNext.show();
}
}
var item;
var tr;
var tdMain;
var daysHash = {};
var creationDate;
var key;
var today = new Date();
var yesterday = new Date();
var msg;
yesterday.setDate(yesterday.getDate() - 1);
dataShownFlags = {};
for (var x = 0; x < healthStream.length; x++) {
item = healthStream[x];
msg = inst.hstreamEvalJson(item);
if (msg.length > 0) {
creationDate = new Date(item.CreationDate);
key = [creationDate.getYear(), creationDate.getMonth(), creationDate.getDate()].join('-');
if (!daysHash[key]) {
if (isDateEqual(creationDate, today)) {
addRowHeader(tbody, BETranslate(inst._channelId, 'HSToday'));
}
else if (isDateEqual(creationDate, yesterday)) {
addRowHeader(tbody, BETranslate(inst._channelId, 'HSYesterday'));
}
else {
addRowHeader(tbody, creationDate.toString('MM/dd/yyyy'));
}
daysHash[key] = true;
}
tr = $(
"<tr>" +
"<td class='date' style='white-space:nowrap;'>" + new Date(item.CreationDate).toString('h:mm tt') + "</td>" +
"<td class='main'><span class='name'>" + msg + "</span>" +
"</tr>"
);
tbody.append(tr);
if (showOptions) {
tr.find("td.main").prepend($("<em rel='opt'> </em>").click(makeShowOptionsHandler(item)));
}
}
}
// If any of the templates created links with a `data` attribute, hook them up
$('#healthStreamList a[data]').click(showTitle).each(function (index) {
this.id = 'data' + index;
});
}
else {
tbody.html('<tr><td colspan="2">' + BETranslate(inst._channelId, 'HSNoActivity') + '</td></tr>');
}
// Trigger completion callback
if (complete) {
complete.call(context, objResponse);
}
}
function makeShowOptionsHandler(item) {
// Our event comes to us from jQuery, but we pass on the raw
// event to the callback
return function (event) {
showOptions.call(context, event.originalEvent || event, item);
};
}
function addRowHeader(listRef, name) {
listRef.append(
"<tr>" +
"<td colspan='2' class='divider'>" + name + "</td>" +
"</tr>"
);
}
function showTitle(event) {
$.stopEvent(event);
var link = this;
var $link = $(this);
var href = $link.attr("href"); // We want the attribute, not the property (the property is usually expanded)
var hrefTitle = $link.attr('hreftitle') || BETranslate(inst._channelId, 'HSMoreInfo');
var data = $link.attr('data') || "";
var linkId = link.id;
if (!dataShownFlags[linkId]) {
dataShownFlags[linkId] = true;
if (data) {
var div = $(
"<div class='data'>" +
"<span data-linkId='" + linkId + "' class='close'>x</span>" +
"<table><thead></thead></table>" +
"</div>"
);
$link.parent().append(div);
var thead = div.find("thead");
var arr = data.split('~');
var splitEntry;
for (var x = 0; x < arr.length; x++) {
splitEntry = arr[x].split('|');
if (splitEntry[0] === 'Changed length') {
splitEntry[1] = splitEntry[1].replace(/\d+/g, BEFramework.prettyTime);
}
if (splitEntry.length > 1 && splitEntry[1].length > 0) {
thead.append(
"<tr>" +
"<td class='hslabel'>" + splitEntry[0] + ":</td>" +
"<td>" + splitEntry[1] + "</td>" +
"</tr>"
);
}
}
div.find("span:first").click(hideTitle);
if (href && href !== "#") {
$("<a target='_blank'>" + hrefTitle + "</a>").attr("href", href).appendTo(div);
}
}
}
}
function hideTitle(event) {
var $this = $(this),
linkId = $this.attr("data-linkId");
delete dataShownFlags[linkId];
$this.parent().remove();
return false;
}
function goToPreviousPage(event) {
--pageIndex;
doLoad();
return false;
}
function goToNextPage(event) {
++pageIndex;
doLoad();
return false;
}
}
var ___x = false;
var __i = 0;
BEFramework.prototype.hstreamEvalJson = hstreamEvalJson;
function hstreamEvalJson(item) {
var inst = this;
if (item.Action === 'saveinsurance' && !___x && __i != 0){
var start = +new Date();
__i = 1;
}
var userId = inst._BEUser ? inst._BEUser.getId() : -1;
var json = eval('(' + item.JSON + ')');
var key = 'HS' + item.Module + '_' + item.Action;
var msg = BETranslate(inst._channelId, key);
var fromIsMe = item.CreatedByContactId == userId;
var toIsMe = item.ContactId == userId;
var fromString = (fromIsMe) ? '<strong>' + BETranslate(inst._channelId, 'HSYou') + '</strong>' : '<a class="vcard" contactId="' + item.CreatedByContactId + '">' + item.CreatedByName + '</a>';
var toString = (toIsMe) ? '<strong>' + BETranslate(inst._channelId, 'HSYour') + '</strong>' : '<a class="vcard" contactId="' + item.ContactId + '">' + item.ContactName + '</a>';
var fromString2 = (fromIsMe) ? '<strong>' + BETranslate(inst._channelId, 'HSYour').toLowerCase() + '</strong>' : '<a class="vcard" contactId="' + item.CreatedByContactId + '">' + item.CreatedByName + '</a>';
var toString2 = (toIsMe) ? '<strong>' + BETranslate(inst._channelId, 'HSYou').toLowerCase() + '</strong>' : '<a class="vcard" contactId="' + item.ContactId + '">' + item.ContactName + '</a>';
var subFormat, subProps;
var configObject = (BEFramework.healthStreamConfig[item.Module] && BEFramework.healthStreamConfig[item.Module][item.Action]) || {};
var standardCase = configObject.standardCase;
var suppress = configObject.suppress || [];
var propertiesInOrder = configObject.displayOrder || [];
if (msg.indexOf('not found in module') != -1) {
try {
switch (item.Module) {
case 'contacts':
if (item.Action == 'setpermission' || item.Action == 'deleterelationship' || item.Action == 'addinvite') {
msg = BETranslate(inst._channelId, key + json.type.toString());
}
break;
case 'tasks':
if (item.Action == 'savetask') {
msg = BETranslate(inst._channelId, key + json.type.toString());
}
break;
default:
msg = '';
}
} catch (ex) {
msg = '';
}
}
for (var prop in json) {
if (typeof (json[prop]) == 'object') {
if (prop === 'changes' || prop === 'deleted'){
subProps = json[prop];
for (var propName in subProps) {
if (indexInArrayCI(propName, propertiesInOrder) === -1 && indexInArrayCI(propName, suppress) === -1){
propertiesInOrder.push(propName);
}
}
}
if (prop == 'changes') {
var changes = '';
var changeFrom = BETranslate(inst._channelId, 'HSChangedFrom');
var changeTo = BETranslate(inst._channelId, 'HSChangedTo');
for (var i = 0; i < propertiesInOrder.length; i++) {
var subprop = propertiesInOrder[i];
if (getObjectValCI(subProps, subprop) == null) continue;
var subSplit = stripHtml(getObjectValCI(subProps, subprop)).split('|');
if (subSplit.length === 1) {
subFormat = BETranslate(inst._channelId, 'HS' + item.Module + '_changes_' + subprop);
if (subFormat.indexOf('not found in module') < 0) {
changes += $.sandr(subFormat, '#{value}', subSplit[0]);
}
else {
changes += "*|" + subprop + " " + subSplit[0] + "~";
}
}
else {
var fromValue = stripHtml(subSplit[0]);
var toValue = stripHtml(subSplit[1]);
var packetInfo = processChangedValues(subprop, fromValue, toValue);
if (packetInfo.skip) continue;
changes = changes + changeFrom + packetInfo.display + '|' + packetInfo.fromValue + '<b>' + changeTo + '</b>' + packetInfo.toValue + '~';
}
}
msg = $.sandr(msg, '#{' + prop + '}', changes);
} else if (prop == 'deleted') {
var deleted = '';
for (var i = 0; i < propertiesInOrder.length; i++) {
var subprop = propertiesInOrder[i];
var currentValue = getObjectValCI(subProps, subprop);
if (currentValue == null || currentValue.toString().length === 0) continue;
deleted = deleted + getDisplayForLabel(subprop) + '|' + getDeleteValue(subprop, currentValue) + '~';
}
msg = $.sandr(msg, '#{' + prop + '}', deleted);
}
} else {
msg = $.sandr(msg, '#{' + prop + '}', $.sandr(json[prop], '"', ' '));
}
function processChangedValues(label, fromValue, toValue){
var typeFormat = (getObjectValCI(configObject, label) || {}).type;
var result = {};
if (typeFormat === 'date'){
var d1 = new Date(fromValue);
var d2 = new Date(toValue);
if (isDateEqual(d1, d2)) result.skip = true;
}
result.fromValue = getValueToDisplay(fromValue, typeFormat);
result.toValue = getValueToDisplay(toValue, typeFormat);
result.display = getDisplayForLabel(label)
return result;
}
function getDeleteValue(label, value){
var typeFormat = (getObjectValCI(configObject, label) || {}).type;
return getValueToDisplay(value, typeFormat);
}
function getValueToDisplay(rawValue, typeFormat){
if (typeFormat === 'date'){
var d = new Date(rawValue);
return isNaN(d.getTime()) ? rawValue : d.toString('MM/dd/yyyy');
} else if (typeof typeFormat === 'function') {
return typeFormat(rawValue)
} else {
return rawValue;
}
}
function getDisplayForLabel(label){
var fixCaseOfProperty = standardCase === '*' || indexInArrayCI(label, standardCase) > -1;
var rawConfigForLabel = getObjectValCI(configObject, label) || {};
return (rawConfigForLabel && rawConfigForLabel.display)
|| (fixCaseOfProperty ? fixCase(label) : null)
|| label;
}
}
msg = $.sandr(msg, '#{contactId}', item.ContactId);
msg = $.sandr(msg, '#{from}', fromString);
msg = $.sandr(msg, '#{to}', toString);
msg = $.sandr(msg, '#{from2}', fromString2);
msg = $.sandr(msg, '#{to2}', toString2);
msg = $.sandr(msg, '#{recordId}', item.RecordId);
msg = msg.replace(/#{[\S]*}/g, '');
if (item.Action === 'saveinsurance' && !___x && __i == 1){
var end = +new Date();
___x = true;
//alert(end - start);
}
if (item.Action === 'saveinsurance') __i++;
if (msg.indexOf('not found in module') == -1) {
return msg;
} else {
return '';
}
}
function stripHtml(html) {
var tmp = document.createElement('DIV');
tmp.innerHTML = html;
return tmp.textContent || tmp.innerText;
}
function isDateEqual(date1, date2) {
if (date1.getDate() === date2.getDate() &&
date1.getMonth() === date2.getMonth() &&
date1.getYear() === date2.getYear()) {
return true;
}
else {
return false;
}
}
function getObjectValCI(obj, key){
for (var k in obj){
if (k.toLowerCase() === key.toLowerCase()){
return obj[k];
}
}
}
function indexInArrayCI(item, arr){
if (!$.isArray(arr)) arr = [];
var target = item.toString().toLowerCase();
for (var i = 0; i < arr.length; i++){
if (target === arr[i].toLowerCase()) return i;
}
return -1;
}
function fixCase(str){
return str.replace(/[a-z][A-Z]/g, function(match) { return match.charAt(0) + ' ' + match.charAt(1).toLowerCase(); }).toLowerCase()
.replace(/\sid\s/g, ' ID ')
.replace(/\sid$/g, ' ID')
.replace(/^id$/g, 'ID');
}
})();
When you use closure compiler you're giving up some control over your code. It will do all sorts of tricks, and potentially remove unused code.
It appears as though your functions are not removed, but are renamed.
For example, your call to getDeleteValue...
getDeleteValue(subprop, currentValue)
is now...
l(g,r)
Because getDeleteValue was not exported, Closure renamed it.
Working with Closure Compiler takes a bit of finesse and quite a bit of documentation scouring until you're familiar with how it works.
Well, there are too many errors to think of. First of all, I don't understand if you want static reference or instantiated values. You are not using jsDoc tags or anything like that. The Compiler does it's best work only with the corresponding jsDoc tag. You're logic is very weird and ill formulated. Prototype alternations, etc, all happening in an IIFE(immediately invoked function expression). Are your functions static? Are they constructors? Are we human or are we dancer?
an IIFE executes before the DOMContentLoaded event is fired by the browser. The most you can do is a jQuery IIFE equivalent $(function() {})(); which binds that to the DOMReady or DOMContentLoaded callback. You are defining inline functions inside blocks, which is not even in the ECMA Language.
While most script engines support Function Declarations within blocks it is not part of ECMAScript (see ECMA-262, clause 13 and 14). Worse implementations are inconsistent with each other and with future EcmaScript proposals. ECMAScript only allows for Function Declarations in the root statement list of a script or function. Instead use a variable initialized with a Function Expression to define a function within a block.
var myFunctionName = function (params) {};
You are also missing loads of semi-colons. Automatic semi-colon insertion on interpretation of your JS is not exactly flawless, so make a habit out of it.
Relying on implicit insertion can cause subtle, hard to debug problems. Don't do it. You're better than that.
There are a couple places where missing semicolons are particularly dangerous:
// 1.
MyClass.prototype.myMethod = function() {
return 42;
} // No semicolon here.
(function() {
// Some initialization code wrapped in a function to create a scope for locals.
})();
var x = {
'i': 1,
'j': 2
} // No semicolon here.
// 2. Trying to do one thing on Internet Explorer and another on Firefox.
// I know you'd never write code like this, but throw me a bone.
[normalVersion, ffVersion][isFF]();
var THINGS_TO_EAT = [apples, oysters, sprayOnCheese] // No semicolon here.
// 3. conditional execution a la bash
-1 == resultOfOperation() || die();
So what happens?
JavaScript error - first the function returning 42 is called with the second function as a parameter, then the number 42 is "called" resulting in an error.
You will most likely get a 'no such property in undefined' error at runtime as it tries to call x[ffVersion][isIE]().
die is called unless resultOfOperation() is NaN and THINGS_TO_EAT gets assigned the result of die().
Why?
JavaScript requires statements to end with a semicolon, except when it thinks it can safely infer their existence. In each of these examples, a function declaration or object or array literal is used inside a statement. The closing brackets are not enough to signal the end of the statement. Javascript never ends a statement if the next token is an infix or bracket operator.
This has really surprised people, so make sure your assignments end with semicolons.

Split causes "Object doesn't support this property or method" exception

call to split on a variable causes a "Object doesn't support this property or method" exception and I don't know why.
Here's my code:
function getKontaktPersonen(kontaktSelectBox) {
var kontaktPersonen = [];
var id_and_name = kontaktSelectBox.attr('id');
var id_part = getID_PartFromName(id_and_name);
var textboxname;
var selectboxname;
if (kontaktSelectBox.attr('class') == 'kontaktSelectBox') {
textboxname = "TextBoxKunde" + id_part;
selectboxname = "SelectBoxKontaktPerson" + id_part;
} else if (kontaktSelectBox.attr('class') == 'NewkontaktSelectBox') {
textboxname = "NewTextBoxKunde" + id_part;
selectboxname = "NewSelectBoxKontaktPerson" + id_part;
} else {
return false;
}
var kundeBox = $('#' + textboxname);
var kundeBoxVal = kundeBox.val();
if (kundeBoxVal != '' && kundeBoxVal != null) {
var adr_id = kundeBoxVal.split(';')[1];
//here comes an ajax call
//[...]
}
}
If the selector didn't find any element the val function will return undefined Try this:
if (kundeBoxVal) {
var adr_id = kundeBoxVal.split(';')[1];
}

Categories

Resources