<commit_before>var net = require('net'),
    sinon = require('sinon');

describe('protocolProfileBehavior', function () {
    var server,
        testrun,
        rawRequest,
        PORT = 5050,
        URL = 'http://localhost:' + PORT;

    before(function (done) {
        // Echo raw request message to handle body for http methods (GET, HEAD)
        // Node's `http` server won't parse body for GET method.
        server = net.createServer(function (socket) {
            socket.on('data', function (chunk) {
                var raw = chunk.toString(); // Request Message: [POSTMAN / HTTP/1.1 ...]
                socket.write('HTTP/1.1 200 ok\r\n');
                socket.write('Content-Type: text/plain\r\n\r\n');

                // Respond with raw request message.
                //
                // @note http-parser will blow up if body is sent for HEAD request.
                // RFC-7231: The HEAD method is identical to GET except that the
                //           server MUST NOT send a message body in the response.
                if (!raw.startsWith('HEAD / HTTP/1.1')) {
                    socket.write(raw);
                }

                socket.end();

                // store raw request message for testing HEAD requests.
                rawRequest = raw;
            });
        }).listen(PORT, done);
    });

    after(function (done) {
        server.close(done);
    });

    describe('with disableBodyPruning: true', function () {
        describe('HTTP GET', function () {
            before(function (done) {
                this.run({
                    collection: {
                        item: [{
                            request: {
                                url: URL,
                                method: 'GET',
                                body: {
                                    mode: 'raw',
                                    raw: 'foo=bar'
                                }
                            },
                            protocolProfileBehavior: {
                                disableBodyPruning: true
                            }
                        }]
                    }
                }, function (err, results) {
                    testrun = results;
                    done(err);
                });
            });

            it('should complete the run', function () {
                expect(testrun).be.ok();
                sinon.assert.calledOnce(testrun.start);
                sinon.assert.calledOnce(testrun.done);
                sinon.assert.calledWith(testrun.done.getCall(0), null);
            });

            it('should send body with GET method correctly', function () {
                sinon.assert.calledOnce(testrun.request);
                sinon.assert.calledWith(testrun.request.getCall(0), null);

                sinon.assert.calledOnce(testrun.response);
                sinon.assert.calledWith(testrun.response.getCall(0), null);

                var response = testrun.request.getCall(0).args[2].stream.toString();

                expect(response).to.contain('GET / HTTP/1.1');
                expect(response).to.contain('Content-Type: text/plain');
                expect(response).to.contain('content-length: 7');
                expect(response).to.contain('foo=bar');
            });
        });

        describe('HTTP HEAD', function () {
            before(function (done) {
                this.run({
                    collection: {
                        item: [{
                            request: {
                                url: URL,
                                method: 'HEAD',
                                body: {
                                    mode: 'raw',
                                    raw: 'foo=bar'
                                }
                            },
                            protocolProfileBehavior: {
                                disableBodyPruning: true
                            }
                        }]
                    }
                }, function (err, results) {
                    testrun = results;
                    done(err);
                });
            });

            it('should complete the run', function () {
                expect(testrun).be.ok();
                sinon.assert.calledOnce(testrun.start);
                sinon.assert.calledOnce(testrun.done);
                sinon.assert.calledWith(testrun.done.getCall(0), null);
            });

            it('should send body with HEAD method correctly', function () {
                sinon.assert.calledOnce(testrun.request);
                sinon.assert.calledWith(testrun.request.getCall(0), null);

                sinon.assert.calledOnce(testrun.response);
                sinon.assert.calledWith(testrun.response.getCall(0), null);

                var response = rawRequest; // raw request message for this request

                expect(response).to.contain('HEAD / HTTP/1.1');
                expect(response).to.contain('Content-Type: text/plain');
                expect(response).to.contain('content-length: 7');
                expect(response).to.contain('foo=bar');
            });
        });
    });

    describe('with disableBodyPruning: false', function () {
        describe('HTTP GET', function () {
            before(function (done) {
                this.run({
                    collection: {
                        item: [{
                            request: {
                                url: URL,
                                method: 'GET',
                                body: {
                                    mode: 'raw',
                                    raw: 'foo=bar'
                                }
                            },
                            protocolProfileBehavior: {
                                disableBodyPruning: false
                            }
                        }]
                    }
                }, function (err, results) {
                    testrun = results;
                    done(err);
                });
            });

            it('should complete the run', function () {
                expect(testrun).be.ok();
                sinon.assert.calledOnce(testrun.start);
                sinon.assert.calledOnce(testrun.done);
                sinon.assert.calledWith(testrun.done.getCall(0), null);
            });

            it('should not send body with GET method', function () {
                sinon.assert.calledOnce(testrun.request);
                sinon.assert.calledWith(testrun.request.getCall(0), null);

                sinon.assert.calledOnce(testrun.response);
                sinon.assert.calledWith(testrun.response.getCall(0), null);

                var response = testrun.request.getCall(0).args[2].stream.toString();

                expect(response).to.contain('GET / HTTP/1.1');
                expect(response).to.not.contain('Content-Type');
                expect(response).to.not.contain('foo=bar');
            });
        });

        describe('HTTP HEAD', function () {
            before(function (done) {
                this.run({
                    collection: {
                        item: [{
                            request: {
                                url: URL,
                                method: 'HEAD',
                                body: {
                                    mode: 'raw',
                                    raw: 'foo=bar'
                                }
                            },
                            protocolProfileBehavior: {
                                disableBodyPruning: false
                            }
                        }]
                    }
                }, function (err, results) {
                    testrun = results;
                    done(err);
                });
            });

            it('should complete the run', function () {
                expect(testrun).be.ok();
                sinon.assert.calledOnce(testrun.start);
                sinon.assert.calledOnce(testrun.done);
                sinon.assert.calledWith(testrun.done.getCall(0), null);
            });

            it('should not send body with HEAD method', function () {
                sinon.assert.calledOnce(testrun.request);
                sinon.assert.calledWith(testrun.request.getCall(0), null);

                sinon.assert.calledOnce(testrun.response);
                sinon.assert.calledWith(testrun.response.getCall(0), null);

                var response = rawRequest; // raw request message for this request

                expect(response).to.contain('HEAD / HTTP/1.1');
                expect(response).to.not.contain('Content-Type');
                expect(response).to.not.contain('foo=bar');
            });
        });
    });

    describe('with disableBodyPruning: undefined', function () {
        describe('HTTP GET', function () {
            before(function (done) {
                this.run({
                    collection: {
                        item: [{
                            request: {
                                url: URL,
                                method: 'GET',
                                body: {
                                    mode: 'raw',
                                    raw: 'foo=bar'
                                }
                            }
                        }]
                    }
                }, function (err, results) {
                    testrun = results;
                    done(err);
                });
            });

            it('should complete the run', function () {
                expect(testrun).be.ok();
                sinon.assert.calledOnce(testrun.start);
                sinon.assert.calledOnce(testrun.done);
                sinon.assert.calledWith(testrun.done.getCall(0), null);
            });

            it('should not send body by default for GET method', function () {
                sinon.assert.calledOnce(testrun.request);
                sinon.assert.calledWith(testrun.request.getCall(0), null);

                sinon.assert.calledOnce(testrun.response);
                sinon.assert.calledWith(testrun.response.getCall(0), null);

                var response = testrun.request.getCall(0).args[2].stream.toString();

                expect(response).to.contain('GET / HTTP/1.1');
                expect(response).to.not.contain('Content-Type');
                expect(response).to.not.contain('foo=bar');
            });
        });

        describe('HTTP HEAD', function () {
            before(function (done) {
                this.run({
                    collection: {
                        item: [{
                            request: {
                                url: URL,
                                method: 'HEAD',
                                body: {
                                    mode: 'raw',
                                    raw: 'foo=bar'
                                }
                            }
                        }]
                    }
                }, function (err, results) {
                    testrun = results;
                    done(err);
                });
            });

            it('should complete the run', function () {
                expect(testrun).be.ok();
                sinon.assert.calledOnce(testrun.start);
                sinon.assert.calledOnce(testrun.done);
                sinon.assert.calledWith(testrun.done.getCall(0), null);
            });

            it('should not send body by default for HEAD method', function () {
                sinon.assert.calledOnce(testrun.request);
                sinon.assert.calledWith(testrun.request.getCall(0), null);

                sinon.assert.calledOnce(testrun.response);
                sinon.assert.calledWith(testrun.response.getCall(0), null);

                var response = rawRequest; // raw request message for this request

                expect(response).to.contain('HEAD / HTTP/1.1');
                expect(response).to.not.contain('Content-Type');
                expect(response).to.not.contain('foo=bar');
            });
        });
    });
});
<commit_msg>Add tests for  protocolProfileBehavior using custom request method<commit_after>var net = require('net'),
    sinon = require('sinon');

describe('protocolProfileBehavior', function () {
    var server,
        testrun,
        rawRequest,
        PORT = 5050,
        URL = 'http://localhost:' + PORT;

    before(function (done) {
        // Echo raw request message to handle body for http methods (GET, HEAD)
        // Node's `http` server won't parse body for GET method.
        server = net.createServer(function (socket) {
            socket.on('data', function (chunk) {
                var raw = chunk.toString(); // Request Message: [POSTMAN / HTTP/1.1 ...]
                socket.write('HTTP/1.1 200 ok\r\n');
                socket.write('Content-Type: text/plain\r\n\r\n');

                // Respond with raw request message.
                //
                // @note http-parser will blow up if body is sent for HEAD request.
                // RFC-7231: The HEAD method is identical to GET except that the
                //           server MUST NOT send a message body in the response.
                if (!raw.startsWith('HEAD / HTTP/1.1')) {
                    socket.write(raw);
                }

                socket.end();

                // store raw request message for testing HEAD requests.
                rawRequest = raw;
            });
        }).listen(PORT, done);
    });

    after(function (done) {
        server.close(done);
    });

    describe('with disableBodyPruning: true', function () {
        describe('HTTP GET', function () {
            before(function (done) {
                this.run({
                    collection: {
                        item: [{
                            request: {
                                url: URL,
                                method: 'GET',
                                body: {
                                    mode: 'raw',
                                    raw: 'foo=bar'
                                }
                            },
                            protocolProfileBehavior: {
                                disableBodyPruning: true
                            }
                        }]
                    }
                }, function (err, results) {
                    testrun = results;
                    done(err);
                });
            });

            it('should complete the run', function () {
                expect(testrun).be.ok();
                sinon.assert.calledOnce(testrun.start);
                sinon.assert.calledOnce(testrun.done);
                sinon.assert.calledWith(testrun.done.getCall(0), null);
            });

            it('should send body with GET method', function () {
                sinon.assert.calledOnce(testrun.request);
                sinon.assert.calledWith(testrun.request.getCall(0), null);

                sinon.assert.calledOnce(testrun.response);
                sinon.assert.calledWith(testrun.response.getCall(0), null);

                var response = testrun.request.getCall(0).args[2].stream.toString();

                expect(response).to.contain('GET / HTTP/1.1');
                expect(response).to.contain('Content-Type: text/plain');
                expect(response).to.contain('content-length: 7');
                expect(response).to.contain('foo=bar');
            });
        });

        describe('HTTP HEAD', function () {
            before(function (done) {
                this.run({
                    collection: {
                        item: [{
                            request: {
                                url: URL,
                                method: 'HEAD',
                                body: {
                                    mode: 'raw',
                                    raw: 'foo=bar'
                                }
                            },
                            protocolProfileBehavior: {
                                disableBodyPruning: true
                            }
                        }]
                    }
                }, function (err, results) {
                    testrun = results;
                    done(err);
                });
            });

            it('should complete the run', function () {
                expect(testrun).be.ok();
                sinon.assert.calledOnce(testrun.start);
                sinon.assert.calledOnce(testrun.done);
                sinon.assert.calledWith(testrun.done.getCall(0), null);
            });

            it('should send body with HEAD method', function () {
                sinon.assert.calledOnce(testrun.request);
                sinon.assert.calledWith(testrun.request.getCall(0), null);

                sinon.assert.calledOnce(testrun.response);
                sinon.assert.calledWith(testrun.response.getCall(0), null);

                var response = rawRequest; // raw request message for this request

                expect(response).to.contain('HEAD / HTTP/1.1');
                expect(response).to.contain('Content-Type: text/plain');
                expect(response).to.contain('content-length: 7');
                expect(response).to.contain('foo=bar');
            });
        });

        describe('HTTP POSTMAN', function () {
            before(function (done) {
                this.run({
                    collection: {
                        item: [{
                            request: {
                                url: URL,
                                method: 'POSTMAN',
                                body: {
                                    mode: 'raw',
                                    raw: 'foo=bar'
                                }
                            },
                            protocolProfileBehavior: {
                                disableBodyPruning: true
                            }
                        }]
                    }
                }, function (err, results) {
                    testrun = results;
                    done(err);
                });
            });

            it('should complete the run', function () {
                expect(testrun).be.ok();
                sinon.assert.calledOnce(testrun.start);
                sinon.assert.calledOnce(testrun.done);
                sinon.assert.calledWith(testrun.done.getCall(0), null);
            });

            it('should send body with custom method', function () {
                sinon.assert.calledOnce(testrun.request);
                sinon.assert.calledWith(testrun.request.getCall(0), null);

                sinon.assert.calledOnce(testrun.response);
                sinon.assert.calledWith(testrun.response.getCall(0), null);

                var response = testrun.request.getCall(0).args[2].stream.toString();

                expect(response).to.contain('POSTMAN / HTTP/1.1');
                expect(response).to.contain('Content-Type: text/plain');
                expect(response).to.contain('content-length: 7');
                expect(response).to.contain('foo=bar');
            });
        });
    });

    describe('with disableBodyPruning: false', function () {
        describe('HTTP GET', function () {
            before(function (done) {
                this.run({
                    collection: {
                        item: [{
                            request: {
                                url: URL,
                                method: 'GET',
                                body: {
                                    mode: 'raw',
                                    raw: 'foo=bar'
                                }
                            },
                            protocolProfileBehavior: {
                                disableBodyPruning: false
                            }
                        }]
                    }
                }, function (err, results) {
                    testrun = results;
                    done(err);
                });
            });

            it('should complete the run', function () {
                expect(testrun).be.ok();
                sinon.assert.calledOnce(testrun.start);
                sinon.assert.calledOnce(testrun.done);
                sinon.assert.calledWith(testrun.done.getCall(0), null);
            });

            it('should not send body with GET method', function () {
                sinon.assert.calledOnce(testrun.request);
                sinon.assert.calledWith(testrun.request.getCall(0), null);

                sinon.assert.calledOnce(testrun.response);
                sinon.assert.calledWith(testrun.response.getCall(0), null);

                var response = testrun.request.getCall(0).args[2].stream.toString();

                expect(response).to.contain('GET / HTTP/1.1');
                expect(response).to.not.contain('Content-Type');
                expect(response).to.not.contain('foo=bar');
            });
        });

        describe('HTTP HEAD', function () {
            before(function (done) {
                this.run({
                    collection: {
                        item: [{
                            request: {
                                url: URL,
                                method: 'HEAD',
                                body: {
                                    mode: 'raw',
                                    raw: 'foo=bar'
                                }
                            },
                            protocolProfileBehavior: {
                                disableBodyPruning: false
                            }
                        }]
                    }
                }, function (err, results) {
                    testrun = results;
                    done(err);
                });
            });

            it('should complete the run', function () {
                expect(testrun).be.ok();
                sinon.assert.calledOnce(testrun.start);
                sinon.assert.calledOnce(testrun.done);
                sinon.assert.calledWith(testrun.done.getCall(0), null);
            });

            it('should not send body with HEAD method', function () {
                sinon.assert.calledOnce(testrun.request);
                sinon.assert.calledWith(testrun.request.getCall(0), null);

                sinon.assert.calledOnce(testrun.response);
                sinon.assert.calledWith(testrun.response.getCall(0), null);

                var response = rawRequest; // raw request message for this request

                expect(response).to.contain('HEAD / HTTP/1.1');
                expect(response).to.not.contain('Content-Type');
                expect(response).to.not.contain('foo=bar');
            });
        });

        describe('HTTP POSTMAN', function () {
            before(function (done) {
                this.run({
                    collection: {
                        item: [{
                            request: {
                                url: URL,
                                method: 'POSTMAN',
                                body: {
                                    mode: 'raw',
                                    raw: 'foo=bar'
                                }
                            },
                            protocolProfileBehavior: {
                                disableBodyPruning: false
                            }
                        }]
                    }
                }, function (err, results) {
                    testrun = results;
                    done(err);
                });
            });

            it('should complete the run', function () {
                expect(testrun).be.ok();
                sinon.assert.calledOnce(testrun.start);
                sinon.assert.calledOnce(testrun.done);
                sinon.assert.calledWith(testrun.done.getCall(0), null);
            });

            it('should send body with custom method', function () {
                sinon.assert.calledOnce(testrun.request);
                sinon.assert.calledWith(testrun.request.getCall(0), null);

                sinon.assert.calledOnce(testrun.response);
                sinon.assert.calledWith(testrun.response.getCall(0), null);

                var response = testrun.request.getCall(0).args[2].stream.toString();

                expect(response).to.contain('POSTMAN / HTTP/1.1');
                expect(response).to.contain('Content-Type: text/plain');
                expect(response).to.contain('content-length: 7');
                expect(response).to.contain('foo=bar');
            });
        });
    });

    describe('with disableBodyPruning: undefined', function () {
        describe('HTTP GET', function () {
            before(function (done) {
                this.run({
                    collection: {
                        item: [{
                            request: {
                                url: URL,
                                method: 'GET',
                                body: {
                                    mode: 'raw',
                                    raw: 'foo=bar'
                                }
                            }
                        }]
                    }
                }, function (err, results) {
                    testrun = results;
                    done(err);
                });
            });

            it('should complete the run', function () {
                expect(testrun).be.ok();
                sinon.assert.calledOnce(testrun.start);
                sinon.assert.calledOnce(testrun.done);
                sinon.assert.calledWith(testrun.done.getCall(0), null);
            });

            it('should not send body by default for GET method', function () {
                sinon.assert.calledOnce(testrun.request);
                sinon.assert.calledWith(testrun.request.getCall(0), null);

                sinon.assert.calledOnce(testrun.response);
                sinon.assert.calledWith(testrun.response.getCall(0), null);

                var response = testrun.request.getCall(0).args[2].stream.toString();

                expect(response).to.contain('GET / HTTP/1.1');
                expect(response).to.not.contain('Content-Type');
                expect(response).to.not.contain('foo=bar');
            });
        });

        describe('HTTP HEAD', function () {
            before(function (done) {
                this.run({
                    collection: {
                        item: [{
                            request: {
                                url: URL,
                                method: 'HEAD',
                                body: {
                                    mode: 'raw',
                                    raw: 'foo=bar'
                                }
                            }
                        }]
                    }
                }, function (err, results) {
                    testrun = results;
                    done(err);
                });
            });

            it('should complete the run', function () {
                expect(testrun).be.ok();
                sinon.assert.calledOnce(testrun.start);
                sinon.assert.calledOnce(testrun.done);
                sinon.assert.calledWith(testrun.done.getCall(0), null);
            });

            it('should not send body by default for HEAD method', function () {
                sinon.assert.calledOnce(testrun.request);
                sinon.assert.calledWith(testrun.request.getCall(0), null);

                sinon.assert.calledOnce(testrun.response);
                sinon.assert.calledWith(testrun.response.getCall(0), null);

                var response = rawRequest; // raw request message for this request

                expect(response).to.contain('HEAD / HTTP/1.1');
                expect(response).to.not.contain('Content-Type');
                expect(response).to.not.contain('foo=bar');
            });
        });

        describe('HTTP POSTMAN', function () {
            before(function (done) {
                this.run({
                    collection: {
                        item: [{
                            request: {
                                url: URL,
                                method: 'POSTMAN',
                                body: {
                                    mode: 'raw',
                                    raw: 'foo=bar'
                                }
                            }
                        }]
                    }
                }, function (err, results) {
                    testrun = results;
                    done(err);
                });
            });

            it('should complete the run', function () {
                expect(testrun).be.ok();
                sinon.assert.calledOnce(testrun.start);
                sinon.assert.calledOnce(testrun.done);
                sinon.assert.calledWith(testrun.done.getCall(0), null);
            });

            it('should send body with custom method', function () {
                sinon.assert.calledOnce(testrun.request);
                sinon.assert.calledWith(testrun.request.getCall(0), null);

                sinon.assert.calledOnce(testrun.response);
                sinon.assert.calledWith(testrun.response.getCall(0), null);

                var response = testrun.request.getCall(0).args[2].stream.toString();

                expect(response).to.contain('POSTMAN / HTTP/1.1');
                expect(response).to.contain('Content-Type: text/plain');
                expect(response).to.contain('content-length: 7');
                expect(response).to.contain('foo=bar');
            });
        });
    });
});
<|endoftext|>


<commit_before>jQuery(function() {

  function ClearForm() {
    document.new_invitation.invitation_email.value= "";
  }

  var scroll = 448;
  var maxScroll = $("#months").width() - $("#timeline").width();
  var currentScroll = 0;

  $("a[href='#next']").click(function() {
    if (currentScroll < maxScroll) {
      $("#timeline .project, .month, #today_bar").animate({ left: "-=" + scroll + "px" });
      currentScroll += scroll;
      console.log(currentScroll);
    }
    return false;
  });

  $("a[href='#prev']").click(function() {
    if (currentScroll >= scroll) {
      $("#timeline .project, .month, #today_bar").animate({ left: "+=" + scroll + "px" });
      currentScroll -= scroll;
    }
    return false;
  });

  $(".relatize").relatizeDate();

  $(".datepicker").datepicker({ dateFormat: 'd MM yy' });

  $("input.person:checked").each(function(){
    $("label[for=" + $(this).attr("id") + "]").addClass("selected");
  });

  $("input.person").hide();

  $(".person label.collection_check_boxes").click(function() {
    if ($(this).hasClass("selected"))
    {
      $("#" + $(this).attr("for")).attr("value", "0");
      $(this).removeClass("selected");
    }
    else
    {
      $("#" + $(this).attr("for")).attr("value", "1");
      $(this).addClass("selected");
    }
  });

  $(".projectcontent").click(function() {
    window.location = $(this).find("h2 a").attr("href");
    return false;
  });
});
<commit_msg>Keyboard events for timeline.<commit_after>jQuery(function() {

  function ClearForm() {
    document.new_invitation.invitation_email.value= "";
  }

  var scroll = 448;
  var maxScroll = $("#months").width() - $("#timeline").width();
  var currentScroll = 0;

  $("a[href='#next']").click(function() {
    if (currentScroll < maxScroll) {
      $("#timeline .project, .month, #today_bar").animate({ left: "-=" + scroll + "px" });
      currentScroll += scroll;
      console.log(currentScroll);
    }
    return false;
  });

  $("a[href='#prev']").click(function() {
    if (currentScroll >= scroll) {
      $("#timeline .project, .month, #today_bar").animate({ left: "+=" + scroll + "px" });
      currentScroll -= scroll;
    }
    return false;
  });

  $(document).keydown(function(e) {
    if (e.keyCode == 37) {
      $("a[href='#prev']").click();
      return false;
    }
    if (e.keyCode == 39) {
      $("a[href='#next']").click();
      return false;
    }
  });

  $(".relatize").relatizeDate();

  $(".datepicker").datepicker({ dateFormat: 'd MM yy' });

  $("input.person:checked").each(function(){
    $("label[for=" + $(this).attr("id") + "]").addClass("selected");
  });

  $("input.person").hide();

  $(".person label.collection_check_boxes").click(function() {
    if ($(this).hasClass("selected"))
    {
      $("#" + $(this).attr("for")).attr("value", "0");
      $(this).removeClass("selected");
    }
    else
    {
      $("#" + $(this).attr("for")).attr("value", "1");
      $(this).addClass("selected");
    }
  });

  $(".projectcontent").click(function() {
    window.location = $(this).find("h2 a").attr("href");
    return false;
  });
});
<|endoftext|>


<commit_before>import Ember from "ember";

const {
  computed,
  run
} = Ember;

const STATE_LIST = ['culled', 'hidden', 'visible'];

const STATE_TRANSITIONS_UP = [
  { state: 'culled', method: '' },
  { state: 'hidden', method: '_ov_insert' },
  { state: 'visible', method: '_ov_reveal' }
];

const STATE_TRANSITIONS_DOWN = [
  { state: 'visible', method: '' },
  { state: 'hidden', method: '_ov_obscure' },
  { state: 'culled', method: '_ov_teardown' }
];

/**
 An occlusion view is one that intelligently removes
 its content when scrolled off the screen.

 @class OcclusionView
 @extends Ember.View
 @namespace Ember
 **/
export default Ember.Component.extend({

  tagName: 'occlusion-item',

  classNameBindings: ['viewStateClass'],
  viewState: 'culled',
  contentVisible: false,
  contentHidden: false,
  contentInserted: false,
  contentCulled: false,
  collection: null,

  viewStateClass: computed('viewState', function() {
    return 'state-' + this.get('viewState');
  }),

  show: function () {
    this._setState('visible');
  },

  hide: function () {
    this._setState('hidden');
  },

  cull: function () {
    this._setState('culled');
  },

  _setState: function (toState) {

    var fromState = this.get('viewState');
    var currentState = fromState;

    if (fromState === toState) {
      return;
    }

    var transitionMap = STATE_LIST.indexOf(fromState) < STATE_LIST.indexOf(toState) ? STATE_TRANSITIONS_UP : STATE_TRANSITIONS_DOWN;
    var i;

    for (i = 0; i < transitionMap.length; i++) {
      if (transitionMap[i].state === fromState) {
        break;
      }
    }
    i++;

    while (transitionMap[i] && currentState !== toState) {
      if (transitionMap[i].method) {
        this[transitionMap[i].method]();
        currentState = transitionMap[i].state;
      }
      i++;
    }

    this.set('viewState', toState);
  },

  /**!
   * Destroy the View/Element
   *
   * Unlike the other methods, this method
   * can be called from any state. It is still not valid
   * to transition to it directly, but willDestroy uses it
   * to teardown the instance.
   *
   * @private
   */
  _ov_teardown: function() {
    this.setProperties({ contentCulled: true, contentHidden: false, contentInserted: false });
  },

  /**!
   * Insert the Element into the DOM in a hidden state.
   *
   * @private
   */
  _ov_insert: function() {
    this.setProperties({ contentHidden: true, contentCulled: false, contentInserted: true });
    if (!this.get('_height')) {
      run.schedule('afterRender', this, function() {
        if (!this.get('isDestroyed')) {
          var height = this.$().height();
          this.set('_height', height);

          // allow growth if needed
          this.element.style.minHeight = height + 'px';
        }
      });
    }
  },

  /**!
   * Reveal the Element
   *
   * @private
   */
  _ov_reveal: function() {
    this.setProperties({ contentHidden: false, contentVisible: true, contentInserted: true });
    this.element.style.visibility = 'visible';
  },

  /**!
   * Hide the Element
   *
   * @private
   */
  _ov_obscure: function() {
    this.setProperties({ contentHidden: true, contentVisible: false, contentInserted: true });
    this.element.style.visibility = 'hidden';
  },

  defaultHeight: 75,
  keyForId: null,

  _height: 0,

  willInsertElement: function() {

    var _height = this.get('_height');
    var defaultHeight = this.get('defaultHeight');
    if (typeof defaultHeight === 'number') {
      defaultHeight += 'px';
    }

    this.element.style.visibility = 'hidden';
    this.element.style.minHeight = _height ? _height + 'px' : defaultHeight;

  },

  didReceiveAttrs: function(attrs) {
    var key = this.get('keyForId');
    var oldKeyVal = attrs.oldAttrs ? attrs.oldAttrs.content.value[key] : false;
    var newKeyVal = attrs.oldAttrs ? attrs.newAttrs.content.value[key] : false;
    if (oldKeyVal && newKeyVal && oldKeyVal !== newKeyVal) {
      this.collection.unregister(oldKeyVal);
      this.collection.register(this, newKeyVal);
    }
  },

  willDestroy: function() {
    this._ov_teardown();
    this.set('viewState', 'culled');
    var key = this.get('keyForId');
    var val = this.get('content.' + key);
    this.collection.unregister(val);
  },

  init: function() {
    var val = this.get('content.' + this.get('keyForId'));
    this.collection.register(this, val);
    this._super();
  }

});
<commit_msg>remove explicit minHeight while item is inDom<commit_after>import Ember from "ember";

const {
  computed,
  run
} = Ember;

const STATE_LIST = ['culled', 'hidden', 'visible'];

const STATE_TRANSITIONS_UP = [
  { state: 'culled', method: '' },
  { state: 'hidden', method: '_ov_insert' },
  { state: 'visible', method: '_ov_reveal' }
];

const STATE_TRANSITIONS_DOWN = [
  { state: 'visible', method: '' },
  { state: 'hidden', method: '_ov_obscure' },
  { state: 'culled', method: '_ov_teardown' }
];

/**
 An occlusion view is one that intelligently removes
 its content when scrolled off the screen.

 @class OcclusionView
 @extends Ember.View
 @namespace Ember
 **/
export default Ember.Component.extend({

  tagName: 'occlusion-item',

  classNameBindings: ['viewStateClass'],
  viewState: 'culled',
  contentVisible: false,
  contentHidden: false,
  contentInserted: false,
  contentCulled: false,
  collection: null,

  viewStateClass: computed('viewState', function() {
    return 'state-' + this.get('viewState');
  }),

  show: function () {
    this._setState('visible');
  },

  hide: function () {
    this._setState('hidden');
  },

  cull: function () {
    this._setState('culled');
  },

  _setState: function (toState) {

    var fromState = this.get('viewState');
    var currentState = fromState;

    if (fromState === toState) {
      return;
    }

    var transitionMap = STATE_LIST.indexOf(fromState) < STATE_LIST.indexOf(toState) ? STATE_TRANSITIONS_UP : STATE_TRANSITIONS_DOWN;
    var i;

    for (i = 0; i < transitionMap.length; i++) {
      if (transitionMap[i].state === fromState) {
        break;
      }
    }
    i++;

    while (transitionMap[i] && currentState !== toState) {
      if (transitionMap[i].method) {
        this[transitionMap[i].method]();
        currentState = transitionMap[i].state;
      }
      i++;
    }

    this.set('viewState', toState);
  },

  /**!
   * Destroy the View/Element
   *
   * Unlike the other methods, this method
   * can be called from any state. It is still not valid
   * to transition to it directly, but willDestroy uses it
   * to teardown the instance.
   *
   * @private
   */
  _ov_teardown: function() {
    this.element.style.minHeight = this.$().height() + 'px';
    this.setProperties({ contentCulled: true, contentHidden: false, contentInserted: false });
  },

  /**!
   * Insert the Element into the DOM in a hidden state.
   *
   * @private
   */
  _ov_insert: function() {
    this.setProperties({ contentHidden: true, contentCulled: false, contentInserted: true });
    run.schedule('afterRender', this, function() {
      if (!this.get('isDestroyed')) {
        // allow height to change organically while the element is inDom
        this.element.style.minHeight = null;
      }
    });
  },

  /**!
   * Reveal the Element
   *
   * @private
   */
  _ov_reveal: function() {
    this.setProperties({ contentHidden: false, contentVisible: true, contentInserted: true });
    this.element.style.visibility = 'visible';
  },

  /**!
   * Hide the Element
   *
   * @private
   */
  _ov_obscure: function() {
    this.setProperties({ contentHidden: true, contentVisible: false, contentInserted: true });
    this.element.style.visibility = 'hidden';
  },

  defaultHeight: 75,
  keyForId: null,

  _height: 0,

  willInsertElement: function() {

    var _height = this.get('_height');
    var defaultHeight = this.get('defaultHeight');
    if (typeof defaultHeight === 'number') {
      defaultHeight += 'px';
    }

    this.element.style.visibility = 'hidden';
    this.element.style.minHeight = _height ? _height + 'px' : defaultHeight;

  },

  didReceiveAttrs: function(attrs) {
    var key = this.get('keyForId');
    var oldKeyVal = attrs.oldAttrs ? attrs.oldAttrs.content.value[key] : false;
    var newKeyVal = attrs.oldAttrs ? attrs.newAttrs.content.value[key] : false;
    if (oldKeyVal && newKeyVal && oldKeyVal !== newKeyVal) {
      this.collection.unregister(oldKeyVal);
      this.collection.register(this, newKeyVal);
    }
  },

  willDestroy: function() {
    this._ov_teardown();
    this.set('viewState', 'culled');
    var key = this.get('keyForId');
    var val = this.get('content.' + key);
    this.collection.unregister(val);
  },

  init: function() {
    var val = this.get('content.' + this.get('keyForId'));
    this.collection.register(this, val);
    this._super();
  }

});
<|endoftext|>


<commit_before>// @flow

import * as React from 'react'
import {
	StyleSheet,
	ScrollView,
	View,
	Text,
	RefreshControl,
	Alert,
} from 'react-native'
import {TabBarIcon} from '@frogpond/navigation-tabs'
import {connect} from 'react-redux'
import {Cell, TableView, Section} from '@frogpond/tableview'
import {
	hasSeenAcknowledgement,
	type LoginStateType,
} from '../../redux/parts/settings'
import {getBalances} from '../../lib/financials'
import {type ReduxState} from '../../redux'
import delay from 'delay'
import * as c from '@frogpond/colors'
import type {TopLevelViewPropsType} from '../types'

const DISCLAIMER = 'This data may be outdated or otherwise inaccurate.'
const LONG_DISCLAIMER =
	'This data may be inaccurate.\nBon Appétit is always right.\nThis app is unofficial.'

type ReactProps = TopLevelViewPropsType

type ReduxStateProps = {
	loginState: LoginStateType,
	alertSeen: boolean,
}

type ReduxDispatchProps = {
	hasSeenAcknowledgement: () => any,
}

type Props = ReactProps & ReduxStateProps & ReduxDispatchProps

type State = {
	loading: boolean,
	flex: ?string,
	ole: ?string,
	print: ?string,
	weeklyMeals: ?string,
	dailyMeals: ?string,
	mealPlan: ?string,
	message: ?string,
}

class BalancesView extends React.PureComponent<Props, State> {
	static navigationOptions = {
		tabBarLabel: 'Balances',
		tabBarIcon: TabBarIcon('card'),
	}

	state = {
		loading: false,
		flex: null,
		ole: null,
		print: null,
		weeklyMeals: null,
		dailyMeals: null,
		mealPlan: null,
		message: null,
	}

	componentDidMount() {
		// calling "refresh" here, to make clear to the user
		// that the data is being updated
		this.refresh()

		if (!this.props.alertSeen) {
			Alert.alert('', LONG_DISCLAIMER, [
				{text: 'I Disagree', onPress: this.goBack, style: 'cancel'},
				{text: 'Okay', onPress: this.props.hasSeenAcknowledgement},
			])
		}
	}

	refresh = async () => {
		let start = Date.now()
		this.setState(() => ({loading: true}))

		await this.fetchData()

		// wait 0.5 seconds – if we let it go at normal speed, it feels broken.
		let elapsed = Date.now() - start
		await delay(500 - elapsed)

		this.setState(() => ({loading: false}))
	}

	fetchData = async () => {
		let balances = await getBalances()

		if (balances.error === true) {
			return
		}

		let {value} = balances

		let {flex, ole, print} = value
		let {weekly: weeklyMeals, daily: dailyMeals, plan: mealPlan} = value

		this.setState(() => ({
			flex,
			ole,
			print,
			weeklyMeals,
			dailyMeals,
			mealPlan,
		}))
	}

	openSettings = () => this.props.navigation.navigate('SettingsView')

	goBack = () => this.props.navigation.goBack(null)

	render() {
		let {
			flex,
			ole,
			print,
			dailyMeals,
			weeklyMeals,
			mealPlan,
			message,
			loading,
		} = this.state
		let {loginState} = this.props

		if (loginState !== 'logged-in' || message) {
			return (

					{loginState !== 'logged-in' ? (
						<Cell
							accessory="DisclosureIndicator"
							cellStyle="Basic"
							onPress={this.openSettings}
							title="Log in with St. Olaf"
						/>
					) : null}

					{message ? <Cell cellStyle="Basic" title={message} /> : null}

			)
		}

		return (
			<ScrollView
				contentContainerStyle={styles.stage}
				refreshControl={
					<RefreshControl
						onRefresh={this.refresh}
						refreshing={this.state.loading}
					/>
				}
			>
				<TableView>

						<View style={styles.balancesRow}>
							<FormattedValueCell
								formatter={getValueOrNa}
								indeterminate={loading}
								label="Flex"
								value={flex}
							/>

							<FormattedValueCell
								formatter={getValueOrNa}
								indeterminate={loading}
								label="Ole"
								value={ole}
							/>

							<FormattedValueCell
								formatter={getValueOrNa}
								indeterminate={loading}
								label="Copy/Print"
								style={styles.finalCell}
								value={print}
							/>
						</View>

						<View style={styles.balancesRow}>
							<FormattedValueCell
								formatter={getValueOrNa}
								indeterminate={loading}
								label="Daily Meals Left"
								value={dailyMeals}
							/>

							<FormattedValueCell
								formatter={getValueOrNa}
								indeterminate={loading}
								label="Weekly Meals Left"
								style={styles.finalCell}
								value={weeklyMeals}
							/>
						</View>
						{mealPlan && (
							<Cell cellStyle="Subtitle" detail={mealPlan} title="Meal Plan" />
						)}

				</TableView>
			</ScrollView>
		)
	}
}

function mapState(state: ReduxState): ReduxStateProps {
	return {
		alertSeen: state.settings ? state.settings.unofficiallyAcknowledged : false,
		loginState: state.settings ? state.settings.loginState : 'logged-out',
	}
}

function mapDispatch(dispatch): ReduxDispatchProps {
	return {
		hasSeenAcknowledgement: () => dispatch(hasSeenAcknowledgement()),
	}
}

export default connect(
	mapState,
	mapDispatch,
)(BalancesView)

let cellMargin = 10
let cellSidePadding = 10
let cellEdgePadding = 10

let styles = StyleSheet.create({
	stage: {
		backgroundColor: c.iosLightBackground,
		paddingTop: 20,
		paddingBottom: 20,
	},

	common: {
		backgroundColor: c.white,
	},

	balances: {
		borderRightWidth: StyleSheet.hairlineWidth,
		borderRightColor: c.iosSeparator,
	},

	finalCell: {
		borderRightWidth: 0,
	},

	balancesRow: {
		flexDirection: 'row',
		marginTop: 0,
		marginBottom: -10,
	},

	rectangle: {
		height: 88,
		flex: 1,
		alignItems: 'center',
		paddingTop: cellSidePadding,
		paddingBottom: cellSidePadding,
		paddingRight: cellEdgePadding,
		paddingLeft: cellEdgePadding,
		marginBottom: cellMargin,
	},

	// Text styling
	financialText: {
		paddingTop: 8,
		color: c.iosDisabledText,
		textAlign: 'center',
		fontWeight: '200',
		fontSize: 23,
	},
	rectangleButtonText: {
		paddingTop: 15,
		color: c.black,
		textAlign: 'center',
		fontSize: 16,
	},
})

function getValueOrNa(value: ?string): string {
	if (value == null) {
		return 'N/A'
	}
	return value
}

function FormattedValueCell({
	indeterminate,
	label,
	value,
	style,
	formatter,
}: {
	indeterminate: boolean,
	label: string,
	value: ?string,
	style?: any,
	formatter: (?string) => string,
}) {
	return (
		<View style={[styles.rectangle, styles.common, styles.balances, style]}>
			<Text
				autoAdjustsFontSize={true}
				selectable={true}
				style={styles.financialText}
			>
				{indeterminate ? '…' : formatter(value)}
			</Text>
			<Text autoAdjustsFontSize={true} style={styles.rectangleButtonText}>
				{label}
			</Text>
		</View>
	)
}
<commit_msg>clean up args in FormattedValueCell<commit_after>// @flow

import * as React from 'react'
import {
	StyleSheet,
	ScrollView,
	View,
	Text,
	RefreshControl,
	Alert,
} from 'react-native'
import {TabBarIcon} from '@frogpond/navigation-tabs'
import {connect} from 'react-redux'
import {Cell, TableView, Section} from '@frogpond/tableview'
import {
	hasSeenAcknowledgement,
	type LoginStateType,
} from '../../redux/parts/settings'
import {getBalances} from '../../lib/financials'
import {type ReduxState} from '../../redux'
import delay from 'delay'
import * as c from '@frogpond/colors'
import type {TopLevelViewPropsType} from '../types'

const DISCLAIMER = 'This data may be outdated or otherwise inaccurate.'
const LONG_DISCLAIMER =
	'This data may be inaccurate.\nBon Appétit is always right.\nThis app is unofficial.'

type ReactProps = TopLevelViewPropsType

type ReduxStateProps = {
	loginState: LoginStateType,
	alertSeen: boolean,
}

type ReduxDispatchProps = {
	hasSeenAcknowledgement: () => any,
}

type Props = ReactProps & ReduxStateProps & ReduxDispatchProps

type State = {
	loading: boolean,
	flex: ?string,
	ole: ?string,
	print: ?string,
	weeklyMeals: ?string,
	dailyMeals: ?string,
	mealPlan: ?string,
	message: ?string,
}

class BalancesView extends React.PureComponent<Props, State> {
	static navigationOptions = {
		tabBarLabel: 'Balances',
		tabBarIcon: TabBarIcon('card'),
	}

	state = {
		loading: false,
		flex: null,
		ole: null,
		print: null,
		weeklyMeals: null,
		dailyMeals: null,
		mealPlan: null,
		message: null,
	}

	componentDidMount() {
		// calling "refresh" here, to make clear to the user
		// that the data is being updated
		this.refresh()

		if (!this.props.alertSeen) {
			Alert.alert('', LONG_DISCLAIMER, [
				{text: 'I Disagree', onPress: this.goBack, style: 'cancel'},
				{text: 'Okay', onPress: this.props.hasSeenAcknowledgement},
			])
		}
	}

	refresh = async () => {
		let start = Date.now()
		this.setState(() => ({loading: true}))

		await this.fetchData()

		// wait 0.5 seconds – if we let it go at normal speed, it feels broken.
		let elapsed = Date.now() - start
		await delay(500 - elapsed)

		this.setState(() => ({loading: false}))
	}

	fetchData = async () => {
		let balances = await getBalances()

		if (balances.error === true) {
			return
		}

		let {value} = balances

		let {flex, ole, print} = value
		let {weekly: weeklyMeals, daily: dailyMeals, plan: mealPlan} = value

		this.setState(() => ({
			flex,
			ole,
			print,
			weeklyMeals,
			dailyMeals,
			mealPlan,
		}))
	}

	openSettings = () => this.props.navigation.navigate('SettingsView')

	goBack = () => this.props.navigation.goBack(null)

	render() {
		let {
			flex,
			ole,
			print,
			dailyMeals,
			weeklyMeals,
			mealPlan,
			message,
			loading,
		} = this.state
		let {loginState} = this.props

		if (loginState !== 'logged-in' || message) {
			return (

					{loginState !== 'logged-in' ? (
						<Cell
							accessory="DisclosureIndicator"
							cellStyle="Basic"
							onPress={this.openSettings}
							title="Log in with St. Olaf"
						/>
					) : null}

					{message ? <Cell cellStyle="Basic" title={message} /> : null}

			)
		}

		return (
			<ScrollView
				contentContainerStyle={styles.stage}
				refreshControl={
					<RefreshControl
						onRefresh={this.refresh}
						refreshing={this.state.loading}
					/>
				}
			>
				<TableView>

						<View style={styles.balancesRow}>
							<FormattedValueCell
								formatter={getValueOrNa}
								indeterminate={loading}
								label="Flex"
								value={flex}
							/>

							<FormattedValueCell
								formatter={getValueOrNa}
								indeterminate={loading}
								label="Ole"
								value={ole}
							/>

							<FormattedValueCell
								formatter={getValueOrNa}
								indeterminate={loading}
								label="Copy/Print"
								style={styles.finalCell}
								value={print}
							/>
						</View>

						<View style={styles.balancesRow}>
							<FormattedValueCell
								formatter={getValueOrNa}
								indeterminate={loading}
								label="Daily Meals Left"
								value={dailyMeals}
							/>

							<FormattedValueCell
								formatter={getValueOrNa}
								indeterminate={loading}
								label="Weekly Meals Left"
								style={styles.finalCell}
								value={weeklyMeals}
							/>
						</View>
						{mealPlan && (
							<Cell cellStyle="Subtitle" detail={mealPlan} title="Meal Plan" />
						)}

				</TableView>
			</ScrollView>
		)
	}
}

function mapState(state: ReduxState): ReduxStateProps {
	return {
		alertSeen: state.settings ? state.settings.unofficiallyAcknowledged : false,
		loginState: state.settings ? state.settings.loginState : 'logged-out',
	}
}

function mapDispatch(dispatch): ReduxDispatchProps {
	return {
		hasSeenAcknowledgement: () => dispatch(hasSeenAcknowledgement()),
	}
}

export default connect(
	mapState,
	mapDispatch,
)(BalancesView)

let cellMargin = 10
let cellSidePadding = 10
let cellEdgePadding = 10

let styles = StyleSheet.create({
	stage: {
		backgroundColor: c.iosLightBackground,
		paddingTop: 20,
		paddingBottom: 20,
	},

	common: {
		backgroundColor: c.white,
	},

	balances: {
		borderRightWidth: StyleSheet.hairlineWidth,
		borderRightColor: c.iosSeparator,
	},

	finalCell: {
		borderRightWidth: 0,
	},

	balancesRow: {
		flexDirection: 'row',
		marginTop: 0,
		marginBottom: -10,
	},

	rectangle: {
		height: 88,
		flex: 1,
		alignItems: 'center',
		paddingTop: cellSidePadding,
		paddingBottom: cellSidePadding,
		paddingRight: cellEdgePadding,
		paddingLeft: cellEdgePadding,
		marginBottom: cellMargin,
	},

	// Text styling
	financialText: {
		paddingTop: 8,
		color: c.iosDisabledText,
		textAlign: 'center',
		fontWeight: '200',
		fontSize: 23,
	},
	rectangleButtonText: {
		paddingTop: 15,
		color: c.black,
		textAlign: 'center',
		fontSize: 16,
	},
})

function getValueOrNa(value: ?string): string {
	if (value == null) {
		return 'N/A'
	}
	return value
}

function FormattedValueCell(props: {
	indeterminate: boolean,
	label: string,
	value: ?string,
	style?: any,
	formatter: (?string) => string,
}) {
	let {indeterminate, label, value, style, formatter} = props

	return (
		<View style={[styles.rectangle, styles.common, styles.balances, style]}>
			<Text
				autoAdjustsFontSize={true}
				selectable={true}
				style={styles.financialText}
			>
				{indeterminate ? '…' : formatter(value)}
			</Text>
			<Text autoAdjustsFontSize={true} style={styles.rectangleButtonText}>
				{label}
			</Text>
		</View>
	)
}
<|endoftext|>


<commit_before>// XXX Abstract this out into a separate module.
// It's almost identical to routes/styl.js and routes/static.js

module.exports = static

var path = require("path")
, cache = {}
, crypto = require('crypto')
, glob = require('glob')
, zlib = require('zlib')
, mimetypes = require('filed/mimetypes.js')
, fs = require('fs')
, url = require('url')

var loading = false
, lastLoaded = Date.now()
, interval = 1000 * 60 // 1 minute

load(true)

function load (startup) {
  if (loading) return
  lastLoaded = Date.now()

  glob.sync('{doc,api}/**/*.*').forEach(function (s) {
    fs.readFile(s, function (er, raw) {
      if (er && startup) throw er
      if (er) return

      if (cache[s] && getETag(raw) === cache[s][0]) return
      render(s, raw, function (er) {
        if (er && startup) throw er
      })
    })
  })
}

function render (s, raw, cb) {
  if (cache[s]) return cb()
  var etag = getETag(raw)
  zlib.gzip(raw, function (er, z) {

    var ext = path.extname(s).substr(1)
    , type = mimetypes.lookup(ext)

    cache[s] = [etag, raw, z, type]
    cb()
  })
}

function send (req, res, cache) {
  var etag = cache[0]
  , raw = cache[1]
  , z = cache[2]
  , type = cache[3]

  if (req.headers['if-none-match'] === etag) {
    res.statusCode = 304
    return res.end()
  }

  var enc = req.negotiator.preferredEncoding('gzip', 'identity')
  res.setHeader('content-type', type)
  res.setHeader('etag', etag)
  if (enc === 'gzip') {
    res.setHeader('content-encoding', 'gzip')
    res.end(z)
  } else {
    res.end(raw)
  }
}

function static (req, res) {
  if (Date.now() - lastLoaded > interval) load()

  var p = url.parse(req.url).pathname
  var f = path.join('/', p)
  , d = path.join(__dirname, '..', f)

  if (cache[d]) return send(req, res, cache[d])

  fs.readFile(d, function (er, raw) {
    if (er && er.code === 'EISDIR') {
      fs.readFile(d + '/index.html', arguments.callee)
    }
    if (er) return res.error(404)
    render(d, raw, function (er) {
      send(req, res, cache[d])
    })
  })
}

function getETag (str) {
  var h = crypto.createHash("sha1")
  h.update(str)
  return '"' + h.digest('base64') + '"'
}
<commit_msg>Better index handling for docs<commit_after>// XXX Abstract this out into a separate module.
// It's almost identical to routes/styl.js and routes/static.js

module.exports = static

var path = require("path")
, cache = {}
, crypto = require('crypto')
, glob = require('glob')
, zlib = require('zlib')
, mimetypes = require('filed/mimetypes.js')
, fs = require('fs')
, url = require('url')

var loading = false
, lastLoaded = Date.now()
, interval = 1000 * 60 // 1 minute

load(true)

function load (startup) {
  if (loading) return
  lastLoaded = Date.now()

  glob.sync('{doc,api}/**/*.*').forEach(function (s) {
    fs.readFile(s, function (er, raw) {
      if (er && startup) throw er
      if (er) return

      if (cache[s] && getETag(raw) === cache[s][0]) return
      delete cache[s]
      render(s, raw, function (er) {
        if (er && startup) throw er
      })
    })
  })
}

function render (s, raw, cb) {
  if (cache[s]) return cb()
  var etag = getETag(raw)
  zlib.gzip(raw, function (er, z) {
    var ext = path.extname(s).substr(1)
    , type = mimetypes.lookup(ext)

    cache[s] = [etag, raw, z, type]
    cb()
  })
}

function send (req, res, cache) {
  var etag = cache[0]
  , raw = cache[1]
  , z = cache[2]
  , type = cache[3]

  if (req.headers['if-none-match'] === etag) {
    res.statusCode = 304
    return res.end()
  }

  var enc = req.negotiator.preferredEncoding('gzip', 'identity')
  res.setHeader('content-type', type)
  res.setHeader('etag', etag)
  if (enc === 'gzip') {
    res.setHeader('content-encoding', 'gzip')
    res.end(z)
  } else {
    res.end(raw)
  }
}

function static (req, res) {
  if (Date.now() - lastLoaded > interval) load()

  var p = url.parse(req.url).pathname
  var f = path.join('/', p)
  , d = path.join(__dirname, '..', f)
  , index = d.replace(/\/+$/, '').replace(/\/+/g, '/') + '/index.html'

  if (cache[d]) return send(req, res, cache[d])
  if (cache[index]) return send(req, res, cache[index])

  fs.readFile(d, function (er, raw) {
    if (er && er.code === 'EISDIR') {
      d = index
      return fs.readFile(d, arguments.callee)
    }

    if (er) return res.error(er, 404)
    render(d, raw, function (er) {
      send(req, res, cache[d])
    })
  })
}

function getETag (str) {
  var h = crypto.createHash("sha1")
  h.update(str)
  return '"' + h.digest('base64') + '"'
}
<|endoftext|>


<commit_before>(function (undefined) {
	var isMSIE = /*@cc_on!@*/false;

	function BBSDat (thread) {
		this.thread = thread;
		this.ajax = new Ajax();
		this.init();
	}

	(function (p) {
		p.init = function () {
			this.data = "";
			this.lastmodified = "Thu, 01 Jun 1970 00:00:00 GMT";
		};
		p.renderHTML = function (options) {
			var html = [];
			var lines = this.data.getResLines();
			var rescnt;

			if(options.end == null)
			{
				options.end = lines.length;
			}

			rescnt = lines.length;

			if((options.first == true) && (options.start > 1))
			{
				html.push(lines[0].createResLine(1));
			}

			for(var i = (options.start - 1) ; ((i < options.end) && (i < rescnt)) ; i++)
			{
				if(lines[i] == "")
				{
					break;
				}

				html.push(lines[i].createResLine(i+1));
			}

			return html.join("\n");
		};

		p.requestDat = function (callback) {
			var url = this.thread.createDatURL();
			var self = this;
			var headers = {};

			headers["Content-Type"] = "text/plain; charset=x-sjis";
			headers["If-Modified-Since"] = this.lastmodified;

			this.ajax.get(url, {
				callback: function (data, status) {
					if(status === 404 || status === 302)
					{
						callback.call(self.thread, false, "YXbh͍폜ꂽA݂܂B");
					}
					else if(status !== 200 && status !== 304)
					{
						callback.call(self.thread, false, "ʐMG[܂B");
					}
					else if(status === 200)
					{
						self.data = data;
						var lastmodified;

						if(!(lastmodified = self.ajax.getResponseHeader("Last-Modified")))
						{
							callback.call(self.thread, false, "dat̍ŏIXV擾ł܂łB");
						}
						else
						{
							self.lastmodified = lastmodified;
							callback.call(self.thread, true, "");
						}
					}
				},
				headers: headers
			});
		};
	})(BBSDat.prototype);

	function BBSThread (paths) {
		this.requestcomp = true;
		this.shiftkey = false;

		var pathinfo = paths.split("/");

		var match = window.location.pathname.match(/^.*(\/test\/read.html)/g);

		this.urlbase = match[0].replace("test/read.html", "");
		this.bbs = pathinfo[1];
		this.key = pathinfo[2];

		var self = this;

		var onKeyDown = function (e) {
			var key = self.keyCode(e);

			if(key === 16) self.shiftkey = true;
			if(key === 13 && self.shiftkey)
			{
				e.preventDefault();
				self.postRes();
			}
		};

		if(window.opera)
		{
			$("#message").on("keypress", function (e) {
				return onKeyDown(e);
			});
		}
		else
		{
			$("#message").on("keydown", function (e) {
				return onKeyDown(e);
			});
		}

		$("#message").on("keyup", function (e) {
			var key = self.keyCode(e);
			if(key === 16) self.shiftkey = false;
		});

		$("#name").val(Cookie.get("FROM"));
		$("#e-mail").val(Cookie.get("mail"));

		this.datdata = new BBSDat(this);
		this.ajax = new Ajax();

		this.startLoadingView();

		this.loadThread(function () {
			this.terminateLoadingView();
			this.parseOptions(decodeURIComponent(pathinfo[3]));
			this.render();
		}, function () {
			if(this.requestcomp == false)
			{
				window.alert("ʐMł...");
				return false;
			}
			return true;
		});
	}

	(function (p) {
		p.keyCode = function (e) {
			if(document.all) return e.originalEvent.keyCode;
			else if(document.getElementById) return (e.originalEvent.keyCode) ? e.originalEvent.keyCode : e.originalEvent.charCode;
			else if(document.layers) return e.originalEvent.which;
			else return 0;
		};
		p.createDatURL = function () {
			return (this.urlbase + this.bbs + "/dat/" + this.key + ".dat?" + (new Date()).getTime());
		};
		p.createPostURL = function () {
			return (this.urlbase + "test/bbs.cgi?guid=On");
		};
		p.loadThread = function (callback, beforeLoad) {
			if(beforeLoad && !beforeLoad.call(this)) return;

			this.requestcomp = false;

			this.datdata.requestDat(function (result, message) {
				if(!result) {
					alert(message);
				}
				else
				{
					callback.call(this);
				}
			});
		};
		p.postRes = function () {
			var self = this;
			var params = $("#resparams");

			this.startSendingView();

			this.ajax.post(this.createPostURL(), {
				callback: function (data, status) {
					self.terminateSendingView();

					var params = $("#resparams");
					if( (status == 200) && (//.test(data)) )
					{
						msgwindow = window.open();
						msgwindow.document.write(data);
					}
					params.find("#message").val("");

					self.options.end = null;
					self.loadThread(function () {
						this.render();
					});
				},
				params: {
					bbs: self.bbs,
					key: self.key,
					FROM: params.find("input[name='FROM']").val(),
					mail: params.find("input[name='mail']").val(),
					MESSAGE: params.find("#message").val(),
					submit: "",
					utn: "utn",
					time: params.find("input[name='time']").val(),
					suka: params.find("input[name='suka']").val()
				},
				headers: {
					"Referer": this.urlbase + "/" + this.bbs + "/",
					"Content-Type": "application/x-www-form-urlencoded"
				},
				encoder: EscapeSJIS
			});
		};
		p.reload = function () {
			this.startLoadingView();
			this.loadThread(function () {
				this.terminateLoadingView();
				this.render();
			});
		};
		p.startLoadingView = function () {
			var count = 3;

			this.timerid = window.setInterval(function () {
				count++;
				var str = "ǂݍݒ";
				for(var i=0; i < count; i++)
				{
					str += ".";
				}

				if(count > 20)
				{
					count = 0;
				}

				$("#resbodys").html("" + str + "");
			}, 100);
		};
		p.terminateLoadingView = function () {
			window.clearInterval(this.timerid);
		};
		p.startSendingView = function () {
			var count = 3;

			this.timerid = window.setInterval( function () {
				count++;
				var str = "ݒ";
				for(var i=0; i < count; i++)
				{
					str += ".";
				}

				if(count > 20)
				{
					count = 0;
				}

				$("#resbodys").html("" + str + "");
			}, 100);
		};
		p.terminateSendingView = function () {
			window.clearInterval(this.timerid);
		};
		p.render = function() {
			$("#resbodys").html(this.datdata.renderHTML(this.options));
			var lines = this.datdata.data.getResLines();
			var subject = lines[0].split("<>")[4];

			if(subject == undefined)
			{
				subject = 'X^Cs';
			}

			$("title").html(subject);
			$("#subject").html(subject);
			var date = new Date();
			var speed = (lines.length - 1) / (Math.floor(date.getTime() / 60000) - Math.floor(this.key / 60)) * 60 * 24;
			$("#speed").html("Speed:" + speed + "");
			$("#size").html(this.datdata.data.length - 1);
		};
		p.parseOptions = function(options) {
			var reslines = this.datdata.data.getResLines();

			this.options = {};

			if(/^\|(\d+)n?$/.test(options))
			{
				var match = options.match(/\d+/g);

				if(/n$/.test(options))
				{
					this.options.first = false;
				}
				else
				{
					this.options.first = true;
				}

				this.options.start = reslines.length - match[0];

				if(this.options.start < 1)
				{
					this.options.start = 1;
				}

				this.options.end = reslines.length;
			}
			else if(/^(\d+)-(\d+)n?$/.test(options))
			{
				var match = options.match(/\d+/g);

				if(/n$/.test(options))
				{
					this.options.first = false;
				}
				else
				{
					this.options.first = true;
				}

				this.options.start = match[0];
				this.options.end   = match[1];
			}
			else if(/^-(\d+)n?$/.test(options))
			{
				var match = options.match(/\d+/g);

				if(/n$/.test(options))
				{
					this.options.first = false;
				}
				else
				{
					this.options.first = true;
				}

				this.options.start = 1;
				this.options.end   = match[0];
			}
			else if(/^(\d+)-n?$/.test(options))
			{
				var match = options.match(/\d+/g);

				if(/n$/.test(options))
				{
					this.options.first = false;
				}
				else
				{
					this.options.first = true;
				}

				this.options.start = match[0];
				this.options.end   = reslines.length;
			}
			else if(/^(\d+)n?$/.test(options))
			{
				var match = options.match(/\d+/g);

				if(/n$/.test(options))
				{
					this.options.first = false;
				}
				else
				{
					this.options.first = true;
				}

				this.options.start = match[0];
				this.options.end   = match[0];
			}
			else if(/^$/.test(options))
			{
				this.options.first = false;
				this.options.start = 1;
				this.options.end   = reslines.length;
			}
			else
			{
				window.alert("IvVw肪słB");
			}

			this.pastfrom = this.options.start - 100;
			if(this.pastfrom < 1)
			{
				this.pastfrom = 1;
			}

			this.pastto = this.options.start - 1;
			if(this.pastto < 1)
			{
				this.pastto = 1;
			}

			this.nextfrom = this.options.end + 1;
			this.nextto   = this.options.end + 100;

		};
	})(BBSThread.prototype);

	window.BBSDat = BBSDat;
	window.BBSThread = BBSThread;
})();
<commit_msg>status304のときの処理を追加<commit_after>(function (undefined) {
	var isMSIE = /*@cc_on!@*/false;

	function BBSDat (thread) {
		this.thread = thread;
		this.ajax = new Ajax();
		this.init();
	}

	(function (p) {
		p.init = function () {
			this.data = "";
			this.lastmodified = "Thu, 01 Jun 1970 00:00:00 GMT";
		};
		p.renderHTML = function (options) {
			var html = [];
			var lines = this.data.getResLines();
			var rescnt;

			if(options.end == null)
			{
				options.end = lines.length;
			}

			rescnt = lines.length;

			if((options.first == true) && (options.start > 1))
			{
				html.push(lines[0].createResLine(1));
			}

			for(var i = (options.start - 1) ; ((i < options.end) && (i < rescnt)) ; i++)
			{
				if(lines[i] == "")
				{
					break;
				}

				html.push(lines[i].createResLine(i+1));
			}

			return html.join("\n");
		};

		p.requestDat = function (callback) {
			var url = this.thread.createDatURL();
			var self = this;
			var headers = {};

			headers["Content-Type"] = "text/plain; charset=x-sjis";
			headers["If-Modified-Since"] = this.lastmodified;

			this.ajax.get(url, {
				callback: function (data, status) {
					if(status === 404 || status === 302)
					{
						callback.call(self.thread, false, "YXbh͍폜ꂽA݂܂B");
					}
					else if(status !== 200 && status !== 304)
					{
						callback.call(self.thread, false, "ʐMG[܂B");
					}
					else if(status === 200)
					{
						self.data = data;
						var lastmodified;

						if(!(lastmodified = self.ajax.getResponseHeader("Last-Modified")))
						{
							callback.call(self.thread, false, "dat̍ŏIXV擾ł܂łB");
						}
						else
						{
							self.lastmodified = lastmodified;
							callback.call(self.thread, true, "");
						}
					}
					else
					{
						callback.call(self.thread, true, "");
					}
				},
				headers: headers
			});
		};
	})(BBSDat.prototype);

	function BBSThread (paths) {
		this.requestcomp = true;
		this.shiftkey = false;

		var pathinfo = paths.split("/");

		var match = window.location.pathname.match(/^.*(\/test\/read.html)/g);

		this.urlbase = match[0].replace("test/read.html", "");
		this.bbs = pathinfo[1];
		this.key = pathinfo[2];

		var self = this;

		var onKeyDown = function (e) {
			var key = self.keyCode(e);

			if(key === 16) self.shiftkey = true;
			if(key === 13 && self.shiftkey)
			{
				e.preventDefault();
				self.postRes();
			}
		};

		if(window.opera)
		{
			$("#message").on("keypress", function (e) {
				return onKeyDown(e);
			});
		}
		else
		{
			$("#message").on("keydown", function (e) {
				return onKeyDown(e);
			});
		}

		$("#message").on("keyup", function (e) {
			var key = self.keyCode(e);
			if(key === 16) self.shiftkey = false;
		});

		$("#name").val(Cookie.get("FROM"));
		$("#e-mail").val(Cookie.get("mail"));

		this.datdata = new BBSDat(this);
		this.ajax = new Ajax();

		this.startLoadingView();

		this.loadThread(function () {
			this.terminateLoadingView();
			this.parseOptions(decodeURIComponent(pathinfo[3]));
			this.render();
		}, function () {
			if(this.requestcomp == false)
			{
				window.alert("ʐMł...");
				return false;
			}
			return true;
		});
	}

	(function (p) {
		p.keyCode = function (e) {
			if(document.all) return e.originalEvent.keyCode;
			else if(document.getElementById) return (e.originalEvent.keyCode) ? e.originalEvent.keyCode : e.originalEvent.charCode;
			else if(document.layers) return e.originalEvent.which;
			else return 0;
		};
		p.createDatURL = function () {
			return (this.urlbase + this.bbs + "/dat/" + this.key + ".dat?" + (new Date()).getTime());
		};
		p.createPostURL = function () {
			return (this.urlbase + "test/bbs.cgi?guid=On");
		};
		p.loadThread = function (callback, beforeLoad) {
			if(beforeLoad && !beforeLoad.call(this)) return;

			this.requestcomp = false;

			this.datdata.requestDat(function (result, message) {
				if(!result) {
					alert(message);
				}
				else
				{
					callback.call(this);
				}
			});
		};
		p.postRes = function () {
			var self = this;
			var params = $("#resparams");

			this.startSendingView();

			this.ajax.post(this.createPostURL(), {
				callback: function (data, status) {
					self.terminateSendingView();

					var params = $("#resparams");
					if( (status == 200) && (//.test(data)) )
					{
						msgwindow = window.open();
						msgwindow.document.write(data);
					}
					params.find("#message").val("");

					self.options.end = null;
					self.loadThread(function () {
						this.render();
					});
				},
				params: {
					bbs: self.bbs,
					key: self.key,
					FROM: params.find("input[name='FROM']").val(),
					mail: params.find("input[name='mail']").val(),
					MESSAGE: params.find("#message").val(),
					submit: "",
					utn: "utn",
					time: params.find("input[name='time']").val(),
					suka: params.find("input[name='suka']").val()
				},
				headers: {
					"Referer": this.urlbase + "/" + this.bbs + "/",
					"Content-Type": "application/x-www-form-urlencoded"
				},
				encoder: EscapeSJIS
			});
		};
		p.reload = function () {
			this.startLoadingView();
			this.loadThread(function () {
				this.terminateLoadingView();
				this.render();
			});
		};
		p.startLoadingView = function () {
			var count = 3;

			this.timerid = window.setInterval(function () {
				count++;
				var str = "ǂݍݒ";
				for(var i=0; i < count; i++)
				{
					str += ".";
				}

				if(count > 20)
				{
					count = 0;
				}

				$("#resbodys").html("" + str + "");
			}, 100);
		};
		p.terminateLoadingView = function () {
			window.clearInterval(this.timerid);
		};
		p.startSendingView = function () {
			var count = 3;

			this.timerid = window.setInterval( function () {
				count++;
				var str = "ݒ";
				for(var i=0; i < count; i++)
				{
					str += ".";
				}

				if(count > 20)
				{
					count = 0;
				}

				$("#resbodys").html("" + str + "");
			}, 100);
		};
		p.terminateSendingView = function () {
			window.clearInterval(this.timerid);
		};
		p.render = function() {
			$("#resbodys").html(this.datdata.renderHTML(this.options));
			var lines = this.datdata.data.getResLines();
			var subject = lines[0].split("<>")[4];

			if(subject == undefined)
			{
				subject = 'X^Cs';
			}

			$("title").html(subject);
			$("#subject").html(subject);
			var date = new Date();
			var speed = (lines.length - 1) / (Math.floor(date.getTime() / 60000) - Math.floor(this.key / 60)) * 60 * 24;
			$("#speed").html("Speed:" + speed + "");
			$("#size").html(this.datdata.data.length - 1);
		};
		p.parseOptions = function(options) {
			var reslines = this.datdata.data.getResLines();

			this.options = {};

			if(/^\|(\d+)n?$/.test(options))
			{
				var match = options.match(/\d+/g);

				if(/n$/.test(options))
				{
					this.options.first = false;
				}
				else
				{
					this.options.first = true;
				}

				this.options.start = reslines.length - match[0];

				if(this.options.start < 1)
				{
					this.options.start = 1;
				}

				this.options.end = reslines.length;
			}
			else if(/^(\d+)-(\d+)n?$/.test(options))
			{
				var match = options.match(/\d+/g);

				if(/n$/.test(options))
				{
					this.options.first = false;
				}
				else
				{
					this.options.first = true;
				}

				this.options.start = match[0];
				this.options.end   = match[1];
			}
			else if(/^-(\d+)n?$/.test(options))
			{
				var match = options.match(/\d+/g);

				if(/n$/.test(options))
				{
					this.options.first = false;
				}
				else
				{
					this.options.first = true;
				}

				this.options.start = 1;
				this.options.end   = match[0];
			}
			else if(/^(\d+)-n?$/.test(options))
			{
				var match = options.match(/\d+/g);

				if(/n$/.test(options))
				{
					this.options.first = false;
				}
				else
				{
					this.options.first = true;
				}

				this.options.start = match[0];
				this.options.end   = reslines.length;
			}
			else if(/^(\d+)n?$/.test(options))
			{
				var match = options.match(/\d+/g);

				if(/n$/.test(options))
				{
					this.options.first = false;
				}
				else
				{
					this.options.first = true;
				}

				this.options.start = match[0];
				this.options.end   = match[0];
			}
			else if(/^$/.test(options))
			{
				this.options.first = false;
				this.options.start = 1;
				this.options.end   = reslines.length;
			}
			else
			{
				window.alert("IvVw肪słB");
			}

			this.pastfrom = this.options.start - 100;
			if(this.pastfrom < 1)
			{
				this.pastfrom = 1;
			}

			this.pastto = this.options.start - 1;
			if(this.pastto < 1)
			{
				this.pastto = 1;
			}

			this.nextfrom = this.options.end + 1;
			this.nextto   = this.options.end + 100;

		};
	})(BBSThread.prototype);

	window.BBSDat = BBSDat;
	window.BBSThread = BBSThread;
})();
<|endoftext|>


<commit_before>import PropTypes from 'prop-types';
import React, { Fragment } from 'react';

/**
 * The Search Results component
 *
 * @disable-docs
 */
const SearchResults = ( page ) => (
		<Fragment>