master a1b072b3d32f cached
4 files
432.0 KB
105.3k tokens
131 symbols
1 requests
Download .txt
Showing preview only (446K chars total). Download the full file or copy to clipboard to get everything.
Repository: Momo707577045/github-directory-downloader
Branch: master
Commit: a1b072b3d32f
Files: 4
Total size: 432.0 KB

Directory structure:
gitextract_ejp4vh9e/

├── .gitignore
├── README.md
├── index.html
└── jszip.js

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

================================================
FILE: .gitignore
================================================
.idea/
todo.md
intro.md


================================================
FILE: README.md
================================================
## [工具在线地址](http://blog.luckly-mjw.cn/tool-show/github-directory-downloader/index.html),推荐使用 chrome 浏览器。


## 使用方式

* 打开想要下载的 github 文件夹,如 facebook 的 react-dom 文件夹,则打开该页面。
![](./imgs/001.jpeg)
* 复制页面链接,粘贴到本工具输入框。
![](./imgs/002.jpeg)
* 点击下载。
![](./imgs/003.jpeg)

## 背景

* github 不支持部分文件下载,只能下载整个项目。
* github 国内网速较慢,只能下载整个项目需要等待很长时间。譬如 facebook 的 react 项目,有时候我们只想下载 [react-dom](https://github.com/facebook/react/tree/master/packages/react-dom) 来学习。但却要下载包含 react-js 等 35 各其他功能的整个项目。导致无谓的时间浪费。
* 网络上有相关解决方案,譬如修改 svn,修改 git 路径等,但均有一定使用成本和软件依赖。本项目意在将使用成本降到最低。

![](./imgs/005.jpg)

## 特点

* 无依赖,无需安装软件,不需要依赖 svn,甚至连 git 都不需要。有浏览器即可使用。
* 操作简单,复制 github 页面链接,点击下载即可。
* 支持特定分支和特定 tag 的文件下载,精确到单文件下载。

![](./imgs/006.jpg)

### 功能说明

![](./imgs/004.jpeg)

* 【解析下载】输入 github 链接,点击下载文件。
* 【重新下载错误文件】当部分文件下载失败时,点击该按钮,重新下载错误文件。
* 【强制下载现有文件】将已经下载好的文件强制整合下载。可以提前观看已经下载的文件。该操作不影响当前下载进程。
* 【文件条】对应每一个文件的下载情况。「灰色」:待下载,「绿色」:下载成功,「红色」:下载失败。点击红色文件栏可重新下载对应错误文件。

## 特别说明

* 项目使用到 github 提供的开放性 API,故仅支持 github 文件下载。不支持 gitlab,gitee 等平台。
* 目标仓库需为公开仓库,否则无权限下载。
![](./imgs/006.jpeg)

## 原理

* 文件下载使用的是 [github 开放 API](https://docs.github.com/cn/rest/reference/repos),如「罗列所有分支」「罗列所有tag」「下载文件」等
* 文件压缩,使用的是 [jszip 库](https://github.com/Stuk/jszip#readme)

## [项目源码](https://github.com/Momo707577045/github-directory-downloader)

* 项目只是简单的功能组合,都比较简单。
* 代码没有做优化,理解思路,看看就好。
![](./imgs/007.jpeg)

## 欢迎加入社区交流群

*群已满员,请添加 mjw707577045 加群*

![界面](./imgs/tools.jpeg) 

================================================
FILE: index.html
================================================
<!doctype html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <meta name="keywords" content="GitHub directory for web">
  <meta name="description" content="GitHub directory downloader for web, Momo's Blog, LuckyMomo">
  <title>GitHub directory downloader</title>
  <style>
    /*全局设置*/
    html,
    body {
      margin: 0;
      padding: 0;
    }

    body::-webkit-scrollbar {
      display: none
    }

    p {
      margin: 0;
    }

    [v-cloak] {
      display: none;
    }

    #m-app {
      height: 100%;
      width: 100%;
      text-align: center;
      padding: 10px 50px 80px;
      box-sizing: border-box;
    }

    .m-p-action {
      margin: 20px auto;
      max-width: 1100px;
      width: 100%;
      font-size: 35px;
      text-align: center;
      font-weight: bold;
    }

    .m-p-other,
    .m-p-github {
      position: fixed;
      right: 50px;
      bottom: 70px;
      background-color: #eff3f6;
      background-image: linear-gradient(-180deg, #fafbfc, #eff3f6 90%);
      color: #24292e;
      border: 1px solid rgba(27, 31, 35, .2);
      border-radius: 3px;
      cursor: pointer;
      display: inline-block;
      font-size: 14px;
      font-weight: 600;
      line-height: 20px;
      padding: 6px 12px;
      z-index: 99;
    }

    .m-p-help {
      position: fixed;
      right: 50px;
      top: 50px;
      width: 30px;
      height: 30px;
      color: #666666;
      z-index: 2;
      line-height: 30px;
      font-weight: bolder;
      border-radius: 50%;
      border: 1px solid rgba(27, 31, 35, .2);
      cursor: pointer;
      background-color: #eff3f6;
      background-image: linear-gradient(-180deg, #fafbfc, #eff3f6 90%);
    }

    .m-p-github:hover,
    .m-p-other:hover,
    .m-p-help {
      opacity: 0.9;
    }

    .m-p-other {
      bottom: 30px;
    }

    /*顶部信息录入*/
    .m-p-temp-url,
    .m-p-intro {
      padding-top: 50px;
      padding-bottom: 6px;
      width: 100%;
      color: #999999;
      text-align: left;
      font-style: italic;
      word-break: break-all;
    }

    .m-p-intro {
      padding-top: 0;
    }

    .m-p-input-container {
      display: flex;
    }

    .m-p-input-container input {
      flex: 1;
      margin-bottom: 30px;
      margin-right: 12px;
      display: block;
      width: 280px;
      padding: 16px;
      font-size: 24px;
      border-radius: 4px;
      box-shadow: none;
      color: #444444;
      border: 1px solid #cccccc;
    }

    .m-p-input-container div {
      position: relative;
      display: inline-block;
      height: 60px;
      line-height: 60px;
      font-size: 24px;
      color: white;
      cursor: pointer;
      border-radius: 4px;
      border: 1px solid #eeeeee;
      background-color: #3D8AC7;
      opacity: 1;
      transition: 0.3s all;
    }

    .m-p-input-container div:last-child {
      margin-left: 10px;
    }

    .m-p-input-container div:hover {
      opacity: 0.9;
    }

    .m-p-input-container div {
      width: 200px;
    }

    .m-p-input-container .disable {
      cursor: not-allowed;
      background-color: #dddddd;
    }

    /*下载状态*/
    .m-p-line {
      margin: 20px 0 50px;
      vertical-align: top;
      width: 100%;
      height: 5px;
      border-bottom: dotted;
    }

    .m-p-tips {
      width: 100%;
      color: #999999;
      text-align: left;
      font-style: italic;
      word-break: break-all;
    }

    .m-p-tips p {
      width: 100px;
      display: inline-block;
    }

    .m-p-segment {
      text-align: left;
    }

    .m-p-segment .item {
      position: relative;
      margin: 10px 6px;
      width: 100%;
      height: 40px;
      color: white;
      line-height: 40px;
      text-align: left;
      border-radius: 4px;
      box-sizing: border-box;
      border: solid 1px #eeeeee;
      background-color: #dddddd;
      overflow: hidden;
    }

    .m-p-segment .item .status,
    .m-p-segment .item .content {
      position: absolute;
      top: 0;
      left: 0;
      width: 0;
      height: 100%;
      transition: width 0.8s cubic-bezier(0.165, 0.84, 0.44, 1);
    }

    .m-p-segment .item .content {
      width: 100%;
      z-index: 2;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }

    .m-p-segment .item .content span {
      display: inline-block;
      width: 40px;
      height: 40px;
      text-align: center;
    }

    .m-p-segment .finish .status {
      width: 100%;
      background-color: #0ACD76;
    }

    .m-p-segment .error .status {
      cursor: pointer;
      width: 100%;
      background-color: #DC5350;
    }

    .m-p-cross,
    .m-p-final {
      display: inline-block;
      width: 100%;
      height: 50px;
      line-height: 50px;
      font-size: 20px;
      color: white;
      cursor: pointer;
      border-radius: 4px;
      border: 1px solid #eeeeee;
      background-color: #3D8AC7;
      opacity: 1;
      transition: 0.3s all;
    }

    .m-p-final {
      margin-top: 10px;
      text-decoration: none;
    }

    .m-p-force,
    .m-p-retry {
      position: absolute;
      right: 50px;
      display: inline-block;
      padding: 6px 12px;
      font-size: 18px;
      color: white;
      cursor: pointer;
      border-radius: 4px;
      border: 1px solid #eeeeee;
      background-color: #3D8AC7;
      opacity: 1;
      transition: 0.3s all;
    }

    .m-p-retry {
      right: 250px;
    }

    .m-p-force:hover,
    .m-p-retry:hover {
      opacity: 0.9;
    }

    .m-p-input-container input[disabled="disabled"] {
      background-color: #f2f2f2;
      cursor: not-allowed;
    }
  </style>
</head>

<body>
  <section id="m-app" v-cloak>
    <!--顶部操作提示-->
    <section class="m-p-action g-box">{{tips}}</section>
    <a class="m-p-help" target="_blank" href="https://segmentfault.com/a/1190000039143806">?</a>
    <a class="m-p-github" target="_blank" href="https://github.com/Momo707577045/github-directory-downloader">github</a>
    <a class="m-p-other" target="_blank" href="http://blog.luckly-mjw.cn/tool-show/index.html">其他实用工具</a>

    <!--文件载入-->
    <div class="m-p-temp-url">测试链接:https://github.com/Momo707577045/m3u8-downloader/tree/master/imgs</div>
    <div class="m-p-intro">支持特定分支,特定 tags 下的文件夹或具体文件,解决 GitHub 只能下载整个项目的问题。直接拷贝当前页面链接至本页面,点击下载</div>
    <section class="m-p-input-container">
      <input type="text" v-model="url" :disabled="downloading" :readonly="downloading" placeholder="请输入 GitHub 当前页面链接">
      <div class="" v-if="!downloading" @click="getFileList" :class="{'disable':downloading}">解析下载</div>
    </section>

    <template v-if="fileInfoList.length > 0">
      <div class="m-p-line"></div>
      <div class="m-p-retry" v-if="errorNum && downloadIndex >= fileInfoList.length" @click="retryAll">重新下载错误文件</div>
      <div class="m-p-force" v-if="finishNum" @click="forceDownload">强制下载现有文件</div>
      <div class="m-p-tips">碎片总量:{{ fileInfoList.length }},已下载:{{ finishNum }},错误:{{ errorNum }},进度:{{ (finishNum / fileInfoList.length * 100).toFixed(2) }}%</div>
      <div class="m-p-tips">若某视频碎片下载发生错误,将标记为红色,可点击相应图标进行重试</div>
      <section class="m-p-segment">
        <div class="item" v-for="(item, index) in fileInfoList" :class="[item.status]" @click="retry(index)" :title="item.path">
          <div class="status"></div>
          <div class="content"><span>{{ index + 1 }}</span> {{ item.absoluteUrl }}</div>
        </div>
      </section>
    </template>
    <div v-else class="m-p-tips" style="border: 1px solid #eeeeee; padding: 20px; border-radius: 10px;">
      <div style="display: flex; align-items: center; justify-content: center; margin-top: 20px; font-weight: bolder; font-size: 30px; color: #24292e;">
        <svg style="color: rgb(59, 130, 246); margin-right: 20px;" xmlns="http://www.w3.org/2000/svg" width="34" height="34" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-users text-blue-500">
          <path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"></path>
          <circle cx="9" cy="7" r="4"></circle>
          <path d="M22 21v-2a4 4 0 0 0-3-3.87"></path>
          <path d="M16 3.13a4 4 0 0 1 0 7.75"></path>
        </svg>加入我们的社区
      </div>
      <div style="color: #999999;font-size: 14px; font-weight: normal;text-align: center;">(群已满员,请添加 mjw707577045 加群)</div>
      <div style="display: flex; align-items: center; justify-content: center; margin-top: 20px;">
        <img src="https://upyun.luckly-mjw.cn/Assets/qrcode/tools.jpeg" alt="效率工具" title="logo" />
      </div>
    </div>
  </section>
</body>

<script>
  var _hmt = _hmt || [];
  (function () {
    var hm = document.createElement("script");
    hm.src = "https://hm.baidu.com/hm.js?1f12b0865d866ae1b93514870d93ce89";
    var s = document.getElementsByTagName("script")[0];
    s.parentNode.insertBefore(hm, s);
  })();
</script>
<!--vue 前端框架-->
<script src="https://upyun.luckly-mjw.cn/lib/vue.js"></script>
<!--js 生成压缩包-->
<script src="https://upyun.luckly-mjw.cn/lib/jszip.js"></script>

<script>
  // script注入
  new Vue({
    el: '#m-app',

    data() {
      return {
        url: '', // 在线链接
        // url: 'https://github.com/SetoSkins/Old_SetoThermal/blob/main/install.sh', // 文件在线链接
        // url: 'https://github.com/SetoSkins/Old_SetoThermal/blob/main/mods/01_8100/files/service.sh', // 文件在线链接
        // url: 'https://github.com/Momo707577045/m3u8-downloader/tree/master/imgs', // 文件夹在线链接
        tips: 'GitHub 「特定」文件夹或文件下载工具', // 顶部提示
        downloading: false, // 是否下载中
        isBlob: false, // 输入的链接是否是具体文件,而不是文件夹
        errorNum: 0, // 错误数
        finishNum: 0, // 已下载数
        downloadIndex: 0, // 当前下载文件
        fileInfoList: [], // 文件信息数组
        projectInfo: { // 项目信息
          ownerName: '',
          projectName: '',
          branchName: '',
          dirPath: '',
        }
      }
    },

    created() {
      this.getSource();
      window.addEventListener('keyup', this.onKeyup)
    },

    beforeDestroy() {
      window.removeEventListener('keyup', this.onKeyup)
    },

    methods: {
      // 获取链接中携带的资源链接
      getSource() {
        let { href } = location
        if (href.indexOf('?source=') > -1) {
          this.url = href.split('?source=')[1]
        }
      },

      // 退出弹窗
      onKeyup(event) {
        if (event.keyCode === 13) { // 键入ESC
          this.getFileList()
        }
      },

      // ajax 请求
      ajax(options) {
        options = options || {};
        let xhr = new XMLHttpRequest();
        if (options.type === 'file') {
          xhr.responseType = 'arraybuffer';
        }

        xhr.onreadystatechange = function () {
          if (xhr.readyState === 4) {
            let status = xhr.status;
            if (status >= 200 && status < 300) {
              options.success && options.success(xhr.response);
            } else {
              options.fail && options.fail(status);
            }
          }
        };

        xhr.open("GET", options.url, true);
        xhr.send(null);
      },

      // 获取分支及标签
      checkBranchAndTags(branchAndPath, callback) {
        if (!branchAndPath) {
          callback()
          return
        }
        if (branchAndPath.indexOf('blob/') === 0) {
          this.isBlob = true
          branchAndPath = branchAndPath.replace('blob/', '') // 去除无用参数
        } else {
          branchAndPath = branchAndPath.replace('tree/', '') // 去除无用参数
        }
        let branchList = []
        this.ajax({
          url: `https://api.github.com/repos/${this.projectInfo.ownerName}/${this.projectInfo.projectName}/branches`,
          success: (treeStr) => {
            branchList = [...branchList, ...JSON.parse(treeStr)]
            this.ajax({
              url: `https://api.github.com/repos/${this.projectInfo.ownerName}/${this.projectInfo.projectName}/tags`,
              success: (treeStr) => {
                [...branchList, ...JSON.parse(treeStr), { name: 'master' }].map(item => item.name).some(branch => {
                  if (branchAndPath.indexOf(branch) === 0) { // 寻找匹配的分支与 tag
                    this.projectInfo.branchName = branch
                    this.projectInfo.dirPath = branchAndPath.replace(branch, '').replace('/', '') + (this.isBlob ? '' : '/') // 去除首个 '/'
                    return true
                  }
                })
                console.log(this.projectInfo)
                callback && callback(branchList)
              },
              fail: _ => this.alertError('链接不正确,请查看链接是否有效')
            })
          },
          fail: _ => this.alertError('链接不正确,请查看链接是否有效')
        })
      },

      // 获取 git tree
      getFileList() {
        if (!this.url) {
          alert('请输入链接')
          return
        } else if (this.downloading) {
          alert('资源下载中,请稍后')
          return
        }

        this.downloading = true
        this.tips = 'git tree 文件下载中,请稍后'
        this.url = decodeURIComponent(this.url)
        const pathParams = this.url.replace('https://github.com/', '').trim().split('/')
        this.projectInfo.ownerName = pathParams[0]
        this.projectInfo.projectName = pathParams[1]
        this.projectInfo.branchName = pathParams[3]
        this.checkBranchAndTags(pathParams.slice(2).join('/'), () => {
          if (this.isBlob) { // 下载特定文件
            this.fileInfoList = [{
              status: '', // 待下载状态
              path: this.projectInfo.dirPath,
              absoluteUrl: `https://raw.githubusercontent.com/${this.projectInfo.ownerName}/${this.projectInfo.projectName}/${this.projectInfo.branchName}/${this.projectInfo.dirPath}`,
            }]
            this.downloadFile()
          } else { // 下载特定文件夹
            this.ajax({
              url: `https://api.github.com/repos/${this.projectInfo.ownerName}/${this.projectInfo.projectName}/git/trees/${this.projectInfo.branchName}?recursive=1`,
              success: (treeStr) => {
                JSON.parse(treeStr).tree.forEach(item => {
                  if (item.path.indexOf(this.projectInfo.dirPath) === 0 && item.type !== 'tree') { //  // 过滤特定数组 不是树节点,只收集文件
                    item.absoluteUrl = `https://raw.githubusercontent.com/${this.projectInfo.ownerName}/${this.projectInfo.projectName}/${this.projectInfo.branchName}/${item.path}`
                    item.status = '' // 待下载状态
                    this.fileInfoList.push(item) // 收集
                  }
                })
                this.downloadFile()
              },
              fail: () => {
                this.alertError('链接不正确,请查看链接是否有效')
              }
            })
          }
        })
      },

      // 下载分片
      downloadFile() {
        this.tips = 'git 项目文件下载中,请稍后'
        let download = () => {
          let index = this.downloadIndex
          this.downloadIndex++
          if (this.fileInfoList[index] && !this.fileInfoList[index].status) {
            this.ajax({
              url: this.fileInfoList[index].absoluteUrl,
              type: 'file',
              success: (file) => {
                this.dealFile(file, index, () => {
                  this.downloadIndex < this.fileInfoList.length && download()
                })
              },
              fail: () => {
                this.errorNum++
                this.fileInfoList[index].status = 'error'
                if (this.downloadIndex < this.fileInfoList.length) {
                  download()
                }
              }
            })
          } else if (this.downloadIndex < this.fileInfoList.length) { // 跳过已经成功的文件
            download()
          }
        }

        // 建立多少个 ajax 线程
        for (let i = 0; i < 10; i++) {
          download(i)
        }
      },

      // 重新下载某个文件
      retry(index) {
        if (this.fileInfoList[index].status === 'error') {
          this.fileInfoList[index].status = ''
          this.ajax({
            url: this.fileInfoList[index].absoluteUrl,
            type: 'file',
            success: (file) => {
              this.errorNum--
              this.dealFile(file, index)
            },
            fail: () => {
              this.fileInfoList[index].status = 'error'
            }
          })
        }
      },

      // 处理文件
      dealFile(file, index, callback) {
        this.fileInfoList[index].fileData = file
        this.fileInfoList[index].status = 'finish'
        this.finishNum++
        this.finishNum === this.fileInfoList.length && this.downloadZip()
        callback && callback()
      },

      // 重新下载所有错误文件
      retryAll() {
        this.errorNum = 0
        this.downloadIndex = 0
        this.fileInfoList.forEach((item) => { // 重置所有文件状态
          if (item.status === 'error') {
            item.status = ''
          }
        })
        this.downloadFile()
      },

      // 强制下载现有文件
      forceDownload() {
        if (this.fileInfoList.length) {
          this.downloadZip()
        } else {
          alert('当前无已下载文件')
        }
      },

      // 下载整合后的 git 文件
      downloadZip() {
        this.tips = 'git 文件整合中,请留意浏览器下载'
        const zip = new JSZip()
        this.fileInfoList.forEach(item => item.status === 'finish' && zip.file(item.path, item.fileData))
        zip.generateAsync({ type: 'blob' }).then((content) => {
          let a = document.createElement('a')
          a.download = `${[this.projectInfo.projectName, this.projectInfo.branchName, this.projectInfo.dirPath].join('/').replace(/\//g, '_')}.zip`
          a.href = URL.createObjectURL(content)
          a.style.display = 'none'
          document.body.appendChild(a)
          a.click()
          a.remove()
        })
      },

      // 发生错误,进行提示
      alertError(tips) {
        alert(tips)
        this.downloading = false
        this.tips = 'GitHub 特定文件夹或文件下载工具';
      },

      // 拷贝剪切板
      copyToClipboard(content) {
        if (!document.queryCommandSupported('copy')) {
          return false
        }

        let $input = document.createElement('textarea')
        $input.style.opacity = '0'
        $input.value = content
        document.body.appendChild($input)
        $input.select()

        const result = document.execCommand('copy')
        document.body.removeChild($input)
        $input = null

        return result
      },

    }
  })
  // script注入
</script>

</html>

================================================
FILE: jszip.js
================================================
/*!

JSZip v3.3.0 - A JavaScript class for generating and reading zip files
<http://stuartk.com/jszip>

(c) 2009-2016 Stuart Knightley <stuart [at] stuartk.com>
Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/master/LICENSE.markdown.

JSZip uses the library pako released under the MIT license :
https://github.com/nodeca/pako/blob/master/LICENSE
*/

(function (f) {
  if (typeof exports === "object" && typeof module !== "undefined") {
    module.exports = f()
  } else if (typeof define === "function" && define.amd) {
    define([], f)
  } else {
    var g;
    if (typeof window !== "undefined") {
      g = window
    } else if (typeof global !== "undefined") {
      g = global
    } else if (typeof self !== "undefined") {
      g = self
    } else {
      g = this
    }
    g.JSZip = f()
  }
})(function () {
  var define, module, exports;
  return (function e(t, n, r) {
    function s(o, u) {
      if (!n[o]) {
        if (!t[o]) {
          var a = typeof require == "function" && require;
          if (!u && a) {
            return a(o, !0);
          }
          if (i) {
            return i(o, !0);
          }
          var f = new Error("Cannot find module '" + o + "'");
          throw f.code = "MODULE_NOT_FOUND", f
        }
        var l = n[o] = { exports: {} };
        t[o][0].call(l.exports, function (e) {
          var n = t[o][1][e];
          return s(n ? n : e)
        }, l, l.exports, e, t, n, r)
      }
      return n[o].exports
    }

    var i = typeof require == "function" && require;
    for (var o = 0; o < r.length; o++) s(r[o]);
    return s
  })({
    1: [function (require, module, exports) {
      'use strict';
      var utils = require('./utils');
      var support = require('./support');
      // private property
      var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";


      // public method for encoding
      exports.encode = function (input) {
        var output = [];
        var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
        var i = 0, len = input.length, remainingBytes = len;

        var isArray = utils.getTypeOf(input) !== "string";
        while (i < input.length) {
          remainingBytes = len - i;

          if (!isArray) {
            chr1 = input.charCodeAt(i++);
            chr2 = i < len ? input.charCodeAt(i++) : 0;
            chr3 = i < len ? input.charCodeAt(i++) : 0;
          } else {
            chr1 = input[i++];
            chr2 = i < len ? input[i++] : 0;
            chr3 = i < len ? input[i++] : 0;
          }

          enc1 = chr1 >> 2;
          enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
          enc3 = remainingBytes > 1 ? (((chr2 & 15) << 2) | (chr3 >> 6)) : 64;
          enc4 = remainingBytes > 2 ? (chr3 & 63) : 64;

          output.push(_keyStr.charAt(enc1) + _keyStr.charAt(enc2) + _keyStr.charAt(enc3) + _keyStr.charAt(enc4));

        }

        return output.join("");
      };

      // public method for decoding
      exports.decode = function (input) {
        var chr1, chr2, chr3;
        var enc1, enc2, enc3, enc4;
        var i = 0, resultIndex = 0;

        var dataUrlPrefix = "data:";

        if (input.substr(0, dataUrlPrefix.length) === dataUrlPrefix) {
          // This is a common error: people give a data url
          // (data:image/png;base64,iVBOR...) with a {base64: true} and
          // wonders why things don't work.
          // We can detect that the string input looks like a data url but we
          // *can't* be sure it is one: removing everything up to the comma would
          // be too dangerous.
          throw new Error("Invalid base64 input, it looks like a data url.");
        }

        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

        var totalLength = input.length * 3 / 4;
        if (input.charAt(input.length - 1) === _keyStr.charAt(64)) {
          totalLength--;
        }
        if (input.charAt(input.length - 2) === _keyStr.charAt(64)) {
          totalLength--;
        }
        if (totalLength % 1 !== 0) {
          // totalLength is not an integer, the length does not match a valid
          // base64 content. That can happen if:
          // - the input is not a base64 content
          // - the input is *almost* a base64 content, with a extra chars at the
          //   beginning or at the end
          // - the input uses a base64 variant (base64url for example)
          throw new Error("Invalid base64 input, bad content length.");
        }
        var output;
        if (support.uint8array) {
          output = new Uint8Array(totalLength | 0);
        } else {
          output = new Array(totalLength | 0);
        }

        while (i < input.length) {

          enc1 = _keyStr.indexOf(input.charAt(i++));
          enc2 = _keyStr.indexOf(input.charAt(i++));
          enc3 = _keyStr.indexOf(input.charAt(i++));
          enc4 = _keyStr.indexOf(input.charAt(i++));

          chr1 = (enc1 << 2) | (enc2 >> 4);
          chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
          chr3 = ((enc3 & 3) << 6) | enc4;

          output[resultIndex++] = chr1;

          if (enc3 !== 64) {
            output[resultIndex++] = chr2;
          }
          if (enc4 !== 64) {
            output[resultIndex++] = chr3;
          }

        }

        return output;
      };

    }, { "./support": 30, "./utils": 32 }], 2: [function (require, module, exports) {
      'use strict';

      var external = require("./external");
      var DataWorker = require('./stream/DataWorker');
      var DataLengthProbe = require('./stream/DataLengthProbe');
      var Crc32Probe = require('./stream/Crc32Probe');
      var DataLengthProbe = require('./stream/DataLengthProbe');

      /**
       * Represent a compressed object, with everything needed to decompress it.
       * @constructor
       * @param {number} compressedSize the size of the data compressed.
       * @param {number} uncompressedSize the size of the data after decompression.
       * @param {number} crc32 the crc32 of the decompressed file.
       * @param {object} compression the type of compression, see lib/compressions.js.
       * @param {String|ArrayBuffer|Uint8Array|Buffer} data the compressed data.
       */
      function CompressedObject(compressedSize, uncompressedSize, crc32, compression, data) {
        this.compressedSize = compressedSize;
        this.uncompressedSize = uncompressedSize;
        this.crc32 = crc32;
        this.compression = compression;
        this.compressedContent = data;
      }

      CompressedObject.prototype = {
        /**
         * Create a worker to get the uncompressed content.
         * @return {GenericWorker} the worker.
         */
        getContentWorker: function () {
          var worker = new DataWorker(external.Promise.resolve(this.compressedContent))
            .pipe(this.compression.uncompressWorker())
            .pipe(new DataLengthProbe("data_length"));

          var that = this;
          worker.on("end", function () {
            if (this.streamInfo['data_length'] !== that.uncompressedSize) {
              throw new Error("Bug : uncompressed data size mismatch");
            }
          });
          return worker;
        },
        /**
         * Create a worker to get the compressed content.
         * @return {GenericWorker} the worker.
         */
        getCompressedWorker: function () {
          return new DataWorker(external.Promise.resolve(this.compressedContent))
            .withStreamInfo("compressedSize", this.compressedSize)
            .withStreamInfo("uncompressedSize", this.uncompressedSize)
            .withStreamInfo("crc32", this.crc32)
            .withStreamInfo("compression", this.compression)
            ;
        }
      };

      /**
       * Chain the given worker with other workers to compress the content with the
       * given compression.
       * @param {GenericWorker} uncompressedWorker the worker to pipe.
       * @param {Object} compression the compression object.
       * @param {Object} compressionOptions the options to use when compressing.
       * @return {GenericWorker} the new worker compressing the content.
       */
      CompressedObject.createWorkerFrom = function (uncompressedWorker, compression, compressionOptions) {
        return uncompressedWorker
          .pipe(new Crc32Probe())
          .pipe(new DataLengthProbe("uncompressedSize"))
          .pipe(compression.compressWorker(compressionOptions))
          .pipe(new DataLengthProbe("compressedSize"))
          .withStreamInfo("compression", compression);
      };

      module.exports = CompressedObject;

    }, { "./external": 6, "./stream/Crc32Probe": 25, "./stream/DataLengthProbe": 26, "./stream/DataWorker": 27 }], 3: [function (require, module, exports) {
      'use strict';

      var GenericWorker = require("./stream/GenericWorker");

      exports.STORE = {
        magic: "\x00\x00",
        compressWorker: function (compressionOptions) {
          return new GenericWorker("STORE compression");
        },
        uncompressWorker: function () {
          return new GenericWorker("STORE decompression");
        }
      };
      exports.DEFLATE = require('./flate');

    }, { "./flate": 7, "./stream/GenericWorker": 28 }], 4: [function (require, module, exports) {
      'use strict';

      var utils = require('./utils');

      /**
       * The following functions come from pako, from pako/lib/zlib/crc32.js
       * released under the MIT license, see pako https://github.com/nodeca/pako/
       */

      // Use ordinary array, since untyped makes no boost here
      function makeTable() {
        var c, table = [];

        for (var n = 0; n < 256; n++) {
          c = n;
          for (var k = 0; k < 8; k++) {
            c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
          }
          table[n] = c;
        }

        return table;
      }

      // Create table on load. Just 255 signed longs. Not a problem.
      var crcTable = makeTable();


      function crc32(crc, buf, len, pos) {
        var t = crcTable, end = pos + len;

        crc = crc ^ (-1);

        for (var i = pos; i < end; i++) {
          crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF];
        }

        return (crc ^ (-1)); // >>> 0;
      }

      // That's all for the pako functions.

      /**
       * Compute the crc32 of a string.
       * This is almost the same as the function crc32, but for strings. Using the
       * same function for the two use cases leads to horrible performances.
       * @param {Number} crc the starting value of the crc.
       * @param {String} str the string to use.
       * @param {Number} len the length of the string.
       * @param {Number} pos the starting position for the crc32 computation.
       * @return {Number} the computed crc32.
       */
      function crc32str(crc, str, len, pos) {
        var t = crcTable, end = pos + len;

        crc = crc ^ (-1);

        for (var i = pos; i < end; i++) {
          crc = (crc >>> 8) ^ t[(crc ^ str.charCodeAt(i)) & 0xFF];
        }

        return (crc ^ (-1)); // >>> 0;
      }

      module.exports = function crc32wrapper(input, crc) {
        if (typeof input === "undefined" || !input.length) {
          return 0;
        }

        var isArray = utils.getTypeOf(input) !== "string";

        if (isArray) {
          return crc32(crc | 0, input, input.length, 0);
        } else {
          return crc32str(crc | 0, input, input.length, 0);
        }
      };

    }, { "./utils": 32 }], 5: [function (require, module, exports) {
      'use strict';
      exports.base64 = false;
      exports.binary = false;
      exports.dir = false;
      exports.createFolders = true;
      exports.date = null;
      exports.compression = null;
      exports.compressionOptions = null;
      exports.comment = null;
      exports.unixPermissions = null;
      exports.dosPermissions = null;

    }, {}], 6: [function (require, module, exports) {
      /* global Promise */
      'use strict';

      // load the global object first:
      // - it should be better integrated in the system (unhandledRejection in node)
      // - the environment may have a custom Promise implementation (see zone.js)
      var ES6Promise = null;
      if (typeof Promise !== "undefined") {
        ES6Promise = Promise;
      } else {
        ES6Promise = require("lie");
      }

      /**
       * Let the user use/change some implementations.
       */
      module.exports = {
        Promise: ES6Promise
      };

    }, { "lie": 37 }], 7: [function (require, module, exports) {
      'use strict';
      var USE_TYPEDARRAY = (typeof Uint8Array !== 'undefined') && (typeof Uint16Array !== 'undefined') && (typeof Uint32Array !== 'undefined');

      var pako = require("pako");
      var utils = require("./utils");
      var GenericWorker = require("./stream/GenericWorker");

      var ARRAY_TYPE = USE_TYPEDARRAY ? "uint8array" : "array";

      exports.magic = "\x08\x00";

      /**
       * Create a worker that uses pako to inflate/deflate.
       * @constructor
       * @param {String} action the name of the pako function to call : either "Deflate" or "Inflate".
       * @param {Object} options the options to use when (de)compressing.
       */
      function FlateWorker(action, options) {
        GenericWorker.call(this, "FlateWorker/" + action);

        this._pako = null;
        this._pakoAction = action;
        this._pakoOptions = options;
        // the `meta` object from the last chunk received
        // this allow this worker to pass around metadata
        this.meta = {};
      }

      utils.inherits(FlateWorker, GenericWorker);

      /**
       * @see GenericWorker.processChunk
       */
      FlateWorker.prototype.processChunk = function (chunk) {
        this.meta = chunk.meta;
        if (this._pako === null) {
          this._createPako();
        }
        this._pako.push(utils.transformTo(ARRAY_TYPE, chunk.data), false);
      };

      /**
       * @see GenericWorker.flush
       */
      FlateWorker.prototype.flush = function () {
        GenericWorker.prototype.flush.call(this);
        if (this._pako === null) {
          this._createPako();
        }
        this._pako.push([], true);
      };
      /**
       * @see GenericWorker.cleanUp
       */
      FlateWorker.prototype.cleanUp = function () {
        GenericWorker.prototype.cleanUp.call(this);
        this._pako = null;
      };

      /**
       * Create the _pako object.
       * TODO: lazy-loading this object isn't the best solution but it's the
       * quickest. The best solution is to lazy-load the worker list. See also the
       * issue #446.
       */
      FlateWorker.prototype._createPako = function () {
        this._pako = new pako[this._pakoAction]({
          raw: true,
          level: this._pakoOptions.level || -1 // default compression
        });
        var self = this;
        this._pako.onData = function (data) {
          self.push({
            data: data,
            meta: self.meta
          });
        };
      };

      exports.compressWorker = function (compressionOptions) {
        return new FlateWorker("Deflate", compressionOptions);
      };
      exports.uncompressWorker = function () {
        return new FlateWorker("Inflate", {});
      };

    }, { "./stream/GenericWorker": 28, "./utils": 32, "pako": 38 }], 8: [function (require, module, exports) {
      'use strict';

      var utils = require('../utils');
      var GenericWorker = require('../stream/GenericWorker');
      var utf8 = require('../utf8');
      var crc32 = require('../crc32');
      var signature = require('../signature');

      /**
       * Transform an integer into a string in hexadecimal.
       * @private
       * @param {number} dec the number to convert.
       * @param {number} bytes the number of bytes to generate.
       * @returns {string} the result.
       */
      var decToHex = function (dec, bytes) {
        var hex = "", i;
        for (i = 0; i < bytes; i++) {
          hex += String.fromCharCode(dec & 0xff);
          dec = dec >>> 8;
        }
        return hex;
      };

      /**
       * Generate the UNIX part of the external file attributes.
       * @param {Object} unixPermissions the unix permissions or null.
       * @param {Boolean} isDir true if the entry is a directory, false otherwise.
       * @return {Number} a 32 bit integer.
       *
       * adapted from http://unix.stackexchange.com/questions/14705/the-zip-formats-external-file-attribute :
       *
       * TTTTsstrwxrwxrwx0000000000ADVSHR
       * ^^^^____________________________ file type, see zipinfo.c (UNX_*)
       *     ^^^_________________________ setuid, setgid, sticky
       *        ^^^^^^^^^________________ permissions
       *                 ^^^^^^^^^^______ not used ?
       *                           ^^^^^^ DOS attribute bits : Archive, Directory, Volume label, System file, Hidden, Read only
       */
      var generateUnixExternalFileAttr = function (unixPermissions, isDir) {

        var result = unixPermissions;
        if (!unixPermissions) {
          // I can't use octal values in strict mode, hence the hexa.
          //  040775 => 0x41fd
          // 0100664 => 0x81b4
          result = isDir ? 0x41fd : 0x81b4;
        }
        return (result & 0xFFFF) << 16;
      };

      /**
       * Generate the DOS part of the external file attributes.
       * @param {Object} dosPermissions the dos permissions or null.
       * @param {Boolean} isDir true if the entry is a directory, false otherwise.
       * @return {Number} a 32 bit integer.
       *
       * Bit 0     Read-Only
       * Bit 1     Hidden
       * Bit 2     System
       * Bit 3     Volume Label
       * Bit 4     Directory
       * Bit 5     Archive
       */
      var generateDosExternalFileAttr = function (dosPermissions, isDir) {

        // the dir flag is already set for compatibility
        return (dosPermissions || 0) & 0x3F;
      };

      /**
       * Generate the various parts used in the construction of the final zip file.
       * @param {Object} streamInfo the hash with information about the compressed file.
       * @param {Boolean} streamedContent is the content streamed ?
       * @param {Boolean} streamingEnded is the stream finished ?
       * @param {number} offset the current offset from the start of the zip file.
       * @param {String} platform let's pretend we are this platform (change platform dependents fields)
       * @param {Function} encodeFileName the function to encode the file name / comment.
       * @return {Object} the zip parts.
       */
      var generateZipParts = function (streamInfo, streamedContent, streamingEnded, offset, platform, encodeFileName) {
        var file = streamInfo['file'],
          compression = streamInfo['compression'],
          useCustomEncoding = encodeFileName !== utf8.utf8encode,
          encodedFileName = utils.transformTo("string", encodeFileName(file.name)),
          utfEncodedFileName = utils.transformTo("string", utf8.utf8encode(file.name)),
          comment = file.comment,
          encodedComment = utils.transformTo("string", encodeFileName(comment)),
          utfEncodedComment = utils.transformTo("string", utf8.utf8encode(comment)),
          useUTF8ForFileName = utfEncodedFileName.length !== file.name.length,
          useUTF8ForComment = utfEncodedComment.length !== comment.length,
          dosTime,
          dosDate,
          extraFields = "",
          unicodePathExtraField = "",
          unicodeCommentExtraField = "",
          dir = file.dir,
          date = file.date;


        var dataInfo = {
          crc32: 0,
          compressedSize: 0,
          uncompressedSize: 0
        };

        // if the content is streamed, the sizes/crc32 are only available AFTER
        // the end of the stream.
        if (!streamedContent || streamingEnded) {
          dataInfo.crc32 = streamInfo['crc32'];
          dataInfo.compressedSize = streamInfo['compressedSize'];
          dataInfo.uncompressedSize = streamInfo['uncompressedSize'];
        }

        var bitflag = 0;
        if (streamedContent) {
          // Bit 3: the sizes/crc32 are set to zero in the local header.
          // The correct values are put in the data descriptor immediately
          // following the compressed data.
          bitflag |= 0x0008;
        }
        if (!useCustomEncoding && (useUTF8ForFileName || useUTF8ForComment)) {
          // Bit 11: Language encoding flag (EFS).
          bitflag |= 0x0800;
        }


        var extFileAttr = 0;
        var versionMadeBy = 0;
        if (dir) {
          // dos or unix, we set the dos dir flag
          extFileAttr |= 0x00010;
        }
        if (platform === "UNIX") {
          versionMadeBy = 0x031E; // UNIX, version 3.0
          extFileAttr |= generateUnixExternalFileAttr(file.unixPermissions, dir);
        } else { // DOS or other, fallback to DOS
          versionMadeBy = 0x0014; // DOS, version 2.0
          extFileAttr |= generateDosExternalFileAttr(file.dosPermissions, dir);
        }

        // date
        // @see http://www.delorie.com/djgpp/doc/rbinter/it/52/13.html
        // @see http://www.delorie.com/djgpp/doc/rbinter/it/65/16.html
        // @see http://www.delorie.com/djgpp/doc/rbinter/it/66/16.html

        dosTime = date.getUTCHours();
        dosTime = dosTime << 6;
        dosTime = dosTime | date.getUTCMinutes();
        dosTime = dosTime << 5;
        dosTime = dosTime | date.getUTCSeconds() / 2;

        dosDate = date.getUTCFullYear() - 1980;
        dosDate = dosDate << 4;
        dosDate = dosDate | (date.getUTCMonth() + 1);
        dosDate = dosDate << 5;
        dosDate = dosDate | date.getUTCDate();

        if (useUTF8ForFileName) {
          // set the unicode path extra field. unzip needs at least one extra
          // field to correctly handle unicode path, so using the path is as good
          // as any other information. This could improve the situation with
          // other archive managers too.
          // This field is usually used without the utf8 flag, with a non
          // unicode path in the header (winrar, winzip). This helps (a bit)
          // with the messy Windows' default compressed folders feature but
          // breaks on p7zip which doesn't seek the unicode path extra field.
          // So for now, UTF-8 everywhere !
          unicodePathExtraField =
            // Version
            decToHex(1, 1) +
            // NameCRC32
            decToHex(crc32(encodedFileName), 4) +
            // UnicodeName
            utfEncodedFileName;

          extraFields +=
            // Info-ZIP Unicode Path Extra Field
            "\x75\x70" +
            // size
            decToHex(unicodePathExtraField.length, 2) +
            // content
            unicodePathExtraField;
        }

        if (useUTF8ForComment) {

          unicodeCommentExtraField =
            // Version
            decToHex(1, 1) +
            // CommentCRC32
            decToHex(crc32(encodedComment), 4) +
            // UnicodeName
            utfEncodedComment;

          extraFields +=
            // Info-ZIP Unicode Path Extra Field
            "\x75\x63" +
            // size
            decToHex(unicodeCommentExtraField.length, 2) +
            // content
            unicodeCommentExtraField;
        }

        var header = "";

        // version needed to extract
        header += "\x0A\x00";
        // general purpose bit flag
        header += decToHex(bitflag, 2);
        // compression method
        header += compression.magic;
        // last mod file time
        header += decToHex(dosTime, 2);
        // last mod file date
        header += decToHex(dosDate, 2);
        // crc-32
        header += decToHex(dataInfo.crc32, 4);
        // compressed size
        header += decToHex(dataInfo.compressedSize, 4);
        // uncompressed size
        header += decToHex(dataInfo.uncompressedSize, 4);
        // file name length
        header += decToHex(encodedFileName.length, 2);
        // extra field length
        header += decToHex(extraFields.length, 2);


        var fileRecord = signature.LOCAL_FILE_HEADER + header + encodedFileName + extraFields;

        var dirRecord = signature.CENTRAL_FILE_HEADER +
          // version made by (00: DOS)
          decToHex(versionMadeBy, 2) +
          // file header (common to file and central directory)
          header +
          // file comment length
          decToHex(encodedComment.length, 2) +
          // disk number start
          "\x00\x00" +
          // internal file attributes TODO
          "\x00\x00" +
          // external file attributes
          decToHex(extFileAttr, 4) +
          // relative offset of local header
          decToHex(offset, 4) +
          // file name
          encodedFileName +
          // extra field
          extraFields +
          // file comment
          encodedComment;

        return {
          fileRecord: fileRecord,
          dirRecord: dirRecord
        };
      };

      /**
       * Generate the EOCD record.
       * @param {Number} entriesCount the number of entries in the zip file.
       * @param {Number} centralDirLength the length (in bytes) of the central dir.
       * @param {Number} localDirLength the length (in bytes) of the local dir.
       * @param {String} comment the zip file comment as a binary string.
       * @param {Function} encodeFileName the function to encode the comment.
       * @return {String} the EOCD record.
       */
      var generateCentralDirectoryEnd = function (entriesCount, centralDirLength, localDirLength, comment, encodeFileName) {
        var dirEnd = "";
        var encodedComment = utils.transformTo("string", encodeFileName(comment));

        // end of central dir signature
        dirEnd = signature.CENTRAL_DIRECTORY_END +
          // number of this disk
          "\x00\x00" +
          // number of the disk with the start of the central directory
          "\x00\x00" +
          // total number of entries in the central directory on this disk
          decToHex(entriesCount, 2) +
          // total number of entries in the central directory
          decToHex(entriesCount, 2) +
          // size of the central directory   4 bytes
          decToHex(centralDirLength, 4) +
          // offset of start of central directory with respect to the starting disk number
          decToHex(localDirLength, 4) +
          // .ZIP file comment length
          decToHex(encodedComment.length, 2) +
          // .ZIP file comment
          encodedComment;

        return dirEnd;
      };

      /**
       * Generate data descriptors for a file entry.
       * @param {Object} streamInfo the hash generated by a worker, containing information
       * on the file entry.
       * @return {String} the data descriptors.
       */
      var generateDataDescriptors = function (streamInfo) {
        var descriptor = "";
        descriptor = signature.DATA_DESCRIPTOR +
          // crc-32                          4 bytes
          decToHex(streamInfo['crc32'], 4) +
          // compressed size                 4 bytes
          decToHex(streamInfo['compressedSize'], 4) +
          // uncompressed size               4 bytes
          decToHex(streamInfo['uncompressedSize'], 4);

        return descriptor;
      };


      /**
       * A worker to concatenate other workers to create a zip file.
       * @param {Boolean} streamFiles `true` to stream the content of the files,
       * `false` to accumulate it.
       * @param {String} comment the comment to use.
       * @param {String} platform the platform to use, "UNIX" or "DOS".
       * @param {Function} encodeFileName the function to encode file names and comments.
       */
      function ZipFileWorker(streamFiles, comment, platform, encodeFileName) {
        GenericWorker.call(this, "ZipFileWorker");
        // The number of bytes written so far. This doesn't count accumulated chunks.
        this.bytesWritten = 0;
        // The comment of the zip file
        this.zipComment = comment;
        // The platform "generating" the zip file.
        this.zipPlatform = platform;
        // the function to encode file names and comments.
        this.encodeFileName = encodeFileName;
        // Should we stream the content of the files ?
        this.streamFiles = streamFiles;
        // If `streamFiles` is false, we will need to accumulate the content of the
        // files to calculate sizes / crc32 (and write them *before* the content).
        // This boolean indicates if we are accumulating chunks (it will change a lot
        // during the lifetime of this worker).
        this.accumulate = false;
        // The buffer receiving chunks when accumulating content.
        this.contentBuffer = [];
        // The list of generated directory records.
        this.dirRecords = [];
        // The offset (in bytes) from the beginning of the zip file for the current source.
        this.currentSourceOffset = 0;
        // The total number of entries in this zip file.
        this.entriesCount = 0;
        // the name of the file currently being added, null when handling the end of the zip file.
        // Used for the emitted metadata.
        this.currentFile = null;


        this._sources = [];
      }

      utils.inherits(ZipFileWorker, GenericWorker);

      /**
       * @see GenericWorker.push
       */
      ZipFileWorker.prototype.push = function (chunk) {

        var currentFilePercent = chunk.meta.percent || 0;
        var entriesCount = this.entriesCount;
        var remainingFiles = this._sources.length;

        if (this.accumulate) {
          this.contentBuffer.push(chunk);
        } else {
          this.bytesWritten += chunk.data.length;

          GenericWorker.prototype.push.call(this, {
            data: chunk.data,
            meta: {
              currentFile: this.currentFile,
              percent: entriesCount ? (currentFilePercent + 100 * (entriesCount - remainingFiles - 1)) / entriesCount : 100
            }
          });
        }
      };

      /**
       * The worker started a new source (an other worker).
       * @param {Object} streamInfo the streamInfo object from the new source.
       */
      ZipFileWorker.prototype.openedSource = function (streamInfo) {
        this.currentSourceOffset = this.bytesWritten;
        this.currentFile = streamInfo['file'].name;

        var streamedContent = this.streamFiles && !streamInfo['file'].dir;

        // don't stream folders (because they don't have any content)
        if (streamedContent) {
          var record = generateZipParts(streamInfo, streamedContent, false, this.currentSourceOffset, this.zipPlatform, this.encodeFileName);
          this.push({
            data: record.fileRecord,
            meta: { percent: 0 }
          });
        } else {
          // we need to wait for the whole file before pushing anything
          this.accumulate = true;
        }
      };

      /**
       * The worker finished a source (an other worker).
       * @param {Object} streamInfo the streamInfo object from the finished source.
       */
      ZipFileWorker.prototype.closedSource = function (streamInfo) {
        this.accumulate = false;
        var streamedContent = this.streamFiles && !streamInfo['file'].dir;
        var record = generateZipParts(streamInfo, streamedContent, true, this.currentSourceOffset, this.zipPlatform, this.encodeFileName);

        this.dirRecords.push(record.dirRecord);
        if (streamedContent) {
          // after the streamed file, we put data descriptors
          this.push({
            data: generateDataDescriptors(streamInfo),
            meta: { percent: 100 }
          });
        } else {
          // the content wasn't streamed, we need to push everything now
          // first the file record, then the content
          this.push({
            data: record.fileRecord,
            meta: { percent: 0 }
          });
          while (this.contentBuffer.length) {
            this.push(this.contentBuffer.shift());
          }
        }
        this.currentFile = null;
      };

      /**
       * @see GenericWorker.flush
       */
      ZipFileWorker.prototype.flush = function () {

        var localDirLength = this.bytesWritten;
        for (var i = 0; i < this.dirRecords.length; i++) {
          this.push({
            data: this.dirRecords[i],
            meta: { percent: 100 }
          });
        }
        var centralDirLength = this.bytesWritten - localDirLength;

        var dirEnd = generateCentralDirectoryEnd(this.dirRecords.length, centralDirLength, localDirLength, this.zipComment, this.encodeFileName);

        this.push({
          data: dirEnd,
          meta: { percent: 100 }
        });
      };

      /**
       * Prepare the next source to be read.
       */
      ZipFileWorker.prototype.prepareNextSource = function () {
        this.previous = this._sources.shift();
        this.openedSource(this.previous.streamInfo);
        if (this.isPaused) {
          this.previous.pause();
        } else {
          this.previous.resume();
        }
      };

      /**
       * @see GenericWorker.registerPrevious
       */
      ZipFileWorker.prototype.registerPrevious = function (previous) {
        this._sources.push(previous);
        var self = this;

        previous.on('data', function (chunk) {
          self.processChunk(chunk);
        });
        previous.on('end', function () {
          self.closedSource(self.previous.streamInfo);
          if (self._sources.length) {
            self.prepareNextSource();
          } else {
            self.end();
          }
        });
        previous.on('error', function (e) {
          self.error(e);
        });
        return this;
      };

      /**
       * @see GenericWorker.resume
       */
      ZipFileWorker.prototype.resume = function () {
        if (!GenericWorker.prototype.resume.call(this)) {
          return false;
        }

        if (!this.previous && this._sources.length) {
          this.prepareNextSource();
          return true;
        }
        if (!this.previous && !this._sources.length && !this.generatedError) {
          this.end();
          return true;
        }
      };

      /**
       * @see GenericWorker.error
       */
      ZipFileWorker.prototype.error = function (e) {
        var sources = this._sources;
        if (!GenericWorker.prototype.error.call(this, e)) {
          return false;
        }
        for (var i = 0; i < sources.length; i++) {
          try {
            sources[i].error(e);
          } catch (e) {
            // the `error` exploded, nothing to do
          }
        }
        return true;
      };

      /**
       * @see GenericWorker.lock
       */
      ZipFileWorker.prototype.lock = function () {
        GenericWorker.prototype.lock.call(this);
        var sources = this._sources;
        for (var i = 0; i < sources.length; i++) {
          sources[i].lock();
        }
      };

      module.exports = ZipFileWorker;

    }, { "../crc32": 4, "../signature": 23, "../stream/GenericWorker": 28, "../utf8": 31, "../utils": 32 }], 9: [function (require, module, exports) {
      'use strict';

      var compressions = require('../compressions');
      var ZipFileWorker = require('./ZipFileWorker');

      /**
       * Find the compression to use.
       * @param {String} fileCompression the compression defined at the file level, if any.
       * @param {String} zipCompression the compression defined at the load() level.
       * @return {Object} the compression object to use.
       */
      var getCompression = function (fileCompression, zipCompression) {

        var compressionName = fileCompression || zipCompression;
        var compression = compressions[compressionName];
        if (!compression) {
          throw new Error(compressionName + " is not a valid compression method !");
        }
        return compression;
      };

      /**
       * Create a worker to generate a zip file.
       * @param {JSZip} zip the JSZip instance at the right root level.
       * @param {Object} options to generate the zip file.
       * @param {String} comment the comment to use.
       */
      exports.generateWorker = function (zip, options, comment) {

        var zipFileWorker = new ZipFileWorker(options.streamFiles, comment, options.platform, options.encodeFileName);
        var entriesCount = 0;
        try {

          zip.forEach(function (relativePath, file) {
            entriesCount++;
            var compression = getCompression(file.options.compression, options.compression);
            var compressionOptions = file.options.compressionOptions || options.compressionOptions || {};
            var dir = file.dir, date = file.date;

            file._compressWorker(compression, compressionOptions)
              .withStreamInfo("file", {
                name: relativePath,
                dir: dir,
                date: date,
                comment: file.comment || "",
                unixPermissions: file.unixPermissions,
                dosPermissions: file.dosPermissions
              })
              .pipe(zipFileWorker);
          });
          zipFileWorker.entriesCount = entriesCount;
        } catch (e) {
          zipFileWorker.error(e);
        }

        return zipFileWorker;
      };

    }, { "../compressions": 3, "./ZipFileWorker": 8 }], 10: [function (require, module, exports) {
      'use strict';

      /**
       * Representation a of zip file in js
       * @constructor
       */
      function JSZip() {
        // if this constructor is used without `new`, it adds `new` before itself:
        if (!(this instanceof JSZip)) {
          return new JSZip();
        }

        if (arguments.length) {
          throw new Error("The constructor with parameters has been removed in JSZip 3.0, please check the upgrade guide.");
        }

        // object containing the files :
        // {
        //   "folder/" : {...},
        //   "folder/data.txt" : {...}
        // }
        this.files = {};

        this.comment = null;

        // Where we are in the hierarchy
        this.root = "";
        this.clone = function () {
          var newObj = new JSZip();
          for (var i in this) {
            if (typeof this[i] !== "function") {
              newObj[i] = this[i];
            }
          }
          return newObj;
        };
      }

      JSZip.prototype = require('./object');
      JSZip.prototype.loadAsync = require('./load');
      JSZip.support = require('./support');
      JSZip.defaults = require('./defaults');

      // TODO find a better way to handle this version,
      // a require('package.json').version doesn't work with webpack, see #327
      JSZip.version = "3.4.0";

      JSZip.loadAsync = function (content, options) {
        return new JSZip().loadAsync(content, options);
      };

      JSZip.external = require("./external");
      module.exports = JSZip;

    }, { "./defaults": 5, "./external": 6, "./load": 11, "./object": 15, "./support": 30 }], 11: [function (require, module, exports) {
      'use strict';
      var utils = require('./utils');
      var external = require("./external");
      var utf8 = require('./utf8');
      var utils = require('./utils');
      var ZipEntries = require('./zipEntries');
      var Crc32Probe = require('./stream/Crc32Probe');
      var nodejsUtils = require("./nodejsUtils");

      /**
       * Check the CRC32 of an entry.
       * @param {ZipEntry} zipEntry the zip entry to check.
       * @return {Promise} the result.
       */
      function checkEntryCRC32(zipEntry) {
        return new external.Promise(function (resolve, reject) {
          var worker = zipEntry.decompressed.getContentWorker().pipe(new Crc32Probe());
          worker.on("error", function (e) {
            reject(e);
          })
            .on("end", function () {
              if (worker.streamInfo.crc32 !== zipEntry.decompressed.crc32) {
                reject(new Error("Corrupted zip : CRC32 mismatch"));
              } else {
                resolve();
              }
            })
            .resume();
        });
      }

      module.exports = function (data, options) {
        var zip = this;
        options = utils.extend(options || {}, {
          base64: false,
          checkCRC32: false,
          optimizedBinaryString: false,
          createFolders: false,
          decodeFileName: utf8.utf8decode
        });

        if (nodejsUtils.isNode && nodejsUtils.isStream(data)) {
          return external.Promise.reject(new Error("JSZip can't accept a stream when loading a zip file."));
        }

        return utils.prepareContent("the loaded zip file", data, true, options.optimizedBinaryString, options.base64)
          .then(function (data) {
            var zipEntries = new ZipEntries(options);
            zipEntries.load(data);
            return zipEntries;
          }).then(function checkCRC32(zipEntries) {
            var promises = [external.Promise.resolve(zipEntries)];
            var files = zipEntries.files;
            if (options.checkCRC32) {
              for (var i = 0; i < files.length; i++) {
                promises.push(checkEntryCRC32(files[i]));
              }
            }
            return external.Promise.all(promises);
          }).then(function addFiles(results) {
            var zipEntries = results.shift();
            var files = zipEntries.files;
            for (var i = 0; i < files.length; i++) {
              var input = files[i];
              zip.file(input.fileNameStr, input.decompressed, {
                binary: true,
                optimizedBinaryString: true,
                date: input.date,
                dir: input.dir,
                comment: input.fileCommentStr.length ? input.fileCommentStr : null,
                unixPermissions: input.unixPermissions,
                dosPermissions: input.dosPermissions,
                createFolders: options.createFolders
              });
            }
            if (zipEntries.zipComment.length) {
              zip.comment = zipEntries.zipComment;
            }

            return zip;
          });
      };

    }, { "./external": 6, "./nodejsUtils": 14, "./stream/Crc32Probe": 25, "./utf8": 31, "./utils": 32, "./zipEntries": 33 }], 12: [function (require, module, exports) {
      "use strict";

      var utils = require('../utils');
      var GenericWorker = require('../stream/GenericWorker');

      /**
       * A worker that use a nodejs stream as source.
       * @constructor
       * @param {String} filename the name of the file entry for this stream.
       * @param {Readable} stream the nodejs stream.
       */
      function NodejsStreamInputAdapter(filename, stream) {
        GenericWorker.call(this, "Nodejs stream input adapter for " + filename);
        this._upstreamEnded = false;
        this._bindStream(stream);
      }

      utils.inherits(NodejsStreamInputAdapter, GenericWorker);

      /**
       * Prepare the stream and bind the callbacks on it.
       * Do this ASAP on node 0.10 ! A lazy binding doesn't always work.
       * @param {Stream} stream the nodejs stream to use.
       */
      NodejsStreamInputAdapter.prototype._bindStream = function (stream) {
        var self = this;
        this._stream = stream;
        stream.pause();
        stream
          .on("data", function (chunk) {
            self.push({
              data: chunk,
              meta: {
                percent: 0
              }
            });
          })
          .on("error", function (e) {
            if (self.isPaused) {
              this.generatedError = e;
            } else {
              self.error(e);
            }
          })
          .on("end", function () {
            if (self.isPaused) {
              self._upstreamEnded = true;
            } else {
              self.end();
            }
          });
      };
      NodejsStreamInputAdapter.prototype.pause = function () {
        if (!GenericWorker.prototype.pause.call(this)) {
          return false;
        }
        this._stream.pause();
        return true;
      };
      NodejsStreamInputAdapter.prototype.resume = function () {
        if (!GenericWorker.prototype.resume.call(this)) {
          return false;
        }

        if (this._upstreamEnded) {
          this.end();
        } else {
          this._stream.resume();
        }

        return true;
      };

      module.exports = NodejsStreamInputAdapter;

    }, { "../stream/GenericWorker": 28, "../utils": 32 }], 13: [function (require, module, exports) {
      'use strict';

      var Readable = require('readable-stream').Readable;

      var utils = require('../utils');
      utils.inherits(NodejsStreamOutputAdapter, Readable);

      /**
       * A nodejs stream using a worker as source.
       * @see the SourceWrapper in http://nodejs.org/api/stream.html
       * @constructor
       * @param {StreamHelper} helper the helper wrapping the worker
       * @param {Object} options the nodejs stream options
       * @param {Function} updateCb the update callback.
       */
      function NodejsStreamOutputAdapter(helper, options, updateCb) {
        Readable.call(this, options);
        this._helper = helper;

        var self = this;
        helper.on("data", function (data, meta) {
          if (!self.push(data)) {
            self._helper.pause();
          }
          if (updateCb) {
            updateCb(meta);
          }
        })
          .on("error", function (e) {
            self.emit('error', e);
          })
          .on("end", function () {
            self.push(null);
          });
      }


      NodejsStreamOutputAdapter.prototype._read = function () {
        this._helper.resume();
      };

      module.exports = NodejsStreamOutputAdapter;

    }, { "../utils": 32, "readable-stream": 16 }], 14: [function (require, module, exports) {
      'use strict';

      module.exports = {
        /**
         * True if this is running in Nodejs, will be undefined in a browser.
         * In a browser, browserify won't include this file and the whole module
         * will be resolved an empty object.
         */
        isNode: typeof Buffer !== "undefined",
        /**
         * Create a new nodejs Buffer from an existing content.
         * @param {Object} data the data to pass to the constructor.
         * @param {String} encoding the encoding to use.
         * @return {Buffer} a new Buffer.
         */
        newBufferFrom: function (data, encoding) {
          if (Buffer.from && Buffer.from !== Uint8Array.from) {
            return Buffer.from(data, encoding);
          } else {
            if (typeof data === "number") {
              // Safeguard for old Node.js versions. On newer versions,
              // Buffer.from(number) / Buffer(number, encoding) already throw.
              throw new Error("The \"data\" argument must not be a number");
            }
            return new Buffer(data, encoding);
          }
        },
        /**
         * Create a new nodejs Buffer with the specified size.
         * @param {Integer} size the size of the buffer.
         * @return {Buffer} a new Buffer.
         */
        allocBuffer: function (size) {
          if (Buffer.alloc) {
            return Buffer.alloc(size);
          } else {
            var buf = new Buffer(size);
            buf.fill(0);
            return buf;
          }
        },
        /**
         * Find out if an object is a Buffer.
         * @param {Object} b the object to test.
         * @return {Boolean} true if the object is a Buffer, false otherwise.
         */
        isBuffer: function (b) {
          return Buffer.isBuffer(b);
        },

        isStream: function (obj) {
          return obj &&
            typeof obj.on === "function" &&
            typeof obj.pause === "function" &&
            typeof obj.resume === "function";
        }
      };

    }, {}], 15: [function (require, module, exports) {
      'use strict';
      var utf8 = require('./utf8');
      var utils = require('./utils');
      var GenericWorker = require('./stream/GenericWorker');
      var StreamHelper = require('./stream/StreamHelper');
      var defaults = require('./defaults');
      var CompressedObject = require('./compressedObject');
      var ZipObject = require('./zipObject');
      var generate = require("./generate");
      var nodejsUtils = require("./nodejsUtils");
      var NodejsStreamInputAdapter = require("./nodejs/NodejsStreamInputAdapter");


      /**
       * Add a file in the current folder.
       * @private
       * @param {string} name the name of the file
       * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data of the file
       * @param {Object} originalOptions the options of the file
       * @return {Object} the new file.
       */
      var fileAdd = function (name, data, originalOptions) {
        // be sure sub folders exist
        var dataType = utils.getTypeOf(data),
          parent;


        /*
     * Correct options.
     */

        var o = utils.extend(originalOptions || {}, defaults);
        o.date = o.date || new Date();
        if (o.compression !== null) {
          o.compression = o.compression.toUpperCase();
        }

        if (typeof o.unixPermissions === "string") {
          o.unixPermissions = parseInt(o.unixPermissions, 8);
        }

        // UNX_IFDIR  0040000 see zipinfo.c
        if (o.unixPermissions && (o.unixPermissions & 0x4000)) {
          o.dir = true;
        }
        // Bit 4    Directory
        if (o.dosPermissions && (o.dosPermissions & 0x0010)) {
          o.dir = true;
        }

        if (o.dir) {
          name = forceTrailingSlash(name);
        }
        if (o.createFolders && (parent = parentFolder(name))) {
          folderAdd.call(this, parent, true);
        }

        var isUnicodeString = dataType === "string" && o.binary === false && o.base64 === false;
        if (!originalOptions || typeof originalOptions.binary === "undefined") {
          o.binary = !isUnicodeString;
        }


        var isCompressedEmpty = (data instanceof CompressedObject) && data.uncompressedSize === 0;

        if (isCompressedEmpty || o.dir || !data || data.length === 0) {
          o.base64 = false;
          o.binary = true;
          data = "";
          o.compression = "STORE";
          dataType = "string";
        }

        /*
     * Convert content to fit.
     */

        var zipObjectContent = null;
        if (data instanceof CompressedObject || data instanceof GenericWorker) {
          zipObjectContent = data;
        } else if (nodejsUtils.isNode && nodejsUtils.isStream(data)) {
          zipObjectContent = new NodejsStreamInputAdapter(name, data);
        } else {
          zipObjectContent = utils.prepareContent(name, data, o.binary, o.optimizedBinaryString, o.base64);
        }

        var object = new ZipObject(name, zipObjectContent, o);
        this.files[name] = object;
        /*
    TODO: we can't throw an exception because we have async promises
    (we can have a promise of a Date() for example) but returning a
    promise is useless because file(name, data) returns the JSZip
    object for chaining. Should we break that to allow the user
    to catch the error ?

    return external.Promise.resolve(zipObjectContent)
    .then(function () {
        return object;
    });
    */
      };

      /**
       * Find the parent folder of the path.
       * @private
       * @param {string} path the path to use
       * @return {string} the parent folder, or ""
       */
      var parentFolder = function (path) {
        if (path.slice(-1) === '/') {
          path = path.substring(0, path.length - 1);
        }
        var lastSlash = path.lastIndexOf('/');
        return (lastSlash > 0) ? path.substring(0, lastSlash) : "";
      };

      /**
       * Returns the path with a slash at the end.
       * @private
       * @param {String} path the path to check.
       * @return {String} the path with a trailing slash.
       */
      var forceTrailingSlash = function (path) {
        // Check the name ends with a /
        if (path.slice(-1) !== "/") {
          path += "/"; // IE doesn't like substr(-1)
        }
        return path;
      };

      /**
       * Add a (sub) folder in the current folder.
       * @private
       * @param {string} name the folder's name
       * @param {boolean=} [createFolders] If true, automatically create sub
       *  folders. Defaults to false.
       * @return {Object} the new folder.
       */
      var folderAdd = function (name, createFolders) {
        createFolders = (typeof createFolders !== 'undefined') ? createFolders : defaults.createFolders;

        name = forceTrailingSlash(name);

        // Does this folder already exist?
        if (!this.files[name]) {
          fileAdd.call(this, name, null, {
            dir: true,
            createFolders: createFolders
          });
        }
        return this.files[name];
      };

      /**
       * Cross-window, cross-Node-context regular expression detection
       * @param  {Object}  object Anything
       * @return {Boolean}        true if the object is a regular expression,
       * false otherwise
       */
      function isRegExp(object) {
        return Object.prototype.toString.call(object) === "[object RegExp]";
      }

      // return the actual prototype of JSZip
      var out = {
        /**
         * @see loadAsync
         */
        load: function () {
          throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide.");
        },


        /**
         * Call a callback function for each entry at this folder level.
         * @param {Function} cb the callback function:
         * function (relativePath, file) {...}
         * It takes 2 arguments : the relative path and the file.
         */
        forEach: function (cb) {
          var filename, relativePath, file;
          for (filename in this.files) {
            if (!this.files.hasOwnProperty(filename)) {
              continue;
            }
            file = this.files[filename];
            relativePath = filename.slice(this.root.length, filename.length);
            if (relativePath && filename.slice(0, this.root.length) === this.root) { // the file is in the current root
              cb(relativePath, file); // TODO reverse the parameters ? need to be clean AND consistent with the filter search fn...
            }
          }
        },

        /**
         * Filter nested files/folders with the specified function.
         * @param {Function} search the predicate to use :
         * function (relativePath, file) {...}
         * It takes 2 arguments : the relative path and the file.
         * @return {Array} An array of matching elements.
         */
        filter: function (search) {
          var result = [];
          this.forEach(function (relativePath, entry) {
            if (search(relativePath, entry)) { // the file matches the function
              result.push(entry);
            }

          });
          return result;
        },

        /**
         * Add a file to the zip file, or search a file.
         * @param   {string|RegExp} name The name of the file to add (if data is defined),
         * the name of the file to find (if no data) or a regex to match files.
         * @param   {String|ArrayBuffer|Uint8Array|Buffer} data  The file data, either raw or base64 encoded
         * @param   {Object} o     File options
         * @return  {JSZip|Object|Array} this JSZip object (when adding a file),
         * a file (when searching by string) or an array of files (when searching by regex).
         */
        file: function (name, data, o) {
          if (arguments.length === 1) {
            if (isRegExp(name)) {
              var regexp = name;
              return this.filter(function (relativePath, file) {
                return !file.dir && regexp.test(relativePath);
              });
            }
            else { // text
              var obj = this.files[this.root + name];
              if (obj && !obj.dir) {
                return obj;
              } else {
                return null;
              }
            }
          }
          else { // more than one argument : we have data !
            name = this.root + name;
            fileAdd.call(this, name, data, o);
          }
          return this;
        },

        /**
         * Add a directory to the zip file, or search.
         * @param   {String|RegExp} arg The name of the directory to add, or a regex to search folders.
         * @return  {JSZip} an object with the new directory as the root, or an array containing matching folders.
         */
        folder: function (arg) {
          if (!arg) {
            return this;
          }

          if (isRegExp(arg)) {
            return this.filter(function (relativePath, file) {
              return file.dir && arg.test(relativePath);
            });
          }

          // else, name is a new folder
          var name = this.root + arg;
          var newFolder = folderAdd.call(this, name);

          // Allow chaining by returning a new object with this folder as the root
          var ret = this.clone();
          ret.root = newFolder.name;
          return ret;
        },

        /**
         * Delete a file, or a directory and all sub-files, from the zip
         * @param {string} name the name of the file to delete
         * @return {JSZip} this JSZip object
         */
        remove: function (name) {
          name = this.root + name;
          var file = this.files[name];
          if (!file) {
            // Look for any folders
            if (name.slice(-1) !== "/") {
              name += "/";
            }
            file = this.files[name];
          }

          if (file && !file.dir) {
            // file
            delete this.files[name];
          } else {
            // maybe a folder, delete recursively
            var kids = this.filter(function (relativePath, file) {
              return file.name.slice(0, name.length) === name;
            });
            for (var i = 0; i < kids.length; i++) {
              delete this.files[kids[i].name];
            }
          }

          return this;
        },

        /**
         * Generate the complete zip file
         * @param {Object} options the options to generate the zip file :
         * - compression, "STORE" by default.
         * - type, "base64" by default. Values are : string, base64, uint8array, arraybuffer, blob.
         * @return {String|Uint8Array|ArrayBuffer|Buffer|Blob} the zip file
         */
        generate: function (options) {
          throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide.");
        },

        /**
         * Generate the complete zip file as an internal stream.
         * @param {Object} options the options to generate the zip file :
         * - compression, "STORE" by default.
         * - type, "base64" by default. Values are : string, base64, uint8array, arraybuffer, blob.
         * @return {StreamHelper} the streamed zip file.
         */
        generateInternalStream: function (options) {
          var worker, opts = {};
          try {
            opts = utils.extend(options || {}, {
              streamFiles: false,
              compression: "STORE",
              compressionOptions: null,
              type: "",
              platform: "DOS",
              comment: null,
              mimeType: 'application/zip',
              encodeFileName: utf8.utf8encode
            });

            opts.type = opts.type.toLowerCase();
            opts.compression = opts.compression.toUpperCase();

            // "binarystring" is preferred but the internals use "string".
            if (opts.type === "binarystring") {
              opts.type = "string";
            }

            if (!opts.type) {
              throw new Error("No output type specified.");
            }

            utils.checkSupport(opts.type);

            // accept nodejs `process.platform`
            if (
              opts.platform === 'darwin' ||
              opts.platform === 'freebsd' ||
              opts.platform === 'linux' ||
              opts.platform === 'sunos'
            ) {
              opts.platform = "UNIX";
            }
            if (opts.platform === 'win32') {
              opts.platform = "DOS";
            }

            var comment = opts.comment || this.comment || "";
            worker = generate.generateWorker(this, opts, comment);
          } catch (e) {
            worker = new GenericWorker("error");
            worker.error(e);
          }
          return new StreamHelper(worker, opts.type || "string", opts.mimeType);
        },
        /**
         * Generate the complete zip file asynchronously.
         * @see generateInternalStream
         */
        generateAsync: function (options, onUpdate) {
          return this.generateInternalStream(options).accumulate(onUpdate);
        },
        /**
         * Generate the complete zip file asynchronously.
         * @see generateInternalStream
         */
        generateNodeStream: function (options, onUpdate) {
          options = options || {};
          if (!options.type) {
            options.type = "nodebuffer";
          }
          return this.generateInternalStream(options).toNodejsStream(onUpdate);
        }
      };
      module.exports = out;

    }, { "./compressedObject": 2, "./defaults": 5, "./generate": 9, "./nodejs/NodejsStreamInputAdapter": 12, "./nodejsUtils": 14, "./stream/GenericWorker": 28, "./stream/StreamHelper": 29, "./utf8": 31, "./utils": 32, "./zipObject": 35 }], 16: [function (require, module, exports) {
      /*
 * This file is used by module bundlers (browserify/webpack/etc) when
 * including a stream implementation. We use "readable-stream" to get a
 * consistent behavior between nodejs versions but bundlers often have a shim
 * for "stream". Using this shim greatly improve the compatibility and greatly
 * reduce the final size of the bundle (only one stream implementation, not
 * two).
 */
      module.exports = require("stream");

    }, { "stream": undefined }], 17: [function (require, module, exports) {
      'use strict';
      var DataReader = require('./DataReader');
      var utils = require('../utils');

      function ArrayReader(data) {
        DataReader.call(this, data);
        for (var i = 0; i < this.data.length; i++) {
          data[i] = data[i] & 0xFF;
        }
      }

      utils.inherits(ArrayReader, DataReader);
      /**
       * @see DataReader.byteAt
       */
      ArrayReader.prototype.byteAt = function (i) {
        return this.data[this.zero + i];
      };
      /**
       * @see DataReader.lastIndexOfSignature
       */
      ArrayReader.prototype.lastIndexOfSignature = function (sig) {
        var sig0 = sig.charCodeAt(0),
          sig1 = sig.charCodeAt(1),
          sig2 = sig.charCodeAt(2),
          sig3 = sig.charCodeAt(3);
        for (var i = this.length - 4; i >= 0; --i) {
          if (this.data[i] === sig0 && this.data[i + 1] === sig1 && this.data[i + 2] === sig2 && this.data[i + 3] === sig3) {
            return i - this.zero;
          }
        }

        return -1;
      };
      /**
       * @see DataReader.readAndCheckSignature
       */
      ArrayReader.prototype.readAndCheckSignature = function (sig) {
        var sig0 = sig.charCodeAt(0),
          sig1 = sig.charCodeAt(1),
          sig2 = sig.charCodeAt(2),
          sig3 = sig.charCodeAt(3),
          data = this.readData(4);
        return sig0 === data[0] && sig1 === data[1] && sig2 === data[2] && sig3 === data[3];
      };
      /**
       * @see DataReader.readData
       */
      ArrayReader.prototype.readData = function (size) {
        this.checkOffset(size);
        if (size === 0) {
          return [];
        }
        var result = this.data.slice(this.zero + this.index, this.zero + this.index + size);
        this.index += size;
        return result;
      };
      module.exports = ArrayReader;

    }, { "../utils": 32, "./DataReader": 18 }], 18: [function (require, module, exports) {
      'use strict';
      var utils = require('../utils');

      function DataReader(data) {
        this.data = data; // type : see implementation
        this.length = data.length;
        this.index = 0;
        this.zero = 0;
      }

      DataReader.prototype = {
        /**
         * Check that the offset will not go too far.
         * @param {string} offset the additional offset to check.
         * @throws {Error} an Error if the offset is out of bounds.
         */
        checkOffset: function (offset) {
          this.checkIndex(this.index + offset);
        },
        /**
         * Check that the specified index will not be too far.
         * @param {string} newIndex the index to check.
         * @throws {Error} an Error if the index is out of bounds.
         */
        checkIndex: function (newIndex) {
          if (this.length < this.zero + newIndex || newIndex < 0) {
            throw new Error("End of data reached (data length = " + this.length + ", asked index = " + (newIndex) + "). Corrupted zip ?");
          }
        },
        /**
         * Change the index.
         * @param {number} newIndex The new index.
         * @throws {Error} if the new index is out of the data.
         */
        setIndex: function (newIndex) {
          this.checkIndex(newIndex);
          this.index = newIndex;
        },
        /**
         * Skip the next n bytes.
         * @param {number} n the number of bytes to skip.
         * @throws {Error} if the new index is out of the data.
         */
        skip: function (n) {
          this.setIndex(this.index + n);
        },
        /**
         * Get the byte at the specified index.
         * @param {number} i the index to use.
         * @return {number} a byte.
         */
        byteAt: function (i) {
          // see implementations
        },
        /**
         * Get the next number with a given byte size.
         * @param {number} size the number of bytes to read.
         * @return {number} the corresponding number.
         */
        readInt: function (size) {
          var result = 0,
            i;
          this.checkOffset(size);
          for (i = this.index + size - 1; i >= this.index; i--) {
            result = (result << 8) + this.byteAt(i);
          }
          this.index += size;
          return result;
        },
        /**
         * Get the next string with a given byte size.
         * @param {number} size the number of bytes to read.
         * @return {string} the corresponding string.
         */
        readString: function (size) {
          return utils.transformTo("string", this.readData(size));
        },
        /**
         * Get raw data without conversion, <size> bytes.
         * @param {number} size the number of bytes to read.
         * @return {Object} the raw data, implementation specific.
         */
        readData: function (size) {
          // see implementations
        },
        /**
         * Find the last occurrence of a zip signature (4 bytes).
         * @param {string} sig the signature to find.
         * @return {number} the index of the last occurrence, -1 if not found.
         */
        lastIndexOfSignature: function (sig) {
          // see implementations
        },
        /**
         * Read the signature (4 bytes) at the current position and compare it with sig.
         * @param {string} sig the expected signature
         * @return {boolean} true if the signature matches, false otherwise.
         */
        readAndCheckSignature: function (sig) {
          // see implementations
        },
        /**
         * Get the next date.
         * @return {Date} the date.
         */
        readDate: function () {
          var dostime = this.readInt(4);
          return new Date(Date.UTC(
            ((dostime >> 25) & 0x7f) + 1980, // year
            ((dostime >> 21) & 0x0f) - 1, // month
            (dostime >> 16) & 0x1f, // day
            (dostime >> 11) & 0x1f, // hour
            (dostime >> 5) & 0x3f, // minute
            (dostime & 0x1f) << 1)); // second
        }
      };
      module.exports = DataReader;

    }, { "../utils": 32 }], 19: [function (require, module, exports) {
      'use strict';
      var Uint8ArrayReader = require('./Uint8ArrayReader');
      var utils = require('../utils');

      function NodeBufferReader(data) {
        Uint8ArrayReader.call(this, data);
      }

      utils.inherits(NodeBufferReader, Uint8ArrayReader);

      /**
       * @see DataReader.readData
       */
      NodeBufferReader.prototype.readData = function (size) {
        this.checkOffset(size);
        var result = this.data.slice(this.zero + this.index, this.zero + this.index + size);
        this.index += size;
        return result;
      };
      module.exports = NodeBufferReader;

    }, { "../utils": 32, "./Uint8ArrayReader": 21 }], 20: [function (require, module, exports) {
      'use strict';
      var DataReader = require('./DataReader');
      var utils = require('../utils');

      function StringReader(data) {
        DataReader.call(this, data);
      }

      utils.inherits(StringReader, DataReader);
      /**
       * @see DataReader.byteAt
       */
      StringReader.prototype.byteAt = function (i) {
        return this.data.charCodeAt(this.zero + i);
      };
      /**
       * @see DataReader.lastIndexOfSignature
       */
      StringReader.prototype.lastIndexOfSignature = function (sig) {
        return this.data.lastIndexOf(sig) - this.zero;
      };
      /**
       * @see DataReader.readAndCheckSignature
       */
      StringReader.prototype.readAndCheckSignature = function (sig) {
        var data = this.readData(4);
        return sig === data;
      };
      /**
       * @see DataReader.readData
       */
      StringReader.prototype.readData = function (size) {
        this.checkOffset(size);
        // this will work because the constructor applied the "& 0xff" mask.
        var result = this.data.slice(this.zero + this.index, this.zero + this.index + size);
        this.index += size;
        return result;
      };
      module.exports = StringReader;

    }, { "../utils": 32, "./DataReader": 18 }], 21: [function (require, module, exports) {
      'use strict';
      var ArrayReader = require('./ArrayReader');
      var utils = require('../utils');

      function Uint8ArrayReader(data) {
        ArrayReader.call(this, data);
      }

      utils.inherits(Uint8ArrayReader, ArrayReader);
      /**
       * @see DataReader.readData
       */
      Uint8ArrayReader.prototype.readData = function (size) {
        this.checkOffset(size);
        if (size === 0) {
          // in IE10, when using subarray(idx, idx), we get the array [0x00] instead of [].
          return new Uint8Array(0);
        }
        var result = this.data.subarray(this.zero + this.index, this.zero + this.index + size);
        this.index += size;
        return result;
      };
      module.exports = Uint8ArrayReader;

    }, { "../utils": 32, "./ArrayReader": 17 }], 22: [function (require, module, exports) {
      'use strict';

      var utils = require('../utils');
      var support = require('../support');
      var ArrayReader = require('./ArrayReader');
      var StringReader = require('./StringReader');
      var NodeBufferReader = require('./NodeBufferReader');
      var Uint8ArrayReader = require('./Uint8ArrayReader');

      /**
       * Create a reader adapted to the data.
       * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data to read.
       * @return {DataReader} the data reader.
       */
      module.exports = function (data) {
        var type = utils.getTypeOf(data);
        utils.checkSupport(type);
        if (type === "string" && !support.uint8array) {
          return new StringReader(data);
        }
        if (type === "nodebuffer") {
          return new NodeBufferReader(data);
        }
        if (support.uint8array) {
          return new Uint8ArrayReader(utils.transformTo("uint8array", data));
        }
        return new ArrayReader(utils.transformTo("array", data));
      };

    }, { "../support": 30, "../utils": 32, "./ArrayReader": 17, "./NodeBufferReader": 19, "./StringReader": 20, "./Uint8ArrayReader": 21 }], 23: [function (require, module, exports) {
      'use strict';
      exports.LOCAL_FILE_HEADER = "PK\x03\x04";
      exports.CENTRAL_FILE_HEADER = "PK\x01\x02";
      exports.CENTRAL_DIRECTORY_END = "PK\x05\x06";
      exports.ZIP64_CENTRAL_DIRECTORY_LOCATOR = "PK\x06\x07";
      exports.ZIP64_CENTRAL_DIRECTORY_END = "PK\x06\x06";
      exports.DATA_DESCRIPTOR = "PK\x07\x08";

    }, {}], 24: [function (require, module, exports) {
      'use strict';

      var GenericWorker = require('./GenericWorker');
      var utils = require('../utils');

      /**
       * A worker which convert chunks to a specified type.
       * @constructor
       * @param {String} destType the destination type.
       */
      function ConvertWorker(destType) {
        GenericWorker.call(this, "ConvertWorker to " + destType);
        this.destType = destType;
      }

      utils.inherits(ConvertWorker, GenericWorker);

      /**
       * @see GenericWorker.processChunk
       */
      ConvertWorker.prototype.processChunk = function (chunk) {
        this.push({
          data: utils.transformTo(this.destType, chunk.data),
          meta: chunk.meta
        });
      };
      module.exports = ConvertWorker;

    }, { "../utils": 32, "./GenericWorker": 28 }], 25: [function (require, module, exports) {
      'use strict';

      var GenericWorker = require('./GenericWorker');
      var crc32 = require('../crc32');
      var utils = require('../utils');

      /**
       * A worker which calculate the crc32 of the data flowing through.
       * @constructor
       */
      function Crc32Probe() {
        GenericWorker.call(this, "Crc32Probe");
        this.withStreamInfo("crc32", 0);
      }

      utils.inherits(Crc32Probe, GenericWorker);

      /**
       * @see GenericWorker.processChunk
       */
      Crc32Probe.prototype.processChunk = function (chunk) {
        this.streamInfo.crc32 = crc32(chunk.data, this.streamInfo.crc32 || 0);
        this.push(chunk);
      };
      module.exports = Crc32Probe;

    }, { "../crc32": 4, "../utils": 32, "./GenericWorker": 28 }], 26: [function (require, module, exports) {
      'use strict';

      var utils = require('../utils');
      var GenericWorker = require('./GenericWorker');

      /**
       * A worker which calculate the total length of the data flowing through.
       * @constructor
       * @param {String} propName the name used to expose the length
       */
      function DataLengthProbe(propName) {
        GenericWorker.call(this, "DataLengthProbe for " + propName);
        this.propName = propName;
        this.withStreamInfo(propName, 0);
      }

      utils.inherits(DataLengthProbe, GenericWorker);

      /**
       * @see GenericWorker.processChunk
       */
      DataLengthProbe.prototype.processChunk = function (chunk) {
        if (chunk) {
          var length = this.streamInfo[this.propName] || 0;
          this.streamInfo[this.propName] = length + chunk.data.length;
        }
        GenericWorker.prototype.processChunk.call(this, chunk);
      };
      module.exports = DataLengthProbe;


    }, { "../utils": 32, "./GenericWorker": 28 }], 27: [function (require, module, exports) {
      'use strict';

      var utils = require('../utils');
      var GenericWorker = require('./GenericWorker');

      // the size of the generated chunks
      // TODO expose this as a public variable
      var DEFAULT_BLOCK_SIZE = 16 * 1024;

      /**
       * A worker that reads a content and emits chunks.
       * @constructor
       * @param {Promise} dataP the promise of the data to split
       */
      function DataWorker(dataP) {
        GenericWorker.call(this, "DataWorker");
        var self = this;
        this.dataIsReady = false;
        this.index = 0;
        this.max = 0;
        this.data = null;
        this.type = "";

        this._tickScheduled = false;

        dataP.then(function (data) {
          self.dataIsReady = true;
          self.data = data;
          self.max = data && data.length || 0;
          self.type = utils.getTypeOf(data);
          if (!self.isPaused) {
            self._tickAndRepeat();
          }
        }, function (e) {
          self.error(e);
        });
      }

      utils.inherits(DataWorker, GenericWorker);

      /**
       * @see GenericWorker.cleanUp
       */
      DataWorker.prototype.cleanUp = function () {
        GenericWorker.prototype.cleanUp.call(this);
        this.data = null;
      };

      /**
       * @see GenericWorker.resume
       */
      DataWorker.prototype.resume = function () {
        if (!GenericWorker.prototype.resume.call(this)) {
          return false;
        }

        if (!this._tickScheduled && this.dataIsReady) {
          this._tickScheduled = true;
          utils.delay(this._tickAndRepeat, [], this);
        }
        return true;
      };

      /**
       * Trigger a tick a schedule an other call to this function.
       */
      DataWorker.prototype._tickAndRepeat = function () {
        this._tickScheduled = false;
        if (this.isPaused || this.isFinished) {
          return;
        }
        this._tick();
        if (!this.isFinished) {
          utils.delay(this._tickAndRepeat, [], this);
          this._tickScheduled = true;
        }
      };

      /**
       * Read and push a chunk.
       */
      DataWorker.prototype._tick = function () {

        if (this.isPaused || this.isFinished) {
          return false;
        }

        var size = DEFAULT_BLOCK_SIZE;
        var data = null, nextIndex = Math.min(this.max, this.index + size);
        if (this.index >= this.max) {
          // EOF
          return this.end();
        } else {
          switch (this.type) {
            case "string":
              data = this.data.substring(this.index, nextIndex);
              break;
            case "uint8array":
              data = this.data.subarray(this.index, nextIndex);
              break;
            case "array":
            case "nodebuffer":
              data = this.data.slice(this.index, nextIndex);
              break;
          }
          this.index = nextIndex;
          return this.push({
            data: data,
            meta: {
              percent: this.max ? this.index / this.max * 100 : 0
            }
          });
        }
      };

      module.exports = DataWorker;

    }, { "../utils": 32, "./GenericWorker": 28 }], 28: [function (require, module, exports) {
      'use strict';

      /**
       * A worker that does nothing but passing chunks to the next one. This is like
       * a nodejs stream but with some differences. On the good side :
       * - it works on IE 6-9 without any issue / polyfill
       * - it weights less than the full dependencies bundled with browserify
       * - it forwards errors (no need to declare an error handler EVERYWHERE)
       *
       * A chunk is an object with 2 attributes : `meta` and `data`. The former is an
       * object containing anything (`percent` for example), see each worker for more
       * details. The latter is the real data (String, Uint8Array, etc).
       *
       * @constructor
       * @param {String} name the name of the stream (mainly used for debugging purposes)
       */
      function GenericWorker(name) {
        // the name of the worker
        this.name = name || "default";
        // an object containing metadata about the workers chain
        this.streamInfo = {};
        // an error which happened when the worker was paused
        this.generatedError = null;
        // an object containing metadata to be merged by this worker into the general metadata
        this.extraStreamInfo = {};
        // true if the stream is paused (and should not do anything), false otherwise
        this.isPaused = true;
        // true if the stream is finished (and should not do anything), false otherwise
        this.isFinished = false;
        // true if the stream is locked to prevent further structure updates (pipe), false otherwise
        this.isLocked = false;
        // the event listeners
        this._listeners = {
          'data': [],
          'end': [],
          'error': []
        };
        // the previous worker, if any
        this.previous = null;
      }

      GenericWorker.prototype = {
        /**
         * Push a chunk to the next workers.
         * @param {Object} chunk the chunk to push
         */
        push: function (chunk) {
          this.emit("data", chunk);
        },
        /**
         * End the stream.
         * @return {Boolean} true if this call ended the worker, false otherwise.
         */
        end: function () {
          if (this.isFinished) {
            return false;
          }

          this.flush();
          try {
            this.emit("end");
            this.cleanUp();
            this.isFinished = true;
          } catch (e) {
            this.emit("error", e);
          }
          return true;
        },
        /**
         * End the stream with an error.
         * @param {Error} e the error which caused the premature end.
         * @return {Boolean} true if this call ended the worker with an error, false otherwise.
         */
        error: function (e) {
          if (this.isFinished) {
            return false;
          }

          if (this.isPaused) {
            this.generatedError = e;
          } else {
            this.isFinished = true;

            this.emit("error", e);

            // in the workers chain exploded in the middle of the chain,
            // the error event will go downward but we also need to notify
            // workers upward that there has been an error.
            if (this.previous) {
              this.previous.error(e);
            }

            this.cleanUp();
          }
          return true;
        },
        /**
         * Add a callback on an event.
         * @param {String} name the name of the event (data, end, error)
         * @param {Function} listener the function to call when the event is triggered
         * @return {GenericWorker} the current object for chainability
         */
        on: function (name, listener) {
          this._listeners[name].push(listener);
          return this;
        },
        /**
         * Clean any references when a worker is ending.
         */
        cleanUp: function () {
          this.streamInfo = this.generatedError = this.extraStreamInfo = null;
          this._listeners = [];
        },
        /**
         * Trigger an event. This will call registered callback with the provided arg.
         * @param {String} name the name of the event (data, end, error)
         * @param {Object} arg the argument to call the callback with.
         */
        emit: function (name, arg) {
          if (this._listeners[name]) {
            for (var i = 0; i < this._listeners[name].length; i++) {
              this._listeners[name][i].call(this, arg);
            }
          }
        },
        /**
         * Chain a worker with an other.
         * @param {Worker} next the worker receiving events from the current one.
         * @return {worker} the next worker for chainability
         */
        pipe: function (next) {
          return next.registerPrevious(this);
        },
        /**
         * Same as `pipe` in the other direction.
         * Using an API with `pipe(next)` is very easy.
         * Implementing the API with the point of view of the next one registering
         * a source is easier, see the ZipFileWorker.
         * @param {Worker} previous the previous worker, sending events to this one
         * @return {Worker} the current worker for chainability
         */
        registerPrevious: function (previous) {
          if (this.isLocked) {
            throw new Error("The stream '" + this + "' has already been used.");
          }

          // sharing the streamInfo...
          this.streamInfo = previous.streamInfo;
          // ... and adding our own bits
          this.mergeStreamInfo();
          this.previous = previous;
          var self = this;
          previous.on('data', function (chunk) {
            self.processChunk(chunk);
          });
          previous.on('end', function () {
            self.end();
          });
          previous.on('error', function (e) {
            self.error(e);
          });
          return this;
        },
        /**
         * Pause the stream so it doesn't send events anymore.
         * @return {Boolean} true if this call paused the worker, false otherwise.
         */
        pause: function () {
          if (this.isPaused || this.isFinished) {
            return false;
          }
          this.isPaused = true;

          if (this.previous) {
            this.previous.pause();
          }
          return true;
        },
        /**
         * Resume a paused stream.
         * @return {Boolean} true if this call resumed the worker, false otherwise.
         */
        resume: function () {
          if (!this.isPaused || this.isFinished) {
            return false;
          }
          this.isPaused = false;

          // if true, the worker tried to resume but failed
          var withError = false;
          if (this.generatedError) {
            this.error(this.generatedError);
            withError = true;
          }
          if (this.previous) {
            this.previous.resume();
          }

          return !withError;
        },
        /**
         * Flush any remaining bytes as the stream is ending.
         */
        flush: function () {},
        /**
         * Process a chunk. This is usually the method overridden.
         * @param {Object} chunk the chunk to process.
         */
        processChunk: function (chunk) {
          this.push(chunk);
        },
        /**
         * Add a key/value to be added in the workers chain streamInfo once activated.
         * @param {String} key the key to use
         * @param {Object} value the associated value
         * @return {Worker} the current worker for chainability
         */
        withStreamInfo: function (key, value) {
          this.extraStreamInfo[key] = value;
          this.mergeStreamInfo();
          return this;
        },
        /**
         * Merge this worker's streamInfo into the chain's streamInfo.
         */
        mergeStreamInfo: function () {
          for (var key in this.extraStreamInfo) {
            if (!this.extraStreamInfo.hasOwnProperty(key)) {
              continue;
            }
            this.streamInfo[key] = this.extraStreamInfo[key];
          }
        },

        /**
         * Lock the stream to prevent further updates on the workers chain.
         * After calling this method, all calls to pipe will fail.
         */
        lock: function () {
          if (this.isLocked) {
            throw new Error("The stream '" + this + "' has already been used.");
          }
          this.isLocked = true;
          if (this.previous) {
            this.previous.lock();
          }
        },

        /**
         *
         * Pretty print the workers chain.
         */
        toString: function () {
          var me = "Worker " + this.name;
          if (this.previous) {
            return this.previous + " -> " + me;
          } else {
            return me;
          }
        }
      };

      module.exports = GenericWorker;

    }, {}], 29: [function (require, module, exports) {
      'use strict';

      var utils = require('../utils');
      var ConvertWorker = require('./ConvertWorker');
      var GenericWorker = require('./GenericWorker');
      var base64 = require('../base64');
      var support = require("../support");
      var external = require("../external");

      var NodejsStreamOutputAdapter = null;
      if (support.nodestream) {
        try {
          NodejsStreamOutputAdapter = require('../nodejs/NodejsStreamOutputAdapter');
        } catch (e) {
        }
      }

      /**
       * Apply the final transformation of the data. If the user wants a Blob for
       * example, it's easier to work with an U8intArray and finally do the
       * ArrayBuffer/Blob conversion.
       * @param {String} type the name of the final type
       * @param {String|Uint8Array|Buffer} content the content to transform
       * @param {String} mimeType the mime type of the content, if applicable.
       * @return {String|Uint8Array|ArrayBuffer|Buffer|Blob} the content in the right format.
       */
      function transformZipOutput(type, content, mimeType) {
        switch (type) {
          case "blob" :
            return utils.newBlob(utils.transformTo("arraybuffer", content), mimeType);
          case "base64" :
            return base64.encode(content);
          default :
            return utils.transformTo(type, content);
        }
      }

      /**
       * Concatenate an array of data of the given type.
       * @param {String} type the type of the data in the given array.
       * @param {Array} dataArray the array containing the data chunks to concatenate
       * @return {String|Uint8Array|Buffer} the concatenated data
       * @throws Error if the asked type is unsupported
       */
      function concat(type, dataArray) {
        var i, index = 0, res = null, totalLength = 0;
        for (i = 0; i < dataArray.length; i++) {
          totalLength += dataArray[i].length;
        }
        switch (type) {
          case "string":
            return dataArray.join("");
          case "array":
            return Array.prototype.concat.apply([], dataArray);
          case "uint8array":
            res = new Uint8Array(totalLength);
            for (i = 0; i < dataArray.length; i++) {
              res.set(dataArray[i], index);
              index += dataArray[i].length;
            }
            return res;
          case "nodebuffer":
            return Buffer.concat(dataArray);
          default:
            throw new Error("concat : unsupported type '" + type + "'");
        }
      }

      /**
       * Listen a StreamHelper, accumulate its content and concatenate it into a
       * complete block.
       * @param {StreamHelper} helper the helper to use.
       * @param {Function} updateCallback a callback called on each update. Called
       * with one arg :
       * - the metadata linked to the update received.
       * @return Promise the promise for the accumulation.
       */
      function accumulate(helper, updateCallback) {
        return new external.Promise(function (resolve, reject) {
          var dataArray = [];
          var chunkType = helper._internalType,
            resultType = helper._outputType,
            mimeType = helper._mimeType;
          helper
            .on('data', function (data, meta) {
              dataArray.push(data);
              if (updateCallback) {
                updateCallback(meta);
              }
            })
            .on('error', function (err) {
              dataArray = [];
              reject(err);
            })
            .on('end', function () {
              try {
                var result = transformZipOutput(resultType, concat(chunkType, dataArray), mimeType);
                resolve(result);
              } catch (e) {
                reject(e);
              }
              dataArray = [];
            })
            .resume();
        });
      }

      /**
       * An helper to easily use workers outside of JSZip.
       * @constructor
       * @param {Worker} worker the worker to wrap
       * @param {String} outputType the type of data expected by the use
       * @param {String} mimeType the mime type of the content, if applicable.
       */
      function StreamHelper(worker, outputType, mimeType) {
        var internalType = outputType;
        switch (outputType) {
          case "blob":
          case "arraybuffer":
            internalType = "uint8array";
            break;
          case "base64":
            internalType = "string";
            break;
        }

        try {
          // the type used internally
          this._internalType = internalType;
          // the type used to output results
          this._outputType = outputType;
          // the mime type
          this._mimeType = mimeType;
          utils.checkSupport(internalType);
          this._worker = worker.pipe(new ConvertWorker(internalType));
          // the last workers can be rewired without issues but we need to
          // prevent any updates on previous workers.
          worker.lock();
        } catch (e) {
          this._worker = new GenericWorker("error");
          this._worker.error(e);
        }
      }

      StreamHelper.prototype = {
        /**
         * Listen a StreamHelper, accumulate its content and concatenate it into a
         * complete block.
         * @param {Function} updateCb the update callback.
         * @return Promise the promise for the accumulation.
         */
        accumulate: function (updateCb) {
          return accumulate(this, updateCb);
        },
        /**
         * Add a listener on an event triggered on a stream.
         * @param {String} evt the name of the event
         * @param {Function} fn the listener
         * @return {StreamHelper} the current helper.
         */
        on: function (evt, fn) {
          var self = this;

          if (evt === "data") {
            this._worker.on(evt, function (chunk) {
              fn.call(self, chunk.data, chunk.meta);
            });
          } else {
            this._worker.on(evt, function () {
              utils.delay(fn, arguments, self);
            });
          }
          return this;
        },
        /**
         * Resume the flow of chunks.
         * @return {StreamHelper} the current helper.
         */
        resume: function () {
          utils.delay(this._worker.resume, [], this._worker);
          return this;
        },
        /**
         * Pause the flow of chunks.
         * @return {StreamHelper} the current helper.
         */
        pause: function () {
          this._worker.pause();
          return this;
        },
        /**
         * Return a nodejs stream for this helper.
         * @param {Function} updateCb the update callback.
         * @return {NodejsStreamOutputAdapter} the nodejs stream.
         */
        toNodejsStream: function (updateCb) {
          utils.checkSupport("nodestream");
          if (this._outputType !== "nodebuffer") {
            // an object stream containing blob/arraybuffer/uint8array/string
            // is strange and I don't know if it would be useful.
            // I you find this comment and have a good usecase, please open a
            // bug report !
            throw new Error(this._outputType + " is not supported by this method");
          }

          return new NodejsStreamOutputAdapter(this, {
            objectMode: this._outputType !== "nodebuffer"
          }, updateCb);
        }
      };


      module.exports = StreamHelper;

    }, { "../base64": 1, "../external": 6, "../nodejs/NodejsStreamOutputAdapter": 13, "../support": 30, "../utils": 32, "./ConvertWorker": 24, "./GenericWorker": 28 }], 30: [function (require, module, exports) {
      'use strict';

      exports.base64 = true;
      exports.array = true;
      exports.string = true;
      exports.arraybuffer = typeof ArrayBuffer !== "undefined" && typeof Uint8Array !== "undefined";
      exports.nodebuffer = typeof Buffer !== "undefined";
      // contains true if JSZip can read/generate Uint8Array, false otherwise.
      exports.uint8array = typeof Uint8Array !== "undefined";

      if (typeof ArrayBuffer === "undefined") {
        exports.blob = false;
      }
      else {
        var buffer = new ArrayBuffer(0);
        try {
          exports.blob = new Blob([buffer], {
            type: "application/zip"
          }).size === 0;
        }
        catch (e) {
          try {
            var Builder = self.BlobBuilder || self.WebKitBlobBuilder || self.MozBlobBuilder || self.MSBlobBuilder;
            var builder = new Builder();
            builder.append(buffer);
            exports.blob = builder.getBlob('application/zip').size === 0;
          }
          catch (e) {
            exports.blob = false;
          }
        }
      }

      try {
        exports.nodestream = !!require('readable-stream').Readable;
      } catch (e) {
        exports.nodestream = false;
      }

    }, { "readable-stream": 16 }], 31: [function (require, module, exports) {
      'use strict';

      var utils = require('./utils');
      var support = require('./support');
      var nodejsUtils = require('./nodejsUtils');
      var GenericWorker = require('./stream/GenericWorker');

      /**
       * The following functions come from pako, from pako/lib/utils/strings
       * released under the MIT license, see pako https://github.com/nodeca/pako/
       */

        // Table with utf8 lengths (calculated by first byte of sequence)
        // Note, that 5 & 6-byte values and some 4-byte values can not be represented in JS,
        // because max possible codepoint is 0x10ffff
      var _utf8len = new Array(256);
      for (var i = 0; i < 256; i++) {
        _utf8len[i] = (i >= 252 ? 6 : i >= 248 ? 5 : i >= 240 ? 4 : i >= 224 ? 3 : i >= 192 ? 2 : 1);
      }
      _utf8len[254] = _utf8len[254] = 1; // Invalid sequence start

      // convert string to array (typed, when possible)
      var string2buf = function (str) {
        var buf, c, c2, m_pos, i, str_len = str.length, buf_len = 0;

        // count binary size
        for (m_pos = 0; m_pos < str_len; m_pos++) {
          c = str.charCodeAt(m_pos);
          if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) {
            c2 = str.charCodeAt(m_pos + 1);
            if ((c2 & 0xfc00) === 0xdc00) {
              c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
              m_pos++;
            }
          }
          buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4;
        }

        // allocate buffer
        if (support.uint8array) {
          buf = new Uint8Array(buf_len);
        } else {
          buf = new Array(buf_len);
        }

        // convert
        for (i = 0, m_pos = 0; i < buf_len; m_pos++) {
          c = str.charCodeAt(m_pos);
          if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) {
            c2 = str.charCodeAt(m_pos + 1);
            if ((c2 & 0xfc00) === 0xdc00) {
              c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00);
              m_pos++;
            }
          }
          if (c < 0x80) {
            /* one byte */
            buf[i++] = c;
          } else if (c < 0x800) {
            /* two bytes */
            buf[i++] = 0xC0 | (c >>> 6);
            buf[i++] = 0x80 | (c & 0x3f);
          } else if (c < 0x10000) {
            /* three bytes */
            buf[i++] = 0xE0 | (c >>> 12);
            buf[i++] = 0x80 | (c >>> 6 & 0x3f);
            buf[i++] = 0x80 | (c & 0x3f);
          } else {
            /* four bytes */
            buf[i++] = 0xf0 | (c >>> 18);
            buf[i++] = 0x80 | (c >>> 12 & 0x3f);
            buf[i++] = 0x80 | (c >>> 6 & 0x3f);
            buf[i++] = 0x80 | (c & 0x3f);
          }
        }

        return buf;
      };

      // Calculate max possible position in utf8 buffer,
      // that will not break sequence. If that's not possible
      // - (very small limits) return max size as is.
      //
      // buf[] - utf8 bytes array
      // max   - length limit (mandatory);
      var utf8border = function (buf, max) {
        var pos;

        max = max || buf.length;
        if (max > buf.length) {
          max = buf.length;
        }

        // go back from last position, until start of sequence found
        pos = max - 1;
        while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) {
          pos--;
        }

        // Fuckup - very small and broken sequence,
        // return max, because we should return something anyway.
        if (pos < 0) {
          return max;
        }

        // If we came to start of buffer - that means vuffer is too small,
        // return max too.
        if (pos === 0) {
          return max;
        }

        return (pos + _utf8len[buf[pos]] > max) ? pos : max;
      };

      // convert array to string
      var buf2string = function (buf) {
        var str, i, out, c, c_len;
        var len = buf.length;

        // Reserve max possible length (2 words per char)
        // NB: by unknown reasons, Array is significantly faster for
        //     String.fromCharCode.apply than Uint16Array.
        var utf16buf = new Array(len * 2);

        for (out = 0, i = 0; i < len;) {
          c = buf[i++];
          // quick process ascii
          if (c < 0x80) {
            utf16buf[out++] = c;
            continue;
          }

          c_len = _utf8len[c];
          // skip 5 & 6 byte codes
          if (c_len > 4) {
            utf16buf[out++] = 0xfffd;
            i += c_len - 1;
            continue;
          }

          // apply mask on first byte
          c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07;
          // join the rest
          while (c_len > 1 && i < len) {
            c = (c << 6) | (buf[i++] & 0x3f);
            c_len--;
          }

          // terminated by end of string?
          if (c_len > 1) {
            utf16buf[out++] = 0xfffd;
            continue;
          }

          if (c < 0x10000) {
            utf16buf[out++] = c;
          } else {
            c -= 0x10000;
            utf16buf[out++] = 0xd800 | ((c >> 10) & 0x3ff);
            utf16buf[out++] = 0xdc00 | (c & 0x3ff);
          }
        }

        // shrinkBuf(utf16buf, out)
        if (utf16buf.length !== out) {
          if (utf16buf.subarray) {
            utf16buf = utf16buf.subarray(0, out);
          } else {
            utf16buf.length = out;
          }
        }

        // return String.fromCharCode.apply(null, utf16buf);
        return utils.applyFromCharCode(utf16buf);
      };


      // That's all for the pako functions.


      /**
       * Transform a javascript string into an array (typed if possible) of bytes,
       * UTF-8 encoded.
       * @param {String} str the string to encode
       * @return {Array|Uint8Array|Buffer} the UTF-8 encoded string.
       */
      exports.utf8encode = function utf8encode(str) {
        if (support.nodebuffer) {
          return nodejsUtils.newBufferFrom(str, "utf-8");
        }

        return string2buf(str);
      };


      /**
       * Transform a bytes array (or a representation) representing an UTF-8 encoded
       * string into a javascript string.
       * @param {Array|Uint8Array|Buffer} buf the data de decode
       * @return {String} the decoded string.
       */
      exports.utf8decode = function utf8decode(buf) {
        if (support.nodebuffer) {
          return utils.transformTo("nodebuffer", buf).toString("utf-8");
        }

        buf = utils.transformTo(support.uint8array ? "uint8array" : "array", buf);

        return buf2string(buf);
      };

      /**
       * A worker to decode utf8 encoded binary chunks into string chunks.
       * @constructor
       */
      function Utf8DecodeWorker() {
        GenericWorker.call(this, "utf-8 decode");
        // the last bytes if a chunk didn't end with a complete codepoint.
        this.leftOver = null;
      }

      utils.inherits(Utf8DecodeWorker, GenericWorker);

      /**
       * @see GenericWorker.processChunk
       */
      Utf8DecodeWorker.prototype.processChunk = function (chunk) {

        var data = utils.transformTo(support.uint8array ? "uint8array" : "array", chunk.data);

        // 1st step, re-use what's left of the previous chunk
        if (this.leftOver && this.leftOver.length) {
          if (support.uint8array) {
            var previousData = data;
            data = new Uint8Array(previousData.length + this.leftOver.length);
            data.set(this.leftOver, 0);
            data.set(previousData, this.leftOver.length);
          } else {
            data = this.leftOver.concat(data);
          }
          this.leftOver = null;
        }

        var nextBoundary = utf8border(data);
        var usableData = data;
        if (nextBoundary !== data.length) {
          if (support.uint8array) {
            usableData = data.subarray(0, nextBoundary);
            this.leftOver = data.subarray(nextBoundary, data.length);
          } else {
            usableData = data.slice(0, nextBoundary);
            this.leftOver = data.slice(nextBoundary, data.length);
          }
        }

        this.push({
          data: exports.utf8decode(usableData),
          meta: chunk.meta
        });
      };

      /**
       * @see GenericWorker.flush
       */
      Utf8DecodeWorker.prototype.flush = function () {
        if (this.leftOver && this.leftOver.length) {
          this.push({
            data: exports.utf8decode(this.leftOver),
            meta: {}
          });
          this.leftOver = null;
        }
      };
      exports.Utf8DecodeWorker = Utf8DecodeWorker;

      /**
       * A worker to endcode string chunks into utf8 encoded binary chunks.
       * @constructor
       */
      function Utf8EncodeWorker() {
        GenericWorker.call(this, "utf-8 encode");
      }

      utils.inherits(Utf8EncodeWorker, GenericWorker);

      /**
       * @see GenericWorker.processChunk
       */
      Utf8EncodeWorker.prototype.processChunk = function (chunk) {
        this.push({
          data: exports.utf8encode(chunk.data),
          meta: chunk.meta
        });
      };
      exports.Utf8EncodeWorker = Utf8EncodeWorker;

    }, { "./nodejsUtils": 14, "./stream/GenericWorker": 28, "./support": 30, "./utils": 32 }], 32: [function (require, module, exports) {
      'use strict';

      var support = require('./support');
      var base64 = require('./base64');
      var nodejsUtils = require('./nodejsUtils');
      var setImmediate = require('set-immediate-shim');
      var external = require("./external");


      /**
       * Convert a string that pass as a "binary string": it should represent a byte
       * array but may have > 255 char codes. Be sure to take only the first byte
       * and returns the byte array.
       * @param {String} str the string to transform.
       * @return {Array|Uint8Array} the string in a binary format.
       */
      function string2binary(str) {
        var result = null;
        if (support.uint8array) {
          result = new Uint8Array(str.length);
        } else {
          result = new Array(str.length);
        }
        return stringToArrayLike(str, result);
      }

      /**
       * Create a new blob with the given content and the given type.
       * @param {String|ArrayBuffer} part the content to put in the blob. DO NOT use
       * an Uint8Array because the stock browser of android 4 won't accept it (it
       * will be silently converted to a string, "[object Uint8Array]").
       *
       * Use only ONE part to build the blob to avoid a memory leak in IE11 / Edge:
       * when a large amount of Array is used to create the Blob, the amount of
       * memory consumed is nearly 100 times the original data amount.
       *
       * @param {String} type the mime type of the blob.
       * @return {Blob} the created blob.
       */
      exports.newBlob = function (part, type) {
        exports.checkSupport("blob");

        try {
          // Blob constructor
          return new Blob([part], {
            type: type
          });
        }
        catch (e) {

          try {
            // deprecated, browser only, old way
            var Builder = self.BlobBuilder || self.WebKitBlobBuilder || self.MozBlobBuilder || self.MSBlobBuilder;
            var builder = new Builder();
            builder.append(part);
            return builder.getBlob(type);
          }
          catch (e) {

            // well, fuck ?!
            throw new Error("Bug : can't construct the Blob.");
          }
        }


      };

      /**
       * The identity function.
       * @param {Object} input the input.
       * @return {Object} the same input.
       */
      function identity(input) {
        return input;
      }

      /**
       * Fill in an array with a string.
       * @param {String} str the string to use.
       * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to fill in (will be mutated).
       * @return {Array|ArrayBuffer|Uint8Array|Buffer} the updated array.
       */
      function stringToArrayLike(str, array) {
        for (var i = 0; i < str.length; ++i) {
          array[i] = str.charCodeAt(i) & 0xFF;
        }
        return array;
      }

      /**
       * An helper for the function arrayLikeToString.
       * This contains static information and functions that
       * can be optimized by the browser JIT compiler.
       */
      var arrayToStringHelper = {
        /**
         * Transform an array of int into a string, chunk by chunk.
         * See the performances notes on arrayLikeToString.
         * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform.
         * @param {String} type the type of the array.
         * @param {Integer} chunk the chunk size.
         * @return {String} the resulting string.
         * @throws Error if the chunk is too big for the stack.
         */
        stringifyByChunk: function (array, type, chunk) {
          var result = [], k = 0, len = array.length;
          // shortcut
          if (len <= chunk) {
            return String.fromCharCode.apply(null, array);
          }
          while (k < len) {
            if (type === "array" || type === "nodebuffer") {
              result.push(String.fromCharCode.apply(null, array.slice(k, Math.min(k + chunk, len))));
            }
            else {
              result.push(String.fromCharCode.apply(null, array.subarray(k, Math.min(k + chunk, len))));
            }
            k += chunk;
          }
          return result.join("");
        },
        /**
         * Call String.fromCharCode on every item in the array.
         * This is the naive implementation, which generate A LOT of intermediate string.
         * This should be used when everything else fail.
         * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform.
         * @return {String} the result.
         */
        stringifyByChar: function (array) {
          var resultStr = "";
          for (var i = 0; i < array.length; i++) {
            resultStr += String.fromCharCode(array[i]);
          }
          return resultStr;
        },
        applyCanBeUsed: {
          /**
           * true if the browser accepts to use String.fromCharCode on Uint8Array
           */
          uint8array: (function () {
            try {
              return support.uint8array && String.fromCharCode.apply(null, new Uint8Array(1)).length === 1;
            } catch (e) {
              return false;
            }
          })(),
          /**
           * true if the browser accepts to use String.fromCharCode on nodejs Buffer.
           */
          nodebuffer: (function () {
            try {
              return support.nodebuffer && String.fromCharCode.apply(null, nodejsUtils.allocBuffer(1)).length === 1;
            } catch (e) {
              return false;
            }
          })()
        }
      };

      /**
       * Transform an array-like object to a string.
       * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform.
       * @return {String} the result.
       */
      function arrayLikeToString(array) {
        // Performances notes :
        // --------------------
        // String.fromCharCode.apply(null, array) is the fastest, see
        // see http://jsperf.com/converting-a-uint8array-to-a-string/2
        // but the stack is limited (and we can get huge arrays !).
        //
        // result += String.fromCharCode(array[i]); generate too many strings !
        //
        // This code is inspired by http://jsperf.com/arraybuffer-to-string-apply-performance/2
        // TODO : we now have workers that split the work. Do we still need that ?
        var chunk = 65536,
          type = exports.getTypeOf(array),
          canUseApply = true;
        if (type === "uint8array") {
          canUseApply = arrayToStringHelper.applyCanBeUsed.uint8array;
        } else if (type === "nodebuffer") {
          canUseApply = arrayToStringHelper.applyCanBeUsed.nodebuffer;
        }

        if (canUseApply) {
          while (chunk > 1) {
            try {
              return arrayToStringHelper.stringifyByChunk(array, type, chunk);
            } catch (e) {
              chunk = Math.floor(chunk / 2);
            }
          }
        }

        // no apply or chunk error : slow and painful algorithm
        // default browser on android 4.*
        return arrayToStringHelper.stringifyByChar(array);
      }

      exports.applyFromCharCode = arrayLikeToString;


      /**
       * Copy the data from an array-like to an other array-like.
       * @param {Array|ArrayBuffer|Uint8Array|Buffer} arrayFrom the origin array.
       * @param {Array|ArrayBuffer|Uint8Array|Buffer} arrayTo the destination array which will be mutated.
       * @return {Array|ArrayBuffer|Uint8Array|Buffer} the updated destination array.
       */
      function arrayLikeToArrayLike(arrayFrom, arrayTo) {
        for (var i = 0; i < arrayFrom.length; i++) {
          arrayTo[i] = arrayFrom[i];
        }
        return arrayTo;
      }

      // a matrix containing functions to transform everything into everything.
      var transform = {};

      // string to ?
      transform["string"] = {
        "string": identity,
        "array": function (input) {
          return stringToArrayLike(input, new Array(input.length));
        },
        "arraybuffer": function (input) {
          return transform["string"]["uint8array"](input).buffer;
        },
        "uint8array": function (input) {
          return stringToArrayLike(input, new Uint8Array(input.length));
        },
        "nodebuffer": function (input) {
          return stringToArrayLike(input, nodejsUtils.allocBuffer(input.length));
        }
      };

      // array to ?
      transform["array"] = {
        "string": arrayLikeToString,
        "array": identity,
        "arraybuffer": function (input) {
          return (new Uint8Array(input)).buffer;
        },
        "uint8array": function (input) {
          return new Uint8Array(input);
        },
        "nodebuffer": function (input) {
          return nodejsUtils.newBufferFrom(input);
        }
      };

      // arraybuffer to ?
      transform["arraybuffer"] = {
        "string": function (input) {
          return arrayLikeToString(new Uint8Array(input));
        },
        "array": function (input) {
          return arrayLikeToArrayLike(new Uint8Array(input), new Array(input.byteLength));
        },
        "arraybuffer": identity,
        "uint8array": function (input) {
          return new Uint8Array(input);
        },
        "nodebuffer": function (input) {
          return nodejsUtils.newBufferFrom(new Uint8Array(input));
        }
      };

      // uint8array to ?
      transform["uint8array"] = {
        "string": arrayLikeToString,
        "array": function (input) {
          return arrayLikeToArrayLike(input, new Array(input.length));
        },
        "arraybuffer": function (input) {
          return input.buffer;
        },
        "uint8array": identity,
        "nodebuffer": function (input) {
          return nodejsUtils.newBufferFrom(input);
        }
      };

      // nodebuffer to ?
      transform["nodebuffer"] = {
        "string": arrayLikeToString,
        "array": function (input) {
          return arrayLikeToArrayLike(input, new Array(input.length));
        },
        "arraybuffer": function (input) {
          return transform["nodebuffer"]["uint8array"](input).buffer;
        },
        "uint8array": function (input) {
          return arrayLikeToArrayLike(input, new Uint8Array(input.length));
        },
        "nodebuffer": identity
      };

      /**
       * Transform an input into any type.
       * The supported output type are : string, array, uint8array, arraybuffer, nodebuffer.
       * If no output type is specified, the unmodified input will be returned.
       * @param {String} outputType the output type.
       * @param {String|Array|ArrayBuffer|Uint8Array|Buffer} input the input to convert.
       * @throws {Error} an Error if the browser doesn't support the requested output type.
       */
      exports.transformTo = function (outputType, input) {
        if (!input) {
          // undefined, null, etc
          // an empty string won't harm.
          input = "";
        }
        if (!outputType) {
          return input;
        }
        exports.checkSupport(outputType);
        var inputType = exports.getTypeOf(input);
        var result = transform[inputType][outputType](input);
        return result;
      };

      /**
       * Return the type of the input.
       * The type will be in a format valid for JSZip.utils.transformTo : string, array, uint8array, arraybuffer.
       * @param {Object} input the input to identify.
       * @return {String} the (lowercase) type of the input.
       */
      exports.getTypeOf = function (input) {
        if (typeof input === "string") {
          return "string";
        }
        if (Object.prototype.toString.call(input) === "[object Array]") {
          return "array";
        }
        if (support.nodebuffer && nodejsUtils.isBuffer(input)) {
          return "nodebuffer";
        }
        if (support.uint8array && input instanceof Uint8Array) {
          return "uint8array";
        }
        if (support.arraybuffer && input instanceof ArrayBuffer) {
          return "arraybuffer";
        }
      };

      /**
       * Throw an exception if the type is not supported.
       * @param {String} type the type to check.
       * @throws {Error} an Error if the browser doesn't support the requested type.
       */
      exports.checkSupport = function (type) {
        var supported = support[type.toLowerCase()];
        if (!supported) {
          throw new Error(type + " is not supported by this platform");
        }
      };

      exports.MAX_VALUE_16BITS = 65535;
      exports.MAX_VALUE_32BITS = -1; // well, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" is parsed as -1

      /**
       * Prettify a string read as binary.
       * @param {string} str the string to prettify.
       * @return {string} a pretty string.
       */
      exports.pretty = function (str) {
        var res = '',
          code, i;
        for (i = 0; i < (str || "").length; i++) {
          code = str.charCodeAt(i);
          res += '\\x' + (code < 16 ? "0" : "") + code.toString(16).toUpperCase();
        }
        return res;
      };

      /**
       * Defer the call of a function.
       * @param {Function} callback the function to call asynchronously.
       * @param {Array} args the arguments to give to the callback.
       */
      exports.delay = function (callback, args, self) {
        setImmediate(function () {
          callback.apply(self || null, args || []);
        });
      };

      /**
       * Extends a prototype with an other, without calling a constructor with
       * side effects. Inspired by nodejs' `utils.inherits`
       * @param {Function} ctor the constructor to augment
       * @param {Function} superCtor the parent constructor to use
       */
      exports.inherits = function (ctor, superCtor) {
        var Obj = function () {};
        Obj.prototype = superCtor.prototype;
        ctor.prototype = new Obj();
      };

      /**
       * Merge the objects passed as parameters into a new one.
       * @private
       * @param {...Object} var_args All objects to merge.
       * @return {Object} a new object with the data of the others.
       */
      exports.extend = function () {
        var result = {}, i, attr;
        for (i = 0; i < arguments.length; i++) { // arguments is not enumerable in some browsers
          for (attr in arguments[i]) {
            if (arguments[i].hasOwnProperty(attr) && typeof result[attr] === "undefined") {
              result[attr] = arguments[i][attr];
            }
          }
        }
        return result;
      };

      /**
       * Transform arbitrary content into a Promise.
       * @param {String} name a name for the content being processed.
       * @param {Object} inputData the content to process.
       * @param {Boolean} isBinary true if the content is not an unicode string
       * @param {Boolean} isOptimizedBinaryString true if the string content only has one byte per character.
       * @param {Boolean} isBase64 true if the string content is encoded with base64.
       * @return {Promise} a promise in a format usable by JSZip.
       */
      exports.prepareContent = function (name, inputData, isBinary, isOptimizedBinaryString, isBase64) {

        // if inputData is already a promise, this flatten it.
        var promise = external.Promise.resolve(inputData).then(function (data) {


          var isBlob = support.blob && (data instanceof Blob || ['[object File]', '[object Blob]'].indexOf(Object.prototype.toString.call(data)) !== -1);

          if (isBlob && typeof FileReader !== "undefined") {
            return new external.Promise(function (resolve, reject) {
              var reader = new FileReader();

              reader.onload = function (e) {
                resolve(e.target.result);
              };
              reader.onerror = function (e) {
                reject(e.target.error);
              };
              reader.readAsArrayBuffer(data);
            });
          } else {
            return data;
          }
        });

        return promise.then(function (data) {
          var dataType = exports.getTypeOf(data);

          if (!dataType) {
            return external.Promise.reject(
              new Error("Can't read the data of '" + name + "'. Is it " +
                "in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?")
            );
          }
          // special case : it's way easier to work with Uint8Array than with ArrayBuffer
          if (dataType === "arraybuffer") {
            data = exports.transformTo("uint8array", data);
          } else if (dataType === "string") {
            if (isBase64) {
              data = base64.decode(data);
            }
            else if (isBinary) {
              // optimizedBinaryString === true means that the file has already been filtered with a 0xFF mask
              if (isOptimizedBinaryString !== true) {
                // this is a string, not in a base64 format.
                // Be sure that this is a correct "binary string"
                data = string2binary(data);
              }
            }
          }
          return data;
        });
      };

    }, { "./base64": 1, "./external": 6, "./nodejsUtils": 14, "./support": 30, "set-immediate-shim": 54 }], 33: [function (require, module, exports) {
      'use strict';
      var readerFor = require('./reader/readerFor');
      var utils = require('./utils');
      var sig = require('./signature');
      var ZipEntry = require('./zipEntry');
      var utf8 = require('./utf8');
      var support = require('./support');

      //  class ZipEntries {{{
      /**
       * All the entries in the zip file.
       * @constructor
       * @param {Object} loadOptions Options for loading the stream.
       */
      function ZipEntries(loadOptions) {
        this.files = [];
        this.loadOptions = loadOptions;
      }

      ZipEntries.prototype = {
        /**
         * Check that the reader is on the specified signature.
         * @param {string} expectedSignature the expected signature.
         * @throws {Error} if it is an other signature.
         */
        checkSignature: function (expectedSignature) {
          if (!this.reader.readAndCheckSignature(expectedSignature)) {
            this.reader.index -= 4;
            var signature = this.reader.readString(4);
            throw new Error("Corrupted zip or bug: unexpected signature " + "(" + utils.pretty(signature) + ", expected " + utils.pretty(expectedSignature) + ")");
          }
        },
        /**
         * Check if the given signature is at the given index.
         * @param {number} askedIndex the index to check.
         * @param {string} expectedSignature the signature to expect.
         * @return {boolean} true if the signature is here, false otherwise.
         */
        isSignature: function (askedIndex, expectedSignature) {
          var currentIndex = this.reader.index;
          this.reader.setIndex(askedIndex);
          var signature = this.reader.readString(4);
          var result = signature === expectedSignature;
          this.reader.setIndex(currentIndex);
          return result;
        },
        /**
         * Read the end of the central directory.
         */
        readBlockEndOfCentral: function () {
          this.diskNumber = this.reader.readInt(2);
          this.diskWithCentralDirStart = this.reader.readInt(2);
          this.centralDirRecordsOnThisDisk = this.reader.readInt(2);
          this.centralDirRecords = this.reader.readInt(2);
          this.centralDirSize = this.reader.readInt(4);
          this.centralDirOffset = this.reader.readInt(4);

          this.zipCommentLength = this.reader.readInt(2);
          // warning : the encoding depends of the system locale
          // On a linux machine with LANG=en_US.utf8, this field is utf8 encoded.
          // On a windows machine, this field is encoded with the localized windows code page.
          var zipComment = this.reader.readData(this.zipCommentLength);
          var decodeParamType = support.uint8array ? "uint8array" : "array";
          // To get consistent behavior with the generation part, we will assume that
          // this is utf8 encoded unless specified otherwise.
          var decodeContent = utils.transformTo(decodeParamType, zipComment);
          this.zipComment = this.loadOptions.decodeFileName(decodeContent);
        },
        /**
         * Read the end of the Zip 64 central directory.
         * Not merged with the method readEndOfCentral :
         * The end of central can coexist with its Zip64 brother,
         * I don't want to read the wrong number of bytes !
         */
        readBlockZip64EndOfCentral: function () {
          this.zip64EndOfCentralSize = this.reader.readInt(8);
          this.reader.skip(4);
          // this.versionMadeBy = this.reader.readString(2);
          // this.versionNeeded = this.reader.readInt(2);
          this.diskNumber = this.reader.readInt(4);
          this.diskWithCentralDirStart = this.reader.readInt(4);
          this.centralDirRecordsOnThisDisk = this.reader.readInt(8);
          this.centralDirRecords = this.reader.readInt(8);
          this.centralDirSize = this.reader.readInt(8);
          this.centralDirOffset = this.reader.readInt(8);

          this.zip64ExtensibleData = {};
          var extraDataSize = this.zip64EndOfCentralSize - 44,
            index = 0,
            extraFieldId,
            extraFieldLength,
            extraFieldValue;
          while (index < extraDataSize) {
            extraFieldId = this.reader.readInt(2);
            extraFieldLength = this.reader.readInt(4);
            extraFieldValue = this.reader.readData(extraFieldLength);
            this.zip64ExtensibleData[extraFieldId] = {
              id: extraFieldId,
              length: extraFieldLength,
              value: extraFieldValue
            };
          }
        },
        /**
         * Read the end of the Zip 64 central directory locator.
         */
        readBlockZip64EndOfCentralLocator: function () {
          this.diskWithZip64CentralDirStart = this.reader.readInt(4);
          this.relativeOffsetEndOfZip64CentralDir = this.reader.readInt(8);
          this.disksCount = this.reader.readInt(4);
          if (this.disksCount > 1) {
            throw new Error("Multi-volumes zip are not supported");
          }
        },
        /**
         * Read the local files, based on the offset read in the central part.
         */
        readLocalFiles: function () {
          var i, file;
          for (i = 0; i < this.files.length; i++) {
            file = this.files[i];
            this.reader.setIndex(file.localHeaderOffset);
            this.checkSignature(sig.LOCAL_FILE_HEADER);
            file.readLocalPart(this.reader);
            file.handleUTF8();
            file.processAttributes();
          }
        },
        /**
         * Read the central directory.
         */
        readCentralDir: function () {
          var file;

          this.reader.setIndex(this.centralDirOffset);
          while (this.reader.readAndCheckSignature(sig.CENTRAL_FILE_HEADER)) {
            file = new ZipEntry({
              zip64: this.zip64
            }, this.loadOptions);
            file.readCentralPart(this.reader);
            this.files.push(file);
          }

          if (this.centralDirRecords !== this.files.length) {
            if (this.centralDirRecords !== 0 && this.files.length === 0) {
              // We expected some records but couldn't find ANY.
              // This is really suspicious, as if something went wrong.
              throw new Error("Corrupted zip or bug: expected " + this.centralDirRecords + " records in central dir, got " + this.files.length);
            } else {
              // We found some records but not all.
              // Something is wrong but we got something for the user: no error here.
              // console.warn("expected", this.centralDirRecords, "records in central dir, got", this.files.length);
            }
          }
        },
        /**
         * Read the end of central directory.
         */
        readEndOfCentral: function () {
          var offset = this.reader.lastIndexOfSignature(sig.CENTRAL_DIRECTORY_END);
          if (offset < 0) {
            // Check if the content is a truncated zip or complete garbage.
            // A "LOCAL_FILE_HEADER" is not required at the beginning (auto
            // extractible zip for example) but it can give a good hint.
            // If an ajax request was used without responseType, we will also
            // get unreadable data.
            var isGarbage = !this.isSignature(0, sig.LOCAL_FILE_HEADER);

            if (isGarbage) {
              throw new Error("Can't find end of central directory : is this a zip file ? " +
                "If it is, see https://stuk.github.io/jszip/documentation/howto/read_zip.html");
            } else {
              throw new Error("Corrupted zip: can't find end of central directory");
            }

          }
          this.reader.setIndex(offset);
          var endOfCentralDirOffset = offset;
          this.checkSignature(sig.CENTRAL_DIRECTORY_END);
          this.readBlockEndOfCentral();


          /* extract from the zip spec :
            4)  If one of the fields in the end of central directory
                record is too small to hold required data, the field
                should be set to -1 (0xFFFF or 0xFFFFFFFF) and the
                ZIP64 format record should be created.
            5)  The end of central directory record and the
                Zip64 end of central directory locator record must
                reside on the same disk when splitting or spanning
                an archive.
         */
          if (this.diskNumber === utils.MAX_VALUE_16BITS || this.diskWithCentralDirStart === utils.MAX_VALUE_16BITS || this.centralDirRecordsOnThisDisk === utils.MAX_VALUE_16BITS || this.centralDirRecords === utils.MAX_VALUE_16BITS || this.centralDirSize === utils.MAX_VALUE_32BITS || this.centralDirOffset === utils.MAX_VALUE_32BITS) {
            this.zip64 = true;

            /*
            Warning : the zip64 extension is supported, but ONLY if the 64bits integer read from
            the zip file can fit into a 32bits integer. This cannot be solved : JavaScript represents
            all numbers as 64-bit double precision IEEE 754 floating point numbers.
            So, we have 53bits for integers and bitwise operations treat everything as 32bits.
            see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Operators/Bitwise_Operators
            and http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-262.pdf section 8.5
            */

            // should look for a zip64 EOCD locator
            offset = this.reader.lastIndexOfSignature(sig.ZIP64_CENTRAL_DIRECTORY_LOCATOR);
            if (offset < 0) {
              throw new Error("Corrupted zip: can't find the ZIP64 end of central directory locator");
            }
            this.reader.setIndex(offset);
            this.checkSignature(sig.ZIP64_CENTRAL_DIRECTORY_LOCATOR);
            this.readBlockZip64EndOfCentralLocator();

            // now the zip64 EOCD record
            if (!this.isSignature(this.relativeOffsetEndOfZip64CentralDir, sig.ZIP64_CENTRAL_DIRECTORY_END)) {
              // console.warn("ZIP64 end of central directory not where expected.");
              this.relativeOffsetEndOfZip64CentralDir = this.reader.lastIndexOfSignature(sig.ZIP64_CENTRAL_DIRECTORY_END);
              if (this.relativeOffsetEndOfZip64CentralDir < 0) {
                throw new Error("Corrupted zip: can't find the ZIP64 end of central directory");
              }
            }
            this.reader.setIndex(this.relativeOffsetEndOfZip64CentralDir);
            this.checkSignature(sig.ZIP64_CENTRAL_DIRECTORY_END);
            this.readBlockZip64EndOfCentral();
          }

          var expectedEndOfCentralDirOffset = this.centralDirOffset + this.centralDirSize;
          if (this.zip64) {
            expectedEndOfCentralDirOffset += 20; // end of central dir 64 locator
            expectedEndOfCentralDirOffset += 12 /* should not include the leading 12 bytes */ + this.zip64EndOfCentralSize;
          }

          var extraBytes = endOfCentralDirOffset - expectedEndOfCentralDirOffset;

          if (extraBytes > 0) {
            // console.warn(extraBytes, "extra bytes at beginning or within zipfile");
            if (this.isSignature(endOfCentralDirOffset, sig.CENTRAL_FILE_HEADER)) {
              // The offsets seem wrong, but we have something at the specified offset.
              // So… we keep it.
            } else {
              // the offset is wrong, update the "zero" of the reader
              // this happens if data has been prepended (crx files for example)
              this.reader.zero = extraBytes;
            }
          } else if (extraBytes < 0) {
            throw new Error("Corrupted zip: missing " + Math.abs(extraBytes) + " bytes.");
          }
        },
        prepareReader: function (data) {
          this.reader = readerFor(data);
        },
        /**
         * Read a zip file and create ZipEntries.
         * @param {String|ArrayBuffer|Uint8Array|Buffer} data the binary string representing a zip file.
         */
        load: function (data) {
          this.prepareReader(data);
          this.readEndOfCentral();
          this.readCentralDir();
          this.readLocalFiles();
        }
      };
      // }}} end of ZipEntries
      module.exports = ZipEntries;

    }, { "./reader/readerFor": 22, "./signature": 23, "./support": 30, "./utf8": 31, "./utils": 32, "./zipEntry": 34 }], 34: [function (require, module, exports) {
      'use strict';
      var readerFor = require('./reader/readerFor');
      var utils = require('./utils');
      var CompressedObject = require('./compressedObject');
      var crc32fn = require('./crc32');
      var utf8 = require('./utf8');
      var compressions = require('./compressions');
      var support = require('./support');

      var MADE_BY_DOS = 0x00;
      var MADE_BY_UNIX = 0x03;

      /**
       * Find a compression registered in JSZip.
       * @param {string} compressionMethod the method magic to find.
       * @return {Object|null} the JSZip compression object, null if none found.
       */
      var findCompression = function (compressionMethod) {
        for (var method in compressions) {
          if (!compressions.hasOwnProperty(method)) {
            continue;
          }
          if (compressions[method].magic === compressionMethod) {
            return compressions[method];
          }
        }
        return null;
      };

      // class ZipEntry {{{
      /**
       * An entry in the zip file.
       * @constructor
       * @param {Object} options Options of the current file.
       * @param {Object} loadOptions Options for loading the stream.
       */
      function ZipEntry(options, loadOptions) {
        this.options = options;
        this.loadOptions = loadOptions;
      }

      ZipEntry.prototype = {
        /**
         * say if the file is encrypted.
         * @return {boolean} true if the file is encrypted, false otherwise.
         */
        isEncrypted: function () {
          // bit 1 is set
          return (this.bitFlag & 0x0001) === 0x0001;
        },
        /**
         * say if the file has utf-8 filename/comment.
         * @return {boolean} true if the filename/comment is in utf-8, false otherwise.
         */
        useUTF8: function () {
          // bit 11 is set
          return (this.bitFlag & 0x0800) === 0x0800;
        },
        /**
         * Read the local part of a zip file and add the info in this object.
         * @param {DataReader} reader the reader to use.
         */
        readLocalPart: function (reader) {
          var compression, localExtraFieldsLength;

          // we already know everything from the central dir !
          // If the central dir data are false, we are doomed.
          // On the bright side, the local part is scary  : zip64, data descriptors, both, etc.
          // The less data we get here, the more reliable this should be.
          // Let's skip the whole header and dash to the data !
          reader.skip(22);
          // in some zip created on windows, the filename stored in the central dir contains \ instead of /.
          // Strangely, the filename here is OK.
          // I would love to treat these zip files as corrupted (see http://www.info-zip.org/FAQ.html#backslashes
          // or APPNOTE#4.4.17.1, "All slashes MUST be forward slashes '/'") but there are a lot of bad zip generators...
          // Search "unzip mismatching "local" filename continuing with "central" filename version" on
          // the internet.
          //
          // I think I see the logic here : the central directory is used to display
          // content and the local directory is used to extract the files. Mixing / and \
          // may be used to display \ to windows users and use / when extracting the files.
          // Unfortunately, this lead also to some issues : http://seclists.org/fulldisclosure/2009/Sep/394
          this.fileNameLength = reader.readInt(2);
          localExtraFieldsLength = reader.readInt(2); // can't be sure this will be the same as the central dir
          // the fileName is stored as binary data, the handleUTF8 method will take care of the encoding.
          this.fileName = reader.readData(this.fileNameLength);
          reader.skip(localExtraFieldsLength);

          if (this.compressedSize === -1 || this.uncompressedSize === -1) {
            throw new Error("Bug or corrupted zip : didn't get enough information from the central directory " + "(compressedSize === -1 || uncompressedSize === -1)");
          }

          compression = findCompression(this.compressionMethod);
          if (compression === null) { // no compression found
            throw new Error("Corrupted zip : compression " + utils.pretty(this.compressionMethod) + " unknown (inner file : " + utils.transformTo("string", this.fileName) + ")");
          }
          this.decompressed = new CompressedObject(this.compressedSize, this.uncompressedSize, this.crc32, compression, reader.readData(this.compressedSize));
        },

        /**
         * Read the central part of a zip file and add the info in this object.
         * @param {DataReader} reader the reader to use.
         */
        readCentralPart: function (reader) {
          this.versionMadeBy = reader.readInt(2);
          reader.skip(2);
          // this.versionNeeded = reader.readInt(2);
          this.bitFlag = reader.readInt(2);
          this.compressionMethod = reader.readString(2);
          this.date = reader.readDate();
          this.crc32 = reader.readInt(4);
          this.compressedSize = reader.readInt(4);
          this.uncompressedSize = reader.readInt(4);
          var fileNameLength = reader.readInt(2);
          this.extraFieldsLength = reader.readInt(2);
          this.fileCommentLength = reader.readInt(2);
          this.diskNumberStart = reader.readInt(2);
          this.internalFileAttributes = reader.readInt(2);
          this.externalFileAttributes = reader.readInt(4);
          this.localHeaderOffset = reader.readInt(4);

          if (this.isEncrypted()) {
            throw new Error("Encrypted zip are not supported");
          }

          // will be read in the local part, see the comments there
          reader.skip(fileNameLength);
          this.readExtraFields(reader);
          this.parseZIP64ExtraField(reader);
          this.fileComment = reader.readData(this.fileCommentLength);
        },

        /**
         * Parse the external file attributes and get the unix/dos permissions.
         */
        processAttributes: function () {
          this.unixPermissions = null;
          this.dosPermissions = null;
          var madeBy = this.versionMadeBy >> 8;

          // Check if we have the DOS directory flag set.
          // We look for it in the DOS and UNIX permissions
          // but some unknown platform could set it as a compatibility flag.
          this.dir = this.externalFileAttributes & 0x0010 ? true : false;

          if (madeBy === MADE_BY_DOS) {
            // first 6 bits (0 to 5)
            this.dosPermissions = this.externalFileAttributes & 0x3F;
          }

          if (madeBy === MADE_BY_UNIX) {
            this.unixPermissions = (this.externalFileAttributes >> 16) & 0xFFFF;
            // the octal permissions are in (this.unixPermissions & 0x01FF).toString(8);
          }

          // fail safe : if the name ends with a / it probably means a folder
          if (!this.dir && this.fileNameStr.slice(-1) === '/') {
            this.dir = true;
          }
        },

        /**
         * Parse the ZIP64 extra field and merge the info in the current ZipEntry.
         * @param {DataReader} reader the reader to use.
         */
        parseZIP64ExtraField: function (reader) {

          if (!this.extraFields[0x0001]) {
            return;
          }

          // should be something, preparing the extra reader
          var extraReader = readerFor(this.extraFields[0x0001].value);

          // I really hope that these 64bits integer can fit in 32 bits integer, because js
          // won't let us have more.
          if (this.uncompressedSize === utils.MAX_VALUE_32BITS) {
            this.uncompressedSize = extraReader.readInt(8);
          }
          if (this.compressedSize === utils.MAX_VALUE_32BITS) {
            this.compressedSize = extraReader.readInt(8);
          }
          if (this.localHeaderOffset === utils.MAX_VALUE_32BITS) {
            this.localHeaderOffset = extraReader.readInt(8);
          }
          if (this.diskNumberStart === utils.MAX_VALUE_32BITS) {
            this.diskNumberStart = extraReader.readInt(4);
          }
        },
        /**
         * Read the central part of a zip file and add the info in this object.
         * @param {DataReader} reader the reader to use.
         */
        readExtraFields: function (reader) {
          var end = reader.index + this.extraFieldsLength,
            extraFieldId,
            extraFieldLength,
            extraFieldValue;

          if (!this.extraFields) {
            this.extraFields = {};
          }

          while (reader.index < end) {
            extraFieldId = reader.readInt(2);
            extraFieldLength = reader.readInt(2);
            extraFieldValue = reader.readData(extraFieldLength);

            this.extraFields[extraFieldId] = {
              id: extraFieldId,
              length: extraFieldLength,
              value: extraFieldValue
            };
          }
        },
        /**
         * Apply an UTF8 transformation if needed.
         */
        handleUTF8: function () {
          var decodeParamType = support.uint8array ? "uint8array" : "array";
          if (this.useUTF8()) {
            this.fileNameStr = utf8.utf8decode(this.fileName);
            this.fileCommentStr = utf8.utf8decode(this.fileComment);
          } else {
            var upath = this.findExtraFieldUnicodePath();
            if (upath !== null) {
              this.fileNameStr = upath;
            } else {
              // ASCII text or unsupported code page
              var fileNameByteArray = utils.transformTo(decodeParamType, this.fileName);
              this.fileNameStr = this.loadOptions.decodeFileName(fileNameByteArray);
            }

            var ucomment = this.findExtraFieldUnicodeComment();
            if (ucomment !== null) {
              this.fileCommentStr = ucomment;
            } else {
              // ASCII text or unsupported code page
              var commentByteArray = utils.transformTo(decodeParamType, this.fileComment);
              this.fileCommentStr = this.loadOptions.decodeFileName(commentByteArray);
            }
          }
        },

        /**
         * Find the unicode path declared in the extra field, if any.
         * @return {String} the unicode path, null otherwise.
         */
        findExtraFieldUnicodePath: function () {
          var upathField = this.extraFields[0x7075];
          if (upathField) {
            var extraReader = readerFor(upathField.value);

            // wrong version
            if (extraReader.readInt(1) !== 1) {
              return null;
            }

            // the crc of the filename changed, this field is out of date.
            if (crc32fn(this.fileName) !== extraReader.readInt(4)) {
              return null;
            }

            return utf8.utf8decode(extraReader.readData(upathField.length - 5));
          }
          return null;
        },

        /**
         * Find the unicode comment declared in the extra field, if any.
         * @return {String} the unicode comment, null otherwise.
         */
        findExtraFieldUnicodeComment: function () {
          var ucommentField = this.extraFields[0x6375];
          if (ucommentField) {
            var extraReader = readerFor(ucommentField.value);

            // wrong version
            if (extraReader.readInt(1) !== 1) {
              return null;
            }

            // the crc of the comment changed, this field is out of date.
            if (crc32fn(this.fileComment) !== extraReader.readInt(4)) {
              return null;
            }

            return utf8.utf8decode(extraReader.readData(ucommentField.length - 5));
          }
          return null;
        }
      };
      module.exports = ZipEntry;

    }, { "./compressedObject": 2, "./compressions": 3, "./crc32": 4, "./reader/readerFor": 22, "./support": 30, "./utf8": 31, "./utils": 32 }], 35: [function (require, module, exports) {
      'use strict';

      var StreamHelper = require('./stream/StreamHelper');
      var DataWorker = require('./stream/DataWorker');
      var utf8 = require('./utf8');
      var CompressedObject = require('./compressedObject');
      var GenericWorker = require('./stream/GenericWorker');

      /**
       * A simple object representing a file in the zip file.
       * @constructor
       * @param {string} name the name of the file
       * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data
       * @param {Object} options the options of the file
       */
      var ZipObject = function (name, data, options) {
        this.name = name;
        this.dir = options.dir;
        this.date = options.date;
        this.comment = options.comment;
        this.unixPermissions = options.unixPermissions;
        this.dosPermissions = options.dosPermissions;

        this._data = data;
        this._dataBinary = options.binary;
        // keep only the compression
        this.options = {
          compression: options.compression,
          compressionOptions: options.compressionOptions
        };
      };

      ZipObject.prototype = {
        /**
         * Create an internal stream for the content of this object.
         * @param {String} type the type of each chunk.
         * @return StreamHelper the stream.
         */
        internalStream: function (type) {
          var result = null, outputType = "string";
          try {
            if (!type) {
              throw new Error("No output type specified.");
            }
            outputType = type.toLowerCase();
            var askUnicodeString = outputType === "string" || outputType === "text";
            if (outputType === "binarystring" || outputType === "text") {
              outputType = "string";
            }
            result = this._decompressWorker();

            var isUnicodeString = !this._dataBinary;

            if (isUnicodeString && !askUnicodeString) {
              result = result.pipe(new utf8.Utf8EncodeWorker());
            }
            if (!isUnicodeString && askUnicodeString) {
              result = result.pipe(new utf8.Utf8DecodeWorker());
            }
          } catch (e) {
            result = new GenericWorker("error");
            result.error(e);
          }

          return new StreamHelper(result, outputType, "");
        },

        /**
         * Prepare the content in the asked type.
         * @param {String} type the type of the result.
         * @param {Function} onUpdate a function to call on each internal update.
         * @return Promise the promise of the result.
         */
        async: function (type, onUpdate) {
          return this.internalStream(type).accumulate(onUpdate);
        },

        /**
         * Prepare the content as a nodejs stream.
         * @param {String} type the type of each chunk.
         * @param {Function} onUpdate a function to call on each internal update.
         * @return Stream the stream.
         */
        nodeStream: function (type, onUpdate) {
          return this.internalStream(type || "nodebuffer").toNodejsStream(onUpdate);
        },

        /**
         * Return a worker for the compressed content.
         * @private
         * @param {Object} compression the compression object to use.
         * @param {Object} compressionOptions the options to use when compressing.
         * @return Worker the worker.
         */
        _compressWorker: function (compression, compressionOptions) {
          if (
            this._data instanceof CompressedObject &&
            this._data.compression.magic === compression.magic
          ) {
            return this._data.getCompressedWorker();
          } else {
            var result = this._decompressWorker();
            if (!this._dataBinary) {
              result = result.pipe(new utf8.Utf8EncodeWorker());
            }
            return CompressedObject.createWorkerFrom(result, compression, compressionOptions);
          }
        },
        /**
         * Return a worker for the decompressed content.
         * @private
         * @return Worker the worker.
         */
        _decompressWorker: function () {
          if (this._data instanceof CompressedObject) {
            return this._data.getContentWorker();
          } else if (this._data instanceof GenericWorker) {
            return this._data;
          } else {
            return new DataWorker(this._data);
          }
        }
      };

      var removedMethods = ["asText", "asBinary", "asNodeBuffer", "asUint8Array", "asArrayBuffer"];
      var removedFn = function () {
        throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide.");
      };

      for (var i = 0; i < removedMethods.length; i++) {
        ZipObject.prototype[removedMethods[i]] = removedFn;
      }
      module.exports = ZipObject;

    }, { "./compressedObject": 2, "./stream/DataWorker": 27, "./stream/GenericWorker": 28, "./stream/StreamHelper": 29, "./utf8": 31 }], 36: [function (require, module, exports) {
      (function (global) {
        'use strict';
        var Mutation = global.MutationObserver || global.WebKitMutationObserver;

        var scheduleDrain;

        {
          if (Mutation) {
            var called = 0;
            var observer = new Mutation(nextTick);
            var element = global.document.createTextNode('');
            observer.observe(element, {
              characterData: true
            });
            scheduleDrain = function () {
              element.data = (called = ++called % 2);
            };
          } else if (!global.setImmediate && typeof global.MessageChannel !== 'undefined') {
            var channel = new global.MessageChannel();
            channel.port1.onmessage = nextTick;
            scheduleDrain = function () {
              channel.port2.postMessage(0);
            };
          } else if ('document' in global && 'onreadystatechange' in global.document.createElement('script')) {
            scheduleDrain = function () {

              // Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
              // into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
              var scriptEl = global.document.createElement('script');
              scriptEl.onreadystatechange = function () {
                nextTick();

                scriptEl.onreadystatechange = null;
                scriptEl.parentNode.removeChild(scriptEl);
                scriptEl = null;
              };
              global.document.documentElement.appendChild(scriptEl);
            };
          } else {
            scheduleDrain = function () {
              setTimeout(nextTick, 0);
            };
          }
        }

        var draining;
        var queue = [];

        //named nextTick for less confusing stack traces
        function nextTick() {
          draining = true;
          var i, oldQueue;
          var len = queue.length;
          while (len) {
            oldQueue = queue;
            queue = [];
            i = -1;
            while (++i < len) {
              oldQueue[i]();
            }
            len = queue.length;
          }
          draining = false;
        }

        module.exports = immediate;

        function immediate(task) {
          if (queue.push(task) === 1 && !draining) {
            scheduleDrain();
          }
        }

      }).call(this, typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
    }, {}], 37: [function (require, module, exports) {
      'use strict';
      var immediate = require('immediate');

      /* istanbul ignore next */
      function INTERNAL() {}

      var handlers = {};

      var REJECTED = ['REJECTED'];
      var FULFILLED = ['FULFILLED'];
      var PENDING = ['PENDING'];

      module.exports = Promise;

      function Promise(resolver) {
        if (typeof resolver !== 'function') {
          throw new TypeError('resolver must be a function');
        }
        this.state = PENDING;
        this.queue = [];
        this.outcome = void 0;
        if (resolver !== INTERNAL) {
          safelyResolveThenable(this, resolver);
        }
      }

      Promise.prototype["finally"] = function (callback) {
        if (typeof callback !== 'function') {
          return this;
        }
        var p = this.constructor;
        return this.then(resolve, reject);

        function resolve(value) {
          function yes() {
            return value;
          }

          return p.resolve(callback()).then(yes);
        }

        function reject(reason) {
          function no() {
            throw reason;
          }

          return p.resolve(callback()).then(no);
        }
      };
      Promise.prototype["catch"] = function (onRejected) {
        return this.then(null, onRejected);
      };
      Promise.prototype.then = function (onFulfilled, onRejected) {
        if (typeof onFulfilled !== 'function' && this.state === FULFILLED ||
          typeof onRejected !== 'function' && this.state === REJECTED) {
          return this;
        }
        var promise = new this.constructor(INTERNAL);
        if (this.state !== PENDING) {
          var resolver = this.state === FULFILLED ? onFulfilled : onRejected;
          unwrap(promise, resolver, this.outcome);
        } else {
          this.queue.push(new QueueItem(promise, onFulfilled, onRejected));
        }

        return promise;
      };

      function QueueItem(promise, onFulfilled, onRejected) {
        this.promise = promise;
        if (typeof onFulfilled === 'function') {
          this.onFulfilled = onFulfilled;
          this.callFulfilled = this.otherCallFulfilled;
        }
        if (typeof onRejected === 'function') {
          this.onRejected = onRejected;
          this.callRejected = this.otherCallRejected;
        }
      }

      QueueItem.prototype.callFulfilled = function (value) {
        handlers.resolve(this.promise, value);
      };
      QueueItem.prototype.otherCallFulfilled = function (value) {
        unwrap(this.promise, this.onFulfilled, value);
      };
      QueueItem.prototype.callRejected = function (value) {
        handlers.reject(this.promise, value);
      };
      QueueItem.prototype.otherCallRejected = function (value) {
        unwrap(this.promise, this.onRejected, value);
      };

      function unwrap(promise, func, value) {
        immediate(function () {
          var returnValue;
          try {
            returnValue = func(value);
          } catch (e) {
            return handlers.reject(promise, e);
          }
          if (returnValue === promise) {
            handlers.reject(promise, new TypeError('Cannot resolve promise with itself'));
          } else {
            handlers.resolve(promise, returnValue);
          }
        });
      }

      handlers.resolve = function (self, value) {
        var result = tryCatch(getThen, value);
        if (result.status === 'error') {
          return handlers.reject(self, result.value);
        }
        var thenable = result.value;

        if (thenable) {
          safelyResolveThenable(self, thenable);
        } else {
          self.state = FULFILLED;
          self.outcome = value;
          var i = -1;
          var len = self.queue.length;
          while (++i < len) {
            self.queue[i].callFulfilled(value);
          }
        }
        return self;
      };
      handlers.reject = function (self, error) {
        self.state = REJECTED;
        self.outcome = error;
        var i = -1;
        var len = self.queue.length;
        while (++i < len) {
          self.queue[i].callRejected(error);
        }
        return self;
      };

      function getThen(obj) {
        // Make sure we only access the accessor once as required by the spec
        var then = obj && obj.then;
        if (obj && (typeof obj === 'object' || typeof obj === 'function') && typeof then === 'function') {
          return function appyThen() {
            then.apply(obj, arguments);
          };
        }
      }

      function safelyResolveThenable(self, thenable) {
        // Either fulfill, reject or reject with error
        var called = false;

        function onError(value) {
          if (called) {
            return;
          }
          called = true;
          handlers.reject(self, value);
        }

        function onSuccess(value) {
          if (called) {
            return;
          }
          called = true;
          handlers.resolve(self, value);
        }

        function tryToUnwrap() {
          thenable(onSuccess, onError);
        }

        var result = tryCatch(tryToUnwrap);
        if (result.status === 'error') {
          onError(result.value);
        }
      }

      function tryCatch(func, value) {
        var out = {};
        try {
          out.value = func(value);
          out.status = 'success';
        } catch (e) {
          out.status = 'error';
          out.value = e;
        }
        return out;
      }

      Promise.resolve = resolve;

      function resolve(value) {
        if (value instanceof this) {
          return value;
        }
        return handlers.resolve(new this(INTERNAL), value);
      }

      Promise.reject = reject;

      function reject(reason) {
        var promise = new this(INTERNAL);
        return handlers.reject(promise, reason);
      }

      Promise.all = all;

      function all(iterable) {
        var self = this;
        if (Object.prototype.toString.call(iterable) !== '[object Array]') {
          return this.reject(new TypeError('must be an array'));
        }

        var len = iterable.length;
        var called = false;
        if (!len) {
          return this.resolve([]);
        }

        var values = new Array(len);
        var resolved = 0;
        var i = -1;
        var promise = new this(INTERNAL);

        while (++i < len) {
          allResolver(iterable[i], i);
        }
        return promise;

        function allResolver(value, i) {
          self.resolve(value).then(resolveFromAll, function (error) {
            if (!called) {
              called = true;
              handlers.reject(promise, error);
            }
          });

          function resolveFromAll(outValue) {
            values[i] = outValue;
            if (++resolved === len && !called) {
              called = true;
              handlers.resolve(promise, values);
            }
          }
        }
      }

      Promise.race = race;

      function race(iterable) {
        var self = this;
        if (Object.prototype.toString.call(iterable) !== '[object Array]') {
          return this.reject(new TypeError('must be an array'));
        }

        var len = iterable.length;
        var called = false;
        if (!len) {
          return this.resolve([]);
        }

        var i = -1;
        var promise = new this(INTERNAL);

        while (++i < len) {
          resolver(iterable[i]);
        }
        return promise;

        function resolver(value) {
          self.resolve(value).then(function (response) {
            if (!called) {
              called = true;
              handlers.resolve(promise, response);
            }
          }, function (error) {
            if (!called) {
              called = true;
              handlers.reject(promise, error);
            }
          });
        }
      }

    }, { "immediate": 36 }], 38: [function (require, module, exports) {
      // Top level file is just a mixin of submodules & constants
      'use strict';

      var assign = require('./lib/utils/common').assign;

      var deflate = require('./lib/deflate');
      var inflate = require('./lib/inflate');
      var constants = require('./lib/zlib/constants');

      var pako = {};

      assign(pako, deflate, inflate, constants);

      module.exports = pako;

    }, { "./lib/deflate": 39, "./lib/inflate": 40, "./lib/utils/common": 41, "./lib/zlib/constants": 44 }], 39: [function (require, module, exports) {
      'use strict';


      var zlib_deflate = require('./zlib/deflate');
      var utils = require('./utils/common');
      var strings = require('./utils/strings');
      var msg = require('./zlib/messages');
      var ZStream = require('./zlib/zstream');

      var toString = Object.prototype.toString;

      /* Public constants ==========================================================*/
      /* ===========================================================================*/

      var Z_NO_FLUSH = 0;
      var Z_FINISH = 4;

      var Z_OK = 0;
      var Z_STREAM_END = 1;
      var Z_SYNC_FLUSH = 2;

      var Z_DEFAULT_COMPRESSION = -1;

      var Z_DEFAULT_STRATEGY = 0;

      var Z_DEFLATED = 8;

      /* ===========================================================================*/


      /**
       * class Deflate
       *
       * Generic JS-style wrapper for zlib calls. If you don't need
       * streaming behaviour - use more simple functions: [[deflate]],
       * [[deflateRaw]] and [[gzip]].
       **/

      /* internal
 * Deflate.chunks -> Array
 *
 * Chunks of output data, if [[Deflate#onData]] not overriden.
 **/

      /**
       * Deflate.result -> Uint8Array|Array
       *
       * Compressed result, generated by default [[Deflate#onData]]
       * and [[Deflate#onEnd]] handlers. Filled after you push last chunk
       * (call [[Deflate#push]] with `Z_FINISH` / `true` param)  or if you
       * push a chunk with explicit flush (call [[Deflate#push]] with
       * `Z_SYNC_FLUSH` param).
       **/

      /**
       * Deflate.err -> Number
       *
       * Error code after deflate finished. 0 (Z_OK) on success.
       * You will not need it in real life, because deflate errors
       * are possible only on wrong options or bad `onData` / `onEnd`
       * custom handlers.
       **/

      /**
       * Deflate.msg -> String
       *
       * Error message, if [[Deflate.err]] != 0
       **/


      /**
       * new Deflate(options)
       * - options (Object): zlib deflate options.
       *
       * Creates new deflator instance with specified params. Throws exception
       * on bad params. Supported options:
       *
       * - `level`
       * - `windowBits`
       * - `memLevel`
       * - `strategy`
       * - `dictionary`
       *
       * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
       * for more information on these.
       *
       * Additional options, for internal needs:
       *
       * - `chunkSize` - size of generated data chunks (16K by default)
       * - `raw` (Boolean) - do raw deflate
       * - `gzip` (Boolean) - create gzip wrapper
       * - `to` (String) - if equal to 'string', then result will be "binary string"
       *    (each char code [0..255])
       * - `header` (Object) - custom header for gzip
       *   - `text` (Boolean) - true if compressed data believed to be text
       *   - `time` (Number) - modification time, unix timestamp
       *   - `os` (Number) - operation system code
       *   - `extra` (Array) - array of bytes with extra data (max 65536)
       *   - `name` (String) - file name (binary string)
       *   - `comment` (String) - comment (binary string)
       *   - `hcrc` (Boolean) - true if header crc should be added
       *
       * ##### Example:
       *
       * ```javascript
       * var pako = require('pako')
       *   , chunk1 = Uint8Array([1,2,3,4,5,6,7,8,9])
       *   , chunk2 = Uint8Array([10,11,12,13,14,15,16,17,18,19]);
       *
       * var deflate = new pako.Deflate({ level: 3});
       *
       * deflate.push(chunk1, false);
       * deflate.push(chunk2, true);  // true -> last chunk
       *
       * if (deflate.err) { throw new Error(deflate.err); }
       *
       * console.log(deflate.result);
       * ```
       **/
      function Deflate(options) {
        if (!(this instanceof Deflate)) {
          return new Deflate(options);
        }

        this.options = utils.assign({
          level: Z_DEFAULT_COMPRESSION,
          method: Z_DEFLATED,
          chunkSize: 16384,
          windowBits: 15,
          memLevel: 8,
          strategy: Z_DEFAULT_STRATEGY,
          to: ''
        }, options || {});

        var opt = this.options;

        if (opt.raw && (opt.windowBits > 0)) {
          opt.windowBits = -opt.windowBits;
        }

        else if (opt.gzip && (opt.windowBits > 0) && (opt.windowBits < 16)) {
          opt.windowBits += 16;
        }

        this.err = 0;      // error code, if happens (0 = Z_OK)
        this.msg = '';     // error message
        this.ended = false;  // used to avoid multiple onEnd() calls
        this.chunks = [];     // chunks of compressed data

        this.strm = new ZStream();
        this.strm.avail_out = 0;

        var status = zlib_deflate.deflateInit2(
          this.strm,
          opt.level,
          opt.method,
          opt.windowBits,
          opt.memLevel,
          opt.strategy
        );

        if (status !== Z_OK) {
          throw new Error(msg[status]);
        }

        if (opt.header) {
          zlib_deflate.deflateSetHeader(this.strm, opt.header);
        }

        if (opt.dictionary) {
          var dict;
          // Convert data if needed
          if (typeof opt.dictionary === 'string') {
            // If we need to compress text, change encoding to utf8.
            dict = strings.string2buf(opt.dictionary);
          } else if (toString.call(opt.dictionary) === '[object ArrayBuffer]') {
            dict = new Uint8Array(opt.dictionary);
          } else {
            dict = opt.dictionary;
          }

          status = zlib_deflate.deflateSetDictionary(this.strm, dict);

          if (status !== Z_OK) {
            throw new Error(msg[status]);
          }

          this._dict_set = true;
        }
      }

      /**
       * Deflate#push(data[, mode]) -> Boolean
       * - data (Uint8Array|Array|ArrayBuffer|String): input data. Strings will be
       *   converted to utf8 byte sequence.
       * - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes.
       *   See constants. Skipped or `false` means Z_NO_FLUSH, `true` meansh Z_FINISH.
       *
       * Sends input data to deflate pipe, generating [[Deflate#onData]] calls with
       * new compressed chunks. Returns `true` on success. The last data block must have
       * mode Z_FINISH (or `true`). That will flush internal pending buffers and call
       * [[Deflate#onEnd]]. For interim explicit flushes (without ending the stream) you
       * can use mode Z_SYNC_FLUSH, keeping the compression context.
       *
       * On fail call [[Deflate#onEnd]] with error code and return false.
       *
       * We strongly recommend to use `Uint8Array` on input for best speed (output
       * array format is detected automatically). Also, don't skip last param and always
       * use the same type in your code (boolean or number). That will improve JS speed.
       *
       * For regular `Array`-s make sure all elements are [0..255].
       *
       * ##### Example
       *
       * ```javascript
       * push(chunk, false); // push one of data chunks
       * ...
       * push(chunk, true);  // push last chunk
       * ```
       **/
      Deflate.prototype.push = function (data, mode) {
        var strm = this.strm;
        var chunkSize = this.options.chunkSize;
        var status, _mode;

        if (this.ended) {
          return false;
        }

        _mode = (mode === ~~mode) ? mode : ((mode === true) ? Z_FINISH : Z_NO_FLUSH);

        // Convert data if needed
        if (typeof data === 'string') {
          // If we need to compress text, change encoding to utf8.
          strm.input = strings.string2buf(data);
        } else if (toString.call(data) === '[object ArrayBuffer]') {
          strm.input = new Uint8Array(data);
        } else {
          strm.input = data;
        }

        strm.next_in = 0;
        strm.avail_in = strm.input.length;

        do {
          if (strm.avail_out === 0) {
            strm.output = new utils.Buf8(chunkSize);
            strm.next_out = 0;
            strm.avail_out = chunkSize;
          }
          status = zlib_deflate.deflate(strm, _mode);
          /* no bad return value */

          if (status !== Z_STREAM_END && status !== Z_OK) {
            this.onEnd(status);
            this.ended = true;
            return false;
          }
          if (strm.avail_out === 0 || (strm.avail_in === 0 && (_mode === Z_FINISH || _mode === Z_SYNC_FLUSH))) {
            if (this.options.to === 'string') {
              this.onData(strings.buf2binstring(utils.shrinkBuf(strm.output, strm.next_out)));
            } else {
              this.onData(utils.shrinkBuf(strm.output, strm.next_out));
            }
          }
        } while ((strm.avail_in > 0 || strm.avail_out === 0) && status !== Z_STREAM_END);

        // Finalize on the last chunk.
        if (_mode === Z_FINISH) {
          status = zlib_deflate.deflateEnd(this.strm);
          this.onEnd(status);
          this.ended = true;
          return status === Z_OK;
        }

        // callback interim results if Z_SYNC_FLUSH.
        if (_mode === Z_SYNC_FLUSH) {
          this.onEnd(Z_OK);
          strm.avail_out = 0;
          return true;
        }

        return true;
      };


      /**
       * Deflate#onData(chunk) -> Void
       * - chunk (Uint8Array|Array|String): ouput data. Type of array depends
       *   on js engine support. When string output requested, each chunk
       *   will be string.
       *
       * By default, stores data blocks in `chunks[]` property and glue
       * those in `onEnd`. Override this handler, if you need another behaviour.
       **/
      Deflate.prototype.onData = function (chunk) {
        this.chunks.push(chunk);
      };


      /**
       * Deflate#onEnd(status) -> Void
       * - status (Number): deflate status. 0 (Z_OK) on success,
       *   other if not.
       *
       * Called once after you tell deflate that the input stream is
       * complete (Z_FINISH) or should be flushed (Z_SYNC_FLUSH)
       * or if an error happened. By default - join collected chunks,
       * free memory and fill `results` / `err` properties.
       **/
      Deflate.prototype.onEnd = function (status) {
        // On success - join
        if (status === Z_OK) {
          if (this.options.to === 'string') {
            this.result = this.chunks.join('');
          } else {
            this.result = utils.flattenChunks(this.chunks);
          }
        }
        this.chunks = [];
        this.err = status;
        this.msg = this.strm.msg;
      };


      /**
       * deflate(data[, options]) -> Uint8Array|Array|String
       * - data (Uint8Array|Array|String): input data to compress.
       * - options (Object): zlib deflate options.
       *
       * Compress `data` with deflate algorithm and `options`.
       *
       * Supported options are:
       *
       * - level
       * - windowBits
       * - memLevel
       * - strategy
       * - dictionary
       *
       * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
       * for more information on these.
       *
       * Sugar (options):
       *
       * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify
       *   negative windowBits implicitly.
       * - `to` (String) - if equal to 'string', then result will be "binary string"
       *    (each char code [0..255])
       *
       * ##### Example:
       *
       * ```javascript
       * var pako = require('pako')
       *   , data = Uint8Array([1,2,3,4,5,6,7,8,9]);
       *
       * console.log(pako.deflate(data));
       * ```
       **/
      function deflate(input, options) {
        var deflator = new Deflate(options);

        deflator.push(input, true);

        // That will never happens, if you don't cheat with options :)
        if (deflator.err) {
          throw deflator.msg || msg[deflator.err];
        }

        return deflator.result;
      }


      /**
       * deflateRaw(data[, options]) -> Uint8Array|Array|String
       * - data (Uint8Array|Array|String): input data to compress.
       * - options (Object): zlib deflate options.
       *
       * The same as [[deflate]], but creates raw data, without wrapper
       * (header and adler32 crc).
       **/
      function deflateRaw(input, options) {
        options = options || {};
        options.raw = true;
        return deflate(input, options);
      }


      /**
       * gzip(data[, options]) -> Uint8Array|Array|String
       * - data (Uint8Array|Array|String): input data to compress.
       * - options (Object): zlib deflate options.
       *
       * The same as [[deflate]], but create gzip wrapper instead of
       * deflate one.
       **/
      function gzip(input, options) {
        options = options || {};
        options.gzip = true;
        return deflate(input, options);
      }


      exports.Deflate = Deflate;
      exports.deflate = deflate;
      exports.deflateRaw = deflateRaw;
      exports.gzip = gzip;

    }, { "./utils/common": 41, "./utils/strings": 42, "./zlib/deflate": 46, "./zlib/messages": 51, "./zlib/zstream": 53 }], 40: [function (require, module, exports) {
      'use strict';


      var zlib_inflate = require('./zlib/inflate');
      var utils = require('./utils/common');
      var strings = require('./utils/strings');
      var c = require('./zlib/constants');
      var msg = require('./zlib/messages');
      var ZStream = require('./zlib/zstream');
      var GZheader = require('./zlib/gzheader');

      var toString = Object.prototype.toString;

      /**
       * class Inflate
       *
       * Generic JS-style wrapper for zlib calls. If you don't need
       * streaming behaviour - use more simple functions: [[inflate]]
       * and [[inflateRaw]].
       **/

      /* internal
 * inflate.chunks -> Array
 *
 * Chunks of output data, if [[Inflate#onData]] not overriden.
 **/

      /**
       * Inflate.result -> Uint8Array|Array|String
       *
       * Uncompressed result, generated by default [[Inflate#onData]]
       * and [[Inflate#onEnd]] handlers. Filled after you push last chunk
       * (call [[Inflate#push]] with `Z_FINISH` / `true` param) or if you
       * push a chunk with explicit flush (call [[Inflate#push]] with
       * `Z_SYNC_FLUSH` param).
       **/

      /**
       * Inflate.err -> Number
       *
       * Error code after inflate finished. 0 (Z_OK) on success.
       * Should be checked if broken data possible.
       **/

      /**
       * Inflate.msg -> String
       *
       * Error message, if [[Inflate.err]] != 0
       **/


      /**
       * new Inflate(options)
       * - options (Object): zlib inflate options.
       *
       * Creates new inflator instance with specified params. Throws exception
       * on bad params. Supported options:
       *
       * - `windowBits`
       * - `dictionary`
       *
       * [http://zlib.net/manual.html#Advanced](htt
Download .txt
gitextract_ejp4vh9e/

├── .gitignore
├── README.md
├── index.html
└── jszip.js
Download .txt
SYMBOL INDEX (131 symbols across 1 files)

FILE: jszip.js
  function s (line 34) | function s(o, u) {
  function CompressedObject (line 186) | function CompressedObject(compressedSize, uncompressedSize, crc32, compr...
  function makeTable (line 272) | function makeTable() {
  function crc32 (line 290) | function crc32(crc, buf, len, pos) {
  function crc32str (line 314) | function crc32str(crc, str, len, pos) {
  function FlateWorker (line 392) | function FlateWorker(action, options) {
  function ZipFileWorker (line 783) | function ZipFileWorker(streamFiles, comment, platform, encodeFileName) {
  function JSZip (line 1069) | function JSZip() {
  function checkEntryCRC32 (line 1132) | function checkEntryCRC32(zipEntry) {
  function NodejsStreamInputAdapter (line 1213) | function NodejsStreamInputAdapter(filename, stream) {
  function NodejsStreamOutputAdapter (line 1293) | function NodejsStreamOutputAdapter(helper, options, updateCb) {
  function isRegExp (line 1540) | function isRegExp(object) {
  function ArrayReader (line 1787) | function ArrayReader(data) {
  function DataReader (line 1846) | function DataReader(data) {
  function NodeBufferReader (line 1966) | function NodeBufferReader(data) {
  function StringReader (line 1988) | function StringReader(data) {
  function Uint8ArrayReader (line 2029) | function Uint8ArrayReader(data) {
  function ConvertWorker (line 2099) | function ConvertWorker(destType) {
  function Crc32Probe (line 2128) | function Crc32Probe() {
  function DataLengthProbe (line 2155) | function DataLengthProbe(propName) {
  function DataWorker (line 2191) | function DataWorker(dataP) {
  function GenericWorker (line 2311) | function GenericWorker(name) {
  function transformZipOutput (line 2586) | function transformZipOutput(type, content, mimeType) {
  function concat (line 2604) | function concat(type, dataArray) {
  function accumulate (line 2637) | function accumulate(helper, updateCallback) {
  function StreamHelper (line 2674) | function StreamHelper(worker, outputType, mimeType) {
  function Utf8DecodeWorker (line 3030) | function Utf8DecodeWorker() {
  function Utf8EncodeWorker (line 3094) | function Utf8EncodeWorker() {
  function string2binary (line 3128) | function string2binary(str) {
  function identity (line 3184) | function identity(input) {
  function stringToArrayLike (line 3194) | function stringToArrayLike(str, array) {
  function arrayLikeToString (line 3276) | function arrayLikeToString(array) {
  function arrayLikeToArrayLike (line 3320) | function arrayLikeToArrayLike(arrayFrom, arrayTo) {
  function ZipEntries (line 3605) | function ZipEntries(loadOptions) {
  function ZipEntry (line 3893) | function ZipEntry(options, loadOptions) {
  function nextTick (line 4336) | function nextTick() {
  function immediate (line 4354) | function immediate(task) {
  function INTERNAL (line 4366) | function INTERNAL() {}
  function Promise (line 4376) | function Promise(resolver) {
  function resolve (line 4395) | function resolve(value) {
  function reject (line 4403) | function reject(reason) {
  function QueueItem (line 4430) | function QueueItem(promise, onFulfilled, onRejected) {
  function unwrap (line 4455) | function unwrap(promise, func, value) {
  function getThen (line 4502) | function getThen(obj) {
  function safelyResolveThenable (line 4512) | function safelyResolveThenable(self, thenable) {
  function tryCatch (line 4542) | function tryCatch(func, value) {
  function resolve (line 4556) | function resolve(value) {
  function reject (line 4565) | function reject(reason) {
  function all (line 4572) | function all(iterable) {
  function race (line 4614) | function race(iterable) {
  function Deflate (line 4785) | function Deflate(options) {
  function deflate (line 5025) | function deflate(input, options) {
  function deflateRaw (line 5047) | function deflateRaw(input, options) {
  function gzip (line 5062) | function gzip(input, options) {
  function Inflate (line 5167) | function Inflate(options) {
  function inflate (line 5461) | function inflate(input, options) {
  function inflateRaw (line 5483) | function inflateRaw(input, options) {
  function buf2binstring (line 5708) | function buf2binstring(buf, len) {
  function adler32 (line 5854) | function adler32(adler, buf, len, pos) {
  function makeTable (line 5978) | function makeTable() {
  function crc32 (line 5996) | function crc32(crc, buf, len, pos) {
  function err (line 6141) | function err(strm, errorCode) {
  function rank (line 6146) | function rank(f) {
  function zero (line 6150) | function zero(buf) {
  function flush_pending (line 6164) | function flush_pending(strm) {
  function flush_block_only (line 6188) | function flush_block_only(s, last) {
  function put_byte (line 6195) | function put_byte(s, b) {
  function putShortMSB (line 6205) | function putShortMSB(s, b) {
  function read_buf (line 6220) | function read_buf(strm, buf, start, size) {
  function longest_match (line 6258) | function longest_match(s, cur_match) {
  function fill_window (line 6379) | function fill_window(s) {
  function deflate_stored (line 6535) | function deflate_stored(s, flush) {
  function deflate_fast (line 6633) | function deflate_fast(s, flush) {
  function deflate_slow (line 6764) | function deflate_slow(s, flush) {
  function deflate_rle (line 6931) | function deflate_rle(s, flush) {
  function deflate_huff (line 7032) | function deflate_huff(s, flush) {
  function Config (line 7091) | function Config(good_length, max_lazy, nice_length, max_chain, func) {
  function lm_init (line 7120) | function lm_init(s) {
  function DeflateState (line 7143) | function DeflateState() {
  function deflateResetKeep (line 7372) | function deflateResetKeep(strm) {
  function deflateReset (line 7401) | function deflateReset(strm) {
  function deflateSetHeader (line 7410) | function deflateSetHeader(strm, head) {
  function deflateInit2 (line 7422) | function deflateInit2(strm, level, method, windowBits, memLevel, strateg...
  function deflateInit (line 7502) | function deflateInit(strm, level) {
  function deflate (line 7507) | function deflate(strm, flush) {
  function deflateEnd (line 7854) | function deflateEnd(strm) {
  function deflateSetDictionary (line 7883) | function deflateSetDictionary(strm, dictionary) {
  function GZheader (line 8009) | function GZheader() {
  function zswap32 (line 8569) | function zswap32(q) {
  function InflateState (line 8577) | function InflateState() {
  function inflateResetKeep (line 8670) | function inflateResetKeep(strm) {
  function inflateReset (line 8700) | function inflateReset(strm) {
  function inflateReset2 (line 8714) | function inflateReset2(strm, windowBits) {
  function inflateInit2 (line 8750) | function inflateInit2(strm, windowBits) {
  function inflateInit (line 8772) | function inflateInit(strm) {
  function fixedtables (line 8791) | function fixedtables(state) {
  function updatewindow (line 8849) | function updatewindow(strm, src, end, copy) {
  function inflate (line 8895) | function inflate(strm, flush) {
  function inflateEnd (line 10099) | function inflateEnd(strm) {
  function inflateGetHeader (line 10113) | function inflateGetHeader(strm, head) {
  function inflateSetDictionary (line 10131) | function inflateSetDictionary(strm, dictionary) {
  function zero (line 10652) | function zero(buf) {
  function StaticTreeDesc (line 10782) | function StaticTreeDesc(static_tree, extra_bits, extra_base, elems, max_...
  function TreeDesc (line 10805) | function TreeDesc(dyn_tree, stat_desc) {
  function d_code (line 10815) | function d_code(dist) {
  function put_short (line 10824) | function put_short(s, w) {
  function send_bits (line 10836) | function send_bits(s, value, length) {
  function send_code (line 10849) | function send_code(s, c, tree) {
  function bi_reverse (line 10859) | function bi_reverse(code, len) {
  function bi_flush (line 10873) | function bi_flush(s) {
  function gen_bitlen (line 10897) | function gen_bitlen(s, desc)
  function gen_codes (line 11012) | function gen_codes(tree, max_code, bl_count)
  function tr_static_init (line 11056) | function tr_static_init() {
  function init_block (line 11166) | function init_block(s) {
  function bi_windup (line 11190) | function bi_windup(s) {
  function copy_block (line 11205) | function copy_block(s, buf, len, header)
  function smaller (line 11229) | function smaller(tree, n, m, depth) {
  function pqdownheap (line 11242) | function pqdownheap(s, tree, k)
  function compress_block (line 11278) | function compress_block(s, ltree, dtree)
  function build_tree (line 11350) | function build_tree(s, desc)
  function scan_tree (line 11454) | function scan_tree(s, tree, max_code)
  function send_tree (line 11530) | function send_tree(s, tree, max_code)
  function build_bl_tree (line 11611) | function build_bl_tree(s) {
  function send_all_trees (line 11648) | function send_all_trees(s, lcodes, dcodes, blcodes)
  function detect_data_type (line 11693) | function detect_data_type(s) {
  function _tr_init (line 11731) | function _tr_init(s) {
  function _tr_stored_block (line 11753) | function _tr_stored_block(s, buf, stored_len, last)
  function _tr_align (line 11770) | function _tr_align(s) {
  function _tr_flush_block (line 11781) | function _tr_flush_block(s, buf, stored_len, last)
  function _tr_tally (line 11873) | function _tr_tally(s, dist, lc)
  function ZStream (line 11960) | function ZStream() {
Condensed preview — 4 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (459K chars).
[
  {
    "path": ".gitignore",
    "chars": 24,
    "preview": ".idea/\ntodo.md\nintro.md\n"
  },
  {
    "path": "README.md",
    "chars": 1433,
    "preview": "## [工具在线地址](http://blog.luckly-mjw.cn/tool-show/github-directory-downloader/index.html),推荐使用 chrome 浏览器。\n\n\n## 使用方式\n\n* 打开"
  },
  {
    "path": "index.html",
    "chars": 18398,
    "preview": "<!doctype html>\n<html lang=\"en\">\n\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, u"
  },
  {
    "path": "jszip.js",
    "chars": 422464,
    "preview": "/*!\n\nJSZip v3.3.0 - A JavaScript class for generating and reading zip files\n<http://stuartk.com/jszip>\n\n(c) 2009-2016 St"
  }
]

About this extraction

This page contains the full source code of the Momo707577045/github-directory-downloader GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 4 files (432.0 KB), approximately 105.3k tokens, and a symbol index with 131 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!