<commit_before>var createServer = require("http").createServer;
var readFile = require("fs").readFile;
var sys = require("sys");
var url = require("url");
DEBUG = false;

var fu = exports;

var NOT_FOUND = "Not Found\n";

function notFound(req, res) {
  res.writeHead(404, { "Content-Type": "text/plain"
                     , "Content-Length": NOT_FOUND.length
                     });
  res.end(NOT_FOUND);
}

var getMap = {};

fu.get = function (path, handler) {
  getMap[path] = handler;
};
var server = createServer(function (req, res) {
  if (req.method === "GET" || req.method === "HEAD") {
    var handler = getMap[url.parse(req.url).pathname] || notFound;

    res.simpleText = function (code, body) {
      res.writeHead(code, { "Content-Type": "text/plain"
                          , "Content-Length": body.length
                          });
      res.end(body);
    };

    res.simpleJSON = function (code, obj) {
      var body = JSON.stringify(obj);
      res.writeHead(code, { "Content-Type": "text/json"
                          , "Content-Length": body.length
                          });
      res.end(body);
    };

    handler(req, res);
  }
});

fu.listen = function (port, host) {
  server.listen(port, host);
  sys.puts("Server at http://" + (host || "127.0.0.1") + ":" + port.toString() + "/");
};

fu.close = function () { server.close(); };

function extname (path) {
  var index = path.lastIndexOf(".");
  return index < 0 ? "" : path.substring(index);
}

fu.staticHandler = function (filename) {
  var body, headers;
  var content_type = fu.mime.lookupExtension(extname(filename));

  function loadResponseData(callback) {
    if (body && headers && !DEBUG) {
      callback();
      return;
    }

    sys.puts("loading " + filename + "...");
    readFile(filename, function (err, data) {
      if (err) {
        sys.puts("Error loading " + filename);
      } else {
        body = data;
        headers = { "Content-Type": content_type
                  , "Content-Length": body.length
                  };
        if (!DEBUG) headers["Cache-Control"] = "public";
        sys.puts("static file " + filename + " loaded");
        callback();
      }
    });
  }

  return function (req, res) {
    loadResponseData(function () {
      res.writeHead(200, headers);
      res.end(body);
    });
  }
};

// stolen from jack- thanks
fu.mime = {
  // returns MIME type for extension, or fallback, or octet-steam
  lookupExtension : function(ext, fallback) {
    return fu.mime.TYPES[ext.toLowerCase()] || fallback || 'application/octet-stream';
  },

  // List of most common mime-types, stolen from Rack.
  TYPES : { ".3gp"   : "video/3gpp"
          , ".a"     : "application/octet-stream"
          , ".ai"    : "application/postscript"
          , ".aif"   : "audio/x-aiff"
          , ".aiff"  : "audio/x-aiff"
          , ".asc"   : "application/pgp-signature"
          , ".asf"   : "video/x-ms-asf"
          , ".asm"   : "text/x-asm"
          , ".asx"   : "video/x-ms-asf"
          , ".atom"  : "application/atom+xml"
          , ".au"    : "audio/basic"
          , ".avi"   : "video/x-msvideo"
          , ".bat"   : "application/x-msdownload"
          , ".bin"   : "application/octet-stream"
          , ".bmp"   : "image/bmp"
          , ".bz2"   : "application/x-bzip2"
          , ".c"     : "text/x-c"
          , ".cab"   : "application/vnd.ms-cab-compressed"
          , ".cc"    : "text/x-c"
          , ".chm"   : "application/vnd.ms-htmlhelp"
          , ".class"   : "application/octet-stream"
          , ".com"   : "application/x-msdownload"
          , ".conf"  : "text/plain"
          , ".cpp"   : "text/x-c"
          , ".crt"   : "application/x-x509-ca-cert"
          , ".css"   : "text/css"
          , ".csv"   : "text/csv"
          , ".cxx"   : "text/x-c"
          , ".deb"   : "application/x-debian-package"
          , ".der"   : "application/x-x509-ca-cert"
          , ".diff"  : "text/x-diff"
          , ".djv"   : "image/vnd.djvu"
          , ".djvu"  : "image/vnd.djvu"
          , ".dll"   : "application/x-msdownload"
          , ".dmg"   : "application/octet-stream"
          , ".doc"   : "application/msword"
          , ".dot"   : "application/msword"
          , ".dtd"   : "application/xml-dtd"
          , ".dvi"   : "application/x-dvi"
          , ".ear"   : "application/java-archive"
          , ".eml"   : "message/rfc822"
          , ".eps"   : "application/postscript"
          , ".exe"   : "application/x-msdownload"
          , ".f"     : "text/x-fortran"
          , ".f77"   : "text/x-fortran"
          , ".f90"   : "text/x-fortran"
          , ".flv"   : "video/x-flv"
          , ".for"   : "text/x-fortran"
          , ".gem"   : "application/octet-stream"
          , ".gemspec" : "text/x-script.ruby"
          , ".gif"   : "image/gif"
          , ".gz"    : "application/x-gzip"
          , ".h"     : "text/x-c"
          , ".hh"    : "text/x-c"
          , ".htm"   : "text/html"
          , ".html"  : "text/html"
          , ".ico"   : "image/vnd.microsoft.icon"
          , ".ics"   : "text/calendar"
          , ".ifb"   : "text/calendar"
          , ".iso"   : "application/octet-stream"
          , ".jar"   : "application/java-archive"
          , ".java"  : "text/x-java-source"
          , ".jnlp"  : "application/x-java-jnlp-file"
          , ".jpeg"  : "image/jpeg"
          , ".jpg"   : "image/jpeg"
          , ".js"    : "application/javascript"
          , ".json"  : "application/json"
          , ".log"   : "text/plain"
          , ".m3u"   : "audio/x-mpegurl"
          , ".m4v"   : "video/mp4"
          , ".man"   : "text/troff"
          , ".mathml"  : "application/mathml+xml"
          , ".mbox"  : "application/mbox"
          , ".mdoc"  : "text/troff"
          , ".me"    : "text/troff"
          , ".mid"   : "audio/midi"
          , ".midi"  : "audio/midi"
          , ".mime"  : "message/rfc822"
          , ".mml"   : "application/mathml+xml"
          , ".mng"   : "video/x-mng"
          , ".mov"   : "video/quicktime"
          , ".mp3"   : "audio/mpeg"
          , ".mp4"   : "video/mp4"
          , ".mp4v"  : "video/mp4"
          , ".mpeg"  : "video/mpeg"
          , ".mpg"   : "video/mpeg"
          , ".ms"    : "text/troff"
          , ".msi"   : "application/x-msdownload"
          , ".odp"   : "application/vnd.oasis.opendocument.presentation"
          , ".ods"   : "application/vnd.oasis.opendocument.spreadsheet"
          , ".odt"   : "application/vnd.oasis.opendocument.text"
          , ".ogg"   : "application/ogg"
          , ".p"     : "text/x-pascal"
          , ".pas"   : "text/x-pascal"
          , ".pbm"   : "image/x-portable-bitmap"
          , ".pdf"   : "application/pdf"
          , ".pem"   : "application/x-x509-ca-cert"
          , ".pgm"   : "image/x-portable-graymap"
          , ".pgp"   : "application/pgp-encrypted"
          , ".pkg"   : "application/octet-stream"
          , ".pl"    : "text/x-script.perl"
          , ".pm"    : "text/x-script.perl-module"
          , ".png"   : "image/png"
          , ".pnm"   : "image/x-portable-anymap"
          , ".ppm"   : "image/x-portable-pixmap"
          , ".pps"   : "application/vnd.ms-powerpoint"
          , ".ppt"   : "application/vnd.ms-powerpoint"
          , ".ps"    : "application/postscript"
          , ".psd"   : "image/vnd.adobe.photoshop"
          , ".py"    : "text/x-script.python"
          , ".qt"    : "video/quicktime"
          , ".ra"    : "audio/x-pn-realaudio"
          , ".rake"  : "text/x-script.ruby"
          , ".ram"   : "audio/x-pn-realaudio"
          , ".rar"   : "application/x-rar-compressed"
          , ".rb"    : "text/x-script.ruby"
          , ".rdf"   : "application/rdf+xml"
          , ".roff"  : "text/troff"
          , ".rpm"   : "application/x-redhat-package-manager"
          , ".rss"   : "application/rss+xml"
          , ".rtf"   : "application/rtf"
          , ".ru"    : "text/x-script.ruby"
          , ".s"     : "text/x-asm"
          , ".sgm"   : "text/sgml"
          , ".sgml"  : "text/sgml"
          , ".sh"    : "application/x-sh"
          , ".sig"   : "application/pgp-signature"
          , ".snd"   : "audio/basic"
          , ".so"    : "application/octet-stream"
          , ".svg"   : "image/svg+xml"
          , ".svgz"  : "image/svg+xml"
          , ".swf"   : "application/x-shockwave-flash"
          , ".t"     : "text/troff"
          , ".tar"   : "application/x-tar"
          , ".tbz"   : "application/x-bzip-compressed-tar"
          , ".tcl"   : "application/x-tcl"
          , ".tex"   : "application/x-tex"
          , ".texi"  : "application/x-texinfo"
          , ".texinfo" : "application/x-texinfo"
          , ".text"  : "text/plain"
          , ".tif"   : "image/tiff"
          , ".tiff"  : "image/tiff"
          , ".torrent" : "application/x-bittorrent"
          , ".tr"    : "text/troff"
          , ".txt"   : "text/plain"
          , ".vcf"   : "text/x-vcard"
          , ".vcs"   : "text/x-vcalendar"
          , ".vrml"  : "model/vrml"
          , ".war"   : "application/java-archive"
          , ".wav"   : "audio/x-wav"
          , ".wma"   : "audio/x-ms-wma"
          , ".wmv"   : "video/x-ms-wmv"
          , ".wmx"   : "video/x-ms-wmx"
          , ".wrl"   : "model/vrml"
          , ".wsdl"  : "application/wsdl+xml"
          , ".xbm"   : "image/x-xbitmap"
          , ".xhtml"   : "application/xhtml+xml"
          , ".xls"   : "application/vnd.ms-excel"
          , ".xml"   : "application/xml"
          , ".xpm"   : "image/x-xpixmap"
          , ".xsl"   : "application/xml"
          , ".xslt"  : "application/xslt+xml"
          , ".yaml"  : "text/yaml"
          , ".yml"   : "text/yaml"
          , ".zip"   : "application/zip"
          }
};
<commit_msg>Fixed a bug in fu's handling of HEAD requests.<commit_after>var createServer = require("http").createServer;
var readFile = require("fs").readFile;
var sys = require("sys");
var url = require("url");
DEBUG = false;

var fu = exports;

var NOT_FOUND = "Not Found\n";

function notFound(req, res) {
  res.writeHead(404, { "Content-Type": "text/plain"
                     , "Content-Length": NOT_FOUND.length
                     });
  res.end(NOT_FOUND);
}

var getMap = {};

fu.get = function (path, handler) {
  getMap[path] = handler;
};
var server = createServer(function (req, res) {
  if (req.method === "GET" || req.method === "HEAD") {
    var handler = getMap[url.parse(req.url).pathname] || notFound;

    res.simpleText = function (code, body) {
      res.writeHead(code, { "Content-Type": "text/plain"
                          , "Content-Length": body.length
                          });
      res.end(body);
    };

    res.simpleJSON = function (code, obj) {
      var body = JSON.stringify(obj);
      res.writeHead(code, { "Content-Type": "text/json"
                          , "Content-Length": body.length
                          });
      res.end(body);
    };

    handler(req, res);
  }
});

fu.listen = function (port, host) {
  server.listen(port, host);
  sys.puts("Server at http://" + (host || "127.0.0.1") + ":" + port.toString() + "/");
};

fu.close = function () { server.close(); };

function extname (path) {
  var index = path.lastIndexOf(".");
  return index < 0 ? "" : path.substring(index);
}

fu.staticHandler = function (filename) {
  var body, headers;
  var content_type = fu.mime.lookupExtension(extname(filename));

  function loadResponseData(callback) {
    if (body && headers && !DEBUG) {
      callback();
      return;
    }

    sys.puts("loading " + filename + "...");
    readFile(filename, function (err, data) {
      if (err) {
        sys.puts("Error loading " + filename);
      } else {
        body = data;
        headers = { "Content-Type": content_type
                  , "Content-Length": body.length
                  };
        if (!DEBUG) headers["Cache-Control"] = "public";
        sys.puts("static file " + filename + " loaded");
        callback();
      }
    });
  }

  return function (req, res) {
    loadResponseData(function () {
      res.writeHead(200, headers);
      res.end(req.method === "HEAD" ? "" : body);
    });
  }
};

// stolen from jack- thanks
fu.mime = {
  // returns MIME type for extension, or fallback, or octet-steam
  lookupExtension : function(ext, fallback) {
    return fu.mime.TYPES[ext.toLowerCase()] || fallback || 'application/octet-stream';
  },

  // List of most common mime-types, stolen from Rack.
  TYPES : { ".3gp"   : "video/3gpp"
          , ".a"     : "application/octet-stream"
          , ".ai"    : "application/postscript"
          , ".aif"   : "audio/x-aiff"
          , ".aiff"  : "audio/x-aiff"
          , ".asc"   : "application/pgp-signature"
          , ".asf"   : "video/x-ms-asf"
          , ".asm"   : "text/x-asm"
          , ".asx"   : "video/x-ms-asf"
          , ".atom"  : "application/atom+xml"
          , ".au"    : "audio/basic"
          , ".avi"   : "video/x-msvideo"
          , ".bat"   : "application/x-msdownload"
          , ".bin"   : "application/octet-stream"
          , ".bmp"   : "image/bmp"
          , ".bz2"   : "application/x-bzip2"
          , ".c"     : "text/x-c"
          , ".cab"   : "application/vnd.ms-cab-compressed"
          , ".cc"    : "text/x-c"
          , ".chm"   : "application/vnd.ms-htmlhelp"
          , ".class"   : "application/octet-stream"
          , ".com"   : "application/x-msdownload"
          , ".conf"  : "text/plain"
          , ".cpp"   : "text/x-c"
          , ".crt"   : "application/x-x509-ca-cert"
          , ".css"   : "text/css"
          , ".csv"   : "text/csv"
          , ".cxx"   : "text/x-c"
          , ".deb"   : "application/x-debian-package"
          , ".der"   : "application/x-x509-ca-cert"
          , ".diff"  : "text/x-diff"
          , ".djv"   : "image/vnd.djvu"
          , ".djvu"  : "image/vnd.djvu"
          , ".dll"   : "application/x-msdownload"
          , ".dmg"   : "application/octet-stream"
          , ".doc"   : "application/msword"
          , ".dot"   : "application/msword"
          , ".dtd"   : "application/xml-dtd"
          , ".dvi"   : "application/x-dvi"
          , ".ear"   : "application/java-archive"
          , ".eml"   : "message/rfc822"
          , ".eps"   : "application/postscript"
          , ".exe"   : "application/x-msdownload"
          , ".f"     : "text/x-fortran"
          , ".f77"   : "text/x-fortran"
          , ".f90"   : "text/x-fortran"
          , ".flv"   : "video/x-flv"
          , ".for"   : "text/x-fortran"
          , ".gem"   : "application/octet-stream"
          , ".gemspec" : "text/x-script.ruby"
          , ".gif"   : "image/gif"
          , ".gz"    : "application/x-gzip"
          , ".h"     : "text/x-c"
          , ".hh"    : "text/x-c"
          , ".htm"   : "text/html"
          , ".html"  : "text/html"
          , ".ico"   : "image/vnd.microsoft.icon"
          , ".ics"   : "text/calendar"
          , ".ifb"   : "text/calendar"
          , ".iso"   : "application/octet-stream"
          , ".jar"   : "application/java-archive"
          , ".java"  : "text/x-java-source"
          , ".jnlp"  : "application/x-java-jnlp-file"
          , ".jpeg"  : "image/jpeg"
          , ".jpg"   : "image/jpeg"
          , ".js"    : "application/javascript"
          , ".json"  : "application/json"
          , ".log"   : "text/plain"
          , ".m3u"   : "audio/x-mpegurl"
          , ".m4v"   : "video/mp4"
          , ".man"   : "text/troff"
          , ".mathml"  : "application/mathml+xml"
          , ".mbox"  : "application/mbox"
          , ".mdoc"  : "text/troff"
          , ".me"    : "text/troff"
          , ".mid"   : "audio/midi"
          , ".midi"  : "audio/midi"
          , ".mime"  : "message/rfc822"
          , ".mml"   : "application/mathml+xml"
          , ".mng"   : "video/x-mng"
          , ".mov"   : "video/quicktime"
          , ".mp3"   : "audio/mpeg"
          , ".mp4"   : "video/mp4"
          , ".mp4v"  : "video/mp4"
          , ".mpeg"  : "video/mpeg"
          , ".mpg"   : "video/mpeg"
          , ".ms"    : "text/troff"
          , ".msi"   : "application/x-msdownload"
          , ".odp"   : "application/vnd.oasis.opendocument.presentation"
          , ".ods"   : "application/vnd.oasis.opendocument.spreadsheet"
          , ".odt"   : "application/vnd.oasis.opendocument.text"
          , ".ogg"   : "application/ogg"
          , ".p"     : "text/x-pascal"
          , ".pas"   : "text/x-pascal"
          , ".pbm"   : "image/x-portable-bitmap"
          , ".pdf"   : "application/pdf"
          , ".pem"   : "application/x-x509-ca-cert"
          , ".pgm"   : "image/x-portable-graymap"
          , ".pgp"   : "application/pgp-encrypted"
          , ".pkg"   : "application/octet-stream"
          , ".pl"    : "text/x-script.perl"
          , ".pm"    : "text/x-script.perl-module"
          , ".png"   : "image/png"
          , ".pnm"   : "image/x-portable-anymap"
          , ".ppm"   : "image/x-portable-pixmap"
          , ".pps"   : "application/vnd.ms-powerpoint"
          , ".ppt"   : "application/vnd.ms-powerpoint"
          , ".ps"    : "application/postscript"
          , ".psd"   : "image/vnd.adobe.photoshop"
          , ".py"    : "text/x-script.python"
          , ".qt"    : "video/quicktime"
          , ".ra"    : "audio/x-pn-realaudio"
          , ".rake"  : "text/x-script.ruby"
          , ".ram"   : "audio/x-pn-realaudio"
          , ".rar"   : "application/x-rar-compressed"
          , ".rb"    : "text/x-script.ruby"
          , ".rdf"   : "application/rdf+xml"
          , ".roff"  : "text/troff"
          , ".rpm"   : "application/x-redhat-package-manager"
          , ".rss"   : "application/rss+xml"
          , ".rtf"   : "application/rtf"
          , ".ru"    : "text/x-script.ruby"
          , ".s"     : "text/x-asm"
          , ".sgm"   : "text/sgml"
          , ".sgml"  : "text/sgml"
          , ".sh"    : "application/x-sh"
          , ".sig"   : "application/pgp-signature"
          , ".snd"   : "audio/basic"
          , ".so"    : "application/octet-stream"
          , ".svg"   : "image/svg+xml"
          , ".svgz"  : "image/svg+xml"
          , ".swf"   : "application/x-shockwave-flash"
          , ".t"     : "text/troff"
          , ".tar"   : "application/x-tar"
          , ".tbz"   : "application/x-bzip-compressed-tar"
          , ".tcl"   : "application/x-tcl"
          , ".tex"   : "application/x-tex"
          , ".texi"  : "application/x-texinfo"
          , ".texinfo" : "application/x-texinfo"
          , ".text"  : "text/plain"
          , ".tif"   : "image/tiff"
          , ".tiff"  : "image/tiff"
          , ".torrent" : "application/x-bittorrent"
          , ".tr"    : "text/troff"
          , ".txt"   : "text/plain"
          , ".vcf"   : "text/x-vcard"
          , ".vcs"   : "text/x-vcalendar"
          , ".vrml"  : "model/vrml"
          , ".war"   : "application/java-archive"
          , ".wav"   : "audio/x-wav"
          , ".wma"   : "audio/x-ms-wma"
          , ".wmv"   : "video/x-ms-wmv"
          , ".wmx"   : "video/x-ms-wmx"
          , ".wrl"   : "model/vrml"
          , ".wsdl"  : "application/wsdl+xml"
          , ".xbm"   : "image/x-xbitmap"
          , ".xhtml"   : "application/xhtml+xml"
          , ".xls"   : "application/vnd.ms-excel"
          , ".xml"   : "application/xml"
          , ".xpm"   : "image/x-xpixmap"
          , ".xsl"   : "application/xml"
          , ".xslt"  : "application/xslt+xml"
          , ".yaml"  : "text/yaml"
          , ".yml"   : "text/yaml"
          , ".zip"   : "application/zip"
          }
};
<|endoftext|>


<commit_before>var fs = require('fs');
var _ = require(""underscore"");
var request = require(""request"");
var url = ""https://spreadsheets.google.com/feeds/list/1pOlPpmQkwQhXSFMDx6wryFhvQL8tjf1gbbiJQLRvDPs/1/public/values?alt=json""
// var url = ""./temp.json"";
    request({
        url: url,
        json: true
    }, function (error, response, DB) {
        console.log(error);
        if (!error && response.statusCode === 200) {
            // console.log(DB) // Print the json response

            var all = _.map(DB.feed.entry,function(data){
                var o = {};
                // 1차정제(스프레드시트 데이터)
                for(key in data){
                    if(key.indexOf(""gsx$"")!= -1){
                        var nkey = key.split(""gsx$"")[1];
                        o[nkey] = data[key].$t;
                    }
                }
                return o;
            });

            var sneakers = _.filter(all,function(sneaker){
                return (sneaker.view != """");
            });

            var getSortSelectData =[]; // 브랜드 정보만
            _.each(sneakers,function(item){
                getSortSelectData.push(item.no);
            });
            getSortSelectData = _.uniq(getSortSelectData);

            // 스니커 페이지 생성
            _.each(sneakers,function(sneaker){
                // 아카이브 생성
                try{
                    fs.mkdirSync('sneakers/'+sneaker.brand+''+sneaker.no);
                }catch(e){
                    if ( e.code != 'EEXIST' ) throw e;
                }
                fs.writeFileSync('sneakers/'+sneaker.brand+''+sneaker.no+'/'+sneaker.style+'-'+sneaker.code+'.html', makeTistoryHtml(sneaker));
                console.log(sneaker.style+""-""+sneaker.code+"" file created."");

                // 릴리즈 생성
                try{
                    fs.mkdirSync('release/'+sneaker.brand+''+sneaker.no);
                }catch(e){
                    if ( e.code != 'EEXIST' ) throw e;
                }
                if(sneaker.enrelease){
                    fs.writeFileSync('release/'+sneaker.brand+''+sneaker.no+'/en_'+sneaker.style+'-'+sneaker.code+'.html', makeReleaseHtml(sneaker,""en""));
                };
                if(sneaker.korelease){
                    fs.writeFileSync('release/'+sneaker.brand+''+sneaker.no+'/ko_'+sneaker.style+'-'+sneaker.code+'.html', makeReleaseHtml(sneaker,""ko""));
                };
            });
        }
    });

// 발매일정(릴리즈) 만들기
function makeReleaseHtml(sneaker,lang){
    var str = '';
    str += '
'+sneaker[lang+""release""]+'

';
    str += '

'+sneaker[lang+""desc1""]+'
'+((sneaker[lang+""desc2""]) ? '
'+sneaker[lang+""desc2""]+'
' : '')+'
';
    str += '

'+sneaker[lang+""name""]+' ""'+sneaker[lang+""nick""]+'""
';
    str += '

Style

'+sneaker.style+'-'+sneaker.code+'

';
    str += '

Color

'+sneaker[lang+""color""]+'

';
    str += '

Release Date

'+sneaker[lang+""date""]+'

';
    if(lang==""en""){
        str += '

Price

$'+sneaker.enprice+'

';
    }else{
        str += '

Price

'+sneaker.koprice.toString().replace(/\B(?=(\d{3})+(?!\d))/g, "","")+'원
';
    };
    str += '

';
    str += '
Via. '+((lang==""en"") ? ""nike.com"" : ""nike.co.kr"")+'
';
    return str;
};

// 스니커 아카이브 파일 만들기
function makeTistoryHtml(sneaker){
    var desc = (sneaker.kodesc1) ? sneaker.kodesc1 : sneaker.endesc1;
    var str = '
'+sneaker.enname+' ""'+sneaker.ennick+'"" | '+desc+'|||
';
    str += '
';
    str += '

';
    if(sneaker.tistoryimage!=""""){
        str += sneaker.tistoryimage;
    }else{
        str += '이미지';
    };
    str += '

image-source : Nike.com

';
    str += '
';
    str += '

'+sneaker.enname+' ""'+sneaker.ennick+'""
';
    str += '

Style

'+sneaker.style+'-'+sneaker.code+'

';
    str += '

Color

'+sneaker.encolor+'
'+( (sneaker.kocolor!="""" && sneaker.kocolor!=""미발매"") ? '
'+sneaker.kocolor+'
':'')+'
';
    str += '

Release Date

'+sneaker.endate+'
'+( (sneaker.kodate!="""" && sneaker.kodate!=""미발매"") ? '
'+sneaker.kodate+'
':'')+'
';
    str += '

Price

$'+sneaker.enprice+'
'+( (sneaker.koprice) ? '
'+sneaker.koprice.toString().replace(/\B(?=(\d{3})+(?!\d))/g, "","")+'원
':'')+'
';
    str += '

';
    str += '
';
    str += '
<button type=""button"" onclick=""loadimg(\''+sneaker.style+'\',\''+sneaker.code+'\');"">Images More</button>

';
    if(sneaker.desc1!=""""){
        str += '

'+sneaker.kodesc1+'
'+((sneaker.kodesc2) ? '
'+sneaker.kodesc2+'
' : '')+'
';
        str += '

'+sneaker.endesc1+'
'+((sneaker.endesc2) ? '
'+sneaker.endesc2+'
' : '')+'
';
    };
    str += '
Via. '+((sneaker.enlink) ? 'nike.com' : '')+' '+((sneaker.kolink) ? 'nike.co.kr' : '')+'
';
    str += '

Market Place(link)
';
    str += '

Nike Inc.

';
    str += 'nike.com';
    str += ', nike.co.kr';
    str += '

';
    str += '

Select Shop(en)

';
    str += 'footlocker';
    str += ', eastbay';
    str += ', finishline';
    str += ', sneakersnstuff';
    str += ', endclothing';
    str += ', ruvilla';
    str += '

';
    str += '

Select Shop(ko)

';
    str += '풋셀스토어';
    str += ', 훕시티온라인';
    str += ', 플레이어';
    str += ', 포포몰';
    str += '

';
    str += '

Search Engine

';
    str += '네이버 지식쇼핑';
    str += ', 에누리 가격비교';
    str += ', 다나와 가격비교';
    str += '

';
    str += '

';
    str += '
';

    return str;
};<commit_msg>go!go<commit_after>var fs = require('fs');
var _ = require("underscore");
var request = require("request");
var url = "https://spreadsheets.google.com/feeds/list/1pOlPpmQkwQhXSFMDx6wryFhvQL8tjf1gbbiJQLRvDPs/1/public/values?alt=json"
// var url = "./temp.json";
    request({
        url: url,
        json: true
    }, function (error, response, DB) {
        console.log(error);
        if (!error && response.statusCode === 200) {
            // console.log(DB) // Print the json response

            var all = _.map(DB.feed.entry,function(data){
                var o = {};
                // 1차정제(스프레드시트 데이터)
                for(key in data){
                    if(key.indexOf("gsx$")!= -1){
                        var nkey = key.split("gsx$")[1];
                        o[nkey] = data[key].$t;
                    }
                }
                return o;
            });

            var sneakers = _.filter(all,function(sneaker){
                return (sneaker.view != "");
            });

            var getSortSelectData =[]; // 브랜드 정보만
            _.each(sneakers,function(item){
                getSortSelectData.push(item.no);
            });
            getSortSelectData = _.uniq(getSortSelectData);

            // 스니커 페이지 생성
            _.each(sneakers,function(sneaker){
                // 아카이브 생성
                try{
                    fs.mkdirSync('sneakers/'+sneaker.brand+''+sneaker.no);
                }catch(e){
                    if ( e.code != 'EEXIST' ) throw e;
                }
                fs.writeFileSync('sneakers/'+sneaker.brand+''+sneaker.no+'/'+sneaker.style+'-'+sneaker.code+'.html', makeTistoryHtml(sneaker));
                console.log(sneaker.style+"-"+sneaker.code+" file created.");

                // 릴리즈 생성
                try{
                    fs.mkdirSync('release/'+sneaker.brand+''+sneaker.no);
                }catch(e){
                    if ( e.code != 'EEXIST' ) throw e;
                }
                if(sneaker.enrelease){
                    fs.writeFileSync('release/'+sneaker.brand+''+sneaker.no+'/en_'+sneaker.style+'-'+sneaker.code+'.html', makeReleaseHtml(sneaker,"en"));
                };
                if(sneaker.korelease){
                    fs.writeFileSync('release/'+sneaker.brand+''+sneaker.no+'/ko_'+sneaker.style+'-'+sneaker.code+'.html', makeReleaseHtml(sneaker,"ko"));
                };
            });
        }
    });

// 발매일정(릴리즈) 만들기
function makeReleaseHtml(sneaker,lang){
    var str = '';
    str += '
'+sneaker[lang+"release"]+'

';
    str += '

'+sneaker[lang+"desc1"]+'
'+((sneaker[lang+"desc2"]) ? '
'+sneaker[lang+"desc2"]+'
' : '')+'
';
    str += '

'+sneaker[lang+"name"]+' "'+sneaker[lang+"nick"]+'"
';
    str += '

Style

'+sneaker.style+'-'+sneaker.code+'

';
    str += '

Color

'+sneaker[lang+"color"]+'

';
    str += '

Release Date

'+sneaker[lang+"date"]+'

';
    if(lang=="en"){
        str += '

Price

$'+sneaker.enprice+'

';
    }else{
        str += '

Price

'+sneaker.koprice.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")+'원
';
    };
    str += '

';
    str += '
Via. '+((lang=="en") ? "nike.com" : "nike.co.kr")+'
';
    return str;
};

// 스니커 아카이브 파일 만들기
function makeTistoryHtml(sneaker){
    var desc = (sneaker.kodesc1) ? sneaker.kodesc1 : sneaker.endesc1;
    var str = '
'+sneaker.enname+' "'+sneaker.ennick+'" | '+desc+'|||
';
    str += '
';
    str += '

';
    if(sneaker.tistoryimage!=""){
        str += sneaker.tistoryimage;
    }else{
        str += '이미지';
    };
    str += '

image-source : Nike.com

';
    str += '
';
    str += '

'+sneaker.enname+' "'+sneaker.ennick+'"
';
    str += '

Style

'+sneaker.style+'-'+sneaker.code+'

';
    str += '

Color

'+sneaker.encolor+'
'+( (sneaker.kocolor!="" && sneaker.kocolor!="미발매") ? '
'+sneaker.kocolor+'
':'')+'
';
    str += '

Release Date

'+sneaker.endate+'
'+( (sneaker.kodate!="" && sneaker.kodate!="미발매") ? '
'+sneaker.kodate+'
':'')+'
';
    str += '

Price

$'+sneaker.enprice+'
'+( (sneaker.koprice) ? '
'+sneaker.koprice.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")+'원
':'')+'
';
    str += '

';
    str += '
';
    str += '
<button type="button" onclick="loadimg(\''+sneaker.style+'\',\''+sneaker.code+'\');">Images More</button>

';
    if(sneaker.desc1!=""){
        str += '

'+sneaker.kodesc1+'
'+((sneaker.kodesc2) ? '
'+sneaker.kodesc2+'
' : '')+'
';
        str += '

'+sneaker.endesc1+'
'+((sneaker.endesc2) ? '
'+sneaker.endesc2+'
' : '')+'
';
    };
    str += '
Via. '+((sneaker.enlink) ? 'nike.com' : '')+' '+((sneaker.kolink) ? 'nike.co.kr' : '')+'
';
    str += '

Market Place(link)
';
    str += '

Nike Inc.

';
    str += 'nike.com';
    str += ', nike.co.kr';
    str += '

';
    str += '

Select Shop(en)

';
    str += 'footlocker';
    str += ', eastbay';
    str += ', finishline';
    str += ', sneakersnstuff';
    str += ', endclothing';
    str += ', ruvilla';
    str += '

';
    str += '

Select Shop(ko)

';
    str += '풋셀스토어';
    str += ', 훕시티온라인';
    str += ', 플레이어';
    str += ', 포포몰';
    str += '

';
    str += '

Search Engine

';
    str += '네이버 지식쇼핑';
    str += ', 에누리 가격비교';
    str += ', 다나와 가격비교';
    str += '

';
    str += '

';
    str += '
';

    return str;
};<|endoftext|>


<commit_before>/**
 * LMLJS Framework
 * Copyright (c) 2014 http://lmlphp.com All rights reserved.
 * Licensed ( http://mit-license.org/ )
 * Author: leiminglin <leiminglin@126.com>
 *
 * A lovely javascript framework.
 *
 * $id: $
 *
 */
(function(win, doc, undf){

	var createDeferred = function(){
		var deferred = {};
		deferred.queue = [];
		deferred.promise = function(){
			if( deferred.queue.length ){
				deferred.queue.shift()();
			}
		};
		deferred.then = function(e){
			deferred.queue.push(e);
		};
		return deferred;
	};

	var deferred = createDeferred();

	function createWithJs(){

		var loadedJs = {};

		function withJs(js, callback){
			var cb = function(){
				if(loadedJs[js].loaded){
					callback();
					loadedJs[js].callback.promise();
				}else{
					loadJs(js, function(){
						callback();
						loadedJs[js].loaded = true;
						loadedJs[js].callback.promise();
					});
				}
			};
			if(!loadedJs[js]){
				var def = createDeferred();
				def.then(cb);
				loadedJs[js] = {
					'loaded': false,
					'callback': def
				};
			}else{
				loadedJs[js].callback.then(cb);
			}
		}

		withJs.start = function(){
			for(var i in loadedJs){
				loadedJs[i].callback.promise();
			}
		};

		return withJs;
	}

	var withJs = createWithJs();

	function getElementViewTop(element){
		var actualTop = element.offsetTop
			,offsetParentElement = element.offsetParent;
		if( offsetParentElement == null && element.parentNode ){
			/* when style display is none */
			var parentNode = element.parentNode;
			while( offsetParentElement == null ){
				offsetParentElement = parentNode.offsetParent;
				parentNode = parentNode.parentNode;
				if( !parentNode ){
					break;
				}
			}
		}
		while ( offsetParentElement !== null /* document.body */ ){
			actualTop += (offsetParentElement.offsetTop+offsetParentElement.clientTop);
			offsetParentElement = offsetParentElement.offsetParent;
		}

		var pageScrollTop;
		if ( typeof win.pageYOffset === 'number' ) {
			pageScrollTop = win.pageYOffset;
		} else {
			docElement = (doc.compatMode && doc.compatMode === 'CSS1Compat')
			? doc.documentElement : doc.body;
			pageScrollTop = docElement.scrollTop;
		}

		return actualTop - pageScrollTop;
	}

	function getViewport(){
		if( doc.compatMode && doc.compatMode == "BackCompat" && doc.body){
			return { width : doc.body.clientWidth,
				height : doc.body.clientHeight }
		}else{
			return { width : doc.documentElement.clientWidth,
				height : doc.documentElement.clientHeight }
		}
	}

	/**
	 * Lazy load img
	 */
	deferred.then(function(){
		var i, length, src, m = doc.getElementsByTagName('IMG'),
			viewport=getViewport(), count=0;
		window.onresize = function(){
			viewport = getViewport();
		};
		var loadImg = function(){
			if( count >= m.length ){
				/* remove event */
				if( window.addEventListener ){
					doc.removeEventListener( 'scroll', loadImg, false );
				}else if( window.attachEvent ){
					window.detachEvent(event, loadImg);
				}
				return;
			}
			for(i=0,j=m.length; i<j; i++){
				if( m[i].getAttribute('src') ){
					continue;
				}
				var viewtop = getElementViewTop(m[i])
					,imgHeight = m[i].getAttribute('height')||0;
				if( viewtop>=-imgHeight && viewtop < viewport.height
					&& (src=m[i].getAttribute('osrc')) ){
					m[i].setAttribute('src',src);
					m[i].onerror=function(){
						if( src=this.getAttribute('osrc-bak') ){
							this.setAttribute('src',src);
							this.onerror=null;
						}
					};
					m[i].onload = function(){
						count++;
					}
				}
			}
		};

		if( window.addEventListener ){
			doc.addEventListener( 'scroll', loadImg, false );
		}else if( window.attachEvent ){
			window.attachEvent("onscroll", loadImg);
		}
		loadImg();
		deferred.promise();
	});

	if( typeof doc.getElementsByClassName != 'function' ){
		doc.getElementsByClassName = function( classname ){
			var d = doc, e = d.getElementsByTagName('*'),
				c = new RegExp('\\b'+classname+'\\b'), r = [];
			for( var i=0,l=e.length; i<l; i++ ){
				var classname = e[i].className;
				if( c.test(classname) ){
					r.push( e[i] );
				}
			}
			return r;
		}
	}

	var addLazyCss = function( css ) {
		var style = doc.createElement('style');
		style.type='text/css';
		if (style.styleSheet) {
			style.styleSheet.cssText = css;
		}else{
			style.innerHTML = css;
		}
		doc.getElementsByTagName('head')[0].appendChild(style);
	};

	/**
	 * Lazy load CSS
	 */
	deferred.then( function(){
		var e = doc.getElementsByClassName('lazyCss');
		for( var i=0; i<e.length; i++ ) {
			addLazyCss( e[i].value || e[i].innerHTML );
		}
		deferred.promise();
	});

	/**
	 * Lazy load HTML
	 */
	deferred.then( function(){
		var e = doc.getElementsByClassName('lazyHtml');
		for( var i=0; i<e.length; i++ ) {
			if(e[i].tagName == 'TEXTAREA'){
				var wrapdiv = doc.createElement('DIV');
				wrapdiv.innerHTML = e[i].value;
				e[i].parentNode.insertBefore(wrapdiv, e[i]);
			}
		}
		deferred.promise();
	});

	var loadJs = function( src, callback, script, stag ) {
		script = doc.createElement('script'),
		stag = doc.getElementsByTagName('script')[0];
		script.async = 1;
		script.src = src;
		try{
			stag.parentNode.insertBefore( script, stag );
			callback = callback || function(){
				deferred.promise();
			};
			if( window.addEventListener ){
				script.addEventListener( 'load', callback, false );
			}else if( window.attachEvent ){
				script.onreadystatechange = function(){
					if(this.readyState.match(/loaded|complete/i)){
						callback();
					}
				}
			}else{
				script.onload = function(){
					callback();
				}
			}
		}catch(e){
			callback();
		}
	};

	var lml = {};
	lml.deferred = deferred;
	lml.createDeferred = createDeferred;
	lml.loadJs = loadJs;
	lml.withJs = withJs;
	lml.run = function(){
		deferred.promise();
		withJs.start();
	};

	win.lml = lml;

})(window, document);

<commit_msg>combile loadJs and withJs<commit_after>/**
 * LMLJS Framework
 * Copyright (c) 2014 http://lmlphp.com All rights reserved.
 * Licensed ( http://mit-license.org/ )
 * Author: leiminglin <leiminglin@126.com>
 *
 * A lovely javascript framework.
 *
 * $id: $
 *
 */
(function(win, doc, undf){

	var createDeferred = function(){
		var deferred = {};
		deferred.queue = [];
		deferred.promise = function(){
			if( deferred.queue.length ){
				deferred.queue.shift()();
			}
		};
		deferred.then = function(e){
			deferred.queue.push(e);
		};
		return deferred;
	};

	var deferred = createDeferred();

	function createWithJs(){

		var loadedJs = {};

		function loadJs( src, callback, script, stag ) {
			script = doc.createElement('script'),
			stag = doc.getElementsByTagName('script')[0];
			script.async = 1;
			script.src = src;
			try{
				stag.parentNode.insertBefore( script, stag );
				callback = callback || function(){
					deferred.promise();
				};
				if( window.addEventListener ){
					script.addEventListener( 'load', callback, false );
				}else if( window.attachEvent ){
					script.onreadystatechange = function(){
						if(this.readyState.match(/loaded|complete/i)){
							callback();
						}
					}
				}else{
					script.onload = function(){
						callback();
					}
				}
			}catch(e){
				callback();
			}
		};

		function withJs(js, callback, isForceAppend){
			var cb = function(){
				if(!loadedJs[js].loaded || isForceAppend+'' == '1'){
					loadJs(js, function(){
						callback();
						loadedJs[js].loaded = true;
						loadedJs[js].callback.promise();
					});
				}else{
					callback();
					loadedJs[js].callback.promise();
				}
			};
			if(!loadedJs[js]){
				var def = createDeferred();
				def.then(cb);
				loadedJs[js] = {
					'loaded': false,
					'callback': def
				};
			}else{
				loadedJs[js].callback.then(cb);
			}
		}

		withJs.start = function(){
			for(var i in loadedJs){
				loadedJs[i].callback.promise();
			}
		};

		return withJs;
	}

	var loadJs = createWithJs();

	function getElementViewTop(element){
		var actualTop = element.offsetTop
			,offsetParentElement = element.offsetParent;
		if( offsetParentElement == null && element.parentNode ){
			/* when style display is none */
			var parentNode = element.parentNode;
			while( offsetParentElement == null ){
				offsetParentElement = parentNode.offsetParent;
				parentNode = parentNode.parentNode;
				if( !parentNode ){
					break;
				}
			}
		}
		while ( offsetParentElement !== null /* document.body */ ){
			actualTop += (offsetParentElement.offsetTop+offsetParentElement.clientTop);
			offsetParentElement = offsetParentElement.offsetParent;
		}

		var pageScrollTop;
		if ( typeof win.pageYOffset === 'number' ) {
			pageScrollTop = win.pageYOffset;
		} else {
			docElement = (doc.compatMode && doc.compatMode === 'CSS1Compat')
			? doc.documentElement : doc.body;
			pageScrollTop = docElement.scrollTop;
		}

		return actualTop - pageScrollTop;
	}

	function getViewport(){
		if( doc.compatMode && doc.compatMode == "BackCompat" && doc.body){
			return { width : doc.body.clientWidth,
				height : doc.body.clientHeight }
		}else{
			return { width : doc.documentElement.clientWidth,
				height : doc.documentElement.clientHeight }
		}
	}

	/**
	 * Lazy load img
	 */
	deferred.then(function(){
		var i, length, src, m = doc.getElementsByTagName('IMG'),
			viewport=getViewport(), count=0;
		window.onresize = function(){
			viewport = getViewport();
		};
		var loadImg = function(){
			if( count >= m.length ){
				/* remove event */
				if( window.addEventListener ){
					doc.removeEventListener( 'scroll', loadImg, false );
				}else if( window.attachEvent ){
					window.detachEvent(event, loadImg);
				}
				return;
			}
			for(i=0,j=m.length; i<j; i++){
				if( m[i].getAttribute('src') ){
					continue;
				}
				var viewtop = getElementViewTop(m[i])
					,imgHeight = m[i].getAttribute('height')||0;
				if( viewtop>=-imgHeight && viewtop < viewport.height
					&& (src=m[i].getAttribute('osrc')) ){
					m[i].setAttribute('src',src);
					m[i].onerror=function(){
						if( src=this.getAttribute('osrc-bak') ){
							this.setAttribute('src',src);
							this.onerror=null;
						}
					};
					m[i].onload = function(){
						count++;
					}
				}
			}
		};

		if( window.addEventListener ){
			doc.addEventListener( 'scroll', loadImg, false );
		}else if( window.attachEvent ){
			window.attachEvent("onscroll", loadImg);
		}
		loadImg();
		deferred.promise();
	});

	if( typeof doc.getElementsByClassName != 'function' ){
		doc.getElementsByClassName = function( classname ){
			var d = doc, e = d.getElementsByTagName('*'),
				c = new RegExp('\\b'+classname+'\\b'), r = [];
			for( var i=0,l=e.length; i<l; i++ ){
				var classname = e[i].className;
				if( c.test(classname) ){
					r.push( e[i] );
				}
			}
			return r;
		}
	}

	var addLazyCss = function( css ) {
		var style = doc.createElement('style');
		style.type='text/css';
		if (style.styleSheet) {
			style.styleSheet.cssText = css;
		}else{
			style.innerHTML = css;
		}
		doc.getElementsByTagName('head')[0].appendChild(style);
	};

	/**
	 * Lazy load CSS
	 */
	deferred.then( function(){
		var e = doc.getElementsByClassName('lazyCss');
		for( var i=0; i<e.length; i++ ) {
			addLazyCss( e[i].value || e[i].innerHTML );
		}
		deferred.promise();
	});

	/**
	 * Lazy load HTML
	 */
	deferred.then( function(){
		var e = doc.getElementsByClassName('lazyHtml');
		for( var i=0; i<e.length; i++ ) {
			if(e[i].tagName == 'TEXTAREA'){
				var wrapdiv = doc.createElement('DIV');
				wrapdiv.innerHTML = e[i].value;
				e[i].parentNode.insertBefore(wrapdiv, e[i]);
			}
		}
		deferred.promise();
	});

	var lml = {};
	lml.deferred = deferred;
	lml.createDeferred = createDeferred;
	lml.loadJs = loadJs;
	lml.run = function(){
		deferred.promise();
		loadJs.start();
	};

	win.lml = lml;

})(window, document);

<|endoftext|>


<commit_before>'use strict';

var fs = require("fs");

var hl = {};

hl.tokenize = function(script) {
	function escapeSpacesInStrings(string) {
		return string.replace(/"[^"]*"/g, function(match) {
			return match.replace(/ /g, "%%s%%");
		});
	}

	function unescapeSpacesInStrings(string) {
		return string.replace(/%%s%%/g, " ");
	}

	script = script.replace(/\/\/.*/g, ""); // Line comment
	script = script.replace(/\/\*[^*]*\*\//g, " "); // Block comment
	script = script.replace(/(\t*)(.+,.+)+/g, function(match, group1, group2){
		return group1 + group2.split(",").join("\r\n" + group1);
	});
	var lines = script.split(/(\r\n)+/);
	var tokenLists = [];
	var inComment = false;
	for (var i in lines) {
		var line = lines[i];

		line = escapeSpacesInStrings(line);
		if (/^\s*$/.test(line)) continue;

		line = line.replace(/\t/g, " tab ");
		line = line.replace(/[\.:()]/g, " $& ");
		var tokens = line.trim().split(/\s+/);

		var tokenList = [];
		for (var i in tokens) {
			tokenList.push(unescapeSpacesInStrings(tokens[i]));
		}

		tokenLists.push(tokenList);
	}
	tokenLists.push(["EOF"]);
	//consoleconsole.log(tokenLists);
	return tokenLists;
}

hl.parse = function(tokenLists) {
	function countTabs(tokens) {
		var numberOfTabs = 0;
		for (var j in tokens) {
			if (tokens[j] == "tab")
				numberOfTabs++;
			else
				break;
		}
		return numberOfTabs;
	}

	function evalLine(tokens, indent) {
		if (indent == tokens.length-1) return tokens[indent];

		var stack = [{service: null, action: null, args: null}];
		var types = ["service", "action", "args"];
		var root = stack[0];
		var typeCounter = 0;

		for (var i = indent; i < tokens.length; i++) {
			var symbol = tokens[i];
			if (symbol == "(") {
				stack.push({root: root, typeCounter: typeCounter});
				root = {service: null, action: null, args: null};
				typeCounter = 0;
			} else if (symbol == ")") {
				var popped = stack.pop();
				typeCounter = popped.typeCounter;
				popped.root[types[typeCounter]] = root;
				root = popped.root;
				typeCounter++;
			} else {
				if (typeCounter >= 3 && typeCounter%2==1) {
					var newRoot = {service: root, action: symbol, args: null};
					root = newRoot;
					typeCounter = 2;
				} else {
					root[types[typeCounter]] = symbol;
					typeCounter++;
				}
			}
		}

		//console.log("evalLine: ", root, "---", stack);

		return root;
	}

	var stack = [{args: []}];
	var indent = 0;
	for (var i in tokenLists) {
		var tokens = tokenLists[i];
		var head = stack[stack.length-1];
		var nofTabs = countTabs(tokens);

		//console.log("->", tokens, indent, JSON.stringify(stack));

		if (nofTabs == indent) {
			head.args.push(evalLine(tokens, indent));
		} else if (nofTabs > indent) {
			indent++;
			stack.push({args: [evalLine(tokens, indent)]});
		} else {
			for (var t = 0; t < indent - nofTabs; t++) {
				var popped = stack.pop();
				var head = stack[stack.length-1];
				head.args[head.args.length-1].args = popped.args;
			}
			indent = nofTabs;
			head.args.push(evalLine(tokens, indent));
		}
	}

	stack[0].args.pop(); // Removing EOF

	return stack[0].args;
}

hl.setDebug = function(shouldLogDebug) {
	global.oldConsoleLog = global.oldConsoleLog || console.log;
	if (shouldLogDebug) {
		console.log = global.oldConsoleLog
	} else {
		console.log = function() {};
	}
}

var scopes = [{}];
var scopeIndex = 0;

hl.clearScope = function() {
	scopes = [{}];
	scopeIndex = 0;
}

hl.setScope = function(scopeIdx) {
	scopeIndex = scopeIdx;
}

hl.getScopes = function() {
	return scopes;
}

hl.saveToScope = function(key, value) {
	scopes[scopeIndex][key] = value;
}

hl.changeInScope = function(key, value) {
	var result = hl.searchScope(key, value);
}

hl.pushScope = function(withoutEntering) {
	var newScope = {parent: scopeIndex};
	var newIndex = scopes.length;
	scopes.push(newScope);
	if (withoutEntering == undefined || withoutEntering == false)
		scopeIndex = newIndex;
	else
		return newIndex;
}

hl.popScope = function() {
	scopeIndex = scopes[scopeIndex].parent;
}

hl.searchScope = function(key, newValue) {
	var index = scopeIndex;
	console.log("searching for", key, "from", index, "(" + newValue + ")");
	while (index != undefined) {
		var scope = scopes[index];
		var result = scope[key];
		console.log("index, result =", index, result);
		if (result) {
			if (newValue) scope[key] = newValue;
			return result;
		}
		index = scope.parent;
	}
	throw new Error("Not found in scope: " + key);
}

hl.printScopes = function() {
	function naiveClone(obj) {
		if (null == obj || "object" != typeof obj) return obj;
		var copy = obj.constructor();
		for (var attr in obj) {
			if (obj.hasOwnProperty(attr)) copy[attr] = naiveClone(obj[attr]);
		}
		return copy;
	}

	function replaceCode(obj) {
		for (var i in obj) {
			var field = obj[i];
			if (field && field.code) {
				field.code = "(..)";
			}
		}
	}
	console.log("scopeIndex:", scopeIndex);
	for (var i in scopes) {
		var scope = naiveClone(scopes[i]);
		replaceCode(scope)
		console.log(i + ": " + JSON.stringify(scope));
	}
}

hl.getServiceType = function(service) {
	if (service == undefined || service == null)
		return null;

	if (service instanceof Array)
		return "Array";
	else if (service[0] == "\"")
		return "String";
	else if (!isNaN(parseInt(service)))
		return "Number";
	else if (service === true || service === "true" || service === false || service === "false")
		return "Boolean"
	else if (service == "#")
		return "Scope";
	else if (service.type == "ScopeReference")
		return "ScopeReference";
	else  if (service.type == "Variable")
		return "Variable";
	else
		return "Service";
}

hl.evaluate = function(trees, returnLast, makeNewScope) {

	function doAction(service, action, args) {
		var serviceType = hl.getServiceType(service);

		console.log("");
		console.log("--- doAction:", service, action, args, "(returnLast=" + returnLast + ", scopeIndex="+scopeIndex+")");
		console.log("->" + serviceType);
		hl.printScopes();

		if (serviceType == null) return null;

		function fail() {
			throw new Error(action + " is not a valid action on " + serviceType);
		}

		if (serviceType == "Array") {
			if (action == "loop") {
				for (var i in service) {
					hl.saveToScope("element", service[i]);
					var args = hl.evaluate(tree.args, returnLast);
				}
			} else fail();
		} else if (serviceType == "String") {
			var args = hl.evaluate(tree.args, returnLast);
			var a = service.substring(1, service.length-1);
			var b = args.substring(1, args.length-1);
			if (action == "+") {
				return "\"" + a + b + "\"";
			} else if (action == "==") {
				return a === b;
			} else if (action == "!=") {
				return a !== b;
			} else fail();
		} else if (serviceType == "Number") {
			var a = parseInt(service);

			if (action == "until") {
				var result = undefined;
				while(result != a) {
					var result = hl.evaluate(tree.args, returnLast);
				}
				return null;
			} else if (action == "as") {
				if (args == "string")
					return "\"" + a + "\"";
				else
					throw new Error("Can not convert " + serviceType + " to " + args);
			}

			var args = hl.evaluate(tree.args, returnLast);

			var b = parseInt(args);

			if (action == "+") {
				return a + b;
			} else if (action == "-") {
				return a - b;
			} else if (action == "==") {
				return a === b;
			} else if (action == "<") {
				return a < b;
			} else if (action == ">") {
				return a > b;
			} else fail();
		} else if (serviceType == "Boolean") {
			if (action == "==") {
				var args = hl.evaluate(tree.args, returnLast);
				return (service == "true") === (args == "true");
			} else if (action == "!=") {
				var args = hl.evaluate(tree.args, returnLast);
				return (service == "true") !== (args == "true");
			} else if (action == "then") {
				if (service === true || service === "true") hl.evaluate(args, returnLast);
			} else fail();
		} else if (serviceType == "Scope") {
			var args = hl.evaluate(tree.args, returnLast);
			if (action == "new") {
				return {type: "Variable", action: "new", name: args};
			} else if (action == "set") {
				return {type: "Variable", action: "set", name: args};
			} else if (action == ".") {
				return hl.searchScope(args);
			} else { // TODO: DRY!
				var oldScopeIndex = scopeIndex;

				var codeHolder = hl.searchScope(action);
				var code = codeHolder.code ? codeHolder.code : codeHolder;
				scopeIndex = codeHolder.scope ? codeHolder.scope : scopeIndex;

				hl.saveToScope("argument", args);
				var result = hl.evaluate(code, true);
				scopeIndex = oldScopeIndex;
				return result;
			}
		} else if (serviceType == "Variable") {
			var saveFunction = service.action == "new" ? hl.saveToScope : hl.changeInScope;
			if (action == "=") {
				var args = hl.evaluate(tree.args, false);
				saveFunction(service.name, args);
				return args;
			} else if (action == ":") {
				var newScopeIndex = hl.pushScope(true);
				saveFunction(service.name, {code: tree.args, scope: newScopeIndex});
			} else if (action == "#") {
				var nextScopeIndex = scopes.length+1;
				var args = hl.evaluate(tree.args, false, true);
				saveFunction(service.name, {type: "ScopeReference", scope: nextScopeIndex});
			} else fail();
		} else if (serviceType == "Service") { // TODO: DRY!
			var oldScopeIndex = scopeIndex;

			scopeIndex = hl.searchScope(service)["scope"];
			var code = hl.searchScope(action)["code"];

			hl.saveToScope("argument", args);
			var result = hl.evaluate(code, true);
			scopeIndex = oldScopeIndex;
			return result;
		} else if (serviceType == "ScopeReference") { // TODO: DRY!
			var oldScopeIndex = scopeIndex;

			scopeIndex = service.scope;
			var code = hl.searchScope(action)["code"];

			hl.saveToScope("argument", args);
			var result = hl.evaluate(code, true);
			scopeIndex = oldScopeIndex;
			return result;
		}

		return null;
	}

	makeNewScope = makeNewScope || false;
	if (returnLast == undefined) returnLast = true;

	if (!(trees instanceof Array)) {
		trees = [trees];
	}

	var result = [];

	if (makeNewScope) hl.pushScope();

	for (var i in trees) {
		var tree = trees[i];

		console.log("");
		console.log("--- evalTree:", tree, "scopeIndex=" + scopeIndex);
		//hl.printScopes();

		if (tree != null && tree.service) {
			var evaluatedService = hl.evaluate(tree.service, returnLast);
			var args = tree.args;
			if (!(tree.args instanceof Array)) var args = hl.evaluate(tree.args, returnLast); // If argument is array, it should be possible for the action implementation to choose to evaluate or not. If the argument is not array, we must evaluate it NOW before the scope is changed. Or else #.argument and other values that only exist in THIS scope can not be used as args in function calls.
			result.push(doAction(evaluatedService, tree.action, args));
		} else {
			result.push(tree);
		}

		console.log("result " + i + ":", result);
	}

	if (makeNewScope) hl.popScope();

	if (returnLast)
		return result[result.length-1];
	else if (result.length == 1)
		return result[0];
	else
		return result;
}

hl.execute = function(script) {
	var tokens = hl.tokenize(script);
	var trees = hl.parse(tokens);
	var result = hl.evaluate(trees);
	return result;
}

if (process.argv[2]) {
	var fileName = process.argv[2];
	var fileContents = fs.readFileSync(fileName, "utf8");
	hl.setDebug(false);
	var result = hl.execute(fileContents);

	process.stdout.write(JSON.stringify(result));
}

module.exports = hl;
<commit_msg>Making it possible to declare variables without setting a value<commit_after>'use strict';

var fs = require("fs");

var hl = {};

hl.tokenize = function(script) {
	function escapeSpacesInStrings(string) {
		return string.replace(/"[^"]*"/g, function(match) {
			return match.replace(/ /g, "%%s%%");
		});
	}

	function unescapeSpacesInStrings(string) {
		return string.replace(/%%s%%/g, " ");
	}

	script = script.replace(/\/\/.*/g, ""); // Line comment
	script = script.replace(/\/\*[^*]*\*\//g, " "); // Block comment
	script = script.replace(/(\t*)(.+,.+)+/g, function(match, group1, group2){
		return group1 + group2.split(",").join("\r\n" + group1);
	});
	var lines = script.split(/(\r\n)+/);
	var tokenLists = [];
	var inComment = false;
	for (var i in lines) {
		var line = lines[i];

		line = escapeSpacesInStrings(line);
		if (/^\s*$/.test(line)) continue;

		line = line.replace(/\t/g, " tab ");
		line = line.replace(/[\.:()]/g, " $& ");
		var tokens = line.trim().split(/\s+/);

		var tokenList = [];
		for (var i in tokens) {
			tokenList.push(unescapeSpacesInStrings(tokens[i]));
		}

		tokenLists.push(tokenList);
	}
	tokenLists.push(["EOF"]);
	//consoleconsole.log(tokenLists);
	return tokenLists;
}

hl.parse = function(tokenLists) {
	function countTabs(tokens) {
		var numberOfTabs = 0;
		for (var j in tokens) {
			if (tokens[j] == "tab")
				numberOfTabs++;
			else
				break;
		}
		return numberOfTabs;
	}

	function evalLine(tokens, indent) {
		if (indent == tokens.length-1) return tokens[indent];

		var stack = [{service: null, action: null, args: null}];
		var types = ["service", "action", "args"];
		var root = stack[0];
		var typeCounter = 0;

		for (var i = indent; i < tokens.length; i++) {
			var symbol = tokens[i];
			if (symbol == "(") {
				stack.push({root: root, typeCounter: typeCounter});
				root = {service: null, action: null, args: null};
				typeCounter = 0;
			} else if (symbol == ")") {
				var popped = stack.pop();
				typeCounter = popped.typeCounter;
				popped.root[types[typeCounter]] = root;
				root = popped.root;
				typeCounter++;
			} else {
				if (typeCounter >= 3 && typeCounter%2==1) {
					var newRoot = {service: root, action: symbol, args: null};
					root = newRoot;
					typeCounter = 2;
				} else {
					root[types[typeCounter]] = symbol;
					typeCounter++;
				}
			}
		}

		//console.log("evalLine: ", root, "---", stack);

		return root;
	}

	var stack = [{args: []}];
	var indent = 0;
	for (var i in tokenLists) {
		var tokens = tokenLists[i];
		var head = stack[stack.length-1];
		var nofTabs = countTabs(tokens);

		//console.log("->", tokens, indent, JSON.stringify(stack));

		if (nofTabs == indent) {
			head.args.push(evalLine(tokens, indent));
		} else if (nofTabs > indent) {
			indent++;
			stack.push({args: [evalLine(tokens, indent)]});
		} else {
			for (var t = 0; t < indent - nofTabs; t++) {
				var popped = stack.pop();
				var head = stack[stack.length-1];
				head.args[head.args.length-1].args = popped.args;
			}
			indent = nofTabs;
			head.args.push(evalLine(tokens, indent));
		}
	}

	stack[0].args.pop(); // Removing EOF

	return stack[0].args;
}

hl.setDebug = function(shouldLogDebug) {
	global.oldConsoleLog = global.oldConsoleLog || console.log;
	if (shouldLogDebug) {
		console.log = global.oldConsoleLog
	} else {
		console.log = function() {};
	}
}

var scopes = [{}];
var scopeIndex = 0;

hl.clearScope = function() {
	scopes = [{}];
	scopeIndex = 0;
}

hl.setScope = function(scopeIdx) {
	scopeIndex = scopeIdx;
}

hl.getScopes = function() {
	return scopes;
}

hl.saveToScope = function(key, value) {
	scopes[scopeIndex][key] = value;
}

hl.changeInScope = function(key, value) {
	var result = hl.searchScope(key, value);
}

hl.pushScope = function(withoutEntering) {
	var newScope = {parent: scopeIndex};
	var newIndex = scopes.length;
	scopes.push(newScope);
	if (withoutEntering == undefined || withoutEntering == false)
		scopeIndex = newIndex;
	else
		return newIndex;
}

hl.popScope = function() {
	scopeIndex = scopes[scopeIndex].parent;
}

hl.searchScope = function(key, newValue) {
	var index = scopeIndex;
	console.log("searching for", key, "from", index, "(" + newValue + ")");
	while (index != undefined) {
		var scope = scopes[index];
		var result = scope[key];
		console.log("index, result =", index, result);
		if (result !== undefined) {
			if (newValue !== undefined) scope[key] = newValue;
			return result;
		}
		index = scope.parent;
	}
	throw new Error("Not found in scope: " + key);
}

hl.printScopes = function() {
	function naiveClone(obj) {
		if (null == obj || "object" != typeof obj) return obj;
		var copy = obj.constructor();
		for (var attr in obj) {
			if (obj.hasOwnProperty(attr)) copy[attr] = naiveClone(obj[attr]);
		}
		return copy;
	}

	function replaceCode(obj) {
		for (var i in obj) {
			var field = obj[i];
			if (field && field.code) {
				field.code = "(..)";
			}
		}
	}
	console.log("scopeIndex:", scopeIndex);
	for (var i in scopes) {
		var scope = naiveClone(scopes[i]);
		replaceCode(scope)
		console.log(i + ": " + JSON.stringify(scope));
	}
}

hl.getServiceType = function(service) {
	if (service == undefined || service == null)
		return null;

	if (service instanceof Array)
		return "Array";
	else if (service[0] == "\"")
		return "String";
	else if (!isNaN(parseInt(service)))
		return "Number";
	else if (service === true || service === "true" || service === false || service === "false")
		return "Boolean"
	else if (service == "#")
		return "Scope";
	else if (service.type == "ScopeReference")
		return "ScopeReference";
	else  if (service.type == "Variable")
		return "Variable";
	else
		return "Service";
}

hl.evaluate = function(trees, returnLast, makeNewScope) {

	function doAction(service, action, args) {
		var serviceType = hl.getServiceType(service);

		console.log("");
		console.log("--- doAction:", service, action, args, "(returnLast=" + returnLast + ", scopeIndex="+scopeIndex+")");
		console.log("->" + serviceType);
		hl.printScopes();

		if (serviceType == null) return null;

		function fail() {
			throw new Error(action + " is not a valid action on " + serviceType);
		}

		if (serviceType == "Array") {
			if (action == "loop") {
				for (var i in service) {
					hl.saveToScope("element", service[i]);
					var args = hl.evaluate(tree.args, returnLast);
				}
			} else fail();
		} else if (serviceType == "String") {
			var args = hl.evaluate(tree.args, returnLast);
			var a = service.substring(1, service.length-1);
			var b = args.substring(1, args.length-1);
			if (action == "+") {
				return "\"" + a + b + "\"";
			} else if (action == "==") {
				return a === b;
			} else if (action == "!=") {
				return a !== b;
			} else fail();
		} else if (serviceType == "Number") {
			var a = parseInt(service);

			if (action == "until") {
				var result = undefined;
				while(result != a) {
					var result = hl.evaluate(tree.args, returnLast);
				}
				return null;
			} else if (action == "as") {
				if (args == "string")
					return "\"" + a + "\"";
				else
					throw new Error("Can not convert " + serviceType + " to " + args);
			}

			var args = hl.evaluate(tree.args, returnLast);

			var b = parseInt(args);

			if (action == "+") {
				return a + b;
			} else if (action == "-") {
				return a - b;
			} else if (action == "==") {
				return a === b;
			} else if (action == "<") {
				return a < b;
			} else if (action == ">") {
				return a > b;
			} else fail();
		} else if (serviceType == "Boolean") {
			if (action == "==") {
				var args = hl.evaluate(tree.args, returnLast);
				return (service == "true") === (args == "true");
			} else if (action == "!=") {
				var args = hl.evaluate(tree.args, returnLast);
				return (service == "true") !== (args == "true");
			} else if (action == "then") {
				if (service === true || service === "true") hl.evaluate(args, returnLast);
			} else fail();
		} else if (serviceType == "Scope") {
			var args = hl.evaluate(tree.args, returnLast);
			if (action == "new") {
				hl.saveToScope(args, null);
				return {type: "Variable", name: args};
			} else if (action == "set") {
				return {type: "Variable", name: args};
			} else if (action == ".") {
				return hl.searchScope(args);
			} else { // TODO: DRY!
				var oldScopeIndex = scopeIndex;

				var codeHolder = hl.searchScope(action);
				var code = codeHolder.code ? codeHolder.code : codeHolder;
				scopeIndex = codeHolder.scope ? codeHolder.scope : scopeIndex;

				hl.saveToScope("argument", args);
				var result = hl.evaluate(code, true);
				scopeIndex = oldScopeIndex;
				return result;
			}
		} else if (serviceType == "Variable") {
			if (action == "=") {
				var args = hl.evaluate(tree.args, false);
				hl.changeInScope(service.name, args);
				return args;
			} else if (action == ":") {
				var newScopeIndex = hl.pushScope(true);
				hl.changeInScope(service.name, {code: tree.args, scope: newScopeIndex});
			} else if (action == "#") {
				var nextScopeIndex = scopes.length+1;
				var args = hl.evaluate(tree.args, false, true);
				hl.changeInScope(service.name, {type: "ScopeReference", scope: nextScopeIndex});
			} else fail();
		} else if (serviceType == "Service") { // TODO: DRY!
			var oldScopeIndex = scopeIndex;

			scopeIndex = hl.searchScope(service)["scope"];
			var code = hl.searchScope(action)["code"];

			hl.saveToScope("argument", args);
			var result = hl.evaluate(code, true);
			scopeIndex = oldScopeIndex;
			return result;
		} else if (serviceType == "ScopeReference") { // TODO: DRY!
			var oldScopeIndex = scopeIndex;

			scopeIndex = service.scope;
			var code = hl.searchScope(action)["code"];

			hl.saveToScope("argument", args);
			var result = hl.evaluate(code, true);
			scopeIndex = oldScopeIndex;
			return result;
		}

		return null;
	}

	makeNewScope = makeNewScope || false;
	if (returnLast == undefined) returnLast = true;

	if (!(trees instanceof Array)) {
		trees = [trees];
	}

	var result = [];

	if (makeNewScope) hl.pushScope();

	for (var i in trees) {
		var tree = trees[i];

		console.log("");
		console.log("--- evalTree:", tree, "scopeIndex=" + scopeIndex);
		//hl.printScopes();

		if (tree != null && tree.service) {
			var evaluatedService = hl.evaluate(tree.service, returnLast);
			var args = tree.args;
			if (!(tree.args instanceof Array)) var args = hl.evaluate(tree.args, returnLast); // If argument is array, it should be possible for the action implementation to choose to evaluate or not. If the argument is not array, we must evaluate it NOW before the scope is changed. Or else #.argument and other values that only exist in THIS scope can not be used as args in function calls.
			result.push(doAction(evaluatedService, tree.action, args));
		} else {
			result.push(tree);
		}

		console.log("result " + i + ":", result);
	}

	if (makeNewScope) hl.popScope();

	if (returnLast)
		return result[result.length-1];
	else if (result.length == 1)
		return result[0];
	else
		return result;
}

hl.execute = function(script) {
	var tokens = hl.tokenize(script);
	var trees = hl.parse(tokens);
	var result = hl.evaluate(trees);
	return result;
}

if (process.argv[2]) {
	var fileName = process.argv[2];
	var fileContents = fs.readFileSync(fileName, "utf8");
	hl.setDebug(false);
	var result = hl.execute(fileContents);

	process.stdout.write(JSON.stringify(result));
}

module.exports = hl;
<|endoftext|>


<commit_before>// Generated by CoffeeScript 1.6.3
(function() {
  var Bad, Box, BoxDispatcher, Error, Event, Fire, Jnoid, Just, Maybe, Nothing, Observable, Reply, Stop, Stream, StreamDispatcher, Wrong, all, any, append, copyArray, empty, fail, foldl, head, id, isFunction, map, method, name, nop, remove, tail, tap, toArray, toError, toEvent, toMaybe, uncurry, value, _ref, _ref1, _ref2, _ref3, _ref4,
    __hasProp = {}.hasOwnProperty,
    __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
    __slice = [].slice;

  Jnoid = {
    VERSION: "0.0.1"
  };

  Maybe = (function() {
    function Maybe() {}

    return Maybe;

  })();

  Just = (function(_super) {
    __extends(Just, _super);

    function Just(value) {
      this.value = value;
    }

    Just.prototype.getOrElse = function() {
      return this.value;
    };

    Just.prototype.filter = function(f) {
      if (this.test(f)) {
        return this;
      } else {
        return Nothing;
      }
    };

    Just.prototype.test = function(f) {
      return f(this.value);
    };

    Just.prototype.map = function(f) {
      return new Just(f(this.value));
    };

    Just.prototype.isEmpty = function() {
      return false;
    };

    Just.prototype.inspect = function() {
      return "Just " + this.value;
    };

    return Just;

  })(Maybe);

  Bad = (function(_super) {
    __extends(Bad, _super);

    function Bad() {
      _ref = Bad.__super__.constructor.apply(this, arguments);
      return _ref;
    }

    Bad.prototype.getOrElse = function(some) {
      return some;
    };

    Bad.prototype.filter = function(f) {
      return this;
    };

    Bad.prototype.test = function(f) {
      return true;
    };

    Bad.prototype.map = function(f) {
      return this;
    };

    Bad.prototype.isEmpty = function() {
      return true;
    };

    return Bad;

  })(Maybe);

  Wrong = (function(_super) {
    __extends(Wrong, _super);

    function Wrong(error) {
      this.error = error;
    }

    Wrong.prototype.inspect = function() {
      return "Wrong " + this.error;
    };

    return Wrong;

  })(Bad);

  Nothing = new ((function(_super) {
    __extends(_Class, _super);

    function _Class() {}

    _Class.prototype.inspect = function() {
      return "Nothing";
    };

    return _Class;

  })(Bad));

  _ref1 = [Maybe, Just, Wrong, Nothing], Event = _ref1[0], Fire = _ref1[1], Error = _ref1[2], Stop = _ref1[3];

  toEvent = function(x) {
    if (x instanceof Event) {
      return x;
    } else {
      return new Fire(x);
    }
  };

  toError = function(x) {
    if (x instanceof Event) {
      return x;
    } else {
      return new Error(x);
    }
  };

  Observable = (function() {
    function Observable(subscribe) {
      this.subscribe = this.dispatched(subscribe);
    }

    Observable.prototype.dispatched = fail;

    Observable.prototype.onValue = function(f) {
      return this.subscribe(function(event) {
        return event.map(function(v) {
          return f(v);
        }).getOrElse(Reply.more);
      });
    };

    Observable.prototype.onError = function(f) {
      return this.subscribe(function(event) {
        if (event instanceof Error) {
          return f(event.error);
        } else {
          return Reply.more;
        }
      });
    };

    Observable.prototype.withHandler = function(handler) {
      return new this.constructor(this.dispatched(this.subscribe, handler));
    };

    Observable.prototype.map = function(f) {
      return this.withHandler(function(event) {
        return this.push(event.map(f));
      });
    };

    Observable.prototype.filter = function(f) {
      return this.withHandler(function(event) {
        if (event.test(f)) {
          return this.push(event);
        } else {
          return Reply.more;
        }
      });
    };

    Observable.prototype.recover = function(f) {
      return this.withHandler(function(event) {
        if (event instanceof Error) {
          return this.push(new Fire(f(event.error)));
        } else {
          return this.push(event);
        }
      });
    };

    Observable.prototype.flatMapGeneric = function(f, lastOnly) {
      var root;
      root = this;
      return new this.constructor(function(sink) {
        var checkStop, children, rootStop, spawner, unbind, unsubChildren, unsubRoot;
        children = [];
        rootStop = false;
        unsubRoot = function() {};
        unsubChildren = function() {
          var unsubChild, _i, _len;
          for (_i = 0, _len = children.length; _i < _len; _i++) {
            unsubChild = children[_i];
            unsubChild();
          }
          return children = [];
        };
        unbind = function() {
          unsubChildren();
          return unsubRoot();
        };
        checkStop = function() {
          if (rootStop && (children.length === 0)) {
            return sink(Stop);
          }
        };
        spawner = function(event) {
          var child, childStopped, handler, removeChild, unsubChild;
          if (event === Stop) {
            rootStop = true;
            return checkStop();
          } else {
            if (lastOnly) {
              unsubChildren();
            }
            if (event instanceof Error) {
              return sink(event);
            } else {
              child = f(event.value);
              unsubChild = void 0;
              childStopped = false;
              removeChild = function() {
                if (unsubChild != null) {
                  remove(unsubChild, children);
                }
                return checkStop();
              };
              handler = function(event) {
                if (event === Stop) {
                  removeChild();
                  childStopped = true;
                  return Reply.stop;
                } else {
                  return tap(sink(event), function(reply) {
                    if (reply === Reply.stop) {
                      return unbind();
                    }
                  });
                }
              };
              unsubChild = child.subscribe(handler);
              if (!childStopped) {
                return children.push(unsubChild);
              }
            }
          }
        };
        unsubRoot = root.subscribe(spawner);
        return unbind;
      });
    };

    Observable.prototype.flatMapAll = function(f) {
      return this.flatMapGeneric(f, false);
    };

    Observable.prototype.flatMapLast = function(f) {
      return this.flatMapGeneric(f, true);
    };

    Observable.prototype.withStateMachine = function(initState, f) {
      var state;
      state = initState;
      return this.withHandler(function(event) {
        var newState, outputs, stopped, _ref2,
          _this = this;
        _ref2 = f(state, event), newState = _ref2[0], outputs = _ref2[1];
        state = newState;
        stopped = any(outputs, function(output) {
          return _this.push(output) === Reply.stop;
        });
        if (stopped) {
          return Reply.stop;
        } else {
          return Reply.more;
        }
      });
    };

    Observable.prototype.skipDuplicates = function(isEqual) {
      if (isEqual == null) {
        isEqual = function(a, b) {
          return a === b;
        };
      }
      return this.withStateMachine(Nothing, function(prev, event) {
        return event.map(function(v) {
          if (prev === Nothing || !prev.test(function(p) {
            return isEqual(v, p);
          })) {
            return [new Just(v), [event]];
          } else {
            return [prev, []];
          }
        }).getOrElse([prev, [event]]);
      });
    };

    Observable.prototype.scan = function(initState, f) {
      return this.withStateMachine(initState, function(acc, event) {
        var newAcc;
        newAcc = event.map(function(v) {
          return f(acc, v);
        }).getOrElse(acc);
        return [newAcc, [toEvent(newAcc)]];
      });
    };

    Observable.prototype.debounce = function(delay) {
      var _this = this;
      return this.flatMapLast(function(value) {
        return _this.constructor.later(delay, value);
      });
    };

    Observable.prototype.take = function(n) {
      if (n <= 0) {
        return this.constructor.nothing();
      }
      return this.withHandler(function(event) {
        if (!(event instanceof Fire)) {
          return this.push(event);
        } else {
          n--;
          if (n > 0) {
            return this.push(event);
          } else {
            if (n === 0) {
              this.push(event);
            }
            this.push(Stop);
            return Reply.stop;
          }
        }
      });
    };

    Observable.prototype.takeWhile = function(f) {
      return this.withHandler(function(event) {
        if (event.test(f)) {
          return this.push(event);
        } else {
          this.push(Stop);
          return Reply.stop;
        }
      });
    };

    Observable.prototype.takeUntil = function(other) {
      var source, stopper;
      source = this;
      stopper = other.changes();
      return new this.constructor(function(sink) {
        var transport, unbind, unsubSource, unsubStopper, watcher;
        unsubSource = function() {};
        unsubStopper = function() {};
        unbind = function() {
          unsubSource();
          return unsubStopper();
        };
        transport = function(event) {
          sink(event);
          if (event === Stop) {
            return unbind();
          }
        };
        watcher = function(event) {
          sink(Stop);
          return unbind();
        };
        unsubSource = source.subscribe(transport);
        unsubStopper = stopper.subscribe(watcher);
        return unbind;
      });
    };

    Observable.prototype.not = function() {
      return this.map(function(x) {
        return !x;
      });
    };

    Observable.fromList = function(values, wrapper) {
      if (wrapper == null) {
        wrapper = toEvent;
      }
      return new this(function(sink) {
        var event, _i, _len, _ref2;
        _ref2 = map(wrapper, values);
        for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
          event = _ref2[_i];
          sink(event);
        }
        return sink(Stop);
      });
    };

    Observable.nothing = function() {
      return this.fromList([]);
    };

    Observable.unit = function(value) {
      return this.fromList([value]);
    };

    Observable.error = function(value) {
      return this.fromList([value], toError);
    };

    Observable.fromBinder = function(binder, transform) {
      if (transform == null) {
        transform = id;
      }
      return new this(function(sink) {
        var unbinder;
        return unbinder = binder(function() {
          var args, event, events, _i, _len, _ref2, _results;
          args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
          events = toArray(transform.apply(null, args));
          _ref2 = map(toEvent, events);
          _results = [];
          for (_i = 0, _len = _ref2.length; _i < _len; _i++) {
            event = _ref2[_i];
            _results.push(tap(sink(event), function(reply) {
              if (event === Stop || reply === Reply.stop) {
                return unbinder();
              }
            }));
          }
          return _results;
        });
      });
    };

    Observable.poll = function(delay, poll) {
      return this.fromBinder(function(handler) {
        var i;
        i = setInterval(handler, delay);
        return function() {
          return clearInterval(i);
        };
      }, poll);
    };

    Observable.interval = function(delay, list) {
      var index;
      index = 0;
      return this.poll(delay, function() {
        var value;
        value = list[index++];
        if (index < list.length) {
          return value;
        } else if (index === list.length) {
          return [value, Stop];
        } else {
          return Stop;
        }
      });
    };

    Observable.later = function(delay, value) {
      return this.interval(delay, [value]);
    };

    Observable.fromPromise = function(promise) {
      return this.fromBinder(function(handler) {
        promise.then(handler, function(e) {
          return handler(new Error(e));
        });
        return function() {
          return typeof promise.abort === "function" ? promise.abort() : void 0;
        };
      }, function(value) {
        return [value, Stop];
      });
    };

    Observable.fromDOM = function(element, event, selector, transform) {
      var _ref2,
        _this = this;
      if (isFunction(selector)) {
        _ref2 = [selector, null], transform = _ref2[0], selector = _ref2[1];
      }
      return this.fromBinder(function(handler) {
        element.on(event, selector, handler);
        return function() {
          return element.off(event, selector, handler);
        };
      }, transform);
    };

    return Observable;

  })();

  StreamDispatcher = (function() {
    function StreamDispatcher(subscribe, handler) {
      var sinks,
        _this = this;
      if (subscribe == null) {
        subscribe = function(event) {};
      }
      sinks = [];
      this.push = function(event) {
        var sink, _i, _results;
        _results = [];
        for (_i = sinks.length - 1; _i >= 0; _i += -1) {
          sink = sinks[_i];
          _results.push(tap(sink(event), function(reply) {
            if (reply === Reply.stop) {
              return remove(sink, sinks);
            }
          }));
        }
        return _results;
      };
      if (handler == null) {
        handler = function(event) {
          return this.push(event);
        };
      }
      this.handler = function(event) {
        return handler.apply(_this, [event]);
      };
      this.subscribe = function(sink) {
        var unsubSelf;
        sinks.push(sink);
        if (sinks.length === 1) {
          unsubSelf = subscribe(_this.handler);
        }
        return function() {
          remove(sink, sinks);
          if (!any(sinks)) {
            return typeof unsubSelf === "function" ? unsubSelf() : void 0;
          }
        };
      };
    }

    return StreamDispatcher;

  })();

  Stream = (function(_super) {
    __extends(Stream, _super);

    function Stream() {
      _ref2 = Stream.__super__.constructor.apply(this, arguments);
      return _ref2;
    }

    Stream.prototype.dispatched = function(subscribe, handler) {
      return new StreamDispatcher(subscribe, handler).subscribe;
    };

    Stream.prototype.flatMap = function() {
      var args;
      args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
      return this.flatMapAll.apply(this, args);
    };

    Stream.prototype.merge = function() {
      var others;
      others = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
      return Stream.fromList(map(method("changes"), [this].concat(__slice.call(others)))).flatten();
    };

    Stream.prototype.flatten = function() {
      return this.flatMap(id);
    };

    Stream.prototype.box = function(initial) {
      var _this = this;
      if (initial == null) {
        initial = Nothing;
      }
      initial = toMaybe(initial);
      return new Box(function(sink) {
        if (!initial.isEmpty()) {
          sink(initial);
        }
        return _this.subscribe(function(event) {
          return sink(event);
        });
      });
    };

    Stream.prototype.changes = function() {
      return this;
    };

    Stream.prototype.zipWith = function() {
      var f, others, _i, _ref3;
      others = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), f = arguments[_i++];
      return (_ref3 = this.box()).zipWith.apply(_ref3, __slice.call(others).concat([f]));
    };

    Stream.prototype.and = function() {
      var others, _ref3;
      others = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
      return (_ref3 = this.box()).and.apply(_ref3, others);
    };

    Stream.prototype.or = function() {
      var others, _ref3;
      others = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
      return (_ref3 = this.box()).or.apply(_ref3, others);
    };

    Stream.prototype.not = function() {
      return this.box().not();
    };

    return Stream;

  })(Observable);

  BoxDispatcher = (function(_super) {
    __extends(BoxDispatcher, _super);

    function BoxDispatcher(subscribe, handler) {
      var current, push, stopped,
        _this = this;
      BoxDispatcher.__super__.constructor.call(this, subscribe, handler);
      current = Nothing;
      push = this.push;
      subscribe = this.subscribe;
      stopped = false;
      this.push = function(event) {
        event.map(function(x) {
          return current = new Just(x);
        });
        if (event === Stop) {
          stopped = true;
        }
        return push.apply(_this, [event]);
      };
      this.subscribe = function(sink) {
        var reply;
        reply = current.map(function(v) {
          return sink(new Fire(v));
        });
        if (reply.getOrElse(Reply.more) === Reply.stop) {
          return nop;
        } else if (stopped) {
          sink(Stop);
          return nop;
        } else {
          return subscribe.apply(_this, [sink]);
        }
      };
    }

    return BoxDispatcher;

  })(StreamDispatcher);

  Box = (function(_super) {
    __extends(Box, _super);

    function Box() {
      _ref3 = Box.__super__.constructor.apply(this, arguments);
      return _ref3;
    }

    Box.prototype.dispatched = function(subscribe, handler) {
      return new BoxDispatcher(subscribe, handler).subscribe;
    };

    Box.prototype.flatMap = function() {
      var args;
      args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
      return this.flatMapLast.apply(this, args);
    };

    Box.prototype.map2 = function(other, f) {
      return this.flatMap(function(x) {
        return other.box().map(function(y) {
          return f(x, y);
        });
      });
    };

    Box.sequence = function(boxes) {
      return foldl(boxes, this.unit([]), function(acc, box) {
        return acc.map2(box, function(memo, value) {
          return append(memo, value);
        });
      });
    };

    Box.zipWith = function(boxes, f) {
      return this.sequence(boxes).map(uncurry(f));
    };

    Box.prototype.zipWith = function() {
      var f, others, _i;
      others = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), f = arguments[_i++];
      return Box.zipWith([this].concat(__slice.call(others)), f);
    };

    Box.prototype.zip = function() {
      var boxes;
      boxes = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
      return Box.sequence([this].concat(__slice.call(boxes)));
    };

    Box.prototype.and = function() {
      var others;
      others = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
      return this.zipWith.apply(this, __slice.call(others).concat([function(a, b) {
        return a && b;
      }]));
    };

    Box.prototype.or = function() {
      var others;
      others = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
      return this.zipWith.apply(this, __slice.call(others).concat([function(a, b) {
        return a || b;
      }]));
    };

    Box.prototype.sampledBy = function(sampler) {
      var _this = this;
      return sampler.flatMap(function() {
        return _this.take(1);
      }).changes();
    };

    Box.prototype.changes = function() {
      var _this = this;
      return new Stream(function(sink) {
        return _this.subscribe(function(event) {
          return sink(event);
        });
      });
    };

    Box.prototype.box = function() {
      return this;
    };

    Box.prototype.merge = function() {
      var others, _ref4;
      others = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
      return (_ref4 = this.changes()).merge.apply(_ref4, others);
    };

    return Box;

  })(Observable);

  Reply = {
    stop: "<stop>",
    more: "<more>"
  };

  empty = function(xs) {
    return xs.length === 0;
  };

  method = function(meth) {
    return function(obj) {
      return obj[meth]();
    };
  };

  nop = function() {};

  id = function(x) {
    return x;
  };

  fail = function() {
    throw "method not implemented";
  };

  head = function(xs) {
    return xs[0];
  };

  tail = function(xs) {
    return xs.slice(1, xs.length);
  };

  uncurry = function(f) {
    return function(args) {
      return f.apply(null, args);
    };
  };

  map = function(f, xs) {
    var x, _i, _len, _results;
    _results = [];
    for (_i = 0, _len = xs.length; _i < _len; _i++) {
      x = xs[_i];
      _results.push(f(x));
    }
    return _results;
  };

  foldl = function(xs, seed, f) {
    var x, _i, _len;
    for (_i = 0, _len = xs.length; _i < _len; _i++) {
      x = xs[_i];
      seed = f(seed, x);
    }
    return seed;
  };

  tap = function(x, f) {
    f(x);
    return x;
  };

  remove = function(x, xs) {
    var i;
    i = xs.indexOf(x);
    if (i >= 0) {
      return xs.splice(i, 1);
    }
  };

  all = function(xs, f) {
    var x, _i, _len;
    if (f == null) {
      f = id;
    }
    for (_i = 0, _len = xs.length; _i < _len; _i++) {
      x = xs[_i];
      if (!f(x)) {
        return false;
      }
    }
    return true;
  };

  any = function(xs, f) {
    var x, _i, _len;
    if (f == null) {
      f = id;
    }
    for (_i = 0, _len = xs.length; _i < _len; _i++) {
      x = xs[_i];
      if (f(x)) {
        return true;
      }
    }
    return false;
  };

  copyArray = function(xs) {
    return xs.slice();
  };

  append = function(xs, x) {
    return tap(copyArray(xs), function(copy) {
      return copy.push(x);
    });
  };

  isFunction = function(f) {
    return typeof f === "function";
  };

  toArray = function(x) {
    if (x instanceof Array) {
      return x;
    } else {
      return [x];
    }
  };

  toMaybe = function(x) {
    if (x instanceof Maybe) {
      return x;
    } else {
      return new Just(x);
    }
  };

  _ref4 = {
    Stream: Stream,
    Box: Box,
    Event: Event,
    Fire: Fire,
    Error: Error,
    Stop: Stop,
    Maybe: Maybe,
    Just: Just,
    Wrong: Wrong,
    Nothing: Nothing
  };
  for (name in _ref4) {
    value = _ref4[name];
    Jnoid[name] = value;
  }

  if (typeof define !== "undefined" && define !== null ? define.amd : void 0) {
    define([], function() {
      return Jnoid;
    });
  } else if (typeof module !== "undefined" && module !== null ? module.exports : void 0) {
    module.exports = Jnoid;
  } else {
    this.Jnoid = Jnoid;
  }

}).call(this);
<commit_msg>Do not commit it with this name, please.<commit_after><|endoftext|>