Titanium Appcelerator iPhone App - App crashes when the table view is scrolled close to 1000th record and slows down when it starts scrolling - iphone

We have a sample app that loads 10 records each from the db and shows in a table vioew. The next ten records are taken when the scroll reaches the bottom (like the sample of dynamic scroll view in kitchen sink and following the same sample code). However the app scrolling becomes slower and slower when the no of records increases and then crashes when we were showing close to 1000th record. We have even more records to show (10000) and all the rows shows an 50X50 image and two texts.
if (search.value != null && search.value != ''){
dbrows = db.execute('select id, name, scientificname from siteRecords where name like \'%' + search.value + '%\' order by commonname limit 10 offset ' + lastRow);
}else{
dbrows = db.execute('select id, name, scientificname from siteRecords order by name limit 10 offset ' + lastRow);
}
tsEnd = new Date;
var duration = tsBegin.getTime() - tsEnd.getTime();
perfTableView.appendRow({title:"To fetch the record from 0 to " + (lastRow + 20) + " took: " + duration + " ms"});
tsBegin = new Date;
var rowCount = 0;
while (dbrows.isValidRow()) {
var row;
if( dbrows.fieldByName('name')[0] != curheader || initHeader == 0){
initHeader = 1;
curheader = dbrows.fieldByName('name')[0];
row = Ti.UI.createTableViewRow({height:55,backgroundColor:'#ffffff',backgroundSelectedColor:'#eeee33',hasChild:true,className:'birds',header:curheader});
index.push({title:curheader,index:rowNumber });
} else {
row = Ti.UI.createTableViewRow({height:55,backgroundColor:'#ffffff',backgroundSelectedColor:'#eeee33',hasChild:true,className:'birds'});
}
var lblBirdID = Ti.UI.createLabel({
text: dbrows.fieldByName('id'),
color: '#000000',
textAlign:'left',
left:4,
top:8,
height:100,
font:{fontWeight:'bold',fontSize:10},
visible:false
});
row.add(lblBirdID);
media = dbrows.fieldByName('scientificname').replace(' ', '_') + '.jpg';
var path = Titanium.Filesystem.resourcesDirectory + 'Birds/images/'
if (Titanium.Filesystem.getFile(path,media).exists())
{
var f;
if (isAndroid){
f = '../images/' + media;
}else{
f = Ti.Filesystem.getFile(Titanium.Filesystem.resourcesDirectory,'Birds/images/' + media);
}
var imgBird = Ti.UI.createImageView({
image:f,
left:4,
height:50,
width:50
});
row.add(imgBird);
}else{
var f;
if (isAndroid){
f = '../images/no_bird.jpg';
}else{
f = f = Ti.Filesystem.getFile(Titanium.Filesystem.resourcesDirectory,'Birds/images/no_bird.jpg');
}
var imgBird = Ti.UI.createImageView({
image:f,
left:4,
height:50,
width:50
});
row.add(imgBird);
}
var lblBirdName = Ti.UI.createLabel({
text: dbrows.fieldByName('name'),
color: '#000000',
textAlign:'left',
left:60,
top:8,
height:20,
font:{fontWeight:'bold',fontSize:16}
});
row.add(lblBirdName);
var lblBirdScientificName = Ti.UI.createLabel({
text: dbrows.fieldByName('scientificname'),
color: '#000000',
textAlign:'left',
left:60,
top:26,
height:20,
font:{fontSize:11}
});
row.add(lblBirdScientificName);
birdRows[rowNumber] = row;
rowCount = rowCount + 1;
rowNumber = rowNumber + 1;
dbrows.next();
}
dbrows.close();
db.close();
if (rowCount == 20){
lastRow = lastRow + 20;
}else{
lastRow = lastRow + rowCount;
}
birdTableView.setData(birdRows);
birdTableView.index = index;
tsEnd = new Date;
duration = tsBegin.getTime() - tsEnd.getTime();
perfTableView.appendRow({title:"To loop through the DB rows and to create table rows took: " + duration + " ms"});
}

Have you tried unloading items at the top, as items are added to the bottom (and vice versa)? Doing something like this would mean there are never more than say 100 rows at any one time.
Also, if possible, use a standard row instead of a custom row. A standard row is MUCH more performant than a custom one.

Related

Getting undefined when selecting html element in Javascript

I have a number of tv display clones loaded onto my page. After the clone is made, it will create a new TV object at the end of the loop definition, but for some reason, when it tries to access the index based canvas element from the page, it returns undefined. I tested that when hardcoding cnvs[0] from inside the constructor, it will return the canvas element. Indexing cnvs1 will return undefined; however, I see the tv has already been cloned before this line is executed. Shouldn't there then be a second canvas element of class ".static" that can be selected? In addition to the code, I attached a screenshot of my debugger screen in Chrome to see what the scope reads following "this.cnv = cnvs1", which should dynamically be cnvs[this.index]. Thanks in advance!
const container = document.getElementsByTagName("main")[0];
const template = document.getElementsByClassName("tv-set")
const cnvs = $(".static");
/**TBD**/
class TV {
constructor (id = "tv-0", name = "Blank Slate") {
this.id = id;
this.name = name;
console.log("NEW TV CREATED: ", this);
console.log("ID: ", this.id);
this.gifArr = gifCarousel_dict[this.name];
console.log("Media selection for this tv: ", this.gifArr);
// this.cnv = $(this.id).find('.static')[0];
// this.cnv = document.querySelector("[data-name=" + CSS.escape(this.id) + "]");
this.index = getSecondPart(id);
// this.cnv = $(".static")[this.index + 1];
this.cnv = cnvs[1];
console.log(this.cnv);
this.cnv.setAttribute("c", this.cnv.getContext("2d"));
this.cnv.setAttribute("cw", this.cnv.offsetWidth);
this.cnv.setAttribute("ch", this.cnv.offsetHeight);
this.staticScrn = this.cnv.getAttribute("c").createImageData(this.cnv.setAttribute("ch"), this.cnv.setAttribute("cw"));
console.log("This canvas element: ", this.cnv);
//Static display
this.isStatic = true;
}
showStatic() {
console.log(`Printing the tv name from the prototype fxn: ${this.name}`);
// this.isStatic = true;
c.clearRect(0, 0, cw, ch);
for (var i = 0; i < staticScrn.data.length; i += 4) {
let shade = 127 + Math.round(Math.random() * 128);
staticScrn.data[0 + i] = shade;
staticScrn.data[1 + i] = shade;
staticScrn.data[2 + i] = shade;
staticScrn.data[3 + i] = 255;
}
c.putImageData(staticScrn, 0, 0);
staticTO = setTimeout(runStatic, 1e3 / staticFPS);
}
}
for (let i = 0; i < phases.length; i++) {
const clone = template[i].cloneNode(true);
clone.setAttribute("id", "tv-" + (i + 1))
console.log("clone id: ", clone.getAttribute("id"))
clone.setAttribute("data-channel", 0)
clone.setAttribute("name", phases[i])
// clone.style.backgroundColor = phases[i].channels[0]
container.appendChild(clone)
var tvName = 'tvPhase' + clone.getAttribute("name");
//Instantiate TV object
window['tvPhase' + clone.getAttribute("name")] = new TV(clone.getAttribute("id"), clone.getAttribute("name"));
console.log("New Tv Created: ", tvName)
}
As I was about to post this question, I caught my bug...but I figured perhaps someone could learn from my stupid mistake! Turns out, it was because of line const cnvs = $(".static"); Setting this constant prior to the cloning operation means it will contain only the first canvas element and cannot be changed even after clones are created since it's a constant (face palm). The solution, of course, is to just define this.cnv = $(".static")[this.index]; directly or set the global cnvs as a var/let cnvs, still wrapping my head around when to use which.

How do I leave the clicked point highlighted in dygraphs?

I am using the selected shapes to draw a larger diamond shape on my graph. When a user clicks a point. I display the data in another div, but I want to leave the clicked point highlighted. In other words, I want to 'toggle' data behind the points on and off and the clicked points need to show if they are included in the dataset. I believe I have seen this somewhere but I cannot find it. Is there a 'standard' way of leaving a clicked point in the 'highlight' state when you mouse away after clicking?
Here is my code. The pointClickCallback is getting the data through ajax and displaying it in another div. That part works. I just want to leave the point highlighted so I know which points I have clicked on.
I also need the point to revert back to normal when I click a second time. This is a toggle that allows me to select and unselect points.
EDIT: I found the interaction model example but when I add it to my code I lose my pointClickCallback functionality. I saw the call to captureCanvas and the interaction model structure.
var g = new Dygraph(document.getElementById('rothmangraph'), lines, {
//showRangeSelector: true,
title: "Personal Wellness Index (PWI)",
labels: ['Date', 'Index'],
color: ['#006699'],
valueRange: [0, 101],
axisLabelFontSize: 12,
drawPoints: true,
gridLineColor: "#aaaaaa",
includeZero: true,
strokeWidth: 2,
rightGap: 20,
pointSize: 4,
highlightCircleSize: 8,
series : {
Index: {
drawHighlightPointCallback : Dygraph.Circles.DIAMOND
},
},
axes: {
y: {
pixelsPerLabel: 20,
},
x: {
valueFormatter: function(ms) {
return ' ' + strftime('%m/%d/%Y %r',new Date(ms)) + ' ';
},
axisLabelWidth: 60,
axisLabelFormatter: function(d, gran) {
return strftime('%m/%d %I:%M %p',new Date(d.getTime())) ;
}
}
},
underlayCallback: function (canvas, area, g) {
var warning = g.toDomCoords(0,41);
var critical = g.toDomCoords(0,66);
// set background color
canvas.fillStyle = graphCol;
canvas.fillRect(area.x, area.y, area.w, area.h);
// critical threshold line
canvas.fillStyle = "#cc0000";
canvas.fillRect(area.x,warning[1],area.w,2);
// warning threshold line
canvas.fillStyle = "#cccc00";
canvas.fillRect(area.x,critical[1],area.w,2);
},
pointClickCallback: function(e,point) {
var idx = point.idx;
var line = lines[idx];
var sqltime = strftime('%Y-%m-%d %H:%M:%S',new Date(line[0]));
var dispdate = strftime('%m/%d %r',new Date(line[0]));
_secureAjax({
url: '/ajax/getDataPoint',
data: {'patient_id': pid, "rdate": sqltime},
success: function (result) {
// parse and add row to table if not exists.
var data = JSON.parse(result);
var aid = data['id'];
var indexCol = "#a9cced"
if (line[1] <= 65) indexCol = "#ede1b7";
if (line[1] <= 40) indexCol = "#e5bfcc";
var headerinfo = '<th class="'+aid+'"><span class="showindex" style="background-color:'+indexCol+'">'+line[1]+'</span></th>';
var fixdate = dispdate.replace(' ','<br>');
var headerdate = '<th class="'+aid+'">'+fixdate+'</th>';
// skip if already exists
var found = false;
var whichone = false;
$('#headerdate tr th').each(function(idx, item) {
if (fixdate == $(this).html()) {
found = true;
whichone = idx;
}
});
if (!found) {
$.each(data, function (idx, item) {
$('#' + idx).append('<td class="'+aid+'" style="width:70px">' + item + '</td>');
});
$('#headerdate tr').append(headerdate);
$('#headerinfo tr').append(headerinfo);
} else {
$('tr').each(function() {
$('.'+aid).remove();
});
}
}
});
}
});
}

Chartjs: display different average line while grouping

Currently I'm working on chartjs and I found that is extremely fast to learn(at least for normal task).Currently I'm facing a problem: I was asked to display a grouped bar chart. like in figure.
grouped bar char
as you can see for the date 30-08-2016 there will be 3 distinct values for B,C,D and for 31-09-2016 same group of data but with different values.
I was asked also to add an average line for each different group in the chart
to look like this:
grouped bar chart with averages
I need to bind the start of one average line with the associated bar group.
I serached on internet but i couldn't find any example.Can you tell me if there is an example or give some suggestion? thanks in advance
this was my solution: i created a plugin and than tried to draw a line for each component of the chart. the code is quite messy (i'll try to clean)
// Define a plugin to provide average for different groups of data
Chart.plugins.register({
afterDatasetsDraw: function(chartInstance, easing) {
// To only draw at the end of animation, check for easing === 1
{
var ctx = chartInstance.chart.ctx;
var mapAverageLinePoints = {};
chartInstance.data.datasets.forEach(function (dataset, i) {
var meta = chartInstance.getDatasetMeta(i);
if (!meta.hidden) {
meta.data.forEach(function(element, index) {
var dataString = dataset.label;
var groupAverageLine = mapAverageLinePoints[dataString];
if(groupAverageLine==null)
{
groupAverageLine = [];
}
//store the point coordinate and the value
var linePoint =
{
x : position.x,
y : position.y,
value: dataset.data[index],
avg : 0
}
//adding the point to the array going to be stored in the map that group the point by the label
groupAverageLine.push(linePoint);
mapAverageLinePoints[dataString]=groupAverageLine;
}
);
}
});
for (var type in mapAverageLinePoints) {
var avgLinePoints = mapAverageLinePoints[type];
//NON E' in valore bensì rispecchia la sommatoria dei posY utilizzati nella rappresentazione
var totalYAxis=0;
var totale=0;
var labelNumero=0;
for(var k=0;k<avgLinePoints.length;k++)
{
var point = avgLinePoints[k];
totalYAxis+=point.y;
totale+=point.value;
//jump the first one
if(k>=1)
{
var prevPoint = avgLinePoints[k-1];
//k start from 0!!!!
var avgYAxis = (totalYAxis/(k+1));
var avg = (totale/(k+1));
// here i draw the line starting from the previous average
ctx.beginPath();
ctx.moveTo(point.x, avgYAxis);
ctx.strokeStyle = '#979797';
ctx.lineTo((prevPoint.x), prevPoint.avg);
ctx.stroke();
point.avg=avgYAxis;
//this one is for drawing a "o" where two segments collide
var fontSize = 12;
var fontStyle = 'normal';
var fontFamily = 'Helvetica Neue';
ctx.font = Chart.helpers.fontString(fontSize, fontStyle, fontFamily);
ctx.fillText("avg: "+(avg/range).toLocaleString() + rangeSuffix + ' €', point.x, point.y+(point.value>0?-30:+10));
}
else{
//for the first one only record the y as the avg
point.avg=point.y;
}
var fontSize = 10;
var fontStyle = 'normal';
var fontFamily = 'Helvetica Neue';
ctx.font = Chart.helpers.fontString(fontSize, fontStyle, fontFamily);
ctx.fillText("o", point.x, point.avg);
}
labelNumero=labelNumero+1;
}
}
}
});
the result is this one:
chart result

iTextSharp - Adding content to a cell later in the code

I am using iTextSharp to generate an invoice PDF. For that I wish to display order number and total amount at top of PDF. However the actual amount is calculated during product displaying part.
To make it simple the report follows this structure
===========order number=========
===========total amount=========
//here I display the products and so total amount is calculated here.
==========end of report=========
To achieve this I am creating a cell for total amount like this
PdfPCell cell = new PdfPCell();
PdfPCell headingAmountCell = cell;
//add some components to cell, reinitialize them again with new cells etc.
//but still have first cell reference in headingAmountCell
//calculate total amount here.
.
.
.
.
headerAmountCell.AddElement(new Phrase("Total Amount:" + totalAmount.ToString());
.
.
.
However this doesn't work.
I can see order number at top but not total amount.
If you could please help, that'll be great.
Thanks in anticipation.
Regards
Anshul
UPDATE:
Here is a small portion of code to help understand the problem
//header
var headerTable = new PdfPTable(logoExists ? 2 : 1);
headerTable.WidthPercentage = 100f;
if (logoExists)
headerTable.SetWidths(new[] { 50, 50 });
float padding = 5f;
//logo
if (logoExists)
{
var logoFilePath = _pictureService.GetPictureUrl(logoPicture, 0, false);
var cellLogo = new PdfPCell(Image.GetInstance(logoFilePath));
cellLogo.Border = Rectangle.BOTTOM_BORDER;
cellLogo.BorderColorBottom = BaseColor.GRAY;
cellLogo.PaddingBottom = padding;
cellLogo.VerticalAlignment = Rectangle.ALIGN_TOP;
headerTable.AddCell(cellLogo);
}
//store info
var cell = new PdfPCell();
cell.Border = Rectangle.BOTTOM_BORDER;
cell.BorderColorBottom = BaseColor.GRAY;
cell.PaddingBottom = padding;
cell.HorizontalAlignment = Element.ALIGN_RIGHT;
cell.VerticalAlignment = Element.ALIGN_TOP;
//payment method
var paymentMethod = _paymentService.LoadPaymentMethodBySystemName(order.PaymentMethodSystemName);
string paymentMethodStr = paymentMethod != null ? paymentMethod.GetLocalizedFriendlyName(_localizationService, lang.Id) : order.PaymentMethodSystemName;
PdfPCell headingAmountCell = cell;
if (!paymentMethodStr.Contains("COD") && order.PaymentStatus == global::Nop.Core.Domain.Payments.PaymentStatus.Paid)
{
cell.AddElement(new Paragraph("PREPAID", bigFont));
}
else if (paymentMethodStr.Contains("COD") && order.PaymentStatus == global::Nop.Core.Domain.Payments.PaymentStatus.Paid)
{
cell.AddElement(new Phrase("PAYMENT COLLECTED ON DELIVERY", bigFont));
}
else
{
cell.AddElement(new Paragraph("COLLECT ON DELIVERY", bigFont));
}
string order_number = order.GetOrderNumber();
cell.AddElement(new Paragraph(String.Format(_localizationService.GetResource("PDFInvoice.Order#", lang.Id), order_number), bigFont));
var store = _storeService.GetStoreById(order.StoreId) ?? _storeContext.CurrentStore;
cell.AddElement(new Paragraph(String.Format("Order Date: {0}", _dateTimeHelper.ConvertToUserTime(order.CreatedOnUtc, DateTimeKind.Utc).ToString("D", new CultureInfo(lang.LanguageCulture))), font));
headerTable.AddCell(cell);
doc.Add(headerTable);
//THEN I PERFORM SOME PRODUCT DISPLAY PART WHERE I CALCULATE THE ORDERTOTAL THEN AT THE BOTTOM
//.
//.
..
headingAmountCell.AddElement(new Phrase(Math.Round(orderTotal) + "<-AMOUNT TO COLLECT")));

How to create a simulator and simulate real-time strategy game like Age of Empires Rise of Rome?

I would be interested to simulate an Age of Empires Rise of Rome gathering process of food, wood etc. Such simulator would help to acheive the economy growth and would find the optimized order to build buildings and gather recources. Note that the simulator would examine only economy process, not fighting. Is there any free software that would be easy to use and make such simulation? The question is how to simulate the process with simulator (not how to create real-time game).
Peladao asked for a example of a RTS engine. I am writting a example code here, and commenting it.
pseudocode:
loop:
"decide what want to build";
"try to build that"; (a verification is made if resources are enough)
"generate resources";
goto loop;
Real code (javascript)
1) not interesting. defining functions to use later.
var rand = function(){
return Math.random()*100;
};
var random = (function(){
return {
betwen:function(min,max){
return Math.random()*(max-min)+min;
},
element:function(datarray){
var len = datarray.length;
if(!len) return;
var r = parseInt(this.betwen(0,len),10);
return datarray[r];
}
};
})();
var scroll = (function(){
var index = 0;
var maximo = 10;
return {
cortarViejas:function(){
var edadCorte = index - maximo;
$(".logline").each(function(){
if($(this).data("nace")<edadCorte)
$(this).remove();
});
},
add:function(txt){
var line = $("<div>");
line.addClass("logline");
line.data("nace",index++);
line.html(txt);
$(line).insertAfter('#marca');
this.cortarViejas();
},
error:function(txt){
this.add("ERROR: "+txt);
},
battle:function(txt){
this.add("Battle: "+ txt);
},
oceaniaDice:function(txt){
this.add("[Oceania broadcast] "+ txt);
},
debugOceania:function(txt){
this.add("[Oceania debug] "+ txt);
}
};
})();
2) heres the interesting part. Oceania is a empire that generate resources, and invest these resources in tanks or soldiers
var oceania = (function(){
var tanks = 0;
var soldiers = 0;
var build = "";//build strategy
var dead = 0;
var prices = {"tank":5,"soldier":1};
var hateLines = [ "Eastasia will be destroyed","We hate Eurasia","Our troops are doing good",
"We have always ben at war with Eastasia","Under the spreading chestnut tree.I sold you and you sold me"];
var metal = 0;
var powerplants = 1;
var factory = 3;
return {
info:function(){
scroll.debugOceania("Build strategy:"+ build);
scroll.debugOceania("Metal:"+ metal);
scroll.debugOceania("Army: soldier "+soldiers + ", tanks "+tanks );
scroll.debugOceania("Dead:"+ dead);
},
militarTick:function(stuff){
if(tanks>10 && soldiers > 20){
tanks -= 10;
soldiers -= 20;
scroll.battle("Some units losts! 10 tanks, 20 soldiers");
dead += 10+ 20;
}
},
3) Here, the empire execute his desire to build [stuff]
buy:function(stuff){
if(!prices[stuff]) {
scroll.error("buying what?:"+stuff);
return;
}
if(prices[stuff]>metal) {
scroll.debugOceania(stuff + " is too expensive");
return;
}
metal -= prices[stuff];
switch(stuff){
case "tank":
tanks++;
//scroll.debugOceania("Oceania create tank");
break;
case "soldier":
soldiers++;
//scroll.debugOceania("Oceania create soldier");
break;
}
},
buildTick:function(){
switch(build){
case "tanks":
this.buy("tank");
break;
case "soldiers":
this.buy("soldier");
break;
}
},
3) Here, the empire decide what is his desire, based on tanks/soldiers ratio, ammount of soldiers, ammount of tanks, or any other strategy-level issue.
strategyTick:function(){
var likeSoldiers=0,likeTanks=0;
build = "nothing";
//You have something, build something!
if( metal> 10) //we have metal, lets buy soldiers!
likeSoldier = 10;
//minimum this
if ( tanks < 10)
likeTanks += 10;
if ( soldiers< 20)
likeSoldiers += 10;
//we want 5 soldiers for 1 tank
if ( soldiers < tanks * 5)
likeSoldiers += 10;
//if we have at least 40 soldiers, we would love to have more tanks!
if ( soldiers > 40)
likeTanks += 10;
//if we have at least 40 tanks, we would love to have moresoldiers!
if ( tanks > 40)
likeSoldiers += 10;
//we want soldiers more than anything else, lets build soldiers!
if(likeSoldiers > likeTanks ){
build = "soldiers";
}else
//we want tanks more than anything else, lets build soldiers!
if( likeTanks > 5) {
build = "tanks";
}
},
4) Everything after this line can be ignored.
produccionTick:function(){
var t;
var energy = powerplants * 3;
var factorycount = factory;
var metalInitial = metal;
for(t=0;t<energy,factorycount>0;t++,factorycount--){
metal = metal + 1;
}
//var produce = metal - metalInitial;
//scroll.debugOceania("Oceania create "+ produce + " metal. Total now:"+metal);
},
propagandaTick:function(){
if(rand()<5) {
var hate = random.element(hateLines);
scroll.oceaniaDice(hate);
}
},
tick:function(){
this.propagandaTick();
this.produccionTick();
this.strategyTick();
this.buildTick();
this.militarTick();
}
};
})();
$(function(){
$("#cmd").keyup(function(event){
if (event.which == 13) {
var command = $(this).val();
$(this).val("");
scroll.add(command);
$("#cmd").focus();
event.preventDefault();
}
});
$("#reload").click(function(){
document.location = "game.htm?r="+Math.random();
});
oceania.tick();
$(document).ready(function(){
setInterval(function(){
oceania.tick();
},300);
});
});