PageSourceSearch

https://fribbels.github.io/e7/copyPaste.js

js fribbels.github.io collected 2026-10-03 09:13:05 UTC 19,301 bytes, 594 lines download raw bytes

1HERO_CACHE = "https://e7-optimizer-game-data.s3-accelerate.amazonaws.com/herodata.json?";
2heroData = {};
3heroesById = {};
4$ = jQuery
5dev = false;
6
7loadedHeroData = false;
8loadedGwdb = false;
9
10selector0 = null
11selector1 = null
12selector2 = null
13selector3 = null
14selector4 = null
15
16buildDefSelector0 = null
17buildDefSelector1 = null
18buildDefSelector2 = null
19buildDefSelector3 = null
20buildDefSelector4 = null
21
22jQuery(document).ready(function($){
23    document.title = "Fribbels GW Meta Tracker"
24    $("#homeLink").attr("href", window.location.href.split('?')[0])
25
26    var options = {
27        sortField: 'text',
28        width: 'resolve', // need to override the changed default
29        placeholder: "Select hero",
30        templateResult: formatHeroList,
31        theme: "classic",
32        allowClear: true
33    }
34    var includeOptions = {
35        sortField: 'text',
36        width: 'resolve', // need to override the changed default
37        placeholder: "Include hero",
38        templateResult: formatHeroList,
39        theme: "classic",
40        allowClear: true,
41    }
42    var excludeOptions = {
43        sortField: 'text',
44        width: 'resolve', // need to override the changed default
45        placeholder: "Exclude hero",
46        templateResult: formatHeroList,
47        theme: "classic",
48        allowClear: true
49    }
50
51    selector0 = $('#heroSelector0').select2(options);
52    selector1 = $('#heroSelector1').select2(options);
53    selector2 = $('#heroSelector2').select2(options);
54    selector3 = $('#heroSelector3').select2(includeOptions);
55    selector4 = $('#heroSelector4').select2(excludeOptions);
56
57    buildDefSelector0 = $('#buildDefHeroSelector0').select2(options);
58    buildDefSelector1 = $('#buildDefHeroSelector1').select2(options);
59    buildDefSelector2 = $('#buildDefHeroSelector2').select2(options);
60    buildDefSelector3 = $('#buildDefHeroSelector3').select2(includeOptions);
61    buildDefSelector4 = $('#buildDefHeroSelector4').select2(excludeOptions);
62
63    $("#searchButton").click(search)
64    $("#buildDefSearchButton").click(buildDefSearch)
65
66    var queryString = window.location.search;
67    var urlParams = new URLSearchParams(queryString).get('def');
68
69    if (urlParams) {
70        $('#resultRows').html("Loading..")
71    } else {
72        $('#metaRows').html("Loading...")
73    }
74
75
76    fetchCache(HERO_CACHE).then(x => {
77        console.log("herodata", x)
78        heroData = x;
79
80        for (var name of Object.keys(heroData)) {
81            heroesById[heroData[name].code] = name;
82        }
83
84        for (var value of Object.values(heroData)) {
85            var img=new Image();
86            img.src=value.assets.icon;
87        }
88
89
90        var entries = Object.entries(heroesById).sort(function compare(a, b) {
91            if (a[1] < b[1])
92                return -1;
93            if (a[1] > b[1])
94                return 1;
95            return 0;
96        })
97
98        for (var entry of entries) {
99            var data = {
100                id: entry[0],
101                text: entry[1]
102            };
103
104            var newOption0 = new Option(data.text, data.id, false, false);
105            var newOption1 = new Option(data.text, data.id, false, false);
106            var newOption2 = new Option(data.text, data.id, false, false);
107            var newOption3 = new Option(data.text, data.id, false, false);
108            var newOption4 = new Option(data.text, data.id, false, false);
109            $('#heroSelector0').append(newOption0);
110            $('#heroSelector1').append(newOption1);
111            $('#heroSelector2').append(newOption2);
112            $('#heroSelector3').append(newOption3);
113            $('#heroSelector4').append(newOption4);
114
115            var buildDefNewOption0 = new Option(data.text, data.id, false, false);
116            var buildDefNewOption1 = new Option(data.text, data.id, false, false);
117            var buildDefNewOption2 = new Option(data.text, data.id, false, false);
118            var buildDefNewOption3 = new Option(data.text, data.id, false, false);
119            var buildDefNewOption4 = new Option(data.text, data.id, false, false);
120            $('#buildDefHeroSelector0').append(buildDefNewOption0);
121            $('#buildDefHeroSelector1').append(buildDefNewOption1);
122            $('#buildDefHeroSelector2').append(buildDefNewOption2);
123            $('#buildDefHeroSelector3').append(buildDefNewOption3);
124            $('#buildDefHeroSelector4').append(buildDefNewOption4);
125        }
126
127        try {
128            if (urlParams) {
129                var names = urlParams.split(",")
130                var ids = names.map(x => Object.entries(heroesById).find(y => y[1] == x)[0])
131
132                selector0.val(ids[0]).trigger("change");
133                selector1.val(ids[1]).trigger("change");
134                selector2.val(ids[2]).trigger("change");
135
136                search();
137            }
138        } catch (e) {
139            console.error("Url parsing failed", e);
140        }
141
142        loadedHeroData = true;
143        checkReady();
144    }).catch((e) => {
145        console.log(e)
146        $('#metaRows').html("</br></br></br>No data for recent guild wars")
147    })
148
149});
150
151function checkReady() {
152    if (!loadedHeroData) {
153        return;
154    }
155
156    showMeta()
157}
158
159function showMeta() {
160    var queryString = window.location.search;
161    var urlParams = new URLSearchParams(queryString).get('def');
162
163    $.ajax({
164        url: dev ? "http://127.0.0.1:5000/getMeta" : "https://z4tfy2r5kc.execute-api.us-west-2.amazonaws.com/dev/getMeta",
165        //force to handle it as text
166        dataType: "text",
167        type: "POST",
168        crossDomain: true,
169        data: "none",
170        success: function(data) {
171            var json = $.parseJSON(data);
172            console.log("meta", json)
173            var defenses = json.data
174            var offenses = Object.entries(json.offenseData)
175            var totalSize = json.totalSize
176
177            $('#intro').html(`This app tracks data from ${totalSize.toLocaleString("en-US")} attacks from top 50 ranked guild wars. Latest update: ${new Date(json.maxTimestamp*1000).toDateString()}.`)
178
179            if (urlParams) {
180                return;
181            }
182
183            defenses.sort((a, b) => (b.w+b.l) - (a.w+a.l))
184            offenses.sort((a, b) => (b[1].w+b[1].l) - (a[1].w+a[1].l))
185
186            var html = "</br></br><h2>Top 50 most common meta defenses in past 4 weeks</h2>";
187            for (var i = 0; i < 50; i++) {
188                var defense = defenses[i];
189                var percent = (defense.w/(defense.l + defense.w) * 100).toFixed(1);
190
191                html +=
192`
193<div class="resultRow">
194    <div class="imageRow">
195            <a href="${"gw-meta.html?def=" + defense.defense.split(",").map(x => heroesById[x]).join(",")}">
196            <div class="metaFightIcons">
197                ${imgHtml(defense.defense)}
198                <div class="vSpace"></div>
199            </div>
200            </a>
201        <div class="resultsContainer">
202            <div class="metaResults W">
203                ${defense.w}
204            </div>
205            <img class="metaAtkImg" src="battle_pvp_icon_def.png"></img>
206
207            <div class="metaResults L">
208                ${defense.l}
209            </div>
210            <img class="metaAtkImg" src="battle_pvp_icon_defeat.png"></img>
211
212            <div class="metaResultsPercent">
213                ${isNaN(percent) ? "No results" : percent + " %"}
214            </div>
215        </div>
216    </div>
217</div>
218`
219            }
220
221            html += "</br></br><h2>Top 50 most common meta offense units in past 4 weeks</h2>"
222
223            for (var i = 0; i < 50; i++) {
224                var offenseName = offenses[i][0];
225                var offenseWL = offenses[i][1];
226                var percent = (offenseWL.w/(offenseWL.l + offenseWL.w) * 100).toFixed(1);
227                // console.log(percent)
228                // console.log(offenseWL)
229                html +=
230`
231<div class="resultRow">
232    <div class="imageRow">
233            <div class="metaFightIconsOffense">
234                ${imgHtml(offenseName)}
235                <div class="vSpace"></div>
236            </div>
237            </a>
238        <div class="resultsContainer">
239            <div class="metaResults W">
240                ${offenseWL.w}
241            </div>
242            <img class="metaAtkImg" src="battle_pvp_icon_win.png"></img>
243
244            <div class="metaResults L">
245                ${offenseWL.l}
246            </div>
247            <img class="metaAtkImg" src="battle_pvp_icon_lose.png"></img>
248
249            <div class="metaResultsPercent">
250                ${isNaN(percent) ? "No results" : percent + " %"}
251            </div>
252        </div>
253    </div>
254</div>
255`
256
257            }
258
259            $('#metaRows').html(html)
260        }
261    })
262}
263
264function formatHeroList(hero) {
265    if (!hero.id) {
266        return hero.text
267    }
268    var output = $(`<div class="searchRowContainer"><img src="${heroData[hero.text].assets.icon}" class="heroSearchIcon" />${hero.text}</div>`);
269
270    return output;
271};
272
273function search() {
274    heroes = [
275        $('#heroSelector0').select2('data')[0],
276        $('#heroSelector1').select2('data')[0],
277        $('#heroSelector2').select2('data')[0]
278    ]
279    var defenseKey = heroes.map(x => x.id).sort()
280    // console.log("defkey", defenseKey);
281
282    $('#resultRows').html("Loading..")
283    var defenseHtml = imgHtml(defenseKey.join(","))
284    $('#defenseIcons').html("<br/>" + defenseHtml)
285
286
287    var names = defenseKey.map(x => heroesById[x]).join(",")
288    window.history.replaceState(null, null, "?def=" + names);
289
290    $.ajax({
291        url: dev ? "http://127.0.0.1:5000/getDef" : "https://z4tfy2r5kc.execute-api.us-west-2.amazonaws.com/dev/getDef",
292        //force to handle it as text
293        dataType: "text",
294        type: "POST",
295        crossDomain: true,
296        data: defenseKey.join(","),
297        success: function(data) {
298            //data downloaded so we call parseJSON function
299            //and pass downloaded data
300            var json = $.parseJSON(data);
301            //now json variable contains data in json format
302            //let's display a few items
303            console.log("getDefResponse", json);
304
305            offenseComps = json.data;
306
307            if (!offenseComps) {
308                $('#resultRows').html("No results")
309                return
310            }
311
312
313            include = $('#heroSelector3').select2('data')[0].id
314            exclude = $('#heroSelector4').select2('data')[0].id
315            // console.log("filter", include)
316            // console.log("exclude", exclude)
317            // var offenses = {}
318            // for (var fight of fights) {
319            //     if (!offenses[fight.offense]) {
320            //         offenses[fight.offense] = []
321            //     }
322            //     offenses[fight.offense].push(fight)
323            // }
324
325            // offenses = Object.keys(offenses).map(x => ({
326            //     offense: x,
327            //     fights: offenses[x]
328            // }))
329
330            offenses = Object.entries(offenseComps).sort(function compare(a, b) {
331                if (a[1].w + a[1].l < b[1].w + b[1].l)
332                    return 1;
333                if (a[1].w + a[1].l > b[1].w + b[1].l)
334                    return -1;
335                return 0;
336            }).filter(x => {
337                if (include.length == 0)
338                    return true
339                else
340                    return x[0].includes(include);
341            }).filter(x => {
342                if (exclude.length == 0)
343                    return true
344                else
345                    return !x[0].includes(exclude);
346            })
347
348            $('#resultRows').html("")
349
350            // console.log("offenses", offenses)
351
352            var html = ""
353
354            for (var i = 0; i < Math.min(100, offenses.length); i++) {
355            // for (var offense of offenses) {
356                var offense = offenses[i]
357
358                var percent = (offense[1].w/(offense[1].l + offense[1].w) * 100).toFixed(1);
359
360                html +=
361// `
362//         <div class="resultRow">
363//             <div class="imageRow">
364//                 <div class="fightIcons">
365//                     ${imgHtml(offense[0])}
366//                 </div>
367//                 <div class="resultsContainer">
368//                     <div class="results W">${offense[1].w}W</div>
369//                     <div class="results L">${offense[1].l}L</div>
370//                 </div>
371//                 <div class="metaResultsPercent">
372//                     ${isNaN(percent) ? "No results" : percent + " %"}
373//                 </div>
374//             </div>
375//         </div>
376// `
377                `
378<div class="resultRow">
379    <div class="imageRow">
380        <div class="metaFightLookup">
381            ${imgHtml(offense[0])}
382        </div>
383        <div class="resultsContainer">
384            <div class="metaResults W">
385                ${offense[1].w}
386            </div>
387            <img class="metaAtkImg" src="battle_pvp_icon_win.png"></img>
388
389            <div class="metaResults L">
390                ${offense[1].l}
391            </div>
392            <img class="metaAtkImg" src="battle_pvp_icon_lose.png"></img>
393
394            <div class="metaResultsPercent">
395                ${isNaN(percent) ? "No results" : percent + " %"}
396            </div>
397        </div>
398    </div>
399</div>
400`
401            }
402
403            $('#resultRows').html(html)
404            $('#metaRows').html("")
405        }
406    });
407}
408
409function buildDefSearch() {
410    heroes = [
411        $('#buildDefHeroSelector0').select2('data')[0],
412        $('#buildDefHeroSelector1').select2('data')[0],
413        $('#buildDefHeroSelector2').select2('data')[0]
414    ]
415
416    var defenseKey = heroes.map(x => x.id).sort()
417    // console.log("defkey", defenseKey);
418
419    $('#buildDefResultRows').html("Loading..")
420    var defenseHtml = imgHtml(defenseKey.join(","))
421    $('#buildDefDefenseIcons').html("<br/>" + defenseHtml)
422
423
424    var names = defenseKey.map(x => heroesById[x]).join(",")
425
426
427    $.ajax({
428        url: dev ? "http://127.0.0.1:5000/buildDef" : "https://z4tfy2r5kc.execute-api.us-west-2.amazonaws.com/dev/buildDef",
429        //force to handle it as text
430        dataType: "text",
431        type: "POST",
432        crossDomain: true,
433        data: defenseKey.join(","),
434        success: function(data) {
435            //data downloaded so we call parseJSON function
436            //and pass downloaded data
437            var json = $.parseJSON(data);
438            //now json variable contains data in json format
439            //let's display a few items
440            console.log("getDefResponse", json);
441
442            offenseComps = json.data;
443
444            if (!offenseComps) {
445                $('#buildDefResultRows').html("No results")
446                return
447            }
448
449
450            include = $('#buildDefHeroSelector3').select2('data')[0].id
451            exclude = $('#buildDefHeroSelector4').select2('data')[0].id
452            // console.log("filter", include)
453            // console.log("exclude", exclude)
454            // var offenses = {}
455            // for (var fight of fights) {
456            //     if (!offenses[fight.offense]) {
457            //         offenses[fight.offense] = []
458            //     }
459            //     offenses[fight.offense].push(fight)
460            // }
461
462            // offenses = Object.keys(offenses).map(x => ({
463            //     offense: x,
464            //     fights: offenses[x]
465            // }))
466
467            offenses = Object.entries(offenseComps).sort(function compare(a, b) {
468                if (a[1].w + a[1].l < b[1].w + b[1].l)
469                    return 1;
470                if (a[1].w + a[1].l > b[1].w + b[1].l)
471                    return -1;
472                return 0;
473            }).filter(x => {
474                if (include.length == 0)
475                    return true
476                else
477                    return x[0].includes(include);
478            }).filter(x => {
479                if (exclude.length == 0)
480                    return true
481                else
482                    return !x[0].includes(exclude);
483            })
484
485            $('#buildDefResultRows').html("")
486
487            // console.log("offenses", offenses)
488
489            var html = ""
490
491            for (var i = 0; i < Math.min(200, offenses.length); i++) {
492            // for (var offense of offenses) {
493                var offense = offenses[i]
494
495                var percent = (offense[1].l/(offense[1].l + offense[1].w) * 100).toFixed(1);
496
497                html +=
498// `
499//         <div class="resultRow">
500//             <div class="imageRow">
501//                 <div class="fightIcons">
502//                     ${imgHtml(offense[0])}
503//                 </div>
504//                 <div class="resultsContainer">
505//                     <div class="results W">${offense[1].w}W</div>
506//                     <div class="results L">${offense[1].l}L</div>
507//                 </div>
508//                 <div class="metaResultsPercent">
509//                     ${isNaN(percent) ? "No results" : percent + " %"}
510//                 </div>
511//             </div>
512//         </div>
513// `
514                `
515<div class="resultRow">
516    <div class="imageRow">
517        <div class="metaFightLookup">
518
519
520            <a href="${"gw-meta.html?def=" + offense[0].split(",").map(x => heroesById[x]).join(",")}">
521            <div class="metaFightIcons">
522                ${imgHtml(offense[0])}
523                <div class="vSpace"></div>
524            </div>
525            </a>
526        </div>
527        <div class="resultsContainer">
528            <div class="metaResults W">
529                ${offense[1].l}
530            </div>
531            <img class="metaAtkImg" src="battle_pvp_icon_def.png"></img>
532
533            <div class="metaResults L">
534                ${offense[1].w}
535            </div>
536            <img class="metaAtkImg" src="battle_pvp_icon_defeat.png"></img>
537
538            <div class="metaResultsPercent">
539                ${isNaN(percent) ? "No results" : percent + " %"}
540            </div>
541        </div>
542    </div>
543</div>
544`
545            }
546
547            $('#buildDefResultRows').html(html)
548            $('#metaRows').html("")
549        }
550    });
551}
552
553async function fetchCache(url) {
554    console.log("Fetching from url: " + url);
555    var myHeaders = new Headers();
556    myHeaders.append('pragma', 'no-cache');
557    myHeaders.append('cache-control', 'no-cache');
558
559    const response = await fetch(url, {
560        method: 'GET',
561        headers: myHeaders,
562        mode: 'cors', // no-cors, *cors, same-origin
563    });
564    const text = await response.text();
565    const json = JSON.parse(text);
566    console.log("Finished fetching from url: " + url);
567
568    return json;
569}
570
571function sortByAttribute (arr, attributeStr) {
572    function compare(a, b) {
573        if (a[attributeStr] < b[attributeStr])
574            return -1;
575        if (a[attributeStr] > b[attributeStr])
576            return 1;
577        return 0;
578    }
579
580    return arr.sort(compare);
581}
582
583var questionCircle = "https://raw.githubusercontent.com/fribbels/Fribbels-Epic-7-Optimizer/main/data/cachedimages/question_circle.png";
584function imgHtml(offenseStr) {
585    var heroIds = offenseStr.split(",")
586    var heroNames = heroIds.map(x => heroesById[x])
587    var imgHtml = heroNames.map(x => {
588        return heroData[x] ?
589        `<img class="portrait" title="${x}" src=${heroData[x].assets.icon}></img>` :
590        `<img class="portrait" src=${questionCircle}></img>`
591    })
592
593    return imgHtml.join(`<div class="vSpace"></div>`)
594}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.