Full Code of listen1/listen1-api for AI

master aa4b9d34aad5 cached
37 files
200.4 KB
77.0k tokens
83 symbols
1 requests
Download .txt
Showing preview only (215K chars total). Download the full file or copy to clipboard to get everything.
Repository: listen1/listen1-api
Branch: master
Commit: aa4b9d34aad5
Files: 37
Total size: 200.4 KB

Directory structure:
gitextract_kcqnihu1/

├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.json
├── .gitignore
├── .nvmrc
├── LICENSE
├── README.md
├── docs/
│   ├── .nojekyll
│   ├── README.md
│   ├── _sidebar.md
│   ├── about.md
│   ├── api.md
│   ├── changelog.md
│   ├── developer.md
│   ├── index.html
│   ├── quickstart.md
│   └── thanks.md
├── package.json
├── src/
│   ├── crypto/
│   │   ├── aes.js
│   │   ├── big-integer.js
│   │   ├── crypto.js
│   │   └── md5.js
│   ├── hack_header.js
│   ├── index.js
│   ├── platform/
│   │   ├── browser.js
│   │   └── node.js
│   ├── provider/
│   │   ├── bilibili.js
│   │   ├── kugou.js
│   │   ├── kuwo.js
│   │   ├── netease.js
│   │   ├── qq.js
│   │   └── xiami.js
│   └── utils.js
├── test/
│   ├── index.spec.js
│   └── mocha.opts
└── webpack.config.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .babelrc
================================================
{
  "presets": ["env"],
  "plugins": ["babel-plugin-add-module-exports"],
  "env": {
    "test": {
      "plugins": [
        ["istanbul"]
      ]
    }
  }
}


================================================
FILE: .editorconfig
================================================
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = LF
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false


================================================
FILE: .eslintignore
================================================
/dist

================================================
FILE: .eslintrc.json
================================================
{
  "extends": "airbnb-base",
  "rules": {
      "camelcase":"off",
      "import/named": "off"
  }
}

================================================
FILE: .gitignore
================================================
# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules

# Remove some common IDE working directories
.idea
.vscode

.DS_Store

dist/

================================================
FILE: .nvmrc
================================================
v6.10


================================================
FILE: LICENSE
================================================
The MIT License (MIT)

Copyright (c) 2019 Listen 1

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.



================================================
FILE: README.md
================================================
# listen1-api

listen1音乐资源API库,可运行在 Nodejs 和 浏览器环境(限chrome extension 或 electron)

## 特性

* 集成网易,QQ,虾米,酷狗,酷我,Bilibili平台的音乐资源API
* 支持获取热门歌单,歌手歌单,专辑歌单
* 支持搜索歌曲
* 支持获取歌曲歌词信息
* 支持获取歌曲的播放地址

## 编译
```
$ git clone git@github.com:listen1/listen1-api.git
$ cd listen1-api
$ yarn install
$ yarn build
```
编译后文件在dist目录下,`listen1-api.js`和`listen1-api.min.js`

## 安装
浏览器环境
```html
<script src="listen1-api.min.js"></script>
<script>
  console.log(listen1Api);
</script>
```
Nodejs环境
```javascript
const listen1Api = require('./listen1-api.min');
```

## 开始使用 (nodejs 环境)

下载压缩版本,或dist目录下的`listen1-api.min.js`到本地目录

```javascript
const listen1Api = require('./listen1-api.min');
const platform = 'netease';
// 获取网易平台的热门歌单列表
const url = '/show_playlist?source='+platform;

listen1Api.apiGet(url).then((data) => {
  console.log(data);
});
```

## 文档
https://listen1.github.io/listen1-api

## 项目技术
* 使用[webpack-library-starter](https://github.com/krasimir/webpack-library-starter.git)模板建立项目。
* 基于Webpack 4打包。
* ES6 语法。
* 导出[umd](https://github.com/umdjs/umd)格式的包,支持在浏览器环境和nodejs环境运行。
* ES6 测试基于 [Mocha](http://mochajs.org/) 和 [Chai](http://chaijs.com/)。
* 使用[ESLint](http://eslint.org/)进行语法检查。

## 开发使用

1. 编译生成
  * 运行 `yarn install` (推荐) 或 `npm install` ,安装依赖包。
  * 运行 `yarn build` 或 `npm run build` 来生成压缩版本的库文件。
2. 开发者模式
  * 运行 `yarn dev` 或 `npm run dev` 安装依赖包。生成一个非压缩版本的库文件并且在文件变化时自动重新编译。
3. 运行测试
  * 运行 `yarn test` 或 `npm run test`。

## 常用命令

* `yarn build` or `npm run build` - 在dist目录下编译生成正式版的库文件。
* `yarn dev` or `npm run dev` - 编译生成dev版本的库并实时更新。
* `yarn test` or `npm run test` - 运行测试。
* `yarn test:watch` or `npm run test:watch` - 在watch模式运行测试。


================================================
FILE: docs/.nojekyll
================================================


================================================
FILE: docs/README.md
================================================
# listen1-api

listen1音乐资源API库,可运行在 Nodejs 和 浏览器环境(限chrome extension 或 electron)

## 特性

* 集成网易,QQ,虾米,酷狗,酷我,Bilibili平台的音乐资源API
* 支持获取热门歌单,歌手歌单,专辑歌单
* 支持搜索歌曲
* 支持获取歌曲歌词信息
* 支持获取歌曲的播放地址

## 编译
```
$ git clone git@github.com:listen1/listen1-api.git
$ cd listen1-api
$ yarn install
$ yarn build
```
编译后文件在dist目录下,`listen1-api.js`和`listen1-api.min.js`

## 安装
浏览器环境
```html
<script src="listen1-api.min.js"></script>
<script>
  console.log(listen1Api);
</script>
```
Nodejs环境
```javascript
const listen1Api = require('./listen1-api.min');
```

## 开始使用 (nodejs 环境)

下载压缩版本,或dist目录下的`listen1-api.min.js`到本地目录

```javascript
const listen1Api = require('./listen1-api.min');
const platform = 'netease';
// 获取网易平台的热门歌单列表
const url = '/show_playlist?source='+platform;

listen1Api.apiGet(url).then((data) => {
  console.log(data);
});
```

## 项目技术
* 使用[webpack-library-starter](https://github.com/krasimir/webpack-library-starter.git)模板建立项目。
* 基于Webpack 4打包。
* ES6 语法。
* 导出[umd](https://github.com/umdjs/umd)格式的包,支持在浏览器环境和nodejs环境运行。
* ES6 测试基于 [Mocha](http://mochajs.org/) 和 [Chai](http://chaijs.com/)。
* 使用[ESLint](http://eslint.org/)进行语法检查。

## 常用命令

* `yarn build` or `npm run build` - 在dist目录下编译生成正式版的库文件。
* `yarn dev` or `npm run dev` - 编译生成dev版本的库并实时更新。
* `yarn test` or `npm run test` - 运行测试。
* `yarn test:watch` or `npm run test:watch` - 在watch模式运行测试。


================================================
FILE: docs/_sidebar.md
================================================
- 开始使用
  - [快速开始](quickstart.md)

- 参考
  - [API](api.md)
  - [开发指南](developer.md)

- [关于](about.md)
- [致谢](thanks.md)
- [版本更新](changelog.md)

================================================
FILE: docs/about.md
================================================
# 关于

当前版本: 1.0.0

[listen-api](https://github.com/listen1/listen1-api) 是Listen1项目的一部分,主要提供其它项目访问音乐资源的API支持。



## 联系方式
* 项目文档主页: https://listen1.github.io/listen1-api
* Github: https://github.com/listen1/listen1-api
* 联系邮箱:githublisten1@gmail.com


## Listen1 相关项目

[listen1/listen1](https://github.com/listen1/listen1): 最初的网页版播放器,使用Python开发Web服务器。可以直接在服务器运行,也可使用打包的Windows版和Mac版在本地运行Web服务器

[listen1/listen1_chrome_extension](https://github.com/listen1/listen1_chrome_extension): Chrome和Firefox插件版

[listen1/listen1_desktop](https://github.com/listen1/listen1_desktop): Windows,Mac,Linux桌面版。使用Electron框架,基于Listen 1 Chrome插件版JS库开发

## 本文档生成工具

[docsify](https://docsify.js.org/#/)

## 开源协议
MIT


================================================
FILE: docs/api.md
================================================
# API

## apiGet
> listen1Api.apiGet(url, httpFunction, promiseFunction, cookieProviderClass) ⇒ Promise resolve(json_object)

访问音乐资源API

| 参数 | 类型 | 必须 | 描述 |
| --- | --- | --- | --- |
| url | <code>string</code> | 是| 音乐资源API, 详细参数规范见下面说明 |
| httpFunction | <code>object</code> | 否 | HTTP请求实现函数 |
| promiseFunction | <code>object</code> | 否 | Promise实现函数 |
| cookieProviderClass | <code>object</code> | 否 | Cookie管理实现类 |

除url以外的三个参数是兼容自定义运行环境时需要传入的,详细情况请参考 运行环境兼容性

### /show_playlist

获取平台热门歌单列表

| 参数 | 类型 | 必须 | 描述 |
| --- | --- | --- | --- |
| source | <code>string</code> | 是 | 平台名称(netease, qq, xiami, kugou, kuwo, bilibili) |
| offset | <code>number</code> | 否 | 列表起始位置,默认值为0。如第一次offset=0, 获得30个歌单(具体数值根据平台不同而不同),offset=30获取下30个|

返回

返回结果为Promise,resolve后为JSON对象。

| 字段 | 类型 | 描述 |
| --- | --- | --- |
| result | <code>[Playlist]</code> | 歌单数组,包含歌单对象 |


例子
```javascript
const listen1Api = require('./listen1-api');
const source = 'netease';
const url = '/show_playlist?source='+source;

listen1Api.apiGet(url).then((data) => {
  console.log(data);
});

// 如果node环境支持await语法
async function fetchShowplaylist() {
  const data = await listen1Api.apiGet(`/show_playlist?source=${platform}`);
  console.log(data);
}

fetchShowplaylist();
```

### /get_playlist

获取歌单详情

| 参数 | 类型 | 必须 | 描述 |
| --- | --- | --- | --- |
| list_id | <code>string</code> | 是 | 歌单id,歌单id可以是播放列表,也可以是歌手id,专辑id |

返回

返回结果为Promise,resolve后为JSON对象。

| 字段 | 类型 | 描述 |
| --- | --- | --- |
| info | <code>Playlist</code> | 歌单数组,包含歌单对象 |
| tracks | <code>[Track]</code> | 歌曲数组,包含歌曲对象 |

例子
```javascript
const listen1Api = require('./listen1-api');
const listId = 'neplaylist_762840531';
const url = '/get_playlist?list_id='+listId;

listen1Api.apiGet(url).then((data) => {
  console.log(data);
});
```

### /search

搜索歌曲

| 参数 | 类型 | 必须 | 描述 |
| --- | --- | --- | --- |
| source | <code>string</code> | 是 | 平台名称(netease, qq, xiami, kugou, kuwo, bilibili) |
| keywords | <code>string</code> | 是 | 搜索关键字 |
| curpage | <code>number</code> | 否 | 搜索页码,默认值为1 |

返回

返回结果为Promise,resolve后为JSON对象。

| 字段 | 类型 | 描述 |
| --- | --- | --- |
| total | <code>number</code> | 共搜索到的结果总数 |
| result | <code>[Track]</code> | 歌曲数组,包含歌曲对象 |


### /lyric

获取歌曲歌词信息

| 参数 | 类型 | 必须 | 描述 |
| --- | --- | --- | --- |
| track_id | <code>string</code> | 是 | 歌曲id |

返回

返回结果为Promise,resolve后为JSON对象。

| 字段 | 类型 | 描述 |
| --- | --- | --- |
| lyric | <code>string</code> | lyric格式的歌词信息 |

### /bootstrap_track

获取歌曲的播放地址

| 参数 | 类型 | 必须 | 描述 |
| --- | --- | --- | --- |
| track_id | <code>string</code> | 是 | 歌曲id |

返回

返回结果为Promise,resolve后为JSON对象。

| 字段 | 类型 | 描述 |
| --- | --- | --- |
| url | <code>string</code> | 歌曲的播放地址 |

## APIGET 返回结果 JSON 对象定义
### Playlist

歌单对象

| 字段 | 类型 | 描述 |
| --- | --- | --- |
| id | <code>string</code> | 歌单id,一般以平台前缀+playlist_[歌单id]作为标识,比如 qqplaylist_123456 |
| cover_img_url | <code>string</code> | 歌单封面url |
| title | <code>string</code> | 歌单标题 |
| source_url | <code>source_url</code> | 歌单来源网页url |

### Track

歌曲对象

| 字段 | 类型 | 描述 |
| --- | --- | --- |
| id | <code>string</code> | 歌曲id,一般以平台前缀+track_[歌曲id]作为标识,比如 qqtrack_123456 |
| img_url | <code>string</code> | 歌曲封面url |
| title | <code>string</code> | 歌曲标题 |
| source | <code>string</code> | 歌曲来源标识,比如netease |
| source_url | <code>string</code> | 歌曲来源网页url |
| artist | <code>string</code> | 歌手名 |
| artist_id | <code>string</code> | 歌手id, 一般以平台前缀+artist_[歌手id]作为标识,比如 qqartist_123456 |
| album | <code>string</code> | 专辑名 |
| album_id | <code>string</code> | 专辑id, 一般以平台前缀+album_[专辑id]作为标识,比如 qqalbum_123456 |
| lyric_url | <code>string</code> | 歌曲歌词url(可选,根据平台不同)  |
| url | <code>string</code> | 备用,现在值和歌曲id相同  |

## hackHeader
> listen1Api.hackHeader(url) ⇒ {}

跨域header处理,修改header中的Referer字段和Origin字段,使其符合同源规则

返回结果

| 字段 | 类型 | 描述 |
| --- | --- | --- |
| replace_referer | <code>bool</code> | 是否替代referer字段 |
| add_referer | <code>bool</code> | 是否增加referer字段 |
| replace_origin | <code>bool</code> | 是否替代origin字段 |
| add_origin | <code>bool</code> | 是否替代origin字段 |
| referer_value | <code>string</code> | referer或origin的修正值 |

================================================
FILE: docs/changelog.md
================================================
# 更新日志
本文件记录项目的所有更新。

格式基于 [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
版本号规则基于 [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.0] - 2019-01-15
### 新增
- 首次发布,基本功能和文档


================================================
FILE: docs/developer.md
================================================
## 开发使用

1. 编译生成
  * 运行 `yarn install` (推荐) 或 `npm install` ,安装依赖包。
  * 运行 `yarn build` 或 `npm run build` 来生成压缩版本的库文件。
2. 开发者模式
  * 运行 `yarn dev` 或 `npm run dev` 安装依赖包。生成一个非压缩版本的库文件并且在文件变化时自动重新编译。
3. 运行测试
  * 运行 `yarn test` 或 `npm run test`。

## 运行环境兼容性

listen1-api本身是umd库,支持运行在nodejs和浏览器环境。但其中一些功能和运行环境相关。所以,如果需要兼容多种兼容环境时,请考虑按照一下接口定义扩展如下类:
httpFunction, promiseFunction, cookieProviderClass。

listen1-api默认会根据程序环境选择默认的类,可以参考 `/platform` 目录下的代码

### httpFunction
> httpFunction(params) ⇒ Promise resolve(json_object)

http请求函数

params是一个字典,包含参数如下

| 参数 | 类型 | 必须 | 描述 |
| --- | --- | --- | --- |
| url | <code>string</code> | 是| 访问url |
| method | <code>string</code> | 是 |  |
| transformResponse | <code>bool</code> | 否 | 是否将结果转换为JSON对象 |
| data | <code>object</code> | 否 | 请求为POST时,传递的表单内容 |
| cookieProvider | <code>object</code> | 否 | cookie管理类 |


### promiseFunction
> promiseFunction((resolve, reject)=>{}) ⇒ Promise

api使用的promise函数。一般都为系统默认的 new Promise,但angularjs中需要传入`$q`,否则不会触发数据刷新。

### cookieProviderClass
cookie管理类

需要实现如下接口

getCookie

| 参数 | 类型 | 必须 | 描述 |
| --- | --- | --- | --- |
| url | <code>string</code> | 是| 访问url |
| name | <code>string</code> | 是| cookie名 |
| callback | <code>string</code> | 是| 回调函数 |

其他接口根据运行环境中的httpFunction需要实现。

================================================
FILE: docs/index.html
================================================
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>listen1-api - Produce universal library with webpack and es6</title>
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
  <meta name="description" content="Produce universal library with webpack and es6">
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <link rel="stylesheet" href="//unpkg.com/docsify/lib/themes/vue.css">
</head>
<body>
  <div id="app"></div>
  <script>
    window.$docsify = {
      name: 'listen1-api',
      loadSidebar: true,
      subMaxLevel: 3,
      repo: 'https://github.com/listen1/listen1-api'
    }
  </script>
  <script src="//unpkg.com/docsify/lib/docsify.min.js"></script>
</body>
</html>


================================================
FILE: docs/quickstart.md
================================================
# 快速开始

## 下载
* 压缩版本 [下载](https://github.com/listen1/listen1-api/releases/download/v1.0.0/listen1-api.min.js)
* 非压缩版本[下载](https://github.com/listen1/listen1-api/releases/download/v1.0.0/listen1-api.js)

## 安装
浏览器环境
```html
<script src="listen1-api.min.js"></script>
<script>
  console.log(listen1Api);
</script>
```
Nodejs环境
```javascript
const listen1Api = require('./listen1-api.min');
```

## 开始使用 (nodejs 环境)

下载压缩版本,或dist目录下的`listen1-api.min.js`到本地目录

```javascript
const listen1Api = require('./listen1-api.min');
const platform = 'netease';
// 获取网易平台的热门歌单列表
const url = '/show_playlist?source='+platform;

listen1Api.apiGet(url).then((data) => {
  console.log(data);
});
```

================================================
FILE: docs/thanks.md
================================================
## 致谢

纪念 亚伦·斯沃茨 (Aaron Swartz) (1986-2013)

感谢开源社区的开发者,为Listen 1贡献了精彩的代码。感谢所有支持本开源项目的朋友。

特别感谢@Dumeng对Listen 1 ES6升级的贡献。


================================================
FILE: package.json
================================================
{
  "name": "listen1-api",
  "version": "1.0.0",
  "description": "One API for all free music in China",
  "main": "dist/listen1-api.js",
  "scripts": {
    "prebuild": "webpack --env dev && webpack --env build",
    "build": "webpack --env dev && webpack --env build && npm run test",
    "dev": "webpack --progress --colors --watch --env dev",
    "test": "mocha --require babel-register --colors ./test/*.spec.js",
    "test:watch": "mocha --require babel-register --colors -w ./test/*.spec.js",
    "test:cover": "cross-env NODE_ENV=test nyc mocha --require babel-register --colors test/*.js",
    "repl": "node -i -e \"$(< ./lib/webpack-library-starter.js)\""
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/listen1/listen1-api.git"
  },
  "keywords": [
    "listen1",
    "music"
  ],
  "author": "Listen 1",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/listen1/listen1-api/issues"
  },
  "homepage": "https://listen1.github.io/listen1-api",
  "devDependencies": {
    "@babel/cli": "^7.0.0-beta.51",
    "@babel/core": "^7.0.0-beta.51",
    "@babel/preset-env": "^7.0.0-beta.51",
    "babel-eslint": "^8.0.3",
    "babel-loader": "^8.0.0-beta.4",
    "babel-plugin-add-module-exports": "^0.2.1",
    "babel-plugin-istanbul": "^5.1.0",
    "babel-preset-env": "^7.0.0-beta.3",
    "babel-register": "^7.0.0-beta.3",
    "chai": "^4.1.2",
    "cross-env": "^5.2.0",
    "eslint": "^5.12.0",
    "eslint-config-airbnb-base": "^13.1.0",
    "eslint-loader": "^2.0.0",
    "eslint-plugin-import": "^2.14.0",
    "jsdom": "11.11.0",
    "jsdom-global": "3.0.2",
    "mocha": "^4.0.1",
    "nyc": "^13.1.0",
    "uglifyjs-webpack-plugin": "^1.2.7",
    "webpack": "^4.12.2",
    "webpack-cli": "^3.0.8",
    "yargs": "^10.0.3"
  },
  "nyc": {
    "sourceMap": false,
    "instrument": false
  },
  "dependencies": {
    "async": "^2.6.1",
    "cheerio": "^1.0.0-rc.2",
    "entities": "^1.1.2",
    "request": "^2.88.0",
    "text-encoding": "^0.7.0"
  }
}


================================================
FILE: src/crypto/aes.js
================================================
"use strict";

(function(root) {

    var createBuffer = null, copyBuffer = null, convertBytesToString, convertStringToBytes = null;

    var slowCreateBuffer = function(arg) {

        // Passed in a single number, the length to pre-allocate
        if (typeof arg === 'number') {
            var result = [];
            for (var i = 0; i < arg; i++) {
                result.push(0);
            }
            return result;

        } else  {
            // Make sure they are passing sensible data
            for (var i = 0; i < arg.length; i++) {
                if (arg[i] < 0 || arg[i] >= 256 || typeof arg[i] !== 'number') {
                    throw new Error('invalid byte at index ' + i + '(' + arg[i] + ')');
                }
            }

            // Most array-like things should support this
            if (arg.slice) {
                return arg.slice(0);
            }

            // Something *weird*; copy it into an array (see PR#2)
            var result = [];
            for (var i = 0; i < arg.length; i++) {
                result.push(arg[i]);
            }
            return result;
        }
    }

    if (typeof(Buffer) === 'undefined') {
        createBuffer = slowCreateBuffer;

        copyBuffer = function(sourceBuffer, targetBuffer, targetStart, sourceStart, sourceEnd) {
            if (targetStart == null) { targetStart = 0; }
            if (sourceStart == null) { sourceStart = 0; }
            if (sourceEnd == null) { sourceEnd = sourceBuffer.length; }
            for (var i = sourceStart; i < sourceEnd; i++) {
                targetBuffer[targetStart++] = sourceBuffer[i];
            }
        }

        convertStringToBytes = function(text, encoding) {

            // "utf8", "utf-8", "utf 8", etc
            if (encoding == null || encoding.toLowerCase().replace(/ |-/g, "") == 'utf8') {
                var result = [], i = 0;
                text = encodeURI(text);
                while (i < text.length) {
                    var c = text.charCodeAt(i++);

                    // if it is a % sign, encode the following 2 bytes as a hex value
                    if (c === 37) {
                        result.push(parseInt(text.substr(i, 2), 16))
                        i += 2;

                    // otherwise, just the actual byte
                    } else {
                        result.push(c)
                    }
                }

                return result;

            // "hex"
            } else if (encoding.toLowerCase() == 'hex') {
                var result = [];
                for (var i = 0; i < text.length; i += 2) {
                    result.push(parseInt(text.substr(i, 2), 16));
                }

                return result;
            }

            // @TODO: Base64...

            return null;
        }

        // http://ixti.net/development/javascript/2011/11/11/base64-encodedecode-of-utf8-in-browser-with-js.html
        var Hex = '0123456789abcdef';
        convertBytesToString = function(bytes, encoding) {

            // "utf8", "utf-8", "utf 8", etc
            if (encoding == null || encoding.toLowerCase().replace(/ |-/g, "") == 'utf8') {
                var result = [], i = 0;

                while (i < bytes.length) {
                    var c = bytes[i];

                    if (c < 128) {
                        result.push(String.fromCharCode(c));
                        i++;
                    } else if (c > 191 && c < 224) {
                        result.push(String.fromCharCode(((c & 0x1f) << 6) | (bytes[i + 1] & 0x3f)));
                        i += 2;
                    } else {
                        result.push(String.fromCharCode(((c & 0x0f) << 12) | ((bytes[i + 1] & 0x3f) << 6) | (bytes[i + 2] & 0x3f)));
                        i += 3;
                    }
                }

                return result.join('');

            // "hex"
            } else if (encoding.toLowerCase() == 'hex') {
                var result = [];
                for (var i = 0; i < bytes.length; i++) {
                    var v = bytes[i];
                    result.push(Hex[(v & 0xf0) >> 4] + Hex[v & 0x0f]);
                }
                return result.join('');
            }

            return result
        }

    } else {
        createBuffer = function(arg) { return new Buffer(arg); }

        copyBuffer = function(sourceBuffer, targetBuffer, targetStart, sourceStart, sourceEnd) {
            sourceBuffer.copy(targetBuffer, targetStart, sourceStart, sourceEnd);
        }

        convertStringToBytes = function(text, encoding) {
            return new Buffer(text, encoding);
        }

        convertBytesToString = function(bytes, encoding) {
            return (new Buffer(bytes)).toString(encoding);
        }
    }


    // Number of rounds by keysize
    var numberOfRounds = {16: 10, 24: 12, 32: 14}

    // Round constant words
    var rcon = [0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91];

    // S-box and Inverse S-box (S is for Substitution)
    var S = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16];
    var Si =[0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d];

    // Transformations for encryption
    var T1 = [0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a];
    var T2 = [0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616];
    var T3 = [0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16];
    var T4 = [0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c];

    // Transformations for decryption
    var T5 = [0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742];
    var T6 = [0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857];
    var T7 = [0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8];
    var T8 = [0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0];

    // Transformations for decryption key expansion
    var U1 = [0x00000000, 0x0e090d0b, 0x1c121a16, 0x121b171d, 0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331, 0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45, 0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69, 0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad, 0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381, 0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5, 0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9, 0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66, 0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a, 0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e, 0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012, 0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6, 0x038f5fe7, 0x0d8652ec, 0x1f9d45f1, 0x119448fa, 0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e, 0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2, 0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb, 0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7, 0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3, 0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f, 0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b, 0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77, 0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203, 0x058ae132, 0x0b83ec39, 0x1998fb24, 0x1791f62f, 0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190, 0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc, 0x0605bed5, 0x080cb3de, 0x1a17a4c3, 0x141ea9c8, 0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4, 0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120, 0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c, 0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978, 0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54, 0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea, 0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6, 0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2, 0x09808683, 0x07898b88, 0x15929c95, 0x1b9b919e, 0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a, 0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976, 0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502, 0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e, 0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691, 0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd, 0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9, 0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5, 0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621, 0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d, 0x0a0fd964, 0x0406d46f, 0x161dc372, 0x1814ce79, 0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55, 0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c, 0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430, 0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844, 0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68, 0x0c0a67b1, 0x02036aba, 0x10187da7, 0x1e1170ac, 0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480, 0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4, 0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8, 0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67, 0x0f853856, 0x018c355d, 0x13972240, 0x1d9e2f4b, 0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f, 0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713, 0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7, 0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb, 0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f, 0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3];
    var U2 = [0x00000000, 0x0b0e090d, 0x161c121a, 0x1d121b17, 0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23, 0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f, 0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b, 0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7, 0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3, 0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af, 0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b, 0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac, 0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498, 0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4, 0x0f9357e7, 0x049d5eea, 0x198f45fd, 0x12814cf0, 0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c, 0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448, 0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814, 0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20, 0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a, 0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e, 0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512, 0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126, 0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa, 0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e, 0x1e3daed5, 0x1533a7d8, 0x0821bccf, 0x032fb5c2, 0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6, 0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1, 0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5, 0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9, 0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d, 0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611, 0x11aef932, 0x1aa0f03f, 0x07b2eb28, 0x0cbce225, 0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79, 0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d, 0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd, 0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9, 0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5, 0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91, 0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d, 0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329, 0x1fd13462, 0x14df3d6f, 0x09cd2678, 0x02c32f75, 0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41, 0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76, 0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842, 0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e, 0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a, 0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6, 0x10426385, 0x1b4c6a88, 0x065e719f, 0x0d507892, 0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce, 0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa, 0x01ec9ab7, 0x0ae293ba, 0x17f088ad, 0x1cfe81a0, 0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594, 0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8, 0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc, 0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170, 0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544, 0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918, 0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c, 0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b, 0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f, 0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273, 0x0e7fcd50, 0x0571c45d, 0x1863df4a, 0x136dd647, 0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb, 0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff, 0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3, 0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697];
    var U3 = [0x00000000, 0x0d0b0e09, 0x1a161c12, 0x171d121b, 0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f, 0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253, 0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77, 0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b, 0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf, 0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3, 0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7, 0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920, 0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104, 0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968, 0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c, 0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0, 0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194, 0x03934be3, 0x0e9845ea, 0x198557f1, 0x148e59f8, 0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc, 0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d, 0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749, 0x05aedd3e, 0x08a5d337, 0x1fb8c12c, 0x12b3cf25, 0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701, 0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd, 0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9, 0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5, 0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791, 0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456, 0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72, 0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e, 0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a, 0x063d96dd, 0x0b3698d4, 0x1c2b8acf, 0x112084c6, 0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2, 0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e, 0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa, 0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7, 0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3, 0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf, 0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b, 0x0a47a17c, 0x074caf75, 0x1051bd6e, 0x1d5ab367, 0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43, 0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f, 0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b, 0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc, 0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8, 0x09d4ea9f, 0x04dfe496, 0x13c2f68d, 0x1ec9f884, 0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0, 0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c, 0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078, 0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814, 0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030, 0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81, 0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5, 0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9, 0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed, 0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11, 0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635, 0x0fe97c42, 0x02e2724b, 0x15ff6050, 0x18f46e59, 0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d, 0x0c7a37a1, 0x017139a8, 0x166c2bb3, 0x1b6725ba, 0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e, 0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2, 0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6, 0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a, 0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e, 0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562, 0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46];
    var U4 = [0x00000000, 0x090d0b0e, 0x121a161c, 0x1b171d12, 0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a, 0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562, 0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a, 0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2, 0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca, 0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582, 0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba, 0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9, 0x1f8f57e3, 0x16825ced, 0x0d9541ff, 0x04984af1, 0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9, 0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281, 0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629, 0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11, 0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59, 0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261, 0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf, 0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787, 0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf, 0x1a3182e5, 0x133c89eb, 0x082b94f9, 0x01269ff7, 0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f, 0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767, 0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f, 0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17, 0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064, 0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c, 0x05bed506, 0x0cb3de08, 0x17a4c31a, 0x1ea9c814, 0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c, 0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084, 0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc, 0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4, 0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc, 0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53, 0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b, 0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223, 0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b, 0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3, 0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b, 0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3, 0x105633e9, 0x195b38e7, 0x024c25f5, 0x0b412efb, 0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188, 0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0, 0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8, 0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0, 0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168, 0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50, 0x0fd9640a, 0x06d46f04, 0x1dc37216, 0x14ce7918, 0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520, 0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe, 0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6, 0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e, 0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6, 0x0a67b10c, 0x036aba02, 0x187da710, 0x1170ac1e, 0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026, 0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e, 0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856, 0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725, 0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d, 0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55, 0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d, 0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5, 0x15e8e6ef, 0x1ce5ede1, 0x07f2f0f3, 0x0efffbfd, 0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5, 0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d];


    function convertToInt32(bytes) {
        var result = [];
        for (var i = 0; i < bytes.length; i += 4) {
            result.push(
                (bytes[i    ] << 24) |
                (bytes[i + 1] << 16) |
                (bytes[i + 2] <<  8) |
                 bytes[i + 3]
            );
        }
        return result;
    }




    var AES = function(key) {
        this.key = createBuffer(key);
        this._prepare();
    }


    AES.prototype._prepare = function() {

        var rounds = numberOfRounds[this.key.length];
        if (rounds == null) {
            throw new Error('invalid key size (must be length 16, 24 or 32)');
        }

        // encryption round keys
        this._Ke = [];

        // decryption round keys
        this._Kd = [];

        for (var i = 0; i <= rounds; i++) {
            this._Ke.push([0, 0, 0, 0]);
            this._Kd.push([0, 0, 0, 0]);
        }

        var roundKeyCount = (rounds + 1) * 4;
        var KC = this.key.length / 4;

        // convert the key into ints
        var tk = convertToInt32(this.key);

        // copy values into round key arrays
        var index;
        for (var i = 0; i < KC; i++) {
            index = i >> 2;
            this._Ke[index][i % 4] = tk[i];
            this._Kd[rounds - index][i % 4] = tk[i];
        }

        // key expansion (fips-197 section 5.2)
        var rconpointer = 0;
        var t = KC, tt;
        while (t < roundKeyCount) {
            tt = tk[KC - 1];
            tk[0] ^= ((S[(tt >> 16) & 0xFF] << 24) ^
                      (S[(tt >>  8) & 0xFF] << 16) ^
                      (S[ tt        & 0xFF] <<  8) ^
                       S[(tt >> 24) & 0xFF]        ^
                      (rcon[rconpointer] << 24));
            rconpointer += 1;

            // key expansion (for non-256 bit)
            if (KC != 8) {
                for (var i = 1; i < KC; i++) {
                    tk[i] ^= tk[i - 1];
                }

            // key expansion for 256-bit keys is "slightly different" (fips-197)
            } else {
                for (var i = 1; i < (KC / 2); i++) {
                    tk[i] ^= tk[i - 1];
                }
                tt = tk[(KC / 2) - 1];

                tk[KC / 2] ^= (S[ tt        & 0xFF]        ^
                              (S[(tt >>  8) & 0xFF] <<  8) ^
                              (S[(tt >> 16) & 0xFF] << 16) ^
                              (S[(tt >> 24) & 0xFF] << 24));

                for (var i = (KC / 2) + 1; i < KC; i++) {
                    tk[i] ^= tk[i - 1];
                }
            }

            // copy values into round key arrays
            var i = 0, r, c;
            while (i < KC && t < roundKeyCount) {
                r = t >> 2;
                c = t % 4;
                this._Ke[r][c] = tk[i];
                this._Kd[rounds - r][c] = tk[i++];
                t++;
            }
        }

        // inverse-cipher-ify the decryption round key (fips-197 section 5.3)
        for (var r = 1; r < rounds; r++) {
            for (var c = 0; c < 4; c++) {
                tt = this._Kd[r][c];
                this._Kd[r][c] = (U1[(tt >> 24) & 0xFF] ^
                                  U2[(tt >> 16) & 0xFF] ^
                                  U3[(tt >>  8) & 0xFF] ^
                                  U4[ tt        & 0xFF]);
            }
        }
    }

    AES.prototype.encrypt = function(plaintext) {
        if (plaintext.length != 16) {
            return new Error('plaintext must be a block of size 16');
        }

        var rounds = this._Ke.length - 1;
        var a = [0, 0, 0, 0];

        // convert plaintext to (ints ^ key)
        var t = convertToInt32(plaintext);
        for (var i = 0; i < 4; i++) {
            t[i] ^= this._Ke[0][i];
        }

        // apply round transforms
        for (var r = 1; r < rounds; r++) {
            for (var i = 0; i < 4; i++) {
                a[i] = (T1[(t[ i         ] >> 24) & 0xff] ^
                        T2[(t[(i + 1) % 4] >> 16) & 0xff] ^
                        T3[(t[(i + 2) % 4] >>  8) & 0xff] ^
                        T4[ t[(i + 3) % 4]        & 0xff] ^
                        this._Ke[r][i]);
            }
            t = a.slice(0);
        }

        // the last round is special
        var result = createBuffer(16), tt;
        for (var i = 0; i < 4; i++) {
            tt = this._Ke[rounds][i];
            result[4 * i    ] = (S[(t[ i         ] >> 24) & 0xff] ^ (tt >> 24)) & 0xff;
            result[4 * i + 1] = (S[(t[(i + 1) % 4] >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
            result[4 * i + 2] = (S[(t[(i + 2) % 4] >>  8) & 0xff] ^ (tt >>  8)) & 0xff;
            result[4 * i + 3] = (S[ t[(i + 3) % 4]        & 0xff] ^  tt       ) & 0xff;
        }

        return result;
    }

    AES.prototype.decrypt = function(ciphertext) {
        if (ciphertext.length != 16) {
            return new Error('ciphertext must be a block of size 16');
        }

        var rounds = this._Kd.length - 1;
        var a = [0, 0, 0, 0];

        // convert plaintext to (ints ^ key)
        var t = convertToInt32(ciphertext);
        for (var i = 0; i < 4; i++) {
            t[i] ^= this._Kd[0][i];
        }

        // apply round transforms
        for (var r = 1; r < rounds; r++) {
            for (var i = 0; i < 4; i++) {
                a[i] = (T5[(t[ i          ] >> 24) & 0xff] ^
                        T6[(t[(i + 3) % 4] >> 16) & 0xff] ^
                        T7[(t[(i + 2) % 4] >>  8) & 0xff] ^
                        T8[ t[(i + 1) % 4]        & 0xff] ^
                        this._Kd[r][i]);
            }
            t = a.slice(0);
        }

        // the last round is special
        var result = createBuffer(16), tt;
        for (var i = 0; i < 4; i++) {
            tt = this._Kd[rounds][i];
            result[4 * i    ] = (Si[(t[ i         ] >> 24) & 0xff] ^ (tt >> 24)) & 0xff;
            result[4 * i + 1] = (Si[(t[(i + 3) % 4] >> 16) & 0xff] ^ (tt >> 16)) & 0xff;
            result[4 * i + 2] = (Si[(t[(i + 2) % 4] >>  8) & 0xff] ^ (tt >>  8)) & 0xff;
            result[4 * i + 3] = (Si[ t[(i + 1) % 4]        & 0xff] ^  tt       ) & 0xff;
        }

        return result;
    }


    /**
     *  Mode Of Operation - Electonic Codebook (ECB)
     */
    var ModeOfOperationECB = function(key) {
        this.description = "Electronic Code Block";
        this.name = "ecb";

        this._aes = new AES(key);
    }

    ModeOfOperationECB.prototype.encrypt = function(plaintext) {
        return this._aes.encrypt(plaintext);
    }

    ModeOfOperationECB.prototype.decrypt = function(ciphertext, encoding) {
        return this._aes.decrypt(ciphertext);
    }


    /**
     *  Mode Of Operation - Cipher Block Chaining (CBC)
     */
    var ModeOfOperationCBC = function(key, iv) {
        this.description = "Cipher Block Chaining";
        this.name = "cbc";

        if (!iv) {
            iv = createBuffer([0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);

        } else if (iv.length != 16) {
            return new Error('initialation vector iv must be of length 16');
        }

        this._lastCipherblock = createBuffer(iv);

        this._aes = new AES(key);
    }

    ModeOfOperationCBC.prototype.encrypt = function(plaintext) {
        if (plaintext.length != 16) {
            return new Error('plaintext must be a block of size 16');
        }

        var precipherblock = createBuffer(plaintext);
        for (var i = 0; i < 16; i++) {
            precipherblock[i] ^= this._lastCipherblock[i];
        }

        this._lastCipherblock = this._aes.encrypt(precipherblock);

        return this._lastCipherblock;
    }

    ModeOfOperationCBC.prototype.decrypt = function(ciphertext) {
        if (ciphertext.length != 16) {
            return new Error('ciphertext must be a block of size 16');
        }

        var plaintext = this._aes.decrypt(ciphertext);
        for (var i = 0; i < 16; i++) {
            plaintext[i] ^= this._lastCipherblock[i];
        }

        copyBuffer(ciphertext, this._lastCipherblock);

        return plaintext;
    }


    /**
     *  Mode Of Operation - Cipher Feedback (CFB)
     */
    var ModeOfOperationCFB = function(key, iv, segmentSize) {
        this.description = "Cipher Feedback";
        this.name = "cfb";

        if (!iv) {
            iv = createBuffer([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);

        } else if (iv.length != 16) {
            return new Error('initialation vector iv must be of length 16');
        }

        if (!segmentSize) { segmentSize = 1; }

        this.segmentSize = segmentSize;

        this._shiftRegister = createBuffer(iv);

        this._aes = new AES(key);
    }

    ModeOfOperationCFB.prototype.encrypt = function(plaintext) {
        if ((plaintext.length % this.segmentSize) != 0) {
            return new Error('plaintext must be a block of size module segmentSize (' + this.segmentSize + ')');
        }

        var encrypted = createBuffer(plaintext);

        var xorSegment;
        for (var i = 0; i < encrypted.length; i += this.segmentSize) {
            xorSegment = this._aes.encrypt(this._shiftRegister);
            for (var j = 0; j < this.segmentSize; j++) {
                encrypted[i + j] ^= xorSegment[j];
            }

            // Shift the register
            copyBuffer(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
            copyBuffer(encrypted, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
        }

        return encrypted;
    }

    ModeOfOperationCFB.prototype.decrypt = function(ciphertext) {
        if ((ciphertext.length % this.segmentSize) != 0) {
            return new Error('ciphertext must be a block of size module segmentSize (' + this.segmentSize + ')');
        }

        var plaintext = createBuffer(ciphertext);

        var xorSegment;
        for (var i = 0; i < plaintext.length; i += this.segmentSize) {
            xorSegment = this._aes.encrypt(this._shiftRegister);

            for (var j = 0; j < this.segmentSize; j++) {
                plaintext[i + j] ^= xorSegment[j];
            }

            // Shift the register
            copyBuffer(this._shiftRegister, this._shiftRegister, 0, this.segmentSize);
            copyBuffer(ciphertext, this._shiftRegister, 16 - this.segmentSize, i, i + this.segmentSize);
        }

        return plaintext;
    }

    /**
     *  Mode Of Operation - Output Feedback (OFB)
     */
    var ModeOfOperationOFB = function(key, iv) {
        this.description = "Output Feedback";
        this.name = "ofb";

        if (!iv) {
            iv = createBuffer([0, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);

        } else if (iv.length != 16) {
            return new Error('initialation vector iv must be of length 16');
        }

        this._lastPrecipher = createBuffer(iv);
        this._lastPrecipherIndex = 16;

        this._aes = new AES(key);
    }

    ModeOfOperationOFB.prototype.encrypt = function(plaintext) {
        var encrypted = createBuffer(plaintext);

        for (var i = 0; i < encrypted.length; i++) {
            if (this._lastPrecipherIndex === 16) {
                this._lastPrecipher = this._aes.encrypt(this._lastPrecipher);
                this._lastPrecipherIndex = 0;
            }
            encrypted[i] ^= this._lastPrecipher[this._lastPrecipherIndex++];
        }

        return encrypted;
    }

    // Decryption is symetric
    ModeOfOperationOFB.prototype.decrypt = ModeOfOperationOFB.prototype.encrypt;


    /**
     *  Counter object for CTR common mode of operation
     */
    var Counter = function(initialValue) {
        // We allow 0, but anything false-ish uses the default 1
        if (initialValue !== 0 && !initialValue) { initialValue = 1; }

        if (typeof(initialValue) === 'number') {
            this._counter = createBuffer([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
            this.setValue(initialValue);

        } else {
            this.setBytes(initialValue);
        }
    }

    Counter.prototype.setValue = function(value) {
        if (typeof(value) !== 'number' || parseInt(value) != value) {
            throw new Error('value must be an integer');
        }

        for (var index = 15; index >= 0; --index) {
            this._counter[index] = value % 256;
            value = value >> 8;
        }
    }

    Counter.prototype.setBytes = function(bytes) {
        if (bytes.length != 16) {
            throw new Error('invalid counter bytes size (must be 16)');
        }
        this._counter = createBuffer(bytes);
    };

    Counter.prototype.increment = function() {
        for (var i = 15; i >= 0; i--) {
            if (this._counter[i] === 255) {
                this._counter[i] = 0;
            } else {
                this._counter[i]++;
                break;
            }
        }
    }


    /**
     *  Mode Of Operation - Counter (CTR)
     */
    var ModeOfOperationCTR = function(key, counter) {
        this.description = "Counter";
        this.name = "ctr";

        if (!(counter instanceof Counter)) {
            counter = new Counter(counter)
        }

        this._counter = counter;

        this._remainingCounter = null;
        this._remainingCounterIndex = 16;

        this._aes = new AES(key);
    }

    ModeOfOperationCTR.prototype.encrypt = function(plaintext) {
        var encrypted = createBuffer(plaintext);

        for (var i = 0; i < encrypted.length; i++) {
            if (this._remainingCounterIndex === 16) {
                this._remainingCounter = this._aes.encrypt(this._counter._counter);
                this._remainingCounterIndex = 0;
                this._counter.increment();
            }
            encrypted[i] ^= this._remainingCounter[this._remainingCounterIndex++];
        }

        return encrypted;
    }

    // Decryption is symetric
    ModeOfOperationCTR.prototype.decrypt = ModeOfOperationCTR.prototype.encrypt;


    // The bsic modes of operation as a map
    var ModeOfOperation = {
        ecb: ModeOfOperationECB,
        cbc: ModeOfOperationCBC,
        cfb: ModeOfOperationCFB,
        ofb: ModeOfOperationOFB,
        ctr: ModeOfOperationCTR
    };


    ///////////////////////
    // Exporting


    // The block cipher
    var aesjs = {
        AES: AES,
        Counter: Counter,
        ModeOfOperation: ModeOfOperation,
        util: {
            convertBytesToString: convertBytesToString,
            convertStringToBytes: convertStringToBytes,
            _slowCreateBuffer: slowCreateBuffer
        }
    };


    // node.js
    if (typeof exports !== 'undefined') {
        module.exports = aesjs

    // RequireJS/AMD
    // http://www.requirejs.org/docs/api.html
    // https://github.com/amdjs/amdjs-api/wiki/AMD
    } else if (typeof(define) === 'function' && define.amd) {
        define(aesjs);

    // Web Browsers
    } else {

        // If there was an existing library at "aes" make sure it's still available
        if (root.aes) {
            aesjs._aes = root.aes;
        }

        root.aesjs = aesjs;
    }


})(this);

================================================
FILE: src/crypto/big-integer.js
================================================
// modified version from https://github.com/peterolson/BigInteger.js
// remove eval to avoid warning in chrome
var bigInt=function(undefined){"use strict";var BASE=1e7,LOG_BASE=7,MAX_INT=9007199254740992,MAX_INT_ARR=smallToArray(MAX_INT),DEFAULT_ALPHABET="0123456789abcdefghijklmnopqrstuvwxyz";var supportsNativeBigInt=typeof BigInt==="function";function Integer(v,radix,alphabet,caseSensitive){if(typeof v==="undefined")return Integer[0];if(typeof radix!=="undefined")return+radix===10&&!alphabet?parseValue(v):parseBase(v,radix,alphabet,caseSensitive);return parseValue(v)}function BigInteger(value,sign){this.value=value;this.sign=sign;this.isSmall=false}BigInteger.prototype=Object.create(Integer.prototype);function SmallInteger(value){this.value=value;this.sign=value<0;this.isSmall=true}SmallInteger.prototype=Object.create(Integer.prototype);function NativeBigInt(value){this.value=value}NativeBigInt.prototype=Object.create(Integer.prototype);function isPrecise(n){return-MAX_INT<n&&n<MAX_INT}function smallToArray(n){if(n<1e7)return[n];if(n<1e14)return[n%1e7,Math.floor(n/1e7)];return[n%1e7,Math.floor(n/1e7)%1e7,Math.floor(n/1e14)]}function arrayToSmall(arr){trim(arr);var length=arr.length;if(length<4&&compareAbs(arr,MAX_INT_ARR)<0){switch(length){case 0:return 0;case 1:return arr[0];case 2:return arr[0]+arr[1]*BASE;default:return arr[0]+(arr[1]+arr[2]*BASE)*BASE}}return arr}function trim(v){var i=v.length;while(v[--i]===0);v.length=i+1}function createArray(length){var x=new Array(length);var i=-1;while(++i<length){x[i]=0}return x}function truncate(n){if(n>0)return Math.floor(n);return Math.ceil(n)}function add(a,b){var l_a=a.length,l_b=b.length,r=new Array(l_a),carry=0,base=BASE,sum,i;for(i=0;i<l_b;i++){sum=a[i]+b[i]+carry;carry=sum>=base?1:0;r[i]=sum-carry*base}while(i<l_a){sum=a[i]+carry;carry=sum===base?1:0;r[i++]=sum-carry*base}if(carry>0)r.push(carry);return r}function addAny(a,b){if(a.length>=b.length)return add(a,b);return add(b,a)}function addSmall(a,carry){var l=a.length,r=new Array(l),base=BASE,sum,i;for(i=0;i<l;i++){sum=a[i]-base+carry;carry=Math.floor(sum/base);r[i]=sum-carry*base;carry+=1}while(carry>0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}BigInteger.prototype.add=function(v){var n=parseValue(v);if(this.sign!==n.sign){return this.subtract(n.negate())}var a=this.value,b=n.value;if(n.isSmall){return new BigInteger(addSmall(a,Math.abs(b)),this.sign)}return new BigInteger(addAny(a,b),this.sign)};BigInteger.prototype.plus=BigInteger.prototype.add;SmallInteger.prototype.add=function(v){var n=parseValue(v);var a=this.value;if(a<0!==n.sign){return this.subtract(n.negate())}var b=n.value;if(n.isSmall){if(isPrecise(a+b))return new SmallInteger(a+b);b=smallToArray(Math.abs(b))}return new BigInteger(addSmall(b,Math.abs(a)),a<0)};SmallInteger.prototype.plus=SmallInteger.prototype.add;NativeBigInt.prototype.add=function(v){return new NativeBigInt(this.value+parseValue(v).value)};NativeBigInt.prototype.plus=NativeBigInt.prototype.add;function subtract(a,b){var a_l=a.length,b_l=b.length,r=new Array(a_l),borrow=0,base=BASE,i,difference;for(i=0;i<b_l;i++){difference=a[i]-borrow-b[i];if(difference<0){difference+=base;borrow=1}else borrow=0;r[i]=difference}for(i=b_l;i<a_l;i++){difference=a[i]-borrow;if(difference<0)difference+=base;else{r[i++]=difference;break}r[i]=difference}for(;i<a_l;i++){r[i]=a[i]}trim(r);return r}function subtractAny(a,b,sign){var value;if(compareAbs(a,b)>=0){value=subtract(a,b)}else{value=subtract(b,a);sign=!sign}value=arrayToSmall(value);if(typeof value==="number"){if(sign)value=-value;return new SmallInteger(value)}return new BigInteger(value,sign)}function subtractSmall(a,b,sign){var l=a.length,r=new Array(l),carry=-b,base=BASE,i,difference;for(i=0;i<l;i++){difference=a[i]+carry;carry=Math.floor(difference/base);difference%=base;r[i]=difference<0?difference+base:difference}r=arrayToSmall(r);if(typeof r==="number"){if(sign)r=-r;return new SmallInteger(r)}return new BigInteger(r,sign)}BigInteger.prototype.subtract=function(v){var n=parseValue(v);if(this.sign!==n.sign){return this.add(n.negate())}var a=this.value,b=n.value;if(n.isSmall)return subtractSmall(a,Math.abs(b),this.sign);return subtractAny(a,b,this.sign)};BigInteger.prototype.minus=BigInteger.prototype.subtract;SmallInteger.prototype.subtract=function(v){var n=parseValue(v);var a=this.value;if(a<0!==n.sign){return this.add(n.negate())}var b=n.value;if(n.isSmall){return new SmallInteger(a-b)}return subtractSmall(b,Math.abs(a),a>=0)};SmallInteger.prototype.minus=SmallInteger.prototype.subtract;NativeBigInt.prototype.subtract=function(v){return new NativeBigInt(this.value-parseValue(v).value)};NativeBigInt.prototype.minus=NativeBigInt.prototype.subtract;BigInteger.prototype.negate=function(){return new BigInteger(this.value,!this.sign)};SmallInteger.prototype.negate=function(){var sign=this.sign;var small=new SmallInteger(-this.value);small.sign=!sign;return small};NativeBigInt.prototype.negate=function(){return new NativeBigInt(-this.value)};BigInteger.prototype.abs=function(){return new BigInteger(this.value,false)};SmallInteger.prototype.abs=function(){return new SmallInteger(Math.abs(this.value))};NativeBigInt.prototype.abs=function(){return new NativeBigInt(this.value>=0?this.value:-this.value)};function multiplyLong(a,b){var a_l=a.length,b_l=b.length,l=a_l+b_l,r=createArray(l),base=BASE,product,carry,i,a_i,b_j;for(i=0;i<a_l;++i){a_i=a[i];for(var j=0;j<b_l;++j){b_j=b[j];product=a_i*b_j+r[i+j];carry=Math.floor(product/base);r[i+j]=product-carry*base;r[i+j+1]+=carry}}trim(r);return r}function multiplySmall(a,b){var l=a.length,r=new Array(l),base=BASE,carry=0,product,i;for(i=0;i<l;i++){product=a[i]*b+carry;carry=Math.floor(product/base);r[i]=product-carry*base}while(carry>0){r[i++]=carry%base;carry=Math.floor(carry/base)}return r}function shiftLeft(x,n){var r=[];while(n-- >0)r.push(0);return r.concat(x)}function multiplyKaratsuba(x,y){var n=Math.max(x.length,y.length);if(n<=30)return multiplyLong(x,y);n=Math.ceil(n/2);var b=x.slice(n),a=x.slice(0,n),d=y.slice(n),c=y.slice(0,n);var ac=multiplyKaratsuba(a,c),bd=multiplyKaratsuba(b,d),abcd=multiplyKaratsuba(addAny(a,b),addAny(c,d));var product=addAny(addAny(ac,shiftLeft(subtract(subtract(abcd,ac),bd),n)),shiftLeft(bd,2*n));trim(product);return product}function useKaratsuba(l1,l2){return-.012*l1-.012*l2+15e-6*l1*l2>0}BigInteger.prototype.multiply=function(v){var n=parseValue(v),a=this.value,b=n.value,sign=this.sign!==n.sign,abs;if(n.isSmall){if(b===0)return Integer[0];if(b===1)return this;if(b===-1)return this.negate();abs=Math.abs(b);if(abs<BASE){return new BigInteger(multiplySmall(a,abs),sign)}b=smallToArray(abs)}if(useKaratsuba(a.length,b.length))return new BigInteger(multiplyKaratsuba(a,b),sign);return new BigInteger(multiplyLong(a,b),sign)};BigInteger.prototype.times=BigInteger.prototype.multiply;function multiplySmallAndArray(a,b,sign){if(a<BASE){return new BigInteger(multiplySmall(b,a),sign)}return new BigInteger(multiplyLong(b,smallToArray(a)),sign)}SmallInteger.prototype._multiplyBySmall=function(a){if(isPrecise(a.value*this.value)){return new SmallInteger(a.value*this.value)}return multiplySmallAndArray(Math.abs(a.value),smallToArray(Math.abs(this.value)),this.sign!==a.sign)};BigInteger.prototype._multiplyBySmall=function(a){if(a.value===0)return Integer[0];if(a.value===1)return this;if(a.value===-1)return this.negate();return multiplySmallAndArray(Math.abs(a.value),this.value,this.sign!==a.sign)};SmallInteger.prototype.multiply=function(v){return parseValue(v)._multiplyBySmall(this)};SmallInteger.prototype.times=SmallInteger.prototype.multiply;NativeBigInt.prototype.multiply=function(v){return new NativeBigInt(this.value*parseValue(v).value)};NativeBigInt.prototype.times=NativeBigInt.prototype.multiply;function square(a){var l=a.length,r=createArray(l+l),base=BASE,product,carry,i,a_i,a_j;for(i=0;i<l;i++){a_i=a[i];carry=0-a_i*a_i;for(var j=i;j<l;j++){a_j=a[j];product=2*(a_i*a_j)+r[i+j]+carry;carry=Math.floor(product/base);r[i+j]=product-carry*base}r[i+l]=carry}trim(r);return r}BigInteger.prototype.square=function(){return new BigInteger(square(this.value),false)};SmallInteger.prototype.square=function(){var value=this.value*this.value;if(isPrecise(value))return new SmallInteger(value);return new BigInteger(square(smallToArray(Math.abs(this.value))),false)};NativeBigInt.prototype.square=function(v){return new NativeBigInt(this.value*this.value)};function divMod1(a,b){var a_l=a.length,b_l=b.length,base=BASE,result=createArray(b.length),divisorMostSignificantDigit=b[b_l-1],lambda=Math.ceil(base/(2*divisorMostSignificantDigit)),remainder=multiplySmall(a,lambda),divisor=multiplySmall(b,lambda),quotientDigit,shift,carry,borrow,i,l,q;if(remainder.length<=a_l)remainder.push(0);divisor.push(0);divisorMostSignificantDigit=divisor[b_l-1];for(shift=a_l-b_l;shift>=0;shift--){quotientDigit=base-1;if(remainder[shift+b_l]!==divisorMostSignificantDigit){quotientDigit=Math.floor((remainder[shift+b_l]*base+remainder[shift+b_l-1])/divisorMostSignificantDigit)}carry=0;borrow=0;l=divisor.length;for(i=0;i<l;i++){carry+=quotientDigit*divisor[i];q=Math.floor(carry/base);borrow+=remainder[shift+i]-(carry-q*base);carry=q;if(borrow<0){remainder[shift+i]=borrow+base;borrow=-1}else{remainder[shift+i]=borrow;borrow=0}}while(borrow!==0){quotientDigit-=1;carry=0;for(i=0;i<l;i++){carry+=remainder[shift+i]-base+divisor[i];if(carry<0){remainder[shift+i]=carry+base;carry=0}else{remainder[shift+i]=carry;carry=1}}borrow+=carry}result[shift]=quotientDigit}remainder=divModSmall(remainder,lambda)[0];return[arrayToSmall(result),arrayToSmall(remainder)]}function divMod2(a,b){var a_l=a.length,b_l=b.length,result=[],part=[],base=BASE,guess,xlen,highx,highy,check;while(a_l){part.unshift(a[--a_l]);trim(part);if(compareAbs(part,b)<0){result.push(0);continue}xlen=part.length;highx=part[xlen-1]*base+part[xlen-2];highy=b[b_l-1]*base+b[b_l-2];if(xlen>b_l){highx=(highx+1)*base}guess=Math.ceil(highx/highy);do{check=multiplySmall(b,guess);if(compareAbs(check,part)<=0)break;guess--}while(guess);result.push(guess);part=subtract(part,check)}result.reverse();return[arrayToSmall(result),arrayToSmall(part)]}function divModSmall(value,lambda){var length=value.length,quotient=createArray(length),base=BASE,i,q,remainder,divisor;remainder=0;for(i=length-1;i>=0;--i){divisor=remainder*base+value[i];q=truncate(divisor/lambda);remainder=divisor-q*lambda;quotient[i]=q|0}return[quotient,remainder|0]}function divModAny(self,v){var value,n=parseValue(v);if(supportsNativeBigInt){return[new NativeBigInt(self.value/n.value),new NativeBigInt(self.value%n.value)]}var a=self.value,b=n.value;var quotient;if(b===0)throw new Error("Cannot divide by zero");if(self.isSmall){if(n.isSmall){return[new SmallInteger(truncate(a/b)),new SmallInteger(a%b)]}return[Integer[0],self]}if(n.isSmall){if(b===1)return[self,Integer[0]];if(b==-1)return[self.negate(),Integer[0]];var abs=Math.abs(b);if(abs<BASE){value=divModSmall(a,abs);quotient=arrayToSmall(value[0]);var remainder=value[1];if(self.sign)remainder=-remainder;if(typeof quotient==="number"){if(self.sign!==n.sign)quotient=-quotient;return[new SmallInteger(quotient),new SmallInteger(remainder)]}return[new BigInteger(quotient,self.sign!==n.sign),new SmallInteger(remainder)]}b=smallToArray(abs)}var comparison=compareAbs(a,b);if(comparison===-1)return[Integer[0],self];if(comparison===0)return[Integer[self.sign===n.sign?1:-1],Integer[0]];if(a.length+b.length<=200)value=divMod1(a,b);else value=divMod2(a,b);quotient=value[0];var qSign=self.sign!==n.sign,mod=value[1],mSign=self.sign;if(typeof quotient==="number"){if(qSign)quotient=-quotient;quotient=new SmallInteger(quotient)}else quotient=new BigInteger(quotient,qSign);if(typeof mod==="number"){if(mSign)mod=-mod;mod=new SmallInteger(mod)}else mod=new BigInteger(mod,mSign);return[quotient,mod]}BigInteger.prototype.divmod=function(v){var result=divModAny(this,v);return{quotient:result[0],remainder:result[1]}};NativeBigInt.prototype.divmod=SmallInteger.prototype.divmod=BigInteger.prototype.divmod;BigInteger.prototype.divide=function(v){return divModAny(this,v)[0]};NativeBigInt.prototype.over=NativeBigInt.prototype.divide=SmallInteger.prototype.over=SmallInteger.prototype.divide=BigInteger.prototype.over=BigInteger.prototype.divide;BigInteger.prototype.mod=function(v){return divModAny(this,v)[1]};NativeBigInt.prototype.mod=NativeBigInt.prototype.remainder=SmallInteger.prototype.remainder=SmallInteger.prototype.mod=BigInteger.prototype.remainder=BigInteger.prototype.mod;BigInteger.prototype.pow=function(v){var n=parseValue(v),a=this.value,b=n.value,value,x,y;if(b===0)return Integer[1];if(a===0)return Integer[0];if(a===1)return Integer[1];if(a===-1)return n.isEven()?Integer[1]:Integer[-1];if(n.sign){return Integer[0]}if(!n.isSmall)throw new Error("The exponent "+n.toString()+" is too large.");if(this.isSmall){if(isPrecise(value=Math.pow(a,b)))return new SmallInteger(truncate(value))}x=this;y=Integer[1];while(true){if(b&1===1){y=y.times(x);--b}if(b===0)break;b/=2;x=x.square()}return y};SmallInteger.prototype.pow=BigInteger.prototype.pow;var pow;NativeBigInt.prototype.pow=function(v){var n=parseValue(v);var a=this.value,b=n.value;if(b===BigInt(0))return Integer[1];if(a===BigInt(0))return Integer[0];if(a===BigInt(1))return Integer[1];if(a===BigInt(-1))return n.isEven()?Integer[1]:Integer[-1];if(n.isNegative())return new NativeBigInt(BigInt(0));return new NativeBigInt(pow(a,b))};BigInteger.prototype.modPow=function(exp,mod){exp=parseValue(exp);mod=parseValue(mod);if(mod.isZero())throw new Error("Cannot take modPow with modulus 0");var r=Integer[1],base=this.mod(mod);while(exp.isPositive()){if(base.isZero())return Integer[0];if(exp.isOdd())r=r.multiply(base).mod(mod);exp=exp.divide(2);base=base.square().mod(mod)}return r};NativeBigInt.prototype.modPow=SmallInteger.prototype.modPow=BigInteger.prototype.modPow;function compareAbs(a,b){if(a.length!==b.length){return a.length>b.length?1:-1}for(var i=a.length-1;i>=0;i--){if(a[i]!==b[i])return a[i]>b[i]?1:-1}return 0}BigInteger.prototype.compareAbs=function(v){var n=parseValue(v),a=this.value,b=n.value;if(n.isSmall)return 1;return compareAbs(a,b)};SmallInteger.prototype.compareAbs=function(v){var n=parseValue(v),a=Math.abs(this.value),b=n.value;if(n.isSmall){b=Math.abs(b);return a===b?0:a>b?1:-1}return-1};NativeBigInt.prototype.compareAbs=function(v){var a=this.value;var b=parseValue(v).value;a=a>=0?a:-a;b=b>=0?b:-b;return a===b?0:a>b?1:-1};BigInteger.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var n=parseValue(v),a=this.value,b=n.value;if(this.sign!==n.sign){return n.sign?1:-1}if(n.isSmall){return this.sign?-1:1}return compareAbs(a,b)*(this.sign?-1:1)};BigInteger.prototype.compareTo=BigInteger.prototype.compare;SmallInteger.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var n=parseValue(v),a=this.value,b=n.value;if(n.isSmall){return a==b?0:a>b?1:-1}if(a<0!==n.sign){return a<0?-1:1}return a<0?1:-1};SmallInteger.prototype.compareTo=SmallInteger.prototype.compare;NativeBigInt.prototype.compare=function(v){if(v===Infinity){return-1}if(v===-Infinity){return 1}var a=this.value;var b=parseValue(v).value;return a===b?0:a>b?1:-1};NativeBigInt.prototype.compareTo=NativeBigInt.prototype.compare;BigInteger.prototype.equals=function(v){return this.compare(v)===0};NativeBigInt.prototype.eq=NativeBigInt.prototype.equals=SmallInteger.prototype.eq=SmallInteger.prototype.equals=BigInteger.prototype.eq=BigInteger.prototype.equals;BigInteger.prototype.notEquals=function(v){return this.compare(v)!==0};NativeBigInt.prototype.neq=NativeBigInt.prototype.notEquals=SmallInteger.prototype.neq=SmallInteger.prototype.notEquals=BigInteger.prototype.neq=BigInteger.prototype.notEquals;BigInteger.prototype.greater=function(v){return this.compare(v)>0};NativeBigInt.prototype.gt=NativeBigInt.prototype.greater=SmallInteger.prototype.gt=SmallInteger.prototype.greater=BigInteger.prototype.gt=BigInteger.prototype.greater;BigInteger.prototype.lesser=function(v){return this.compare(v)<0};NativeBigInt.prototype.lt=NativeBigInt.prototype.lesser=SmallInteger.prototype.lt=SmallInteger.prototype.lesser=BigInteger.prototype.lt=BigInteger.prototype.lesser;BigInteger.prototype.greaterOrEquals=function(v){return this.compare(v)>=0};NativeBigInt.prototype.geq=NativeBigInt.prototype.greaterOrEquals=SmallInteger.prototype.geq=SmallInteger.prototype.greaterOrEquals=BigInteger.prototype.geq=BigInteger.prototype.greaterOrEquals;BigInteger.prototype.lesserOrEquals=function(v){return this.compare(v)<=0};NativeBigInt.prototype.leq=NativeBigInt.prototype.lesserOrEquals=SmallInteger.prototype.leq=SmallInteger.prototype.lesserOrEquals=BigInteger.prototype.leq=BigInteger.prototype.lesserOrEquals;BigInteger.prototype.isEven=function(){return(this.value[0]&1)===0};SmallInteger.prototype.isEven=function(){return(this.value&1)===0};NativeBigInt.prototype.isEven=function(){return(this.value&BigInt(1))===BigInt(0)};BigInteger.prototype.isOdd=function(){return(this.value[0]&1)===1};SmallInteger.prototype.isOdd=function(){return(this.value&1)===1};NativeBigInt.prototype.isOdd=function(){return(this.value&BigInt(1))===BigInt(1)};BigInteger.prototype.isPositive=function(){return!this.sign};SmallInteger.prototype.isPositive=function(){return this.value>0};NativeBigInt.prototype.isPositive=SmallInteger.prototype.isPositive;BigInteger.prototype.isNegative=function(){return this.sign};SmallInteger.prototype.isNegative=function(){return this.value<0};NativeBigInt.prototype.isNegative=SmallInteger.prototype.isNegative;BigInteger.prototype.isUnit=function(){return false};SmallInteger.prototype.isUnit=function(){return Math.abs(this.value)===1};NativeBigInt.prototype.isUnit=function(){return this.abs().value===BigInt(1)};BigInteger.prototype.isZero=function(){return false};SmallInteger.prototype.isZero=function(){return this.value===0};NativeBigInt.prototype.isZero=function(){return this.value===BigInt(0)};BigInteger.prototype.isDivisibleBy=function(v){var n=parseValue(v);if(n.isZero())return false;if(n.isUnit())return true;if(n.compareAbs(2)===0)return this.isEven();return this.mod(n).isZero()};NativeBigInt.prototype.isDivisibleBy=SmallInteger.prototype.isDivisibleBy=BigInteger.prototype.isDivisibleBy;function isBasicPrime(v){var n=v.abs();if(n.isUnit())return false;if(n.equals(2)||n.equals(3)||n.equals(5))return true;if(n.isEven()||n.isDivisibleBy(3)||n.isDivisibleBy(5))return false;if(n.lesser(49))return true}function millerRabinTest(n,a){var nPrev=n.prev(),b=nPrev,r=0,d,t,i,x;while(b.isEven())b=b.divide(2),r++;next:for(i=0;i<a.length;i++){if(n.lesser(a[i]))continue;x=bigInt(a[i]).modPow(b,n);if(x.isUnit()||x.equals(nPrev))continue;for(d=r-1;d!=0;d--){x=x.square().mod(n);if(x.isUnit())return false;if(x.equals(nPrev))continue next}return false}return true}BigInteger.prototype.isPrime=function(strict){var isPrime=isBasicPrime(this);if(isPrime!==undefined)return isPrime;var n=this.abs();var bits=n.bitLength();if(bits<=64)return millerRabinTest(n,[2,325,9375,28178,450775,9780504,1795265022]);var logN=Math.log(2)*bits.toJSNumber();var t=Math.ceil(strict===true?2*Math.pow(logN,2):logN);for(var a=[],i=0;i<t;i++){a.push(bigInt(i+2))}return millerRabinTest(n,a)};NativeBigInt.prototype.isPrime=SmallInteger.prototype.isPrime=BigInteger.prototype.isPrime;BigInteger.prototype.isProbablePrime=function(iterations){var isPrime=isBasicPrime(this);if(isPrime!==undefined)return isPrime;var n=this.abs();var t=iterations===undefined?5:iterations;for(var a=[],i=0;i<t;i++){a.push(bigInt.randBetween(2,n.minus(2)))}return millerRabinTest(n,a)};NativeBigInt.prototype.isProbablePrime=SmallInteger.prototype.isProbablePrime=BigInteger.prototype.isProbablePrime;BigInteger.prototype.modInv=function(n){var t=bigInt.zero,newT=bigInt.one,r=parseValue(n),newR=this.abs(),q,lastT,lastR;while(!newR.isZero()){q=r.divide(newR);lastT=t;lastR=r;t=newT;r=newR;newT=lastT.subtract(q.multiply(newT));newR=lastR.subtract(q.multiply(newR))}if(!r.isUnit())throw new Error(this.toString()+" and "+n.toString()+" are not co-prime");if(t.compare(0)===-1){t=t.add(n)}if(this.isNegative()){return t.negate()}return t};NativeBigInt.prototype.modInv=SmallInteger.prototype.modInv=BigInteger.prototype.modInv;BigInteger.prototype.next=function(){var value=this.value;if(this.sign){return subtractSmall(value,1,this.sign)}return new BigInteger(addSmall(value,1),this.sign)};SmallInteger.prototype.next=function(){var value=this.value;if(value+1<MAX_INT)return new SmallInteger(value+1);return new BigInteger(MAX_INT_ARR,false)};NativeBigInt.prototype.next=function(){return new NativeBigInt(this.value+BigInt(1))};BigInteger.prototype.prev=function(){var value=this.value;if(this.sign){return new BigInteger(addSmall(value,1),true)}return subtractSmall(value,1,this.sign)};SmallInteger.prototype.prev=function(){var value=this.value;if(value-1>-MAX_INT)return new SmallInteger(value-1);return new BigInteger(MAX_INT_ARR,true)};NativeBigInt.prototype.prev=function(){return new NativeBigInt(this.value-BigInt(1))};var powersOfTwo=[1];while(2*powersOfTwo[powersOfTwo.length-1]<=BASE)powersOfTwo.push(2*powersOfTwo[powersOfTwo.length-1]);var powers2Length=powersOfTwo.length,highestPower2=powersOfTwo[powers2Length-1];function shift_isSmall(n){return Math.abs(n)<=BASE}BigInteger.prototype.shiftLeft=function(v){var n=parseValue(v).toJSNumber();if(!shift_isSmall(n)){throw new Error(String(n)+" is too large for shifting.")}if(n<0)return this.shiftRight(-n);var result=this;if(result.isZero())return result;while(n>=powers2Length){result=result.multiply(highestPower2);n-=powers2Length-1}return result.multiply(powersOfTwo[n])};NativeBigInt.prototype.shiftLeft=SmallInteger.prototype.shiftLeft=BigInteger.prototype.shiftLeft;BigInteger.prototype.shiftRight=function(v){var remQuo;var n=parseValue(v).toJSNumber();if(!shift_isSmall(n)){throw new Error(String(n)+" is too large for shifting.")}if(n<0)return this.shiftLeft(-n);var result=this;while(n>=powers2Length){if(result.isZero()||result.isNegative()&&result.isUnit())return result;remQuo=divModAny(result,highestPower2);result=remQuo[1].isNegative()?remQuo[0].prev():remQuo[0];n-=powers2Length-1}remQuo=divModAny(result,powersOfTwo[n]);return remQuo[1].isNegative()?remQuo[0].prev():remQuo[0]};NativeBigInt.prototype.shiftRight=SmallInteger.prototype.shiftRight=BigInteger.prototype.shiftRight;function bitwise(x,y,fn){y=parseValue(y);var xSign=x.isNegative(),ySign=y.isNegative();var xRem=xSign?x.not():x,yRem=ySign?y.not():y;var xDigit=0,yDigit=0;var xDivMod=null,yDivMod=null;var result=[];while(!xRem.isZero()||!yRem.isZero()){xDivMod=divModAny(xRem,highestPower2);xDigit=xDivMod[1].toJSNumber();if(xSign){xDigit=highestPower2-1-xDigit}yDivMod=divModAny(yRem,highestPower2);yDigit=yDivMod[1].toJSNumber();if(ySign){yDigit=highestPower2-1-yDigit}xRem=xDivMod[0];yRem=yDivMod[0];result.push(fn(xDigit,yDigit))}var sum=fn(xSign?1:0,ySign?1:0)!==0?bigInt(-1):bigInt(0);for(var i=result.length-1;i>=0;i-=1){sum=sum.multiply(highestPower2).add(bigInt(result[i]))}return sum}BigInteger.prototype.not=function(){return this.negate().prev()};NativeBigInt.prototype.not=SmallInteger.prototype.not=BigInteger.prototype.not;BigInteger.prototype.and=function(n){return bitwise(this,n,function(a,b){return a&b})};NativeBigInt.prototype.and=SmallInteger.prototype.and=BigInteger.prototype.and;BigInteger.prototype.or=function(n){return bitwise(this,n,function(a,b){return a|b})};NativeBigInt.prototype.or=SmallInteger.prototype.or=BigInteger.prototype.or;BigInteger.prototype.xor=function(n){return bitwise(this,n,function(a,b){return a^b})};NativeBigInt.prototype.xor=SmallInteger.prototype.xor=BigInteger.prototype.xor;var LOBMASK_I=1<<30,LOBMASK_BI=(BASE&-BASE)*(BASE&-BASE)|LOBMASK_I;function roughLOB(n){var v=n.value,x=typeof v==="number"?v|LOBMASK_I:typeof v==="bigint"?v|BigInt(LOBMASK_I):v[0]+v[1]*BASE|LOBMASK_BI;return x&-x}function integerLogarithm(value,base){if(base.compareTo(value)<=0){var tmp=integerLogarithm(value,base.square(base));var p=tmp.p;var e=tmp.e;var t=p.multiply(base);return t.compareTo(value)<=0?{p:t,e:e*2+1}:{p:p,e:e*2}}return{p:bigInt(1),e:0}}BigInteger.prototype.bitLength=function(){var n=this;if(n.compareTo(bigInt(0))<0){n=n.negate().subtract(bigInt(1))}if(n.compareTo(bigInt(0))===0){return bigInt(0)}return bigInt(integerLogarithm(n,bigInt(2)).e).add(bigInt(1))};NativeBigInt.prototype.bitLength=SmallInteger.prototype.bitLength=BigInteger.prototype.bitLength;function max(a,b){a=parseValue(a);b=parseValue(b);return a.greater(b)?a:b}function min(a,b){a=parseValue(a);b=parseValue(b);return a.lesser(b)?a:b}function gcd(a,b){a=parseValue(a).abs();b=parseValue(b).abs();if(a.equals(b))return a;if(a.isZero())return b;if(b.isZero())return a;var c=Integer[1],d,t;while(a.isEven()&&b.isEven()){d=min(roughLOB(a),roughLOB(b));a=a.divide(d);b=b.divide(d);c=c.multiply(d)}while(a.isEven()){a=a.divide(roughLOB(a))}do{while(b.isEven()){b=b.divide(roughLOB(b))}if(a.greater(b)){t=b;b=a;a=t}b=b.subtract(a)}while(!b.isZero());return c.isUnit()?a:a.multiply(c)}function lcm(a,b){a=parseValue(a).abs();b=parseValue(b).abs();return a.divide(gcd(a,b)).multiply(b)}function randBetween(a,b){a=parseValue(a);b=parseValue(b);var low=min(a,b),high=max(a,b);var range=high.subtract(low).add(1);if(range.isSmall)return low.add(Math.floor(Math.random()*range));var digits=toBase(range,BASE).value;var result=[],restricted=true;for(var i=0;i<digits.length;i++){var top=restricted?digits[i]:BASE;var digit=truncate(Math.random()*top);result.push(digit);if(digit<top)restricted=false}return low.add(Integer.fromArray(result,BASE,false))}var parseBase=function(text,base,alphabet,caseSensitive){alphabet=alphabet||DEFAULT_ALPHABET;text=String(text);if(!caseSensitive){text=text.toLowerCase();alphabet=alphabet.toLowerCase()}var length=text.length;var i;var absBase=Math.abs(base);var alphabetValues={};for(i=0;i<alphabet.length;i++){alphabetValues[alphabet[i]]=i}for(i=0;i<length;i++){var c=text[i];if(c==="-")continue;if(c in alphabetValues){if(alphabetValues[c]>=absBase){if(c==="1"&&absBase===1)continue;throw new Error(c+" is not a valid digit in base "+base+".")}}}base=parseValue(base);var digits=[];var isNegative=text[0]==="-";for(i=isNegative?1:0;i<text.length;i++){var c=text[i];if(c in alphabetValues)digits.push(parseValue(alphabetValues[c]));else if(c==="<"){var start=i;do{i++}while(text[i]!==">"&&i<text.length);digits.push(parseValue(text.slice(start+1,i)))}else throw new Error(c+" is not a valid character")}return parseBaseFromArray(digits,base,isNegative)};function parseBaseFromArray(digits,base,isNegative){var val=Integer[0],pow=Integer[1],i;for(i=digits.length-1;i>=0;i--){val=val.add(digits[i].times(pow));pow=pow.times(base)}return isNegative?val.negate():val}function stringify(digit,alphabet){alphabet=alphabet||DEFAULT_ALPHABET;if(digit<alphabet.length){return alphabet[digit]}return"<"+digit+">"}function toBase(n,base){base=bigInt(base);if(base.isZero()){if(n.isZero())return{value:[0],isNegative:false};throw new Error("Cannot convert nonzero numbers to base 0.")}if(base.equals(-1)){if(n.isZero())return{value:[0],isNegative:false};if(n.isNegative())return{value:[].concat.apply([],Array.apply(null,Array(-n.toJSNumber())).map(Array.prototype.valueOf,[1,0])),isNegative:false};var arr=Array.apply(null,Array(n.toJSNumber()-1)).map(Array.prototype.valueOf,[0,1]);arr.unshift([1]);return{value:[].concat.apply([],arr),isNegative:false}}var neg=false;if(n.isNegative()&&base.isPositive()){neg=true;n=n.abs()}if(base.isUnit()){if(n.isZero())return{value:[0],isNegative:false};return{value:Array.apply(null,Array(n.toJSNumber())).map(Number.prototype.valueOf,1),isNegative:neg}}var out=[];var left=n,divmod;while(left.isNegative()||left.compareAbs(base)>=0){divmod=left.divmod(base);left=divmod.quotient;var digit=divmod.remainder;if(digit.isNegative()){digit=base.minus(digit).abs();left=left.next()}out.push(digit.toJSNumber())}out.push(left.toJSNumber());return{value:out.reverse(),isNegative:neg}}function toBaseString(n,base,alphabet){var arr=toBase(n,base);return(arr.isNegative?"-":"")+arr.value.map(function(x){return stringify(x,alphabet)}).join("")}BigInteger.prototype.toArray=function(radix){return toBase(this,radix)};SmallInteger.prototype.toArray=function(radix){return toBase(this,radix)};NativeBigInt.prototype.toArray=function(radix){return toBase(this,radix)};BigInteger.prototype.toString=function(radix,alphabet){if(radix===undefined)radix=10;if(radix!==10)return toBaseString(this,radix,alphabet);var v=this.value,l=v.length,str=String(v[--l]),zeros="0000000",digit;while(--l>=0){digit=String(v[l]);str+=zeros.slice(digit.length)+digit}var sign=this.sign?"-":"";return sign+str};SmallInteger.prototype.toString=function(radix,alphabet){if(radix===undefined)radix=10;if(radix!=10)return toBaseString(this,radix,alphabet);return String(this.value)};NativeBigInt.prototype.toString=SmallInteger.prototype.toString;NativeBigInt.prototype.toJSON=BigInteger.prototype.toJSON=SmallInteger.prototype.toJSON=function(){return this.toString()};BigInteger.prototype.valueOf=function(){return parseInt(this.toString(),10)};BigInteger.prototype.toJSNumber=BigInteger.prototype.valueOf;SmallInteger.prototype.valueOf=function(){return this.value};SmallInteger.prototype.toJSNumber=SmallInteger.prototype.valueOf;NativeBigInt.prototype.valueOf=NativeBigInt.prototype.toJSNumber=function(){return parseInt(this.toString(),10)};function parseStringValue(v){if(isPrecise(+v)){var x=+v;if(x===truncate(x))return supportsNativeBigInt?new NativeBigInt(BigInt(x)):new SmallInteger(x);throw new Error("Invalid integer: "+v)}var sign=v[0]==="-";if(sign)v=v.slice(1);var split=v.split(/e/i);if(split.length>2)throw new Error("Invalid integer: "+split.join("e"));if(split.length===2){var exp=split[1];if(exp[0]==="+")exp=exp.slice(1);exp=+exp;if(exp!==truncate(exp)||!isPrecise(exp))throw new Error("Invalid integer: "+exp+" is not a valid exponent.");var text=split[0];var decimalPlace=text.indexOf(".");if(decimalPlace>=0){exp-=text.length-decimalPlace-1;text=text.slice(0,decimalPlace)+text.slice(decimalPlace+1)}if(exp<0)throw new Error("Cannot include negative exponent part for integers");text+=new Array(exp+1).join("0");v=text}var isValid=/^([0-9][0-9]*)$/.test(v);if(!isValid)throw new Error("Invalid integer: "+v);if(supportsNativeBigInt){return new NativeBigInt(BigInt(sign?"-"+v:v))}var r=[],max=v.length,l=LOG_BASE,min=max-l;while(max>0){r.push(+v.slice(min,max));min-=l;if(min<0)min=0;max-=l}trim(r);return new BigInteger(r,sign)}function parseNumberValue(v){if(supportsNativeBigInt){return new NativeBigInt(BigInt(v))}if(isPrecise(v)){if(v!==truncate(v))throw new Error(v+" is not an integer.");return new SmallInteger(v)}return parseStringValue(v.toString())}function parseValue(v){if(typeof v==="number"){return parseNumberValue(v)}if(typeof v==="string"){return parseStringValue(v)}if(typeof v==="bigint"){return new NativeBigInt(v)}return v}for(var i=0;i<1e3;i++){Integer[i]=parseValue(i);if(i>0)Integer[-i]=parseValue(-i)}Integer.one=Integer[1];Integer.zero=Integer[0];Integer.minusOne=Integer[-1];Integer.max=max;Integer.min=min;Integer.gcd=gcd;Integer.lcm=lcm;Integer.isInstance=function(x){return x instanceof BigInteger||x instanceof SmallInteger||x instanceof NativeBigInt};Integer.randBetween=randBetween;Integer.fromArray=function(digits,base,isNegative){return parseBaseFromArray(digits.map(parseValue),parseValue(base||10),isNegative)};return Integer}();if(typeof module!=="undefined"&&module.hasOwnProperty("exports")){module.exports=bigInt}if(typeof define==="function"&&define.amd){define("big-integer",[],function(){return bigInt})}

================================================
FILE: src/crypto/crypto.js
================================================
/* eslint-disable import/named */
/* eslint-disable no-undef */
/* eslint-disable no-param-reassign */
import aesjs from './aes';
import { MD5 } from './md5';

const bigInt = require('./big-integer');

function aesEncrypt(text, secKey, ivString) {
  const pad = 16 - (text.length % 16);
  for (let i = 0; i < pad; i += 1) {
    text += String.fromCharCode(pad);
  }
  const key = aesjs.util.convertStringToBytes(secKey);
  // The initialization vector, which must be 16 bytes
  const iv = aesjs.util.convertStringToBytes(ivString);
  let textBytes = aesjs.util.convertStringToBytes(text);
  // eslint-disable-next-line new-cap
  const aesCbc = new aesjs.ModeOfOperation.cbc(key, iv);
  const cipherArray = [];
  while (textBytes.length !== 0) {
    const block = aesCbc.encrypt(textBytes.slice(0, 16));
    Array.prototype.push.apply(cipherArray, block);
    textBytes = textBytes.slice(16);
  }
  let ciphertext = '';
  for (let i = 0; i < cipherArray.length; i += 1) {
    ciphertext += String.fromCharCode(cipherArray[i]);
  }
  ciphertext = btoa(ciphertext);
  return ciphertext;
}

function hexify(text) {
  return text.split('').map(x => x.charCodeAt(0).toString(16)).join('');
}

function zfill(num, size) {
  let s = `${num}`;
  while (s.length < size) s = `0${s}`;
  return s;
}

function expmod(base, exp, mymod) {
  let result;
  if (exp.eq(0)) return bigInt(1, 10);
  if (exp.mod(bigInt(2, 10)).eq(0)) {
    let newexp = bigInt(exp);
    newexp = newexp.shiftRight(1);
    result = expmod(base, newexp, mymod).modPow(2, mymod);
    return result;
  }
  result = expmod(base, exp.subtract(bigInt(1, 10)), mymod).multiply(base).mod(mymod);
  return result;
}

function rsaEncrypt(text, pubKey, modulus) {
  const reversedText = text.split('').reverse().join('');
  const base = bigInt(hexify(reversedText), 16);
  const exp = bigInt(pubKey, 16);
  const mod = bigInt(modulus, 16);
  const bigNumber = expmod(base, exp, mod);
  const rs = bigNumber.toString(16);
  return zfill(rs, 256).toLowerCase();
}

export default { aesEncrypt, rsaEncrypt, MD5 };


================================================
FILE: src/crypto/md5.js
================================================
/**
*
*  MD5 (Message-Digest Algorithm)
*  http://www.webtoolkit.info/
*  refer to: https://github.com/david-sabata/web-scrobbler/blob/master/vendor/md5.js
**/

function MD5(string) {

	function RotateLeft(lValue, iShiftBits) {
		return (lValue<<iShiftBits) | (lValue>>>(32-iShiftBits));
	}

	function AddUnsigned(lX,lY) {
		var lX4,lY4,lX8,lY8,lResult;
		lX8 = (lX & 0x80000000);
		lY8 = (lY & 0x80000000);
		lX4 = (lX & 0x40000000);
		lY4 = (lY & 0x40000000);
		lResult = (lX & 0x3FFFFFFF)+(lY & 0x3FFFFFFF);
		if (lX4 & lY4) {
			return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
		}
		if (lX4 | lY4) {
			if (lResult & 0x40000000) {
				return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);
			} else {
				return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
			}
		} else {
			return (lResult ^ lX8 ^ lY8);
		}
    }

    function F(x,y,z) { return (x & y) | ((~x) & z); }
    function G(x,y,z) { return (x & z) | (y & (~z)); }
    function H(x,y,z) { return (x ^ y ^ z); }
	function I(x,y,z) { return (y ^ (x | (~z))); }

	function FF(a,b,c,d,x,s,ac) {
		a = AddUnsigned(a, AddUnsigned(AddUnsigned(F(b, c, d), x), ac));
		return AddUnsigned(RotateLeft(a, s), b);
	};

	function GG(a,b,c,d,x,s,ac) {
		a = AddUnsigned(a, AddUnsigned(AddUnsigned(G(b, c, d), x), ac));
		return AddUnsigned(RotateLeft(a, s), b);
	};

	function HH(a,b,c,d,x,s,ac) {
		a = AddUnsigned(a, AddUnsigned(AddUnsigned(H(b, c, d), x), ac));
		return AddUnsigned(RotateLeft(a, s), b);
	};

	function II(a,b,c,d,x,s,ac) {
		a = AddUnsigned(a, AddUnsigned(AddUnsigned(I(b, c, d), x), ac));
		return AddUnsigned(RotateLeft(a, s), b);
	};

	function ConvertToWordArray(string) {
		var lWordCount;
		var lMessageLength = string.length;
		var lNumberOfWords_temp1=lMessageLength + 8;
		var lNumberOfWords_temp2=(lNumberOfWords_temp1-(lNumberOfWords_temp1 % 64))/64;
		var lNumberOfWords = (lNumberOfWords_temp2+1)*16;
		var lWordArray=Array(lNumberOfWords-1);
		var lBytePosition = 0;
		var lByteCount = 0;
		while ( lByteCount < lMessageLength ) {
			lWordCount = (lByteCount-(lByteCount % 4))/4;
			lBytePosition = (lByteCount % 4)*8;
			lWordArray[lWordCount] = (lWordArray[lWordCount] | (string.charCodeAt(lByteCount)<<lBytePosition));
			lByteCount++;
		}
		lWordCount = (lByteCount-(lByteCount % 4))/4;
		lBytePosition = (lByteCount % 4)*8;
		lWordArray[lWordCount] = lWordArray[lWordCount] | (0x80<<lBytePosition);
		lWordArray[lNumberOfWords-2] = lMessageLength<<3;
		lWordArray[lNumberOfWords-1] = lMessageLength>>>29;
		return lWordArray;
	};

	function WordToHex(lValue) {
		var WordToHexValue="",WordToHexValue_temp="",lByte,lCount;
		for (lCount = 0;lCount<=3;lCount++) {
			lByte = (lValue>>>(lCount*8)) & 255;
			WordToHexValue_temp = "0" + lByte.toString(16);
			WordToHexValue = WordToHexValue + WordToHexValue_temp.substr(WordToHexValue_temp.length-2,2);
		}
		return WordToHexValue;
	};

	function Utf8Encode(string) {
		string = string.replace(/\r\n/g,"\n");
		var utftext = "";

		for (var n = 0; n < string.length; n++) {

			var c = string.charCodeAt(n);

			if (c < 128) {
				utftext += String.fromCharCode(c);
			}
			else if((c > 127) && (c < 2048)) {
				utftext += String.fromCharCode((c >> 6) | 192);
				utftext += String.fromCharCode((c & 63) | 128);
			}
			else {
				utftext += String.fromCharCode((c >> 12) | 224);
				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
				utftext += String.fromCharCode((c & 63) | 128);
			}

		}

		return utftext;
	};

	var x=Array();
	var k,AA,BB,CC,DD,a,b,c,d;
	var S11=7, S12=12, S13=17, S14=22;
	var S21=5, S22=9 , S23=14, S24=20;
	var S31=4, S32=11, S33=16, S34=23;
	var S41=6, S42=10, S43=15, S44=21;

	string = Utf8Encode(string);

	x = ConvertToWordArray(string);

	a = 0x67452301; b = 0xEFCDAB89; c = 0x98BADCFE; d = 0x10325476;

	for (k=0;k<x.length;k+=16) {
		AA=a; BB=b; CC=c; DD=d;
		a=FF(a,b,c,d,x[k+0], S11,0xD76AA478);
		d=FF(d,a,b,c,x[k+1], S12,0xE8C7B756);
		c=FF(c,d,a,b,x[k+2], S13,0x242070DB);
		b=FF(b,c,d,a,x[k+3], S14,0xC1BDCEEE);
		a=FF(a,b,c,d,x[k+4], S11,0xF57C0FAF);
		d=FF(d,a,b,c,x[k+5], S12,0x4787C62A);
		c=FF(c,d,a,b,x[k+6], S13,0xA8304613);
		b=FF(b,c,d,a,x[k+7], S14,0xFD469501);
		a=FF(a,b,c,d,x[k+8], S11,0x698098D8);
		d=FF(d,a,b,c,x[k+9], S12,0x8B44F7AF);
		c=FF(c,d,a,b,x[k+10],S13,0xFFFF5BB1);
		b=FF(b,c,d,a,x[k+11],S14,0x895CD7BE);
		a=FF(a,b,c,d,x[k+12],S11,0x6B901122);
		d=FF(d,a,b,c,x[k+13],S12,0xFD987193);
		c=FF(c,d,a,b,x[k+14],S13,0xA679438E);
		b=FF(b,c,d,a,x[k+15],S14,0x49B40821);
		a=GG(a,b,c,d,x[k+1], S21,0xF61E2562);
		d=GG(d,a,b,c,x[k+6], S22,0xC040B340);
		c=GG(c,d,a,b,x[k+11],S23,0x265E5A51);
		b=GG(b,c,d,a,x[k+0], S24,0xE9B6C7AA);
		a=GG(a,b,c,d,x[k+5], S21,0xD62F105D);
		d=GG(d,a,b,c,x[k+10],S22,0x2441453);
		c=GG(c,d,a,b,x[k+15],S23,0xD8A1E681);
		b=GG(b,c,d,a,x[k+4], S24,0xE7D3FBC8);
		a=GG(a,b,c,d,x[k+9], S21,0x21E1CDE6);
		d=GG(d,a,b,c,x[k+14],S22,0xC33707D6);
		c=GG(c,d,a,b,x[k+3], S23,0xF4D50D87);
		b=GG(b,c,d,a,x[k+8], S24,0x455A14ED);
		a=GG(a,b,c,d,x[k+13],S21,0xA9E3E905);
		d=GG(d,a,b,c,x[k+2], S22,0xFCEFA3F8);
		c=GG(c,d,a,b,x[k+7], S23,0x676F02D9);
		b=GG(b,c,d,a,x[k+12],S24,0x8D2A4C8A);
		a=HH(a,b,c,d,x[k+5], S31,0xFFFA3942);
		d=HH(d,a,b,c,x[k+8], S32,0x8771F681);
		c=HH(c,d,a,b,x[k+11],S33,0x6D9D6122);
		b=HH(b,c,d,a,x[k+14],S34,0xFDE5380C);
		a=HH(a,b,c,d,x[k+1], S31,0xA4BEEA44);
		d=HH(d,a,b,c,x[k+4], S32,0x4BDECFA9);
		c=HH(c,d,a,b,x[k+7], S33,0xF6BB4B60);
		b=HH(b,c,d,a,x[k+10],S34,0xBEBFBC70);
		a=HH(a,b,c,d,x[k+13],S31,0x289B7EC6);
		d=HH(d,a,b,c,x[k+0], S32,0xEAA127FA);
		c=HH(c,d,a,b,x[k+3], S33,0xD4EF3085);
		b=HH(b,c,d,a,x[k+6], S34,0x4881D05);
		a=HH(a,b,c,d,x[k+9], S31,0xD9D4D039);
		d=HH(d,a,b,c,x[k+12],S32,0xE6DB99E5);
		c=HH(c,d,a,b,x[k+15],S33,0x1FA27CF8);
		b=HH(b,c,d,a,x[k+2], S34,0xC4AC5665);
		a=II(a,b,c,d,x[k+0], S41,0xF4292244);
		d=II(d,a,b,c,x[k+7], S42,0x432AFF97);
		c=II(c,d,a,b,x[k+14],S43,0xAB9423A7);
		b=II(b,c,d,a,x[k+5], S44,0xFC93A039);
		a=II(a,b,c,d,x[k+12],S41,0x655B59C3);
		d=II(d,a,b,c,x[k+3], S42,0x8F0CCC92);
		c=II(c,d,a,b,x[k+10],S43,0xFFEFF47D);
		b=II(b,c,d,a,x[k+1], S44,0x85845DD1);
		a=II(a,b,c,d,x[k+8], S41,0x6FA87E4F);
		d=II(d,a,b,c,x[k+15],S42,0xFE2CE6E0);
		c=II(c,d,a,b,x[k+6], S43,0xA3014314);
		b=II(b,c,d,a,x[k+13],S44,0x4E0811A1);
		a=II(a,b,c,d,x[k+4], S41,0xF7537E82);
		d=II(d,a,b,c,x[k+11],S42,0xBD3AF235);
		c=II(c,d,a,b,x[k+2], S43,0x2AD7D2BB);
		b=II(b,c,d,a,x[k+9], S44,0xEB86D391);
		a=AddUnsigned(a,AA);
		b=AddUnsigned(b,BB);
		c=AddUnsigned(c,CC);
		d=AddUnsigned(d,DD);
	}

	var temp = WordToHex(a)+WordToHex(b)+WordToHex(c)+WordToHex(d);

	return temp.toLowerCase();
}

export default {MD5};


================================================
FILE: src/hack_header.js
================================================
function hackHeader(url) {
  const replace_referer = true;
  let replace_origin = true;
  const add_referer = true;
  let add_origin = true;

  let referer_value = '';

  if (url.indexOf('://music.163.com/') !== -1) {
    referer_value = 'http://music.163.com/';
  }
  if (url.indexOf('://gist.githubusercontent.com/') !== -1) {
    referer_value = 'https://gist.githubusercontent.com/';
  }

  if (url.indexOf('api.xiami.com/') !== -1 || url.indexOf('.xiami.com/song/playlist/id/') !== -1
      || url.indexOf('www.xiami.com/api/') !== -1
  ) {
    add_origin = false;
    referer_value = 'https://www.xiami.com';
  }

  if (url.indexOf('www.xiami.com/api/search/searchSongs') !== -1) {
    const key = /key%22:%22(.*?)%22/.exec(url)[1];
    replace_origin = false;
    add_origin = false;
    referer_value = `https://www.xiami.com/search?key=${key}`;
  }

  if (url.indexOf('c.y.qq.com/') !== -1) {
    referer_value = 'https://y.qq.com';
  }
  if ((url.indexOf('i.y.qq.com/') !== -1)
      || (url.indexOf('qqmusic.qq.com/') !== -1)
      || (url.indexOf('music.qq.com/') !== -1)
      || (url.indexOf('imgcache.qq.com/') !== -1)) {
    referer_value = 'https://y.qq.com/';
  }

  if (url.indexOf('.kugou.com/') !== -1) {
    referer_value = 'http://www.kugou.com/';
  }

  if (url.indexOf('.kuwo.cn/') !== -1) {
    referer_value = 'http://www.kuwo.cn/';
  }

  if (url.indexOf('.bilibili.com/') !== -1) {
    referer_value = 'http://www.bilibili.com/';
    replace_origin = false;
    add_origin = false;
  }

  return {
    replace_referer, add_referer, replace_origin, add_origin, referer_value,
  };
}

export default { hackHeader };


================================================
FILE: src/index.js
================================================
import {
  getParameterByName,
  httpParamEncode,
} from './utils';
import { hackHeader } from './hack_header';
import NeteaseFactory from './provider/netease';
import XiamiFactory from './provider/xiami';
import QQFactory from './provider/qq';
import KugouFactory from './provider/kugou';
import KuwoFactory from './provider/kuwo';
import BiliFactory from './provider/bilibili';

function getAllProviders() {
  return [NeteaseFactory, QQFactory, XiamiFactory, KugouFactory, KuwoFactory, BiliFactory];
}

function getProviderByName(sourceName) {
  if (sourceName === 'netease') {
    return NeteaseFactory;
  }
  if (sourceName === 'xiami') {
    return XiamiFactory;
  }
  if (sourceName === 'qq') {
    return QQFactory;
  }
  if (sourceName === 'kugou') {
    return KugouFactory;
  }
  if (sourceName === 'kuwo') {
    return KuwoFactory;
  }
  if (sourceName === 'bilibili') {
    return BiliFactory;
  }
  return null;
}

function getProviderByItemId(itemId) {
  const prefix = itemId.slice(0, 2);
  if (prefix === 'ne') {
    return NeteaseFactory;
  }
  if (prefix === 'xm') {
    return XiamiFactory;
  }
  if (prefix === 'qq') {
    return QQFactory;
  }
  if (prefix === 'kg') {
    return KugouFactory;
  }
  if (prefix === 'kw') {
    return KuwoFactory;
  }
  if (prefix === 'bi') {
    return BiliFactory;
  }
  return null;
}

let globalCookieProvider = null;
let globalHTTPClient = null;
const platform = {};

function loadNodejsDefaults() {
  // eslint-disable-next-line global-require
  platform.nodejs = require('./platform/node');
  globalCookieProvider = new platform.nodejs.CookieProvider();
  globalHTTPClient = platform.nodejs.HTTPClient;
}
function loadBrowserDefaults() {
  // eslint-disable-next-line global-require
  platform.browser = require('./platform/browser');
  globalCookieProvider = platform.browser.CookieProvider;
}

if (typeof window === 'undefined') {
  // nodejs
  // eslint-disable-next-line global-require
  loadNodejsDefaults();
} else {
  // chrome
  // eslint-disable-next-line global-require
  loadBrowserDefaults();
}

function apiGet(url, httpClient, pfn, cookieProvider) {
  // default auto set
  if (httpClient === undefined || httpClient === null) {
    // eslint-disable-next-line no-param-reassign
    httpClient = globalHTTPClient;
  }
  if (pfn === undefined || pfn === null) {
    // eslint-disable-next-line no-param-reassign
    pfn = fn => new Promise(fn);
  }
  if (cookieProvider === undefined || cookieProvider === null) {
    // eslint-disable-next-line no-param-reassign
    cookieProvider = globalCookieProvider;
  }


  // router
  if (url.search('/show_playlist') !== -1) {
    const source = getParameterByName('source', url);
    const provider = getProviderByName(source);
    return provider.showPlaylist(url, httpClient, pfn, cookieProvider);
  }
  if (url.search('/playlist') !== -1) {
    const listId = getParameterByName('list_id', url);
    const provider = getProviderByItemId(listId);
    return provider.getPlaylist(url, httpClient, pfn, cookieProvider);
  }
  if (url.search('/search') !== -1) {
    const source = getParameterByName('source', url);
    const provider = getProviderByName(source);
    return provider.search(url, httpClient, pfn, cookieProvider);
  }
  if (url.search('/lyric') !== -1) {
    const trackId = getParameterByName('track_id', url);
    const provider = getProviderByItemId(trackId);
    return provider.lyric(url, httpClient, pfn, cookieProvider);
  }
  if (url.search('/bootstrap_track') !== -1) {
    const trackId = getParameterByName('track_id', url);
    const provider = getProviderByItemId(trackId);
    return provider.bootstrapTrack(trackId, httpClient, pfn, cookieProvider);
  }
  return null;
}

export default {
  getAllProviders,
  getProviderByItemId,
  getProviderByName,
  apiGet,
  hackHeader,
  httpParamEncode,
  platform,
  loadNodejsDefaults,
  loadBrowserDefaults,
};


================================================
FILE: src/platform/browser.js
================================================
// function getDomain(url) {
//   const matches = url.match(/^https?:\/\/([^/?#]+)(?:[/?#]|$)/i);
//   const domain = matches && matches[1];
//   return domain;
// }

function getCookieProvider() {
  // const domain = 'https://www.xiami.com';
  // const name = 'xm_sg_tk';
  const cookieProvider = {};
  if (typeof chrome !== 'undefined') {
    // chrome cookie provider
    // eslint-disable-next-line func-names
    cookieProvider.getCookie = function (url, name, callback) {
      // eslint-disable-next-line no-undef
      chrome.cookies.get({
        url,
        name,
      }, (cookie) => {
        if (cookie == null) {
          return callback('');
        }
        return callback(cookie.value);
      });
    };
  } else {
    // TODO: fix electron auto load, now webpack will require electron in any platform build.
    // // electron cookie provider
    // cookieProvider.getCookie = function getCookie(url, name, callback) {
    //   // trick to avoid require electron not found in compile time
    //   const remote = require('electron').remote; // eslint-disable-line
    //   const domain = getDomain(url);
    //   remote.session.defaultSession.cookies.get({
    //     domain,
    //     name,
    //   }, (err, cookie) => {
    //     if (cookie.length === 0) {
    //       return callback('');
    //     }
    //     return callback(cookie[0].value);
    //   });
    // };
  }
  return cookieProvider;
}

export default { CookieProvider: getCookieProvider() };


================================================
FILE: src/platform/node.js
================================================
import { hackHeader } from '../hack_header';

// eslint-disable-next-line import/no-dynamic-require
const request = require('request').defaults({ jar: true });

class CookieProvider {
  constructor() {
    this.data = {};
  }

  getCookie(url, name, callback) {
    const domain = this.getDomain(url);
    if (this.data[domain] == null) {
      return callback('');
    }
    return callback(this.data[domain][name]);
  }

  setCookie(url, name, value) {
    const domain = this.getDomain(url);
    if (this.data[domain] == null) {
      this.data[domain] = {};
    }
    this.data[domain][name] = value;
  }

  getCookieForHTTPHeader(url) {
    const domain = this.getDomain(url);
    if (this.data[domain] == null) {
      return '';
    }
    const kvArray = [];
    Object.keys(this.data[domain]).forEach((k) => {
      const v = this.data[domain][k];
      kvArray.push(`${k}=${v}`);
    });
    return `${kvArray.join(';')};`;
  }

  // eslint-disable-next-line class-methods-use-this
  getDomain(url) {
    const matches = url.match(/^https?:\/\/([^/?#]+)(?:[/?#]|$)/i);
    const domain = matches && matches[1];
    return domain;
  }
}

function HTTPClient(params) {
  // console.log(params);
  // cookieProvider in params? should update it
  return new Promise((resolve, reject) => {
    let headers = {
      // eslint-disable-next-line max-len
      'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36',
    };
    if (params.headers !== undefined) {
      headers = Object.assign(headers, params.headers);
    }
    const action = hackHeader(params.url);
    if (action.add_referer || (action.replace_referer && headers.Referer === undefined)) {
      headers.Referer = action.referer_value;
    }
    if (action.add_origin || (action.replace_origin && headers.Origin === undefined)) {
      headers.Origin = action.referer_value;
    }
    // if cookie provider is provided, send cookie related to this domain
    if (params.cookieProvider !== undefined) {
      headers.Cookie = params.cookieProvider.getCookieForHTTPHeader(params.url);
    }
    if (params.method === 'GET') {
      return request.get({
        headers,
        uri: params.url,
      }, (error, response, body) => {
        if (error) {
          return reject();
        }
        if (params.cookieProvider !== undefined && response.headers['set-cookie'] !== undefined) {
          response.headers['set-cookie'].forEach((cookieString) => {
            const kvString = cookieString.split(';')[0];
            const k = kvString.split('=')[0];
            const v = kvString.split('=')[1];
            params.cookieProvider.setCookie(params.url, k, v);
          });
        }
        if (params.transformResponse === false) {
          return resolve({
            data: body,
          });
        }
        return resolve({
          data: JSON.parse(body),
        });
      });
    } if (params.method === 'POST') {
      return request.post({
        headers,
        uri: params.url,
        form: params.data,
      }, (error, response, body) => {
        if (error) {
          return reject();
        }
        if (params.transformResponse === false) {
          return resolve({
            data: body,
          });
        }
        return resolve({
          data: JSON.parse(body),
        });
      });
    }
    return null;
  });
}
export default { CookieProvider, HTTPClient };


================================================
FILE: src/provider/bilibili.js
================================================
/* eslint-disable no-unused-vars */
/* Author: @71e6fd52 */
import { getParameterByName } from '../utils';

function build_bilibili() {
  function bi_convert_song(song_info) {
    const track = {
      id: `bitrack_${song_info.id}`,
      title: song_info.title,
      artist: song_info.uname,
      artist_id: `biartist_${song_info.uid}`,
      source: 'bilibili',
      source_url: `https://www.bilibili.com/audio/au${song_info.id}`,
      img_url: song_info.cover,
      url: song_info.id,
      lyric_url: song_info.lyric,
    };
    return track;
  }

  function bi_show_playlist(url, hm, pfn) {
    let offset = getParameterByName('offset', url);
    if (offset === undefined) {
      offset = 0;
    }
    const page = offset / 20 + 1;
    const target_url = `https://www.bilibili.com/audio/music-service-c/web/menu/hit?ps=20&pn=${page}`;
    return pfn((resolve, reject) => {
      hm({ url: target_url, method: 'GET' }).then((response) => {
        const { data } = response.data.data;
        const result = data.map(item => ({
          cover_img_url: item.cover,
          title: item.title,
          id: `biplaylist_${item.menuId}`,
          source_url: `https://www.bilibili.com/audio/am${item.menuId}`,
        }));
        return resolve({
          result,
        });
      });
    });
  }

  function bi_get_playlist(url, hm, pfn) { // eslint-disable-line no-unused-vars
    const list_id = getParameterByName('list_id', url).split('_').pop();
    const target_url = `https://www.bilibili.com/audio/music-service-c/web/menu/info?sid=${list_id}`;
    return pfn((resolve, reject) => {
      hm({ url: target_url, method: 'GET' }).then((response) => {
        const { data } = response.data;
        const info = {
          cover_img_url: data.cover,
          title: data.title,
          id: `biplaylist_${list_id}`,
          source_url: `https://www.bilibili.com/audio/am${list_id}`,
        };
        const target = `https://www.bilibili.com/audio/music-service-c/web/song/of-menu?pn=1&ps=100&sid=${list_id}`;
        hm({ url: target, method: 'GET' }).then((res) => {
          const tracks = res.data.data.data.map(item => bi_convert_song(item));
          return resolve({
            info,
            tracks,
          });
        });
      });
    });
  }
  function bi_album(url, hm, pfn) { // eslint-disable-line no-unused-vars
    return pfn((resolve, reject) => resolve({
      tracks: [],
      info: {},
    }));
    // bilibili havn't album
    // const album_id = getParameterByName('list_id', url).split('_').pop();
    // const target_url = '';
    // hm({url:target_url, method:'GET'}).then((response) => {
    //   const data = response.data;
    //   const info = {};
    //   const tracks = [];
    //   return fn({
    //     tracks,
    //     info,
    //   });
    // });
  }
  function bi_artist(url, hm, pfn) { // eslint-disable-line no-unused-vars
    return pfn((resolve, reject) => {
      const artist_id = getParameterByName('list_id', url).split('_').pop();
      let target_url = 'https://space.bilibili.com/ajax/member/GetInfo';
      hm({
        url: target_url,
        method: 'POST',
        data: { mid: artist_id },
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
      })
        .then((response) => {
          const { data } = response.data;
          const info = {
            cover_img_url: data.face,
            title: data.name,
            id: `biartist_${artist_id}`,
            source_url: `https://space.bilibili.com/${artist_id}/#/audio`,
          };
          // eslint-disable-next-line max-len
          target_url = `https://api.bilibili.com/audio/music-service-c/web/song/upper?pn=1&ps=0&order=2&uid=${artist_id}`;
          hm({ url: target_url, method: 'GET' }).then((res) => {
            const tracks = res.data.data.data.map(item => bi_convert_song(item));
            return resolve({
              tracks,
              info,
            });
          });
        });
    });
  }
  function bi_parse_url(url) {
    let result;
    const match = /\/\/www.bilibili.com\/audio\/am([0-9]+)/.exec(url);
    if (match != null) {
      const playlist_id = match[1];
      result = {
        type: 'playlist',
        id: `biplaylist_${playlist_id}`,
      };
    }
    return result;
  }
  // eslint-disable-next-line no-unused-vars
  function bi_bootstrap_track(trackId, hm, pfn) {
    const song_id = trackId.slice('bitrack_'.length);
    const target_url = `https://www.bilibili.com/audio/music-service-c/web/url?sid=${song_id}`;
    return pfn((resolve, reject) => {
      hm({ url: target_url, method: 'GET' }).then((response) => {
        const { data } = response;
        if (data.code === 0) {
          resolve({ url: data.data.cdns[0] });
        } else {
          reject();
        }
      });
    });
  }
  function bi_search(url, hm, pfn) { // eslint-disable-line no-unused-vars
    return pfn((resolve, reject) => {
      const keyword = getParameterByName('keywords', url);
      const curpage = getParameterByName('curpage', url);
      const au = /\d+$/.exec(keyword);
      if (au != null) {
        const target_url = `https://www.bilibili.com/audio/music-service-c/web/song/info?sid=${au}`;
        hm({ url: target_url, method: 'GET' }).then((response) => {
          const { data } = response.data;
          const tracks = [bi_convert_song(data)];
          return resolve({
            result: tracks,
            total: 1,
          });
        });
      } else {
        return resolve({
          result: [],
          total: 0,
        });
      }
      // inferred, not implemented yet
      // eslint-disable-next-line max-len
      const target_url = `https://api.bilibili.com/x/web-interface/search/type?search_type=audio&keyword=${keyword}&page=${curpage}`;
      hm({ url: target_url, method: 'GET' }).then((response) => {
        const { data } = response.data;
        const tracks = data.result.map(item => bi_convert_song(item));
        return resolve({
          result: tracks,
          total: data.numResults,
        });
      });
      return null;
    });
  }
  function bi_lyric(url, hm, pfn) { // eslint-disable-line no-unused-vars
    // const track_id = getParameterByName('track_id', url).split('_').pop();
    const lyric_url = getParameterByName('lyric_url', url);
    return pfn((resolve, reject) => {
      hm({ url: lyric_url, method: 'GET' }).then((response) => {
        const { data } = response;
        return resolve({
          lyric: data,
        });
      });
    });
  }

  function get_playlist(url, hm, pfn) {
    const list_id = getParameterByName('list_id', url).split('_')[0];
    switch (list_id) {
      case 'biplaylist':
        return bi_get_playlist(url, hm, pfn);
      case 'bialbum':
        return bi_album(url, hm, pfn);
      case 'biartist':
        return bi_artist(url, hm, pfn);
      default:
        return null;
    }
  }

  return {
    showPlaylist: bi_show_playlist,
    getPlaylist: get_playlist,
    parseUrl: bi_parse_url,
    bootstrapTrack: bi_bootstrap_track,
    search: bi_search,
    lyric: bi_lyric,
  };
}

export default build_bilibili(); // eslint-disable-line no-unused-vars


================================================
FILE: src/provider/kugou.js
================================================
/* eslint-disable no-unused-vars */
/* Author: @WinterXMQ */
import async from 'async';
import { getParameterByName } from '../utils';

function build_kugou() {
  function kg_convert_song(song) {
    const track = {
      id: `kgtrack_${song.FileHash}`,
      title: song.SongName,
      artist: '',
      artist_id: '',
      album: song.AlbumName,
      album_id: `kgalbum_${song.AlbumID}`,
      source: 'kugou',
      source_url: `http://www.kugou.com/song/#hash=${song.FileHash}&album_id=${song.AlbumID}`,
      img_url: '',
      url: `kgtrack_${song.FileHash}`,
      lyric_url: song.FileHash,
    };
    let singer_id = song.SingerId;
    let singer_name = song.SingerName;
    if (song.SingerId instanceof Array) {
      [singer_id] = singer_id;
      [singer_name] = singer_name.split('、');
    }
    track.artist = singer_name;
    track.artist_id = `kgartist_${singer_id}`;
    return track;
  }

  function async_process_list(data_list, handler, handler_extra_param_list, callback) {
    const fnDict = {};
    data_list.forEach((item, index) => {
      fnDict[index] = cb => handler(index, item, handler_extra_param_list, cb);
    });
    async.parallel(fnDict,
      (_, results) => callback(null, data_list.map((item, index) => results[index])));
  }

  function kg_render_search_result_item(index, item, params, callback) {
    const hm = params[0];

    const track = kg_convert_song(item);
    // Add singer img
    const url = `${'http://www.kugou.com/yy/index.php?'
              + 'r=play/getdata&hash='}${track.lyric_url}`;
    hm({
      url,
      method: 'GET',
      transformResponse: false,
    }).then((response) => {
      let { data } = response;
      data = JSON.parse(data);
      track.img_url = data.data.img;
      callback(null, track);
    });
  }

  function kg_search(url, hm, pfn) { // eslint-disable-line no-unused-vars
    return pfn((resolve, reject) => {
      const keyword = getParameterByName('keywords', url);
      const curpage = getParameterByName('curpage', url);
      const target_url = `${'http://songsearch.kugou.com/'
            + 'song_search_v2?keyword='}${keyword}&page=${curpage}`;
      hm({
        url: target_url,
        method: 'GET',
        transformResponse: false,
      })
        .then((response) => {
          let { data } = response;
          data = JSON.parse(data);
          async_process_list(data.data.lists, kg_render_search_result_item, [hm],
            (_, tracks) => resolve({
              result: tracks,
              total: data.data.total,
            }));
        });
    });
  }

  function kg_render_playlist_result_item(index, item, params, callback) {
    const hm = params[0];
    let target_url = `${'http://m.kugou.com/app/i/getSongInfo.php?'
          + 'cmd=playInfo&hash='}${item.hash}`;

    const track = {
      id: `kgtrack_${item.hash}`,
      title: '',
      artist: '',
      artist_id: '',
      album: '',
      album_id: `kgalbum_${item.album_id}`,
      source: 'kugou',
      source_url: `http://www.kugou.com/song/#hash=${
        item.hash}&album_id=${item.album_id}`,
      img_url: '',
      url: `xmtrack_${item.hash}`,
      lyric_url: item.hash,
    };
      // Fix song info
    hm({ url: target_url, method: 'GET' }).then((response) => {
      const { data } = response;
      track.title = data.songName;
      track.artist = data.singerId === 0
        ? '未知' : data.singerName;
      track.artist_id = `kgartist_${data.singerId}`;
      if (data.imgUrl !== undefined) {
        track.img_url = data.imgUrl.replace('{size}', '400');
      } else {
        // track['img_url'] = data.imgUrl.replace('{size}', '400');
      }
      // Fix album
      target_url = `http://mobilecdnbj.kugou.com/api/v3/album/info?albumid=${
        item.album_id}`;
      hm({ url: target_url, method: 'GET' }).then((res) => {
        const { data: res_data } = res;
        if (res_data.status && res_data.data !== undefined) {
          track.album = res_data.data.albumname;
        } else {
          track.album = '';
        }
        return callback(null, track);
      });
    });
  }

  function kg_get_playlist(url, hm, pfn) { // eslint-disable-line no-unused-vars
    return pfn((resolve, reject) => {
      const list_id = getParameterByName('list_id', url).split('_').pop();
      const target_url = `http://m.kugou.com/plist/list/${list_id}?json=true`;

      hm({ url: target_url, method: 'GET' }).then((response) => {
        const { data } = response;

        const info = {
          cover_img_url: data.info.list.imgurl
            ? data.info.list.imgurl.replace('{size}', '400') : '',
          title: data.info.list.specialname,
          id: `kgplaylist_${data.info.list.specialid}`,
          source_url: 'http://www.kugou.com/yy/special/single/{size}.html'
            .replace('{size}', data.info.list.specialid),
        };

        async_process_list(data.list.list.info, kg_render_playlist_result_item, [hm],
          (_, tracks) => resolve({
            tracks,
            info,
          }));
      });
    });
  }

  function kg_render_artist_result_item(index, item, params, callback) {
    const hm = params[0];
    const info = params[1];
    const track = {
      id: `kgtrack_${item.hash}`,
      title: '',
      artist: '',
      artist_id: info.id,
      album: '',
      album_id: `kgalbum_${item.album_id}`,
      source: 'kugou',
      source_url: `http://www.kugou.com/song/#hash=${
        item.hash}&album_id=${item.album_id}`,
      img_url: '',
      url: `kgtrack_${item.hash}`,
      lyric_url: item.hash,
    };
    const one = item.filename.split('-');
    track.title = one[1].trim();
    track.artist = one[0].trim();
    // Fix album name and img
    const target_url = `${'http://www.kugou.com/yy/index.php?'
              + 'r=play/getdata&hash='}${item.hash}`;
    hm({
      url: `http://mobilecdnbj.kugou.com/api/v3/album/info?albumid=${item.album_id}`,
      method: 'GET',
      transformResponse: false,
    }).then((response) => {
      let { data } = response;
      data = JSON.parse(data);
      if (data.status && data.data !== undefined) {
        track.album = data.data.albumname;
      } else {
        track.album = '';
      }
      hm({
        url: target_url,
        method: 'GET',
        transformResponse: false,
      }).then((res) => {
        const res_data = JSON.parse(res.data);
        track.img_url = res_data.data.img;
        callback(null, track);
      });
    });
  }

  function kg_artist(url, hm, pfn) { // eslint-disable-line no-unused-vars
    return pfn((resolve, reject) => {
      const artist_id = getParameterByName('list_id', url).split('_').pop();
      let target_url = `http://mobilecdnbj.kugou.com/api/v3/singer/info?singerid=${artist_id}`;
      hm({
        url: target_url,
        method: 'GET',
        transformResponse: false,
      }).then((response) => {
        let { data } = response;
        data = JSON.parse(data);
        const info = {
          cover_img_url: data.data.imgurl.replace('{size}', '400'),
          title: data.data.singername,
          id: `kgartist_${artist_id}`,
          source_url: 'http://www.kugou.com/singer/{id}.html'.replace('{id}', artist_id),
        };
        target_url = `http://mobilecdnbj.kugou.com/api/v3/singer/song?singerid=${
          artist_id}&page=1&pagesize=30`;
        hm({
          url: target_url,
          method: 'GET',
          transformResponse: false,
        }).then((res) => {
          let { data: res_data } = res;
          res_data = JSON.parse(res_data);
          async_process_list(res_data.data.info, kg_render_artist_result_item, [hm, info],
            (_, tracks) => resolve({
              tracks,
              info,
            }));
        });
      });
    });
  }

  function getTimestampString() {
    return (new Date()).getTime().toString();
  }

  function getRandomIntString() {
    return (Math.random() * 100).toString().replace(/\D/g, '');
  }

  // eslint-disable-next-line no-unused-vars
  function kg_bootstrap_track(trackId, hm, pfn) {
    let target_url = 'https://wwwapi.kugou.com/yy/index.php?r=play/getdata';
    const jQueryHeader = `jQuery1910${getRandomIntString()}_${getTimestampString()}`;

    const song_id = trackId.slice('kgtrack_'.length);

    target_url = `${target_url}&callback=${jQueryHeader}&hash=${song_id}&_=${getTimestampString()}`;
    return pfn((resolve, reject) => {
      hm({
        url: target_url,
        method: 'GET',
        transformResponse: false,
      }).then((response) => {
        let data = response.data.slice(jQueryHeader.length + 1, response.data.length - 2);
        data = JSON.parse(data);
        if (data.status === 1) {
          resolve({ url: data.data.play_url });
        } else {
          reject();
        }
      });
    });
  }

  function kg_lyric(url, hm, pfn) { // eslint-disable-line no-unused-vars
    const track_id = getParameterByName('track_id', url).split('_').pop();
    const lyric_url = `http://www.kugou.com/yy/index.php?r=play/getdata&hash=${
      track_id}`;
    return pfn((resolve, reject) => {
      hm({
        url: lyric_url,
        method: 'GET',
        transformResponse: false,
      }).then((response) => {
        let { data } = response;
        data = JSON.parse(data);
        return resolve({
          lyric: data.data.lyrics,
        });
      });
    });
  }

  function kg_render_album_result_item(index, item, params, callback) {
    const hm = params[0];
    const info = params[1];
    const album_id = params[2];
    const track = {
      id: `kgtrack_${item.hash}`,
      title: '',
      artist: '',
      artist_id: '',
      album: info.title,
      album_id: `kgalbum_${album_id}`,
      source: 'kugou',
      source_url: `http://www.kugou.com/song/#hash=${
        item.hash}&album_id=${album_id}`,
      img_url: '',
      url: `xmtrack_${item.hash}`,
      lyric_url: item.hash,
    };
      // Fix other data
    const target_url = `${'http://m.kugou.com/app/i/getSongInfo.php?'
              + 'cmd=playInfo&hash='}${item.hash}`;
    hm({
      url: target_url,
      method: 'GET',
      transformResponse: false,
    }).then((response) => {
      const data = JSON.parse(response.data);
      track.title = data.songName;
      track.artist = data.singerId === 0
        ? '未知' : data.singerName;
      track.artist_id = `kgartist_${data.singerId}`;
      track.img_url = data.imgUrl.replace('{size}', '400');
      callback(null, track);
    });
  }

  function kg_album(url, hm, pfn) { // eslint-disable-line no-unused-vars
    return pfn((resolve, reject) => {
      const album_id = getParameterByName('list_id', url).split('_').pop();
      let target_url = `${'http://mobilecdnbj.kugou.com/api/v3/album/info?'
            + 'albumid='}${album_id}`;

      let info;
      // info
      hm({
        url: target_url,
        method: 'GET',
        transformResponse: false,
      }).then((response) => {
        let { data } = response;
        data = JSON.parse(data);

        info = {
          cover_img_url: data.data.imgurl.replace('{size}', '400'),
          title: data.data.albumname,
          id: `kgalbum_${data.data.albumid}`,
          source_url: 'http://www.kugou.com/album/{id}.html'
            .replace('{id}', data.data.albumid),
        };

        target_url = `${'http://mobilecdnbj.kugou.com/api/v3/album/song?'
              + 'albumid='}${album_id}&page=1&pagesize=-1`;
        hm({
          url: target_url,
          method: 'GET',
          transformResponse: false,
        }).then((res) => {
          let res_data = res.data;
          res_data = JSON.parse(res_data);

          async_process_list(res_data.data.info, kg_render_album_result_item,
            [hm, info, album_id],
            (_, tracks) => resolve({
              tracks,
              info,
            }));
        });
      });
    });
  }

  function kg_show_playlist(url, hm, pfn) {
    let offset = getParameterByName('offset', url);
    if (offset === undefined) {
      offset = 0;
    }
    // const page = offset / 30 + 1;
    const target_url = `${'http://m.kugou.com/plist/index'
              + '&json=true&page='}${offset}`;
    return pfn((resolve, reject) => {
      hm({ url: target_url, method: 'GET' }).then((response) => {
        const { data } = response;
        // const total = data.plist.total;
        const result = data.plist.list.info.map(item => ({
          cover_img_url: item.imgurl ? item.imgurl.replace('{size}', '400') : '',
          title: item.specialname,
          id: `kgplaylist_${item.specialid}`,
          source_url: 'http://www.kugou.com/yy/special/single/{size}.html'.replace('{size}', item.specialid),
        }));
        return resolve({
          result,
        });
      });
    });
  }

  function kg_parse_url(url) {
    let result;
    const match = /\/\/www.kugou.com\/yy\/special\/single\/([0-9]+).html/.exec(url);
    if (match != null) {
      const playlist_id = match[1];
      result = {
        type: 'playlist',
        id: `kgplaylist_${playlist_id}`,
      };
    }
    return result;
  }

  function get_playlist(url, hm, pfn) { // eslint-disable-line no-unused-vars
    const list_id = getParameterByName('list_id', url).split('_')[0];
    switch (list_id) {
      case 'kgplaylist':
        return kg_get_playlist(url, hm, pfn);
      case 'kgalbum':
        return kg_album(url, hm, pfn);
      case 'kgartist':
        return kg_artist(url, hm, pfn);
      default:
        return null;
    }
  }

  return {
    showPlaylist: kg_show_playlist,
    getPlaylist: get_playlist,
    parseUrl: kg_parse_url,
    bootstrapTrack: kg_bootstrap_track,
    search: kg_search,
    lyric: kg_lyric,
  };
}

export default build_kugou(); // eslint-disable-line no-unused-vars


================================================
FILE: src/provider/kuwo.js
================================================
/* eslint-disable no-unused-vars */
/* Author: @WinterXMQ */
import async from 'async';
import { getParameterByName } from '../utils';

function build_kuwo() {
  // Convert html code
  function html_decode(str) {
    return str.replace(/(&nbsp;)/g, ' ');
  }
  // Fix single quote in json
  function fix_json(data) {
    return data.replace(/(')/g, '"');
  }

  function num2str(num) {
    // const t = parseInt(num, 10);
    return parseInt(num / 10, 10).toString() + (num % 10).toString();
  }

  function kw_convert_song(item) {
    const song_id = item.MUSICRID.split('_').pop();
    const track = {
      id: `kwtrack_${song_id}`,
      title: html_decode(item.SONGNAME),
      artist: item.ARTIST,
      artist_id: `kwartist_${item.ARTISTID}`,
      album: html_decode(item.ALBUM),
      album_id: `kwalbum_${item.ALBUMID}`,
      source: 'kuwo',
      source_url: `http://www.kuwo.cn/yinyue/${song_id}`,
      img_url: '',
      url: `xmtrack_${song_id}`,
      lyric_url: song_id,
    };
    return track;
  }

  function async_process_list(data_list, handler, handler_extra_param_list, callback) {
    const fnDict = {};
    data_list.forEach((item, index) => {
      fnDict[index] = cb => handler(index, item, handler_extra_param_list, cb);
    });
    async.parallel(fnDict, (_, results) => {
      callback(null, data_list.map((item, index) => results[index]));
    });
  }

  function kw_add_song_pic_in_track(track, params, callback) {
    const hm = params[0];

    // Add song picture image
    const target_url = `${'http://artistpicserver.kuwo.cn/pic.web?'
        + 'type=rid_pic&pictype=url&size=240&rid='}${track.lyric_url}`;
    hm({
      url: target_url,
      method: 'GET',
      transformResponse: false,
    }).then((response) => {
      const { data } = response;
      track.img_url = data; // eslint-disable-line no-param-reassign
      callback(null, track);
    });
  }

  function kw_render_search_result_item(index, item, params, callback) {
    // const hm = params[0];

    const track = kw_convert_song(item);
    kw_add_song_pic_in_track(track, params, callback);
  }

  function kw_render_artist_result_item(index, item, params, callback) {
    // const hm = params[0];

    const track = {
      id: `kwtrack_${item.musicrid}`,
      title: html_decode(item.name),
      artist: item.artist,
      artist_id: `kwartist_${item.artistid}`,
      album: html_decode(item.album),
      album_id: `kwalbum_${item.albumid}`,
      source: 'kuwo',
      source_url: `http://www.kuwo.cn/yinyue/${item.musicrid}`,
      img_url: '',
      url: `xmtrack_${item.musicrid}`,
      lyric_url: item.musicrid,
    };

    kw_add_song_pic_in_track(track, params, callback);
  }

  function kw_render_album_result_item(index, item, params, callback) {
    // const hm = params[0];
    const info = params[1];

    const track = {
      id: `kwtrack_${item.id}`,
      title: html_decode(item.name),
      artist: item.artist,
      artist_id: `kwartist_${item.artistid}`,
      album: info.title,
      album_id: `kwalbum_${info.id}`,
      source: 'kuwo',
      source_url: `http://www.kuwo.cn/yinyue/${item.id}`,
      img_url: '',
      url: `xmtrack_${item.id}`,
      lyric_url: item.id,
    };

    kw_add_song_pic_in_track(track, params, callback);
  }

  function kw_render_playlist_result_item(index, item, params, callback) {
    // const hm = params[0];

    const track = {
      id: `kwtrack_${item.id}`,
      title: html_decode(item.name),
      artist: item.artist,
      artist_id: `kwartist_${item.artistid}`,
      album: html_decode(item.album),
      album_id: `kwalbum_${item.albumid}`,
      source: 'kuwo',
      source_url: `http://www.kuwo.cn/yinyue/${item.id}`,
      img_url: '',
      url: `xmtrack_${item.id}`,
      lyric_url: item.id,
    };

    kw_add_song_pic_in_track(track, params, callback);
  }

  function kw_search(url, hm, pfn) { // eslint-disable-line no-unused-vars
    // API From https://blog.csdn.net/u011354613/article/details/52756467
    const keyword = getParameterByName('keywords', url);
    let curpage = getParameterByName('curpage', url);
    // page number is started from 0
    curpage -= 1;
    const target_url = `${'http://search.kuwo.cn/r.s?'
        + 'ft=music&itemset=web_2013&client=kt'
        + '&rformat=json&encoding=utf8'}${
      '&all={0}&pn={1}&rn=20'.replace('{0}', keyword).replace('{1}', curpage)}`;

    return pfn((resolve, reject) => {
      hm({
        url: target_url,
        method: 'GET',
        transformResponse: false,
      }).then((response) => {
        let { data } = response;
        data = JSON.parse(fix_json(data));
        async_process_list(data.abslist, kw_render_search_result_item, [hm],
          (_, tracks) => resolve({
            result: tracks,
            total: parseInt(data.TOTAL, 10),
          }));
      });
    });
  }

  // eslint-disable-next-line no-unused-vars
  function kw_bootstrap_track(trackId, hm, pfn) {
    const song_id = trackId.slice('kwtrack_'.length);
    const target_url = `${'http://antiserver.kuwo.cn/anti.s?'
        + 'type=convert_url&format=aac|mp3|wma&response=url&rid=MUSIC_'}${song_id}`;
    return pfn((resolve, reject) => {
      hm({
        url: target_url,
        method: 'GET',
        transformResponse: false,
      }).then((response) => {
        const { data } = response;
        if (data.length > 0) {
          resolve({ url: data });
        } else {
          reject();
        }
      });
    });
  }

  function kw_lyric(url, hm, pfn) { // eslint-disable-line no-unused-vars
    const track_id = getParameterByName('lyric_url', url);
    const target_url = `http://m.kuwo.cn/newh5/singles/songinfoandlrc?musicId=${track_id}`;

    return pfn((resolve, reject) => {
      hm({
        url: target_url,
        method: 'GET',
        transformResponse: false,
      }).then((response) => {
        let { data } = response;
        data = JSON.parse(data);

        const lyric = data.data.lrclist.reduce((str, item) => {
          const t = parseFloat(item.time);
          const m = parseInt(t / 60, 10);
          const s = parseInt(t - m * 60, 10);
          const ms = parseInt((t - m * 60 - s) * 100, 10);
          return `${str}[${num2str(m)}:${num2str(parseInt(s, 10))}.${num2str(ms)}]${item.lineLyric}\n`;
        }, '');
        return resolve({
          lyric,
        });
      });
    });
  }

  function kw_artist(url, hm, pfn) { // eslint-disable-line no-unused-vars
    const artist_id = getParameterByName('list_id', url).split('_').pop();
    return pfn((resolve, reject) => {
      let target_url = `${'http://search.kuwo.cn/r.s?stype=artistinfo'
            + '&artistid='}${artist_id}&encoding=utf8`;
      hm({
        url: target_url,
        method: 'GET',
        transformResponse: false,
      }).then((response) => {
        let { data } = response;
        data = JSON.parse(fix_json(data));
        const info = {
          cover_img_url: `http://img1.sycdn.kuwo.cn/star/starheads/${data.pic}`,
          title: html_decode(data.name),
          id: `kwartist_${data.id}`,
          source_url: `http://www.kuwo.cn/artist/content?name=${data.name}`,
        };

        // Get songs
        target_url = `${'http://search.kuwo.cn/r.s?stype=artist2music'
              + '&sortby=0&alflac=1&pcmp4=1&encoding=utf8'
              + '&artistid='}${artist_id}&pn=0&rn=100`;
        hm({
          url: target_url,
          method: 'GET',
          transformResponse: false,
        }).then((res) => {
          let { data: res_data } = res;
          res_data = JSON.parse(fix_json(res_data));
          async_process_list(res_data.musiclist, kw_render_artist_result_item, [hm],
            (_, tracks) => resolve({
              tracks,
              info,
            }));
        });
      });
    });
  }

  function kw_album(url, hm, pfn) { // eslint-disable-line no-unused-vars
    const album_id = getParameterByName('list_id', url).split('_').pop();
    return pfn((resolve, reject) => {
      const target_url = `${'http://search.kuwo.cn/r.s?pn=0&rn=1000&stype=albuminfo'
            + '&albumid='}${album_id
      }&alflac=1&pcmp4=1&encoding=utf8&vipver=MUSIC_8.7.7.0_W4`;
      hm({
        url: target_url,
        method: 'GET',
        transformResponse: false,
      }).then((response) => {
        let { data } = response;
        data = JSON.parse(fix_json(data));

        const info = {
          cover_img_url: `http://img1.sycdn.kuwo.cn/star/albumcover/${data.pic}`,
          title: html_decode(data.name),
          id: data.albumid,
          source_url: `http://www.kuwo.cn/album/${data.albumid}`,
        };

        // Get songs
        async_process_list(data.musiclist, kw_render_album_result_item, [hm, info],
          (_, tracks) => resolve({
            tracks,
            info,
          }));
      });
    });
  }

  function kw_show_playlist(url, hm, pfn) {
    let offset = getParameterByName('offset', url);
    if (!offset) {
      offset = 0;
    }
    /* const id_available = {
        1265: '经典',
        577: '纯音乐',
        621: '网络',
        155: '怀旧',
        1879: '网红',
        220: '佛乐',
        180: '影视',
        578: '器乐',
        1877: '游戏',
        181: '二次元',
        882: 'KTV',
        216: '喊麦',
        1366: '3D',
        146: '伤感',
        62: '放松',
        58: '励志',
        143: '开心',
        137: '甜蜜',
        139: '兴奋',
        67: '安静',
        66: '治愈',
        147: '寂寞',
        160: '四年',
        366: '运动',
        354: '睡前',
        378: '跳舞',
        1876: '学习',
        353: '清晨',
        359: '夜店',
        382: '校园',
        544: '亲热',
        363: '咖啡店',
        375: '旅行',
        371: '散步',
        386: '工作',
        336: '婚礼',
        637: '70后',
        638: '80后',
        639: '90后',
        640: '00后',
        268: '10后',
        393: '流行',
        391: '电子',
        389: '摇滚',
        1921: '民歌',
        392: '民谣',
        399: '乡村',
        35: '欧洲',
        37: '华语',
      }; */
    const target_url = `${'http://www.kuwo.cn/www/categoryNew/getPlayListInfoUnderCategory?'
        + 'type=taglist&digest=10000&id='}${37}&start=${offset}&count=50`;

    return pfn((resolve, reject) => {
      hm({ url: target_url, method: 'GET' }).then((response) => {
        const { data } = response.data;
        if (!data[0]) {
          return resolve([]);
        }
        const result = data[0].data.map(item => ({
          cover_img_url: item.img,
          title: item.name,
          id: `kwplaylist_${item.id}`,
          source_url: `http://www.kuwo.cn/playlist/index?pid=${item.id}`,
        }));
        return resolve({
          result,
        });
      });
    });
  }

  function kw_get_playlist(url, hm, pfn) { // eslint-disable-line no-unused-vars
    const list_id = getParameterByName('list_id', url).split('_').pop();
    const target_url = `${'http://nplserver.kuwo.cn/pl.svc?'
        + 'op=getlistinfo&pn=0&rn=200&encode=utf-8&keyset=pl2012&pcmp4=1'
        + '&pid='}${list_id}&vipver=MUSIC_9.0.2.0_W1&newver=1`;

    return pfn((resolve, reject) => {
      hm({ url: target_url, method: 'GET' }).then((response) => {
        const { data } = response;

        const info = {
          cover_img_url: data.pic,
          title: data.title,
          id: `kwplaylist_${data.id}`,
          source_url: `http://www.kuwo.cn/playlist/index?pid=${data.id}`,
        };

        async_process_list(data.musiclist, kw_render_playlist_result_item, [hm],
          (_, tracks) => resolve({
            tracks,
            info,
          }));
      });
    });
  }

  function kw_parse_url(url) {
    let result;
    const match = /\/\/www.kuwo.cn\/playlist\/index\?pid=([0-9]+)/.exec(url);
    if (match != null) {
      const playlist_id = match[1];
      result = {
        type: 'playlist',
        id: `kwplaylist_${playlist_id}`,
      };
    }
    return result;
  }

  function get_playlist(url, hm, pfn) {
    const list_id = getParameterByName('list_id', url).split('_')[0];
    switch (list_id) {
      case 'kwplaylist':
        return kw_get_playlist(url, hm, pfn);
      case 'kwalbum':
        return kw_album(url, hm, pfn);
      case 'kwartist':
        return kw_artist(url, hm, pfn);
      default:
        return null;
    }
  }

  return {
    showPlaylist: kw_show_playlist,
    getPlaylist: get_playlist,
    parseUrl: kw_parse_url,
    bootstrapTrack: kw_bootstrap_track,
    search: kw_search,
    lyric: kw_lyric,
  };
}

export default build_kuwo(); // eslint-disable-line no-unused-vars


================================================
FILE: src/provider/netease.js
================================================
/* eslint-disable import/named */
/* eslint-disable no-unused-vars */
/* eslint-disable no-param-reassign */
import { getParameterByName, getRandomHexString } from '../utils';
import { aesEncrypt, rsaEncrypt } from '../crypto/crypto';

const $ = require('cheerio');

function encryptedRequest(text) {
  const modulus = '00e0b509f6259df8642dbc35662901477df22677ec152b5ff68ace615bb7b72'
    + '5152b3ab17a876aea8a5aa76d2e417629ec4ee341f56135fccf695280104e0312ecbd'
    + 'a92557c93870114af6c9d05c4f7f0c3685b7a46bee255932575cce10b424d813cfe48'
    + '75d3e82047b97ddef52741d546b8e289dc6935b3ece0462db0a22b8e7';
  const nonce = '0CoJUm6Qyw8W8jud';
  const pubKey = '010001';
  text = JSON.stringify(text);
  const secKey = getRandomHexString(16);
  const ivString = '0102030405060708';
  const encText = aesEncrypt(aesEncrypt(text, nonce, ivString), secKey, ivString);
  const encSecKey = rsaEncrypt(secKey, pubKey, modulus);
  const data = {
    params: encText,
    encSecKey,
  };

  return data;
}

function NeteaseFactory() {
  function neShowPlaylist(url, hm, pfn) {
    const order = 'hot';
    const offset = getParameterByName('offset', url);
    let targetUrl;

    if (offset != null) {
      targetUrl = `http://music.163.com/discover/playlist/?order=${order}&limit=35&offset=${offset}`;
    } else {
      targetUrl = `http://music.163.com/discover/playlist/?order=${order}`;
    }

    return pfn((resolve, reject) => {
      const result = [];

      hm({
        url: targetUrl,
        method: 'GET',
        transformResponse: false,
      }).then((response) => {
        let { data } = response;
        // TODO: replace jquery to raw document api
        data = $.parseHTML(data);
        // eslint-disable-next-line func-names
        $(data).find('.m-cvrlst li').each(function () {
          const defaultPlaylist = {
            cover_img_url: '',
            title: '',
            id: '',
            source_url: '',
          };

          defaultPlaylist.cover_img_url = $(this).find('img')[0].attribs.src;
          defaultPlaylist.title = $(this).find('div a')[0].attribs.title;
          const url2 = $(this).find('div a')[0].attribs.href;
          const listId = getParameterByName('id', url2);
          defaultPlaylist.id = `neplaylist_${listId}`;
          defaultPlaylist.source_url = `http://music.163.com/#/playlist?id=${listId}`;
          result.push(defaultPlaylist);
        });
        return resolve({
          result,
        });
      });
    });
  }

  // function neEnsureCookie(callback) {
  //   var domain = 'https://music.163.com';
  //   var nuidName = '_ntes_nuid';
  //   var nnidName = '_ntes_nnid';
  //   // TODO: replace chrome for dependency injection
  //   var chrome = {};
  //   chrome.cookies.get({
  //     'url': domain,
  //     'name': nuidName
  //   }, function (cookie) {
  //     if (cookie == null) {
  //       var nuidValue = getRandomHexString(32);
  //       var nnidValue = nuidValue + ',' + (new Date()).getTime();
  //       // netease default cookie expire time: 100 years
  //       var expire = ((new Date()).getTime() + 1e3 * 60 * 60 * 24 * 365 * 100) / 1000;

  //       chrome.cookies.set({
  //         'url': domain,
  //         'name': nuidName,
  //         'value': nuidValue,
  //         'expirationDate': expire
  //       }, function (cookie) {
  //         chrome.cookies.set({
  //           'url': domain,
  //           'name': nnidName,
  //           'value': nnidValue,
  //           'expirationDate': expire
  //         }, function (cookie2) {
  //           callback(cookie.value);
  //         });
  //       });
  //     } else {
  //       callback(cookie.value);
  //     }
  //   });
  // }

  function neGetPlaylist(url, hm, pfn) {
    // special thanks for @Binaryify
    // https://github.com/Binaryify/NeteaseCloudMusicApi
    return pfn((resolve, reject) => {
      const listId = getParameterByName('list_id', url).split('_').pop();
      const targetUrl = 'http://music.163.com/weapi/v3/playlist/detail';
      const d = {
        id: listId,
        offset: 0,
        total: true,
        limit: 1000,
        n: 1000,
        csrf_token: '',
      };
      const data = encryptedRequest(d);
      // neEnsureCookie(function () {
      hm({
        url: targetUrl,
        method: 'POST',
        data,
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
      }).then((response) => {
        const { data: res_data } = response;
        const info = {
          id: `neplaylist_${listId}`,
          cover_img_url: res_data.playlist.coverImgUrl,
          title: res_data.playlist.name,
          source_url: `http://music.163.com/#/playlist?id=${listId}`,
        };
        const tracks = [];
        Array.prototype.forEach.call(res_data.playlist.tracks, (trackJson, index) => {
          const defaultTrack = {
            id: '0',
            title: '',
            artist: '',
            artist_id: 'neartist_0',
            album: '',
            album_id: 'nealbum_0',
            source: 'netease',
            source_url: 'http://www.xiami.com/song/0',
            img_url: '',
            url: '',
          };
          defaultTrack.id = `netrack_${trackJson.id}`;
          defaultTrack.title = trackJson.name;
          defaultTrack.artist = trackJson.ar[0].name;
          defaultTrack.artist_id = `neartist_${trackJson.ar[0].id}`;
          defaultTrack.album = trackJson.al.name;
          defaultTrack.album_id = `nealbum_${trackJson.al.id}`;
          defaultTrack.source_url = `http://music.163.com/#/song?id=${trackJson.id}`;
          defaultTrack.img_url = trackJson.al.picUrl;
          defaultTrack.url = defaultTrack.id;

          tracks.push(defaultTrack);
        });
        return resolve({
          info,
          tracks,
        });
      });
    });
  }

  function neBootstrapTrack(songId, hm, pfn) {
    const targetUrl = 'http://music.163.com/weapi/song/enhance/player/url?csrf_token=';
    const csrf = '';
    songId = songId.slice('netrack_'.length);
    const d = {
      ids: [songId],
      br: 320000,
      csrf_token: csrf,
    };
    const data = encryptedRequest(d);

    return pfn((resolve, reject) => {
      hm({
        url: targetUrl,
        method: 'POST',
        data,
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
      }).then((response) => {
        const { data: res_data } = response;
        const { url } = res_data.data[0];
        if (url != null) {
          resolve({ url });
        } else {
          reject({});
        }
      });
    });
  }

  function isPlayable(song) {
    return ((song.status >= 0) && (song.fee !== 4));
  }

  function neSearch(url, hm, pfn) {
    // use chrome extension to modify referer.
    const targetUrl = 'http://music.163.com/api/search/pc';
    const keyword = getParameterByName('keywords', url);
    const curpage = getParameterByName('curpage', url);
    const reqData = {
      s: keyword,
      offset: 20 * (curpage - 1),
      limit: 20,
      type: 1,
    };
    return pfn((resolve, reject) => {
      hm({
        url: targetUrl,
        method: 'POST',
        data: reqData,
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
      }).then((response) => {
        const { data } = response;
        const tracks = [];
        Array.prototype.forEach.call(data.result.songs, (songInfo, index) => {
          const defaultTrack = {
            id: `netrack_${songInfo.id}`,
            title: songInfo.name,
            artist: songInfo.artists[0].name,
            artist_id: `neartist_${songInfo.artists[0].id}`,
            album: songInfo.album.name,
            album_id: `nealbum_${songInfo.album.id}`,
            source: 'netease',
            source_url: `http://music.163.com/#/song?id=${songInfo.id}`,
            img_url: songInfo.album.picUrl,
            url: `netrack_${songInfo.id}`,
          };
          if (!isPlayable(songInfo)) {
            defaultTrack.disabled = true;
          } else {
            defaultTrack.disabled = false;
          }
          tracks.push(defaultTrack);
        });
        return resolve({
          result: tracks,
          total: data.result.songCount,
        });
      });
    });
  }

  function neAlbum(url, hm, pfn) {
    const albumId = getParameterByName('list_id', url).split('_').pop();
    // use chrome extension to modify referer.
    const targetUrl = `http://music.163.com/api/album/${albumId}`;

    return pfn((resolve, reject) => {
      hm({
        url: targetUrl,
        method: 'GET',
      }).then((response) => {
        const { data } = response;
        const info = {
          cover_img_url: data.album.picUrl,
          title: data.album.name,
          id: `nealbum_${data.album.id}`,
          source_url: `http://music.163.com/#/album?id=${data.album.id}`,
        };

        const tracks = [];
        Array.prototype.forEach.call(data.album.songs, (songInfo, index) => {
          const defaultTrack = {
            id: `netrack_${songInfo.id}`,
            title: songInfo.name,
            artist: songInfo.artists[0].name,
            artist_id: `neartist_${songInfo.artists[0].id}`,
            album: songInfo.album.name,
            album_id: `nealbum_${songInfo.album.id}`,
            source: 'netease',
            source_url: `http://music.163.com/#/song?id=${songInfo.id}`,
            img_url: songInfo.album.picUrl,
            url: `netrack_${songInfo.id}`,
          };
          if (!isPlayable(songInfo)) {
            defaultTrack.disabled = true;
          } else {
            defaultTrack.disabled = false;
          }
          tracks.push(defaultTrack);
        });
        return resolve({
          tracks,
          info,
        });
      });
    });
  }

  function neArtist(url, hm, pfn) {
    const artistId = getParameterByName('list_id', url).split('_').pop();
    // use chrome extension to modify referer.
    const targetUrl = `http://music.163.com/api/artist/${artistId}`;

    return pfn((resolve, reject) => {
      hm({
        url: targetUrl,
        method: 'GET',
      }).then((response) => {
        const { data } = response;
        const info = {
          cover_img_url: data.artist.picUrl,
          title: data.artist.name,
          id: `neartist_${data.artist.id}`,
          source_url: `http://music.163.com/#/artist?id=${data.artist.id}`,
        };

        const tracks = [];
        Array.prototype.forEach.call(data.hotSongs, (songInfo, i) => {
          const defaultTrack = {
            id: `netrack_${songInfo.id}`,
            title: songInfo.name,
            artist: songInfo.artists[0].name,
            artist_id: `neartist_${songInfo.artists[0].id}`,
            album: songInfo.album.name,
            album_id: `nealbum_${songInfo.album.id}`,
            source: 'netease',
            source_url: `http://music.163.com/#/song?id=${songInfo.id}`,
            img_url: songInfo.album.picUrl,
            url: `netrack_${songInfo.id}`,
          };
          if (!isPlayable(songInfo)) {
            defaultTrack.disabled = true;
          } else {
            defaultTrack.disabled = false;
          }
          tracks.push(defaultTrack);
        });
        return resolve({
          tracks,
          info,
        });
      });
    });
  }

  function neLyric(url, hm, pfn) {
    const trackId = getParameterByName('track_id', url).split('_').pop();
    // use chrome extension to modify referer.
    const targetUrl = 'http://music.163.com/weapi/song/lyric?csrf_token=';
    const csrf = '';
    const d = {
      id: trackId,
      lv: -1,
      tv: -1,
      csrf_token: csrf,
    };
    const data = encryptedRequest(d);
    return pfn((resolve, reject) => {
      hm({
        url: targetUrl,
        method: 'POST',
        data,
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
      }).then((response) => {
        const { data: res_data } = response;
        let lrc = '';
        if (res_data.lrc != null) {
          lrc = res_data.lrc.lyric;
        }
        return resolve({
          lyric: lrc,
        });
      });
    });
  }

  function neParseUrl(url) {
    let result;
    url = url.replace('music.163.com/#/my/m/music/playlist?', 'music.163.com/#/playlist?');
    if (url.search('//music.163.com/#/m/playlist') !== -1 || url.search('//music.163.com/#/playlist') !== -1
     || url.search('//music.163.com/playlist') !== -1) {
      result = {
        type: 'playlist',
        id: `neplaylist_${getParameterByName('id', url)}`,
      };
    }
    return result;
  }

  function getPlaylist(url, hm, pfn) {
    const listId = getParameterByName('list_id', url).split('_')[0];
    if (listId === 'neplaylist') {
      return neGetPlaylist(url, hm, pfn);
    }
    if (listId === 'nealbum') {
      return neAlbum(url, hm, pfn);
    }
    if (listId === 'neartist') {
      return neArtist(url, hm, pfn);
    }
    return null;
  }

  return {
    showPlaylist: neShowPlaylist,
    getPlaylist,
    parseUrl: neParseUrl,
    bootstrapTrack: neBootstrapTrack,
    search: neSearch,
    lyric: neLyric,
  };
}

export default NeteaseFactory();


================================================
FILE: src/provider/qq.js
================================================
/* eslint-disable no-unused-vars */
/* global atob */
import entities from 'entities';
import encoding from 'text-encoding';
import { getParameterByName } from '../utils';

function build_qq() {
  function htmlDecode(value) {
    return entities.decodeHTML(value);
  }

  function qq_show_playlist(url, hm, pfn) {
    const offset = Number(getParameterByName('offset', url)) || 0;
    const target_url = `${'https://c.y.qq.com/splcloud/fcgi-bin/fcg_get_diss_by_tag.fcg'
        + '?rnd=0.4781484879517406&g_tk=732560869&jsonpCallback=MusicJsonCallback'
        + '&loginUin=0&hostUin=0&format=jsonp&inCharset=utf8&outCharset=utf-8'
        + '&notice=0&platform=yqq&needNewCode=0'
        + '&categoryId=10000000&sortId=5&sin='}${offset}&ein=${49 + offset}`;

    return pfn((resolve, reject) => {
      hm({
        url: target_url,
        method: 'GET',
        transformResponse: false,
      }).then((response) => {
        let { data } = response;
        data = data.slice('MusicJsonCallback('.length, -')'.length);
        data = JSON.parse(data);

        const playlists = data.data.list.map(item => ({
          cover_img_url: item.imgurl,
          title: htmlDecode(item.dissname),
          id: `qqplaylist_${item.dissid}`,
          source_url: `http://y.qq.com/#type=taoge&id=${item.dissid}`,
        }));

        return resolve({
          result: playlists,
        });
      });
    });
  }

  function qq_get_image_url(qqimgid, img_type) {
    if (qqimgid == null) {
      return '';
    }
    let category = '';
    if (img_type === 'artist') {
      category = 'mid_singer_300';
    }
    if (img_type === 'album') {
      category = 'mid_album_300';
    }

    const s = [category, qqimgid[qqimgid.length - 2], qqimgid[qqimgid.length - 1], qqimgid].join('/');
    const url = `http://imgcache.qq.com/music/photo/${s}.jpg`;
    return url;
  }

  function qq_is_playable(song) {
    const switch_flag = song.switch.toString(2).split('');
    switch_flag.pop();
    switch_flag.reverse();
    // flag switch table meaning:
    // ["play_lq", "play_hq", "play_sq", "down_lq", "down_hq", "down_sq", "soso",
    //  "fav", "share", "bgm", "ring", "sing", "radio", "try", "give"]
    const play_flag = switch_flag[0];
    const try_flag = switch_flag[13];
    return ((play_flag === '1') || ((play_flag === '1') && (try_flag === '1')));
  }

  function qq_convert_song(song) {
    const d = {
      id: `qqtrack_${song.songmid}`,
      title: htmlDecode(song.songname),
      artist: htmlDecode(song.singer[0].name),
      artist_id: `qqartist_${song.singer[0].mid}`,
      album: htmlDecode(song.albumname),
      album_id: `qqalbum_${song.albummid}`,
      img_url: qq_get_image_url(song.albummid, 'album'),
      source: 'qq',
      source_url: `http://y.qq.com/#type=song&mid=${song.songmid}&tpl=yqq_song_detail`,
      url: `qqtrack_${song.songmid}`,
      disabled: !qq_is_playable(song),
    };
    return d;
  }

  function qq_get_playlist(url, hm, pfn) { // eslint-disable-line no-unused-vars
    const list_id = getParameterByName('list_id', url).split('_').pop();

    return pfn((resolve, reject) => {
      const target_url = `${'http://i.y.qq.com/qzone-music/fcg-bin/fcg_ucc_getcdinfo_'
            + 'byids_cp.fcg?type=1&json=1&utf8=1&onlysong=0&jsonpCallback='
            + 'jsonCallback&nosign=1&disstid='}${list_id}&g_tk=5381&loginUin=0&hostUin=0`
            + '&format=jsonp&inCharset=GB2312&outCharset=utf-8&notice=0'
            + '&platform=yqq&jsonpCallback=jsonCallback&needNewCode=0';
      hm({
        url: target_url,
        method: 'GET',
        transformResponse: false,
      })
        .then((response) => {
          let { data } = response;
          data = data.slice('jsonCallback('.length, -')'.length);
          data = JSON.parse(data);

          const info = {
            cover_img_url: data.cdlist[0].logo,
            title: data.cdlist[0].dissname,
            id: `qqplaylist_${list_id}`,
            source_url: `http://y.qq.com/#type=taoge&id=${list_id}`,
          };

          const tracks = data.cdlist[0].songlist.map(item => qq_convert_song(item));
          return resolve({
            tracks,
            info,
          });
        });
    });
  }

  function qq_album(url, hm, pfn) {
    const album_id = getParameterByName('list_id', url).split('_').pop();

    return pfn((resolve, reject) => {
      const target_url = `${'http://i.y.qq.com/v8/fcg-bin/fcg_v8_album_info_cp.fcg'
            + '?platform=h5page&albummid='}${album_id}&g_tk=938407465`
            + '&uin=0&format=jsonp&inCharset=utf-8&outCharset=utf-8'
            + '&notice=0&platform=h5&needNewCode=1&_=1459961045571'
            + '&jsonpCallback=asonglist1459961045566';
      hm({
        url: target_url,
        method: 'GET',
        transformResponse: false,
      })
        .then((response) => {
          let { data } = response;
          data = data.slice(' asonglist1459961045566('.length, -')'.length);
          data = JSON.parse(data);

          const info = {
            cover_img_url: qq_get_image_url(album_id, 'album'),
            title: data.data.name,
            id: `qqalbum_${album_id}`,
            source_url: `http://y.qq.com/#type=album&mid=${album_id}`,
          };

          const tracks = data.data.list.map(item => qq_convert_song(item));
          return resolve({
            tracks,
            info,
          });
        });
    });
  }

  function qq_artist(url, hm, pfn) {
    const artist_id = getParameterByName('list_id', url).split('_').pop();

    return pfn((resolve, reject) => {
      const target_url = `${'http://i.y.qq.com/v8/fcg-bin/fcg_v8_singer_track_cp.fcg'
            + '?platform=h5page&order=listen&begin=0&num=50&singermid='}${artist_id}`
            + '&g_tk=938407465&uin=0&format=jsonp&'
            + 'inCharset=utf-8&outCharset=utf-8&notice=0&platform='
            + 'h5&needNewCode=1&from=h5&_=1459960621777&'
            + 'jsonpCallback=ssonglist1459960621772';
      hm({
        url: target_url,
        method: 'GET',
        transformResponse: false,
      })
        .then((response) => {
          let { data } = response;
          data = data.slice(' ssonglist1459960621772('.length, -')'.length);
          data = JSON.parse(data);

          const info = {
            cover_img_url: qq_get_image_url(artist_id, 'artist'),
            title: data.data.singer_name,
            id: `qqartist_${artist_id}`,
            source_url: `http://y.qq.com/#type=singer&mid=${artist_id}`,
          };

          const tracks = data.data.list.map(item => qq_convert_song(item.musicData));
          return resolve({
            tracks,
            info,
          });
        });
    });
  }

  function qq_search(url, hm, pfn) { // eslint-disable-line no-unused-vars
    return pfn((resolve, reject) => {
      const keyword = getParameterByName('keywords', url);
      const curpage = getParameterByName('curpage', url);
      const target_url = `${'http://i.y.qq.com/s.music/fcgi-bin/search_for_qq_cp?'
            + 'g_tk=938407465&uin=0&format=jsonp&inCharset=utf-8'
            + '&outCharset=utf-8&notice=0&platform=h5&needNewCode=1'
            + '&w='}${keyword}&zhidaqu=1&catZhida=1`
            + `&t=0&flag=1&ie=utf-8&sem=1&aggr=0&perpage=20&n=20&p=${curpage
            }&remoteplace=txt.mqq.all&_=1459991037831&jsonpCallback=jsonp4`;
      hm({
        url: target_url,
        method: 'GET',
        transformResponse: false,
      })
        .then((response) => {
          let { data } = response;
          data = data.slice('jsonp4('.length, -')'.length);
          data = JSON.parse(data);
          const tracks = data.data.song.list.map(item => qq_convert_song(item));
          return resolve({
            result: tracks,
            total: data.data.song.totalnum,
          });
        });
    });
  }

  // eslint-disable-next-line no-unused-vars
  function qq_bootstrap_track(trackId, hm, pfn) {
    const songId = trackId.slice('qqtrack_'.length);
    const target_url = `${'https://u.y.qq.com/cgi-bin/musicu.fcg?loginUin=0&'
        + 'hostUin=0&format=json&inCharset=utf8&outCharset=utf-8&notice=0&'
        + 'platform=yqq.json&needNewCode=0&data=%7B%22req_0%22%3A%7B%22'
        + 'module%22%3A%22vkey.GetVkeyServer%22%2C%22method%22%3A%22'
        + 'CgiGetVkey%22%2C%22param%22%3A%7B%22guid%22%3A%2210000%22%2C%22songmid%22%3A%5B%22'}${
      songId}%22%5D%2C%22songtype%22%3A%5B0%5D%2C%22uin%22%3A%220%22%2C%22loginflag%22`
        + '%3A1%2C%22platform%22%3A%2220%22%7D%7D%2C%22comm%22%3A%7B%22uin%22%3A0%2C%22'
        + 'format%22%3A%22json%22%2C%22ct%22%3A20%2C%22cv%22%3A0%7D%7D';
    return pfn((resolve, reject) => {
      hm({
        url: target_url,
        method: 'GET',
        transformResponse: false,
      })
        .then((response) => {
          let { data } = response;
          data = JSON.parse(data);
          const url = data.req_0.data.sip[0] + data.req_0.data.midurlinfo[0].purl;
          resolve({ url });
        });
    });
  }

  function str2ab(str) {
    // string to array buffer.
    const buf = new ArrayBuffer(str.length);
    const bufView = new Uint8Array(buf);
    for (let i = 0, strLen = str.length; i < strLen; i += 1) {
      bufView[i] = str.charCodeAt(i);
    }
    return buf;
  }

  function qq_lyric(url, hm, pfn) { // eslint-disable-line no-unused-vars
    const track_id = getParameterByName('track_id', url).split('_').pop();
    // use chrome extension to modify referer.
    const target_url = `${'http://i.y.qq.com/lyric/fcgi-bin/fcg_query_lyric.fcg?'
        + 'songmid='}${track_id}&loginUin=0&hostUin=0&format=jsonp&inCharset=GB2312`
        + '&outCharset=utf-8&notice=0&platform=yqq&jsonpCallback=MusicJsonCallback&needNewCode=0';
    return pfn((resolve, reject) => {
      hm({
        url: target_url,
        method: 'GET',
        transformResponse: false,
      }).then((response) => {
        let { data } = response;
        data = data.slice('MusicJsonCallback('.length, -')'.length);
        data = JSON.parse(data);
        let lrc = '';
        if (data.lyric != null) {
          // const td = new TextDecoder('utf8');
          const td = new encoding.TextDecoder();
          lrc = td.decode(str2ab(atob(data.lyric)));
        }
        return resolve({
          lyric: lrc,
        });
      });
    });
  }

  function qq_parse_url(url) {
    let result;
    let match = /\/\/y.qq.com\/n\/yqq\/playlist\/([0-9]+)/.exec(url);
    if (match != null) {
      const playlist_id = match[1];
      result = {
        type: 'playlist',
        id: `qqplaylist_${playlist_id}`,
      };
    }
    match = /\/\/y.qq.com\/n\/yqq\/playsquare\/([0-9]+)/.exec(url);
    if (match != null) {
      const playlist_id = match[1];
      result = {
        type: 'playlist',
        id: `qqplaylist_${playlist_id}`,
      };
    }
    match = /\/\/y.qq.com\/n\/m\/detail\/taoge\/index.html\?id=([0-9]+)/.exec(url);
    if (match != null) {
      const playlist_id = match[1];
      result = {
        type: 'playlist',
        id: `qqplaylist_${playlist_id}`,
      };
    }
    return result;
  }

  function get_playlist(url, hm, pfn) {
    const list_id = getParameterByName('list_id', url).split('_')[0];
    switch (list_id) {
      case 'qqplaylist':
        return qq_get_playlist(url, hm, pfn);
      case 'qqalbum':
        return qq_album(url, hm, pfn);
      case 'qqartist':
        return qq_artist(url, hm, pfn);
      default:
        return null;
    }
  }

  return {
    showPlaylist: qq_show_playlist,
    getPlaylist: get_playlist,
    parseUrl: qq_parse_url,
    bootstrapTrack: qq_bootstrap_track,
    search: qq_search,
    lyric: qq_lyric,
  };
}

export default build_qq(); // eslint-disable-line no-unused-vars


================================================
FILE: src/provider/xiami.js
================================================
/* eslint-disable no-unused-vars */
/* global parseInt */
/* eslint-disable no-param-reassign */
import { MD5 } from '../crypto/crypto';
import { getParameterByName } from '../utils';

function build_xiami() {
  function caesar(location) {
    const num = location[0];
    const avg_len = Math.floor(location.slice(1).length / num);
    const remainder = location.slice(1).length % num;

    const result = [];
    for (let i = 0; i < remainder; i += 1) {
      const line = location.slice(i * (avg_len + 1) + 1, (i + 1) * (avg_len + 1) + 1);
      result.push(line);
    }

    for (let i = 0; i < num - remainder; i += 1) {
      const line = location.slice((avg_len + 1) * remainder)
        .slice(i * avg_len + 1, (i + 1) * avg_len + 1);
      result.push(line);
    }

    const s = [];
    for (let i = 0; i < avg_len; i += 1) {
      for (let j = 0; j < num; j += 1) {
        s.push(result[j][i]);
      }
    }

    for (let i = 0; i < remainder; i += 1) {
      s.push(result[i].slice(-1));
    }

    return unescape(s.join('')).replace(/\^/g, '0');
  }

  function handleProtocolRelativeUrl(url) {
    const regex = /^.*?\/\//;
    const result = url.replace(regex, 'http://');
    return result;
  }

  function xm_retina_url(s) {
    if (s.slice(-6, -4) === '_1') {
      return s.slice(0, -6) + s.slice(-4);
    }
    return s;
  }

  function xm_get_api_url(api, params, token) {
    const params_string = JSON.stringify(params);
    const origin = `${token.split('_')[0]}_xmMain_${api}_${params_string}`;
    const sign = MD5(origin);
    const baseUrl = 'https://www.xiami.com';
    return encodeURI(`${baseUrl + api}?_q=${params_string}&_s=${sign}`);
  }

  function xm_api_get(hm, api, params, cookieProvider, callback) {
    const domain = 'https://www.xiami.com';
    const name = 'xm_sg_tk';
    cookieProvider.getCookie(domain, name, (token) => {
      const url = xm_get_api_url(api, params, token);
      hm({ method: 'GET', url, cookieProvider }).then((response) => {
        if (response.data.code === 'SG_TOKEN_EMPTY' || response.data.code === 'SG_TOKEN_EXPIRED'
            || response.data.code === 'SG_INVALID') {
          // token expire, refetch token and start get url
          cookieProvider.getCookie(domain, name, (token2) => {
            const url2 = xm_get_api_url(api, params, token2);
            hm({ method: 'GET', url: url2, cookieProvider }).then((res) => {
              callback(res);
            });
          });
        } else {
          callback(response);
        }
      });
    });
  }

  function xm_get_low_quality_img_url(url) {
    return `${url}?x-oss-process=image/resize,m_fill,limit_0,s_330/quality,q_80`;
  }

  function xm_show_playlist(url, hm, pfn, cookieProvider) {
    const offset = getParameterByName('offset', url);
    const page = offset / 30 + 1;
    const pageSize = 60;

    return pfn((resolve, reject) => {
      const api = '/api/list/collect';
      const params = {
        pagingVO: {
          page,
          pageSize,
        },
        dataType: 'system',
      };
      xm_api_get(hm, api, params, cookieProvider, (response) => {
        const result = response.data.result.data.collects.map((d) => {
          const default_playlist = {
            cover_img_url: '',
            title: '',
            id: '',
            source_url: '',
          };
          default_playlist.cover_img_url = xm_get_low_quality_img_url(d.collectLogo);
          default_playlist.title = d.collectName;
          const list_id = d.listId;
          default_playlist.id = `xmplaylist_${list_id}`;
          default_playlist.source_url = `http://www.xiami.com/collect/${list_id}`;
          return default_playlist;
        });
        return resolve({
          result,
        });
      });
    });
  }

  // eslint-disable-next-line no-unused-vars
  function xm_bootstrap_track(trackId, hm, pfn) {
    const target_url = `http://www.xiami.com/song/playlist/id/${trackId.slice('xmtrack_'.length)
    }/object_name/default/object_id/0/cat/json`;
    return pfn((resolve, reject) => {
      hm({ method: 'GET', url: target_url }).then((response) => {
        const { data } = response;
        if (data.data.trackList == null) {
          return reject();
        }
        const { location } = data.data.trackList[0];
        // eslint-disable-next-line
        const url = handleProtocolRelativeUrl(caesar(location));
        const img_url = xm_retina_url(handleProtocolRelativeUrl(data.data.trackList[0].pic));
        const album = data.data.trackList[0].album_name;
        const album_id = `xmalbum_${data.data.trackList[0].album_id}`;
        const lyric_url = handleProtocolRelativeUrl(data.data.trackList[0].lyric_url);
        return resolve({
          url, img_url, album, album_id, lyric_url,
        });
      });
    });
  }

  function xm_convert_song(song_info, artist_field_name) {
    const track = {
      id: `xmtrack_${song_info.song_id}`,
      title: song_info.song_name,
      artist: song_info[artist_field_name],
      artist_id: `xmartist_${song_info.artist_id}`,
      album: song_info.album_name,
      album_id: `xmalbum_${song_info.album_id}`,
      source: 'xiami',
      source_url: `http://www.xiami.com/song/${song_info.song_id}`,
      img_url: song_info.album_logo,
      url: `xmtrack_${song_info.song_id}`,
      lyric_url: song_info.lyric_file,
    };
    return track;
  }

  function xm_convert_song2(song_info, artist_field_name) { // eslint-disable-line no-unused-vars
    const track = {
      id: `xmtrack_${song_info.songId}`,
      title: song_info.songName,
      artist: song_info.artistName,
      artist_id: `xmartist_${song_info.artistId}`,
      album: song_info.albumName,
      album_id: `xmalbum_${song_info.albumId}`,
      source: 'xiami',
      source_url: `http://www.xiami.com/song/${song_info.songId}`,
      img_url: song_info.albumLogo,
      url: `xmtrack_${song_info.songId}`,
      // 'lyric_url': song_info.lyricInfo.lyricFile
    };
    if (song_info.lyricInfo) {
      track.lyric_url = song_info.lyricInfo.lyricFile;
    }
    return track;
  }

  function xm_get_playlist(url, hm, pfn, cookieProvider) { // eslint-disable-line no-unused-vars
    const list_id = getParameterByName('list_id', url).split('_').pop();

    return pfn((resolve, reject) => {
      const api = '/api/collect/initialize';
      const params = {
        listId: parseInt(list_id, 10),
      };
      xm_api_get(hm, api, params, cookieProvider, (response) => {
        const collect = response.data.result.data.collectDetail;
        const info = {
          cover_img_url: xm_get_low_quality_img_url(collect.collectLogo),
          title: collect.collectName,
          id: `xmplaylist_${list_id}`,
          source_url: `http://www.xiami.com/collect/${list_id}`,
        };
        const tracks = response.data.result.data.collectSongs.map(item => xm_convert_song2(item, 'artist_name'));
        return resolve({
          tracks,
          info,
        });
      });
    });
  }

  function xm_search(url, hm, pfn, cookieProvider) { // eslint-disable-line no-unused-vars
    return pfn((resolve, reject) => {
      const api = '/api/search/searchSongs';
      const keyword = getParameterByName('keywords', url);
      const curpage = getParameterByName('curpage', url);
      const pageSize = 60;
      const params = {
        pagingVO: {
          page: curpage,
          pageSize,
        },
        key: keyword,
      };
      xm_api_get(hm, api, params, cookieProvider, (response) => {
        const tracks = response.data.result.data.songs.map(item => xm_convert_song2(item, 'artistName'));
        return resolve({
          result: tracks,
          total: response.data.result.data.pagingVO.pages,
        });
      });
    });
  }

  function xm_album(url, hm, pfn, cookieProvider) { // eslint-disable-line no-unused-vars
    return pfn((resolve, reject) => {
      const album_id = getParameterByName('list_id', url).split('_').pop();
      const target_url = `http://api.xiami.com/web?v=2.0&app_key=1&id=${album_id
      }&page=1&limit=20&callback=jsonp217&r=album/detail`;
      hm({
        url: target_url,
        method: 'GET',
        transformResponse: false,
      })
        .then((response) => {
          let { data } = response;
          data = data.slice('jsonp217('.length, -')'.length);
          data = JSON.parse(data);

          const info = {
            cover_img_url: data.data.album_logo,
            title: data.data.album_name,
            id: `xmalbum_${data.data.album_id}`,
            source_url: `http://www.xiami.com/album/${data.data.album_id}`,
          };

          const tracks = data.data.songs.map(item => xm_convert_song(item, 'singers'));
          return resolve({
            tracks,
            info,
          });
        });
    });
  }

  function xm_artist(url, hm, pfn, cookieProvider) { // eslint-disable-line no-unused-vars
    return pfn((resolve, reject) => {
      const artist_id = getParameterByName('list_id', url).split('_').pop();

      let target_url = `http://api.xiami.com/web?v=2.0&app_key=1&id=${artist_id
      }&page=1&limit=20&_ksTS=1459931285956_216`
            + '&callback=jsonp217&r=artist/detail';

      hm({
        url: target_url,
        method: 'GET',
        transformResponse: false,
      })
        .then((response) => {
          let { data } = response;
          data = data.slice('jsonp217('.length, -')'.length);
          data = JSON.parse(data);

          const info = {
            cover_img_url: xm_retina_url(data.data.logo),
            title: data.data.artist_name,
            id: `xmartist_${artist_id}`,
            source_url: `http://www.xiami.com/artist/${artist_id}`,
          };

          target_url = `http://api.xiami.com/web?v=2.0&app_key=1&id=${artist_id
          }&page=1&limit=20&callback=jsonp217&r=artist/hot-songs`;
          hm({
            url: target_url,
            method: 'GET',
            transformResponse: false,
          })
            .then((res) => {
              let { data: res_data } = res;
              res_data = res_data.slice('jsonp217('.length, -')'.length);
              res_data = JSON.parse(res_data);

              const tracks = res_data.data.map((item) => {
                const track = xm_convert_song(item, 'singers');
                track.artist_id = `xmartist_${artist_id}`;
                return track;
              });
              return resolve({
                tracks,
                info,
              });
            });
        });
    });
  }
Download .txt
gitextract_kcqnihu1/

├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.json
├── .gitignore
├── .nvmrc
├── LICENSE
├── README.md
├── docs/
│   ├── .nojekyll
│   ├── README.md
│   ├── _sidebar.md
│   ├── about.md
│   ├── api.md
│   ├── changelog.md
│   ├── developer.md
│   ├── index.html
│   ├── quickstart.md
│   └── thanks.md
├── package.json
├── src/
│   ├── crypto/
│   │   ├── aes.js
│   │   ├── big-integer.js
│   │   ├── crypto.js
│   │   └── md5.js
│   ├── hack_header.js
│   ├── index.js
│   ├── platform/
│   │   ├── browser.js
│   │   └── node.js
│   ├── provider/
│   │   ├── bilibili.js
│   │   ├── kugou.js
│   │   ├── kuwo.js
│   │   ├── netease.js
│   │   ├── qq.js
│   │   └── xiami.js
│   └── utils.js
├── test/
│   ├── index.spec.js
│   └── mocha.opts
└── webpack.config.js
Download .txt
SYMBOL INDEX (83 symbols across 16 files)

FILE: src/crypto/aes.js
  function convertToInt32 (line 172) | function convertToInt32(bytes) {

FILE: src/crypto/big-integer.js
  function Integer (line 3) | function Integer(v,radix,alphabet,caseSensitive){if(typeof v==="undefine...
  function BigInteger (line 3) | function BigInteger(value,sign){this.value=value;this.sign=sign;this.isS...
  function SmallInteger (line 3) | function SmallInteger(value){this.value=value;this.sign=value<0;this.isS...
  function NativeBigInt (line 3) | function NativeBigInt(value){this.value=value}
  function isPrecise (line 3) | function isPrecise(n){return-MAX_INT<n&&n<MAX_INT}
  function smallToArray (line 3) | function smallToArray(n){if(n<1e7)return[n];if(n<1e14)return[n%1e7,Math....
  function arrayToSmall (line 3) | function arrayToSmall(arr){trim(arr);var length=arr.length;if(length<4&&...
  function trim (line 3) | function trim(v){var i=v.length;while(v[--i]===0);v.length=i+1}
  function createArray (line 3) | function createArray(length){var x=new Array(length);var i=-1;while(++i<...
  function truncate (line 3) | function truncate(n){if(n>0)return Math.floor(n);return Math.ceil(n)}
  function add (line 3) | function add(a,b){var l_a=a.length,l_b=b.length,r=new Array(l_a),carry=0...
  function addAny (line 3) | function addAny(a,b){if(a.length>=b.length)return add(a,b);return add(b,a)}
  function addSmall (line 3) | function addSmall(a,carry){var l=a.length,r=new Array(l),base=BASE,sum,i...
  function subtract (line 3) | function subtract(a,b){var a_l=a.length,b_l=b.length,r=new Array(a_l),bo...
  function subtractAny (line 3) | function subtractAny(a,b,sign){var value;if(compareAbs(a,b)>=0){value=su...
  function subtractSmall (line 3) | function subtractSmall(a,b,sign){var l=a.length,r=new Array(l),carry=-b,...
  function multiplyLong (line 3) | function multiplyLong(a,b){var a_l=a.length,b_l=b.length,l=a_l+b_l,r=cre...
  function multiplySmall (line 3) | function multiplySmall(a,b){var l=a.length,r=new Array(l),base=BASE,carr...
  function shiftLeft (line 3) | function shiftLeft(x,n){var r=[];while(n-- >0)r.push(0);return r.concat(x)}
  function multiplyKaratsuba (line 3) | function multiplyKaratsuba(x,y){var n=Math.max(x.length,y.length);if(n<=...
  function useKaratsuba (line 3) | function useKaratsuba(l1,l2){return-.012*l1-.012*l2+15e-6*l1*l2>0}
  function multiplySmallAndArray (line 3) | function multiplySmallAndArray(a,b,sign){if(a<BASE){return new BigIntege...
  function square (line 3) | function square(a){var l=a.length,r=createArray(l+l),base=BASE,product,c...
  function divMod1 (line 3) | function divMod1(a,b){var a_l=a.length,b_l=b.length,base=BASE,result=cre...
  function divMod2 (line 3) | function divMod2(a,b){var a_l=a.length,b_l=b.length,result=[],part=[],ba...
  function divModSmall (line 3) | function divModSmall(value,lambda){var length=value.length,quotient=crea...
  function divModAny (line 3) | function divModAny(self,v){var value,n=parseValue(v);if(supportsNativeBi...
  function compareAbs (line 3) | function compareAbs(a,b){if(a.length!==b.length){return a.length>b.lengt...
  function isBasicPrime (line 3) | function isBasicPrime(v){var n=v.abs();if(n.isUnit())return false;if(n.e...
  function millerRabinTest (line 3) | function millerRabinTest(n,a){var nPrev=n.prev(),b=nPrev,r=0,d,t,i,x;whi...
  function shift_isSmall (line 3) | function shift_isSmall(n){return Math.abs(n)<=BASE}
  function bitwise (line 3) | function bitwise(x,y,fn){y=parseValue(y);var xSign=x.isNegative(),ySign=...
  function roughLOB (line 3) | function roughLOB(n){var v=n.value,x=typeof v==="number"?v|LOBMASK_I:typ...
  function integerLogarithm (line 3) | function integerLogarithm(value,base){if(base.compareTo(value)<=0){var t...
  function max (line 3) | function max(a,b){a=parseValue(a);b=parseValue(b);return a.greater(b)?a:b}
  function min (line 3) | function min(a,b){a=parseValue(a);b=parseValue(b);return a.lesser(b)?a:b}
  function gcd (line 3) | function gcd(a,b){a=parseValue(a).abs();b=parseValue(b).abs();if(a.equal...
  function lcm (line 3) | function lcm(a,b){a=parseValue(a).abs();b=parseValue(b).abs();return a.d...
  function randBetween (line 3) | function randBetween(a,b){a=parseValue(a);b=parseValue(b);var low=min(a,...
  function parseBaseFromArray (line 3) | function parseBaseFromArray(digits,base,isNegative){var val=Integer[0],p...
  function stringify (line 3) | function stringify(digit,alphabet){alphabet=alphabet||DEFAULT_ALPHABET;i...
  function toBase (line 3) | function toBase(n,base){base=bigInt(base);if(base.isZero()){if(n.isZero(...
  function toBaseString (line 3) | function toBaseString(n,base,alphabet){var arr=toBase(n,base);return(arr...
  function parseStringValue (line 3) | function parseStringValue(v){if(isPrecise(+v)){var x=+v;if(x===truncate(...
  function parseNumberValue (line 3) | function parseNumberValue(v){if(supportsNativeBigInt){return new NativeB...
  function parseValue (line 3) | function parseValue(v){if(typeof v==="number"){return parseNumberValue(v...

FILE: src/crypto/crypto.js
  function aesEncrypt (line 9) | function aesEncrypt(text, secKey, ivString) {
  function hexify (line 34) | function hexify(text) {
  function zfill (line 38) | function zfill(num, size) {
  function expmod (line 44) | function expmod(base, exp, mymod) {
  function rsaEncrypt (line 57) | function rsaEncrypt(text, pubKey, modulus) {

FILE: src/crypto/md5.js
  function MD5 (line 8) | function MD5(string) {

FILE: src/hack_header.js
  function hackHeader (line 1) | function hackHeader(url) {

FILE: src/index.js
  function getAllProviders (line 13) | function getAllProviders() {
  function getProviderByName (line 17) | function getProviderByName(sourceName) {
  function getProviderByItemId (line 39) | function getProviderByItemId(itemId) {
  function loadNodejsDefaults (line 66) | function loadNodejsDefaults() {
  function loadBrowserDefaults (line 72) | function loadBrowserDefaults() {
  function apiGet (line 88) | function apiGet(url, httpClient, pfn, cookieProvider) {

FILE: src/platform/browser.js
  function getCookieProvider (line 7) | function getCookieProvider() {

FILE: src/platform/node.js
  class CookieProvider (line 6) | class CookieProvider {
    method constructor (line 7) | constructor() {
    method getCookie (line 11) | getCookie(url, name, callback) {
    method setCookie (line 19) | setCookie(url, name, value) {
    method getCookieForHTTPHeader (line 27) | getCookieForHTTPHeader(url) {
    method getDomain (line 41) | getDomain(url) {
  function HTTPClient (line 48) | function HTTPClient(params) {

FILE: src/provider/bilibili.js
  function build_bilibili (line 5) | function build_bilibili() {

FILE: src/provider/kugou.js
  function build_kugou (line 6) | function build_kugou() {

FILE: src/provider/kuwo.js
  function build_kuwo (line 6) | function build_kuwo() {

FILE: src/provider/netease.js
  function encryptedRequest (line 9) | function encryptedRequest(text) {
  function NeteaseFactory (line 29) | function NeteaseFactory() {

FILE: src/provider/qq.js
  function build_qq (line 7) | function build_qq() {

FILE: src/provider/xiami.js
  function build_xiami (line 7) | function build_xiami() {

FILE: src/utils.js
  function getParameterByName (line 1) | function getParameterByName(name, url) {
  function getRandomHexString (line 12) | function getRandomHexString(size) {
  function httpParamEncode (line 22) | function httpParamEncode(obj) {

FILE: test/index.spec.js
  function testPlatformShowList (line 16) | function testPlatformShowList(platform) {
  function testPlatformPlaylist (line 33) | function testPlatformPlaylist(platform) {
  function testSearch (line 67) | function testSearch(platform) {
  function testLyric (line 87) | function testLyric(platform) {
  function testBootstrapTrack (line 112) | function testBootstrapTrack(platform) {
Condensed preview — 37 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (216K chars).
[
  {
    "path": ".babelrc",
    "chars": 170,
    "preview": "{\r\n  \"presets\": [\"env\"],\r\n  \"plugins\": [\"babel-plugin-add-module-exports\"],\r\n  \"env\": {\r\n    \"test\": {\r\n      \"plugins\":"
  },
  {
    "path": ".editorconfig",
    "chars": 200,
    "preview": "root = true\r\n\r\n[*]\r\nindent_style = space\r\nindent_size = 2\r\nend_of_line = LF\r\ncharset = utf-8\r\ntrim_trailing_whitespace ="
  },
  {
    "path": ".eslintignore",
    "chars": 5,
    "preview": "/dist"
  },
  {
    "path": ".eslintrc.json",
    "chars": 107,
    "preview": "{\r\n  \"extends\": \"airbnb-base\",\r\n  \"rules\": {\r\n      \"camelcase\":\"off\",\r\n      \"import/named\": \"off\"\r\n  }\r\n}"
  },
  {
    "path": ".gitignore",
    "chars": 650,
    "preview": "# Logs\r\nlogs\r\n*.log\r\n\r\n# Runtime data\r\npids\r\n*.pid\r\n*.seed\r\n\r\n# Directory for instrumented libs generated by jscoverage/"
  },
  {
    "path": ".nvmrc",
    "chars": 7,
    "preview": "v6.10\r\n"
  },
  {
    "path": "LICENSE",
    "chars": 1098,
    "preview": "The MIT License (MIT)\r\n\r\nCopyright (c) 2019 Listen 1\r\n\r\nPermission is hereby granted, free of charge, to any person obta"
  },
  {
    "path": "README.md",
    "chars": 1713,
    "preview": "# listen1-api\r\n\r\nlisten1音乐资源API库,可运行在 Nodejs 和 浏览器环境(限chrome extension 或 electron)\r\n\r\n## 特性\r\n\r\n* 集成网易,QQ,虾米,酷狗,酷我,Bilibi"
  },
  {
    "path": "docs/.nojekyll",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "docs/README.md",
    "chars": 1412,
    "preview": "# listen1-api\r\n\r\nlisten1音乐资源API库,可运行在 Nodejs 和 浏览器环境(限chrome extension 或 electron)\r\n\r\n## 特性\r\n\r\n* 集成网易,QQ,虾米,酷狗,酷我,Bilibi"
  },
  {
    "path": "docs/_sidebar.md",
    "chars": 140,
    "preview": "- 开始使用\n  - [快速开始](quickstart.md)\n\n- 参考\n  - [API](api.md)\n  - [开发指南](developer.md)\n\n- [关于](about.md)\n- [致谢](thanks.md)\n- "
  },
  {
    "path": "docs/about.md",
    "chars": 695,
    "preview": "# 关于\n\n当前版本: 1.0.0\n\n[listen-api](https://github.com/listen1/listen1-api) 是Listen1项目的一部分,主要提供其它项目访问音乐资源的API支持。\n\n\n\n## 联系方式\n"
  },
  {
    "path": "docs/api.md",
    "chars": 4077,
    "preview": "# API\n\n## apiGet\n> listen1Api.apiGet(url, httpFunction, promiseFunction, cookieProviderClass) ⇒ Promise resolve(json_obj"
  },
  {
    "path": "docs/changelog.md",
    "chars": 200,
    "preview": "# 更新日志\n本文件记录项目的所有更新。\n\n格式基于 [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\n版本号规则基于 [Semantic Versioning](https"
  },
  {
    "path": "docs/developer.md",
    "chars": 1261,
    "preview": "## 开发使用\n\n1. 编译生成\n  * 运行 `yarn install` (推荐) 或 `npm install` ,安装依赖包。\n  * 运行 `yarn build` 或 `npm run build` 来生成压缩版本的库文件。\n2"
  },
  {
    "path": "docs/index.html",
    "chars": 795,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\">\n  <title>listen1-api - Produce universal library with w"
  },
  {
    "path": "docs/quickstart.md",
    "chars": 681,
    "preview": "# 快速开始\n\n## 下载\n* 压缩版本 [下载](https://github.com/listen1/listen1-api/releases/download/v1.0.0/listen1-api.min.js)\n* 非压缩版本[下载"
  },
  {
    "path": "docs/thanks.md",
    "chars": 122,
    "preview": "## 致谢\n\n纪念 亚伦·斯沃茨 (Aaron Swartz) (1986-2013)\n\n感谢开源社区的开发者,为Listen 1贡献了精彩的代码。感谢所有支持本开源项目的朋友。\n\n特别感谢@Dumeng对Listen 1 ES6升级的贡献"
  },
  {
    "path": "package.json",
    "chars": 2065,
    "preview": "{\r\n  \"name\": \"listen1-api\",\r\n  \"version\": \"1.0.0\",\r\n  \"description\": \"One API for all free music in China\",\r\n  \"main\": \""
  },
  {
    "path": "src/crypto/aes.js",
    "chars": 60558,
    "preview": "\"use strict\";\n\n(function(root) {\n\n    var createBuffer = null, copyBuffer = null, convertBytesToString, convertStringToB"
  },
  {
    "path": "src/crypto/big-integer.js",
    "chars": 31753,
    "preview": "// modified version from https://github.com/peterolson/BigInteger.js\n// remove eval to avoid warning in chrome\nvar bigIn"
  },
  {
    "path": "src/crypto/crypto.js",
    "chars": 2062,
    "preview": "/* eslint-disable import/named */\n/* eslint-disable no-undef */\n/* eslint-disable no-param-reassign */\nimport aesjs from"
  },
  {
    "path": "src/crypto/md5.js",
    "chars": 6588,
    "preview": "/**\n*\n*  MD5 (Message-Digest Algorithm)\n*  http://www.webtoolkit.info/\n*  refer to: https://github.com/david-sabata/web-"
  },
  {
    "path": "src/hack_header.js",
    "chars": 1643,
    "preview": "function hackHeader(url) {\n  const replace_referer = true;\n  let replace_origin = true;\n  const add_referer = true;\n  le"
  },
  {
    "path": "src/index.js",
    "chars": 3904,
    "preview": "import {\n  getParameterByName,\n  httpParamEncode,\n} from './utils';\nimport { hackHeader } from './hack_header';\nimport N"
  },
  {
    "path": "src/platform/browser.js",
    "chars": 1487,
    "preview": "// function getDomain(url) {\n//   const matches = url.match(/^https?:\\/\\/([^/?#]+)(?:[/?#]|$)/i);\n//   const domain = ma"
  },
  {
    "path": "src/platform/node.js",
    "chars": 3460,
    "preview": "import { hackHeader } from '../hack_header';\n\n// eslint-disable-next-line import/no-dynamic-require\nconst request = requ"
  },
  {
    "path": "src/provider/bilibili.js",
    "chars": 7218,
    "preview": "/* eslint-disable no-unused-vars */\n/* Author: @71e6fd52 */\nimport { getParameterByName } from '../utils';\n\nfunction bui"
  },
  {
    "path": "src/provider/kugou.js",
    "chars": 13790,
    "preview": "/* eslint-disable no-unused-vars */\n/* Author: @WinterXMQ */\nimport async from 'async';\nimport { getParameterByName } fr"
  },
  {
    "path": "src/provider/kuwo.js",
    "chars": 12527,
    "preview": "/* eslint-disable no-unused-vars */\n/* Author: @WinterXMQ */\nimport async from 'async';\nimport { getParameterByName } fr"
  },
  {
    "path": "src/provider/netease.js",
    "chars": 13241,
    "preview": "/* eslint-disable import/named */\n/* eslint-disable no-unused-vars */\n/* eslint-disable no-param-reassign */\nimport { ge"
  },
  {
    "path": "src/provider/qq.js",
    "chars": 11762,
    "preview": "/* eslint-disable no-unused-vars */\n/* global atob */\nimport entities from 'entities';\nimport encoding from 'text-encodi"
  },
  {
    "path": "src/provider/xiami.js",
    "chars": 12071,
    "preview": "/* eslint-disable no-unused-vars */\n/* global parseInt */\n/* eslint-disable no-param-reassign */\nimport { MD5 } from '.."
  },
  {
    "path": "src/utils.js",
    "chars": 906,
    "preview": "function getParameterByName(name, url) {\n  // if (!url) url = window.location.href;\n  const replacedName = name.replace("
  },
  {
    "path": "test/index.spec.js",
    "chars": 5332,
    "preview": "/* global describe, it */\n\nimport chai from 'chai';\n\nconst listen1Api = require('../dist/listen1-api');\n\nchai.expect();\n"
  },
  {
    "path": "test/mocha.opts",
    "chars": 24,
    "preview": "-r jsdom-global/register"
  },
  {
    "path": "webpack.config.js",
    "chars": 1529,
    "preview": "/* global __dirname, require, module */\n\n// eslint-disable-next-line no-unused-vars\nconst webpack = require('webpack');\n"
  }
]

About this extraction

This page contains the full source code of the listen1/listen1-api GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 37 files (200.4 KB), approximately 77.0k tokens, and a symbol index with 83 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!