let INFO =
  teramako
  MIT
  
  
    特定ページの画像とかのURLを取ってきて一気にZIPにしてダウンロードするお
    
  
  - 
    :zipd :zipdownload
    :zipdownload -list -filter=filter downloadPath
    
      
        downloadPathへZIPでアーカイブする。
        downloadPathがディレクトリの場合、"ページタイトル.zip"となる。
        省略された場合、以下の順に値を見て、そのディレクトリへダウンロードされる。
        
          - g:zipDownloadDir (liberator.globalVariables.zipDownloadDir)
 
          - browser.download.lastDir (Preference)
 
          - ホームディレクトリ
 
        
       
      
        -listオプションを指定すると、ダウンロードされるURLをリストする。
        (ダウンロードはされない)
      
      
        -filterオプションを指定すると、マッチするURLのアイテムのみダウンロードする。
      
    
  
  - 
    g:zipDownloadDir
    g:zipDownloadDir
    liberator.globalVariables.zipDownloadDir
    
      
ダウンロード先ディレクトリ。downloadPathを省略した場合に、使用される。
      例
        :let g:zipDownloadDir="~/downloads"
      
    
   
  - 
    g:zipDownloadFilter
    g:zipDownloadFilter
    liberator.globalVariables.zipDownloadFilter
    
      
デフォルトのフィルタfilterを省略した場合に、使用される。
      例
        :let g:zipDownloadFilter="\.(jpe?g|gif|png)$"
      
    
   
  - 
    plugins.zipDeDownload.SITE_INFO
    plugins.zipDeDownload.SITE_INFO
    
      
        ページ毎の設定。詳細はコードを見よ。(見れば分かると思う)
      
    
   
;
// FIXME: 将来的には、storageに入れるべき
// FIXME: あと、それぞれダウンロード先を指定できた方が良い(?)
// XXX: WeData化してもOK
let SITE_INFO = [
  {
    label: "みんくちゃんねる",
    site: "http://minkch\\.com/archives/.*\\.html",
    xpath: '//a[img[@class="pict"]]|//div/img[@class="pict"]',
    filter: "\\.(jpe?g|gif|png)$"
  }, {
    label: "カナ速",
    site: "http://kanasoku\\.blog82\\.fc2\\.com/blog-entry-.*\\.html",
    xpath: '//div[@class="entry_body"]//a[img]',
    filter: "\\.(jpe?g|gif|png)$"
  }, {
    label: "がぞう~速報",
    site: "http://stalker\\.livedoor\\.biz/archives/.*\\.html",
    xpath: '//div[@class="main" or @class="mainmore"]//a/img[@class="pict"]/..',
    filter: "\\.(jpe?g|gif|png)$"
  }, {
    label: "ギャルゲーブログ",
    site: "http://suiseisekisuisui\\.blog107\\.fc2\\.com/blog-entry-.*.html",
    xpath: '//div[@class="ently_text"]/a[img]',
    filter: "\\.(jpe?g|gif|png)$"
  }, {
    label: "わくてか速報",
    site: "http://blog\\.livedoor\\.jp/wakusoku/archives/.*\\.html",
    xpath: '//div[@class="article-body-inner" or @class="article-body-more"]//a[//img[@class="pict"]]',
    filter: "\\.(jpe?g|gif|png)$"
  }, {
    label: "らばQ",
    site: "http://labaq\\.com/archives/.*\\.html",
    xpath: '//img[@class="pict"]',
  }, {
    labe: "【2ch】ニュー速クオリティ",
    site: "http://news4vip\\.livedoor\\.biz/archives/.*\\.html",
    xpath: '//a[img[@class="pict"]] | //div/img[@class="pict"]',
    filter: "\\.(jpe?g|gif|png)$"
  }, {
    label: "ねとねた",
    site: "http://vitaminabcdefg\\.blog6\\.fc2\\.com/blog-entry-.*\\.html",
    xpath: '//div[@class="mainEntryBody" or @class="mainEntryMore"]//a[img]',
    filter: "\\.(jpe?g|gif|png)$"
  }, {
    label: "PINK速報",
    site: "http://pinkimg\\.blog57\\.fc2\\.com/blog-entry-.*\\.html",
    xpath: '//div[@class="entry_text"]/a[img]',
    filter: "\\.(jpe?g|gif|png)$"
  }
];
(function(){
  // nsIZipWriter#open io-flags
  const PR_RDONLY      = 0x01;
  const PR_WRONLY      = 0x02;
  const PR_RDWR        = 0x04;
  const PR_CREATE_FILE = 0x08;
  const PR_APPEND      = 0x10;
  const PR_TRUNCATE    = 0x20;
  const PR_SYNC        = 0x40;
  const PR_EXCL        = 0x80;
  const mimeService = Cc["@mozilla.org/mime;1"].getService(Ci.nsIMIMEService);
  const zipWriter = Components.Constructor("@mozilla.org/zipwriter;1", "nsIZipWriter");
  function getFile(aFile){
    return liberator.modules.io.File(aFile);
  }
  function createChannel(url){
    return liberator.modules.services.get("io").newChannel(url, "UTF-8", null);
  }
  function getEntryName(uri, mimeType){
    let mime;
    try {
      mime = mimeService.getTypeFromURI(uri);
    } catch(e) {
      liberator.reportError(e);
    };
    let ext = mimeService.getPrimaryExtension(mime ? mime : mimeType, null)
    let name = uri.path.split("/").pop();
    name = (name ? name : "index") + (mime ? "" : "." + ext);
    return name;
  }
  function getDownloadDirectory(){
    let path = liberator.globalVariables.zipDownloadDir ||
               liberator.modules.options.getPref("browser.download.lastDir", null) ||
               liberator.modules.services.get("directory").get("Home", Ci.nsIFile).path;
    return getFile(path);
  }
  function fixFilename(filename){
    const badChars = /[\\\/:;\*\?\"\<\>\|\#]/g;
    return liberator.has('windows') ? filename.replace(badChars, '_') :  filename;
  }
  function getXPathFromExtensions(exts){
    function getXPath(elem){
      if (!elem)
        return '';
      // 連番かもしれない id は無視する
      let id = elem.getAttribute('id');
      if (id && !/\d/.test(id))
        return 'id("' + id + '")';
      return getXPath(elem.parentNode) + '/' + elem.tagName.toLowerCase();
    }
    let extPattern = RegExp('(' + exts.join('|')+')(\\W|$)');
    let links =
      Array.slice( content.document.querySelectorAll('a')).filter(
        function (link) (link.href && extPattern.test(link.href)));
    let xs = {};
    for each(let link in links){
      let xpath = getXPath(link);
      if (xs[xpath])
        xs[xpath]++;
      else
        xs[xpath] = 1;
    }
    let result = null, max = 0;
    for(let [xpath, count] in Iterator(xs)){
      if (count > max)
        [result, max] = [xpath, count];
    }
    return result;
  }
  function extensionValidator(vs)
    vs && vs.every(function (v) /^[\da-zA-Z]+$/.test(v));
  let self = {
    downloadZip: function(path, urls, comment, isAppend){
      let zipW = new zipWriter();
      urls = [url for each(url in urls)];
      liberator.assert(urls.length > 0, "None of URLs");
      if (!(/\.zip$/i).test(path)){
        path += ".zip";
      }
      let zipFile = getFile(path);
      if (isAppend && zipFile.exists()){
        zipW.open(zipFile, PR_RDWR | PR_APPEND);
      } else {
        zipW.open(zipFile, PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE);
      }
      if (comment)
        zipW.comment = comment;
      let i = 0;
      for each(let url in urls){
        let ch = createChannel(url);
        try {
          let stream = ch.open();
          let entryName = ("000" + ++i).slice(-3) + "-" + getEntryName(ch.URI, ch.contentType);
          liberator.echomsg("zip: " + url + " to " + entryName, commandline.FORCE_SINGLELINE);
          zipW.addEntryStream(entryName, Date.now() * 1000, Ci.nsIZipWriter.COMPRESSION_DEFAULT, stream, false);
        } catch (e) {
          // XXX エラー分を通知すべき?
          liberator.log('zip-de-download: error: ' + e);
        }
      }
      zipW.close();
      return zipFile;
    },
    getInfoFromBuffer: function(){
      for each(data in SITE_INFO){
        let reg = new RegExp(data.site);
        if (reg.test(liberator.modules.buffer.URL)){
          return data;
        }
      }
      return null;
    },
    getURLs: function(info){
      let filter = new RegExp(info.filter || liberator.globalVariables.zipDownloadFilter || ".");
      let i = 0;
      for (let elm in liberator.modules.util.evaluateXPath(info.xpath, content.document)){
        let url;
        if (elm instanceof Ci.nsIDOMHTMLAnchorElement)
          url = elm.href;
        else if (elm instanceof Ci.nsIDOMHTMLImageElement)
          url = elm.src;
        else
          continue;
        if (filter.test(url))
          yield url;
      }
    },
    download: function(zipPath, listOnly, option){
      let info = this.getInfoFromBuffer() || {};
      if (option){
        let infoBuf = {};
        for (let key in info){
          infoBuf[key] = info[key];
        }
        for (let key in option){
          infoBuf[key] = option[key];
        }
        info = infoBuf;
      }
      liberator.assert(info.xpath, "not registered in SITE_INFO");
      let urls = this.getURLs(info);
      let title = fixFilename(liberator.modules.buffer.title);
      let comment = [title, liberator.modules.buffer.URL].join("\n");
      let file;
      if (!zipPath){
        file = getDownloadDirectory();
        file.append(title + ".zip");
      } else {
        file = getFile(zipPath);
        if (file.exists() && file.isDirectory()){
          file.append(title + ".zip");
        }
      }
      if (listOnly){
        return [file, urls, comment];
      }
      return this.downloadZip(file.path, urls, comment, info.append);
    }
  };
  // ---------------------------------------------------
  // Commands
  // ---------------------------------------------------
  liberator.modules.commands.addUserCommand(
    ["zipd[ownload]"], "download and archive to ZIP",
    function (arg){
      let option = {}
      option.append = ("-append" in arg);
      if ("-auto-detect" in arg){
        option.xpath = getXPathFromExtensions(arg["-auto-detect"]);
      }
      if ("-xpath" in arg){
        option.xpath = arg["-xpath"];
      }
      if ("-filter" in arg){
        option.filter = arg["-filter"];
      }
      if ("-list" in arg){
        let [file, urls, comment] = self.download(arg[0], true, option);
        let xml = <>
          Download :{file.path}
          {comment}
          
            {liberator.modules.template.map(urls, function(url) - {url}
 )}
          
          
        >;
        liberator.echo(xml, true);
        return;
      }
      liberator.echo("Started DownloadZip");
      let zipFile = self.download(arg[0], false, option);
      liberator.echo("Completed DownloadZip: " + zipFile.path);
    }, {
      argCount: "?",
      literal: true,
      options: [
        [["-list", "-l"], liberator.modules.commands.OPTION_NOARG],
        [["-append", "-a"], liberator.modules.commands.OPTION_NOARG],
        [["-xpath", "-x"], liberator.modules.commands.OPTION_STRING],
        [["-auto-detect", "-d"], liberator.modules.commands.OPTION_LIST, extensionValidator,
         [["jpeg,jpg,png", "images"]]],
        [["-filter", "-f"], liberator.modules.commands.OPTION_STRING]
      ],
      completer: liberator.modules.completion.file
    }, true);
  util.extend(__context__, self);
})();
// vim: sw=2 ts=2 et: