helialprofile.png
Welcome to IOPWiki, Commander. You can contribute to this wiki without an account. Learn how to contribute and join our Discord server.

Difference between revisions of "MediaWiki:Gadget-artTab.js"

Welcome to IOP Wiki. This website is maintained by the Girls' Frontline community and is free to edit by anyone.
Jump to navigation Jump to search
(First try making a progress bar)
Line 26: Line 26:
 
       var stage = view.data('live2dStage');
 
       var stage = view.data('live2dStage');
 
       stage.removeChildren();
 
       stage.removeChildren();
       loadLive2dGirl(tdoll, costume, variant, function(live2dData) {
+
       loadLive2dGirl($(artTabDiv), tdoll, costume, variant, function(live2dData) {
 
         view.closest(".artTab").removeClass("loading");
 
         view.closest(".artTab").removeClass("loading");
 
         stage.addChild(live2dData);
 
         stage.addChild(live2dData);
Line 70: Line 70:
 
}
 
}
  
function loadLive2dGirl(tdoll, costume, variant, callback) {
+
function base64ArrayBuffer(arrayBuffer) {
 +
  var base64    = ''
 +
  var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
 +
 
 +
  var bytes        = new Uint8Array(arrayBuffer)
 +
  var byteLength    = bytes.byteLength
 +
  var byteRemainder = byteLength % 3
 +
  var mainLength    = byteLength - byteRemainder
 +
 
 +
  var a, b, c, d
 +
  var chunk
 +
 
 +
  // Main loop deals with bytes in chunks of 3
 +
  for (var i = 0; i < mainLength; i = i + 3) {
 +
    // Combine the three bytes into a single integer
 +
    chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]
 +
 
 +
    // Use bitmasks to extract 6-bit segments from the triplet
 +
    a = (chunk & 16515072) >> 18 // 16515072 = (2^6 - 1) << 18
 +
    b = (chunk & 258048)  >> 12 // 258048  = (2^6 - 1) << 12
 +
    c = (chunk & 4032)    >>  6 // 4032    = (2^6 - 1) << 6
 +
    d = chunk & 63              // 63      = 2^6 - 1
 +
 
 +
    // Convert the raw binary segments to the appropriate ASCII encoding
 +
    base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]
 +
  }
 +
 
 +
  // Deal with the remaining bytes and padding
 +
  if (byteRemainder == 1) {
 +
    chunk = bytes[mainLength]
 +
 
 +
    a = (chunk & 252) >> 2 // 252 = (2^6 - 1) << 2
 +
 
 +
    // Set the 4 least significant bits to zero
 +
    b = (chunk & 3)  << 4 // 3  = 2^2 - 1
 +
 
 +
    base64 += encodings[a] + encodings[b] + '=='
 +
  } else if (byteRemainder == 2) {
 +
    chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1]
 +
 
 +
    a = (chunk & 64512) >> 10 // 64512 = (2^6 - 1) << 10
 +
    b = (chunk & 1008)  >>  4 // 1008  = (2^6 - 1) << 4
 +
 
 +
    // Set the 2 least significant bits to zero
 +
    c = (chunk & 15)    <<  2 // 15    = 2^4 - 1
 +
 
 +
    base64 += encodings[a] + encodings[b] + encodings[c] + '='
 +
  }
 +
 
 +
  return base64
 +
}
 +
 
 +
function makeBase64Request(url) {
 +
  let xhr = new XMLHttpRequest();
 +
  let prom = new Promise(function (resolve, reject) {
 +
    xhr.open('GET', url, true);
 +
    xhr.responseType = 'arraybuffer';
 +
    xhr.onload = function () {
 +
      if (this.status >= 200 && this.status < 300) {
 +
        resolve(base64ArrayBuffer(xhr.response));
 +
      } else {
 +
        reject({
 +
          status: this.status,
 +
          statusText: xhr.statusText
 +
        });
 +
      }
 +
    };
 +
    xhr.onerror = function () {
 +
      reject({
 +
        status: this.status,
 +
        statusText: xhr.statusText
 +
      });
 +
    };
 +
  });
 +
 
 +
  prom.startProcess = function(param) { xhr.send(param); };
 +
 
 +
  return prom;
 +
}
 +
 
 +
 
 +
function hideProgress(container) {
 +
  let progress = $(container).find('.live2dloadingprogress').first();
 +
  progress.hide();
 +
}
 +
 
 +
function showProgress(container, done, length) {
 +
  let progress = $(container).find('.live2dloadingprogress').first();
 +
 
 +
  if (progress.length == 0) {
 +
    let centeringFix = $('<span></span>');
 +
    let live2dprogress = $('<div></div>').addClass("live2dprogress").append(centeringFix);
 +
    let live2dprogressbar = $('<div></div>').addClass("live2dprogressbar");
 +
    let live2dprogressbarcontainer = $('<div></div>').addClass("live2dprogressbarcontainer").append(live2dprogressbar);
 +
    progress = $('<div></div>').addClass("live2dloadingprogress").append(live2dprogress).append(live2dprogressbarcontainer);
 +
    $(container).append(progress);
 +
  }
 +
 
 +
  progress.show();
 +
 
 +
  let progressAmount = parseFloat(done / length * 100).toFixed(2);
 +
  progress.find('.live2dprogress span').text(progressAmount);
 +
  progress.find('.live2dprogressbar').css('width', progressAmount + "%");
 +
}
 +
 
 +
function loadLive2dGirl(container, tdoll, costume, variant, callback) {
 
   var modelId = tdoll;
 
   var modelId = tdoll;
 
   if (costume.length > 0) {
 
   if (costume.length > 0) {
Line 81: Line 186:
 
   $.ajax({
 
   $.ajax({
 
   dataType: "json",
 
   dataType: "json",
 +
  mimeType: "text/plain",
 
   url: curl(modelId + "_live2d_model.txt"),
 
   url: curl(modelId + "_live2d_model.txt"),
  // data: null,
 
 
   success: function (data) {
 
   success: function (data) {
 +
            // Init
 +
            var counter = 0;
 +
            var maxItems = 0;
 +
            var confun = function() {
 +
              counter++;
 +
              showProgress(container, counter, maxItems)
 +
            }
 +
           
 +
            confun
 +
           
 
             json = data;
 
             json = data;
 +
           
 +
            var loadingJobs = new Array();
 +
           
 +
            // Correct paths
 
             json.model = curl(modelId + "_live2d_model.moc" + ".skel");
 
             json.model = curl(modelId + "_live2d_model.moc" + ".skel");
             if (json.hasOwnProperty('physics')) { json.physics = curl(modelId + "_live2d_physics" + ".txt"); }
+
             if (json.hasOwnProperty('physics')) { json.physics = curl(modelId + "_live2d_" + json.physics + ".txt"; }
             if (json.hasOwnProperty('pose')) { json.pose = curl(modelId + "_live2d_pose" + ".txt"); }
+
             if (json.hasOwnProperty('pose')) { json.pose = curl(modelId + json.pose + ".txt"; }
 
              
 
              
             for (var idx=0; idx < json.textures.length; idx++) {
+
             for (var idx=json.motions.idle.length-1; idx >= 0; idx--) {
               json.textures[idx] = curl(modelId + "_live2d_" + json.textures[idx]);
+
               let job = makeBase64Request(curl(modelId + json.motions.idle[idx].file + ".txt"));
 +
              job.then(function(promiseData) {
 +
                let _datatoPush = "data:text/plain;base64," + promiseData;
 +
                json.motions.idle.push({ "file": _datatoPush });
 +
                confun();
 +
              });
 +
              json.motions.idle.splice(idx, 1);
 +
              loadingJobs.push(job);
 +
             
 
             }
 
             }
 
              
 
              
             for (var idx=0; idx < json.motions.idle.length; idx++) {
+
             for (var idx=json.motions.tap_figure.length-1; idx >= 0 ; idx--) {
               json.motions.idle[idx].file = curl(modelId + "_live2d_" + json.motions.idle[idx].file + ".txt");
+
               // json.motions.tap_figure[idx].file = folder + json.motions.tap_figure[idx].file + ".txt";
 +
              let job = makeBase64Request(curl(modelId + json.motions.tap_figure[idx].file + ".txt"));
 +
              job.then(function(promiseData) {
 +
                let _datatoPush = "data:text/plain;base64," + promiseData;
 +
                json.motions.tap_figure.push({ "file": _datatoPush });
 +
                confun();
 +
              });
 +
              json.motions.tap_figure.splice(idx, 1);
 +
              loadingJobs.push(job);
 
             }
 
             }
 
              
 
              
             for (var idx=0; idx < json.motions.tap_figure.length; idx++) {
+
             for (var idx=0; idx < json.textures.length; idx++) {
               json.motions.tap_figure[idx].file = curl(modelId + "_live2d_" + json.motions.tap_figure[idx].file + ".txt");
+
               let job = makeBase64Request(curl(modelId + json.textures[idx]));
 +
              job.then(function(promiseData) {
 +
                let _datatoPush = "data:image/png;base64," + promiseData;
 +
                json.textures.push(_datatoPush);
 +
                confun();
 +
              });
 +
              json.textures.splice(idx, 1);
 +
              loadingJobs.push(job);
 
             }
 
             }
 
              
 
              
             var l2d_tdoll_sprite = createLive2dSprite(json);
+
             // Ajaxing
 +
            maxItems = loadingJobs.length;
 +
            for (var idxXhr=0; idxXhr < loadingJobs.length; idxXhr++) { loadingJobs[idxXhr].startProcess(null); }
 
              
 
              
             callback(l2d_tdoll_sprite);
+
             // Do it
 +
            $.when.apply($, loadingJobs)
 +
              .done(function(x) {
 +
                startLive2dStuff(json);
 +
              })
 +
              .fail(function(x) { console.log("Failed ajax", x); })
 +
              .always(function(x) { hideProgress(container); });
 
         }
 
         }
 
   });
 
   });

Revision as of 23:52, 16 January 2018

function switchVariant(artTabDivElement, variant) {
  var artTabDiv = $(artTabDivElement);
  artTabDiv.find('.artTabLinks').removeClass('active');
  artTabDiv.attr('data-tdoll-variant', variant);

  refreshView(artTabDiv);
  
  // Set "active" at the end
  artTabDiv.find(".artTabLinks[data-tdoll-variant='" + variant + "']").addClass('active');
}

function refreshView(artTabDiv) {
  var tdoll = $(artTabDiv).data('tdollId');
  var costume = $(artTabDiv).data('tdollCostume');
  var variant = $(artTabDiv).attr('data-tdoll-variant'); // why not data though?
  
  $(artTabDiv).find('.artTabContent .fullart').hide();
  
  var live2dActive = $(artTabDiv).find('.live2dstage').is(':visible');
  
  $(artTabDiv).addClass("loading");
  
  if (live2dActive) {
    createLive2dView(artTabDiv, function(view) {
      view.show();
      var stage = view.data('live2dStage');
      stage.removeChildren();
      loadLive2dGirl($(artTabDiv), tdoll, costume, variant, function(live2dData) {
        view.closest(".artTab").removeClass("loading");
        stage.addChild(live2dData);
      });
    });
  } else {
    $(artTabDiv).find(".fullart[data-variant='" + variant + "']").show();
    $(artTabDiv).removeClass("loading");
  }
}

function create_url(base, file) {
  return base + window.gfUtils.createWikiPathPart(file) + file;
}

function curl(file) {
  return create_url("../images/", file);
}

function createLive2dSprite(model) {
  // Live 2D Sprite
  var l2d_tdoll_sprite = new PIXI.Live2DSprite(model, {
    debugLog: false,
    randomMotion: false,
    eyeBlink: true
  });
  l2d_tdoll_sprite.startRandomMotion('idle');
  l2d_tdoll_sprite.on('mousemove', function(evt) {
    const point = evt.data.global;
    l2d_tdoll_sprite.setViewPoint(point.x, point.y);
  });
  l2d_tdoll_sprite.on('click', function(evt) {
    const point = evt.data.global;
    if (l2d_tdoll_sprite.hitTest('body', point.x, point.y)) {
      l2d_tdoll_sprite.startRandomMotionOnce('tap_figure');
      // Info: Maybe add individual hit tests?
    } else {
      l2d_tdoll_sprite.startRandomMotionOnce('tap_figure');
    }
  });
  
  return l2d_tdoll_sprite;
}

function base64ArrayBuffer(arrayBuffer) {
  var base64    = ''
  var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

  var bytes         = new Uint8Array(arrayBuffer)
  var byteLength    = bytes.byteLength
  var byteRemainder = byteLength % 3
  var mainLength    = byteLength - byteRemainder

  var a, b, c, d
  var chunk

  // Main loop deals with bytes in chunks of 3
  for (var i = 0; i < mainLength; i = i + 3) {
    // Combine the three bytes into a single integer
    chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]

    // Use bitmasks to extract 6-bit segments from the triplet
    a = (chunk & 16515072) >> 18 // 16515072 = (2^6 - 1) << 18
    b = (chunk & 258048)   >> 12 // 258048   = (2^6 - 1) << 12
    c = (chunk & 4032)     >>  6 // 4032     = (2^6 - 1) << 6
    d = chunk & 63               // 63       = 2^6 - 1

    // Convert the raw binary segments to the appropriate ASCII encoding
    base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]
  }

  // Deal with the remaining bytes and padding
  if (byteRemainder == 1) {
    chunk = bytes[mainLength]

    a = (chunk & 252) >> 2 // 252 = (2^6 - 1) << 2

    // Set the 4 least significant bits to zero
    b = (chunk & 3)   << 4 // 3   = 2^2 - 1

    base64 += encodings[a] + encodings[b] + '=='
  } else if (byteRemainder == 2) {
    chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1]

    a = (chunk & 64512) >> 10 // 64512 = (2^6 - 1) << 10
    b = (chunk & 1008)  >>  4 // 1008  = (2^6 - 1) << 4

    // Set the 2 least significant bits to zero
    c = (chunk & 15)    <<  2 // 15    = 2^4 - 1

    base64 += encodings[a] + encodings[b] + encodings[c] + '='
  }

  return base64
}

function makeBase64Request(url) {
  let xhr = new XMLHttpRequest();
  let prom = new Promise(function (resolve, reject) {
    xhr.open('GET', url, true);
    xhr.responseType = 'arraybuffer';
    xhr.onload = function () {
      if (this.status >= 200 && this.status < 300) {
        resolve(base64ArrayBuffer(xhr.response));
      } else {
        reject({
          status: this.status,
          statusText: xhr.statusText
        });
      }
    };
    xhr.onerror = function () {
      reject({
        status: this.status,
        statusText: xhr.statusText
      });
    };
  });
  
  prom.startProcess = function(param) { xhr.send(param); };
  
  return prom;
}


function hideProgress(container) {
  let progress = $(container).find('.live2dloadingprogress').first();
  progress.hide();
}

function showProgress(container, done, length) {
  let progress = $(container).find('.live2dloadingprogress').first();
  
  if (progress.length == 0) {
    let centeringFix = $('<span></span>');
    let live2dprogress = $('<div></div>').addClass("live2dprogress").append(centeringFix);
    let live2dprogressbar = $('<div></div>').addClass("live2dprogressbar");
    let live2dprogressbarcontainer = $('<div></div>').addClass("live2dprogressbarcontainer").append(live2dprogressbar);
    progress = $('<div></div>').addClass("live2dloadingprogress").append(live2dprogress).append(live2dprogressbarcontainer);
    $(container).append(progress);
  }
  
  progress.show();
  
  let progressAmount = parseFloat(done / length * 100).toFixed(2);
  progress.find('.live2dprogress span').text(progressAmount);
  progress.find('.live2dprogressbar').css('width', progressAmount + "%");
}

function loadLive2dGirl(container, tdoll, costume, variant, callback) {
  var modelId = tdoll;
  if (costume.length > 0) {
    modelId = modelId + "_" + costume;
  }
  if (variant && variant.length > 0) {
    modelId = modelId + "_" + variant;
  }
  
  $.ajax({
  dataType: "json",
  mimeType: "text/plain",
  url: curl(modelId + "_live2d_model.txt"),
  success: function (data) {
            // Init
            var counter = 0;
            var maxItems = 0;
            var confun = function() {
              counter++;
              showProgress(container, counter, maxItems)
            }
            
            confun
            
            json = data;
            
            var loadingJobs = new Array();
            
            // Correct paths
            json.model = curl(modelId + "_live2d_model.moc" + ".skel");
            if (json.hasOwnProperty('physics')) { json.physics = curl(modelId + "_live2d_" + json.physics + ".txt"; }
            if (json.hasOwnProperty('pose')) { json.pose = curl(modelId + json.pose + ".txt"; }
            
            for (var idx=json.motions.idle.length-1; idx >= 0; idx--) {
              let job = makeBase64Request(curl(modelId + json.motions.idle[idx].file + ".txt"));
              job.then(function(promiseData) {
                let _datatoPush = "data:text/plain;base64," + promiseData;
                json.motions.idle.push({ "file": _datatoPush });
                confun();
              });
              json.motions.idle.splice(idx, 1);
              loadingJobs.push(job);
              
            }
            
            for (var idx=json.motions.tap_figure.length-1; idx >= 0 ; idx--) {
              // json.motions.tap_figure[idx].file = folder + json.motions.tap_figure[idx].file + ".txt";
              let job = makeBase64Request(curl(modelId + json.motions.tap_figure[idx].file + ".txt"));
              job.then(function(promiseData) {
                let _datatoPush = "data:text/plain;base64," + promiseData;
                json.motions.tap_figure.push({ "file": _datatoPush });
                confun();
              });
              json.motions.tap_figure.splice(idx, 1);
              loadingJobs.push(job);
            }
            
            for (var idx=0; idx < json.textures.length; idx++) {
              let job = makeBase64Request(curl(modelId + json.textures[idx]));
              job.then(function(promiseData) {
                let _datatoPush = "data:image/png;base64," + promiseData;
                json.textures.push(_datatoPush);
                confun();
              });
              json.textures.splice(idx, 1);
              loadingJobs.push(job);
            }
            
            // Ajaxing
            maxItems = loadingJobs.length;
            for (var idxXhr=0; idxXhr < loadingJobs.length; idxXhr++) { loadingJobs[idxXhr].startProcess(null); }
            
            // Do it
            $.when.apply($, loadingJobs)
              .done(function(x) {
                startLive2dStuff(json);
              })
              .fail(function(x) { console.log("Failed ajax", x); })
              .always(function(x) { hideProgress(container); });
        }
  });
}

function doesLive2dAnimationExist(artTabDiv, costume) {
  var costumeId = costume;
  if (costumeId[0] === '_') {
    costumeId = costumeId.substr(1);
  }
  if (costume == "") {
    costumeId = "costume0";
  }
  
  return $(artTabDiv).attr("data-live2d-exist-" + costumeId) == "true";
}

function hideLive2dView(artTabDiv) {
  var live2dView = $(artTabDiv).find('.live2dstage');
  live2dView.hide();
  var stage = live2dView.data('live2dStage');
  if (stage) {
    stage.removeChildren();
  }

  refreshView(artTabDiv);
}

function createLive2dView(artTabDiv, callback) {
  var live2dView = $(artTabDiv).find('.live2dstage');
  
  if (live2dView.length < 1) {
    mw.loader.using('ext.gadget.live2d').then( function () {
	// Init renderer
	const l2d_tdoll_renderer = PIXI.autoDetectRenderer(550, 550, {transparent: true});
	const l2d_tdoll_stage = new PIXI.Container();

	// Start the animation
	function animate() {
		requestAnimationFrame(animate);
		l2d_tdoll_renderer.render(l2d_tdoll_stage);
	}
	animate();

        var view = $(l2d_tdoll_renderer.view);
        view.data('live2dStage', l2d_tdoll_stage);
        view.addClass('live2dstage');
        view.data('renderer', l2d_tdoll_renderer);

        callback(view);
    });
  } else {
    callback(live2dView.first());
  }
}

function modelChanged(event, costumeSuffix) {
  var artTabDiv = $(event.target).closest('.artTab');
  var tdollId = artTabDiv.data('tdollId');
  var artModelId = tdollId + costumeSuffix;
  var variantswitcher = artTabDiv.find('.variantswitcher');
  
  var costumeId = costumeSuffix;
  if (costumeId[0] === '_') {
    costumeId = costumeId.substr(1);
  }
  artTabDiv.attr('data-tdoll-costume', costumeId);
  
  hideLive2dView(artTabDiv);
  variantswitcher.find('.right').remove();
  
  var basePath = mw.config.get("wgServer") + "/images/";
  var cpp = window.gfUtils.createWikiPathPart;
  var fullartPath = basePath + "thumb/" + cpp(artModelId +".png") + artModelId +".png/600px-" + artModelId +".png";
  var fullartDamagedPath = basePath + "thumb/" + cpp(artModelId +"_D.png") + artModelId +"_D.png/600px-" + artModelId +"_D.png";
    
  var normArt = artTabDiv.find(".fullart:not(.damaged) a");
  normArt.attr("href", fullartPath );
  normArt.find('img').attr("src", fullartPath );
  normArt.find('img').attr("srcset", fullartPath );
  normArt.find('img').attr("alt", fullartPath );

  var damagedArt = artTabDiv.find(".fullart.damaged a");
  damagedArt.attr("href", fullartDamagedPath );
  damagedArt.find('img').attr("src", fullartDamagedPath );
  damagedArt.find('img').attr("srcset", fullartDamagedPath );
  damagedArt.find('img').attr("alt", fullartDamagedPath );

  var isLive2dPossible = doesLive2dAnimationExist(artTabDiv, costumeSuffix);
  if (isLive2dPossible) {
    var live2dButton = $('<button></button>');
    live2dButton.addClass('artTabLinks');
    live2dButton.addClass('right');
    live2dButton.click(function() {
      if (live2dButton.is('.enabled')) {
        hideLive2dView($(artTabDiv));
        live2dButton.removeClass('enabled');
      } else {
        $(artTabDiv).addClass("loading");
        createLive2dView(artTabDiv, function(view) {
          view.show();
          $(artTabDiv).find('.artTabContent').first().append(view);
          refreshView(artTabDiv);
        });
        live2dButton.addClass('enabled');
      }
    });
    live2dButton.text("Live2D");
    variantswitcher.append(live2dButton);
  }
  
  refreshView(artTabDiv);
};

RLQ.push(function () {
  $(document).ready(function() { 
    var artTabDiv = $('.artTab');
    artTabDiv.on('costume_changed', modelChanged);

    var buttonEventHandler = function(event) {
      var currentElement = $(event.target);
      var currentArtTab = currentElement.closest('.artTab');
      var variant = currentElement.data('tdollVariant');
      switchVariant(currentArtTab, variant);
    };
  
    var normalButton = $('<button></button>');
    normalButton.addClass('artTabLinks');
    normalButton.click(buttonEventHandler);
    normalButton.attr('data-tdoll-variant', "");
    normalButton.text("Normal");

    var damagedButton = $('<button></button>');
    damagedButton.addClass('artTabLinks');
    damagedButton.click(buttonEventHandler);
    damagedButton.attr('data-tdoll-variant', "D");
    damagedButton.text("Damaged");
  
    var variantswitcher = $('<div></div>');
    variantswitcher.addClass('variantswitcher');
    variantswitcher.append(normalButton);
    variantswitcher.append(damagedButton);
    
    artTabDiv.prepend(variantswitcher);

    normalButton.click();
  });
});