From e3d6f7b3a77bacbbcb26dd99fe23793205a754aa Mon Sep 17 00:00:00 2001 From: Sarjuuk Date: Mon, 3 Nov 2025 18:00:50 +0100 Subject: [PATCH] Profiler/Completions * show completion info for claimed characters in infobox on appropriate db pages --- endpoints/achievement/achievement.php | 7 +- endpoints/faction/faction.php | 9 +- endpoints/item/item.php | 5 +- endpoints/quest/quest.php | 10 +- endpoints/spell/spell.php | 14 +- endpoints/title/title.php | 7 +- .../frontend/infoboxmarkup.class.php | 6 +- includes/dbtypes/item.class.php | 5 + includes/dbtypes/quest.class.php | 8 + includes/dbtypes/spell.class.php | 5 +- includes/user.class.php | 53 ++++ setup/sql/updates/1762199391_01.sql | 1 + .../templates/global.js/listview_templates.js | 286 +++++++++++++++++- .../filegen/templates/global.js/profiler.js | 134 ++++++++ .../tools/filegen/templates/global.js/wow.js | 6 + setup/tools/filegen/templates/power.js.in | 43 +++ static/css/aowow.css | 30 ++ static/js/Profiler.js | 13 +- static/js/locale_dede.js | 6 + static/js/locale_enus.js | 6 + static/js/locale_eses.js | 6 + static/js/locale_frfr.js | 6 + static/js/locale_ruru.js | 6 + static/js/locale_zhcn.js | 6 + template/bricks/infobox.tpl.php | 1 + 25 files changed, 640 insertions(+), 39 deletions(-) create mode 100644 setup/sql/updates/1762199391_01.sql diff --git a/endpoints/achievement/achievement.php b/endpoints/achievement/achievement.php index 7ab7fb86..1068fe42 100644 --- a/endpoints/achievement/achievement.php +++ b/endpoints/achievement/achievement.php @@ -124,10 +124,7 @@ class AchievementBaseResponse extends TemplateResponse implements ICache $y = DB::Aowow()->selectCell('SELECT COUNT(1) FROM ?_profiler_profiles WHERE `realm` IS NOT NULL AND `realmGUID` IS NOT NULL AND `cuFlags` & ?d = 0', PROFILER_CU_NEEDS_RESYNC); $infobox[] = Lang::profiler('attainedBy', [round(($x ?: 0) * 100 / ($y ?: 1))]); - // - js component missing; - // - can't yet assign styles to li element - // if (User::getPinnedCharacter()) - // $infobox[] = Lang::profiler('completion') . '[span class="compact-completion-display"][/span]'; // [li style="display:none"]...[/li] + // completion row added by InfoboxMarkup } // original name @@ -135,7 +132,7 @@ class AchievementBaseResponse extends TemplateResponse implements ICache $infobox[] = Util::ucFirst(Lang::lang(Locale::EN->value) . Lang::main('colon')) . '[copy button=false]'.$this->subject->getField('name_loc0').'[/copy][/li]'; if ($infobox) - $this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0'); + $this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0', !($this->subject->getField('flags') & ACHIEVEMENT_FLAG_COUNTER)); /**********/ diff --git a/endpoints/faction/faction.php b/endpoints/faction/faction.php index 84ba340f..2ca7ca92 100644 --- a/endpoints/faction/faction.php +++ b/endpoints/faction/faction.php @@ -106,18 +106,15 @@ class FactionBaseResponse extends TemplateResponse implements ICache $y = DB::Aowow()->selectCell('SELECT COUNT(1) FROM ?_profiler_profiles WHERE `realm` IS NOT NULL AND `realmGUID` IS NOT NULL AND `cuFlags` & ?d = 0', PROFILER_CU_NEEDS_RESYNC); $infobox[] = Lang::profiler('attainedBy', [round(($x ?: 0) * 100 / ($y ?: 1))]); - // - js component missing; - // - can't yet assign styles to li element - // if (User::getPinnedCharacter()) - // $infobox[] = Lang::profiler('completion') . '[span class="compact-completion-display"][/span]'; // [li style="display:none"]...[/li] + // completion row added by InfoboxMarkup } // original name if (Lang::getLocale() != Locale::EN) $infobox[] = Util::ucFirst(Lang::lang(Locale::EN->value) . Lang::main('colon')) . '[copy button=false]'.$this->subject->getField('name_loc0').'[/copy][/li]'; - if ($infobox) - $this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0'); + if ($infobox) // unsure if this should be tracked (needs data dump in User::getCompletion()) + $this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0', 0); /****************/ diff --git a/endpoints/item/item.php b/endpoints/item/item.php index d6582962..34d94c1b 100644 --- a/endpoints/item/item.php +++ b/endpoints/item/item.php @@ -319,12 +319,15 @@ class ItemBaseResponse extends TemplateResponse implements ICache if ($_bagFamily & 0x0100) $infobox[] = Lang::item('atKeyring'); + // completion row added by InfoboxMarkup + // original name if (Lang::getLocale() != Locale::EN) $infobox[] = Util::ucFirst(Lang::lang(Locale::EN->value) . Lang::main('colon')) . '[copy button=false]'.$this->subject->getField('name_loc0').'[/copy][/li]'; + $hasCompletion = !($this->subject->getField('cuFlags') & CUSTOM_EXCLUDE_FOR_LISTVIEW) && ($_class == ITEM_CLASS_RECIPE || ($_class == ITEM_CLASS_MISC && in_array($_subClass, [2, 5, -7]))); if ($infobox) - $this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0'); + $this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0', $hasCompletion); /****************/ diff --git a/endpoints/quest/quest.php b/endpoints/quest/quest.php index 4c01b65a..6830704d 100644 --- a/endpoints/quest/quest.php +++ b/endpoints/quest/quest.php @@ -64,6 +64,7 @@ class QuestBaseResponse extends TemplateResponse implements ICache $_flags = $this->subject->getField('flags'); $_specialFlags = $this->subject->getField('specialFlags'); $_side = ChrRace::sideFromMask($this->subject->getField('reqRaceMask')); + $hasCompletion = !($_flags & QUEST_FLAG_UNAVAILABLE || $this->subject->getField('cuFlags') & CUSTOM_EXCLUDE_FOR_LISTVIEW); /*************/ @@ -275,16 +276,13 @@ class QuestBaseResponse extends TemplateResponse implements ICache $infobox[] = Lang::quest('id') . $this->typeId; // profiler relateed (note that this is part of the cache. I don't think this is important enough to calc for every view) - if (Cfg::get('PROFILER_ENABLE') && !($_flags & QUEST_FLAG_UNAVAILABLE || $this->subject->getField('cuFlags') & CUSTOM_EXCLUDE_FOR_LISTVIEW)) + if (Cfg::get('PROFILER_ENABLE') && $hasCompletion) { $x = DB::Aowow()->selectCell('SELECT COUNT(1) FROM ?_profiler_completion_quests WHERE `questId` = ?d', $this->typeId); $y = DB::Aowow()->selectCell('SELECT COUNT(1) FROM ?_profiler_profiles WHERE `realm` IS NOT NULL AND `realmGUID` IS NOT NULL AND `cuFlags` & ?d = 0', PROFILER_CU_NEEDS_RESYNC); $infobox[] = Lang::profiler('attainedBy', [round(($x ?: 0) * 100 / ($y ?: 1))]); - // - js component missing; - // - can't yet assign styles to li element - // if (User::getPinnedCharacter()) - // $infobox[] = Lang::profiler('completion') . '[span class="compact-completion-display"][/span]'; // [li style="display:none"]...[/li] + // completion row added by InfoboxMarkup } // original name @@ -292,7 +290,7 @@ class QuestBaseResponse extends TemplateResponse implements ICache $infobox[] = Util::ucFirst(Lang::lang(Locale::EN->value) . Lang::main('colon')) . '[copy button=false]'.$this->subject->getField('name_loc0').'[/copy][/li]'; if ($infobox) - $this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0'); + $this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0', $hasCompletion); /*******************/ diff --git a/endpoints/spell/spell.php b/endpoints/spell/spell.php index 28bafde2..5508beff 100644 --- a/endpoints/spell/spell.php +++ b/endpoints/spell/spell.php @@ -2352,8 +2352,9 @@ class SpellBaseResponse extends TemplateResponse implements ICache private function createInfobox() : void { - $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags')); - $typeCat = $this->subject->getField('typeCat'); + $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags')); + $typeCat = $this->subject->getField('typeCat'); + $hasCompletion = in_array($typeCat, [-5, -6]) && !($this->subject->getField('cuFlags') & CUSTOM_EXCLUDE_FOR_LISTVIEW); // level if (!in_array($typeCat, [-5, -6])) // not mount or vanity pet @@ -2445,16 +2446,13 @@ class SpellBaseResponse extends TemplateResponse implements ICache } // profiler relateed (note that this is part of the cache. I don't think this is important enough to calc for every view) - if (Cfg::get('PROFILER_ENABLE') && in_array($this->subject->getField('typeCat'), [-5, -6]) && !($this->subject->getField('cuFlags') & CUSTOM_EXCLUDE_FOR_LISTVIEW)) + if (Cfg::get('PROFILER_ENABLE') && $hasCompletion) { $x = DB::Aowow()->selectCell('SELECT COUNT(1) FROM ?_profiler_completion_spells WHERE `spellId` = ?d', $this->typeId); $y = DB::Aowow()->selectCell('SELECT COUNT(1) FROM ?_profiler_profiles WHERE `realm` IS NOT NULL AND `realmGUID` IS NOT NULL AND `cuFlags` & ?d = 0', PROFILER_CU_NEEDS_RESYNC); $infobox[] = Lang::profiler('attainedBy', [round(($x ?: 0) * 100 / ($y ?: 1))]); - // - js component missing; - // - can't yet assign styles to li element - // if (User::getPinnedCharacter()) - // $infobox[] = Lang::profiler('completion') . '[span class="compact-completion-display"][/span]'; // [li style="display:none"]...[/li] + // completion row added by InfoboxMarkup } // original name @@ -2484,7 +2482,7 @@ class SpellBaseResponse extends TemplateResponse implements ICache $infobox[] = 'Script'.Lang::main('colon').$_; - $this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0'); + $this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0', $hasCompletion); // append glyph symbol if available $glyphId = 0; diff --git a/endpoints/title/title.php b/endpoints/title/title.php index 7bc4ae01..1c935196 100644 --- a/endpoints/title/title.php +++ b/endpoints/title/title.php @@ -94,10 +94,7 @@ class TitleBaseResponse extends TemplateResponse implements ICache $y = DB::Aowow()->selectCell('SELECT COUNT(1) FROM ?_profiler_profiles WHERE `realm` IS NOT NULL AND `realmGUID` IS NOT NULL AND `cuFlags` & ?d = 0', PROFILER_CU_NEEDS_RESYNC); $infobox[] = Lang::profiler('attainedBy', [round(($x ?: 0) * 100 / ($y ?: 1))]); - // - js component missing; - // - can't yet assign styles to li element - // if (User::getPinnedCharacter()) - // $infobox[] = Lang::profiler('completion') . '[span class="compact-completion-display"][/span]'; // [li style="display:none"]...[/li] + // completion row added by InfoboxMarkup } // original name @@ -105,7 +102,7 @@ class TitleBaseResponse extends TemplateResponse implements ICache $infobox[] = Util::ucFirst(Lang::lang(Locale::EN->value) . Lang::main('colon')) . '[copy button=false]'.$this->subject->getField('name_loc0').'[/copy][/li]'; if ($infobox) - $this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0'); + $this->infobox = new InfoboxMarkup($infobox, ['allow' => Markup::CLASS_STAFF, 'dbpage' => true], 'infobox-contents0', 1); /****************/ diff --git a/includes/components/frontend/infoboxmarkup.class.php b/includes/components/frontend/infoboxmarkup.class.php index bf6d5d18..dff22075 100644 --- a/includes/components/frontend/infoboxmarkup.class.php +++ b/includes/components/frontend/infoboxmarkup.class.php @@ -8,7 +8,7 @@ if (!defined('AOWOW_REVISION')) class InfoboxMarkup extends Markup { - public function __construct(private array $items, array $opts, string $parent = '') + public function __construct(private array $items, array $opts, string $parent = '', private int $completionRowType = 0) { parent::__construct('', $opts, $parent); } @@ -31,6 +31,10 @@ class InfoboxMarkup extends Markup public function __toString() : string { + // inject before output to avoid adding it to cache + if ($this->completionRowType && User::getCharacters()) + $this->items[] = [Lang::profiler('completion') . '[span class="compact-completion-display"][/span]', ['style' => 'display:none']]; + if ($_ = $this->prepare()) $this->replace($_); diff --git a/includes/dbtypes/item.class.php b/includes/dbtypes/item.class.php index 10c4fc1d..ceca727c 100644 --- a/includes/dbtypes/item.class.php +++ b/includes/dbtypes/item.class.php @@ -487,6 +487,11 @@ class ItemList extends DBTypeList 'quality' => $this->curTpl['quality'], 'icon' => $this->curTpl['iconString'] ); + + if ($this->curTpl['class'] == ITEM_CLASS_RECIPE) + $data[Type::ITEM][$id]['completion_category'] = $this->curTpl['class']; + else if ($this->curTpl['class'] == ITEM_CLASS_MISC && in_array($this->curTpl['subClass'], [2, 5, -7])) + $data[Type::ITEM][$id]['completion_category'] = $this->curTpl['class'].'-'.$this->curTpl['subClass']; } if ($addMask & GLOBALINFO_EXTRA) diff --git a/includes/dbtypes/quest.class.php b/includes/dbtypes/quest.class.php index f7798935..7754a173 100644 --- a/includes/dbtypes/quest.class.php +++ b/includes/dbtypes/quest.class.php @@ -415,7 +415,15 @@ class QuestList extends DBTypeList } if ($addMask & GLOBALINFO_SELF) + { $data[Type::QUEST][$this->id] = ['name' => $this->getField('name', true)]; + + if ($this->curTpl['flags'] & QUEST_FLAG_DAILY) + $data[Type::QUEST][$this->id]['daily'] = true; + + if ($this->curTpl['flags'] & QUEST_FLAG_WEEKLY) + $data[Type::QUEST][$this->id]['weekly'] = true; + } } return $data; diff --git a/includes/dbtypes/spell.class.php b/includes/dbtypes/spell.class.php index 70d6e46f..aabf72e0 100644 --- a/includes/dbtypes/spell.class.php +++ b/includes/dbtypes/spell.class.php @@ -2150,8 +2150,11 @@ class SpellList extends DBTypeList { $data[Type::SPELL][$id] = array( 'icon' => $this->curTpl['iconStringAlt'] ?: $this->curTpl['iconString'], - 'name' => $this->getField('name', true), + 'name' => $this->getField('name', true) ); + + if (($_ = $this->curTpl['typeCat']) && in_array($_, [-5, -6, 9, 11])) + $data[Type::SPELL][$id]['completion_category'] = $_; } if ($addMask & GLOBALINFO_EXTRA) diff --git a/includes/user.class.php b/includes/user.class.php index 12928616..8ebd6261 100644 --- a/includes/user.class.php +++ b/includes/user.class.php @@ -569,6 +569,7 @@ class User $gUser['downvoteRep'] = Cfg::get('REP_REQ_DOWNVOTE'); $gUser['upvoteRep'] = Cfg::get('REP_REQ_UPVOTE'); $gUser['characters'] = self::getCharacters(); + $gUser['completion'] = self::getCompletion(); $gUser['excludegroups'] = self::$excludeGroups; if (self::$debug) @@ -723,6 +724,58 @@ class User return $data; } + + public static function getCompletion() : array + { + $ids = []; + foreach (self::$profiles->iterate() as $_) + if (!self::$profiles->isCustom()) + $ids[] = self::$profiles->id; + + if (!$ids) + return []; + + $completion = []; + + $x = DB::Aowow()->selectCol('SELECT `id` AS ARRAY_KEY, `questId` AS ARRAY_KEY2, `questId` FROM ?_profiler_completion_quests WHERE `id` IN (?a)', $ids); + $completion[Type::QUEST] = $x ? array_map(array_values(...), $x) : []; + + $x = DB::Aowow()->selectCol('SELECT `id` AS ARRAY_KEY, `achievementId` AS ARRAY_KEY2, `achievementId` FROM ?_profiler_completion_achievements WHERE `id` IN (?a)', $ids); + $completion[Type::ACHIEVEMENT] = $x ? array_map(array_values(...), $x) : []; + + $x = DB::Aowow()->selectCol('SELECT `id` AS ARRAY_KEY, `titleId` AS ARRAY_KEY2, `titleId` FROM ?_profiler_completion_titles WHERE `id` IN (?a)', $ids); + $completion[Type::TITLE] = $x ? array_map(array_values(...), $x) : []; + + $completion[Type::ITEM] = []; + + $spells = DB::Aowow()->select( + 'SELECT pcs.`id` AS ARRAY_KEY, pcs.`spellId` AS ARRAY_KEY2, pcs.`spellId`, i.`id` AS "itemId" + FROM ?_spell s + JOIN ?_profiler_completion_spells pcs ON s.`id` = pcs.`spellId` + LEFT JOIN ?_items i ON i.spellId1 IN (?a) AND i.spellId2 = pcs.spellId + WHERE s.`typeCat` IN (?a) AND pcs.`id` IN (?a)', + LEARN_SPELLS, [-5, -6, 9, 11], $ids + ); + + if ($spells) + { + $completion[Type::SPELL] = array_map(fn($x) => array_column($x, 'spellId'), $spells); + + if ($recipes = array_map(fn($x) => array_filter(array_column($x, 'itemId')), $spells)) + foreach ($ids as $id) // array_merge_recursive does not respect numeric keys + $completion[Type::ITEM][$id] = array_merge($completion[Type::ITEM][$id] ?? [], $recipes[$id] ?? []); + } + else + $completion[Type::SPELL] = []; + + // init empty result sets + foreach ($completion as &$c) + foreach ($ids as $id) + if (!isset($c[$id])) + $c[$id] = []; + + return $completion; + } } ?> diff --git a/setup/sql/updates/1762199391_01.sql b/setup/sql/updates/1762199391_01.sql new file mode 100644 index 00000000..168a5f1b --- /dev/null +++ b/setup/sql/updates/1762199391_01.sql @@ -0,0 +1 @@ +UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' globaljs tooltips'); diff --git a/setup/tools/filegen/templates/global.js/listview_templates.js b/setup/tools/filegen/templates/global.js/listview_templates.js index 2dc8aca2..4f3b1d92 100644 --- a/setup/tools/filegen/templates/global.js/listview_templates.js +++ b/setup/tools/filegen/templates/global.js/listview_templates.js @@ -905,6 +905,43 @@ Listview.templates = { var _ = Listview.funcBox.getItemType; return $WH.strcmp(_(a.classs, a.subclass, a.subsubclass).text, _(b.classs, b.subclass, b.subsubclass).text); } + }, + { + id: 'completed', // Listview.COLUMN_ID_COMPLETION + name: LANG.completion, // WH.TERMS.completion + hidden: true, + compute: function (item, td) + { + var skip = !item.hasOwnProperty('classs') || !Listview.templates.item._validCompletionCategory(item.classs, item.subclass, item.quality); + $WH.addCompletionIcons(td, 3, item.id, skip); + }, + sortFunc: function (a, b) + { + // return $WH.stringCompare( + return $WH.strcmp( + $WH.getCompletionFlags(3, a.id), + $WH.getCompletionFlags(3, b.id) + ); + }, + getValue: function (item) { + var value = 0; + var completionData = g_user.completion?.hasOwnProperty(3) ? g_user.completion[3] : {}; + + if (!item.hasOwnProperty('classs') || !Listview.templates.item._validCompletionCategory(item.classs, item.subclass, item.quality)) + return -1; + + for (var i in g_user.characters) + { + var profile = g_user.characters[i]; + if (!(profile.id in completionData)) + continue; + + if ($WH.in_array(completionData[profile.id], item.id) != -1) + value++; + } + + return value; + } } ], @@ -912,6 +949,12 @@ Listview.templates = { return item.name.charAt(0) == '@' ? 'javascript:;' : '?item=' + item.id; }, + _validCompletionCategory: function (classs, subclass, quality) { + return $WH.in_array(g_completion_categories[3], classs) != -1 || + $WH.in_array(g_completion_categories[3], '' + classs + '-' + subclass) != -1 || + $WH.in_array(g_completion_categories[3], '' + classs + 'q' + quality) != -1 + }, + onBeforeCreate: function() { var nComparable = false; @@ -930,6 +973,23 @@ Listview.templates = { this.mode = Listview.MODE_CHECKBOX; this._nComparable = nComparable; } + + for (var i in this.columns) + { + if (this.columns[i].id == 'completed' && this.columns[i].hidden) + { + if ($WH.isset('g_user') && 'characters' in g_user && $WH.in_array(this.hiddenCols, this.columns[i].id) == -1) + { + var n = 0; + for (var j in this.data) + if (this.data[j].hasOwnProperty('classs') && Listview.templates.item._validCompletionCategory(this.data[j].classs, this.data[j].subclass, this.data[j].quality)) + n++; + + if (n > this.data.length * 0.1) + this.visibility.push(parseInt(i)); + } + } + } }, createCbControls: function(div, topBar) { @@ -1148,7 +1208,7 @@ Listview.templates = { } } else { - return - 1; + return -1; } }, sortFunc: function(a, b, col) { @@ -1902,11 +1962,69 @@ Listview.templates = { var _ = Listview.funcBox.getQuestCategory; return $WH.strcmp(_(a.category), _(b.category)); } + }, + { + id: 'completed', // Listview.COLUMN_ID_COMPLETION + name: LANG.completion, // WH.TERMS.completion + hidden: true, + compute: function (quest, td) + { + if (quest.daily || quest.weekly) + return; + + $WH.addCompletionIcons(td, 5, quest.id) + }, + sortFunc: function (a, b) + { + // return $WH.stringCompare( + return $WH.strcmp( + $WH.getCompletionFlags(5, a.id), + $WH.getCompletionFlags(5, b.id) + ); + }, + getValue: function (quest) { + var value = 0; + var completionData = g_user.completion?.hasOwnProperty(5) ? g_user.completion[5] : {}; + + if (quest.daily || quest.weekly) + return -1; + + for (var i in g_user.characters) + { + var profile = g_user.characters[i]; + if (!(profile.id in completionData)) + continue; + + if ($WH.in_array(completionData[profile.id], quest.id) != -1) + value++; + } + + return value; + } } ], getItemLink: function(quest) { return '?quest=' + quest.id; + }, + + onBeforeCreate: function () { + for (var i in this.columns) + { + if (this.columns[i].id == 'completed' && this.columns[i].hidden) + { + if ($WH.isset('g_user') && 'characters' in g_user && $WH.in_array(this.hiddenCols, this.columns[i].id) == -1) + { + var n = 0; + for (var j in this.data) + if (!this.data[j].daily && !this.data[j].weekly) + n++; + + if (n > this.data.length * 0.1) + this.visibility.push(parseInt(i)); + } + } + } } }, @@ -3432,12 +3550,74 @@ Listview.templates = { return 0; } - } + }, /* AoWoW: custom end */ + { + id: 'completed', // Listview.COLUMN_ID_COMPLETION + name: LANG.completion, // WH.TERMS.completion + hidden: true, + compute: function (spell, td) + { + if (!spell.hasOwnProperty('cat') || !Listview.templates.spell._validCompletionCategory(spell.cat)) + return; + + $WH.addCompletionIcons(td, 6, spell.id) + }, + sortFunc: function (a, b) + { + // return $WH.stringCompare( + return $WH.strcmp( + $WH.getCompletionFlags(6, a.id), + $WH.getCompletionFlags(6, b.id) + ); + }, + getValue: function (spell) { + var value = 0; + var completionData = g_user.completion?.hasOwnProperty(6) ? g_user.completion[6] : {}; + + if (!spell.hasOwnProperty('cat') || !Listview.templates.spell._validCompletionCategory(spell.cat)) + return -1; + + for (var i in g_user.characters) + { + var profile = g_user.characters[i]; + if (!(profile.id in completionData)) + continue; + + if ($WH.in_array(completionData[profile.id], spell.id) != -1) + value++; + } + + return value; + } + } ], getItemLink: function(spell) { return '?spell=' + spell.id; + }, + + _validCompletionCategory: function (category) { + return $WH.in_array(g_completion_categories[6], category) != -1; + }, + + onBeforeCreate: function () { + for (var i in this.columns) + { + if (this.columns[i].id == 'completed' && this.columns[i].hidden) + { + if ($WH.isset('g_user') && 'characters' in g_user && $WH.in_array(this.hiddenCols, this.columns[i].id) == -1) + { + var n = 0; + for (var j in this.data) + if (this.data[j].hasOwnProperty('cat') && Listview.templates.spell._validCompletionCategory(this.data[j].cat)) + n++; + + if (n > this.data.length * 0.1) + this.visibility.push(parseInt(i)); + } + } + } } }, @@ -5834,11 +6014,69 @@ Listview.templates = { return $WH.strcmp(g_achievement_categories[a.category], g_achievement_categories[b.category]); }, hidden: true + }, + { + id: 'completed', // Listview.COLUMN_ID_COMPLETION + name: LANG.completion, // WH.TERMS.completion + hidden: true, + compute: function (achievement, td) + { + if (achievement.type) + return; + + $WH.addCompletionIcons(td, 10, achievement.id) + }, + sortFunc: function (a, b) + { + // return $WH.stringCompare( + return $WH.strcmp( + $WH.getCompletionFlags(10, a.id), + $WH.getCompletionFlags(10, b.id) + ); + }, + getValue: function (achievement) { + var value = 0; + var completionData = g_user.completion?.hasOwnProperty(10) ? g_user.completion[10] : {}; + + if (achievement.type) + return -1; + + for (var i in g_user.characters) + { + var profile = g_user.characters[i]; + if (!(profile.id in completionData)) + continue; + + if ($WH.in_array(completionData[profile.id], achievement.id) != -1) + value++; + } + + return value; + } } ], getItemLink: function(achievement) { return '?achievement=' + achievement.id; + }, + + onBeforeCreate: function () { + for (var i in this.columns) + { + if (this.columns[i].id == 'completed' && this.columns[i].hidden) + { + if ($WH.isset('g_user') && 'characters' in g_user && $WH.in_array(this.hiddenCols, this.columns[i].id) == -1) + { + var n = 0; + for (var j in this.data) + if (!this.data[j].type) + n++; + + if (n > this.data.length * 0.1) + this.visibility.push(parseInt(i)); + } + } + } } }, @@ -6052,11 +6290,55 @@ Listview.templates = { return $WH.strcmp(g_title_categories[a.category], g_title_categories[b.category]); }, hidden: true + }, + { + id: 'completed', // Listview.COLUMN_ID_COMPLETION + name: LANG.completion, // WH.TERMS.completion + hidden: true, + compute: function (title, td) + { + $WH.addCompletionIcons(td, 11, title.id) + }, + sortFunc: function (a, b) + { + // return $WH.stringCompare( + return $WH.strcmp( + $WH.getCompletionFlags(11, a.id), + $WH.getCompletionFlags(11, b.id) + ); + }, + getValue: function (title) { + var value = 0; + var completionData = g_user.completion?.hasOwnProperty(11) ? g_user.completion[11] : {}; + + for (var i in g_user.characters) + { + var profile = g_user.characters[i]; + if (!(profile.id in completionData)) + continue; + + if ($WH.in_array(completionData[profile.id], title.id) != -1) + value++; + } + + return value; + } } ], getItemLink: function(title) { return '?title=' + title.id; + }, + + onBeforeCreate: function () { + for (var i in this.columns) + { + if (this.columns[i].id == 'completed' && this.columns[i].hidden) + { + if ($WH.isset('g_user') && 'characters' in g_user && $WH.in_array(this.hiddenCols, this.columns[i].id) == -1) + this.visibility.push(parseInt(i)); + } + } } }, diff --git a/setup/tools/filegen/templates/global.js/profiler.js b/setup/tools/filegen/templates/global.js/profiler.js index 52a23ecc..273ea12f 100644 --- a/setup/tools/filegen/templates/global.js/profiler.js +++ b/setup/tools/filegen/templates/global.js/profiler.js @@ -17,3 +17,137 @@ function g_getProfileUrl(profile) { function g_getProfileRealmUrl(profile) { return '?profiles=' + profile.region + '.' + profile.realm; } + +$WH.prepInfobox = function () { + $('.infobox').each(function () + { + var row = $(this); + if (row.data('infobox-completion-info-added') !== true && g_user.characters && + typeof g_pageInfo == 'object' && typeof g_pageInfo.type == 'number' && typeof g_pageInfo.typeId == 'number') + { + var wardrobe = $('.compact-completion-display', row); + if (wardrobe.length) + { + var completionIcon = $('span', wardrobe); + if (completionIcon.length) + { + var i = 0; + completionIcon.each(function () + { + var e = $(this); + var t = e.html(); + + try { t = JSON.parse(t) } + catch (e) { return } + + var a; + for (var n = 0, s; s = g_user.characters[n]; n++) + { + if (s.id == t.id) + { + a = s; + break + } + } + + if (a) + { + e.parent().append($WH.createCompletionIcon(a, t.completed ? 1 : 0, t.rel)); + e.remove(); + i++ + } + }); + + if (i) + wardrobe.parent().parent().show() + } + else if (wardrobe.is(':empty') && $WH.addCompletionIcons(wardrobe.get(0), g_pageInfo.type, g_pageInfo.typeId)) + wardrobe.parent().parent().show() + } + + row.data('infobox-completion-info-added', true) + } + }); +}; + +$WH.addCompletionIcons = function (parent, type, typeIdOrData, skipIncomplete) { + var nComplete = 0; + + for (var i in g_user.characters) + { + if (!g_user.characters.hasOwnProperty(i)) + continue; + + let profile = g_user.characters[i]; + + let completion = 0; + let completionData = g_user.completion?.hasOwnProperty(type) ? g_user.completion[type] : {}; + if (!completionData.hasOwnProperty(profile.id)) + continue; + + completion = completionData[profile.id].includes(typeIdOrData) ? 1 : 0; + + if (skipIncomplete && !completion) + continue; + + $WH.ae(parent, $WH.createCompletionIcon(profile, completion)); + nComplete++; + } + + return nComplete; +}; + +$WH.createCompletionIcon = function (profile, completePct, rel) { + var icon = $WH.ce('a'); + icon.href = '?profile=' + profile.region + '.' + profile.realm + '.' + profile.name; + + // aowow - so the generic tooltips dont override our completion tooltip + icon.setAttribute('data-disable-wowhead-tooltip', true); + + icon.className = 'progress-icon progress-' + (completePct ? Math.max(1, Math.floor(completePct * 8)) : 0); + $WH.Tooltip.simple(icon, $WH.getCompletionTooltip(profile, completePct), null, true); + + if (rel) + icon.rel = rel; + + return icon; +}; + +$WH.getCompletionTooltip = function (profile, completePct) { + let tooltip = $WH.ce('div'); + $WH.ae(tooltip, $WH.ce('span', { className: 'q' }, $WH.ct((completePct >= 1 ? LANG.complete : LANG.incomplete) + LANG.colon))); + $WH.ae(tooltip, $WH.ce('br')); + + let charRow = $WH.ce('span', { style: { whiteSpace: 'nowrap' } }); + $WH.ae(tooltip, charRow); + $WH.ae(charRow, $WH.ce('b', { className: 'c' + profile.classs }, $WH.ct(profile.name))); + + let server = [' ', profile.realmname]; + + if (profile.hasOwnProperty('region')) + server.push(profile.region.toUpperCase()); + + $WH.ae(charRow, $WH.ce('span', { className: 'q0' }, $WH.ct(server.join(' ')))); + + if (completePct > 0 && completePct < 1) + $WH.ae(charRow, $WH.ct( $WH.sprintf(LANG.parens_format, '', Math.round(completePct * 100) + '%') )); + + return tooltip.innerHTML; +}; + +$WH.getCompletionFlags = function (type, typeId) { + var flags = 0; + var profiles = g_user.characters || []; + let completionData = g_user.completion?.hasOwnProperty(type) ? g_user.completion[type] : {}; + + for (var i = profiles.length - 1; i >= 0; i--) + { + var profile = profiles[i]; + if (!(profile.id in completionData)) + continue + + flags = flags << 1 | (completionData[profile.id].includes(typeId) ? 1 : 0) + } + + return flags; +}; diff --git a/setup/tools/filegen/templates/global.js/wow.js b/setup/tools/filegen/templates/global.js/wow.js index d482e5a7..99323c8b 100644 --- a/setup/tools/filegen/templates/global.js/wow.js +++ b/setup/tools/filegen/templates/global.js/wow.js @@ -269,6 +269,12 @@ var g_types = { 504: 'mail' }; +var g_completion_categories = { + // 1: [12], // NPCs: Battle Pets + 3: [9, "15-2", "15-5", "15--7"], // Items: Recipes, Minipets, Mounts (Ground), Mounts (Flying) + 6: [-5, -6, 9, 11] // Spells: Mounts, Minipets, Sec. Skills, Prim. Skills +}; + // Items $WH.cO(g_items, { add: function(id, json) diff --git a/setup/tools/filegen/templates/power.js.in b/setup/tools/filegen/templates/power.js.in index 717345d7..f6e916f2 100644 --- a/setup/tools/filegen/templates/power.js.in +++ b/setup/tools/filegen/templates/power.js.in @@ -644,6 +644,49 @@ if (typeof $WowheadPower == "undefined") { } } + if (!isRemote && window.g_user && g_user.characters) + { + var completion = ''; + let completionData = g_user.completion.hasOwnProperty(currentType) ? g_user.completion[currentType] : false; + + let entity = {}; + if (currentType == TYPE_QUEST && $WH.isset('g_quests')) + entity = g_quests[currentId] || {}; + if (currentType == TYPE_ACHIEVEMENT && $WH.isset('g_achievements')) + entity = g_achievements[currentId] || {}; + if (currentType == TYPE_ITEM && $WH.isset('g_items')) + entity = g_items[currentId] || {}; + if (currentType == TYPE_SPELL && $WH.isset('g_spells')) + entity = g_spells[currentId] || {}; + + if ((!LOOKUPS[currentType][0] || LOOKUPS[currentType][0][currentId].status[currentLocale] !== STATUS_OK) || + (currentType === TYPE_QUEST && (entity.daily || entity.weekly)) || + (currentType === TYPE_ACHIEVEMENT && entity.type)) + completionData = false; + + let CompetionWithoutCatg = !(completionData && currentType in g_completion_categories && $WH.in_array(g_completion_categories[currentType], entity.completion_category) === -1); + + if (completionData) + { + for (var i in g_user.characters) + { + var profile = g_user.characters[i]; + if (!(profile.id in completionData)) + continue; + + let isComplete = $WH.in_array(completionData[profile.id], currentId) !== - 1; + if (!isComplete && !CompetionWithoutCatg) + continue; + + completion += '
'; + completion += profile.name + ' - ' + profile.realmname + ' ' + profile.region.toUpperCase(); + } + } + + if (completion !== '') + html += '
' + LANG.completion + ':' + completion; + } + if (currentParams.map && map && map.getMap) { html2 = map.getMap(); } diff --git a/static/css/aowow.css b/static/css/aowow.css index 64c3fa45..4befa58b 100644 --- a/static/css/aowow.css +++ b/static/css/aowow.css @@ -4260,3 +4260,33 @@ a.button-red.fa-clipboard > em > span { .fav-star-1 { background-position: -65px center; } + +/* imported from lists */ +.compact-completion-display a { + margin-left: 2px; + vertical-align: text-top; /* middle; */ +} + +.progress-icon { + /* background: url(/images/ListManager/completion.png?4) no-repeat; */ + background: url(../images/ui/check.png) no-repeat -1px -15px; + border-radius: 99px; + display: inline-block; + height: 13px; /* 16px; */ + line-height: 16px; + vertical-align: sub; /* bottom; */ + width: 13px; /* 16px; */ +} + +.progress-icon:focus { + box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5); +} + +/* newer atlas has 9 icons */ +.progress-icon.progress-8 { + background-position: -1px 0px; +} + +.progress-icon.with-text { + padding-left: 16px; +} diff --git a/static/js/Profiler.js b/static/js/Profiler.js index ca60a8bf..2856b7e0 100644 --- a/static/js/Profiler.js +++ b/static/js/Profiler.js @@ -8538,10 +8538,15 @@ function ProfilerCompletion(_parent) { _tabsListview.show((_subtotal[_category].complete[_subcategory] ? 2 : 3)); if (_opt.subname) { - _listview.createIndicator($WH.sprintf(LANG['lvnote_' + _mode + 'ind'], _category, _subcategory, _opt.subname(_subcategory)), Listview.headerFilter.bind(_listview, _listview.columns[_listview.columns.length - 2], '')); - _excluded.createIndicator($WH.sprintf(LANG['lvnote_' + _mode + 'ind'], _category, _subcategory, _opt.subname(_subcategory)), Listview.headerFilter.bind(_excluded, _listview.columns[_listview.columns.length - 2], '')); - setTimeout(Listview.headerFilter.bind(_listview, _listview.columns[_listview.columns.length - 2], _opt.subname(_subcategory)), 1); - setTimeout(Listview.headerFilter.bind(_excluded, _excluded.columns[_excluded.columns.length - 2], _opt.subname(_subcategory)), 1); + // aowow - adressing col by offset breaks everytime we add/remove cols in a listview template + // _listview.createIndicator($WH.sprintf(LANG['lvnote_' + _mode + 'ind'], _category, _subcategory, _opt.subname(_subcategory)), Listview.headerFilter.bind(_listview, _listview.columns[_listview.columns.length - 2], '')); + // _excluded.createIndicator($WH.sprintf(LANG['lvnote_' + _mode + 'ind'], _category, _subcategory, _opt.subname(_subcategory)), Listview.headerFilter.bind(_excluded, _listview.columns[_listview.columns.length - 2], '')); + // setTimeout(Listview.headerFilter.bind(_listview, _listview.columns[_listview.columns.length - 2], _opt.subname(_subcategory)), 1); + // setTimeout(Listview.headerFilter.bind(_excluded, _excluded.columns[_excluded.columns.length - 2], _opt.subname(_subcategory)), 1); + _listview.createIndicator($WH.sprintf(LANG['lvnote_' + _mode + 'ind'], _category, _subcategory, _opt.subname(_subcategory)), Listview.headerFilter.bind(_listview, _listview.columns.find((x) => x.id == (_opt.catgcol || 'category')), '')); + _excluded.createIndicator($WH.sprintf(LANG['lvnote_' + _mode + 'ind'], _category, _subcategory, _opt.subname(_subcategory)), Listview.headerFilter.bind(_excluded, _listview.columns.find((x) => x.id == (_opt.catgcol || 'category')), '')); + setTimeout(Listview.headerFilter.bind(_listview, _listview.columns.find((x) => x.id == (_opt.catgcol || 'category')), _opt.subname(_subcategory)), 1); + setTimeout(Listview.headerFilter.bind(_excluded, _excluded.columns.find((x) => x.id == (_opt.catgcol || 'category')), _opt.subname(_subcategory)), 1); } } } diff --git a/static/js/locale_dede.js b/static/js/locale_dede.js index 12724635..67e6393b 100644 --- a/static/js/locale_dede.js +++ b/static/js/locale_dede.js @@ -4899,6 +4899,12 @@ var LANG = { /* AoWoW: start custom */ + // Profiler completions import + completion: 'Vervollständigung', // WH.TERMS.completion + complete: 'Vollständig', // WH.TERMS.complete + incomplete: 'Unvollständig', // WH.TERMS.incomplete + parens_format: '$1 ($2)', // WH.TERMS.parens_format + // click to copy fn copied: 'Kopiert', clickToCopy: 'Klicke zum Kopieren', diff --git a/static/js/locale_enus.js b/static/js/locale_enus.js index 65eac3b5..1255bfce 100644 --- a/static/js/locale_enus.js +++ b/static/js/locale_enus.js @@ -4947,6 +4947,12 @@ var LANG = { /* AoWoW: start custom */ + // Profiler completions import + completion: 'Completion', // WH.TERMS.completion + complete: 'Complete', // WH.TERMS.complete + incomplete: 'Incomplete', // WH.TERMS.incomplete + parens_format: '$1 ($2)', // WH.TERMS.parens_format + // click to copy fn copied: 'Copied', clickToCopy: 'Click to Copy', diff --git a/static/js/locale_eses.js b/static/js/locale_eses.js index 5ddeb9ce..a2ea7de4 100644 --- a/static/js/locale_eses.js +++ b/static/js/locale_eses.js @@ -4901,6 +4901,12 @@ var LANG = { /* AoWoW: start custom */ + // Profiler completions import + completion: 'Terminación', // WH.TERMS.completion + complete: 'Completo', // WH.TERMS.complete + incomplete: 'Incompleto', // WH.TERMS.incomplete + parens_format: '$1 ($2)', // WH.TERMS.parens_format + // click to copy fn copied: 'Copiado', clickToCopy: 'Click para copiar', diff --git a/static/js/locale_frfr.js b/static/js/locale_frfr.js index 234acbc0..37edaeb0 100644 --- a/static/js/locale_frfr.js +++ b/static/js/locale_frfr.js @@ -4901,6 +4901,12 @@ var LANG = { /* AoWoW: start custom */ + // Profiler completions import + completion: 'Achèvement', // WH.TERMS.completion + complete: 'Complète', // WH.TERMS.complete + incomplete: 'Incomplet', // WH.TERMS.incomplete + parens_format: '$1 ($2)', // WH.TERMS.parens_format + // click to copy fn copied: 'Copié', clickToCopy: 'Cliquer pour Copier', diff --git a/static/js/locale_ruru.js b/static/js/locale_ruru.js index a88b356f..a3f5c265 100644 --- a/static/js/locale_ruru.js +++ b/static/js/locale_ruru.js @@ -4903,6 +4903,12 @@ var LANG = { /* AoWoW: start custom */ + // Profiler completions import + completion: 'Завершено', // WH.TERMS.completion + complete: 'Завершено', // WH.TERMS.complete + incomplete: 'Не завершено', // WH.TERMS.incomplete + parens_format: '$1 ($2)', // WH.TERMS.parens_format + // click to copy fn copied: 'Скопировано', clickToCopy: 'Нажмите, чтобы скопировать', diff --git a/static/js/locale_zhcn.js b/static/js/locale_zhcn.js index 54f20940..065eb7e4 100644 --- a/static/js/locale_zhcn.js +++ b/static/js/locale_zhcn.js @@ -4927,6 +4927,12 @@ var LANG = { /* AoWoW: start custom */ + // Profiler completions import + completion: '达成', // WH.TERMS.completion + complete: '完成', // WH.TERMS.complete + incomplete: '未完成', // WH.TERMS.incomplete + parens_format: '$1($2)', // WH.TERMS.parens_format + // click to copy fn copied: '已复制', clickToCopy: '点击复制', diff --git a/template/bricks/infobox.tpl.php b/template/bricks/infobox.tpl.php index d9ca62ed..60252209 100644 --- a/template/bricks/infobox.tpl.php +++ b/template/bricks/infobox.tpl.php @@ -51,6 +51,7 @@ echo " \n"; ?> +