<commit_before>/*jshint strict: false, maxlen: 500 */
/*global require, assertTrue, assertEqual, assertNotEqual, AQL_EXECUTE, AQL_EXPLAIN */

////////////////////////////////////////////////////////////////////////////////
/// @brief tests for COLLECT w/ COUNT
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

var jsunity = require("jsunity");
var internal = require("internal");
var errors = internal.errors;
var db = require("org/arangodb").db;
var helper = require("org/arangodb/aql-helper");
var assertQueryError = helper.assertQueryError;

////////////////////////////////////////////////////////////////////////////////
/// @brief test suite
////////////////////////////////////////////////////////////////////////////////

function optimizerCountTestSuite () {
  var c;

  return {
    setUp : function () {
      db._drop("UnitTestsCollection");
      c = db._create("UnitTestsCollection");

      for (var i = 0; i < 1000; ++i) {
        c.save({ group: "test" + (i % 10), value: i });
      }
    },

    tearDown : function () {
      db._drop("UnitTestsCollection");
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test no into, no count
////////////////////////////////////////////////////////////////////////////////

    testInvalidSyntax : function () {
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT COUNT RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT WITH COUNT RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT WITH COUNT i RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT WITH COUNT INTO RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT WITH COUNT g RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT COUNT INTO g RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT g WITH COUNT RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT g WITH COUNT INTO RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT class = i.group WITH COUNT RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT class = i.group WITH COUNT i RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT class = i.group WITH COUNT i INTO group RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT class = i.group WITH COUNT COUNT INTO group RETURN 1");
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count
////////////////////////////////////////////////////////////////////////////////

    testCountTotalSimple : function () {
      var query = "FOR i IN " + c.name() + " COLLECT WITH COUNT INTO count RETURN count";

      var results = AQL_EXECUTE(query);
      assertEqual(1, results.json.length);
      assertEqual(1000, results.json[0]);

      var plan = AQL_EXPLAIN(query).plan;
      // must not have a SortNode
      assertEqual(-1, plan.nodes.map(function(node) { return node.type; }).indexOf("SortNode"));
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count
////////////////////////////////////////////////////////////////////////////////

    testCountTotalFiltered : function () {
      var query = "FOR i IN " + c.name() + " FILTER i.group == 'test4' COLLECT WITH COUNT INTO count RETURN count";

      var results = AQL_EXECUTE(query);
      assertEqual(1, results.json.length);
      assertEqual(100, results.json[0]);

      var plan = AQL_EXPLAIN(query).plan;
      // must not have a SortNode
      assertEqual(-1, plan.nodes.map(function(node) { return node.type; }).indexOf("SortNode"));
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count
////////////////////////////////////////////////////////////////////////////////

    testCountTotalFilteredMulti : function () {
      var query = "FOR i IN " + c.name() + " FILTER i.group >= 'test2' && i.group <= 'test4' COLLECT WITH COUNT INTO count RETURN count";

      var results = AQL_EXECUTE(query);
      assertEqual(1, results.json.length);
      assertEqual(300, results.json[0]);

      var plan = AQL_EXPLAIN(query).plan;
      // must not have a SortNode
      assertEqual(-1, plan.nodes.map(function(node) { return node.type; }).indexOf("SortNode"));
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count
////////////////////////////////////////////////////////////////////////////////

    testCountTotalFilteredEmpty : function () {
      var query = "FOR i IN " + c.name() + " FILTER i.group >= 'test99' COLLECT WITH COUNT INTO count RETURN count";

      var results = AQL_EXECUTE(query);
      assertEqual(1, results.json.length);
      assertEqual(0, results.json[0]);

      var plan = AQL_EXPLAIN(query).plan;
      // must not have a SortNode
      assertEqual(-1, plan.nodes.map(function(node) { return node.type; }).indexOf("SortNode"));
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count
////////////////////////////////////////////////////////////////////////////////

    testCountTotalFilteredBig : function () {
      var i;
      for (i = 0; i < 10000; ++i) {
        c.save({ age: 10 + (i % 80), type: 1 });
      }
      for (i = 0; i < 10000; ++i) {
        c.save({ age: 10 + (i % 80), type: 2 });
      }

      var query = "FOR i IN " + c.name() + " FILTER i.age >= 20 && i.age < 50 && i.type == 1 COLLECT WITH COUNT INTO count RETURN count";

      var results = AQL_EXECUTE(query);
      assertEqual(1, results.json.length);
      assertEqual(125 * 30, results.json[0]);

      var plan = AQL_EXPLAIN(query).plan;
      // must not have a SortNode
      assertEqual(-1, plan.nodes.map(function(node) { return node.type; }).indexOf("SortNode"));
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count
////////////////////////////////////////////////////////////////////////////////

    testCountTotalNested : function () {
      var query = "FOR i IN 1..2 FOR j IN " + c.name() + " COLLECT WITH COUNT INTO count RETURN count";

      var results = AQL_EXECUTE(query);
      assertEqual(1, results.json.length);
      assertEqual(2000, results.json[0]);

      var plan = AQL_EXPLAIN(query).plan;
      // must not have a SortNode
      assertEqual(-1, plan.nodes.map(function(node) { return node.type; }).indexOf("SortNode"));
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count
////////////////////////////////////////////////////////////////////////////////

    testCountSimple : function () {
      var query = "FOR i IN " + c.name() + " COLLECT class = i.group WITH COUNT INTO count RETURN [ class, count ]";

      var results = AQL_EXECUTE(query);
      assertEqual(10, results.json.length);
      for (var i = 0; i < results.json.length; ++i) {
        var group = results.json[i];
        assertTrue(Array.isArray(group));
        assertEqual("test" + i, group[0]);
        assertEqual(100, group[1]);
      }

      var plan = AQL_EXPLAIN(query).plan;
      // must have a SortNode
      assertNotEqual(-1, plan.nodes.map(function(node) { return node.type; }).indexOf("SortNode"));
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count
////////////////////////////////////////////////////////////////////////////////

    testCountFiltered : function () {
      var query = "FOR i IN " + c.name() + " FILTER i.group >= 'test1' && i.group <= 'test4' COLLECT class = i.group WITH COUNT INTO count RETURN [ class, count ]";

      var results = AQL_EXECUTE(query);
      assertEqual(4, results.json.length);
      for (var i = 0; i < results.json.length; ++i) {
        var group = results.json[i];
        assertTrue(Array.isArray(group));
        assertEqual("test" + (i + 1), group[0]);
        assertEqual(100, group[1]);
      }

      var plan = AQL_EXPLAIN(query).plan;
      // must have a SortNode
      assertNotEqual(-1, plan.nodes.map(function(node) { return node.type; }).indexOf("SortNode"));
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count
////////////////////////////////////////////////////////////////////////////////

    testCountFilteredEmpty : function () {
      var query = "FOR i IN " + c.name() + " FILTER i.group >= 'test99' COLLECT class = i.group WITH COUNT INTO count RETURN [ class, count ]";

      var results = AQL_EXECUTE(query);
      assertEqual(0, results.json.length);

      var plan = AQL_EXPLAIN(query).plan;
      // must have a SortNode
      assertNotEqual(-1, plan.nodes.map(function(node) { return node.type; }).indexOf("SortNode"));
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count
////////////////////////////////////////////////////////////////////////////////

    testCountFilteredBig : function () {
      var i;
      for (i = 0; i < 10000; ++i) {
        c.save({ age: 10 + (i % 80), type: 1 });
      }
      for (i = 0; i < 10000; ++i) {
        c.save({ age: 10 + (i % 80), type: 2 });
      }

      var query = "FOR i IN " + c.name() + " FILTER i.age >= 20 && i.age < 50 && i.type == 1 COLLECT age = i.age WITH COUNT INTO count RETURN [ age, count ]";

      var results = AQL_EXECUTE(query);
      assertEqual(30, results.json.length);
      for (i = 0; i < results.json.length; ++i) {
        var group = results.json[i];
        assertTrue(Array.isArray(group));
        assertEqual(20 + i, group[0]);
        assertEqual(125, group[1]);
      }

      var plan = AQL_EXPLAIN(query).plan;
      // must have a SortNode
      assertNotEqual(-1, plan.nodes.map(function(node) { return node.type; }).indexOf("SortNode"));
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count
////////////////////////////////////////////////////////////////////////////////

    testCountNested : function () {
      var query = "FOR i IN 1..2 FOR j IN " + c.name() + " COLLECT class1 = i, class2 = j.group WITH COUNT INTO count RETURN [ class1, class2, count ]";

      var results = AQL_EXECUTE(query), x = 0;
      assertEqual(20, results.json.length);
      for (var i = 1; i <= 2; ++i) {
        for (var j = 0; j < 10; ++j) {
          var group = results.json[x++];
          assertTrue(Array.isArray(group));
          assertEqual(i, group[0]);
          assertEqual("test" + j, group[1]);
          assertEqual(100, group[2]);
        }
      }

      var plan = AQL_EXPLAIN(query).plan;
      // must have a SortNode
      assertNotEqual(-1, plan.nodes.map(function(node) { return node.type; }).indexOf("SortNode"));
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count shaped
////////////////////////////////////////////////////////////////////////////////

    testCountShaped : function () {
      var query = "FOR j IN " + c.name() + " COLLECT doc = j WITH COUNT INTO count RETURN doc";

      var results = AQL_EXECUTE(query), x = 0;
      // expectation is that we get 1000 different docs and do not crash (issue #1265)
      assertEqual(1000, results.json.length);
    }

  };
}

////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suite
////////////////////////////////////////////////////////////////////////////////

jsunity.run(optimizerCountTestSuite);

return jsunity.done();

// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:
<commit_msg>fixed warning<commit_after>/*jshint strict: false, maxlen: 500 */
/*global require, assertTrue, assertEqual, assertNotEqual, AQL_EXECUTE, AQL_EXPLAIN */

////////////////////////////////////////////////////////////////////////////////
/// @brief tests for COLLECT w/ COUNT
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2010-2012 triagens GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Jan Steemann
/// @author Copyright 2012, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

var jsunity = require("jsunity");
var internal = require("internal");
var errors = internal.errors;
var db = require("org/arangodb").db;
var helper = require("org/arangodb/aql-helper");
var assertQueryError = helper.assertQueryError;

////////////////////////////////////////////////////////////////////////////////
/// @brief test suite
////////////////////////////////////////////////////////////////////////////////

function optimizerCountTestSuite () {
  var c;

  return {
    setUp : function () {
      db._drop("UnitTestsCollection");
      c = db._create("UnitTestsCollection");

      for (var i = 0; i < 1000; ++i) {
        c.save({ group: "test" + (i % 10), value: i });
      }
    },

    tearDown : function () {
      db._drop("UnitTestsCollection");
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test no into, no count
////////////////////////////////////////////////////////////////////////////////

    testInvalidSyntax : function () {
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT COUNT RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT WITH COUNT RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT WITH COUNT i RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT WITH COUNT INTO RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT WITH COUNT g RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT COUNT INTO g RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT g WITH COUNT RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT g WITH COUNT INTO RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT class = i.group WITH COUNT RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT class = i.group WITH COUNT i RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT class = i.group WITH COUNT i INTO group RETURN 1");
      assertQueryError(errors.ERROR_QUERY_PARSE.code, "FOR i IN " + c.name() + " COLLECT class = i.group WITH COUNT COUNT INTO group RETURN 1");
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count
////////////////////////////////////////////////////////////////////////////////

    testCountTotalSimple : function () {
      var query = "FOR i IN " + c.name() + " COLLECT WITH COUNT INTO count RETURN count";

      var results = AQL_EXECUTE(query);
      assertEqual(1, results.json.length);
      assertEqual(1000, results.json[0]);

      var plan = AQL_EXPLAIN(query).plan;
      // must not have a SortNode
      assertEqual(-1, plan.nodes.map(function(node) { return node.type; }).indexOf("SortNode"));
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count
////////////////////////////////////////////////////////////////////////////////

    testCountTotalFiltered : function () {
      var query = "FOR i IN " + c.name() + " FILTER i.group == 'test4' COLLECT WITH COUNT INTO count RETURN count";

      var results = AQL_EXECUTE(query);
      assertEqual(1, results.json.length);
      assertEqual(100, results.json[0]);

      var plan = AQL_EXPLAIN(query).plan;
      // must not have a SortNode
      assertEqual(-1, plan.nodes.map(function(node) { return node.type; }).indexOf("SortNode"));
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count
////////////////////////////////////////////////////////////////////////////////

    testCountTotalFilteredMulti : function () {
      var query = "FOR i IN " + c.name() + " FILTER i.group >= 'test2' && i.group <= 'test4' COLLECT WITH COUNT INTO count RETURN count";

      var results = AQL_EXECUTE(query);
      assertEqual(1, results.json.length);
      assertEqual(300, results.json[0]);

      var plan = AQL_EXPLAIN(query).plan;
      // must not have a SortNode
      assertEqual(-1, plan.nodes.map(function(node) { return node.type; }).indexOf("SortNode"));
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count
////////////////////////////////////////////////////////////////////////////////

    testCountTotalFilteredEmpty : function () {
      var query = "FOR i IN " + c.name() + " FILTER i.group >= 'test99' COLLECT WITH COUNT INTO count RETURN count";

      var results = AQL_EXECUTE(query);
      assertEqual(1, results.json.length);
      assertEqual(0, results.json[0]);

      var plan = AQL_EXPLAIN(query).plan;
      // must not have a SortNode
      assertEqual(-1, plan.nodes.map(function(node) { return node.type; }).indexOf("SortNode"));
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count
////////////////////////////////////////////////////////////////////////////////

    testCountTotalFilteredBig : function () {
      var i;
      for (i = 0; i < 10000; ++i) {
        c.save({ age: 10 + (i % 80), type: 1 });
      }
      for (i = 0; i < 10000; ++i) {
        c.save({ age: 10 + (i % 80), type: 2 });
      }

      var query = "FOR i IN " + c.name() + " FILTER i.age >= 20 && i.age < 50 && i.type == 1 COLLECT WITH COUNT INTO count RETURN count";

      var results = AQL_EXECUTE(query);
      assertEqual(1, results.json.length);
      assertEqual(125 * 30, results.json[0]);

      var plan = AQL_EXPLAIN(query).plan;
      // must not have a SortNode
      assertEqual(-1, plan.nodes.map(function(node) { return node.type; }).indexOf("SortNode"));
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count
////////////////////////////////////////////////////////////////////////////////

    testCountTotalNested : function () {
      var query = "FOR i IN 1..2 FOR j IN " + c.name() + " COLLECT WITH COUNT INTO count RETURN count";

      var results = AQL_EXECUTE(query);
      assertEqual(1, results.json.length);
      assertEqual(2000, results.json[0]);

      var plan = AQL_EXPLAIN(query).plan;
      // must not have a SortNode
      assertEqual(-1, plan.nodes.map(function(node) { return node.type; }).indexOf("SortNode"));
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count
////////////////////////////////////////////////////////////////////////////////

    testCountSimple : function () {
      var query = "FOR i IN " + c.name() + " COLLECT class = i.group WITH COUNT INTO count RETURN [ class, count ]";

      var results = AQL_EXECUTE(query);
      assertEqual(10, results.json.length);
      for (var i = 0; i < results.json.length; ++i) {
        var group = results.json[i];
        assertTrue(Array.isArray(group));
        assertEqual("test" + i, group[0]);
        assertEqual(100, group[1]);
      }

      var plan = AQL_EXPLAIN(query).plan;
      // must have a SortNode
      assertNotEqual(-1, plan.nodes.map(function(node) { return node.type; }).indexOf("SortNode"));
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count
////////////////////////////////////////////////////////////////////////////////

    testCountFiltered : function () {
      var query = "FOR i IN " + c.name() + " FILTER i.group >= 'test1' && i.group <= 'test4' COLLECT class = i.group WITH COUNT INTO count RETURN [ class, count ]";

      var results = AQL_EXECUTE(query);
      assertEqual(4, results.json.length);
      for (var i = 0; i < results.json.length; ++i) {
        var group = results.json[i];
        assertTrue(Array.isArray(group));
        assertEqual("test" + (i + 1), group[0]);
        assertEqual(100, group[1]);
      }

      var plan = AQL_EXPLAIN(query).plan;
      // must have a SortNode
      assertNotEqual(-1, plan.nodes.map(function(node) { return node.type; }).indexOf("SortNode"));
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count
////////////////////////////////////////////////////////////////////////////////

    testCountFilteredEmpty : function () {
      var query = "FOR i IN " + c.name() + " FILTER i.group >= 'test99' COLLECT class = i.group WITH COUNT INTO count RETURN [ class, count ]";

      var results = AQL_EXECUTE(query);
      assertEqual(0, results.json.length);

      var plan = AQL_EXPLAIN(query).plan;
      // must have a SortNode
      assertNotEqual(-1, plan.nodes.map(function(node) { return node.type; }).indexOf("SortNode"));
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count
////////////////////////////////////////////////////////////////////////////////

    testCountFilteredBig : function () {
      var i;
      for (i = 0; i < 10000; ++i) {
        c.save({ age: 10 + (i % 80), type: 1 });
      }
      for (i = 0; i < 10000; ++i) {
        c.save({ age: 10 + (i % 80), type: 2 });
      }

      var query = "FOR i IN " + c.name() + " FILTER i.age >= 20 && i.age < 50 && i.type == 1 COLLECT age = i.age WITH COUNT INTO count RETURN [ age, count ]";

      var results = AQL_EXECUTE(query);
      assertEqual(30, results.json.length);
      for (i = 0; i < results.json.length; ++i) {
        var group = results.json[i];
        assertTrue(Array.isArray(group));
        assertEqual(20 + i, group[0]);
        assertEqual(125, group[1]);
      }

      var plan = AQL_EXPLAIN(query).plan;
      // must have a SortNode
      assertNotEqual(-1, plan.nodes.map(function(node) { return node.type; }).indexOf("SortNode"));
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count
////////////////////////////////////////////////////////////////////////////////

    testCountNested : function () {
      var query = "FOR i IN 1..2 FOR j IN " + c.name() + " COLLECT class1 = i, class2 = j.group WITH COUNT INTO count RETURN [ class1, class2, count ]";

      var results = AQL_EXECUTE(query), x = 0;
      assertEqual(20, results.json.length);
      for (var i = 1; i <= 2; ++i) {
        for (var j = 0; j < 10; ++j) {
          var group = results.json[x++];
          assertTrue(Array.isArray(group));
          assertEqual(i, group[0]);
          assertEqual("test" + j, group[1]);
          assertEqual(100, group[2]);
        }
      }

      var plan = AQL_EXPLAIN(query).plan;
      // must have a SortNode
      assertNotEqual(-1, plan.nodes.map(function(node) { return node.type; }).indexOf("SortNode"));
    },

////////////////////////////////////////////////////////////////////////////////
/// @brief test count shaped
////////////////////////////////////////////////////////////////////////////////

    testCountShaped : function () {
      var query = "FOR j IN " + c.name() + " COLLECT doc = j WITH COUNT INTO count RETURN doc";

      var results = AQL_EXECUTE(query);
      // expectation is that we get 1000 different docs and do not crash (issue #1265)
      assertEqual(1000, results.json.length);
    }

  };
}

////////////////////////////////////////////////////////////////////////////////
/// @brief executes the test suite
////////////////////////////////////////////////////////////////////////////////

jsunity.run(optimizerCountTestSuite);

return jsunity.done();

// Local Variables:
// mode: outline-minor
// outline-regexp: "^\\(/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @page\\|/// @}\\)"
// End:
<|endoftext|>


<commit_before>window.addEventListener('load', main);

function initialWorld(width, height) {
    var sheet = new SpriteSheet(document.getElementById('pasi'), 4);
    return {
        keys: {left: false, right: false},
        gamma: 0,

        width: width,
        height: height,
        g: 1,

        pasi: new Pasi({
            spriteSheet: sheet,
            x: width / 2,
            y: height - sheet.spriteHeight,
            nLeapTicks: 4,
            speed: 4,
            jumpSpeed: 12,
            gammaFactor: 1
        }),

        platformGenerator: new PlatformGenerator(width, width / 2, height)
    };
}

function main() {
    var config = {
        width: 180,
        height: 240,
        fps: 20,
        g: 1
    };

    startGame(document.getElementById('mainCanvas'), config, draw, update,
        [
            { name: 'keydown', fn: keydown },
            { name: 'keyup', fn: keyup },
            { name: 'deviceorientation', fn: orientation }
        ], [], initialWorld(config.width, config.height));
}

function draw(ctx, world) {
    ctx.clearRect(0, 0, world.width, world.height);

    world.platformGenerator.platforms.forEach(function(platform) {
        platform.draw(ctx);
    });

    world.pasi.draw(ctx);
}

function update(world) {
    if (world.keys.right) {
        world.pasi.vx = world.pasi.speed;
    } else if (world.keys.left) {
        world.pasi.vx = -world.pasi.speed;
    } else {
        world.pasi.vx = world.pasi.gammaFactor * world.gamma;
    }

    world.pasi.x += world.pasi.vx;
    world.pasi.y += world.pasi.vy;

    world.pasi.wrap(world.width);
    world.pasi.update();

    world.platformGenerator.generatePlatforms(world.height, 0);
}

function keydown(e, world) {
    if (e.keyCode === 39) {
        world.keys.right = true;
    } else if (e.keyCode === 37) {
        world.keys.left = true;
    }
}

function keyup(e, world) {
    if (e.keyCode === 39) {
        world.keys.right = false;
    } else if (e.keyCode === 37) {
        world.keys.left = false;
    }
}

function orientation(e, world) {
    world.gamma = e.gamma;
}

<commit_msg>Add gravity.<commit_after>window.addEventListener('load', main);

function initialWorld(width, height) {
    var sheet = new SpriteSheet(document.getElementById('pasi'), 4);
    return {
        keys: {left: false, right: false},
        gamma: 0,

        width: width,
        height: height,
        g: 1,

        pasi: new Pasi({
            spriteSheet: sheet,
            x: width / 2,
            y: height - sheet.spriteHeight,
            nLeapTicks: 4,
            speed: 4,
            jumpSpeed: 12,
            gammaFactor: 1
        }),

        platformGenerator: new PlatformGenerator(width, width / 2, height)
    };
}

function main() {
    var config = {
        width: 180,
        height: 240,
        fps: 20
    };

    startGame(document.getElementById('mainCanvas'), config, draw, update,
        [
            { name: 'keydown', fn: keydown },
            { name: 'keyup', fn: keyup },
            { name: 'deviceorientation', fn: orientation }
        ], [], initialWorld(config.width, config.height));
}

function draw(ctx, world) {
    ctx.clearRect(0, 0, world.width, world.height);

    world.platformGenerator.platforms.forEach(function(platform) {
        platform.draw(ctx);
    });

    world.pasi.draw(ctx);
}

function update(world) {
    if (world.keys.right) {
        world.pasi.vx = world.pasi.speed;
    } else if (world.keys.left) {
        world.pasi.vx = -world.pasi.speed;
    } else {
        world.pasi.vx = world.pasi.gammaFactor * world.gamma;
    }

    world.pasi.x += world.pasi.vx;
    world.pasi.y += world.pasi.vy;
    world.pasi.vy += world.g;

    world.pasi.wrap(world.width);
    world.pasi.update();

    world.platformGenerator.generatePlatforms(world.height, 0);
}

function keydown(e, world) {
    if (e.keyCode === 39) {
        world.keys.right = true;
    } else if (e.keyCode === 37) {
        world.keys.left = true;
    }
}

function keyup(e, world) {
    if (e.keyCode === 39) {
        world.keys.right = false;
    } else if (e.keyCode === 37) {
        world.keys.left = false;
    }
}

function orientation(e, world) {
    world.gamma = e.gamma;
}

<|endoftext|>


<commit_before>/**
 * Copyright 2015-present 650 Industries. All rights reserved.
 *
 * @providesModule MenuView
 */
'use strict';

import React, { PropTypes } from 'react';
import {
  Animated,
  Dimensions,
  Easing,
  Image,
  NativeModules,
  PixelRatio,
  ScrollView,
  StatusBar,
  StyleSheet,
  Text,
  TouchableHighlight,
  TouchableOpacity,
  View,
} from 'react-native';

let { ExponentKernel } = NativeModules;

import Expo, { Constants } from 'expo';
import { Ionicons } from '@expo/vector-icons';
import ResponsiveImage from '@expo/react-native-responsive-image';

import Browser from 'Browser';
import BrowserActions from 'BrowserActions';
import Colors from '../Home/constants/Colors';
import DevIndicator from '../Home/components/DevIndicator';
import ExStore from 'ExStore';
import FriendlyUrls from 'FriendlyUrls';

let SCREEN_WIDTH = Dimensions.get('window').width;
let MENU_NARROW_SCREEN = SCREEN_WIDTH < 375;

const AnimatedBlurView = Animated.createAnimatedComponent(Expo.BlurView);

export default class MenuView extends React.Component {
  static propTypes = {
    task: PropTypes.object.isRequired,
    shouldFadeIn: PropTypes.bool,
    isNuxFinished: PropTypes.bool,
  };

  constructor(props, context) {
    super(props, context);

    this.state = {
      transitionIn: new Animated.Value(props.shouldFadeIn ? 0 : 1),
      enableDevMenuTools: false,
      devMenuItems: {},
    };
  }

  async componentDidMount() {
    this._mounted = true;
    if (this.props.shouldFadeIn) {
      requestAnimationFrame(() => {
        this._mounted &&
          Animated.timing(this.state.transitionIn, {
            easing: Easing.inOut(Easing.quad),
            toValue: 1,
            duration: 100,
          }).start();
      });
    }
    this.forceStatusBarUpdateAsync();
    let enableDevMenuTools = await ExponentKernel.doesCurrentTaskEnableDevtools();
    let devMenuItems = await ExponentKernel.getDevMenuItemsToShow();
    if (this._mounted) {
      this.setState({ enableDevMenuTools, devMenuItems });
    }
  }

  componentWillUnmount() {
    this.restoreStatusBar();
    this._mounted = false;
  }

  forceStatusBarUpdateAsync = async () => {
    if (NativeModules.StatusBarManager._captureProperties) {
      this._statusBarValuesToRestore = await NativeModules.StatusBarManager._captureProperties();
      // HACK: StatusBar only updates changed props.
      // because MenuView typically lives under a different RN bridge, its stack of StatusBar
      // props does not necessarily reflect what the user is seeing.
      // so we force StatusBar to clear its state and update all props when we mount.
      StatusBar._currentValues = null;
    }
  };
  restoreStatusBar = () => {
    if (
      NativeModules.StatusBarManager._applyPropertiesAndForget &&
      this._statusBarValuesToRestore
    ) {
      NativeModules.StatusBarManager._applyPropertiesAndForget(
        this._statusBarValuesToRestore
      );
    }
  };
  render() {
    let scale = this.state.transitionIn.interpolate({
      inputRange: [0, 1],
      outputRange: [1.05, 1],
    });
    let intensity = this.state.transitionIn.interpolate({
      inputRange: [0, 1],
      outputRange: [0, 85],
    });

    return (
      <AnimatedBlurView
        style={styles.container}
        tint="light"
        intensity={intensity}>
        <StatusBar barStyle="default" />
        <Animated.View
          style={[
            styles.overlay,
            { opacity: this.state.transitionIn, transform: [{ scale }] },
          ]}>
          <ScrollView>
            {this.props.isNuxFinished
              ? this._renderTaskInfoRow()
              : this._renderNUXRow()}
            <View style={styles.separator} />
            <View style={styles.buttonContainer}>
              {this._renderButton(
                'refresh',
                'Refresh',
                Browser.refresh,
                require('../Assets/ios-menu-refresh.png')
              )}
              {this._renderButton(
                'home',
                'Go to Expo Home',
                this._goToHome,
                require('../Assets/ios-menu-home.png')
              )}
            </View>
            {this._maybeRenderDevMenuTools()}
            <TouchableHighlight
              style={styles.closeButton}
              onPress={this._onPressClose}
              underlayColor="#eee"
              hitSlop={{ top: 4, bottom: 8, left: 8, right: 4 }}>
              <Ionicons
                name="md-close"
                size={20}
                style={styles.closeButtonIcon}
              />
            </TouchableHighlight>
          </ScrollView>
        </Animated.View>
      </AnimatedBlurView>
    );
  }

  _renderNUXRow() {
    let tooltipMessage;
    if (Expo.Constants.isDevice) {
      tooltipMessage = 'Shake your device to show this menu.';
    } else {
      tooltipMessage = 'In iPhone Simulator, press Cmd+D to show this menu.';
    }
    let headingStyles = MENU_NARROW_SCREEN
      ? [styles.nuxHeading, styles.nuxHeadingNarrow]
      : styles.nuxHeading;
    return (
      <View style={styles.nuxRow}>
        <View style={styles.nuxHeadingRow}>
          <ResponsiveImage
            sources={{
              2: {
                uri: 'https://s3.amazonaws.com/exp-us-standard/exponent-icon@2x.png',
              },
              3: {
                uri: 'https://s3.amazonaws.com/exp-us-standard/exponent-icon@3x.png',
              },
            }}
            style={styles.nuxLogo}
          />
          <Text style={headingStyles}>
            Welcome to Expo!
          </Text>
        </View>
        <Text style={styles.nuxTooltip}>
          {tooltipMessage}
        </Text>
        <TouchableOpacity
          style={styles.nuxButton}
          onPress={this._onPressFinishNux}>
          <Text style={styles.nuxButtonLabel}>
            Got it
          </Text>
        </TouchableOpacity>
      </View>
    );
  }

  _renderTaskInfoRow() {
    let { task } = this.props;
    let taskUrl;
    if (task.loadingError) {
      taskUrl = task.loadingError.originalUrl;
    } else {
      taskUrl = task.manifestUrl
        ? FriendlyUrls.toFriendlyString(task.manifestUrl)
        : '';
    }

    let iconUrl = task.manifest && task.manifest.get('iconUrl');
    let taskName = task.manifest && task.manifest.get('name');

    let icon = iconUrl
      ? <Image source={{ uri: iconUrl }} style={styles.taskIcon} />
      : <ResponsiveImage
          resizeMode="contain"
          sources={{
            2: {
              uri: 'https://s3.amazonaws.com/exp-us-standard/exponent-icon@2x.png',
            },
            3: {
              uri: 'https://s3.amazonaws.com/exp-us-standard/exponent-icon@3x.png',
            },
          }}
          style={styles.taskIcon}
        />;
    let taskNameStyles = taskName
      ? styles.taskName
      : [styles.taskName, { color: '#c5c6c7' }];
    return (
      <View style={styles.taskMetaRow}>
        <View style={styles.taskIconColumn}>
          {icon}
        </View>
        <View style={styles.taskInfoColumn}>
          <Text style={taskNameStyles} numberOfLines={1}>
            {taskName ? taskName : 'Untitled Experience'}
          </Text>
          <Text style={[styles.taskUrl]} numberOfLines={1}>
            {taskUrl}
          </Text>
          {this._maybeRenderDevServerName()}
        </View>
      </View>
    );
  }

  _maybeRenderDevServerName() {
    let { task } = this.props;
    let devServerName =
      task.manifest && task.manifest.getIn(['developer', 'tool']);
    if (devServerName) {
      // XDE is upper
      if (devServerName === 'xde') {
        devServerName = devServerName.toUpperCase();
      }
      return (
        <View style={{ flexDirection: 'row' }}>
          <DevIndicator style={{ marginTop: 4.5, marginRight: 7 }} />
          <Text style={styles.taskDevServerName}>{devServerName}</Text>
        </View>
      );
    }
    return null;
  }

  _maybeRenderDevMenuTools() {
    if (this.state.enableDevMenuTools && this.state.devMenuItems) {
      return (
        <View>
          <View style={styles.separator} />
          <View style={styles.buttonContainer}>
            {Object.keys(this.state.devMenuItems).map((key, idx) => {
              return this._renderDevMenuItem(key, this.state.devMenuItems[key]);
            })}
          </View>
        </View>
      );
    }
    return null;
  }

  _renderDevMenuItem(key, item) {
    let { label, isEnabled } = item;
    if (isEnabled) {
      return this._renderButton(
        key,
        label,
        () => {
          this._onPressDevMenuButton(key);
        },
        null,
        true
      );
    } else {
      return (
        <View style={[styles.button, styles.buttonWithSeparator]} key={key}>
          <View style={styles.buttonIcon} />
          <Text style={[styles.buttonText, { color: '#9ca0a6' }]}>
            {label}
          </Text>
        </View>
      );
    }
  }

  _renderButton(key, text, onPress, iconSource, withSeparator) {
    let icon, buttonStyles;
    if (iconSource) {
      icon = <Image style={styles.buttonIcon} source={iconSource} />;
    } else {
      icon = <View style={styles.buttonIcon} />;
    }
    if (withSeparator) {
      buttonStyles = [styles.button, styles.buttonWithSeparator];
    } else {
      buttonStyles = styles.button;
    }
    return (
      <TouchableOpacity key={key} style={buttonStyles} onPress={onPress}>
        {icon}
        <Text style={styles.buttonText}>
          {text}
        </Text>
      </TouchableOpacity>
    );
  }

  _onPressFinishNux = () => {
    ExStore.dispatch(BrowserActions.setIsNuxFinishedAsync(true));
    ExStore.dispatch(BrowserActions.showMenuAsync(false));
  };
  _onPressClose = () => {
    if (this.props.isNuxFinished) {
      ExStore.dispatch(BrowserActions.showMenuAsync(false));
    }
  };
  _goToHome = () => {
    ExStore.dispatch(BrowserActions.foregroundHomeAsync());
  };
  _onPressDevMenuButton = key => {
    ExponentKernel.selectDevMenuItemWithKey(key);
    ExStore.dispatch(BrowserActions.showMenuAsync(false));
  };
}

let styles = StyleSheet.create({
  container: {
    position: 'absolute',
    left: 0,
    top: 0,
    bottom: 0,
    right: 0,
    justifyContent: 'center',
    flexDirection: 'row',
  },
  overlay: {
    flex: 1,
    backgroundColor: 'transparent',
    marginTop: Constants.statusBarHeight,
  },
  closeButtonIcon: {
    color: '#49a7e8',
  },
  closeButton: {
    position: 'absolute',
    right: 10,
    top: 10,
    paddingVertical: 2,
    paddingHorizontal: 6,
    borderRadius: 2,
  },
  taskMetaRow: {
    flexDirection: 'row',
    paddingHorizontal: 14,
    paddingBottom: 12,
  },
  taskInfoColumn: {
    flex: 4,
    justifyContent: 'center',
  },
  taskIconColumn: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  taskName: {
    color: '#595c68',
    backgroundColor: 'transparent',
    fontWeight: '700',
    fontSize: 16,
    marginTop: 14,
    marginBottom: 1,
  },
  taskUrl: {
    color: '#9ca0a6',
    backgroundColor: 'transparent',
    marginRight: 16,
    marginBottom: 2,
    marginTop: 1,
    fontSize: 12,
  },
  taskIcon: {
    width: 52,
    height: 52,
    marginTop: 12,
    marginRight: 10,
    alignSelf: 'center',
    backgroundColor: 'transparent',
  },
  taskDevServerName: {
    fontSize: 12,
    color: '#9ca0a6',
    fontWeight: '700',
  },
  separator: {
    borderColor: '#e3e3e3',
    borderTopWidth: 1 / PixelRatio.get(),
    backgroundColor: '#f6f6f7',
    height: 12,
    marginVertical: 4,
    marginHorizontal: -1,
  },
  buttonContainer: {
    backgroundColor: 'transparent',
  },
  button: {
    backgroundColor: 'transparent',
    flexDirection: 'row',
  },
  buttonWithSeparator: {
    borderBottomWidth: StyleSheet.hairlineWidth * 2,
    borderBottomColor: '#f4f4f5',
  },
  buttonIcon: {
    width: 16,
    height: 16,
    marginVertical: 12,
    marginLeft: 20,
    alignSelf: 'flex-start',
  },
  buttonText: {
    color: '#595c68',
    fontSize: 14,
    textAlign: 'left',
    marginVertical: 12,
    marginRight: 5,
    paddingHorizontal: 12,
    fontWeight: '700',
  },
  nuxRow: {
    paddingHorizontal: 12,
  },
  nuxHeadingRow: {
    flexDirection: 'row',
    marginTop: 16,
    marginRight: 16,
    marginBottom: 8,
  },
  nuxLogo: {
    width: 47 * 0.7,
    height: 40 * 0.7,
    marginRight: 12,
    marginLeft: 8,
    alignSelf: 'flex-start',
  },
  nuxHeading: {
    flex: 1,
    color: '#595c68',
    fontWeight: '700',
    fontSize: 22,
  },
  nuxHeadingNarrow: {
    fontSize: 18,
    marginTop: 2,
  },
  nuxTooltip: {
    color: '#595c68',
    marginRight: 16,
    marginVertical: 4,
    fontSize: 16,
  },
  nuxButton: {
    alignItems: 'center',
    marginVertical: 12,
    paddingVertical: 10,
    backgroundColor: '#056ecf',
    borderRadius: 3,
  },
  nuxButtonLabel: {
    color: '#ffffff',
    fontSize: 16,
  },
});
<commit_msg>Prevent menu text from colliding with close button, readjust colors to work with dark backgrounds<commit_after>/**
 * Copyright 2015-present 650 Industries. All rights reserved.
 *
 * @providesModule MenuView
 */
'use strict';

import React, { PropTypes } from 'react';
import {
  Animated,
  Dimensions,
  Easing,
  Image,
  NativeModules,
  PixelRatio,
  ScrollView,
  StatusBar,
  StyleSheet,
  Text,
  TouchableHighlight,
  TouchableOpacity,
  View,
} from 'react-native';

let { ExponentKernel } = NativeModules;

import Expo, { Constants } from 'expo';
import { Ionicons } from '@expo/vector-icons';
import ResponsiveImage from '@expo/react-native-responsive-image';

import Browser from 'Browser';
import BrowserActions from 'BrowserActions';
import Colors from '../Home/constants/Colors';
import DevIndicator from '../Home/components/DevIndicator';
import ExStore from 'ExStore';
import FriendlyUrls from 'FriendlyUrls';

let SCREEN_WIDTH = Dimensions.get('window').width;
let MENU_NARROW_SCREEN = SCREEN_WIDTH < 375;

const AnimatedBlurView = Animated.createAnimatedComponent(Expo.BlurView);

export default class MenuView extends React.Component {
  static propTypes = {
    task: PropTypes.object.isRequired,
    shouldFadeIn: PropTypes.bool,
    isNuxFinished: PropTypes.bool,
  };

  constructor(props, context) {
    super(props, context);

    this.state = {
      transitionIn: new Animated.Value(props.shouldFadeIn ? 0 : 1),
      enableDevMenuTools: false,
      devMenuItems: {},
    };
  }

  async componentDidMount() {
    this._mounted = true;
    if (this.props.shouldFadeIn) {
      requestAnimationFrame(() => {
        this._mounted &&
          Animated.timing(this.state.transitionIn, {
            easing: Easing.inOut(Easing.quad),
            toValue: 1,
            duration: 100,
          }).start();
      });
    }
    this.forceStatusBarUpdateAsync();
    let enableDevMenuTools = await ExponentKernel.doesCurrentTaskEnableDevtools();
    let devMenuItems = await ExponentKernel.getDevMenuItemsToShow();
    if (this._mounted) {
      this.setState({ enableDevMenuTools, devMenuItems });
    }
  }

  componentWillUnmount() {
    this.restoreStatusBar();
    this._mounted = false;
  }

  forceStatusBarUpdateAsync = async () => {
    if (NativeModules.StatusBarManager._captureProperties) {
      this._statusBarValuesToRestore = await NativeModules.StatusBarManager._captureProperties();
      // HACK: StatusBar only updates changed props.
      // because MenuView typically lives under a different RN bridge, its stack of StatusBar
      // props does not necessarily reflect what the user is seeing.
      // so we force StatusBar to clear its state and update all props when we mount.
      StatusBar._currentValues = null;
    }
  };
  restoreStatusBar = () => {
    if (
      NativeModules.StatusBarManager._applyPropertiesAndForget &&
      this._statusBarValuesToRestore
    ) {
      NativeModules.StatusBarManager._applyPropertiesAndForget(
        this._statusBarValuesToRestore
      );
    }
  };
  render() {
    let scale = this.state.transitionIn.interpolate({
      inputRange: [0, 1],
      outputRange: [1.05, 1],
    });
    let intensity = this.state.transitionIn.interpolate({
      inputRange: [0, 1],
      outputRange: [0, 85],
    });

    return (
      <AnimatedBlurView
        style={styles.container}
        tint="light"
        intensity={intensity}>
        <StatusBar barStyle="default" />
        <Animated.View
          style={[
            styles.overlay,
            { opacity: this.state.transitionIn, transform: [{ scale }] },
          ]}>
          <ScrollView>
            {this.props.isNuxFinished
              ? this._renderTaskInfoRow()
              : this._renderNUXRow()}
            <View style={styles.separator} />
            <View style={styles.buttonContainer}>
              {this._renderButton(
                'refresh',
                'Refresh',
                Browser.refresh,
                require('../Assets/ios-menu-refresh.png')
              )}
              {this._renderButton(
                'home',
                'Go to Expo Home',
                this._goToHome,
                require('../Assets/ios-menu-home.png')
              )}
            </View>
            {this._maybeRenderDevMenuTools()}
            <TouchableHighlight
              style={styles.closeButton}
              onPress={this._onPressClose}
              underlayColor="#eee"
              hitSlop={{ top: 4, bottom: 8, left: 8, right: 4 }}>
              <Ionicons
                name="md-close"
                size={20}
                style={styles.closeButtonIcon}
              />
            </TouchableHighlight>
          </ScrollView>
        </Animated.View>
      </AnimatedBlurView>
    );
  }

  _renderNUXRow() {
    let tooltipMessage;
    if (Expo.Constants.isDevice) {
      tooltipMessage = 'Shake your device to show this menu.';
    } else {
      tooltipMessage = 'In iPhone Simulator, press Cmd+D to show this menu.';
    }
    let headingStyles = MENU_NARROW_SCREEN
      ? [styles.nuxHeading, styles.nuxHeadingNarrow]
      : styles.nuxHeading;
    return (
      <View style={styles.nuxRow}>
        <View style={styles.nuxHeadingRow}>
          <ResponsiveImage
            sources={{
              2: {
                uri: 'https://s3.amazonaws.com/exp-us-standard/exponent-icon@2x.png',
              },
              3: {
                uri: 'https://s3.amazonaws.com/exp-us-standard/exponent-icon@3x.png',
              },
            }}
            style={styles.nuxLogo}
          />
          <Text style={headingStyles}>
            Welcome to Expo!
          </Text>
        </View>
        <Text style={styles.nuxTooltip}>
          {tooltipMessage}
        </Text>
        <TouchableOpacity
          style={styles.nuxButton}
          onPress={this._onPressFinishNux}>
          <Text style={styles.nuxButtonLabel}>
            Got it
          </Text>
        </TouchableOpacity>
      </View>
    );
  }

  _renderTaskInfoRow() {
    let { task } = this.props;
    let taskUrl;
    if (task.loadingError) {
      taskUrl = task.loadingError.originalUrl;
    } else {
      taskUrl = task.manifestUrl
        ? FriendlyUrls.toFriendlyString(task.manifestUrl)
        : '';
    }

    let iconUrl = task.manifest && task.manifest.get('iconUrl');
    let taskName = task.manifest && task.manifest.get('name');

    let icon = iconUrl
      ? <Image source={{ uri: iconUrl }} style={styles.taskIcon} />
      : <ResponsiveImage
          resizeMode="contain"
          sources={{
            2: {
              uri: 'https://s3.amazonaws.com/exp-us-standard/exponent-icon@2x.png',
            },
            3: {
              uri: 'https://s3.amazonaws.com/exp-us-standard/exponent-icon@3x.png',
            },
          }}
          style={styles.taskIcon}
        />;
    let taskNameStyles = taskName
      ? styles.taskName
      : [styles.taskName, { color: '#c5c6c7' }];
    return (
      <View style={styles.taskMetaRow}>
        <View style={styles.taskIconColumn}>
          {icon}
        </View>
        <View style={styles.taskInfoColumn}>
          <Text style={taskNameStyles} numberOfLines={1}>
            {taskName ? taskName : 'Untitled Experience'}
          </Text>
          <Text style={[styles.taskUrl]} numberOfLines={1}>
            {taskUrl}
          </Text>
          {this._maybeRenderDevServerName()}
        </View>
      </View>
    );
  }

  _maybeRenderDevServerName() {
    let { task } = this.props;
    let devServerName =
      task.manifest && task.manifest.getIn(['developer', 'tool']);
    if (devServerName) {
      // XDE is upper
      if (devServerName === 'xde') {
        devServerName = devServerName.toUpperCase();
      }
      return (
        <View style={{ flexDirection: 'row' }}>
          <DevIndicator style={{ marginTop: 4.5, marginRight: 7 }} />
          <Text style={styles.taskDevServerName}>{devServerName}</Text>
        </View>
      );
    }
    return null;
  }

  _maybeRenderDevMenuTools() {
    if (this.state.enableDevMenuTools && this.state.devMenuItems) {
      return (
        <View>
          <View style={styles.separator} />
          <View style={styles.buttonContainer}>
            {Object.keys(this.state.devMenuItems).map((key, idx) => {
              return this._renderDevMenuItem(key, this.state.devMenuItems[key]);
            })}
          </View>
        </View>
      );
    }
    return null;
  }

  _renderDevMenuItem(key, item) {
    let { label, isEnabled } = item;
    if (isEnabled) {
      return this._renderButton(
        key,
        label,
        () => {
          this._onPressDevMenuButton(key);
        },
        null,
        true
      );
    } else {
      return (
        <View style={[styles.button, styles.buttonWithSeparator]} key={key}>
          <View style={styles.buttonIcon} />
          <Text style={[styles.buttonText, { color: '#9ca0a6' }]}>
            {label}
          </Text>
        </View>
      );
    }
  }

  _renderButton(key, text, onPress, iconSource, withSeparator) {
    let icon, buttonStyles;
    if (iconSource) {
      icon = <Image style={styles.buttonIcon} source={iconSource} />;
    } else {
      icon = <View style={styles.buttonIcon} />;
    }
    if (withSeparator) {
      buttonStyles = [styles.button, styles.buttonWithSeparator];
    } else {
      buttonStyles = styles.button;
    }
    return (
      <TouchableOpacity key={key} style={buttonStyles} onPress={onPress}>
        {icon}
        <Text style={styles.buttonText}>
          {text}
        </Text>
      </TouchableOpacity>
    );
  }

  _onPressFinishNux = () => {
    ExStore.dispatch(BrowserActions.setIsNuxFinishedAsync(true));
    ExStore.dispatch(BrowserActions.showMenuAsync(false));
  };
  _onPressClose = () => {
    if (this.props.isNuxFinished) {
      ExStore.dispatch(BrowserActions.showMenuAsync(false));
    }
  };
  _goToHome = () => {
    ExStore.dispatch(BrowserActions.foregroundHomeAsync());
  };
  _onPressDevMenuButton = key => {
    ExponentKernel.selectDevMenuItemWithKey(key);
    ExStore.dispatch(BrowserActions.showMenuAsync(false));
  };
}

let styles = StyleSheet.create({
  container: {
    position: 'absolute',
    left: 0,
    top: 0,
    bottom: 0,
    right: 0,
    justifyContent: 'center',
    flexDirection: 'row',
  },
  overlay: {
    flex: 1,
    backgroundColor: 'transparent',
    marginTop: Constants.statusBarHeight,
  },
  closeButtonIcon: {
    color: '#49a7e8',
  },
  closeButton: {
    position: 'absolute',
    right: 10,
    top: 10,
    paddingVertical: 2,
    paddingHorizontal: 6,
    borderRadius: 2,
  },
  taskMetaRow: {
    flexDirection: 'row',
    paddingHorizontal: 14,
    paddingBottom: 12,
  },
  taskInfoColumn: {
    flex: 4,
    justifyContent: 'center',
  },
  taskIconColumn: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  taskName: {
    color: '#595c68',
    backgroundColor: 'transparent',
    fontWeight: '700',
    fontSize: 16,
    marginTop: 14,
    marginBottom: 1,
    marginRight: 24,
  },
  taskUrl: {
    color: '#9ca0a6',
    backgroundColor: 'transparent',
    marginRight: 16,
    marginBottom: 2,
    marginTop: 1,
    fontSize: 12,
  },
  taskIcon: {
    width: 52,
    height: 52,
    marginTop: 12,
    marginRight: 10,
    alignSelf: 'center',
    backgroundColor: 'transparent',
  },
  taskDevServerName: {
    fontSize: 12,
    color: '#9ca0a6',
    fontWeight: '700',
  },
  separator: {
    borderColor: '#d5d6d7',
    borderTopWidth: 1 / PixelRatio.get(),
    backgroundColor: '#f0f0f1',
    height: 12,
    marginVertical: 4,
    marginHorizontal: -1,
  },
  buttonContainer: {
    backgroundColor: 'transparent',
  },
  button: {
    backgroundColor: 'transparent',
    flexDirection: 'row',
  },
  buttonWithSeparator: {
    borderBottomWidth: StyleSheet.hairlineWidth * 2,
    borderBottomColor: '#f4f4f5',
  },
  buttonIcon: {
    width: 16,
    height: 16,
    marginVertical: 12,
    marginLeft: 20,
    alignSelf: 'flex-start',
  },
  buttonText: {
    color: '#595c68',
    fontSize: 14,
    textAlign: 'left',
    marginVertical: 12,
    marginRight: 5,
    paddingHorizontal: 12,
    fontWeight: '700',
  },
  nuxRow: {
    paddingHorizontal: 12,
  },
  nuxHeadingRow: {
    flexDirection: 'row',
    marginTop: 16,
    marginRight: 16,
    marginBottom: 8,
  },
  nuxLogo: {
    width: 47 * 0.7,
    height: 40 * 0.7,
    marginRight: 12,
    marginLeft: 8,
    alignSelf: 'flex-start',
  },
  nuxHeading: {
    flex: 1,
    color: '#595c68',
    fontWeight: '700',
    fontSize: 22,
  },
  nuxHeadingNarrow: {
    fontSize: 18,
    marginTop: 2,
  },
  nuxTooltip: {
    color: '#595c68',
    marginRight: 16,
    marginVertical: 4,
    fontSize: 16,
  },
  nuxButton: {
    alignItems: 'center',
    marginVertical: 12,
    paddingVertical: 10,
    backgroundColor: '#056ecf',
    borderRadius: 3,
  },
  nuxButtonLabel: {
    color: '#ffffff',
    fontSize: 16,
  },
});
<|endoftext|>


<commit_before>const fs = require('fs');
const {
	HTTP_CODES,
	getFileInfo
} = require('../index');
const {
	createPoolOf
} = require('./pool');
const FilePump = require('./FilePump');

/**
 * Enable caching of file stat and file descriptors.
 */
module.exports = getFileCacheSettings;

/**
 * @private
 */
const cacheOfFiles = Object.create(null);
const poolOfPumps = createPoolOf(FilePump);

/**
 * Return settings needed for using fs cache with serve-files.
 *
 * @return {object}
 */
function getFileCacheSettings () {
	return {
		pumps           : poolOfPumps,
		getFileInfo     : getCachedFileInfo,
		getFileStream   : getCachedFilePump,
		sendResponseData: pumpResponseData
	};
}

/**
 * Update stats value and call back all listeners awaiting for the change.
 * @param {object}            cachedFile
 * @param {external:fs.Stats} fileStats
 */
function cachedFileSetStats (cachedFile, fileStats) {
	var list = cachedFile.await;
	var len = (list && list.length) || 0;

	cachedFile.stats = fileStats;
	cachedFile.await = [];

	if (len < 1) {
		return;
	}

	for (var i = 0; i < len; i++) {
		list[i](fileStats);
	}
}

function getCachedFileInfo (cfg, req, res, filePath, callback) {
	var cachedFile = cacheOfFiles[filePath] || {
		stats: null,
		fd   : null,
		until: Infinity,
		await: [],
		pipes: 0,
		acquire: function () {
			this.pipes += 1;
			return this;
		},
		release: function () {
			this.pipes -= 1;
			if (this.pipes < 1) {
				this.stats = null;
				fs.close(this.fd, err => err && console.error(err));
				this.fd = null;
			}
			return this;
		}
	};

	// This is for testing only. "Real" cache should have timeout and/or be updated on file changes.
	// The one here can be DDoS-ed simply by requesting hundreds of non-existing files per second
	// (which will consume RAM until there's no more left to use).
	if (cachedFile.stats && cachedFile.until > Date.now()) {
		callback(cfg, req, res, filePath, cachedFile.stats);
		return;
	}

	if (cachedFile.fd !== null) {
		// TODO: ref count pumps and close FD after exiting pumps are done.
		// fs.close(cachedFile.fd, err => err && console.error(err));
		// cachedFile.fd = null;
	}

	// Reset stats, or some other request may see them as ready and use old data
	// TODO: recreate cachedFile object instead, or some existing task may break if it tries to read
	//       after we nullify this and before we get new stats.
	cachedFile.stats = null;

	cacheOfFiles[filePath] = cachedFile;

	if (cachedFile.await.push(fileStats => callback(cfg, req, res, filePath, fileStats)) > 1) {
		return;
	}

	fs.open(filePath, 'r', function (err, fd) {
		// `cachedFile.stats` are nullified before we get here, so it is safe to update `until` before
		// we even get the new stats.
		cachedFile.until = cfg.cacheTimeInSeconds === 0 ? 0 : Date.now() + (cfg.cacheTimeInSeconds * 1000);

		if (err) {
			// Enforce cache timeout on errors, to prevent keeping them forever.
			cachedFile.until = Math.min(cachedFile.until, 10000);
			cachedFileSetStats(cachedFile, err);
			return;
		}

		cachedFile.fd = fd;

		getFileInfo(cfg, req, res, filePath, function (cfg, req, res, filePath, fileStats) {
			if (fileStats instanceof Error) {
				cachedFile.fd = null;
				fs.close(fd);
				fd = null;
			}

			cachedFileSetStats(cachedFile, fileStats);
		});
	});
}

function getCachedFilePump (cfg, req, res, filePath, fileStats, options) {
	const cachedFile = cacheOfFiles[filePath];

	if (!cachedFile) {
		res.statusCode = HTTP_CODES.SERVER_ERROR;
		return null;
	}

	var pump = cfg.pumps.get();
	pump.prepare(cachedFile, options && options.start, options && options.end);

	return pump;
}

function pumpResponseData (cfg, req, res, filePath, fileStats, data) {
	if (req.method === 'HEAD' || !data) {
		res.end();
		return;
	}

	if (typeof data.pipe !== 'function') {
		res.end(data);
		return;
	}

	data.pipe(res);
}
<commit_msg>fix: `callback` is a required parameter for `fs.close()`<commit_after>const fs = require('fs');
const {
	HTTP_CODES,
	getFileInfo
} = require('../index');
const {
	createPoolOf
} = require('./pool');
const FilePump = require('./FilePump');

/**
 * Enable caching of file stat and file descriptors.
 */
module.exports = getFileCacheSettings;

/**
 * @private
 */
const cacheOfFiles = Object.create(null);
const poolOfPumps = createPoolOf(FilePump);

/**
 * Return settings needed for using fs cache with serve-files.
 *
 * @return {object}
 */
function getFileCacheSettings () {
	return {
		pumps           : poolOfPumps,
		getFileInfo     : getCachedFileInfo,
		getFileStream   : getCachedFilePump,
		sendResponseData: pumpResponseData
	};
}

/**
 * Noop.
 */
function noop () {
	// Function intentionally left empty.
}

/**
 * Update stats value and call back all listeners awaiting for the change.
 * @param {object}            cachedFile
 * @param {external:fs.Stats} fileStats
 */
function cachedFileSetStats (cachedFile, fileStats) {
	var list = cachedFile.await;
	var len = (list && list.length) || 0;

	cachedFile.stats = fileStats;
	cachedFile.await = [];

	if (len < 1) {
		return;
	}

	for (var i = 0; i < len; i++) {
		list[i](fileStats);
	}
}

function getCachedFileInfo (cfg, req, res, filePath, callback) {
	var cachedFile = cacheOfFiles[filePath] || {
		stats: null,
		fd   : null,
		until: Infinity,
		await: [],
		pipes: 0,
		acquire: function () {
			this.pipes += 1;
			return this;
		},
		release: function () {
			this.pipes -= 1;
			if (this.pipes < 1) {
				this.stats = null;
				fs.close(this.fd, err => err && console.error(err));
				this.fd = null;
			}
			return this;
		}
	};

	// This is for testing only. "Real" cache should have timeout and/or be updated on file changes.
	// The one here can be DDoS-ed simply by requesting hundreds of non-existing files per second
	// (which will consume RAM until there's no more left to use).
	if (cachedFile.stats && cachedFile.until > Date.now()) {
		callback(cfg, req, res, filePath, cachedFile.stats);
		return;
	}

	if (cachedFile.fd !== null) {
		// TODO: ref count pumps and close FD after exiting pumps are done.
		// fs.close(cachedFile.fd, err => err && console.error(err));
		// cachedFile.fd = null;
	}

	// Reset stats, or some other request may see them as ready and use old data
	// TODO: recreate cachedFile object instead, or some existing task may break if it tries to read
	//       after we nullify this and before we get new stats.
	cachedFile.stats = null;

	cacheOfFiles[filePath] = cachedFile;

	if (cachedFile.await.push(fileStats => callback(cfg, req, res, filePath, fileStats)) > 1) {
		return;
	}

	fs.open(filePath, 'r', function (err, fd) {
		// `cachedFile.stats` are nullified before we get here, so it is safe to update `until` before
		// we even get the new stats.
		cachedFile.until = cfg.cacheTimeInSeconds === 0 ? 0 : Date.now() + (cfg.cacheTimeInSeconds * 1000);

		if (err) {
			// Enforce cache timeout on errors, to prevent keeping them forever.
			cachedFile.until = Math.min(cachedFile.until, 10000);
			cachedFileSetStats(cachedFile, err);
			return;
		}

		cachedFile.fd = fd;

		getFileInfo(cfg, req, res, filePath, function (cfg, req, res, filePath, fileStats) {
			if (fileStats instanceof Error) {
				cachedFile.fd = null;
				fs.close(fd, noop);
				fd = null;
			}

			cachedFileSetStats(cachedFile, fileStats);
		});
	});
}

function getCachedFilePump (cfg, req, res, filePath, fileStats, options) {
	const cachedFile = cacheOfFiles[filePath];

	if (!cachedFile) {
		res.statusCode = HTTP_CODES.SERVER_ERROR;
		return null;
	}

	var pump = cfg.pumps.get();
	pump.prepare(cachedFile, options && options.start, options && options.end);

	return pump;
}

function pumpResponseData (cfg, req, res, filePath, fileStats, data) {
	if (req.method === 'HEAD' || !data) {
		res.end();
		return;
	}

	if (typeof data.pipe !== 'function') {
		res.end(data);
		return;
	}

	data.pipe(res);
}
<|endoftext|>


<commit_before>var db = require('../../models');

module.exports.addMeme = function(req, res) {
    //TODO create meme
    // res.sendStatus(503)
    db.meme_texts.create({text:req.body.top}).then(function(top_text) {
        db.meme_texts.create({text:req.body.bottom}).then(function(bottom_text) {
            db.images.create({filename:req.body.image}).then(function(images) {
                db.memes.create({image_id:images.id,top_text_id:top_text.id,bottom_text_id:bottom_text.id}).then(function(memes) {
                    memes.setImages(images);
                    memes.setTopText(top_text);
                    memes.setBottomText(bottom_text);
                    res.json({memes: memes});
                }).catch(function(err) {
                    console.log("meme fail");
                    res.status(400).send(err);
                });
            }).catch(function(err) {
                console.log("image fail");
                res.status(400).send(err);
            });
        }).catch(function(err) {
            console.log("bottom text fail");
            res.status(400).send(err);
        });
    }).catch(function(err) {
        console.log("top text fail");
        res.status(400).send(err);
    });

};

module.exports.getAllMemes = function(req, res) {
    db.memes.findAll().then(function(memes) {
        res.json({memes: memes});
    }).catch(function(err) {
        res.status(400).send(err);
    });
};
<commit_msg>remove image save from memes create<commit_after>var db = require('../../models');

module.exports.addMeme = function(req, res) {
    //TODO create meme
    // res.sendStatus(503)
    db.meme_texts.create({text:req.body.top}).then(function(top_text) {
        db.meme_texts.create({text:req.body.bottom}).then(function(bottom_text) {
                db.memes.create().then(function(memes) {

                    // the image here should be the id of an already existing/ newly uploaded image,
                    // i.e. this image id should already be saved in db
                    memes.setImages(req.body.image);
                    memes.setTopText(top_text);
                    memes.setBottomText(bottom_text);
                    res.json({memes: memes});
                }).catch(function(err) {
                    console.log("meme fail");
                    res.status(400).send(err);
                });
        }).catch(function(err) {
            console.log("bottom text fail");
            res.status(400).send(err);
        });
    }).catch(function(err) {
        console.log("top text fail");
        res.status(400).send(err);
    });

};

module.exports.getAllMemes = function(req, res) {
    db.memes.findAll().then(function(memes) {
        res.json({memes: memes});
    }).catch(function(err) {
        res.status(400).send(err);
    });
};
<|endoftext|>


<commit_before>import md5 from 'md5';
import { Base64 } from 'js-base64';

import {
  VERSION,
  DOMAIN_REGEX,
  DEFAULT_OPTIONS,
  DPR_QUALITIES
} from './constants';

import {
  validateRange,
  validateWidths,
  validateAndDestructureOptions,
  validateVariableQuality,
  validateWidthTolerance
} from './validators';

export default class ImgixClient {
  constructor(opts = {}) {
    this.settings = { ...DEFAULT_OPTIONS, ...opts };
    // a cache to store memoized srcset width-pairs
    this.targetWidthsCache = {};
    if (typeof this.settings.domain != "string") {
      throw new Error('ImgixClient must be passed a valid string domain');
    }

    if (DOMAIN_REGEX.exec(this.settings.domain) == null) {
      throw new Error(
        'Domain must be passed in as fully-qualified ' +
        'domain name and should not include a protocol or any path ' +
        'element, i.e. "example.imgix.net".');
    }

    if (this.settings.includeLibraryParam) {
      this.settings.libraryParam = "js-" + VERSION;
    }

    this.settings.urlPrefix = this.settings.useHTTPS ? 'https://' : 'http://';
  }

  buildURL(path = '', params = {}) {
    const sanitizedPath = this._sanitizePath(path);

    let finalParams = this._buildParams(params);
    if (!!this.settings.secureURLToken) {
      finalParams = this._signParams(sanitizedPath, finalParams);
    }
    return this.settings.urlPrefix + this.settings.domain + sanitizedPath + finalParams;
  }

  _buildParams(params = {}) {
    const queryParams = [
      // Set the libraryParam if applicable.
      ...(this.settings.libraryParam ? [`ixlib=${this.settings.libraryParam}`] : []),

      // Map over the key-value pairs in params while applying applicable encoding.
      ...(Object.entries(params).map(([key, value]) => {
        const encodedKey = encodeURIComponent(key);
        const encodedValue = key.substr(-2) === '64' ? Base64.encodeURI(value) : encodeURIComponent(value);
        return `${encodedKey}=${encodedValue}`;
      })),
    ];

    return `${queryParams.length > 0 ? '?' : ''}${queryParams.join('&')}`;
  }

  _signParams(path, queryParams) {
    const signatureBase = this.settings.secureURLToken + path + queryParams;
    const signature = md5(signatureBase);

    return queryParams.length > 0 ? queryParams + "&s=" + signature : "?s=" + signature;
  }

  _sanitizePath(path) {
    // Strip leading slash first (we'll re-add after encoding)
    let _path = path.replace(/^\//, '');

    if (/^https?:\/\//.test(_path)) {
      // Use de/encodeURIComponent to ensure *all* characters are handled,
      // since it's being used as a path
      _path = encodeURIComponent(_path);
    } else {
      // Use de/encodeURI if we think the path is just a path,
      // so it leaves legal characters like '/' and '@' alone
      _path = encodeURI(_path).replace(/[#?:]/g, encodeURIComponent);
    }

    return '/' + _path;
  }

  buildSrcSet(path, params = {}, options = {}) {
    const {w, h, ar} = params;

    if ((w) || (h && ar)) {
      return this._buildDPRSrcSet(path, params, options);
    } else {
      return this._buildSrcSetPairs(path, params, options);
    }
  }

  _buildSrcSetPairs(path, params, options) {
    const [widthTolerance, minWidth, maxWidth]  = validateAndDestructureOptions(options);
    const customWidths = options.widths;

    let targetWidths;
    if (customWidths) {
      validateWidths(customWidths);
      targetWidths = customWidths;
    } else {
      validateRange(minWidth, maxWidth);
      validateWidthTolerance(widthTolerance);
      targetWidths = this._generateTargetWidths(widthTolerance, minWidth, maxWidth);
    }

    const srcset = targetWidths.map(w => `${this.buildURL(path, { ...params, w })} ${w}w`);

    return srcset.join(',\n')
  };

  _buildDPRSrcSet(path, params, options) {
    const targetRatios = [1, 2, 3, 4, 5];
    const disableVariableQuality = options.disableVariableQuality || false;

    if (!disableVariableQuality) {
      validateVariableQuality(disableVariableQuality);
    }

    const withQuality = (path, params, dpr) => {
      return `${this.buildURL(
        path, { ...params, dpr: dpr, q: params.q || DPR_QUALITIES[dpr] })} ${dpr}x`;
    };

    const srcset = disableVariableQuality
      ? targetRatios.map(dpr => `${this.buildURL(path, { ...params, dpr })} ${dpr}x`)
      : targetRatios.map(dpr => withQuality(path, params, dpr));

    return srcset.join(',\n');
  };

  // returns an array of width values used during scrset generation
  _generateTargetWidths(widthTolerance, minWidth, maxWidth) {
    const resolutions = [];
    const INCREMENT_PERCENTAGE = widthTolerance;
    const _minWidth = Math.floor(minWidth);
    const _maxWidth = Math.floor(maxWidth);
    const cacheKey = INCREMENT_PERCENTAGE + '/' + _minWidth + '/' + _maxWidth;

    if (cacheKey in this.targetWidthsCache) {
      return this.targetWidthsCache[cacheKey];
    }

    const ensureEven = (n) => {
      return 2 * Math.round(n / 2);
    };

    let prev = _minWidth;
    while (prev < _maxWidth) {
      resolutions.push(ensureEven(prev));
      prev *= 1 + (INCREMENT_PERCENTAGE * 2);
    }

    resolutions.push(_maxWidth);

    this.targetWidthsCache[cacheKey] = resolutions;

    return resolutions;
  };
}
<commit_msg>refactor: avoid temp variable, spread instead<commit_after>import md5 from 'md5';
import { Base64 } from 'js-base64';

import {
  VERSION,
  DOMAIN_REGEX,
  DEFAULT_OPTIONS,
  DPR_QUALITIES
} from './constants';

import {
  validateRange,
  validateWidths,
  validateAndDestructureOptions,
  validateVariableQuality,
  validateWidthTolerance
} from './validators';

export default class ImgixClient {
  constructor(opts = {}) {
    this.settings = { ...DEFAULT_OPTIONS, ...opts };
    // a cache to store memoized srcset width-pairs
    this.targetWidthsCache = {};
    if (typeof this.settings.domain != "string") {
      throw new Error('ImgixClient must be passed a valid string domain');
    }

    if (DOMAIN_REGEX.exec(this.settings.domain) == null) {
      throw new Error(
        'Domain must be passed in as fully-qualified ' +
        'domain name and should not include a protocol or any path ' +
        'element, i.e. "example.imgix.net".');
    }

    if (this.settings.includeLibraryParam) {
      this.settings.libraryParam = "js-" + VERSION;
    }

    this.settings.urlPrefix = this.settings.useHTTPS ? 'https://' : 'http://';
  }

  buildURL(path = '', params = {}) {
    const sanitizedPath = this._sanitizePath(path);

    let finalParams = this._buildParams(params);
    if (!!this.settings.secureURLToken) {
      finalParams = this._signParams(sanitizedPath, finalParams);
    }
    return this.settings.urlPrefix + this.settings.domain + sanitizedPath + finalParams;
  }

  _buildParams(params = {}) {
    const queryParams = [
      // Set the libraryParam if applicable.
      ...(this.settings.libraryParam ? [`ixlib=${this.settings.libraryParam}`] : []),

      // Map over the key-value pairs in params while applying applicable encoding.
      ...(Object.entries(params).map(([key, value]) => {
        const encodedKey = encodeURIComponent(key);
        const encodedValue = key.substr(-2) === '64' ? Base64.encodeURI(value) : encodeURIComponent(value);
        return `${encodedKey}=${encodedValue}`;
      })),
    ];

    return `${queryParams.length > 0 ? '?' : ''}${queryParams.join('&')}`;
  }

  _signParams(path, queryParams) {
    const signatureBase = this.settings.secureURLToken + path + queryParams;
    const signature = md5(signatureBase);

    return queryParams.length > 0 ? queryParams + "&s=" + signature : "?s=" + signature;
  }

  _sanitizePath(path) {
    // Strip leading slash first (we'll re-add after encoding)
    let _path = path.replace(/^\//, '');

    if (/^https?:\/\//.test(_path)) {
      // Use de/encodeURIComponent to ensure *all* characters are handled,
      // since it's being used as a path
      _path = encodeURIComponent(_path);
    } else {
      // Use de/encodeURI if we think the path is just a path,
      // so it leaves legal characters like '/' and '@' alone
      _path = encodeURI(_path).replace(/[#?:]/g, encodeURIComponent);
    }

    return '/' + _path;
  }

  buildSrcSet(path, params = {}, options = {}) {
    const {w, h, ar} = params;

    if ((w) || (h && ar)) {
      return this._buildDPRSrcSet(path, params, options);
    } else {
      return this._buildSrcSetPairs(path, params, options);
    }
  }

  _buildSrcSetPairs(path, params, options) {
    const [widthTolerance, minWidth, maxWidth]  = validateAndDestructureOptions(options);

    let targetWidths;
    if (options.widths) {
      validateWidths(options.widths);
      targetWidths = [...options.widths];
    } else {
      validateRange(minWidth, maxWidth);
      validateWidthTolerance(widthTolerance);
      targetWidths = this._generateTargetWidths(widthTolerance, minWidth, maxWidth);
    }

    const srcset = targetWidths.map(w => `${this.buildURL(path, { ...params, w })} ${w}w`);

    return srcset.join(',\n')
  };

  _buildDPRSrcSet(path, params, options) {
    const targetRatios = [1, 2, 3, 4, 5];
    const disableVariableQuality = options.disableVariableQuality || false;

    if (!disableVariableQuality) {
      validateVariableQuality(disableVariableQuality);
    }

    const withQuality = (path, params, dpr) => {
      return `${this.buildURL(
        path, { ...params, dpr: dpr, q: params.q || DPR_QUALITIES[dpr] })} ${dpr}x`;
    };

    const srcset = disableVariableQuality
      ? targetRatios.map(dpr => `${this.buildURL(path, { ...params, dpr })} ${dpr}x`)
      : targetRatios.map(dpr => withQuality(path, params, dpr));

    return srcset.join(',\n');
  };

  // returns an array of width values used during scrset generation
  _generateTargetWidths(widthTolerance, minWidth, maxWidth) {
    const resolutions = [];
    const INCREMENT_PERCENTAGE = widthTolerance;
    const _minWidth = Math.floor(minWidth);
    const _maxWidth = Math.floor(maxWidth);
    const cacheKey = INCREMENT_PERCENTAGE + '/' + _minWidth + '/' + _maxWidth;

    if (cacheKey in this.targetWidthsCache) {
      return this.targetWidthsCache[cacheKey];
    }

    const ensureEven = (n) => {
      return 2 * Math.round(n / 2);
    };

    let prev = _minWidth;
    while (prev < _maxWidth) {
      resolutions.push(ensureEven(prev));
      prev *= 1 + (INCREMENT_PERCENTAGE * 2);
    }

    resolutions.push(_maxWidth);

    this.targetWidthsCache[cacheKey] = resolutions;

    return resolutions;
  };
}
<|endoftext|>


<commit_before>// Copyright 2002-2013, University of Colorado Boulder

/**
 * MultiLine plain text, with alignment.
 * The line break character is '\n'.
 * Specify alignment via the 'align' option.
 * Text node options can be specified to style the text.
 *

 * Example: new MultiLineText( 'Hello\nWorld', { align: 'left', font: new PhetFont(20), fill: 'red' } );
 *
 * @author Sam Reid
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var Text = require( 'SCENERY/nodes/Text' );
  var VBox = require( 'SCENERY/nodes/VBox' );

  /**
   *
   * @param {string} text
   * @param {Object} [options]
   * @constructor
   */
  function MultiLineText( text, options ) {

    options = _.extend( {
      font: new PhetFont(),
      align: 'center' // 'center', 'left' or 'right' (as supported by VBox)
    }, options );
    this.options = options; // @private

    Node.call( this );

    this._text = null; // @private underscore prefix because it has ES5 set/get
    this.textParent = null; // @private
    this.text = text; // call ES5 setter

    this.mutate( _.omit( options, 'align' ) ); // mutate after removing options that are specific to this subtype
  }

  inherit( Node, MultiLineText, {

      get text() {
        return this._text;
      },

      set text( value ) {

        // save the new text
        this._text = value;

        // parse the text and create {Text[]}
        var thisNode = this;
        var textNodes = value.split( '\n' ).map( function( line ) {
          if ( line.length === 0 ) { line = ' '; }  // creates a blank line between consecutive line breaks
          return new Text( line, _.omit( thisNode.options, 'align' ) );
        } );

        // determine where the textParent was, so we can maintain rendering order
        var index = this.textParent ? this.indexOfChild( this.textParent ) : 0;

        // remove the old textParent
        if ( this.textParent ) { this.removeChild( this.textParent ); }

        // add the new textParent
        this.textParent = new VBox( {
          children: textNodes,
          align: this.options.align
        } );
        this.insertChild( index, this.textParent );
      },

      /**
       * Sets the fill for all Text nodes.
       * @param {Color|string} fill
       */
      setFill: function( fill ) {
        this.options.fill = fill;
        var children = this.textParent.getChildren();
        for ( var i = 0; i < children.length; i++ ) {
          children[ i ].setFill( fill );
        }
      },
      set fill( value ) { this.setFill( fill ); },

      /**
       * Gets the fill used for the text.
       * @returns {Color|string}
       */
      getFill: function() { return this.options.fill; },
      get fill() { return getFill(); }
    }
  );

  return MultiLineText;
} );
<commit_msg>#162 use scenery pattern for ES5 set/get, make them called set/get functions<commit_after>// Copyright 2002-2013, University of Colorado Boulder

/**
 * MultiLine plain text, with alignment.
 * The line break character is '\n'.
 * Specify alignment via the 'align' option.
 * Text node options can be specified to style the text.
 *

 * Example: new MultiLineText( 'Hello\nWorld', { align: 'left', font: new PhetFont(20), fill: 'red' } );
 *
 * @author Sam Reid
 * @author Chris Malley (PixelZoom, Inc.)
 */
define( function( require ) {
  'use strict';

  // modules
  var inherit = require( 'PHET_CORE/inherit' );
  var Node = require( 'SCENERY/nodes/Node' );
  var PhetFont = require( 'SCENERY_PHET/PhetFont' );
  var Text = require( 'SCENERY/nodes/Text' );
  var VBox = require( 'SCENERY/nodes/VBox' );

  /**
   *
   * @param {string} text
   * @param {Object} [options]
   * @constructor
   */
  function MultiLineText( text, options ) {

    options = _.extend( {
      font: new PhetFont(),
      align: 'center' // 'center', 'left' or 'right' (as supported by VBox)
    }, options );
    this.options = options; // @private

    Node.call( this );

    this._text = null; // @private underscore prefix because it has ES5 set/get
    this.textParent = null; // @private
    this.text = text; // call ES5 setter

    this.mutate( _.omit( options, 'align' ) ); // mutate after removing options that are specific to this subtype
  }

  inherit( Node, MultiLineText, {

      /**
       * Sets the text.
       * @param {string} text
       */
      setText: function( text ) {

        // save the new text
        this._text = text;

        // parse the text and create {Text[]}
        var thisNode = this;
        var textNodes = text.split( '\n' ).map( function( line ) {
          if ( line.length === 0 ) { line = ' '; }  // creates a blank line between consecutive line breaks
          return new Text( line, _.omit( thisNode.options, 'align' ) );
        } );

        // determine where the textParent was, so we can maintain rendering order
        var index = this.textParent ? this.indexOfChild( this.textParent ) : 0;

        // remove the old textParent
        if ( this.textParent ) { this.removeChild( this.textParent ); }

        // add the new textParent
        this.textParent = new VBox( {
          children: textNodes,
          align: this.options.align
        } );
        this.insertChild( index, this.textParent );
      },
      set text( value ) { this.setText( value ); }, // ES5 setter

      /**
       * Gets the text.
       * @returns {string}
       */
      getText: function() { return this._text; },
      get text() { return this.getText(); }, // ES5 getter

      /**
       * Sets the fill for all Text nodes.
       * @param {Color|string} fill
       */
      setFill: function( fill ) {
        this.options.fill = fill;
        var children = this.textParent.getChildren();
        for ( var i = 0; i < children.length; i++ ) {
          children[ i ].setFill( fill );
        }
      },
      set fill( value ) { this.setFill( value ); }, // ES5 setter

      /**
       * Gets the fill used for the text.
       * @returns {Color|string}
       */
      getFill: function() { return this.options.fill; },
      get fill() { return getFill(); } // ES5 getter
    }
  );

  return MultiLineText;
} );
<|endoftext|>


<commit_before>
var express = require('express');
var router = express.Router();

// Users
var userController = require("../../controllers/api/UserController.js");
router.get('/users', userController.getAll);
router.post('/users', userController.signIn);
router.put('/users', userController.update);

router.route('/users/:id')
    .delete(userController.delete)
    .get(userController.get);

// Supplier
var supplierController = require("../../controllers/api/SupplierController.js");
router.get('/suppliers', supplierController.getAll);
router.post('/suppliers/signUp', supplierController.signUp);
router.post('/suppliers/signIn', supplierController.signIn);
router.put('/suppliers', supplierController.update);

router.route('/suppliers/:id')
    .delete(supplierController.delete)
    .get(supplierController.get);

module.exports = router;
<commit_msg>Router configure<commit_after>
var express = require('express');
var router = express.Router();

// Users
var userController = require("../../controllers/api/UserController.js");
router.get('/users', userController.getAll);
router.post('/users', userController.signIn);
router.put('/users', userController.update);

router.route('/users/:id')
    .delete(userController.delete)
    .get(userController.get);

// Supplier
var supplierController = require("../../controllers/api/SupplierController.js");
router.get('/suppliers', supplierController.getAll);
router.post('/suppliers/signUp', supplierController.signUp);
router.post('/suppliers/signIn', supplierController.signIn);
router.put('/suppliers', supplierController.update);

router.route('/suppliers/:id')
    .delete(supplierController.delete)
    .get(supplierController.get);

// Event
var eventController = require("../../controllers/api/EventController");
router.get('/events', eventController.getAllOfSupplier);
router.post('events', eventController.createEvent);

router.route('/events/:event_id')
      .get(eventController.getDetail);
      .put(eventController.updateEvent);
      .delete(eventController.deleteEvent);

// Tasks
var taskController = require("../../controllers/api/TaskController");
router.route('/events/:event_id/')
      .post(taskController.postTask);

router.route('/events/:event_id/:task_id')
      .get(taskController.getTask)
      .put(taskController.updateTask)
      .delete(taskController.deleteTask)

// Awards
var awardController = require("../../controllers/api/AwardController");
router.get('/awards', awardController.getAllOfSupplier);
router.post('/awards', awardController.createAward);

rotuer.route('/awards/:award_id')
      .get(awardController.getDetail)
      .put(awardController.updateAward)
      .delete(awardController.deleteAward)

// Locations
var locationController = require("../../controllers/api/LocationController");
router.get('/locations', locationController.getAllOfSupplier);
router.post('/locations', locationController.create);

rotuer.route('/locations/:location_id')
      .get(locationController.getDetail)
      .put(locationController.update)
      .delete(locationController.delete)

// Items
var itemController = require("../../controllers/api/ItemController");
router.get('/items', itemController.getAllOfSupplier);
router.post('/items', itemController.create);

rotuer.route('/locations/:item_id')
      .get(itemController.getDetail)
      .put(itemController.update)
      .delete(itemController.delete)

// Push notification
var pushNotification = require("../../controllers/PushNotificationController");
router.get('/notifications',pushNotification.getNotification);
router.post('/notifications', pushNotification.pushNotification);

module.exports = router;
<|endoftext|>


<commit_before>
/*
 * ncksllvn
 * a bunch of cool classes for Backbone that I kept finding myself using.
 * just include this file after Backbone
 */

 ~function(){

    Backbone.HistoryWith404 = (function(){

       var routers = [];

       HistoryWith404 = Backbone.History.extend({

            loadUrl: (function(loadUrl){
                return function(){
                    var match = loadUrl.apply(this, arguments),
                        fragment = this.fragment;
                    if ( !match ){
                        _.chain(routers).compact().all(function(router){
                            router && router.trigger('statusCode:404', fragment);
                        });
                    }
                    return match;
                }
            })( Backbone.History.prototype.loadUrl )

        });

       Backbone.RouterWith404 = Backbone.Router.extend({
           constructor: (function(superConstructor){
                return function(){
                    superConstructor.apply(this, arguments);
                    routers.push(this);
                };
           })(Backbone.Router)
        });

        return HistoryWith404;

    })();

    Backbone.Cacheable = Backbone.Cacheable || {};

    Backbone.Cacheable.cachedFetch = function(fetch){
        return function(options){

            var _this = this,
                options = options || {},
                callback;

            if ( !this.cache ){
                this.cache = fetch.call( this, {
                    success: function(){
                        _this.cache.outcome = 'success',
                        _this.cache.callbackArgs = arguments;
                        if (options.success) options.success.apply(window, arguments);
                    },
                    error: function(){
                        _this.cache.outcome = 'error',
                        _this.cache.callbackArgs = arguments;
                        if (options.error) options.error.apply(window, arguments);
                    }
                });
            }

            else{
                callback = options[ this.cache.outcome ];
                if ( callback ) callback.apply(window, this.cache.callbackArgs);
            }

            return this.cache;
        };
    };

    _.extend( Backbone.Cacheable, {

        Collection: Backbone.Collection.extend({
            fetch: Backbone.Cacheable.cachedFetch( Backbone.Collection.prototype.fetch )
        }),

        Model: Backbone.Model.extend({
            fetch: Backbone.Cacheable.cachedFetch( Backbone.Model.prototype.fetch )
        })

    });

}();

<commit_msg>add a hard fetch functino to the cacheables<commit_after>
/*
 * ncksllvn
 * a bunch of cool classes for Backbone that I kept finding myself using.
 * just include this file after Backbone
 */

 ~function(){

    Backbone.HistoryWith404 = (function(){

       var routers = [];

       HistoryWith404 = Backbone.History.extend({

            loadUrl: (function(loadUrl){
                return function(){
                    var match = loadUrl.apply(this, arguments);
                    var fragment = this.fragment;
                    if ( !match ){
                        _.chain(routers).compact().all(function(router){
                            router && router.trigger('statusCode:404', fragment);
                        });
                    }
                    return match;
                }
            })( Backbone.History.prototype.loadUrl )

        });

       Backbone.RouterWith404 = Backbone.Router.extend({
           constructor: (function(superConstructor){
                return function(){
                    superConstructor.apply(this, arguments);
                    routers.push(this);
                };
           })(Backbone.Router)
        });

        return HistoryWith404;

    })();

    Backbone.Cacheable = Backbone.Cacheable || {};

    Backbone.Cacheable.cachedFetch = function(fetch){
        return function(options){

            var _this = this;
            var callback;
            options = options || {};

            if ( !this.cache ){
                this.cache = fetch.call( this, {
                    success: function(){
                        _this.cache.outcome = 'success',
                        _this.cache.callbackArgs = arguments;
                        if (options.success) options.success.apply(window, arguments);
                    },
                    error: function(){
                        _this.cache.outcome = 'error',
                        _this.cache.callbackArgs = arguments;
                        if (options.error) options.error.apply(window, arguments);
                    }
                });
            }

            else{
                callback = options[ this.cache.outcome ];
                if ( callback ) callback.apply(window, this.cache.callbackArgs);
            }

            return this.cache;
        };
    };

    function hardFetch(){
        delete this.cache;
        return this.fetch();
    }

    _.extend( Backbone.Cacheable, {

        Collection: Backbone.Collection.extend({
            fetch: Backbone.Cacheable.cachedFetch( Backbone.Collection.prototype.fetch ),
            hardFetch: hardFetch
        }),

        Model: Backbone.Model.extend({
            fetch: Backbone.Cacheable.cachedFetch( Backbone.Model.prototype.fetch ),
            hardFetch: hardFetch
        })

    });

}();

<|endoftext|>


<commit_before>define( function( require ) {
  "use strict";

  var Image = require( 'SCENERY/nodes/Image' );
  var VBox = require( 'SCENERY/nodes/VBox' );
  var DOM = require( 'SCENERY/nodes/DOM' );
  var Node = require( 'SCENERY/nodes/Node' );
  var HBox = require( 'SCENERY/nodes/HBox' );
  var Scene = require( 'SCENERY/Scene' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Path = require( 'SCENERY/nodes/Path' );
  var Shape = require( 'KITE/Shape' );
  var SimpleDragHandler = require( 'SCENERY/input/SimpleDragHandler' );
  var Bounds2 = require( 'DOT/Bounds2' );
  var Inheritance = require( 'PHETCOMMON/util/Inheritance' );

  var HEIGHT = 40;

  function NavigationBar( $element, tabs, selectedTabProperty, homePressed ) {
    var navigationBar = this;
    this.handleResize = function() {
      var width = $( window ).width();
      this.tabsNode.centerX = width / 2;
      this.textLabel.right = this.tabsNode.left - 5;
      this.phetLabel.right = width - 5;
      this.homeIcon.left = this.tabsNode.right + 5;
      this.resize( width, HEIGHT );
      this.updateScene();
    };
    Scene.call( this, $element, {width: 1, height: 1, allowDevicePixelRatioScaling: true} );

    this.initializeStandaloneEvents(); // sets up listeners on the document with preventDefault(), and forwards those events to our scene

    this.textLabel = new Text( "Tug of War", {fontSize: 24, fill: 'white'} ).mutate( {centerY: HEIGHT / 2} );
    this.phetLabel = new Text( "PhET", {fontSize: 24, fill: 'yellow'} ).mutate( {centerY: HEIGHT / 2} );
    this.addChild( this.textLabel );
    this.addChild( this.phetLabel );

    var index = 0;
    var tabChildren = [];
    _.each( tabs, function( tab ) {
      tab.index = index++;
      tab.icon.scale( HEIGHT / tab.icon.height );
      tab.icon.paintCanvas = function( state ) {
        state.layer.context.globalAlpha = selectedTabProperty.get() === tab.index ? 1.0 : 0.5;
        Image.prototype.paintCanvas.call( tab.icon, state );
        state.layer.context.globalAlpha = 1.0;
      };
      tab.icon.cursor = 'pointer';
      selectedTabProperty.link( function() {
        tab.icon.invalidateBounds();
        tab.icon.invalidatePaint();
      } );
      tab.icon.addInputListener( { down: function() { selectedTabProperty.set( tab.index ); }} );
      tabChildren.push( tab.icon );
    } );

    this.tabsNode = new HBox( {children: tabChildren, spacing: 3} );
    this.addChild( this.tabsNode );

    //Add the home icon, uses font awesome to render it.  The unicode character was looked up in the CSS
    this.homeIcon = new Text( '\uf015', {fontFamily: 'FontAwesome', fontSize: '40px', fill: 'white', centerY: HEIGHT / 2, cursor: 'pointer'} );
    this.homeIcon.addInputListener( {down: homePressed} );
    this.addChild( this.homeIcon );

    _.each( tabs, function( tab ) {
      selectedTabProperty.link( function( m, value ) {
        navigationBar.textLabel.text = tabs[value].name;

        //TODO: could speed it up by just moving the text
        navigationBar.handleResize();
      } );
    } );

    //Fit to the window and render the initial scene
    $( window ).resize( this.handleResize.bind( this ) );
    this.handleResize();
  }

  Inheritance.inheritPrototype( NavigationBar, Scene );

  return NavigationBar;
} );<commit_msg>Updating NavigationBar with opacity control<commit_after>define( function( require ) {
  "use strict";

  var Image = require( 'SCENERY/nodes/Image' );
  var VBox = require( 'SCENERY/nodes/VBox' );
  var DOM = require( 'SCENERY/nodes/DOM' );
  var Node = require( 'SCENERY/nodes/Node' );
  var HBox = require( 'SCENERY/nodes/HBox' );
  var Scene = require( 'SCENERY/Scene' );
  var Text = require( 'SCENERY/nodes/Text' );
  var Path = require( 'SCENERY/nodes/Path' );
  var Shape = require( 'KITE/Shape' );
  var SimpleDragHandler = require( 'SCENERY/input/SimpleDragHandler' );
  var Bounds2 = require( 'DOT/Bounds2' );
  var Inheritance = require( 'PHETCOMMON/util/Inheritance' );

  var HEIGHT = 40;

  function NavigationBar( $element, tabs, selectedTabProperty, homePressed ) {
    var navigationBar = this;
    this.handleResize = function() {
      var width = $( window ).width();
      this.tabsNode.centerX = width / 2;
      this.textLabel.right = this.tabsNode.left - 5;
      this.phetLabel.right = width - 5;
      this.homeIcon.left = this.tabsNode.right + 5;
      this.resize( width, HEIGHT );
      this.updateScene();
    };
    Scene.call( this, $element, {width: 1, height: 1, allowDevicePixelRatioScaling: true} );

    this.initializeStandaloneEvents(); // sets up listeners on the document with preventDefault(), and forwards those events to our scene

    this.textLabel = new Text( "Tug of War", {fontSize: 24, fill: 'white'} ).mutate( {centerY: HEIGHT / 2} );
    this.phetLabel = new Text( "PhET", {fontSize: 24, fill: 'yellow'} ).mutate( {centerY: HEIGHT / 2} );
    this.addChild( this.textLabel );
    this.addChild( this.phetLabel );

    var index = 0;
    var tabChildren = [];
    _.each( tabs, function( tab ) {
      tab.index = index++;
      tab.icon.scale( HEIGHT / tab.icon.height );
      tab.icon.cursor = 'pointer';
      selectedTabProperty.link( function() {
        tab.icon.invalidateBounds();
        tab.icon.opacity = selectedTabProperty.get() === tab.index ? 1 : 0.5;
      } );
      tab.icon.addInputListener( { down: function() { selectedTabProperty.set( tab.index ); }} );
      tabChildren.push( tab.icon );
    } );

    this.tabsNode = new HBox( {children: tabChildren, spacing: 3} );
    this.addChild( this.tabsNode );

    //Add the home icon, uses font awesome to render it.  The unicode character was looked up in the CSS
    this.homeIcon = new Text( '\uf015', {fontFamily: 'FontAwesome', fontSize: '40px', fill: 'white', centerY: HEIGHT / 2, cursor: 'pointer'} );
    this.homeIcon.addInputListener( {down: homePressed} );
    this.addChild( this.homeIcon );

    _.each( tabs, function( tab ) {
      selectedTabProperty.link( function( m, value ) {
        navigationBar.textLabel.text = tabs[value].name;

        //TODO: could speed it up by just moving the text
        navigationBar.handleResize();
      } );
    } );

    //Fit to the window and render the initial scene
    $( window ).resize( this.handleResize.bind( this ) );
    this.handleResize();
  }

  Inheritance.inheritPrototype( NavigationBar, Scene );

  return NavigationBar;
} );
<|endoftext|>