1# Copyright (C) 2015 Wildfire Games. 2# 3# Permission is hereby granted, free of charge, to any person obtaining a copy 4# of this software and associated documentation files (the "Software"), to deal 5# in the Software without restriction, including without limitation the rights 6# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7# copies of the Software, and to permit persons to whom the Software is 8# furnished to do so, subject to the following conditions: 9# 10# The above copyright notice and this permission notice shall be included in 11# all copies or substantial portions of the Software. 12# 13# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19# THE SOFTWARE. 20 21import xml.etree.ElementTree as ET 22import os 23import glob 24 25AttackTypes = ["Hack","Pierce","Crush"] 26Resources = ["food", "wood", "stone", "metal"] 27 28# Generic templates to load 29# The way this works is it tries all generic templates 30# But only loads those who have one of the following parents 31# EG adding "template_unit.xml" will load all units. 32LoadTemplatesIfParent = ["template_unit_infantry.xml", "template_unit_cavalry.xml", "template_unit_champion.xml", "template_unit_hero.xml"] 33 34# Those describe Civs to analyze. 35# The script will load all entities that derive (to the nth degree) from one of the above templates. 36Civs = ["athen", "brit", "cart", "gaul", "iber", "kush", "mace", "maur", "pers", "ptol", "rome", "sele", "spart"] 37 38# Remote Civ templates with those strings in their name. 39FilterOut = ["marian", "thureophoros", "thorakites", "kardakes"] 40 41# Sorting parameters for the "roster variety" table 42ComparativeSortByCav = True 43ComparativeSortByChamp = True 44SortTypes = ["Support", "Pike", "Spear", "Sword", "Archer", "Javelin", "Sling", "Elephant"] # Classes 45 46# Disable if you want the more compact basic data. Enable to allow filtering and sorting in-place. 47AddSortingOverlay = True 48 49# This is the path to the /templates/ folder to consider. Change this for mod support. 50basePath = os.path.realpath(__file__).replace("unitTables.py","") + "../../../binaries/data/mods/public/simulation/templates/" 51 52# For performance purposes, cache opened templates files. 53globalTemplatesList = {} 54 55def htbout(file, balise, value): 56 file.write("<" + balise + ">" + value + "</" + balise + ">\n" ) 57def htout(file, value): 58 file.write("<p>" + value + "</p>\n" ) 59 60def fastParse(templateName): 61 if templateName in globalTemplatesList: 62 return globalTemplatesList[templateName] 63 globalTemplatesList[templateName] = ET.parse(templateName) 64 return globalTemplatesList[templateName] 65 66# This function checks that a template has the given parent. 67def hasParentTemplate(UnitName, parentName): 68 Template = fastParse(UnitName) 69 70 found = False 71 Name = UnitName 72 while found != True and Template.getroot().get("parent") != None: 73 Name = Template.getroot().get("parent") + ".xml" 74 if Name == parentName: 75 return True 76 Template = ET.parse(Name) 77 78 return False 79 80def NumericStatProcess(unitValue, templateValue): 81 if not "op" in templateValue.attrib: 82 return float(templateValue.text) 83 if (templateValue.attrib["op"] == "add"): 84 unitValue += float(templateValue.text) 85 elif (templateValue.attrib["op"] == "sub"): 86 unitValue -= float(templateValue.text) 87 elif (templateValue.attrib["op"] == "mul"): 88 unitValue *= float(templateValue.text) 89 elif (templateValue.attrib["op"] == "div"): 90 unitValue /= float(templateValue.text) 91 return unitValue 92 93 94# This function parses the entity values manually. 95def CalcUnit(UnitName, existingUnit = None): 96 unit = { 'HP' : "0", "BuildTime" : "0", "Cost" : { 'food' : "0", "wood" : "0", "stone" : "0", "metal" : "0", "population" : "0"}, 97 'Attack' : { "Melee" : { "Hack" : 0, "Pierce" : 0, "Crush" : 0 }, "Ranged" : { "Hack" : 0, "Pierce" : 0, "Crush" : 0 } }, 98 'RepeatRate' : {"Melee" : "0", "Ranged" : "0"},'PrepRate' : {"Melee" : "0", "Ranged" : "0"}, "Armour" : { "Hack" : 0, "Pierce" : 0, "Crush" : 0}, 99 "Ranged" : False, "Classes" : [], "AttackBonuses" : {}, "Restricted" : [], "WalkSpeed" : 0, "Range" : 0, "Spread" : 0, 100 "Civ" : None } 101 102 if (existingUnit != None): 103 unit = existingUnit 104 105 Template = fastParse(UnitName) 106 107 # Recursively get data from our parent which we'll override. 108 if (Template.getroot().get("parent") != None): 109 unit = CalcUnit(Template.getroot().get("parent") + ".xml", unit) 110 unit["Parent"] = Template.getroot().get("parent") + ".xml" 111 112 if (Template.find("./Identity/Civ") != None): 113 unit['Civ'] = Template.find("./Identity/Civ").text 114 115 if (Template.find("./Health/Max") != None): 116 unit['HP'] = NumericStatProcess(unit['HP'], Template.find("./Health/Max")) 117 118 if (Template.find("./Cost/BuildTime") != None): 119 unit['BuildTime'] = NumericStatProcess(unit['BuildTime'], Template.find("./Cost/BuildTime")) 120 121 if (Template.find("./Cost/Resources") != None): 122 for type in list(Template.find("./Cost/Resources")): 123 unit['Cost'][type.tag] = NumericStatProcess(unit['Cost'][type.tag], type) 124 125 if (Template.find("./Cost/Population") != None): 126 unit['Cost']["population"] = NumericStatProcess(unit['Cost']["population"], Template.find("./Cost/Population")) 127 128 if (Template.find("./Attack/Melee") != None): 129 if (Template.find("./Attack/Melee/RepeatTime") != None): 130 unit['RepeatRate']["Melee"] = NumericStatProcess(unit['RepeatRate']["Melee"], Template.find("./Attack/Melee/RepeatTime")) 131 if (Template.find("./Attack/Melee/PrepareTime") != None): 132 unit['PrepRate']["Melee"] = NumericStatProcess(unit['PrepRate']["Melee"], Template.find("./Attack/Melee/PrepareTime")) 133 for atttype in AttackTypes: 134 if (Template.find("./Attack/Melee/"+atttype) != None): 135 unit['Attack']['Melee'][atttype] = NumericStatProcess(unit['Attack']['Melee'][atttype], Template.find("./Attack/Melee/"+atttype)) 136 if (Template.find("./Attack/Melee/Bonuses") != None): 137 for Bonus in Template.find("./Attack/Melee/Bonuses"): 138 Against = [] 139 CivAg = [] 140 if (Bonus.find("Classes") != None and Bonus.find("Classes").text != None): 141 Against = Bonus.find("Classes").text.split(" ") 142 if (Bonus.find("Civ") != None and Bonus.find("Civ").text != None): 143 CivAg = Bonus.find("Civ").text.split(" ") 144 Val = float(Bonus.find("Multiplier").text) 145 unit["AttackBonuses"][Bonus.tag] = {"Classes" : Against, "Civs" : CivAg, "Multiplier" : Val} 146 if (Template.find("./Attack/Melee/RestrictedClasses") != None): 147 newClasses = Template.find("./Attack/Melee/RestrictedClasses").text.split(" ") 148 for elem in newClasses: 149 if (elem.find("-") != -1): 150 newClasses.pop(newClasses.index(elem)) 151 if elem in unit["Restricted"]: 152 unit["Restricted"].pop(newClasses.index(elem)) 153 unit["Restricted"] += newClasses 154 155 156 if (Template.find("./Attack/Ranged") != None): 157 unit['Ranged'] = True 158 if (Template.find("./Attack/Ranged/MaxRange") != None): 159 unit['Range'] = NumericStatProcess(unit['Range'], Template.find("./Attack/Ranged/MaxRange")) 160 if (Template.find("./Attack/Ranged/Spread") != None): 161 unit['Spread'] = NumericStatProcess(unit['Spread'], Template.find("./Attack/Ranged/Spread")) 162 if (Template.find("./Attack/Ranged/RepeatTime") != None): 163 unit['RepeatRate']["Ranged"] = NumericStatProcess(unit['RepeatRate']["Ranged"], Template.find("./Attack/Ranged/RepeatTime")) 164 if (Template.find("./Attack/Ranged/PrepareTime") != None): 165 unit['PrepRate']["Ranged"] = NumericStatProcess(unit['PrepRate']["Ranged"], Template.find("./Attack/Ranged/PrepareTime")) 166 for atttype in AttackTypes: 167 if (Template.find("./Attack/Ranged/"+atttype) != None): 168 unit['Attack']['Ranged'][atttype] = NumericStatProcess(unit['Attack']['Ranged'][atttype], Template.find("./Attack/Ranged/"+atttype)) 169 if (Template.find("./Attack/Ranged/Bonuses") != None): 170 for Bonus in Template.find("./Attack/Ranged/Bonuses"): 171 Against = [] 172 CivAg = [] 173 if (Bonus.find("Classes") != None and Bonus.find("Classes").text != None): 174 Against = Bonus.find("Classes").text.split(" ") 175 if (Bonus.find("Civ") != None and Bonus.find("Civ").text != None): 176 CivAg = Bonus.find("Civ").text.split(" ") 177 Val = float(Bonus.find("Multiplier").text) 178 unit["AttackBonuses"][Bonus.tag] = {"Classes" : Against, "Civs" : CivAg, "Multiplier" : Val} 179 if (Template.find("./Attack/Melee/RestrictedClasses") != None): 180 newClasses = Template.find("./Attack/Melee/RestrictedClasses").text.split(" ") 181 for elem in newClasses: 182 if (elem.find("-") != -1): 183 newClasses.pop(newClasses.index(elem)) 184 if elem in unit["Restricted"]: 185 unit["Restricted"].pop(newClasses.index(elem)) 186 unit["Restricted"] += newClasses 187 188 if (Template.find("./Armour") != None): 189 for atttype in AttackTypes: 190 if (Template.find("./Armour/"+atttype) != None): 191 unit['Armour'][atttype] = NumericStatProcess(unit['Armour'][atttype], Template.find("./Armour/"+atttype)) 192 193 if (Template.find("./UnitMotion") != None): 194 if (Template.find("./UnitMotion/WalkSpeed") != None): 195 unit['WalkSpeed'] = NumericStatProcess(unit['WalkSpeed'], Template.find("./UnitMotion/WalkSpeed")) 196 197 if (Template.find("./Identity/VisibleClasses") != None): 198 newClasses = Template.find("./Identity/VisibleClasses").text.split(" ") 199 for elem in newClasses: 200 if (elem.find("-") != -1): 201 newClasses.pop(newClasses.index(elem)) 202 if elem in unit["Classes"]: 203 unit["Classes"].pop(newClasses.index(elem)) 204 unit["Classes"] += newClasses 205 206 if (Template.find("./Identity/Classes") != None): 207 newClasses = Template.find("./Identity/Classes").text.split(" ") 208 for elem in newClasses: 209 if (elem.find("-") != -1): 210 newClasses.pop(newClasses.index(elem)) 211 if elem in unit["Classes"]: 212 unit["Classes"].pop(newClasses.index(elem)) 213 unit["Classes"] += newClasses 214 215 216 return unit 217 218def WriteUnit(Name, UnitDict): 219 ret = "<tr>" 220 221 ret += "<td class=\"Sub\">" + Name + "</td>" 222 223 ret += "<td>" + str(int(UnitDict["HP"])) + "</td>" 224 225 ret += "<td>" +str("%.0f" % float(UnitDict["BuildTime"])) + "</td>" 226 227 ret += "<td>" + str("%.1f" % float(UnitDict["WalkSpeed"])) + "</td>" 228 229 for atype in AttackTypes: 230 PercentValue = 1.0 - (0.9 ** float(UnitDict["Armour"][atype])) 231 ret += "<td>" + str("%.0f" % float(UnitDict["Armour"][atype])) + " / " + str("%.0f" % (PercentValue*100.0)) + "%</td>" 232 233 attType = ("Ranged" if UnitDict["Ranged"] == True else "Melee") 234 if UnitDict["RepeatRate"][attType] != "0": 235 for atype in AttackTypes: 236 repeatTime = float(UnitDict["RepeatRate"][attType])/1000.0 237 ret += "<td>" + str("%.1f" % (float(UnitDict["Attack"][attType][atype])/repeatTime)) + "</td>" 238 239 ret += "<td>" + str("%.1f" % (float(UnitDict["RepeatRate"][attType])/1000.0)) + "</td>" 240 else: 241 for atype in AttackTypes: 242 ret += "<td> - </td>" 243 ret += "<td> - </td>" 244 245 if UnitDict["Ranged"] == True and UnitDict["Range"] > 0: 246 ret += "<td>" + str("%.1f" % float(UnitDict["Range"])) + "</td>" 247 spread = float(UnitDict["Spread"]) 248 ret += "<td>" + str("%.1f" % spread) + "</td>" 249 else: 250 ret += "<td> - </td><td> - </td>" 251 252 for rtype in Resources: 253 ret += "<td>" + str("%.0f" % float(UnitDict["Cost"][rtype])) + "</td>" 254 255 ret += "<td>" + str("%.0f" % float(UnitDict["Cost"]["population"])) + "</td>" 256 257 ret += "<td style=\"text-align:left;\">" 258 for Bonus in UnitDict["AttackBonuses"]: 259 ret += "[" 260 for classe in UnitDict["AttackBonuses"][Bonus]["Classes"]: 261 ret += classe + " " 262 ret += ': ' + str(UnitDict["AttackBonuses"][Bonus]["Multiplier"]) + "] " 263 ret += "</td>" 264 265 ret += "</tr>\n" 266 return ret 267 268# Sort the templates dictionary. 269def SortFn(A): 270 sortVal = 0 271 for classe in SortTypes: 272 sortVal += 1 273 if classe in A[1]["Classes"]: 274 break 275 if ComparativeSortByChamp == True and A[0].find("champion") == -1: 276 sortVal -= 20 277 if ComparativeSortByCav == True and A[0].find("cavalry") == -1: 278 sortVal -= 10 279 if A[1]["Civ"] != None and A[1]["Civ"] in Civs: 280 sortVal += 100 * Civs.index(A[1]["Civ"]) 281 return sortVal 282 283# helper to write coloured text. 284def WriteColouredDiff(file, diff, PositOrNegat): 285 286 def cleverParse(diff): 287 if float(diff) - int(diff) < 0.001: 288 return str(int(diff)) 289 else: 290 return str("%.1f" % float(diff)) 291 292 if (PositOrNegat == "positive"): 293 file.write("<td><span style=\"color:rgb(" +("200,200,200" if diff == 0 else ("180,0,0" if diff > 0 else "0,150,0")) + ");\">" + cleverParse(diff) + "</span></td>") 294 elif (PositOrNegat == "negative"): 295 file.write("<td><span style=\"color:rgb(" +("200,200,200" if diff == 0 else ("180,0,0" if diff < 0 else "0,150,0")) + ");\">" + cleverParse(diff) + "</span></td>") 296 else: 297 complain 298 299 300############################################################ 301############################################################ 302# Create the HTML file 303 304f = open(os.path.realpath(__file__).replace("unitTables.py","") + 'unit_summary_table.html', 'w') 305 306f.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n<html>\n<head>\n <title>Unit Tables</title>\n <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n<body>") 307htbout(f,"h1","Unit Summary Table") 308f.write("\n") 309 310os.chdir(basePath) 311 312############################################################ 313# Load generic templates 314 315templates = {} 316 317htbout(f,"h2", "Units") 318 319f.write("<table id=\"genericTemplates\">\n") 320f.write("<thead><tr>") 321f.write("<th></th><th>HP</th> <th>BuildTime</th> <th>Speed(walk)</th> <th colspan=\"3\">Armour</th> <th colspan=\"6\">Attack (DPS)</th> <th colspan=\"5\">Costs</th> <th>Efficient Against</th> </tr>\n") 322f.write("<tr class=\"Label\" style=\"border-bottom:1px black solid;\">") 323f.write("<th></th><th></th> <th></th> <th></th> <th>H</th><th>P</th><th>C</th> <th>H</th><th>P</th><th>C</th><th>Rate</th><th>Range</th><th>Spread\n(/100m)</th> <th>F</th><th>W</th><th>S</th><th>M</th><th>P</th> <th></th> </tr>\n</thead>\n") 324 325for template in list(glob.glob('template_*.xml')): 326 if os.path.isfile(template): 327 found = False 328 for possParent in LoadTemplatesIfParent: 329 if hasParentTemplate(template, possParent): 330 found = True 331 break 332 if found == True: 333 templates[template] = CalcUnit(template) 334 f.write(WriteUnit(template, templates[template])) 335 336f.write("</table>") 337 338############################################################ 339# Load Civ specific templates 340 341CivTemplates = {} 342 343for Civ in Civs: 344 CivTemplates[Civ] = {} 345 # Load all templates that start with that civ indicator 346 for template in list(glob.glob('units/' + Civ + '_*.xml')): 347 if os.path.isfile(template): 348 349 # filter based on FilterOut 350 breakIt = False 351 for filter in FilterOut: 352 if template.find(filter) != -1: breakIt = True 353 if breakIt: continue 354 355 # filter based on loaded generic templates 356 breakIt = True 357 for possParent in LoadTemplatesIfParent: 358 if hasParentTemplate(template, possParent): 359 breakIt = False 360 break 361 if breakIt: continue 362 363 unit = CalcUnit(template) 364 365 # Remove variants for now 366 if unit["Parent"].find("template_") == -1: 367 continue 368 369 # load template 370 CivTemplates[Civ][template] = unit 371 372############################################################ 373f.write("\n\n<h2>Units Specializations</h2>\n") 374f.write("<p class=\"desc\">This table compares each template to its parent, showing the differences between the two.<br/>Note that like any table, you can copy/paste this in Excel (or Numbers or ...) and sort it.</p>") 375 376TemplatesByParent = {} 377 378#Get them in the array 379for Civ in Civs: 380 for CivUnitTemplate in CivTemplates[Civ]: 381 parent = CivTemplates[Civ][CivUnitTemplate]["Parent"] 382 if parent in templates and templates[parent]["Civ"] == None: 383 if parent not in TemplatesByParent: 384 TemplatesByParent[parent] = [] 385 TemplatesByParent[parent].append( (CivUnitTemplate,CivTemplates[Civ][CivUnitTemplate])) 386 387#Sort them by civ and write them in a table. 388f.write("<table id=\"TemplateParentComp\">\n") 389f.write("<thead><tr>") 390f.write("<th></th><th></th><th>HP</th> <th>BuildTime</th> <th>Speed</th> <th colspan=\"3\">Armour</th> <th colspan=\"6\">Attack</th> <th colspan=\"5\">Costs</th> <th>Civ</th> </tr>\n") 391f.write("<tr class=\"Label\" style=\"border-bottom:1px black solid;\">") 392f.write("<th></th><th></th><th></th> <th></th> <th></th> <th>H</th><th>P</th><th>C</th> <th>H</th><th>P</th><th>C</th><th>Rate</th><th>Range</th><th>Spread</th> <th>F</th><th>W</th><th>S</th><th>M</th><th>P</th> <th></th> </tr>\n<tr></thead>") 393for parent in TemplatesByParent: 394 TemplatesByParent[parent].sort(key=lambda x : Civs.index(x[1]["Civ"])) 395 for tp in TemplatesByParent[parent]: 396 f.write("<th style='font-size:10px'>" + parent.replace(".xml","").replace("template_","") + "</th>") 397 398 f.write("<td class=\"Sub\">" + tp[0].replace(".xml","").replace("units/","") + "</td>") 399 400 # HP 401 diff = int(tp[1]["HP"]) - int(templates[parent]["HP"]) 402 WriteColouredDiff(f, diff, "negative") 403 404 # Build Time 405 diff = int(tp[1]["BuildTime"]) - int(templates[parent]["BuildTime"]) 406 WriteColouredDiff(f, diff, "positive") 407 408 # walk speed 409 diff = float(tp[1]["WalkSpeed"]) - float(templates[parent]["WalkSpeed"]) 410 WriteColouredDiff(f, diff, "negative") 411 412 # Armor 413 for atype in AttackTypes: 414 diff = float(tp[1]["Armour"][atype]) - float(templates[parent]["Armour"][atype]) 415 WriteColouredDiff(f, diff, "negative") 416 417 # Attack types (DPS) and rate. 418 attType = ("Ranged" if tp[1]["Ranged"] == True else "Melee") 419 if tp[1]["RepeatRate"][attType] != "0": 420 for atype in AttackTypes: 421 myDPS = float(tp[1]["Attack"][attType][atype]) / (float(tp[1]["RepeatRate"][attType])/1000.0) 422 parentDPS = float(templates[parent]["Attack"][attType][atype]) / (float(templates[parent]["RepeatRate"][attType])/1000.0) 423 WriteColouredDiff(f, myDPS - parentDPS, "negative") 424 WriteColouredDiff(f, float(tp[1]["RepeatRate"][attType])/1000.0 - float(templates[parent]["RepeatRate"][attType])/1000.0, "negative") 425 # range and spread 426 if tp[1]["Ranged"] == True: 427 WriteColouredDiff(f, float(tp[1]["Range"]) - float(templates[parent]["Range"]), "negative") 428 mySpread = float(tp[1]["Spread"]) 429 parentSpread = float(templates[parent]["Spread"]) 430 WriteColouredDiff(f, mySpread - parentSpread, "positive") 431 else: 432 f.write("<td></td><td></td>") 433 else: 434 f.write("<td></td><td></td><td></td><td></td><td></td><td></td>") 435 436 for rtype in Resources: 437 WriteColouredDiff(f, float(tp[1]["Cost"][rtype]) - float(templates[parent]["Cost"][rtype]), "positive") 438 439 WriteColouredDiff(f, float(tp[1]["Cost"]["population"]) - float(templates[parent]["Cost"]["population"]), "positive") 440 441 f.write("<td>" + tp[1]["Civ"] + "</td>") 442 443 f.write("</tr>\n<tr>") 444f.write("<table/>") 445 446# Table of unit having or not having some units. 447f.write("\n\n<h2>Roster Variety</h2>\n") 448f.write("<p class=\"desc\">This table show which civilizations have units who derive from each loaded generic template.<br/>Green means 1 deriving unit, blue means 2, black means 3 or more.<br/>The total is the total number of loaded units for this civ, which may be more than the total of units inheriting from loaded templates.</p>") 449f.write("<table class=\"CivRosterVariety\">\n") 450f.write("<tr><th>Template</th>\n") 451for civ in Civs: 452 f.write("<td class=\"vertical-text\">" + civ + "</td>\n") 453f.write("</tr>\n") 454 455sortedDict = sorted(templates.items(), key=SortFn) 456 457for tp in sortedDict: 458 if tp[0] not in TemplatesByParent: 459 continue 460 f.write("<tr><td>" + tp[0] +"</td>\n") 461 for civ in Civs: 462 found = 0 463 for temp in TemplatesByParent[tp[0]]: 464 if temp[1]["Civ"] == civ: 465 found += 1 466 if found == 1: 467 f.write("<td style=\"background-color:rgb(0,230,0);\"></td>") 468 elif found == 2: 469 f.write("<td style=\"background-color:rgb(0,0,200);\"></td>") 470 elif found >= 3: 471 f.write("<td style=\"background-color:rgb(0,0,0);\"></td>") 472 else: 473 f.write("<td style=\"background-color:rgb(235,0,0);\"></td>") 474 f.write("</tr>\n") 475f.write("<tr style=\"margin-top:2px;border-top:2px #aaa solid;\"><th style=\"text-align:right; padding-right:10px;\">Total:</th>\n") 476for civ in Civs: 477 count = 0 478 for units in CivTemplates[civ]: count += 1 479 f.write("<td style=\"text-align:center;\">" + str(count) + "</td>\n") 480 481f.write("</tr>\n") 482 483f.write("<table/>") 484 485# Add a simple script to allow filtering on sorting directly in the HTML page. 486if AddSortingOverlay: 487 f.write("<script src=\"tablefilter/tablefilter.js\"></script>\n\ 488 \n\ 489 <script data-config>\n\ 490 \ 491 var cast = function (val) {\n\ 492 console.log(val);\ 493 if (+val != val)\n\ 494 return -999999999999;\n\ 495 return +val;\n\ 496 }\n\ 497 \n\n\ 498 var filtersConfig = {\n\ 499 base_path: 'tablefilter/',\n\ 500 col_0: 'checklist',\n\ 501 alternate_rows: true,\n\ 502 rows_counter: true,\n\ 503 btn_reset: true,\n\ 504 loader: false,\n\ 505 status_bar: false,\n\ 506 mark_active_columns: true,\n\ 507 highlight_keywords: true,\n\ 508 col_number_format: [\n\ 509 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US'\n\ 510 ],\n\ 511 filters_row_index: 2,\n\ 512 headers_row_index: 1,\n\ 513 extensions:[{\ 514 name: 'sort',\ 515 types: [\ 516 'string', 'us', 'us', 'us', 'us', 'us', 'us', 'mytype', 'mytype', 'mytype', 'mytype', 'mytype', 'mytype', 'us', 'us', 'us', 'us', 'us', 'string'\ 517 ],\ 518 on_sort_loaded: function(o, sort) {\ 519 sort.addSortType('mytype',cast);\ 520 },\ 521 }],\n\ 522 col_widths: [\n\ 523 null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,'120px'\n\ 524 ],\n\ 525 };\n\ 526 var tf = new TableFilter('genericTemplates', filtersConfig,2);\n\ 527 tf.init();\n\ 528 \n\ 529 \ 530 var secondFiltersConfig = {\n\ 531 base_path: 'tablefilter/',\n\ 532 col_0: 'checklist',\n\ 533 col_19: 'checklist',\n\ 534 alternate_rows: true,\n\ 535 rows_counter: true,\n\ 536 btn_reset: true,\n\ 537 loader: false,\n\ 538 status_bar: false,\n\ 539 mark_active_columns: true,\n\ 540 highlight_keywords: true,\n\ 541 col_number_format: [\n\ 542 null, null, 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', 'US', null\n\ 543 ],\n\ 544 filters_row_index: 2,\n\ 545 headers_row_index: 1,\n\ 546 extensions:[{\ 547 name: 'sort',\ 548 types: [\ 549 'string', 'string', 'us', 'us', 'us', 'us', 'us', 'us', 'typetwo', 'typetwo', 'typetwo', 'typetwo', 'typetwo', 'typetwo', 'us', 'us', 'us', 'us', 'us', 'string'\ 550 ],\ 551 on_sort_loaded: function(o, sort) {\ 552 sort.addSortType('typetwo',cast);\ 553 },\ 554 }],\n\ 555 col_widths: [\n\ 556 null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null\n\ 557 ],\n\ 558 };\n\ 559 \ 560 var tf2 = new TableFilter('TemplateParentComp', secondFiltersConfig,2);\n\ 561 tf2.init();\n\ 562 \n\ 563 </script>\n") 564 565 566f.write("</body>\n</html>") 567