Disable script on condition - javascript

Good evening, could you help me, please? From this saved (static) page, page two, I’m running a script that extracts, in “Nome” field, the publisher from the bibliographic item (Feltrinelli, in the example). I would like the script to be NOT executed when on the previous page, page one, there is the expression “Editore moderno” (that is the publisher) under the heading “Persone, enti e famiglie”, that is in “fieldset.legami_tito”).
On Page one I tried to set a local variable if the expression “Editore moderno” is found:
var elencoLabel = document.querySelectorAll("span.grid-6");
if (elencoLabel.length > 8)
{
localStorage.removeItem("voce");
if (elencoLabel[8].innerText == "Editore moderno")
{
localStorage.setItem("voce","Editore moderno");
}
}
On Page two I tried this script (replacing the last line of the script):
function getNome(tmp) {
var idx = tmp.indexOf('. ((');
if(idx > -1){
tmp = tmp.substr(0, idx);
}
tmp = tmp.split('. - ');
switch(tmp.length){
case 3:
tmp = tmp[1];
break;
case 4:
tmp = tmp[2];
break;
default:
tmp = "";
break;
}
if(tmp !== ''){
tmp = tmp.substr(tmp.indexOf(' : ') + 2);
console.log(tmp);
if(tmp.indexOf('.') != -1 && tmp.split('.').length == 2){
tmp = tmp.substr(tmp.indexOf('. ') + 1, tmp.indexOf(', ') -3);
tmp = tmp.trim();
}
else {
tmp = tmp.split(",")[0];
tmp = tmp.trim();
}
}
return tmp;
}
function impostaNome(tmp) {
Array.from(document.querySelectorAll('article section.grid_container form div.grid-row label span')).filter( e => e.innerText.trim() === 'Nome')[0].parentNode.querySelector('input').value = tmp;
}
var elencoLabel = document.querySelectorAll("span.grid-6");
if (elencoLabel.length < 8 )
{
var miaVoce = localStorage.getItem("voce");
if (miaVoce == "Editore moderno")
{
impostaNome(getNome(document.querySelector('div.meta.tito div.evidence.isbd').innerText));
}
}
But with these changes, the publisher is never extracted.

Related

How to debug injected javascript code in IE compatibility mode?

I need to inject some Javascript scripts to IE and then invoke some methods.
I tried the following simple C# code (the Javascript code is to build xpath from a known HTML element).
string xpath = #"
(function(win) {
""use strict"";
var doc = win.document;
if (doc._xpath_installed) return;
doc._xpath_installed = true;
doc.createXPath = function (node, optimized) {
if (node.nodeType === Node.DOCUMENT_NODE) {
return '/';
}
var steps = [];
var contextNode = node;
while (contextNode) {
var step = _xPathValue(contextNode, optimized);
if (!step) {
break;
} // Error - bail out early.
steps.push(step);
if (step.optimized) {
break;
}
contextNode = contextNode.parentNode;
}
steps.reverse();
var stepvalues = [];
steps.forEach(function (step) {
stepvalues.push(_steptostring(step));
});
return (steps.length && steps[0].optimized ? '' : '/') + stepvalues.join('/');
};
var _xPathValue = function (node, optimized) {
var ownValue;
var ownIndex = _xPathIndex(node);
if (ownIndex === -1) {
return null;
} // Error.
switch (node.nodeType) {
case Node.ELEMENT_NODE:
if (optimized && node.getAttribute('id')) {
return _stepnew('//*[#id=""' + node.getAttribute('id') + '""]', true);
}
ownValue = node.localName;
break;
case Node.ATTRIBUTE_NODE:
ownValue = '#' + node.nodeName;
break;
case Node.TEXT_NODE:
case Node.CDATA_SECTION_NODE:
ownValue = 'text()';
break;
case Node.PROCESSING_INSTRUCTION_NODE:
ownValue = 'processing-instruction()';
break;
case Node.COMMENT_NODE:
ownValue = 'comment()';
break;
case Node.DOCUMENT_NODE:
ownValue = '';
break;
default:
ownValue = '';
break;
}
if (ownIndex > 0) {
ownValue += '[' + ownIndex + ']';
}
return _stepnew(ownValue, node.nodeType === Node.DOCUMENT_NODE);
};
var _xPathIndex = function (node) {
// Returns -1 in case of error, 0 if no siblings matching the same expression,
// <XPath index among the same expression-matching sibling nodes> otherwise.
function areNodesSimilar(left, right) {
if (left === right) {
return true;
}
if (left.nodeType === Node.ELEMENT_NODE && right.nodeType === Node.ELEMENT_NODE) {
return left.localName === right.localName;
}
if (left.nodeType === right.nodeType) {
return true;
}
// XPath treats CDATA as text nodes.
var leftType = left.nodeType === Node.CDATA_SECTION_NODE ? Node.TEXT_NODE : left.nodeType;
var rightType = right.nodeType === Node.CDATA_SECTION_NODE ? Node.TEXT_NODE : right.nodeType;
return leftType === rightType;
}
var siblings = node.parentNode ? node.parentNode.children : null;
if (!siblings) {
return 0;
} // Root node - no siblings.
var hasSameNamedElements;
for (var i = 0; i < siblings.length; ++i) {
if (areNodesSimilar(node, siblings[i]) && siblings[i] !== node) {
hasSameNamedElements = true;
break;
}
}
if (!hasSameNamedElements) {
return 0;
}
var ownIndex = 1; // XPath indices start with 1.
for (var i = 0; i < siblings.length; ++i) {
if (areNodesSimilar(node, siblings[i])) {
if (siblings[i] === node) {
return ownIndex;
}
++ownIndex;
}
}
return -1;
};
var _stepnew = function(value, optimized) {
return {
value: value,
optimized: optimized || false
}
};
var _steptostring = function(step) {
return step[""value""];
};
})(window);
";
And then I try to inject the xpath string and invoke a method,
// doc is the html document (type: mshtml.IHTMLDocument2)
// element is the html element (type: mshtml.IHTMLElement)
doc.parentWindow.execScript(xpath, "JScript");
object[] args = new object[2];
args[0] = element;
args[1] = 1;
object result = doc.GetType().InvokeMember("createXPath", BindingFlags.Instance | BindingFlags.InvokeMethod, null, doc, args);
It works fine in Internet Explorer 11 (Windows 10).
However, when I add the website to Compatibility View (settings-> compatibility view settings), the InvokeMember call throws exception "Exception from HRESULT: 0x80020101", which means there are some script errors in the Javascript code.
My question is, is there a way to debug into this Javascript code in the above situation? Without debugging it is almost impossible to find the root cause only based on the information 0x80020101.
If you have script errors in your JS code, why don't you trace the client-side code's execution with console.log() or console.debug() so that you know what is happening in your application ?
If you just want to debug code in Edge IE mode, you can use IEChooser to open Internet Explorer DevTools, as follows:
In Windows, open the Run dialog box. For example, press the Windows logo key + R.
Enter %systemroot%\system32\f12\IEChooser.exe, and then click OK.
In IEChooser, select the entry for the IE mode tab.
For more details, you could also refer to this doc: Open DevTools on a tab in IE mode.

Pulling data from API as XML and converting to JSON

This is my first javascript project where I am trying to pull data from different APIs in order to make a small dashboard. I'm pulling data from an API in XML format and would like to convert it directly to JSON and store it in a variable.
const testdataurl = 'https://api.data.abs.gov.au/data/ABS,RES_DWELL/3.3GBRI.Q';
async function getData() {
const response = await fetch(testdataurl);
data = await response.text();
console.log(data);
}
getData()
You can see the API endpoint in the code above if you want to have a look at the XML. What's the easiest way for me to convert that as soon as it's pulled to JSON and store in a variable?
I have looked into using this:
https://goessner.net/download/prj/jsonxml/
but I'm not sure how to best implement it.
Thanks
There are other resources that better explain how to import script into your html to make it available in your project. The script referenced was small enough for me to just copy / paste the snippet into a single post. For some reason the json string the code snippet was supposed to provide had an undefined segment returned. I am not sure why this is the case, but after examining the xml and the string returned, it does not look like it was important so I just remove it from the json string before parsing into an object.
keep in mind this code will not work on stackoverflow because of CORS. I tested it on the api.data.abs.gov.au website to avoid CORS issues.
Hopefully this helps.
function parseXml(xml) {
var dom = null;
if (window.DOMParser) {
try {
dom = (new DOMParser()).parseFromString(xml, "text/xml");
}
catch (e) { dom = null; }
}
else if (window.ActiveXObject) {
try {
dom = new ActiveXObject('Microsoft.XMLDOM');
dom.async = false;
if (!dom.loadXML(xml)) // parse error ..
window.alert(dom.parseError.reason + dom.parseError.srcText);
}
catch (e) { dom = null; }
}
else
alert("cannot parse xml string!");
return dom;
}
/* This work is licensed under Creative Commons GNU LGPL License.
License: http://creativecommons.org/licenses/LGPL/2.1/
Version: 0.9
Author: Stefan Goessner/2006
Web: http://goessner.net/
*/
function xml2json(xml, tab) {
var X = {
toObj: function(xml) {
var o = {};
if (xml.nodeType==1) { // element node ..
if (xml.attributes.length) // element with attributes ..
for (var i=0; i<xml.attributes.length; i++)
o["#"+xml.attributes[i].nodeName] = (xml.attributes[i].nodeValue||"").toString();
if (xml.firstChild) { // element has child nodes ..
var textChild=0, cdataChild=0, hasElementChild=false;
for (var n=xml.firstChild; n; n=n.nextSibling) {
if (n.nodeType==1) hasElementChild = true;
else if (n.nodeType==3 && n.nodeValue.match(/[^ \f\n\r\t\v]/)) textChild++; // non-whitespace text
else if (n.nodeType==4) cdataChild++; // cdata section node
}
if (hasElementChild) {
if (textChild < 2 && cdataChild < 2) { // structured element with evtl. a single text or/and cdata node ..
X.removeWhite(xml);
for (var n=xml.firstChild; n; n=n.nextSibling) {
if (n.nodeType == 3) // text node
o["#text"] = X.escape(n.nodeValue);
else if (n.nodeType == 4) // cdata node
o["#cdata"] = X.escape(n.nodeValue);
else if (o[n.nodeName]) { // multiple occurence of element ..
if (o[n.nodeName] instanceof Array)
o[n.nodeName][o[n.nodeName].length] = X.toObj(n);
else
o[n.nodeName] = [o[n.nodeName], X.toObj(n)];
}
else // first occurence of element..
o[n.nodeName] = X.toObj(n);
}
}
else { // mixed content
if (!xml.attributes.length)
o = X.escape(X.innerXml(xml));
else
o["#text"] = X.escape(X.innerXml(xml));
}
}
else if (textChild) { // pure text
if (!xml.attributes.length)
o = X.escape(X.innerXml(xml));
else
o["#text"] = X.escape(X.innerXml(xml));
}
else if (cdataChild) { // cdata
if (cdataChild > 1)
o = X.escape(X.innerXml(xml));
else
for (var n=xml.firstChild; n; n=n.nextSibling)
o["#cdata"] = X.escape(n.nodeValue);
}
}
if (!xml.attributes.length && !xml.firstChild) o = null;
}
else if (xml.nodeType==9) { // document.node
o = X.toObj(xml.documentElement);
}
else
alert("unhandled node type: " + xml.nodeType);
return o;
},
toJson: function(o, name, ind) {
var json = name ? ("\""+name+"\"") : "";
if (o instanceof Array) {
for (var i=0,n=o.length; i<n; i++)
o[i] = X.toJson(o[i], "", ind+"\t");
json += (name?":[":"[") + (o.length > 1 ? ("\n"+ind+"\t"+o.join(",\n"+ind+"\t")+"\n"+ind) : o.join("")) + "]";
}
else if (o == null)
json += (name&&":") + "null";
else if (typeof(o) == "object") {
var arr = [];
for (var m in o)
arr[arr.length] = X.toJson(o[m], m, ind+"\t");
json += (name?":{":"{") + (arr.length > 1 ? ("\n"+ind+"\t"+arr.join(",\n"+ind+"\t")+"\n"+ind) : arr.join("")) + "}";
}
else if (typeof(o) == "string")
json += (name&&":") + "\"" + o.toString() + "\"";
else
json += (name&&":") + o.toString();
return json;
},
innerXml: function(node) {
var s = ""
if ("innerHTML" in node)
s = node.innerHTML;
else {
var asXml = function(n) {
var s = "";
if (n.nodeType == 1) {
s += "<" + n.nodeName;
for (var i=0; i<n.attributes.length;i++)
s += " " + n.attributes[i].nodeName + "=\"" + (n.attributes[i].nodeValue||"").toString() + "\"";
if (n.firstChild) {
s += ">";
for (var c=n.firstChild; c; c=c.nextSibling)
s += asXml(c);
s += "</"+n.nodeName+">";
}
else
s += "/>";
}
else if (n.nodeType == 3)
s += n.nodeValue;
else if (n.nodeType == 4)
s += "<![CDATA[" + n.nodeValue + "]]>";
return s;
};
for (var c=node.firstChild; c; c=c.nextSibling)
s += asXml(c);
}
return s;
},
escape: function(txt) {
return txt.replace(/[\\]/g, "\\\\")
.replace(/[\"]/g, '\\"')
.replace(/[\n]/g, '\\n')
.replace(/[\r]/g, '\\r');
},
removeWhite: function(e) {
e.normalize();
for (var n = e.firstChild; n; ) {
if (n.nodeType == 3) { // text node
if (!n.nodeValue.match(/[^ \f\n\r\t\v]/)) { // pure whitespace text node
var nxt = n.nextSibling;
e.removeChild(n);
n = nxt;
}
else
n = n.nextSibling;
}
else if (n.nodeType == 1) { // element node
X.removeWhite(n);
n = n.nextSibling;
}
else // any other node
n = n.nextSibling;
}
return e;
}
};
if (xml.nodeType == 9) // document node
xml = xml.documentElement;
var json = X.toJson(X.toObj(X.removeWhite(xml)), xml.nodeName, "\t");
return "{\n" + tab + (tab ? json.replace(/\t/g, tab) : json.replace(/\t|\n/g, "")) + "\n}";
}
const testdataurl = 'https://api.data.abs.gov.au/data/ABS,RES_DWELL/3.3GBRI.Q';
async function getData() {
const response = await fetch(testdataurl);
data = await response.text();
const dom = parseXml(data);
const myJsonStr = xml2json(dom);
const fixedJsonStr = myJsonStr.replace(/\nundefined/, "");
const jsonObj = JSON.parse(fixedJsonStr);
console.log(jsonObj);
}
getData();

isset equivalent in javascript to find palindrome

I created a script in PHP to find a palindrome, but when I try to do the same in JavaScript, then it is not working as expected. It's not just a matter of checking if the string that is reversed matches, but any order of the string has to be checked as well.
In other words, "mom" should return as true, "mmo" should return as true, "omm" should return as true, etc..., which is what the PHP script does, but the JS script below doesn't even work for the first iteration for the string "mom"
The following is the PHP script:
<?php
function is_palindrom($str) {
$str_array = str_split($str);
$count = array();
foreach ($str_array as $key) {
if(isset($count[$key])) {
$count[$key]++;
} else {
$count[$key] = 1;
}
}
$odd_counter = 0;
foreach ($count as $key => $val) {
if(($val % 2) == 1) {
$odd_counter++;
}
}
return $odd_counter <= 1;
}
echo is_palindrom('mom') ? "true" : "false";
The following is what I have tried in JS:
var count = [];
var strArr = [];
var oddCounter = 0;
var foreach_1 = function(item, index) {
console.log("count[index]: " + count[index]);
if (typeof count[index] !== "undefined") {
count[index]++;
} else {
count[index] = 1;
}
};
var foreach_2 = function(item, index) {
console.log("item: " + item + " item % 2: " + eval(item % 2));
if (eval(item % 2) == 1) {
oddCounter++;
}
console.log("oddCounter: " + oddCounter);
return oddCounter <= 1;
};
var isPalindrom = function(str) {
strArr = str.split("");
console.log(strArr);
strArr.forEach(foreach_1);
console.log(count);
count.forEach(foreach_2);
};
I believe it is failing where I try to replicate isset in javascript, with the following code:
if (typeof count[index] !== "undefined") {
As a result, I have tried to write my own isset function, but still the same result, it is not working:
var isset = function(obj) {
if (typeof obj === "undefined" || obj === null) {
return false;
} else {
return true;
}
};
With the following function being called:
if (isset(count[index])) {
count[index]++;
} else {
count[index] = 1;
}
As usual, any help would be appreciated and thanks in advance
BTW, it's killing me that I cannot remember the word for several revisions or iterations of something - I know that it starts with "re"
My attempt:
let p1 = `No 'x' in Nixon.`
let p2 = `Was it a car or a cat I saw?`
let p3 = `A man, a plan, a canal, Panama!`
function is_palindrome (str) {
const normalize = str => str.replace(/[.,:;`'"!?\/#$%\^&\*{}=\-_~()\s]/g, '').toLowerCase()
const reverse = str => [...str].reverse().join('')
return normalize(str) === reverse(normalize(str))
? true
: false
}
console.log(is_palindrome(p1))
console.log(is_palindrome(p2))
console.log(is_palindrome(p3))
First, thank you for all the comments.
Second, I ran a var_dump on the count array in the PHP file and this was the result:
array (size=2)
'm' => int 2
'o' => int 1
Which lead me to understand that count in js has to be an object for this work and I would have to create indexes of the object, depending on the string entered.
One thing lead to another and a complete re-write, but it works, along with a spell checker - see link at the bottom for complete code:
var count = {};
var strArr = [];
var oddCounter = 0;
var objKeys = [];
var splitString;
var reverseArray;
var joinArray;
var url = "test-spelling.php";
var someRes = "";
var mForN = function(obj, strArr) {
for (var y = 0; y < strArr.length; y++) {
// console.log("obj[strArr[" + y + "]]: " + obj[strArr[y]]);
if (isset(obj[strArr[y]])) {
obj[strArr[y]]++;
} else {
obj[strArr[y]] = 1;
}
}
return obj;
};
var mForN_2 = function(obj, objKeys) {
for (var z = 0; z < objKeys.length; z++) {
/* console.log(
"obj[objKeys[z]]: " +
obj[objKeys[z]] +
" obj[objKeys[z]] % 2: " +
eval(obj[objKeys[z]] % 2)
); */
if (eval(obj[objKeys[z]] % 2) == 1) {
oddCounter++;
}
// console.log("oddCounter: " + oddCounter);
}
return oddCounter <= 1;
};
var isset = function(obj) {
if (typeof obj === "undefined" || obj === null) {
return false;
} else {
return true;
}
};
var isPalindrom = function(str) {
// reverse original string
splitString = str.split("");
reverseArray = splitString.reverse();
joinArray = reverseArray.join("");
var checking = checkSpellingOfStr(str);
if (str == joinArray) {
strArr = str.split("");
// console.log("strArr: " + strArr);
objKeys = makeObjKeys(count, strArr);
// console.log("filled count before mForN: " + JSON.stringify(count));
// create array of keys in the count object
objKeys = Object.keys(count);
// console.log("objKeys: " + objKeys);
count = mForN(count, strArr);
// console.log("count after mForN: " + JSON.stringify(count));
return mForN_2(count, objKeys);
} else {
return 0;
}
};
var makeObjKeys = function(obj, arr) {
for (var x = 0; x < arr.length; x++) {
obj[arr[x]] = null;
}
return obj;
};
var checkSpellingOfStr = function(someStr) {
var formData = {
someWord: someStr
};
$.ajax({
type: "GET",
url: url,
data: formData,
success: function(result) {
if (!$.trim(result)) {
} else {
console.log(result);
$("#checkSpelling").html(result);
}
}
});
};
Start everything with the following call:
isPalindrom("mom") ? demoP.innerHTML = "is pal" : demoP.innerHTML = "is not pal";
In my example, I have a form and I listen for a button click as follows:
var palindromeTxt = document.getElementById("palindromeTxt").value;
var btn = document.getElementById("button");
btn.addEventListener("click", function (event) {
isPalindrom(palindromeTxt) ? demoP.innerHTML = "is pal" : demoP.innerHTML = "is not pal";
});
The following is the php for spell check:
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
if(!empty($_REQUEST['someWord']))
{
$someWord = $_REQUEST['someWord'];
}
$pspell_link = pspell_new("en");
if (pspell_check($pspell_link, $someWord)) {
echo trim($someWord) . " is a recognized word in the English language";
} else {
echo "Your word is either misspelled or that is not a recognized word";
}
You will need pspell installed on your server, as well as adding extension=pspell.so to your php.ini
This is what I did, to get it running locally on my mac:
cd /Users/username/Downloads/php-5.6.2/ext/pspell
/usr/local/bin/phpize
./configure --with-php-config=/usr/local/php5-5.6.2-20141102-094039/bin/php-config --with-pspell=/opt/local/
make
cp ./modules/* /usr/local/php5-5.6.2-20141102-094039/lib/php/extensions/no-debug-non-zts-20131226
sudo apachectl restart
check your phpinfo file and you should see the following:
pspell
PSpell Support enabled
Live example

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

Convert Indented Text List to HTML List (jQuery)

I am attempting to create a jQuery script which will convert an indented text list of arbitrary length and depth into a properly formatted HTML list. The lists on which I will be running this script are simple tree structures for directories. Within the tree structures, folders are denoted by a semicolon following the folder name (and files have no ending punctuation). Given this, I would like to attach a <span class="folder"></span> or <span class="file"></span> to the lines as appropriate.
I've found it to be fairly easy to generate most of the structure, but I cannot seem to get the recursion (which I suspect will be necessary) down to ensure that the tags are properly nested. The page on which this will be implemented will include the most recent (i.e., 3.0.3) version of Bootstrap, so feel free to use any of its functionality. I have about two dozen (generally abortive) fragments of code which I've tried or which I'm currently attempting to tweak to produce the desired result. Instead of posting a mass of (likely unhelpful) code, I've created a JSFiddle with the basic form which will be used for input/output, a bit of jQuery, and an example list and some external libraries loaded.
Any help or suggestions will be greatly appreciated.
Try this. I copied it to your fiddle and it seems to work.
var indentedToHtmlList = function indentedToHtmlList (text, indentChar, folderChar, listType, showIcons) {
indentChar = indentChar || '\t';
folderChar = folderChar || ':';
listType = listType || 'ul';
showIcons = !!showIcons;
var lastDepth,
lines = text.split(/\r?\n/),
output = '<' + listType + '>\n',
depthCounter = new RegExp('^(' + indentChar + '*)(.*)');
for (var i = 0; i < lines.length; i++) {
var splitted = lines[i].match(depthCounter),
indentStr = splitted[1],
fileName = splitted[2],
currentDepth = (indentStr === undefined) ? 0 : (indentStr.length / indentChar.length),
isFolder = (fileName.charAt(fileName.length - 1) === folderChar);
if (isFolder) {
fileName = fileName.substring(0, fileName.length -1);
}
if (lastDepth === currentDepth) {
output += '</li>\n';
} else if (lastDepth > currentDepth) {
while (lastDepth > currentDepth) {
output += '</li>\n</' + listType + '>\n</li>\n';
lastDepth--;
}
} else if (lastDepth < currentDepth) {
output += '\n<' + listType + '>\n';
}
output += '<li>';
if (showIcons) {
output += '<span class=" glyphicon glyphicon-' +
(isFolder ? 'folder-open' : 'file') +
'"></span> ';
}
output += fileName;
lastDepth = currentDepth;
}
while (lastDepth >= 0) {
output += '\n</li>\n</' + listType + '>';
lastDepth--;
}
return output;
};
You could use spans and classes to denote files and folders, but you should consider using ul and li elements, they were built for that.
The whole list should be enclosed within an ul element. Each entry on the top level list should create an li element inside of the main element. If the element is a folder, then it should also append another ul. This is where you'll need recursion to allow proper nesting.
However, if you intend to use indentation (no pun indented) the tab and or whitespace parsing is a problem by itself which I'm not solving in this answer. For the sake of this example, I'll just pretend you have a magic function that turns text into a parsed list called MyList, and that files that belong to a folder are whatever lies after the first semicolon of each list element.
var turnTextIntoList=function(AText) {
//magic stuff;
return SomeList;
};
var populateList=function(AList) {
var mainelement=jQuery('<ul></ul>');
for(element in AList) {
var the_li=jQuery('<li></li>');
if(element.indexOf(';')!=-1) {
the_li.append('<span class="file">'+element+'</span>');
} else {
var thefolder=element.split(';')
the_li.append('<span class="folder">'+thefolder[0]+'</span>');
the_li.append(populateList(turnTextIntoList(thefolder[1])));
}
mainelement.append(the_li);
}
return mainelement;
};
var MyList=turnTextIntoList(MyText);
jQuery('#targetdiv').append(populateList(MyList));
See, the recursion part is where you do
the_li.append(populateList(turnTextIntoList(thefolder[1])));
which will keep drilling into nesting levels until it reaches a file so it can start its way back.
It appears that someone already created a script which does this. Unfortunately, that script is in CoffeeScript, not JavaScript. However, there are a number online converters which will convert from CoffeeScript to JavaScript. Thanks to #charlietfl who provided a link to a working converter, supra.
Here is the converted, working code:
var bind, blank, convert, index, li, lineToMap, linesToMaps, parse, parseTuples, ptAccum, runConvert, tabCount, ul, ulEnd;
convert = function(text) {
return parse(text.split('\n'));
};
li = function(t) {
var html;
html = "<li>" + t['line'] + "</li>";
ptAccum.push(html);
return html;
};
ul = function(t) {
return ptAccum.push("<ul>" + (li(t)));
};
ulEnd = function() {
return ptAccum.push("</ul>");
};
ptAccum = [];
index = 0;
parse = function(lines) {
var ts;
ts = linesToMaps(lines);
ptAccum = ["<ul>"];
index = 0;
parseTuples(ts, 0);
ulEnd();
return ptAccum.join("\n");
};
parseTuples = function(tuples, level) {
var stop, _p, _results;
stop = false;
_p = function() {
var curLevel, t;
t = tuples[index];
curLevel = t['level'];
index++;
if (curLevel === level) {
return li(t);
} else if (curLevel < level) {
index--;
return stop = true;
} else {
ul(t);
parseTuples(tuples, level + 1);
return ulEnd();
}
};
_results = [];
while (!stop && index < tuples.length) {
_results.push(_p());
}
return _results;
};
tabCount = function(line) {
var c, count, i, inc, isTab, tc;
tc = 0;
c = '\t';
count = 0;
if (line) {
count = line.length;
}
i = 0;
isTab = function() {
return c === '\t';
};
inc = function() {
c = line.charAt(i);
if (isTab()) {
tc++;
}
return i++;
};
while (isTab() && i < count) {
inc();
}
return tc;
};
lineToMap = function(line) {
return {
line: line,
level: tabCount(line)
};
};
blank = function(line) {
return !line || line.length === 0 || line.match(/^ *$/);
};
linesToMaps = function(lines) {
var line, _i, _len, _results;
_results = [];
for (_i = 0, _len = lines.length; _i < _len; _i++) {
line = lines[_i];
if (!(blank(line))) {
_results.push(lineToMap(line));
}
}
return _results;
};
runConvert = function() {
var result;
result = convert($('#textarea-plain-text').val());
$('#textarea-converted-text').val(result);
return $('#div-converted-text').html(result);
};
bind = function() {
return $('#list-conversion-button').click(runConvert);
};
$(bind);
JSFiddle

Categories

Resources