Knockout.js: How to sort table? - javascript

I use this example:
http://stevescodingblog.co.uk/real-time-system-resource-monitor-with-signalr-mvc-knockout-and-webapi/#comment-3293
Table displays observable array. This array is updating by clients via signalr. All works fine.
I want to add sorting by selected column.
<div id="computerInfo">
<h2>Real-time System Resource Monitor</h2>
<h5 data-bind="visible: connected">Connected to message hub</h5>
<table border="0" class="table table-striped">
<tr>
<th>Machine</th>
<th>CPU %</th>
<th>Memory Available (Mb)</th>
<th>Total Memory (Mb)</th>
<th>Mem Available %</th>
</tr>
<!-- ko foreach: machines -->
<tr data-bind="css: { highCpu: cpu() > 90 || memPercent()<30 }">
<td data-bind="text: machineName"></td>
<td data-bind="text: cpu"></td>
<td data-bind="text: memUsage"></td>
<td data-bind="text: memTotal"></td>
<td data-bind="text: memPercent"></td>
</tr>
<!-- /ko -->
</table>
$(function () {
// The view model that is bound to our view
var ViewModel = function () {
var self = this;
// Whether we're connected or not
self.connected = ko.observable(false);
// Collection of machines that are connected
self.machines = ko.observableArray();
self.headers = [
{ title: 'Machine Name', sortKey: 'keyMachineName' },
{ title: 'CPU', sortKey: 'keyCpu' },
{ title: 'Mem Usage', sortKey: 'keyMemUsage' },
{ title: 'Mem Total', sortKey: 'keyMemTotal' },
{ title: 'Mem Percent', sortKey: 'keyMemPercent' }
];
self.sort = function (header, event) {
var sortKey = header.sortKey;
//implementation of sort
};
self.sort = function (header, event) {
var sortKey = header.sortKey;
switch (sortKey) {
case 'keyMachineName':
self.machines.sort(function (a, b) {
var n = a.machineName < b.machineName ? -1 : a.machineName > b.machineName ? 1 : 0;
alert(n);
return n;//1;
});
break;
case 'keyCpu':
self.machines.sort(function (a, b) {
var n = a.cpu < b.cpu ? -1 : a.cpu > b.cpu ? 1 : 0;
alert(n);
return n;
});
break;
case 'keyMemUsage':
self.machines.sort(function (a, b) {
var n = a.memUsage < b.memUsage ? -1 : a.memUsage > b.memUsage ? 1 : 0;
alert(n);
return n;
});
break;
}
};
};
// Instantiate the viewmodel..
var vm = new ViewModel();
// .. and bind it to the view
//ko.applyBindings(vm, $("#computerInfo")[0]);
// Get a reference to our hub
var hub = $.connection.cpuInfo;
// Add a handler to receive updates from the server
hub.client.cpuInfoMessage = function (machineName, cpu, memUsage, memTotal) {
var machine = {
machineName: machineName,
cpu: cpu.toFixed(0),
memUsage: (memUsage / 1024).toFixed(2),
memTotal: (memTotal / 1024).toFixed(2),
memPercent: ((memUsage / memTotal) * 100).toFixed(1) + "%"
};
var machineModel = ko.mapping.fromJS(machine);
// Check if we already have it:
var match = ko.utils.arrayFirst(vm.machines(), function (item) {
return item.machineName() == machineName;
});
if (!match) {
vm.machines.push(machineModel);
} else {
var index = vm.machines.indexOf(match);
vm.machines.replace(vm.machines()[index], machineModel);
}
// vm.machines.sort();
//ko.applyBindings(vm, $("#computerInfo")[0]);
//$("#infoTable").tablesorter({sortList: [0,0]});
};
///
ko.applyBindings(vm, $("#computerInfo")[0]);
///
// Start the connectio
$.connection.hub.start().done(function () {
vm.connected(true);
});
});
The problem is:
sort function always returns 0, i.e. there values always the same:
var n = a.machineName < b.machineName
When I for the test return 1 then sorting works fine.

When you create a machine using
var machineModel = ko.mapping.fromJS(machine);
you create a view model with observable properties. In this case in your sort method you need to treat them as observables, so you must use them using parenthesis. For example:
case 'keyMachineName':
self.machines.sort(function (a, b) {
var n = a.machineName() < b.machineName() ? -1 : a.machineName() > b.machineName() ? 1 : 0;
alert(n);
return n;//1;
});
break;

The jQuery tablesorter plugin does not work with knockout. Simply forget it.
Knockout cannot handle when DOM nodes are moved around without its knowledge, but that's what jQuery Tablesorter does.
But knockout is perfectly prepared to sort your table for you.
Below example features
fully configurable columns, i.e. not hard-coded in the view
sortable table columns on click, including visual feedback which is the sorted column
alternating ascending/descending sort order
value formatting via a Knockout extender
automatic update of values in an interval (likely scenario in this use case)
automatic re-sort when the values update
ko.extenders.formatted = function (underlyingObservable, formatFunction) {
underlyingObservable.formatted = ko.computed(function () {
return formatFunction(underlyingObservable());
});
};
function Machine(data) {
var self = this,
memoryFormat = function (val) { return Math.round(val / 1024, 0).toLocaleString(); };
self.machineName = ko.observable().extend({formatted: function (val) { return val; }});
self.cpu = ko.observable(0).extend({formatted: function (val) { return val.toFixed(1); }});
self.memUsage = ko.observable(0).extend({formatted: memoryFormat});
self.memTotal = ko.observable(0).extend({formatted: memoryFormat});
self.memPercent = ko.computed(function () {
return self.memUsage() / self.memTotal() * 100;
}).extend({formatted: function (val) { return val.toFixed(1) + '%'; }});
self.conditions = ko.computed(function () {
return {
highCpu: self.cpu() > 50,
lowMem: self.memPercent() < 30
};
});
self.update(data);
};
Machine.prototype.update = function (data) {
ko.mapping.fromJS(data, {}, this);
};
function MachineList() {
var self = this;
self.connected = ko.observable(false);
self.machines = ko.observableArray();
self.headers = [
{title: 'Machine Name', key: 'machineName', cssClass: ''},
{title: 'CPU %', key: 'cpu', cssClass: 'right'},
{title: 'Mem Usage (MB)', key: 'memUsage', cssClass: 'right'},
{title: 'Mem Total (MB)', key: 'memTotal', cssClass: 'right'},
{title: 'Mem Available %', key: 'memPercent', cssClass: 'right'}
];
self.sortHeader = ko.observable(self.headers[0]);
self.sortDirection = ko.observable(1);
self.toggleSort = function (header) {
if (header === self.sortHeader()) {
self.sortDirection(self.sortDirection() * -1);
} else {
self.sortHeader(header);
self.sortDirection(1);
}
};
// use a computed to subscribe to both self.sortHeader() and self.sortDirection()
self.sortMachines = ko.computed(function () {
var sortHeader = self.sortHeader(),
dir = self.sortDirection(),
tempMachines = self.machines(),
prop = sortHeader ? sortHeader.key : "";
if (!prop) return;
tempMachines.sort(function (a, b) {
var va = ko.unwrap(a[prop]),
vb = ko.unwrap(b[prop]);
return va < vb ? -dir : va > vb ? dir : 0;
});
self.machines.notifySubscribers();
});
self.insertMachine = function (data) {
var machine = ko.utils.arrayFirst(vm.machines(), function (item) {
return ko.unwrap(item.machineName) == ko.unwrap(data.machineName);
});
if (machine) machine.update(data);
else vm.machines.push(new Machine(data));
self.sortMachines();
};
};
// -------------------------------------------------------------------------------
// connection mockup
$.connection = {
cpuInfo: {client: {}},
hub: {start: function() { return $.Deferred().resolve(); }}
};
var vm = new MachineList(),
hub = $.connection.cpuInfo;
hub.client.cpuInfoMessage = function (data) { vm.insertMachine(data); };
// start the connection as soon as possible...
$.connection.hub.start().done(function () {
vm.connected(true);
$(function () {
// ...but don't apply bindings before the document is ready
ko.applyBindings(vm, $("#computerInfo")[0]);
});
// create some random sample data
setInterval(function () {
var GB = 1024 * 1024;
['A', 'B', 'C', 'D'].forEach(function (name) {
hub.client.cpuInfoMessage({
machineName: name,
cpu: getRandomInt(0, 1000) / 10,
memUsage: getRandomInt(5 * GB, 7 * GB),
memTotal: 10 * GB
});
});
}, 1000);
});
// -------------------------------------------------------------------------------
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
.table { border-collapse: collapse; }
.table th, .table td { border: 1px solid silver; padding: 0 2px; }
.sortable { cursor: pointer; }
.table-striped tbody tr:nth-child(odd) { background-color: #f7f7f7; }
.highCpu { color: red; }
.lowMem { color: red; }
.right { text-align: right; }
.sorted { background-color: #B5E0FF; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.min.js"></script>
<div id="computerInfo">
<h2>Real-time System Resource Monitor</h2>
<h5 data-bind="visible: connected">Connected to message hub</h5>
<table border="0" class="table table-striped">
<thead>
<tr data-bind="foreach: headers">
<th class="sortable" data-bind="click: $root.toggleSort, text: title, css: {sorted: $data === $root.sortHeader()}"></th>
</tr>
</thead>
<tbody data-bind="foreach: machines">
<tr data-bind="foreach: $root.headers, css: conditions">
<td data-bind="text: $parent[key].formatted, css: cssClass"></td>
</tr>
</tbody>
</table>
</div>

Related

I need to merge cells in a table. Exceljs library

There was a problem.
I create worksheet in library EXCELJS
function save_export(){
var workbook = new ExcelJS.Workbook();
var worksheet = workbook.addWorksheet('sheet', {
pageSetup:{paperSize: 9, orientation:'portrait',fitToPage: true,fitToHeight : 99 , fitToWidth : 1, horizontalCentered: true}
});
var tfoot = [];
$(element).each(function(index_tfoot, element_tfoot) {
$($(element_tfoot).find("#tfoot tr")).each(function(index_tr, element_tr) {
$(this.cells).each(function(index_td, element_td) {
tfoot.push($(element_td).text());
});
var row_foot = worksheet.addRow(tfoot);
row_foot.eachCell(function(cell,rowNumber) {
cell.font = { name: 'Verdana', size: 11};
cell.alignment = {horizontal: 'center' };
cell.border = {
top: { style: "thin" },
left: { style: "thin" },
bottom: { style: "thin" },
right: { style: "thin" }
};
<!-- if(cell._value.model.value === 'ИТОГО'){ -->
<!-- var c = 'A'; -->
<!-- var r = cell._row._number; -->
<!-- var sum = c+r; -->
<!-- worksheet.mergeCells(sum + ': B31'); -->
<!-- } // ПОЛУЧИЛ ИТОГО -->
});
tfoot = [];
});
});
});
workbook.xlsx.writeBuffer().then(function (data) {
var blob = new Blob([data], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
saveAs(blob, "Исходная выборка.xlsx");
});
};
when I merge cells, my merged rows over cells as in the screenshot "before". You see what number 238 disappear.
I need the cells to move.
[1]: https://i.stack.imgur.com/kyquZ.png - before
[2]: https://i.stack.imgur.com/0DeRc.png - after
How we see in screenshots, cells merge and value ​​disappear ((
need to shift(
I have faced the same problem.The data needs to be added after merging the cells. Please refer the documentation https://github.com/exceljs/exceljs#merged-cells

How to draw a line on a highcharts graph from an AJAX call?

I have in my html page a highcharts graph that I want to update dynamically. I have some input boxes that once they get updated by the user, trigger an AJAX post request. The request does some calculations and I want the output to be used to re-draw the line of my chart's second serie . That line represents a simple y = x function, the 'x' variable being calculated during the AJAX call.
Here is my html/JS code for the chart:
<script type="text/javascript">
$(function () {
$(document).ready(function () {
Highcharts.setOptions({
global: {
useUTC: false
}
});
var chart;
$('#container').highcharts({
chart: {
type: 'line',
animation: Highcharts.svg,
marginRight: 10,
},
title: {
text: 'Strategy Payoff'
},
xAxis: {
//type: 'datetime',
tickPixelInterval: 150
},
yAxis: {
title: {
text: 'Value'
},
plotLines: [{
value: 0,
width: 1,
color: '#808080'
}]
},
legend: {
enabled: false
},
exporting: {
enabled: false
},
series: [{
name: 'PnL',
data: (function () {
var data = [],
time = (new Date()).getTime(),
i;
var V = document.getElementById('V').value;
var Q = document.getElementById('Q').value;
var S = document.getElementById('S').value;
var K = document.getElementById('K').value;
var Type = document.getElementById('Type').value;
if (Type == 'Call') {
direction = 1;
} else {
direction = -1;
}
if (S >= 5000) {
stepSize = 500;
} else if (S >= 500) {
stepSize = 50;
} else {
stepSize = 1;
}
for (i = 0; i <= S * 2; i+=stepSize) { // i+=stepSize
data.push({
x: i,
y: Math.max(-V * Q, -V * Q + Q * direction * (i-K))
});
}
return data;
})()
}, {
name: 'Current Option Strategy PnL',
data: (function pnl(value=10) {
var data2 = [],
time2 = (new Date()).getTime(),
i;
var S = document.getElementById('S').value;
if (S >= 5000) {
stepSize = 500;
} else if (S >= 500) {
stepSize = 50;
} else {
stepSize = 1;
}
for (i = 0; i <= S * 2; i+=stepSize) {
data2.push({
x: i,
y: value
});
}
return data2;
})()
}]
});
});
});
</script>
Here are the input boxes that trigger the AJAX request when updated by the user:
<div class="chart" id="container"></div>
<div class="slider-wrapper">
<span>Option 1 Imp. Vol.</span>
<input class="toChange" id="rangeInput" name="rangeInput" type="range" value="{{Sigma}}" min="0.1" max="150" lang="en_EN" step="0.1" oninput="amount.value=rangeInput.value" />
<input class="toChange" id="amount" type="number" value="{{Sigma}}" min="0.1" max="150" lang="en_EN" step="0.1"oninput="rangeInput.value=amount.value" />
</div>
Finally, here is the AJAX request itself:
<script type="text/javascript">
function inputChange () {
var Sigma = document.getElementById("rangeInput").value;
var Type = document.getElementById('Type').value;
var S = document.getElementById('S').value;
var K = document.getElementById('K').value;
var t = document.getElementById('t').value;
var r = document.getElementById('r').value;
var V = document.getElementById('V').value;
var Q = document.getElementById('Q').value;
$.ajax({
url: '/optionstrategies/',
type: 'POST',
data: {
'Type': Type,
'S': S,
'K': K,
'r': r,
't': t,
'Sigma': Sigma,
},
success: function(optionVal) {
alert((optionVal - V) * Q);
document.getElementById("oPrice").innerHTML = optionVal;
document.getElementById("PnL").innerHTML = (optionVal - V) * Q;
// pnl(12);
}
});
}
$(".toChange").change(inputChange);
</script>
The AJAX call works well as the alert shows the expected value. I now need to use that value to update my chart. So for instance, if the value is equal to 12, I need the second serie of my chart to draw a line representing the y = 12 function.
I've named the function dealing with my second serie 'pnl' as you can see. I've been trying to call that function in the 'success' part of my AJAX request by writing something like 'pnl(12);', but it didn't do anything. Could anybody please help?
Use the series.update feature inside the success call and set the new data on it. Please check the available demos under below link.
API: https://api.highcharts.com/class-reference/Highcharts.Series#update
If this clue wouldn't help, please reproduce a simplified version of your code on some online editor which I could work on.

VBA web scraper to access search results from JavaScript form

I am working on a web scraper in VBA.
I have a website with a JavaScript form and I don't know how to access a table from the search results of that JavaScript form.
I know how to navigate and pull out the required info from a normal HTML site. I already put in my searching keywords and click the "search" button all through VBA.
After my search (e.g. "SN857X00PE") the search results are displayed in a table:
SN857X00PE/01 StudioLine 9702 - 9709
SN857X00PE/38 StudioLine 9711 - 9801
SN857X00PE/42 StudioLine 9802 - 9804
SN857X00PE/46 StudioLine 9805 - 9806
I'm looking to access all the left serial numbers (e.g. SN857X00PE/01, SN857X00PE/38 etc.).
When I go into the Firefox debugger I see many .js files and an index.xhtml.
I can find everything I need in the index.xhtml file (code included below with example search SN857X00PE) but when I access the HTML through IE.Document.getElementById("body").InnerHtml it doesn't show me the content of the index.xhtml file but instead the content of the TP4.js file (code below).
As you can see below, the TP4.js file doesn't include any useful info about the search results or any way to access them.
Is there a way to access the search result table of the JavaScript form? If I am able to put keywords in it and make the search result inside VBA it should be possible the access the results too. I'd like IE.Documents to point to the content of the index.xhtml file instead of the default TP4.js file if that's possible.
When I go to the Firefox inspector to see the HTML the javascript website is producing in the end it looks so easy to access my information. Is there a way to get direct access to the nice and clean "endresult-html" after the browser finishes compiling all the JavaScript?
The website is tradeplace(DOT)com but the Javascript form is hidden behind a login.
Here an overview on how the site looks like when it displays the search results. On the right I am showing the table with the search results inside the index.xhtml that I am trying to access but I don't know how to reach it, since I only have access to the contents of the tp4.js file.
Due to max character limit, I couldn't include the whole HTML code, here are some parts I think could be important/relevant, so you can get a general idea how the site looks:
index.xhtml:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="head">
<title>Tradeplace Marketplace</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link href="/tp4-web/css/jquery.theme.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="/tp4-web/javax.faces.resource/jquery/jquery.js.xhtml?ln=primefaces&v=6.0"></script>
<script type="text/javascript" src="/tp4-web/javax.faces.resource/core.js.xhtml?ln=primefaces&v=6.0"></script>
<script type="text/javascript" src="/tp4-web/javax.faces.resource/jsf.js.xhtml?ln=javax.faces"></script>
<link rel="stylesheet" type="text/css" href="/tp4-web/javax.faces.resource/components.css.xhtml?ln=primefaces&v=6.0" />
<script type="text/javascript" src="/tp4-web/javax.faces.resource/components.js.xhtml?ln=primefaces&v=6.0"></script>
<script type="text/javascript" src="/tp4-web/javax.faces.resource/jquery/jquery-plugins.js.xhtml?ln=primefaces&v=6.0"></script>
<script type="text/javascript" src="/tp4-web/javax.faces.resource/primefaces-extensions.js.xhtml?ln=primefaces-extensions&v=6.0.0"></script>
<script type="text/javascript">
if (window.PrimeFaces) {
PrimeFaces.settings.locale = 'en_US';
}
</script>
<link href="/tp4-web/css/tp4.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" src="/tp4-web/js/tp4.js"></script>
</head>
<body id="body">
<script type="text/javascript">
$(document).ready(function() {
TP4.initPageLayout();
TP4.enableSubmenus();
});
$(window).unload(function() {
TP4.hideLoadingScreen();
});
</script>
<div id="page">
<div id="header">
<div id="logoline" class="clearfix">
<img id="mainLogo" src="/tp4-web/images/sparetable/logo-tradeplace.png" alt="tradeplace" />
<img id="sparesLogo" src="/tp4-web/images/sparetable/spares.png" alt="spares" />
<table id="oemLogo">
<tr>
<td valign="middle"><img id="logoLine:brandImage" src="/tp4-web/pages/streamImage.xhtml?type=1&identifier=100001&pfdrid_c=true" alt="" />
</td>
</tr>
</table>
</div>
------------------------------------------------------------------------- ... SKIPPING SOME CODE UP TO THE RELEVANT CODE PART -------------------------------------------------------------------------
<tr class="data alt">
<td>
<a href="/tp4-web/pages/secure/product/productSearchDetail.xhtml?productNumber=SN857X00PE%2F01&formattedProductNumber=SN857X00PE%2F01&windowId=909" onclick="TP4.showLoadingScreen(this)">
SN857X00PE/01
</a>
</td>
<td>
StudioLine
</td>
<td>9702 - 9709
</td>
<td valign="middle" class="nowrap">
<span>
<a href="/tp4-web/pages/secure/product/productSearchDetail.xhtml?productNumber=SN857X00PE%2F01&formattedProductNumber=SN857X00PE%2F01&windowId=909" onclick="TP4.showLoadingScreen(this)">
<img alt="Dokumente" title="Dokumente" src="/tp4-web/images/icons/docs.gif" /></a>
</span>
<span>
<a href="/tp4-web/pages/secure/product/part/productPartList.xhtml?productNumber=SN857X00PE%2F01&formattedProductNumber=SN857X00PE%2F01&windowId=909" onclick="TP4.showLoadingScreen(this)">
<img alt="Ersatzteilliste" title="Ersatzteilliste" src="/tp4-web/images/icons/spare.gif" /></a>
</span>
<span>
<a href="/tp4-web/pages/secure/product/explodedView.xhtml?productNumber=SN857X00PE%2F01&formattedProductNumber=SN857X00PE%2F01&typeId1=A02&typeId2=A02E32&typeId3=A02E32V&windowId=909" onclick="TP4.showLoadingScreen(this)">
<img alt="Explosionszeichnung" title="Explosionszeichnung" src="/tp4-web/images/icons/explos.gif" /></a>
</span>
<span>
<a href="/tp4-web/pages/secure/accessory/accessoryForProduct.xhtml?commercialItemNumber=SN857X00PE%2F01&commercialItemNumberFormatted=SN857X00PE%2F01&windowId=909" onclick="TP4.showLoadingScreen(this)">
<img alt="Zubehör" title="Zubehör" src="/tp4-web/images/icons/commercial.gif" /></a>
</span>
</td>
</tr>
-------------------------------------------------------------- SKIPPING CODE AGAIN TO THE END -------------------------------------------------------------
</div>
<div id="ajaxStatus"></div>
<script id="ajaxStatus_s" type="text/javascript">
$(function() {
PrimeFaces.cw("AjaxStatus", "ajaxStatusWidget", {
id: "ajaxStatus",
start: function() {
TP4.showLoadingScreen();
},
success: function() {
TP4.hideLoadingScreen();
}
});
});
</script>
<div id="loadingDialog" class="ui-dialog ui-widget ui-widget-content ui-corner-all ui-shadow ui-hidden-container">
<div class="ui-dialog-content ui-widget-content">
<table id="loadingScreen">
<tr>
<td>
<p><b>Bitte warten - Datenbeschaffung...</b></p>
</td>
<td><img src="/tp4-web/images/ajax-loading.gif" /></td>
</tr>
</table>
</div>
</div>
<script id="loadingDialog_s" type="text/javascript">
$(function() {
PrimeFaces.cw("Dialog", "loadingDialogWidget", {
id: "loadingDialog",
resizable: false,
modal: true
});
});
</script>
</body>
<script type="text/javascript">
(function(i, s, o, g, r, a, m) {
i['GoogleAnalyticsObject'] = r;
i[r] = i[r] || function() {
(i[r].q = i[r].q || []).push(arguments)
}, i[r].l = 1 * new Date();
a = s.createElement(o),
m = s.getElementsByTagName(o)[0];
a.async = 1;
a.src = g;
m.parentNode.insertBefore(a, m)
})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');
ga('create', 'UA-55961901-1', 'auto');
ga('set', '&uid', '27d62c9d4ec32f32a829bed7142036c05d9516ac93c8935d18acf1fdc3d59145');
ga('send', 'pageview', {
'title': 'ProductSearchResult'
});
</script>
</html>
TP4.js
PrimeFaces.widget.ExtImageAreaSelect && (PrimeFaces.widget.ExtImageAreaSelect.prototype.cancelSelection = function () {
this.instance && this.instance.cancelSelection()
});
TP4 = {
Constants: {
PAGE_ELEMENT_SELECTOR: '#page',
HEADER_ELEMENT_SELECTOR: '#header',
FOOTER_ELEMENT_SELECTOR: '#footer',
BASKET_RELOAD_LINK_ID: '#reloadBasketLink',
BASKET_POPUP_WIDTH: '460',
BASKET_POPUP_HEIGHT: '400',
SPECIAL_NOTICE_PART_POPUP_WIDTH: '380',
SPECIAL_NOTICE_PART_POPUP_HEIGHT: '365',
SPECIAL_NOTICE_DOCUMENT_LIST_POPUP_WIDTH: '600',
SPECIAL_NOTICE_DOCUMENT_LIST_POPUP_HEIGHT: '400',
SPECIAL_NOTICE_DOCUMENT_POPUP_WIDTH: '500',
SPECIAL_NOTICE_DOCUMENT_POPUP_HEIGHT: '200',
CUSTOMER_INFO_POPUP_WIDTH: '480',
CUSTOMER_INFO_POPUP_HEIGHT: '405',
SMALL_POPUP_WIDTH: '360',
SMALL_POPUP_HEIGHT: '330',
MEDIUM_POPUP_WIDTH: '800',
MEDIUM_POPUP_HEIGHT: '600',
NON_MODAL_DIALOG_Z_INDEX: 500
},
Context: {
currentDialogZIndex: 0
},
showLoadingScreen: function (a) {
a ? !a.href || a.href && '#' == a.href ? TP4.showLoadingScreenAfterTimeout() : a.href && PF('loadingDialogWidget').show() : PF('loadingDialogWidget').show()
},
showLoadingScreenAfterTimeout: function () {
setTimeout(function () {
PF('loadingDialogWidget').show()
}, 100)
},
hideLoadingScreen: function () {
PF('loadingDialogWidget').hide()
},
initPageLayout: function () {
$(TP4.Constants.PAGE_ELEMENT_SELECTOR).css({
'padding-top': $(TP4.Constants.HEADER_ELEMENT_SELECTOR).height(),
'padding-bottom': $(TP4.Constants.FOOTER_ELEMENT_SELECTOR).height()
})
},
adjustElementHeightToAvailableHeight: function (a, b) {
var c = $(a),
d = $(TP4.Constants.HEADER_ELEMENT_SELECTOR),
g = $(TP4.Constants.FOOTER_ELEMENT_SELECTOR),
e = $(window).height();
0 < d.length && (e -= d.height());
0 < g.length && (e -= g.height());
null != b && (e -= b);
c.height(e)
},
setElementCssHeightToContentHeight: function (a) {
a = $(a);
var b = a.height();
a.css('height', b + 'px')
},
setDefaultCommand: function (a, b) {
a.keydown(function (a) {
var d = $.ui.keyCode;
if (a.which ==
d.ENTER || a.which == d.NUMPAD_ENTER) b.click(),
a.preventDefault()
})
},
scrollTo: function (a) {
window.location.hash = '#' + a
},
scrollToTop: function () {
$('html, body').animate({
scrollTop: 0
}, 0)
},
scrollToBottom: function (a) {
$('html, body').animate({
scrollTop: $(document).height()
}, a)
},
adjustMarginForTwoColumnLayout: function (a, b) {
$(b).css({
'margin-left': $(a).width()
})
},
restoreInputFieldValueFromHtml: function (a) {
a.each(function (b) {
$(this).val(a[b].defaultValue)
})
},
disableLoadingScreenModality: function () {
PF('loadingDialogWidget').cfg.modal = !1
},
enableLoadingScreenModality: function () {
PF('loadingDialogWidget').cfg.modal = !0
},
moveDialogBehindModalContainer: function (a) {
TP4.Context.currentDialogZIndex = a.jq.css('z-index');
a.jq.css('z-index', TP4.Constants.NON_MODAL_DIALOG_Z_INDEX)
},
moveDialogBeforeModalContainer: function (a) {
a.jq.css('z-index', TP4.Context.currentDialogZIndex)
},
synchronizeDialogModality: function (a) {
TP4.disableLoadingScreenModality();
TP4.moveDialogBehindModalContainer(a)
},
resetDialogModalityToDefault: function (a) {
TP4.enableLoadingScreenModality();
TP4.moveDialogBeforeModalContainer(a)
},
activateMaxLengthHandlingOnTextareas: function () {
$('textarea[maxlength]').keyup(function () {
var a = parseInt($(this).attr('maxlength'));
$(this).val().length > a && $(this).val($(this).val().substr(0, $(this).attr('maxlength')))
})
},
resizeTo: function (a, b, c) {
var d = - 1 < navigator.userAgent.toLowerCase().indexOf('chrome');
null != a && (d ? setTimeout(function () {
a.resizeTo(b, c)
}, 4) : a.resizeTo(b, c))
},
enableSubmenus: function () {
$('li').hover(function () {
var a = $(this);
a.closest('ul').children('li.hover').removeClass('hover');
a.addClass('hover');
a.attr('isHovered', !0)
}, function () {
var a = $(this);
a.removeAttr('isHovered');
setTimeout(function () {
a.attr('isHovered') || a.removeClass('hover')
}, 350)
})
},
redirect: function (a) {
window.location.href = a
},
overwriteIfZero: function (a) {
0 < a.value.length && '0' == a.value.substring(0, 1) && (a.value = a.value.substring(1), TP4.overwriteIfZero(a))
},
Part: {
showChildList: function (a) {
$('#childList' + a).show();
$('#showChildList' + a).hide();
$('#hideChildList' + a).show()
},
hideChildList: function (a) {
$('#childList' +
a).hide();
$('#showChildList' + a).show();
$('#hideChildList' + a).hide()
},
setAllDefaultCommands: function () {
TP4.setDefaultCommand($('#inputTextDescription'), $('#commandLinkDescription'));
TP4.setDefaultCommand($('#inputTextPartNumber'), $('#commandLinkPartNumber'));
TP4.setDefaultCommand($('#inputTextPicPos'), $('#commandLinkPicPos'))
}
},
Basket: {
openAddToBasketPopUp: function (a) {
TP4.PopUp.openPopUpWithSize(a.href, a.target, TP4.Constants.BASKET_POPUP_WIDTH, TP4.Constants.BASKET_POPUP_HEIGHT, 0, 0);
return !1
},
initializeDatePicker: function (a) {
a = $('#requestDeliveryDate').datepicker({
dateFormat: 'yy-mm-dd',
changeMonth: !0,
changeYear: !0,
yearRange: '-90:+0',
showOn: 'button',
buttonImageOnly: !0,
buttonImage: a + '/images/calendar.gif',
minDate: 0
});
a.attr('readonly', !0);
a.datepicker('option', 'showAnim', 'show')
},
setAllDefaultCommands: function () {
TP4.setDefaultCommand($('#basketOverviewItemListWrapper input'), $('#updateBasket'))
}
},
SpecialNotices: {
openSpecialNoticePartPopUp: function (a) {
TP4.PopUp.openPopUpWithSize(a.href, a.target, TP4.Constants.SPECIAL_NOTICE_PART_POPUP_WIDTH, TP4.Constants.SPECIAL_NOTICE_PART_POPUP_HEIGHT, 0, 0);
return !1
},
openSpecialNoticeDocumentListPopUp: function (a) {
TP4.PopUp.openPopUpWithSize(a.href, a.target, TP4.Constants.SPECIAL_NOTICE_DOCUMENT_LIST_POPUP_WIDTH, TP4.Constants.SPECIAL_NOTICE_DOCUMENT_LIST_POPUP_HEIGHT, 0, 0);
return !1
},
openSpecialNoticeDocumentPopUp: function (a) {
TP4.PopUp.openPopUpWithSize(a.href, a.target, TP4.Constants.SPECIAL_NOTICE_DOCUMENT_POPUP_WIDTH, TP4.Constants.SPECIAL_NOTICE_DOCUMENT_POPUP_HEIGHT, 0, 0);
return !1
},
initializeDocumentView: function (a) {
if (a) {
a = $('#specialNoticeHeader');
var b = 0;
0 < a.length && (b = a.height());
$('#specialNoticesPdf').css('visibility', 'visible');
$.browser.webkit ? setTimeout(function () {
TP4.adjustElementHeightToAvailableHeight('#specialNoticesDocumentContainer', b)
}, 100) : TP4.adjustElementHeightToAvailableHeight('#specialNoticesDocumentContainer', b)
}
},
afterAddToBasket: function (a, b) {
b && TP4.SpecialNotices.initializeDocumentView(a);
TP4.SpecialNotices.triggerBasketRefreshInMainWindow();
scroll(0, 0)
},
triggerBasketRefreshInMainWindow: function () {
TP4.SpecialNotices.triggerBasketRefresh(window.opener)
},
triggerBasketRefresh: function (a) {
var b = a.$(TP4.Constants.BASKET_RELOAD_LINK_ID);
0 < b.length ? (a.TP4.showLoadingScreen(), a.location.href = b.attr('href')) : a.opener && TP4.SpecialNotices.triggerBasketRefresh(a.opener)
}
},
PopUp: {
openSmallPopUpForLink: function (a) {
TP4.PopUp.openPopUpWithSize(a.href, a.target, TP4.Constants.SMALL_POPUP_WIDTH, TP4.Constants.SMALL_POPUP_HEIGHT, 0, 0);
return !1
},
-----------------------------------------------------------------
SKIPPING CODE TO THE END
-------------------------------------------------------------------
Infoline: {
openCustomerInfoPopUpForLink: function (a) {
TP4.PopUp.openPopUpWithSize(a.href, a.target, TP4.Constants.CUSTOMER_INFO_POPUP_WIDTH, TP4.Constants.CUSTOMER_INFO_POPUP_HEIGHT, 0, 0);
return !1
},
triggerInfolineRefreshInMainWindow: function () {
var a = window.opener.$(TP4.Constants.BASKET_RELOAD_LINK_ID);
0 < a.length ? (window.opener.location.href = a.attr('href'), window.opener.TP4.showLoadingScreen()) : (window.opener.TP4.showLoadingScreen(), window.opener.location.search = TP4.Infoline.getOpenerUrlWithEmptyWindowId())
},
getOpenerUrlWithEmptyWindowId: function () {
for (var a = {
}, b = window.opener.location.search.substring(1), c = /([^&=]+)=([^&]*)/g, d; d = c.exec(b); ) a[decodeURIComponent(d[1])] = decodeURIComponent(d[2]);
a.windowId = '';
return $.param(a)
}
},
History: {
initializeDatePicker: function (a) {
var b = $('#dateFrom, #dateTo').datepicker({
dateFormat: 'yy-mm-dd',
changeMonth: !0,
changeYear: !0,
yearRange: '-90:+0',
showOn: 'button',
buttonImageOnly: !0,
buttonImage: a + '/images/calendar.gif',
onSelect: function (a) {
var d = 'dateFrom' == this.id ? 'minDate' :
'maxDate',
g = $(this).data('datepicker');
a = $.datepicker.parseDate(g.settings.dateFormat || $.datepicker._defaults.dateFormat, a, g.settings);
b.not(this).datepicker('option', d, a)
}
});
b.attr('readonly', !0);
b.datepicker('option', 'showAnim', 'show')
}
},
FAQ: {
Context: {
selectedIssueId: - 1,
selectedQuestionsIssueId: - 1,
selectedQuestionAnchor: ''
},
setSelectedIssueId: function (a) {
TP4.FAQ.Context.selectedIssueId = a;
TP4.FAQ.Context.selectedQuestionsIssueId = a
},
setSelectedQuestionsIssueId: function (a) {
TP4.FAQ.Context.selectedQuestionsIssueId = a;
- 1 === TP4.FAQ.Context.selectedIssueId && (TP4.FAQ.Context.selectedIssueId = a)
},
setSelectionQuestionAnchor: function (a) {
TP4.FAQ.Context.selectedQuestionAnchor = 'question' + a
},
areQuestionsAlreadyLoaded: function () {
if (TP4.FAQ.Context.selectedIssueId === TP4.FAQ.Context.selectedQuestionsIssueId) return TP4.FAQ.scrollToAnchor(),
!1;
TP4.FAQ.Context.selectedIssueId = TP4.FAQ.Context.selectedQuestionsIssueId
},
scrollToAnchor: function () {
TP4.scrollTo(TP4.FAQ.Context.selectedQuestionAnchor)
},
moveOnClickFromInnerLinkToTreeNodeContainer: function (a, b) {
$(a).find('.ui-treenode-leaf').each(function () {
var a = $(this).find(b),
d = a.attr('onclick');
$(this).attr('onclick', d);
a.attr('onclick', null)
})
}
}
};
PrimeFaces.widget.Poll = PrimeFaces.widget.BaseWidget.extend({
init: function (a) {
this.cfg = a;
this.id = this.cfg.id;
this.active = !1;
this.cfg.autoStart && this.start()
},
refresh: function (a) {
this.isActive() && this.stop();
this.init(a)
},
start: function () {
this.timer = setInterval(this.cfg.fn, 1000 * this.cfg.frequency);
this.active = !0
},
stop: function () {
clearInterval(this.timer);
this.active = !1
},
handleComplete: function (a, b, c) {
c.stop && this.stop()
},
isActive: function () {
return this.active
}
});
CSS selector:
So using the HTML supplied I can use a CSS selector as follows:
#productList td > a
This selects elements based on their styling. "#" stands for class. The td > a means, select all a elements where the parent is a td element. So select those inside of an element with class productList.
I apply the selector using the querySelectorAll method of document which returns a nodeList of matching elements, the length of which I then traverse.
I am reading your HTML in from a file but you could use:
ie.document.querySelectorAll("#productList td > a")
CSS query:
Output to immediate window:
VBA:
Option Explicit
Public Sub HTMLQuery()
Dim oXHTTP As Object, HTML As New HTMLDocument, aNodeList As Object, i As Long
Set oXHTTP = CreateObject("MSXML2.XMLHTTP")
With oXHTTP
.Open "GET", "C:\Users\User\Desktop\index.html", False
.send
HTML.body.innerHTML = oXHTTP.responseText
Set aNodeList = HTML.querySelectorAll("#productList td > a")
For i = 0 To aNodeList.Length - 1
Debug.Print aNodeList.item(i).innerText
Next i
End With
End Sub
For your code:
Dim aNodeList As Object
Set aNodeList = IE.document.querySelectorAll("#productList td > a")
For i = 0 To aNodeList.Length - 1
Debug.Print aNodeList.item(i).innerText
Next i
Looping with a timer for page to load:
While IE.Busy Or IE.readyState < 4: DoEvents: Wend
Dim t As Date
t = Timer
Do
DoEvents
On Error Resume Next
Set aNodeList = IE.document.querySelectorAll("#productList td > a")
On Error GoTo 0
If Timer - t = 3 Then Exit Do '<==To avoid infinite loop. Adjust 3 seconds as required
Loop While aNodeList Is Nothing
If Not aNodeList Is Nothing Then
For i = 0 To aNodeList.Length - 1
Debug.Print aNodeList.item(i).innerText
Next i
End If
References:
You need to go VBE > Tools > References and add a reference to HTML Object Library if using an HTMLDocument variable as I have. You don't need this is accessing direct from IE.document.

trying to show chessboard js in odoo form widget, no error no pieces

Hi i´m trying to show chessboardjs on a form view in odoo backend, I finally make the widget to show the board, but the pieces are hidden, I don´t know why because seems to work fine, except for the pieces. If I use dragable : true in the options and move a hidden piece then the board is rendered with all the pieces. do I´m missing something, on my code that the chessboard its not rendered well??
here is mi widget code:
(function (instance) {
var _t = instance.web._t,
_lt = instance.web._lt;
var QWeb = instance.web.qweb;
openerp.chess_base = function (instance, local) {
local.YourWidgetClassName = instance.web.form.FormWidget.extend({
start: function () {
this.$el.append('<div id="board" style="width: 300px">BOARD GOES HERE</div>'); // creating the board in the DOM
this.onBoard();
},
onBoard: function (position, orientation) {
if (!position) {
this.position = 'start'
} else {
this.position = position
}
if (!orientation) {
this.orientation = 'white'
} else {
this.orientation = orientation
}
this.el_board = this.$('#board');
this.cfg = {
position: this.position,
orientation: this.orientation,
draggable: false,
pieceTheme: '/chess_base/static/img/chesspieces/wikipedia/{piece}.png'
};
this.board = ChessBoard(this.el_board, this.cfg);
}
});
instance.web.form.custom_widgets.add('widget_tag_name', 'instance.chess_base.YourWidgetClassName');
}
})(openerp);
I don't know why but this solve the issue, if someone have an explanation to me please ...
(function (instance) {
var _t = instance.web._t,
_lt = instance.web._lt;
var QWeb = instance.web.qweb;
openerp.chess_base = function (instance, local) {
local.ShowBoard = instance.web.form.FormWidget.extend({
start: function () {
this.$el.append('<div id="board" style="width: 300px">BOARD GOES HERE</div>');
this.show_board();
},
show_board: function () {
var Game = new instance.web.Model("chess.game"),
record_id = this.field_manager.datarecord.id,
record_name = this.field_manager.datarecord.name,
self = this;
self.el_board = self.$('#board');
Game.query(['pgn']).filter([['id', '=', record_id], ['name', '=', record_name]]).all().then(function (data) {
console.log(data);
self.cfg = {
position: data[0].pgn,
orientation: 'white',
pieceTheme: '/chess_base/static/img/chesspieces/wikipedia/{piece}.png'
};
ChessBoard(self.el_board, self.cfg);
});
}
});
instance.web.form.custom_widgets.add('board', 'instance.chess_base.ShowBoard');
}
})(openerp);

Is there any way to track Knockout Observable Array item is displayed in UI?

I am using foreach with pagination for displaying the notification list to the user. Now I want to know whether the item is displayed for the user in screen enough time to update the notification status from New to Viewed. I cannot update it to all the rendered item since some of the items might not be displayed since I am using pagination also I want to mark it as updated after displaying it enough time(Eg 5 sec)
Knockout has a very handy rateLimit extension you can use to perform delayed updates. By creating a computed copy of your current page observable, and extending it to only notify you 5 seconds after it has stopped changing, you can update the items on that page to a read status. For example:
var delayedPage = ko.computed(function() {
// Loop through the items that are rendered (a computed of `page`)
// Note: this makes the computed impure
itemsOnDisplay().forEach(function(item) {
// Set an observable status property of their viewmodel
item.read(true);
});
// This creates the subscription to page changes
return page();
}, this).extend({
rateLimit: {
timeout: 5000,
method: "notifyWhenChangesStop"
}
});
In a working example:
A collection of items with a boolean read observable
An observable page property that tells us what page we're on
A computed set of itemsOnDisplay that holds the items that are currently rendered
A rate limited reflection of the current page that updates 5 seconds after the last page change
var ViewModel = function(data) {
this.itemsPerPage = 6;
this.page = ko.observable();
this.items = ko.observableArray(data);
this.displayItems = ko.pureComputed(function() {
var start = this.page() * this.itemsPerPage;
var end = start + this.itemsPerPage;
return this.items().slice(start, end);
}, this);
this.canGoBack = ko.pureComputed(function() {
return this.page() > 0;
}, this);
this.canGoForward = ko.pureComputed(function() {
return (this.page() + 1) * this.itemsPerPage < this.items().length;
}, this);
// The important part:
this.delayedPage = ko.computed(function() {
var currentPage = this.page();
if (typeof currentPage === "undefined") return null;
this.displayItems().forEach(function(item) {
item.read(true);
});
console.log("Read items on page " + currentPage);
return currentPage;
}, this).extend({ rateLimit: { timeout: 5000, method: "notifyWhenChangesStop" } });
this.page(0);
}
ViewModel.prototype.prev = function() {
this.page(Math.max(this.page() - 1, 0));
};
ViewModel.prototype.next = function() {
this.page(Math.min(this.page() + 1, Math.ceil(this.items().length / this.itemsPerPage)));
};
var myData = [];
for (var i = 0; i < 50; i += 1) {
myData.push({
label: "Item " + i,
read: ko.observable(false)
});
}
var vm = new ViewModel(myData);
ko.applyBindings(vm);
li::after {
content: "new";
font-style: italic;
margin-left: 1rem;
padding: .25rem;
background: orange;
}
.is-read {
background: #efefef;
}
.is-read::after {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<ul data-bind="foreach: displayItems">
<li data-bind="text: label, css: {'is-read': read }"></li>
</ul>
<button data-bind="click: prev, enable: canGoBack">prev</button>
<button data-bind="click: next, enable: canGoForward">next</button>

Categories

Resources