- // ==UserScript==
- // @name Pixiv鼠標預覽
- // @namespace Pixiv鼠標預覽
- // @version 2.0.3
- // @description 滑鼠移入縮圖時彈出預覽視窗,滑動滾輪切換上下張圖片
- // @author Eltair
- // @match https://www.pixiv.net/*
- // @connect i.pximg.net
- // @grant GM_xmlhttpRequest
- // @license MIT
- // ==/UserScript==
-
- (function() {
- 'use strict';
-
- // [可更改的數值]
- const BOXSIZE = window.innerHeight * 0.75; // 彈出框寬度,單位px
- const SPEED = 0.6; // 鼠標移動到圖上多久後彈出預覽框,單位秒
- const ADV = 2; // 多圖時,提前載入的張數
-
- let t = null; // 計時器
- let zp = null; // ZipImagePlayer
- let css = document.createElement("style"); // css
- let line = document.createElement("div"); // 底線
-
- // [設定CSS]
- // under css
- css.innerText = `@keyframes showUnder { from { width: 0%; } to { width: 100%; } } .under { background-color: #008FEA; height: 2px; position: absolute; bottom: 0; left: 0; width: 0%; } .showUnder { animation: showUnder ${SPEED}s linear; width: 100%; }` ;
- // box css
- css.innerText += ".imgbox{z-index:10001;position:absolute;text-align:center;border:1px solid #cbd3da;background-color:#f2f4f6}.imgbox p{width:100%}.imgbox p.tags{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.imgbox img{vertical-align:middle}.imgbox .dl{right:0;bottom:0;position:absolute;font-weight:700;color:#1f1f1f;cursor:pointer}.imgpv{overflow:hidden}.dl:visited{color:#adadad}.dl:hover{color:#5c5c5c}.am{display:flex;align-items:center;justify-content:center}";
- document.head.appendChild(css);
- line.className = "under showUnder";
-
- // [綁定監聽器]
- if(location.pathname == "/ranking.php"){
- document.querySelector(".ranking-items").addEventListener("mouseover", in_img.bind(null, ["_work"]));
- document.querySelector(".ranking-items").addEventListener("mouseout", out_img.bind(null, ["_work"]));
- } else {
- document.querySelector("#root").addEventListener("mouseover", in_img.bind(null, ["sc-rp5asc-10", "sc-7bef31-2"]));
- document.querySelector("#root").addEventListener("mouseout", out_img.bind(null, ["sc-rp5asc-10", "sc-7bef31-2"]));
- }
-
- // ========== 主要方法 ========== //
- // [滑鼠移入圖片]
- function in_img(target_class, e) {
- if (e.target.classList.contains(target_class[0]) || e.target.classList.contains(target_class[1])) {
- e.target.parentNode.appendChild(line);
- window.clearTimeout(t);
- t = window.setTimeout(function() {
- window.clearTimeout(t);
- new previewbox(e.target);
- }, SPEED * 1000)
- }
- }
- // [滑鼠移出圖片]
- function out_img(target_class, e) {
- if (e.target.classList.contains(target_class[0]) || e.target.classList.contains(target_class[1])) {
- e.target.parentNode.removeChild(line);
- window.clearTimeout(t);
- }
- }
- // [預覽框]
- function previewbox(target) {
- this.size = BOXSIZE; // 彈出框寬度,單位px
- this.adv = ADV; // 多圖時,提前載入的張數
- this.target = target;
- this.original_size = this.size; // 彈出框原始大小
- this.pid = null;
- this.box = null;
- this.box_html = "<p class='tags'>取得標籤中...</p><div class='imgpv' data-cursor='0' style='width: {size}px; height: {size}px;'></div><div style='position: relative;' class='under'></div><div><p class='count'>1/1</p><span class='dl' data-src=''>下載</span></div>";
- this.img_html = "<a href='{href}'><img src='{imgsrc}' style='max-width: {size}px; max-height: {size}px;'></a>";
- this._createBox();
- return this;
- }
- // [加入彈出框]
- previewbox.prototype._createBox = function() {
- let pos = this._calcPosition();
- this.pid = this._getId();
- this.box = document.createElement("div");
- this.box.className = "imgbox";
- this.box.innerHTML = this.box_html.replace(/{size}/g, this.size);
- this.box.style.top = `${pos.top}px`;
- this.box.style.left = `${pos.left}px`;
- this.box.style.width = `${this.size + 2}px`;
- this.box.addEventListener("mouseleave", this._removeBox);
- document.querySelector("body").appendChild(this.box);
- this._getData(function(json) {
- let body = json.body;
- // 動圖或是一般插畫
- if(body.illustType == 2) {
- this._addAm(body.urls.small);
- } else if(body.illustType == 0 || body.illustType == 1) {
- this._addImgs(this._getImgurls(body.urls.original, body.pageCount));
- }
- // 設定標籤
- this._addTags(body.tags.tags);
- }.bind(this));
- }
- // [刪除彈出框]
- previewbox.prototype._removeBox = function() {
- this != null && this.remove();
- zp && zp.loadAbort();
- zp && window.clearTimeout(zp._loadTimer);
- zp && window.clearTimeout(zp._timer);
- zp = null;
- }
- // [計算彈出框位置]
- previewbox.prototype._calcPosition = function() {
- let windowWidth = window.innerWidth,
- windowHeight = window.innerHeight,
- bounding = this.target.getBoundingClientRect(),
- scrollTop = document.documentElement.scrollTop,
- scrollLeft = document.documentElement.scrollLeft,
- top = 0,
- left = 0;
- this.size = this.original_size;
- if (windowWidth < this.size || windowHeight < this.size) {
- this.size = Math.min(windowWidth, windowHeight) / 100 * 80;
- }
- left = scrollLeft + bounding.left - (this.size - bounding.width) / 2;
- top = scrollTop + bounding.top - (this.size - bounding.height) / 2;
- // 如果上邊超出視窗
- if (top < scrollTop) {
- top = scrollTop;
- }
- // 如果下邊超出視窗
- if (top + (this.size + 38) > scrollTop + windowHeight) {
- top = windowHeight - (this.size + 38) + scrollTop - (document.documentElement.scrollWidth > document.documentElement.clientWidth ? 15 : 0);
- }
- // 如果左邊超出視窗
- if (left < scrollLeft) {
- left = scrollLeft;
- }
- // 如果右邊超出視窗
- if (left + this.size > scrollLeft + windowWidth) {
- left = windowWidth - this.size + scrollLeft - 15;
- }
- return { top: top, left: left };
- }
- // [插入圖片方法]
- previewbox.prototype._addImg = function(url) {
- let div = document.createElement("div");
- div.style.width = `${this.size}px`;
- div.style.height = `${this.size}px`;
- div.style.lineHeight = `${this.size}px`;
- div.innerHTML = this.img_html.replace(/{href}/g, url[0]).replace(/{imgsrc}/g, url[1]).replace(/{size}/g, this.size);
- this.box.querySelector(".imgpv").appendChild(div);
- }
- // [插入多圖方法]
- previewbox.prototype._addImgs = function(urls) {
- let cursor = 0; // 當前圖片位置
- let imgpv = this.box.querySelector(".imgpv"); // 圖片視窗
- let count = this.box.querySelector(".count"); // 頁數
- let dl = this.box.querySelector(".dl"); // 下載
-
- // 圖片數量標籤
- count.innerText = `1/${urls[1].length}`;
- // 圖片下載按鈕
- dl.dataset.src = urls[2][0];
- dl.addEventListener("click", function() {
- GM_xmlhttpRequest({
- method: "GET",
- url: this.dataset.src,
- headers: { referer: "https://www.pixiv.net/" },
- responseType: "blob",
- onload: function (e) {
- let blob = window.URL.createObjectURL(e.response);
- let a = document.createElement("a");
- a.href = blob;
- a.download = e.finalUrl.match(/[0-9]+\_p[0-9]+\..+/g)[0];
- a.click();
- }
- });
- });
-
- if(urls[1].length == 1) {
- this._addImg([urls[0], urls[1][0]]);
- return;
- }
-
- // 提前載入圖片
- for(let i = 0; i <= this.adv && i < urls[1].length; i++) {
- this._addImg([urls[0], urls[1][i]]);
- }
- // 滾輪移動監聽器
- imgpv.addEventListener("mousewheel", function(e) {
- e.preventDefault();
- // 滾輪往下或往上
- if (e.deltaY > 0 && cursor < urls[1].length - 1) {
- cursor++;
- if (cursor == imgpv.childNodes.length - this.adv && cursor < urls[1].length - this.adv) {
- this._addImg([urls[0], urls[1][cursor + this.adv]]);
- }
- imgpv.childNodes[cursor - 1].style.display = "none";
- imgpv.childNodes[cursor].style.display = "block";
- } else if (e.deltaY < 0 && cursor > 0) {
- cursor--;
- imgpv.childNodes[cursor + 1].style.display = "none";
- imgpv.childNodes[cursor].style.display = "block";
- }
- count.innerText = `${cursor + 1}/${urls[1].length}`;
- dl.dataset.src = urls[2][cursor];
- }.bind(this));
- }
- // [插入動圖方法]
- previewbox.prototype._addAm = function(imgurl) {
- let img = new Image();
- let canvas = document.createElement("canvas");
- let dl = this.box.querySelector(".dl"); // 下載
-
- this.box.querySelector(".imgpv").appendChild(canvas);
- img.src = imgurl.replace(/540x540_70/g, "600x600");
- img.onload = function() {
- canvas.width = this.width;
- canvas.height = this.height;
- }
- canvas.style.maxWidth = `${this.size}px`;
- canvas.style.maxHeight = `${this.size}px`;
- canvas.parentNode.classList.add("am");
- fetch(`https://www.pixiv.net/ajax/illust/${this.pid}/ugoira_meta`).then(function(response) {
- if (!response.ok) throw new Error(response.statusText);
- return response.json();
- }).then(function(json) {
- let options = {
- canvas: canvas,
- source: json.body.src,
- metadata: json.body,
- chunkSize: 300000,
- loop: true,
- };
- let box = this.box.querySelector(".under");
- let count = this.box.querySelector(".count");
- zp = new ZipImagePlayer(options);
- document.addEventListener("frame", function(e) {
- box.style.width = `${e.detail.frame / e.detail.count * 100}%`;
- count.innerText = `${e.detail.frame}/${e.detail.count}`;
- });
- // 圖片下載按鈕
- dl.dataset.src = json.body.originalSrc; // json.body.src(小圖) or json.body.originalSrc(原圖)
- dl.addEventListener("click", function() {
- GM_xmlhttpRequest({
- method: "GET",
- url: this.dataset.src,
- headers: { referer: "https://www.pixiv.net/" },
- responseType: "blob",
- onload: function (e) {
- let blob = window.URL.createObjectURL(e.response);
- let a = document.createElement("a");
- a.href = blob;
- a.download = e.finalUrl.match(/[0-9]+_ugoira/g)[0] + ".zip";
- a.click();
- }
- });
- });
- }.bind(this)).catch(function(err) {
- console.log("抓取動圖資料發生問題:", err);
- })
- }
- // [設定標籤]
- previewbox.prototype._addTags = function(tags) {
- let R18 = "";
- let nR18 = "";
- for(let i = 0; i < tags.length; i++) {
- if(tags[i].tag.match(/R-18G|R-18|R18|R18G/g)) {
- R18 += `${tags[i].tag} `;
- } else {
- nR18 += `${tags[i].tag} `;
- }
- }
- this.box.querySelector(".tags").innerHTML = `<span style='color: tomato'>${R18}</span>${nR18}`;
- }
- // [取得圖片ID]
- previewbox.prototype._getId = function() {
- if (this.target.nodeName == "A") {
- // return /illust_id=([0-9]+)/g.exec(this.target.href)[1];
- return /([0-9]+)/g.exec(this.target.href)[1];
- } else if (this.target.nodeName == "IMG") {
- return /\/img\/.+\/([0-9]+)/g.exec(this.target.src)[1];
- } else if (this.target.nodeName == "DIV") {
- return /\/img\/.+\/([0-9]+)/g.exec(this.target.style.backgroundImage)[1];
- }
- }
- // [取得圖片連結]
- previewbox.prototype._getImgurls = function(url, count) {
- let urls = [];
- let urls_o = [];
- let regexp = new RegExp("(\/img\/.+" + this.pid + ")", "g");
- let img_date_url = url.match(regexp)[0];
- for(let i = 0; i < count; i++) {
- urls.push(`https://i.pximg.net/c/600x600/img-master${img_date_url}_p${i}_master1200.jpg`);
- urls_o.push(url.replace(/p0/, `p${i}`));
- }
- return [`https://www.pixiv.net/member_illust.php?mode=medium&illust_id=${this.pid}`, urls, urls_o];
- }
- // [抓取圖片資料]
- // https://www.pixiv.net/ajax/illust/:illustId
- // https://www.pixiv.net/ajax/illust/:illustId/pages
- // https://www.pixiv.net/ajax/tags/illust/:illustId
- // https://www.pixiv.net/ajax/user/:userId
- previewbox.prototype._getData = function(fun) {
- fetch(`https://www.pixiv.net/ajax/illust/${this.pid}`).then(function(response) {
- if (!response.ok) throw new Error(response.statusText);
- return response.json();
- }).then(fun).catch(function(err) {
- console.log("抓取圖片資料發生問題:", err)
- })
- }
-
- // ========== pixiv zipplayer ========== //
- function ZipImagePlayer(options) {
- this._Uint8Array = (window.Uint8Array || window.WebKitUint8Array || window.MozUint8Array || window.MSUint8Array);
- this._DataView = (window.DataView || window.WebKitDataView || window.MozDataView || window.MSDataView);
- this._ArrayBuffer = (window.ArrayBuffer || window.WebKitArrayBuffer || window.MozArrayBuffer || window.MSArrayBuffer);
- this.op = options;
- this._loadingState = 0;
- this._context = options.canvas.getContext("2d");
- this._files = {};
- this._frameCount = this.op.metadata.frames.length;
- this._frame = 0;
- this._loadFrame = 0;
- this._frameImages = [];
- this._paused = false;
- this._loadTimer = null;
- this._startLoad();
- this._loadXhr = null;
- }
- ZipImagePlayer.prototype = {
- _trailerBytes: 30000,
- _load: function(offset, length, callback) {
- var _this = this;
- var xhr = new XMLHttpRequest();
- xhr.addEventListener("load", function(ev) {
- if (!zp) return;
- if (xhr.status == 200) {
- offset = 0;
- length = xhr.response.byteLength;
- _this._len = length;
- _this._buf = xhr.response;
- _this._bytes = new _this._Uint8Array(_this._buf);
- } else {
- _this._bytes.set(new _this._Uint8Array(xhr.response), offset);
- }
- if (callback) {
- callback.apply(_this, [offset, length]);
- }
- }, false);
- xhr.open("GET", this.op.source);
- xhr.responseType = "arraybuffer";
- if (offset != null && length != null) {
- var end = offset + length;
- xhr.setRequestHeader("Range", "bytes=" + offset + "-" + (end - 1));
- if (this._isSafari) {
- xhr.setRequestHeader("Cache-control", "no-cache");
- xhr.setRequestHeader("If-None-Match", Math.random().toString());
- }
- }
- this._loadXhr = xhr;
- this._loadXhr.send();
- },
- _startLoad: function() {
- var _this = this;
- var http = new XMLHttpRequest();
- http.open('HEAD', this.op.source);
- http.onreadystatechange = function() {
- if (this.readyState == this.DONE) {
- _this._pHead = 0;
- _this._pNextHead = 0;
- _this._pFetch = 0;
- var len = parseInt(this.getResponseHeader("Content-Length"));
- _this._len = len;
- _this._buf = new _this._ArrayBuffer(len);
- _this._bytes = new _this._Uint8Array(_this._buf);
- var off = len - _this._trailerBytes;
- if (off < 0) {
- off = 0;
- }
- _this._pTail = len;
- _this._load(off, len - off, function(off, len) {
- _this._pTail = off;
- _this._findCentralDirectory();
- });
- }
- };
- http.send();
- },
- _findCentralDirectory: function() {
- var dv = new this._DataView(this._buf, this._len - 22, 22);
- var cd_count = dv.getUint16(10, true);
- var cd_size = dv.getUint32(12, true);
- var cd_off = dv.getUint32(16, true);
- if (cd_off < this._pTail) {
- this._load(cd_off, this._pTail - cd_off, function() {
- this._pTail = cd_off;
- this._readCentralDirectory(cd_off, cd_size, cd_count);
- });
- } else {
- this._readCentralDirectory(cd_off, cd_size, cd_count);
- }
- },
- _readCentralDirectory: function(offset, size, count) {
- var dv = new this._DataView(this._buf, offset, size);
- var p = 0;
- for (var i = 0; i < count; i++ ) {
- var compMethod = dv.getUint16(p + 10, true);
- var uncompSize = dv.getUint32(p + 24, true);
- var nameLen = dv.getUint16(p + 28, true);
- var extraLen = dv.getUint16(p + 30, true);
- var cmtLen = dv.getUint16(p + 32, true);
- var off = dv.getUint32(p + 42, true);
- p += 46;
- var nameView = new this._Uint8Array(this._buf, offset + p, nameLen);
- var name = "";
- for (var j = 0; j < nameLen; j++) {
- name += String.fromCharCode(nameView[j]);
- }
- p += nameLen + extraLen + cmtLen;
- this._files[name] = {off: off, len: uncompSize};
- }
- if (this._pHead >= this._pTail) {
- this._pHead = this._len;
- document.dispatchEvent(new CustomEvent("loadProgress", {detail: this._pHead / this._len}));
- this._loadNextFrame();
- } else {
- this._loadNextChunk();
- this._loadNextChunk();
- }
- },
- _loadNextChunk: function() {
- if (this._pFetch >= this._pTail) {
- return;
- }
- var off = this._pFetch;
- var len = this.op.chunkSize;
- if (this._pFetch + len > this._pTail) {
- len = this._pTail - this._pFetch;
- }
- this._pFetch += len;
- this._load(off, len, function() {
- if (off == this._pHead) {
- if (this._pNextHead) {
- this._pHead = this._pNextHead;
- this._pNextHead = 0;
- } else {
- this._pHead = off + len;
- }
- if (this._pHead >= this._pTail) {
- this._pHead = this._len;
- }
- document.dispatchEvent(new CustomEvent("loadProgress", {detail: this._pHead / this._len}));
- if (!this._loadTimer) {
- this._loadNextFrame();
- }
- } else {
- this._pNextHead = off + len;
- }
- this._loadNextChunk();
- });
- },
- _fileDataStart: function(offset) {
- var dv = new DataView(this._buf, offset, 30);
- var nameLen = dv.getUint16(26, true);
- var extraLen = dv.getUint16(28, true);
- return offset + 30 + nameLen + extraLen;
- },
- _isFileAvailable: function(name) {
- var info = this._files[name];
- if (!info) {
- this._error("File " + name + " not found in ZIP");
- }
- if (this._pHead < (info.off + 30)) {
- return false;
- }
- return this._pHead >= (this._fileDataStart(info.off) + info.len);
- },
- _loadNextFrame: function() {
- if (this._dead) {
- return;
- }
- var frame = this._loadFrame;
- if (frame >= this._frameCount) {
- return;
- }
- var meta = this.op.metadata.frames[frame];
- if (!this.op.source) {
- // Unpacked mode (individiual frame URLs)
- this._loadFrame += 1;
- this._loadImage(frame, meta.file, false);
- return;
- }
- if (!this._isFileAvailable(meta.file)) {
- return;
- }
- this._loadFrame += 1;
- var off = this._fileDataStart(this._files[meta.file].off);
- var end = off + this._files[meta.file].len;
- var url;
- var mime_type = this.op.metadata.mime_type || "image/png";
- if (this._URL) {
- var slice;
- if (!this._buf.slice) {
- slice = new this._ArrayBuffer(this._files[meta.file].len);
- var view = new this._Uint8Array(slice);
- view.set(this._bytes.subarray(off, end));
- } else {
- slice = this._buf.slice(off, end);
- }
- var blob;
- try {
- blob = new this._Blob([slice], {type: mime_type});
- }
- catch (err) {
- var bb = new this._BlobBuilder();
- bb.append(slice);
- blob = bb.getBlob();
- }
- url = this._URL.createObjectURL(blob);
- this._loadImage(frame, url, true);
- } else {
- url = ("data:" + mime_type + ";base64,"
- + base64ArrayBuffer(this._buf, off, end - off));
- this._loadImage(frame, url, false);
- }
- },
- _loadImage: function(frame, url, isBlob) {
- var _this = this;
- var image = new Image();
- var meta = this.op.metadata.frames[frame];
- image.addEventListener('load', function() {
- if (isBlob) {
- _this._URL.revokeObjectURL(url);
- }
- if (_this._dead) {
- return;
- }
- _this._frameImages[frame] = image;
- document.dispatchEvent(new CustomEvent("frameLoaded", [frame]));
- if (_this._loadingState == 0) {
- _this._displayFrame.apply(_this);
- }
- if (frame >= (_this._frameCount - 1)) {
- _this._setLoadingState(2);
- _this._buf = null;
- _this._bytes = null;
- } else {
- if (!_this._maxLoadAhead ||
- (frame - _this._frame) < _this._maxLoadAhead) {
- _this._loadNextFrame();
- } else if (!_this._loadTimer) {
- _this._loadTimer = setTimeout(function() {
- _this._loadTimer = null;
- _this._loadNextFrame();
- }, 200);
- }
- }
- });
- image.src = url;
- },
- _setLoadingState: function(state) {
- if (this._loadingState != state) {
- this._loadingState = state;
- document.dispatchEvent(new CustomEvent("loadingStateChanged", [state]));
- }
- },
- _displayFrame: function() {
- if (this._dead) {
- return;
- }
- var _this = this;
- var meta = this.op.metadata.frames[this._frame];
- var image = this._frameImages[this._frame];
- if (!image) {
- this._setLoadingState(0);
- return;
- }
- if (this._loadingState != 2) {
- this._setLoadingState(1);
- }
- if (this.op.autosize) {
- if (this._context.canvas.width != image.width || this._context.canvas.height != image.height) {
- // make the canvas autosize itself according to the images drawn on it
- // should set it once, since we don't have variable sized frames
- this._context.canvas.width = image.width;
- this._context.canvas.height = image.height;
- }
- };
- this._context.clearRect(0, 0, this.op.canvas.width,
- this.op.canvas.height);
- this._context.drawImage(image, 0, 0);
- document.dispatchEvent(new CustomEvent("frame", { detail: { frame: this._frame, count: this._frameCount } }));
- if (!this._paused) {
- this._timer = setTimeout(function() {
- _this._timer = null;
- _this._nextFrame.apply(_this);
- }, meta.delay);
- }
- },
- _nextFrame: function(frame) {
- if (this._frame >= (this._frameCount - 1)) {
- if (this.op.loop) {
- this._frame = 0;
- } else {
- this.pause();
- return;
- }
- } else {
- this._frame += 1;
- }
- this._displayFrame();
- },
- play: function() {
- if (this._dead) {
- return;
- }
- if (this._paused) {
- document.dispatchEvent(new CustomEvent("play", [this._frame]));
- this._paused = false;
- this._displayFrame();
- }
- },
- getCurrentFrame: function() {
- return this._frame;
- },
- getLoadedFrames: function() {
- return this._frameImages.length;
- },
- getFrameCount: function() {
- return this._frameCount;
- },
- loadAbort: function() {
- this._loadXhr.abort()
- this._loadXhr = null;
- }
- }
- function base64ArrayBuffer(arrayBuffer, off, byteLength) {
- var base64 = '';
- var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
- var bytes = new Uint8Array(arrayBuffer);
- var byteRemainder = byteLength % 3;
- var mainLength = off + byteLength - byteRemainder;
- var a, b, c, d;
- var chunk;
- // Main loop deals with bytes in chunks of 3
- for (var i = off; 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;
- }
- })();