Widget:CharacterTrackerScript

From Granblue Fantasy Wiki
Jump to navigation Jump to search

<script type="text/javascript">

 (function($) {
   'use strict';
   var otherSkip = [];
   $('#tracker-filter-obtain > label').each(function() {
     otherSkip.push(this.dataset.value);
   });
   function Uint8ArrayToBase64(bytes) {
     var binary = ;
     var len = bytes.byteLength;
     for (var i = 0; i < len; i++)
       binary += String.fromCharCode(bytes[i]);
     var result = window.btoa(binary);
      result = result.replace('=', ); // Remove any trailing '='s
      result = result.replace('+', '-'); // 62nd char of encoding
      result = result.replace('/', '_'); // 63rd char of encoding      
     if (result == 'AAAA')
       return ;
      return result;
   }
   function Base64ToUint8Array(text) {
     var s = text.replace('_', '/'); // 63rd char of encoding      
     s = s.replace('-', '+'); // 62nd char of encoding
     var pl = (s.length % 4);
     if (pl > 0)
       s += '='.repeat(4-pl); // Restore any trailing '='s
     var binary = window.atob(s);
     var len = binary.length;
     var result = new Uint8Array(len);
     for (var i = 0; i < len; i++) 
       result[i] = binary.charCodeAt(i);
     return result;
   }
   var last_hash = ;
   $(window).on('hashchange', function() {
     if (window.location.hash != last_hash) {
       readHash();
       updateUncap();
       updateItems();
     }
   });
   function replaceHash(newHash) {
     if ((+newHash).charAt(0) !== '#') 
       newHash = '#' + newHash;
     history.replaceState(, , newHash);
     last_hash = newHash;
   }
   function readHash() {
     // format: Options;SSR Characters;SR Characters;R Characters;SSR Summons;SR Summons; R Summons
     // - Options is a base64 encoded json object
     // - Character/Summons are base64 encoded sets of bits, where each character is 3 bits offset by the counter in its ID
     last_hash = window.location.hash; 
     var hash = window.location.hash.substr(1);
     var parts = hash.split(';');
     var ix = 1;
     var strings = {
       'c': {2: parts[3], 3: parts[2], 4: parts[1]}, 
       's': {2: parts[6], 3: parts[5], 4: parts[4]}, 
     };
     // load options
     var reOptions = /[g-z]{1}[0-9a-f]{1,8}/ig;
     var options = parts[0].match(reOptions);
     if (options != null) {
       for (var i = 0; i < options.length; i++) {
         var text = options[i];
         var option = text.charAt(0);
         var bits = parseInt(text.substr(1), 16);
         var $option = $('div[data-option="'+option+'"]');
         if ($option.length <= 0)
           continue;
         var $bits = $option.find('[data-bit]');
         if ($bits.length <= 0)
           continue;
         $option.find('label').removeClass('mw-ui-progressive');
         $bits.each(function() {
           var selected = ((1 << this.dataset.bit) & bits) > 0;
           if ($(this).is('input')) {
             $(this).prop('checked', selected);
           } else if (selected) {
             $(this).addClass('mw-ui-progressive');
           }
         });
         // verify we actually selected something
         if ($option.find('.mw-ui-progressive').length > 0)
           continue;
         $option.find('label[data-value="*"]').addClass('mw-ui-progressive');
       }
     }
     // reset items
     $('.tracker-item').each(function() {
       this.dataset.evo = 0;
       this.dataset.owned = 'false';
       evolve(this, 0);
     });
     // load items
     for (var type in strings) {
       for (var rarity in strings[type]) {
         var str = strings[type][rarity];
         if ((str == undefined) || (str.length == 0))
           continue;
         var buffer = Base64ToUint8Array(str);
         var len = buffer.length / 3;
         for (var i = 0; i < len; i++) {
           var evos = 0;
           evos |= (buffer[i*3  ] <<  0);
           evos |= (buffer[i*3+1] <<  8);
           evos |= (buffer[i*3+2] << 16);
           for (var j = 0; j < 8; j++) {
             var evo = (evos >> (j*3)) & 0x07;
             if (evo <= 0)
               continue;
             var short_id =  + rarity + ('000'+(i*8+j)).slice(-3);
             var $items = $('.tracker-item[data-type="'+type+'"][data-short_id="'+short_id+'"]');
             $items.each(function() {
               evolve(this, evo);
             });
           }
         }
       }
     }
   }
   function makeHash() {
     // for format see readHash
     var strings = {
       'c': {2:,3:,4:}, 
       's': {2:,3:,4:},
     };
     var selected = {
       'c': {2:{},3:{},4:{}}, 
       's': {2:{},3:{},4:{}},
     };
     // store which items have been selected
     $('.tracker-item.selected').each(function() {
       var short_id = this.dataset.short_id;
       if (short_id.length == 4) {
         var type = this.dataset.type;
         var rarity = parseInt(short_id[0], 10);
         if ((rarity < 2) || (rarity > 4))
           return;
         var index = parseInt(short_id.substr(1), 10);
         selected[type][rarity][index] = parseInt(this.dataset.evo,10);
       }
     });
     // convert to a bit array
     for (var type in selected) {
       for (var rarity in selected[type]) {
         var high_id = 0;
         var obj = selected[type][rarity];
         for (var index in obj)
           high_id = Math.max(high_id, index);
         // Group 8 items with 3 bits each
         var parts = Math.floor(high_id / 8) + 1;
         var size = parts * 3;
         var buffer = new Uint8Array(size);
         for (var i = 0; i <= Math.floor(high_id / 8); i++) {
           var evos = 0x000000;
           for (var j = 0; j < 8; j++) {
             var evo = obj[i*8+j];
             if (evo == undefined)
               evo = 0;
             evos |= (evo << (j*3));
           }
           buffer[i*3]   = (evos >>  0) & 0xFF;
           buffer[i*3+1] = (evos >>  8) & 0xFF;
           buffer[i*3+2] = (evos >> 16) & 0xFF;
         }
         strings[type][rarity] += Uint8ArrayToBase64(buffer);
       }
       // Options are stored as hex encoded bit array
       var options = ;
       $('div[data-option]').each(function() {
         var $active = $(this).find('[data-bit].mw-ui-progressive, input[data-bit]:checked');
         if ($active.length > 0) {
           var option = 0;
           $active.each(function() {
             option |= 1 << this.dataset.bit;
           });
           options += this.dataset.option + option.toString(16);
         }
       });
       replaceHash(options+';'+strings['c'][4]+';'+strings['c'][3]+';'+strings['c'][2]+';'+strings['s'][4]+';'+strings['s'][3]+';'+strings['s'][2]);
     }
   }
   function evolve(node, levels) {
     var toggle = $(node.parentNode).hasClass('tracker-hide-uncap');
     var evo = parseInt(node.dataset.evo, 10);
     if (toggle) {
       evo = evo > 0 ? 0 : 1;
     } else {
       evo += levels;
       if (evo > Math.max(node.dataset.evo_max, 1))
         evo = 0;
     }
     node.dataset.evo = evo;
     node.dataset.owned = evo > 0 ? 'true' : 'false';
     $(node).toggleClass('selected', evo > 0);
     $(node).find('.tracker-uncap-star').each(function() {
       $(this).toggleClass('selected', evo > 0);
       evo -= 1;
     });
   }
   function updateUncap() {
     var cb = document.getElementById('tracker-character-uncap');
     $('.tracker-box[id$="-characters"]').toggleClass('tracker-hide-uncap', !cb.checked);
     cb = document.getElementById('tracker-summon-uncap');
     $('.tracker-box[id$="-summons"]').toggleClass('tracker-hide-uncap', !cb.checked);
     makeHash();
   }
   var starDivs = [];
   function moveItems(div, type, element, rarity) {
     var tracker = document.getElementById(div);
     $('.tracker-item[data-type="'+type+'"][data-element="'+element+'"][data-rarity="'+rarity+'"]').each(function() {
       var node = this;
       node.dataset.owned = 'false';
       node.dataset.evo = 0;
       if ($.isNumeric(node.dataset.baseevo) && $.isNumeric(node.dataset.maxevo)) {
         node.dataset.evo_base = parseInt(node.dataset.baseevo, 10);
         node.dataset.evo_max = parseInt(node.dataset.maxevo, 10);
       } else {
         node.dataset.evo_base = 0;
         node.dataset.evo_max = 0;
       }
       node.parentNode.removeChild(node);
       tracker.insertBefore(node, null);

var $uncap = $('

')
         .addClass('tracker-uncap');
       for (var i = 1; i <= node.dataset.evo_base; i++) {
$uncap.append($('
')
           .addClass('tracker-uncap-star')
           .addClass('tracker-uncap-base')
         );
       }
       for (var i = node.dataset.evo_base+1; i <= node.dataset.evo_max; i++) {
$uncap.append($('
')
           .addClass('tracker-uncap-star')
           .addClass('tracker-uncap-max')
         );
       }
       $(node).append($uncap);
     });
   }
   function getFilter(name) {
     var result = [];
     $('#tracker-filter-'+name+' > label.mw-ui-progressive').each(function() {
       if (this.dataset.value == '*') {
         result = true;
       } else {
         result.push(this.dataset.value);
       }
     });
     return result;
   }
   function updateItems() {
     console.time('updating');
     var rarity = getFilter('rarity');
     var type = getFilter('type');
     var element = getFilter('element');
     var obtain = getFilter('obtain');
     var style = getFilter('style');
     var race = getFilter('race');
     var gender = getFilter('gender');
     var maxevo = getFilter('maxevo');
     var owned = getFilter('owned');
     var search = $('#tracker-search').val();
     var reSearch;
     try {
       reSearch = new RegExp('.*'+search+'.*','i');
     } catch (e) {
       reSearch = new RegExp('.*'+search.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&")+'.*','i');
     }
     $('.tracker-item').each(function() {
       var visible = true;
       if ((rarity !== true) && (rarity.indexOf(this.dataset.rarity) === -1)) {
         visible = false;
       } else if ((type !== true) && (type.indexOf(this.dataset.type) === -1)) {
         visible = false;
       } else if ((element !== true) && (element.indexOf(this.dataset.element) === -1)) {
         visible = false;
       } else if ((style !== true) && (style.indexOf(this.dataset.style) === -1)) {
         visible = false;
       } else if ((race !== true) && (race.indexOf(this.dataset.race) === -1)) {
         visible = false;
       } else if ((gender !== true) && (gender.indexOf(this.dataset.gender) === -1)) {
         visible = false;
       } else if ((maxevo !== true) && (maxevo.indexOf(this.dataset.maxevo) === -1)) {
         visible = false;
       } else if ((owned !== true) && (owned.indexOf(this.dataset.owned) === -1)) {
         visible = false;
       } else if (obtain !== true) {
         visible = false;
         if ((obtain == 'other') && (otherSkip.indexOf(this.dataset.obtain) === -1))
           visible = true
         else if ((obtain != 'other') && (obtain.indexOf(this.dataset.obtain) >= 0))
           visible = true;
       }
       if (visible && (search.length > 0)) {
         visible = reSearch.test(this.dataset.id) || reSearch.test(this.dataset.name);
       }
       $(this).toggle(visible);
     });
     $('.tracker-box').each(function() {
       $(this).show();
       $(this).toggle($(this).children(':visible').length > 0);
     });
     console.timeEnd('updating');
   }
   console.time('moving');
   var elements = ['fire','water','earth','wind','light','dark','any'];
   var rarities = ['ssr','sr','r'];
   for (var ei = 0; ei < elements.length; ei++) {
     for (var ri = 0; ri < rarities.length; ri++) {
       var element = elements[ei];
       var rarity = rarities[ri];
       moveItems('tracker-'+element+'-characters', 'c', element, rarity);
       if (element != 'any')
         moveItems('tracker-'+element+'-summons', 's', element, rarity);
     }
   }
   console.timeEnd('moving');
   $('.tracker-item').on('click', function(event) {
     event.preventDefault();
     console.time('clickitem');
     evolve(this, 1);
     makeHash();
     console.timeEnd('clickitem');
   });
   $('.mw-ui-button-group > label').on('click', function(event) {
     console.time('clicklabel');
     var group = this.parentNode;
     if (this.dataset.value == '*') {
       if (!$(this).hasClass('mw-ui-progressive')) {
         $(group).children('label').removeClass('mw-ui-progressive');
         $(this).addClass('mw-ui-progressive');
       }
     } else {
       $(group).children('label[data-value="*"]').removeClass('mw-ui-progressive');
       $(this).toggleClass('mw-ui-progressive');
       if ($(group).children('.mw-ui-progressive').length == 0) 
         $(group).children('label[data-value="*"]').addClass('mw-ui-progressive');
     }
     updateItems();
     makeHash();
     console.timeEnd('clicklabel');
   });
   $('#tracker-character-uncap').on('click', updateUncap);
   $('#tracker-summon-uncap').on('click', updateUncap);
   $('#tracker-search').on('change', function() {
     updateItems();
   });
   $('#tracker-search').on('keyup', function() {
     updateItems();
   });
   readHash();
   updateUncap();
   updateItems();
 })(jQuery);

</script>