<commit_before>var request = require('request'),
    moment = require('moment'),
    parseString = require('xml2js').parseString,
    h = require('apis-helpers'),
    app = require('../../server');

/* Skjar 1 */
app.get('/tv/skjar1', function (req, res) {
  var url = 'http://www.skjarinn.is/einn/dagskrarupplysingar/?channel_id=7&output_format=xml';

  request.get({
    headers: {'User-Agent': h.browser()},
    url: url
  }, function (error, response, body) {
    if (error) throw new Error(url + ' did not respond');

    parseSkjar1(function (data) {
      res.cache(1800).json(200, {
        results: data
      });
    }, body);
  });
});

/* Parse feed from Skjarinn */
var parseSkjar1 = function (callback, data) {
  parseString(data, function (err, result, title) {
    if (err) throw new Error('Parsing of XML failed. Title '+title);

    var schedule = [];

    for (var i = 0; i < result.schedule.service[0].event.length; ++i) {
      var event = result.schedule.service[0].event[i];
      if (moment().add('d',1).startOf('day').hour(6) > moment(event.$['start-time'])) {
        schedule.push({
          title: event.title[0],
          originalTitle: event['original-title'][0],
          duration: event.$.duration,
          description: event.description[0],
          shortDescription: event['short-description'][0],
          live: event.live[0] == 'yes' ? true : false,
          premier: event.rerun[0] == 'yes' ? false : true,
          startTime: event.$['start-time'],
          aspectRatio: event['aspect-ratio'][0].size[0],
          series: {
            episode: event.episode[0].$.number,
            series: event.episode[0].$['number-of-episodes']
          }
        });
      }
    }
    return callback(schedule);
  });
};
<commit_msg>Allow usage of both skjar1 and skjareinn, since it is referred to as skjareinn these days<commit_after>var request = require('request'),
    moment = require('moment'),
    parseString = require('xml2js').parseString,
    h = require('apis-helpers'),
    app = require('../../server');

/* Skjar 1 */
app.get('/tv/:var(skjar1|skjareinn)', function (req, res) {
  var url = 'http://www.skjarinn.is/einn/dagskrarupplysingar/?channel_id=7&output_format=xml';

  request.get({
    headers: {'User-Agent': h.browser()},
    url: url
  }, function (error, response, body) {
    if (error) throw new Error(url + ' did not respond');

    parseSkjar1(function (data) {
      res.cache(1800).json(200, {
        results: data
      });
    }, body);
  });
});

/* Parse feed from Skjarinn */
var parseSkjar1 = function (callback, data) {
  parseString(data, function (err, result, title) {
    if (err) throw new Error('Parsing of XML failed. Title '+title);

    var schedule = [];

    for (var i = 0; i < result.schedule.service[0].event.length; ++i) {
      var event = result.schedule.service[0].event[i];
      if (moment().add('d',1).startOf('day').hour(6) > moment(event.$['start-time'])) {
        schedule.push({
          title: event.title[0],
          originalTitle: event['original-title'][0],
          duration: event.$.duration,
          description: event.description[0],
          shortDescription: event['short-description'][0],
          live: event.live[0] == 'yes' ? true : false,
          premier: event.rerun[0] == 'yes' ? false : true,
          startTime: event.$['start-time'],
          aspectRatio: event['aspect-ratio'][0].size[0],
          series: {
            episode: event.episode[0].$.number,
            series: event.episode[0].$['number-of-episodes']
          }
        });
      }
    }
    return callback(schedule);
  });
};
<|endoftext|>


<commit_before>class Player {
  constructor() {
    // this.sprite = new Pentagine.Sprite(penta, 'helicopter.png',
                                       // 100, penta.context.height / 2 - 100);

    this.sprite = new Pentagine.Animation({
      game: penta,
      frames: ['helicopter.png'],
      x: 100,
      y: penta.context.height / 2 - 100
    });

    this.sprite.vx = 600;
    this.sprite.vy = 600;
  }

  update(dt) {
    if (penta.isMouseDown('left')) {
      if (penta.mouse.y < penta.context.height / 2) {
        this.sprite.y -= this.sprite.vy * dt;
      } else {
        this.sprite.y += this.sprite.vy * dt;
      }
    }

    if (penta.isDown('up') || penta.isDown('w')) {
      this.sprite.y -= this.sprite.vy * dt;
    }

    if (penta.isDown('down') || penta.isDown('s')) {
      this.sprite.y += this.sprite.vy * dt;
    }

    if (penta.isDown('left') || penta.isDown('a')) {
      this.sprite.x -= this.sprite.vx * dt;
    }

    if (penta.isDown('right') || penta.isDown('d')) {
      this.sprite.x += this.sprite.vx * dt;
    }
  }
}

class PlayState {
  setup() {
    this.helicopter = new Player();

    this.walls = [];
    this.speed = 20;
    this.minWallHeight = 50;
    this.walls[penta.context.width - 1] = this.minWallHeight;
    this.obstacles = [];

    this.score = 0;

    /* Generate new obstacles along the way */
    setInterval((function() {
      this.obstacles.push({ x: penta.context.width + 100,
                            y: Math.floor(Math.random() * 0.5 * penta.context.height) + 0.2 * penta.context.height,
                            width: 40,
                            height: 175 });
    }).bind(this), 1000);
  }

  update() {
    this.helicopter.update(this.dt); // Player tick
    this.score++; // Update score

    /* Update top and bottom walls */
    for (var i = 0; i < this.walls.length - this.speed; i++) {
      for (var u = 0; u < this.speed; u++) {
        this.walls[i + u] = this.walls[i + u + 1];
      }
    }

    /* Figure out new wall on right end */
    var newWallHeight = -1;

    do {
      newWallHeight = this.walls[this.walls.length - 1] + Math.floor(Math.random() * 10) - 5;
    } while (newWallHeight < this.minWallHeight);

    this.walls[this.walls.length - 1] = newWallHeight;

    /* Update obstacles */
    for (var obstacle = 0; obstacle < this.obstacles.length; obstacle++) {
      this.obstacles[obstacle].x -= this.speed / 2;
    }
  }

  draw() {
    penta.clearCanvas('#49637e');

    for (var i = 0; i < this.walls.length; i++) {
      penta.drawRectangle(i, 0, 1, this.walls[i], '#123');
      penta.drawRectangle(i, penta.context.height - this.walls[i], 1, this.walls[i], '#123');
    }

    for (i = 0; i < this.obstacles.length; i++) {
      penta.drawRectangle(this.obstacles[i].x,
                          this.obstacles[i].y,
                          this.obstacles[i].width,
                          this.obstacles[i].height);
    }

    penta.currentFont = '20px arial';
    penta.drawString('Score: ' + this.score.toString(), 5, 65, '#000');
    penta.drawString('Delta Time: ' + Math.floor((this.dt * 1000).toString()) + 'ms', 5, 105, '#000');
    penta.drawString('FPS: ' + Math.floor((1 / this.dt).toString()) + '', 5, 145, '#000');

    this.helicopter.sprite.draw();
  }
}

/* Start up the game */
var penta = new Pentagine.Game();

penta.setup({ desiredFPS: 60,
              preventedKeys: ['down', 'right', 'left', 'up', 'space'],
              firstState: new PlayState(),
              width: document.documentElement.clientWidth,
              height: document.documentElement.clientHeight });
<commit_msg>Fixes a bug with the x and y properties of the Animation class.<commit_after>class Player {
  constructor() {
    // this.sprite = new Pentagine.Sprite(penta, 'helicopter.png',
                                       // 100, penta.context.height / 2 - 100);

    this.sprite = new Pentagine.Animation({
      game: penta,
      frames: ['helicopter.png', 'helicopter2.png'],
      x: 100,
      y: penta.context.height / 2 - 100
    });

    console.log(this.sprite);

    this.sprite.vx = 600;
    this.sprite.vy = 600;
  }

  update(dt) {
    this.sprite.update();

    if (penta.isMouseDown('left')) {
      if (penta.mouse.y < penta.context.height / 2) {

        // console.log(this.sprite.vy * dt);

        // this.sprite.y -= this.sprite.vy * dt;

        console.log(this.sprite.y);
        console.log(this.sprite.vy);
        console.log(dt);
        console.log(this.sprite.y - this.sprite.vy * dt);

        this.sprite.y = this.sprite.y - this.sprite.vy * dt;
      } else {
        this.sprite.y = this.sprite.y + this.sprite.vy * dt;

        // this.sprite.y += this.sprite.vy * dt;
      }
    }

    if (penta.isDown('up') || penta.isDown('w')) {
      this.sprite.y -= this.sprite.vy * dt;
    }

    if (penta.isDown('down') || penta.isDown('s')) {
      this.sprite.y += this.sprite.vy * dt;
    }

    if (penta.isDown('left') || penta.isDown('a')) {
      this.sprite.x -= this.sprite.vx * dt;
    }

    if (penta.isDown('right') || penta.isDown('d')) {
      this.sprite.x += this.sprite.vx * dt;
    }
  }
}

class PlayState {
  setup() {
    this.helicopter = new Player();

    this.walls = [];
    this.speed = 20;
    this.minWallHeight = 50;
    this.walls[penta.context.width - 1] = this.minWallHeight;
    this.obstacles = [];

    this.score = 0;

    /* Generate new obstacles along the way */
    setInterval((function() {
      this.obstacles.push({ x: penta.context.width + 100,
                            y: Math.floor(Math.random() * 0.5 * penta.context.height) + 0.2 * penta.context.height,
                            width: 40,
                            height: 175 });
    }).bind(this), 1000);
  }

  update() {
    this.helicopter.update(this.dt); // Player tick
    this.score++; // Update score

    /* Update top and bottom walls */
    for (var i = 0; i < this.walls.length - this.speed; i++) {
      for (var u = 0; u < this.speed; u++) {
        this.walls[i + u] = this.walls[i + u + 1];
      }
    }

    /* Figure out new wall on right end */
    var newWallHeight = -1;

    do {
      newWallHeight = this.walls[this.walls.length - 1] + Math.floor(Math.random() * 10) - 5;
    } while (newWallHeight < this.minWallHeight);

    this.walls[this.walls.length - 1] = newWallHeight;

    /* Update obstacles */
    for (var obstacle = 0; obstacle < this.obstacles.length; obstacle++) {
      this.obstacles[obstacle].x -= this.speed / 2;
    }
  }

  draw() {
    penta.clearCanvas('#49637e');

    for (var i = 0; i < this.walls.length; i++) {
      penta.drawRectangle(i, 0, 1, this.walls[i], '#123');
      penta.drawRectangle(i, penta.context.height - this.walls[i], 1, this.walls[i], '#123');
    }

    for (i = 0; i < this.obstacles.length; i++) {
      penta.drawRectangle(this.obstacles[i].x,
                          this.obstacles[i].y,
                          this.obstacles[i].width,
                          this.obstacles[i].height);
    }

    penta.currentFont = '20px arial';
    penta.drawString('Score: ' + this.score.toString(), 5, 65, '#000');
    penta.drawString('Delta Time: ' + Math.floor((this.dt * 1000).toString()) + 'ms', 5, 105, '#000');
    penta.drawString('FPS: ' + Math.floor((1 / this.dt).toString()) + '', 5, 145, '#000');

    this.helicopter.sprite.draw();
  }
}

/* Start up the game */
var penta = new Pentagine.Game();

penta.setup({ desiredFPS: 60,
              preventedKeys: ['down', 'right', 'left', 'up', 'space'],
              firstState: new PlayState(),
              width: document.documentElement.clientWidth,
              height: document.documentElement.clientHeight });
<|endoftext|>


<commit_before>/**
 * Koowa jqTree wrapper
 *
 * Customized instance of jqTree to render a list of categories in a tree structure.
 * It deals with turning a flat list into a hierarchy structure that jqTree understands.
 *
 * @copyright	Copyright (C) 2007 - 2013 Johan Janssens and Timble CVBA. (http://www.timble.net)
 * @license		GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
 * @link		http://github.com/joomlatools/koowa for the canonical source repository
 * @requires    Koowa.Class, jqTree plugin
 */

(function($){

    Koowa.Tree = Koowa.Class.extend({

        initialize: function(element, options){

            this.setOptions(options);

            this.element = $(element);

            if(this.options.onBeforeInitialize) this.options.onBeforeInitialize.call(this);

            // For scrollTo to work, needs to be position:relative;
            this.element.css('position', 'relative');

            this.attachHandlers();

            if(this.options.data && this.options.data.length) this.options.data = this.parseData(this.options.data);

            // Shortcut for accessing tree apis, like this: selected = this.tree('getSelectedNode')
            this.tree = $.proxy(this.element.tree, this.element);

            // Instantiate the jqTree plugin with the parsed options
            this.tree(this.options);

            if(this.options.onAfterInitialize) this.options.onAfterInitialize.call(this);
        },

        getDefaults: function(){

            var self = this,
                defaults = {
                    selected: null, //The node id of a selected node on init, if any
                    data: [], //Default empty value to avoid errors when there are no items yet
                    autoOpen: 0, //Auto open just "All Categories" by default, this value is the nesting level not the node id
                    useContextMenu: false, //This allows us to right-click menu items again
                    toggler: [{triangle: ['icon-triangle-right', '►'], folder: 'icon-folder-close'},//Styling options for toggler
                              {triangle: ['icon-triangle-down', '▼'], folder: 'icon-folder-open'}],
                    onCreateLi: function(node, $li){ //Method for customizing
 markup

                        /**
                         * Adds a title attribute for the full title in case of overflow.
                         * Wraps the inner elements of the
 with a  tag to apply nav list styling from bootstrap
                         * on each nav element.
                         * Doing this also increases click area to the entire row, instead of a smaller element just
                         * around the title.
                         * The href="#" gives each element cursor:pointer styling.
                         * Click handler makes sure clicking the outer  element selects the node, instead of doing nothing.
                         */

                        $li.find('.jqtree-element').attr('title', node.name).wrap($('', {
                            /**
                             * @TODO check if possible to be rid of the # attribute, or use the javascript uri
                             */
                            href: '#',
                            on: {
                                click: function(event){
                                    //If event target is .jqtree-element then the following code isn't needed
                                    if(!$(event.target).is('.jqtree-element')) {
                                        event.preventDefault(); //Prevent '#' added to the url, causing a scroll to top
                                        event.stopPropagation(); //Prevent bubbling up to the click handler in jqTree

                                        // Pass null to deselect, selectNode API never deselects when passing a node
                                        self.tree('selectNode', self.tree('getSelectedNode') !== node ? node : null);
                                    }
                                }
                            }
                        }));

                        if(node.isFolder()) {
                            // states variable is for easy toggling on the click event
                            var states = self.options.toggler,
                                state = states[node.is_open ? 1 : 0],
                                triangle = $('', {
                                    'class': 'icon-toggler '+state.triangle[0], //Either icon-triangle-right or icon-triangle-down
                                    html: state.triangle[1], //The html entity code for either a down or right arrow
                                    on: {
                                        click: function(event){
                                            // making sure that select.node doesn't fire when clicking the open/close toggler
                                            event.preventDefault();
                                            event.stopPropagation();

                                            // display or hide children items and fire the tree.open or tree.close event
                                            self.element.tree('toggle', node);
                                        }
                                    }
                                });
                            // prepend the toggler triangle and the folder icon to the title
                            $li.find('.jqtree-title').prepend(' ').prepend(triangle);
                        } else {
                            // prepend the folder icon, and an empty space for the triangle so the indendation is correct
                            $li.find('.jqtree-title').prepend(' ').prepend('');
                        }

                        /**
                         * Generates indentation for each list item according to nesting level.
                         * @TODO the node.getLevel() property lookup isn't cached, submit patch on the jqTree github
                         */
                        var level = node.getLevel();
                        for (var i = 1; i < level; ++i) {
                            $li.find('.jqtree-title').prepend(' ');
                        }
                    }
                };

                return defaults;
        },

        setOptions: function(options){

            this.options = $.extend(true, {}, this.getDefaults(), options);

            return this;
        },

        /* Selects a node while opening all parent nodes, requiring the node to have a 'path' property in its data */
        selectNode: function(node, element){

            // the -1 value is the root node
            var nodes = [-1], state = element.tree('getState');

            // fetch parent node ids from path variable, map the array with parseInt to ensure each array item is an integer
            nodes.push.apply(nodes, node.path.split('/').map(function(item){return parseInt(item, 10)}));

            state.selected_node = node.id; // setting current selected node state to the new node
            state.open_nodes.push.apply(state.open_nodes, nodes); // set the root node, parent nodes and current node as open nodes

            // Using setState instead of selectNode allows selecting a node without firing tree.select
            element.tree('setState', state);
        },

        /**
         * Customizable parse data method
         *
         * Customize this method if the structure is a bit non-standard, like DOClink in DOCman
         * or if you need to wrap all nodes in a root node, like the DOCman documents view categories sidebar tree.
         *
         * The following code sample shows how to create a root node like seen in DOCman:
         * return [{
         *      label: 'All Categories',
         *      id: -1, //negative 1 used as jqTree isn't optimized to deal with zero integer ids, methods like selectNode fail
         *      children: this._parseData(list)
         *  }];
         */
        parseData: function(list){
            return this._parseData(list);
        },

        /**
         * Internal parse data method
         *
         * Only customize this method if you know what you're doing and if it's impossible to control the data format
         * that's passed to the script during initialization.
         *
         * @param array list
         *
         * The data objects in the list are required to have the following properties:
         * @property string|integer id          Required to be unique, integer or string is optional
         * @property string|integer parent      Integer or string is optional, zero when no parent
         * @property string         path        Containing parent ids descending left to right separated by '/'
         * @property string         label       The text to be displayed
         *
         * Nodes can contain any custom data properties passed to it, except the following reserved properties:
         * @property array      children
         * @property Element    element
         * @property string     name
         * @property Node       tree
         *
         * Simple data sample with unique ids:
         *  [
         *      {id: 1, parent: 0, path: '', label: 'Blog'},
         *      {id: 2, parent: 1, path: '1', label: 'News'},
         *      {id: 3, parent: 2, path: '1/2', label: 'Nooku Code Jam'},
         *      {id: 4, parent: 2, path: '1/2', label: 'Nooku Framework'},
         *      {id: 5, parent: 0, path: '', label: 'Tutorials'}
         *  ]
         *
         *  Advanced data sample with non-unique ids:
         *  [
         *      {id: 's1', section_id: 1, parent: 0, path: '', label: 'Blog'},
         *      {id: 's1c1', section_id: 1, category_id: 1, parent: 's1', path: 's1', label: 'News'},
         *      {id: 's1c2', section_id: 1, category_id: 2, parent: 's1c1', path: 's1/s1c1', label: 'Nooku Code Jam'},
         *      {id: 's1c3', section_id: 1, category_id: 3, parent: 's1c1', path: 's1/s1c1', label: 'Nooku Framework'},
         *      {id: 's2', section_id: 2, parent: 0, path: '', label: 'Tutorials'}
         *  ]
         */
        _parseData: function(list){

            var data = [], index = {}; // 'data' is an hierarchial list while 'index' is flat and used to lookup parents

            $.each(list, function(key,item){

                index[item.id] = item; // always add the item to the lookup index

                if(item.parent == 0 || !index.hasOwnProperty(item.parent)) {
                    data.push(item); // top level items are added directly to the new list or if orphan
                } else {
                    if(!index[item.parent].hasOwnProperty('children')) index[item.parent].children = [];
                    // changing items in 'index' changes the items in 'data' as they're not deep cloned
                    index[item.parent].children.push(item);
                }
            });

            //Return the data inside a 'root' node to replicate legacy mootree behavior
            return data;
        },

        // create a params object from a querystring
        unserialize: function (query) {
            var pair, params = {};
            query = query.replace(/^\?/, '').split(/&/);
            for (pair in query) {
                if(query.hasOwnProperty(pair)) {
                    pair = query[pair].split('=');
                    params[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
                }
            }
            return params;
        },

        /**
         * Internal event handlers for customizing the jqTree markup and behavior to integrate better with bootstrap.
         *
         * DO NOT Customize this method to add your own events, use attachHandlers instead
         */
        _attachHandlers: function(){

            var options = this.options, self = this, states = options.toggler;

            this.element.bind({
                'tree.select': // The select event happens when a node is clicked
                    function(event) {
                        $(this).find('.active').removeClass('active').find('.icon-white').removeClass('icon-white');
                        if(event.node) { // When event.node is null, it's actually a deselect event
                            //Style the clicked element
                            $(this).find('.jqtree-selected').addClass('active').children('a').find('[class^=icon-folder]').addClass('icon-white');
                        }
                    },
                'tree.open':
                    function(event) {
                        // toggle classes and html on the triangle, and folder icon
                        var node = event.node, state = states[1], old = states[0], triangle = $(node.element).children('a').find('.icon-toggler');
                        triangle.removeClass(old.triangle[0]).addClass(state.triangle[0]).html(state.triangle[1]);

                        triangle.closest('a').find('.'+old.folder).removeClass(old.folder).addClass(state.folder);
                    },
                'tree.close':
                    function(event) {
                        // toggle classes and html on the triangle, and folder icon
                        var node = event.node, state = states[0], old = states[1], triangle = $(node.element).children('a').find('.icon-toggler');
                        triangle.removeClass(old.triangle[0]).addClass(state.triangle[0]).html(state.triangle[1]);

                        triangle.closest('a').find('.'+old.folder).removeClass(old.folder).addClass(state.folder);
                    },
                'tree.init':
                    function() {
                        // .sidebar-nav needed for bootstrap styling to apply
                        $(this).find('ul.jqtree-tree').addClass('sidebar-nav');

                        // If a node should be preselected on init, select it right away
                        if(options.selected) {
                            self.selectNode($(this).tree('getNodeById', options.selected), $(this));
                        }
                    },
                'tree.refresh': //Refreshes reset the html, and happen on events like setState
                    function() {
                        $(this).find('ul.jqtree-tree').addClass('sidebar-nav'); // .sidebar-nav needed for bootstrap styling to apply
                        $(this).find('.jqtree-selected').addClass('active').children('a').find('[class^=icon-folder]').addClass('icon-white');
                    }
            });

        },

        /**
         * Attach event handlers for jQtree events for behaviors and interactivity
         *
         * @link http://mbraak.github.io/jqTree/#events
         */
        attachHandlers: function(){

            this._attachHandlers(); // @NOTE Attach needed events, remember to call this if you customize this method

        },
        /**
         * @TODO move scrollIntoView logic from doclink.js, categories.tree.js and files.tree.js into here
         * @param node
         * @param viewport
         * @param duration
         */
        scrollIntoView: function(node, viewport, duration){

        }
    });
}(window.jQuery));<commit_msg>re #101 adding bug placeholder<commit_after>/**
 * Koowa jqTree wrapper
 *
 * Customized instance of jqTree to render a list of categories in a tree structure.
 * It deals with turning a flat list into a hierarchy structure that jqTree understands.
 *
 * @copyright	Copyright (C) 2007 - 2013 Johan Janssens and Timble CVBA. (http://www.timble.net)
 * @license		GNU GPLv3 <http://www.gnu.org/licenses/gpl.html>
 * @link		http://github.com/joomlatools/koowa for the canonical source repository
 * @requires    Koowa.Class, jqTree plugin
 */

(function($){

    Koowa.Tree = Koowa.Class.extend({

        initialize: function(element, options){

            this.setOptions(options);

            this.element = $(element);

            if(this.options.onBeforeInitialize) this.options.onBeforeInitialize.call(this);

            // For scrollTo to work, needs to be position:relative;
            this.element.css('position', 'relative');

            this.attachHandlers();

            if(this.options.data && this.options.data.length) this.options.data = this.parseData(this.options.data);

            // Shortcut for accessing tree apis, like this: selected = this.tree('getSelectedNode')
            this.tree = $.proxy(this.element.tree, this.element);

            // Instantiate the jqTree plugin with the parsed options
            this.tree(this.options);

            if(this.options.onAfterInitialize) this.options.onAfterInitialize.call(this);
        },

        getDefaults: function(){

            var self = this,
                defaults = {
                    selected: null, //The node id of a selected node on init, if any
                    data: [], //Default empty value to avoid errors when there are no items yet
                    autoOpen: 0, //Auto open just "All Categories" by default, this value is the nesting level not the node id
                    useContextMenu: false, //This allows us to right-click menu items again
                    toggler: [{triangle: ['icon-triangle-right', '►'], folder: 'icon-folder-close'},//Styling options for toggler
                              {triangle: ['icon-triangle-down', '▼'], folder: 'icon-folder-open'}],
                    onCreateLi: function(node, $li){ //Method for customizing
 markup

                        /**
                         * Adds a title attribute for the full title in case of overflow.
                         * Wraps the inner elements of the
 with a  tag to apply nav list styling from bootstrap
                         * on each nav element.
                         * Doing this also increases click area to the entire row, instead of a smaller element just
                         * around the title.
                         * The href="#" gives each element cursor:pointer styling.
                         * Click handler makes sure clicking the outer  element selects the node, instead of doing nothing.
                         */

                        $li.find('.jqtree-element').attr('title', node.name).wrap($('', {
                            /**
                             * @TODO check if possible to be rid of the # attribute, or use the javascript uri
                             */
                            href: '#',
                            on: {
                                click: function(event){
                                    //If event target is .jqtree-element then the following code isn't needed
                                    if(!$(event.target).is('.jqtree-element')) {
                                        event.preventDefault(); //Prevent '#' added to the url, causing a scroll to top
                                        event.stopPropagation(); //Prevent bubbling up to the click handler in jqTree

                                        // Pass null to deselect, selectNode API never deselects when passing a node
                                        self.tree('selectNode', self.tree('getSelectedNode') !== node ? node : null);
                                    }
                                }
                            }
                        }));

                        if(node.isFolder()) {
                            // states variable is for easy toggling on the click event
                            var states = self.options.toggler,
                                state = states[node.is_open ? 1 : 0],
                                triangle = $('', {
                                    'class': 'icon-toggler '+state.triangle[0], //Either icon-triangle-right or icon-triangle-down
                                    html: state.triangle[1], //The html entity code for either a down or right arrow
                                    on: {
                                        //@TODO investigate bug
                                        click: function(event){
                                            // making sure that select.node doesn't fire when clicking the open/close toggler
                                            event.preventDefault();
                                            event.stopPropagation();

                                            // display or hide children items and fire the tree.open or tree.close event
                                            self.element.tree('toggle', node);
                                        }
                                    }
                                });
                            // prepend the toggler triangle and the folder icon to the title
                            $li.find('.jqtree-title').prepend(' ').prepend(triangle);
                        } else {
                            // prepend the folder icon, and an empty space for the triangle so the indendation is correct
                            $li.find('.jqtree-title').prepend(' ').prepend('');
                        }

                        /**
                         * Generates indentation for each list item according to nesting level.
                         * @TODO the node.getLevel() property lookup isn't cached, submit patch on the jqTree github
                         */
                        var level = node.getLevel();
                        for (var i = 1; i < level; ++i) {
                            $li.find('.jqtree-title').prepend(' ');
                        }
                    }
                };

                return defaults;
        },

        setOptions: function(options){

            this.options = $.extend(true, {}, this.getDefaults(), options);

            return this;
        },

        /* Selects a node while opening all parent nodes, requiring the node to have a 'path' property in its data */
        selectNode: function(node, element){

            // the -1 value is the root node
            var nodes = [-1], state = element.tree('getState');

            // fetch parent node ids from path variable, map the array with parseInt to ensure each array item is an integer
            nodes.push.apply(nodes, node.path.split('/').map(function(item){return parseInt(item, 10)}));

            state.selected_node = node.id; // setting current selected node state to the new node
            state.open_nodes.push.apply(state.open_nodes, nodes); // set the root node, parent nodes and current node as open nodes

            // Using setState instead of selectNode allows selecting a node without firing tree.select
            element.tree('setState', state);
        },

        /**
         * Customizable parse data method
         *
         * Customize this method if the structure is a bit non-standard, like DOClink in DOCman
         * or if you need to wrap all nodes in a root node, like the DOCman documents view categories sidebar tree.
         *
         * The following code sample shows how to create a root node like seen in DOCman:
         * return [{
         *      label: 'All Categories',
         *      id: -1, //negative 1 used as jqTree isn't optimized to deal with zero integer ids, methods like selectNode fail
         *      children: this._parseData(list)
         *  }];
         */
        parseData: function(list){
            return this._parseData(list);
        },

        /**
         * Internal parse data method
         *
         * Only customize this method if you know what you're doing and if it's impossible to control the data format
         * that's passed to the script during initialization.
         *
         * @param array list
         *
         * The data objects in the list are required to have the following properties:
         * @property string|integer id          Required to be unique, integer or string is optional
         * @property string|integer parent      Integer or string is optional, zero when no parent
         * @property string         path        Containing parent ids descending left to right separated by '/'
         * @property string         label       The text to be displayed
         *
         * Nodes can contain any custom data properties passed to it, except the following reserved properties:
         * @property array      children
         * @property Element    element
         * @property string     name
         * @property Node       tree
         *
         * Simple data sample with unique ids:
         *  [
         *      {id: 1, parent: 0, path: '', label: 'Blog'},
         *      {id: 2, parent: 1, path: '1', label: 'News'},
         *      {id: 3, parent: 2, path: '1/2', label: 'Nooku Code Jam'},
         *      {id: 4, parent: 2, path: '1/2', label: 'Nooku Framework'},
         *      {id: 5, parent: 0, path: '', label: 'Tutorials'}
         *  ]
         *
         *  Advanced data sample with non-unique ids:
         *  [
         *      {id: 's1', section_id: 1, parent: 0, path: '', label: 'Blog'},
         *      {id: 's1c1', section_id: 1, category_id: 1, parent: 's1', path: 's1', label: 'News'},
         *      {id: 's1c2', section_id: 1, category_id: 2, parent: 's1c1', path: 's1/s1c1', label: 'Nooku Code Jam'},
         *      {id: 's1c3', section_id: 1, category_id: 3, parent: 's1c1', path: 's1/s1c1', label: 'Nooku Framework'},
         *      {id: 's2', section_id: 2, parent: 0, path: '', label: 'Tutorials'}
         *  ]
         */
        _parseData: function(list){

            var data = [], index = {}; // 'data' is an hierarchial list while 'index' is flat and used to lookup parents

            $.each(list, function(key,item){

                index[item.id] = item; // always add the item to the lookup index

                if(item.parent == 0 || !index.hasOwnProperty(item.parent)) {
                    data.push(item); // top level items are added directly to the new list or if orphan
                } else {
                    if(!index[item.parent].hasOwnProperty('children')) index[item.parent].children = [];
                    // changing items in 'index' changes the items in 'data' as they're not deep cloned
                    index[item.parent].children.push(item);
                }
            });

            //Return the data inside a 'root' node to replicate legacy mootree behavior
            return data;
        },

        // create a params object from a querystring
        unserialize: function (query) {
            var pair, params = {};
            query = query.replace(/^\?/, '').split(/&/);
            for (pair in query) {
                if(query.hasOwnProperty(pair)) {
                    pair = query[pair].split('=');
                    params[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
                }
            }
            return params;
        },

        /**
         * Internal event handlers for customizing the jqTree markup and behavior to integrate better with bootstrap.
         *
         * DO NOT Customize this method to add your own events, use attachHandlers instead
         */
        _attachHandlers: function(){

            var options = this.options, self = this, states = options.toggler;

            this.element.bind({
                'tree.select': // The select event happens when a node is clicked
                    function(event) {
                        $(this).find('.active').removeClass('active').find('.icon-white').removeClass('icon-white');
                        if(event.node) { // When event.node is null, it's actually a deselect event
                            //Style the clicked element
                            $(this).find('.jqtree-selected').addClass('active').children('a').find('[class^=icon-folder]').addClass('icon-white');
                        }
                    },
                'tree.open':
                    function(event) {
                        // toggle classes and html on the triangle, and folder icon
                        var node = event.node, state = states[1], old = states[0], triangle = $(node.element).children('a').find('.icon-toggler');
                        triangle.removeClass(old.triangle[0]).addClass(state.triangle[0]).html(state.triangle[1]);

                        triangle.closest('a').find('.'+old.folder).removeClass(old.folder).addClass(state.folder);
                    },
                'tree.close':
                    function(event) {
                        // toggle classes and html on the triangle, and folder icon
                        var node = event.node, state = states[0], old = states[1], triangle = $(node.element).children('a').find('.icon-toggler');
                        triangle.removeClass(old.triangle[0]).addClass(state.triangle[0]).html(state.triangle[1]);

                        triangle.closest('a').find('.'+old.folder).removeClass(old.folder).addClass(state.folder);
                    },
                'tree.init':
                    function() {
                        // .sidebar-nav needed for bootstrap styling to apply
                        $(this).find('ul.jqtree-tree').addClass('sidebar-nav');

                        // If a node should be preselected on init, select it right away
                        if(options.selected) {
                            self.selectNode($(this).tree('getNodeById', options.selected), $(this));
                        }
                    },
                'tree.refresh': //Refreshes reset the html, and happen on events like setState
                    function() {
                        $(this).find('ul.jqtree-tree').addClass('sidebar-nav'); // .sidebar-nav needed for bootstrap styling to apply
                        $(this).find('.jqtree-selected').addClass('active').children('a').find('[class^=icon-folder]').addClass('icon-white');
                    }
            });

        },

        /**
         * Attach event handlers for jQtree events for behaviors and interactivity
         *
         * @link http://mbraak.github.io/jqTree/#events
         */
        attachHandlers: function(){

            this._attachHandlers(); // @NOTE Attach needed events, remember to call this if you customize this method

        },
        /**
         * @TODO move scrollIntoView logic from doclink.js, categories.tree.js and files.tree.js into here
         * @param node
         * @param viewport
         * @param duration
         */
        scrollIntoView: function(node, viewport, duration){

        }
    });
}(window.jQuery));<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();
var ctrlHome = require('../controllers/home');
var ctrlLogin = require('../controllers/login');
var ctrlShop = require('../controllers/shop');
var ctrlRun = require('../controllers/run');
var ctrlWish = require('../controllers/wishes');

/* GET home page */
router.get('/', ctrlHome.homepageController);

/* GET login page */
router.get('/login', ctrlLogin.loginController);

/* GET shop page */
router.get('/shop', ctrlShop.shopController);

/* GET runway page */
router.get('/runway', ctrlRun.runController);

/* GET wishlist page */
router.get('/wish', ctrlWish.wishlistController);

module.exports = router;
<commit_msg>yang test<commit_after>var express = require('express');
var router = express.Router();
//var ctrlHome = require('../controllers/home');
var ctrlHome = require('../controllers/home');
/*var ctrlLogin = require('../controllers/login');
var ctrlShop = require('../controllers/shop');
var ctrlRun = require('../controllers/run');
var ctrlWish = require('../controllers/wishes');*/

/* GET home page */
//router.get('/', ctrlHome.homepageController);
/* GET home page */
router.get('/', ctrlHome.testController);

/* GET login page */
//router.get('/login', ctrlLogin.loginController);

/* GET shop page */
//router.get('/shop', ctrlShop.shopController);

/* GET runway page */
//router.get('/runway', ctrlRun.runController);

/* GET wishlist page */
//router.get('/wish', ctrlWish.wishlistController);

module.exports = router;
<|endoftext|>


<commit_before>require({
           packages: [
               { name: 'jqueryui', location: '../plugins/WebApollo/jslib/jqueryui' },
               { name: 'jquery', location: '../plugins/WebApollo/jslib/jquery', main: 'jquery' }
           ]
       },
       [],
       function() {

define.amd.jQuery = true;

define(
       [
           'dojo/_base/declare',
           'dijit/MenuItem',
           'dijit/CheckedMenuItem',
           'dijit/form/DropDownButton',
           'dijit/DropDownMenu',
           'dijit/form/Button',
           'JBrowse/Plugin',
           './FeatureEdgeMatchManager',
	   './FeatureSelectionManager',
	   './View/Track/AnnotTrack'
       ],
    function( declare, dijitMenuItem, dijitCheckedMenuItem, dijitDropDownButton, dijitDropDownMenu, dijitButton, JBPlugin,
              FeatureEdgeMatchManager, FeatureSelectionManager, AnnotTrack ) {

return declare( JBPlugin,
{
//    colorCdsByFrame: false,
//    searchMenuInitialized: false,

    constructor: function( args ) {
        var thisB = this;
        this.colorCdsByFrame = false;
        this.searchMenuInitialized = false;
        var browser = this.browser;  // this.browser set in Plugin superclass constructor

        if (! browser.config.helpUrl)  {
	    browser.config.helpUrl = "http://genomearchitect.org/WebApolloHelp";
        }

        // hand the browser object to the feature edge match manager
        FeatureEdgeMatchManager.setBrowser( browser );

	this.featSelectionManager = new FeatureSelectionManager();
	this.annotSelectionManager = new FeatureSelectionManager();

	// setting up selection exclusiveOr --
	//    if selection is made in annot track, any selection in other tracks is deselected, and vice versa,
	//    regardless of multi-select mode etc.
	this.annotSelectionManager.addMutualExclusion(this.featSelectionManager);
	this.featSelectionManager.addMutualExclusion(this.annotSelectionManager);

	FeatureEdgeMatchManager.addSelectionManager(this.featSelectionManager);
	FeatureEdgeMatchManager.addSelectionManager(this.annotSelectionManager);

        // add a global menu option for setting CDS color
        var cds_frame_toggle = new dijitCheckedMenuItem(
                {
                    label: "Color by CDS frame",
                    checked: false,
                    onClick: function(event) {
                        thisB.colorCdsByFrame = cds_frame_toggle.checked;
                        browser.view.redrawTracks();
                    }
                });
        browser.addGlobalMenuItem( 'options', cds_frame_toggle );
        var plus_strand_toggle = new dijitCheckedMenuItem(
                {
                    label: "Show plus strand",
                    checked: true,
                    onClick: function(event) {
                        var plus = plus_strand_toggle.checked;
                        var minus = minus_strand_toggle.checked;
                        console.log("plus: ", plus, " minus: ", minus);
                        if (plus && minus)  {
                            browser.view.featureFilter = browser.view.passAllFilter;
                        }
                        else if (plus)  {
                            browser.view.featureFilter = browser.view.plusStrandFilter;
                        }
                        else if (minus)  {
                            browser.view.featureFilter = browser.view.minusStrandFilter;
                        }
                        else  {
                            browser.view.featureFilter = browser.view.passNoneFilter;
                        }
                        // browser.view.redrawTracks();
                        thisB.redoLayout();
                    }
                });
        browser.addGlobalMenuItem( 'options', plus_strand_toggle );
        var minus_strand_toggle = new dijitCheckedMenuItem(
                {
                    label: "Show minus strand",
                    checked: true,
                    onClick: function(event) {
                        var plus = plus_strand_toggle.checked;
                        var minus = minus_strand_toggle.checked;
                        console.log("plus: ", plus, " minus: ", minus);
                        if (plus && minus)  {
                            browser.view.featureFilter = browser.view.passAllFilter;
                        }
                        else if (plus)  {
                            browser.view.featureFilter = browser.view.plusStrandFilter;
                        }
                        else if (minus)  {
                            browser.view.featureFilter = browser.view.minusStrandFilter;
                        }
                        else  {
                            browser.view.featureFilter = browser.view.passNoneFilter;
                        }
                        // browser.view.redrawTracks();
                        thisB.redoLayout();
                    }
                });
        browser.addGlobalMenuItem( 'options', minus_strand_toggle );

        // register the WebApollo track types with the browser, so
        // that the open-file dialog and other things will have them
        // as options
        browser.registerTrackType({
            type:                 'WebApollo/View/Track/DraggableHTMLFeatures',
            defaultForStoreTypes: [ 'JBrowse/Store/SeqFeature/NCList',
                                    'JBrowse/Store/SeqFeature/GFF3'
                                  ],
            label: 'WebApollo Features'
        });
        browser.registerTrackType({
            type:                 'WebApollo/View/Track/DraggableAlignments',
            defaultForStoreTypes: [
                                    'JBrowse/Store/SeqFeature/BAM',
                                  ],
            label: 'WebApollo Alignments'
        });
        browser.registerTrackType({
            type:                 'WebApollo/View/Track/SequenceTrack',
            defaultForStoreTypes: [ 'JBrowse/Store/Sequence/StaticChunked' ],
            label: 'WebApollo Sequence'
        });

        // put the WebApollo logo in the powered_by place in the main JBrowse bar
        browser.afterMilestone( 'initView', function() {
            browser.poweredByLink.innerHTML = '';
            browser.poweredByLink.href = 'http://www.gmod.org/wiki/WebApollo';
            browser.poweredByLink.target = "_blank";
        });

    },

    // would rather call view.redrawTracks()
    //
    // BUT, view.redrawTracks currently doesn't force relayout
    //     browser.view.redrawTracks();
    // track.changed() forces relayout (at least for HTMLFeatures)
    //    but also call changeCallBack(), which currently is always view.showVisibleBlocks()
    //    thus will needlessly call view.showVisibleBlocks() repeatedly
    // so trying for now to be explicit
    redoLayout: function()  {
        this.browser.view.trackIterate( function(t) {
                                       t.hideAll();
                                       if (t._clearLayout)  {
                                           // console.log("clearing layout for track: " + t.label);
                                           t._clearLayout();
                                       }
                                   }
                                 );
        this.browser.view.showVisibleBlocks(true);
    },

/**
 * hacking addition of a "tools" menu to standard JBrowse menubar,
 *    with a "Search Sequence" dropdown
 */
    initSearchMenu: function()  {
        if (! this.searchMenuInitialized) {
            var webapollo = this;
            this.browser.addGlobalMenuItem( 'tools',
                                            new dijitMenuItem(
                                                {
		                                    label: "Search sequence",
		                                    onClick: function() {
		                                        webapollo.getAnnotTrack().searchSequence();
		                                    }
                                                }) );
            var toolMenu = this.browser.makeGlobalMenu('tools');
            if( toolMenu ) {
                var toolButton = new dijitDropDownButton(
                    { className: 'file',
                      innerHTML: 'Tools',
                      //title: '',
                      dropDown: toolMenu
                    });
                dojo.addClass( toolButton.domNode, 'menu' );
                this.browser.menuBar.appendChild( toolButton.domNode );
            }
        }
        this.searchMenuInitialized = true;
    },

    initLoginMenu: function(username) {
    	var webapollo = this;
    	var loginButton;
    	if (username)  {   // permission only set if permission request succeeded
    		this.browser.addGlobalMenuItem( 'user',
    				new dijitMenuItem(
    						{
    							label: 'Logout',
    							onClick: function()  {
    								webapollo.getAnnotTrack().logout();
    							}
    						})
    		);
    		var userMenu = this.browser.makeGlobalMenu('user');
    		loginButton = new dijitDropDownButton(
    				{ className: 'user',
    					innerHTML: '' + username,
    					title: 'user logged in: UserName',
    					dropDown: userMenu
    				});
    		// if add 'menu' class, button will be placed on left side of menubar instead (because of 'float: left'
    		//     styling in CSS rule for 'menu' class
    		// dojo.addClass( loginButton.domNode, 'menu' );
    	}
    	else  {
    		loginButton = new dijitButton(
    				{ className: 'login',
    					innerHTML: "Login",
    					onClick: function()  {
    						webapollo.getAnnotTrack().login();
    					}
    				});
    	}
    	this.browser.menuBar.appendChild( loginButton.domNode );
    	this.loginMenuInitialized = true;
    },

    getAnnotTrack: function()  {
        if (this.browser && this.browser.view && this.browser.view.tracks)  {
            var tracks = this.browser.view.tracks;
            for (var i = 0; i < tracks.length; i++)  {
	        // should be doing instanceof here, but class setup is not being cooperative
                if (tracks[i].isWebApolloAnnotTrack)  {
                    console.log("annot track refseq: " + tracks[i].refSeq.name);
                    return tracks[i];
                }
            }
        }
        return null;
    }

});

});

});<commit_msg>Updated link to help page from Help button<commit_after>require({
           packages: [
               { name: 'jqueryui', location: '../plugins/WebApollo/jslib/jqueryui' },
               { name: 'jquery', location: '../plugins/WebApollo/jslib/jquery', main: 'jquery' }
           ]
       },
       [],
       function() {

define.amd.jQuery = true;

define(
       [
           'dojo/_base/declare',
           'dijit/MenuItem',
           'dijit/CheckedMenuItem',
           'dijit/form/DropDownButton',
           'dijit/DropDownMenu',
           'dijit/form/Button',
           'JBrowse/Plugin',
           './FeatureEdgeMatchManager',
	   './FeatureSelectionManager',
	   './View/Track/AnnotTrack'
       ],
    function( declare, dijitMenuItem, dijitCheckedMenuItem, dijitDropDownButton, dijitDropDownMenu, dijitButton, JBPlugin,
              FeatureEdgeMatchManager, FeatureSelectionManager, AnnotTrack ) {

return declare( JBPlugin,
{
//    colorCdsByFrame: false,
//    searchMenuInitialized: false,

    constructor: function( args ) {
        var thisB = this;
        this.colorCdsByFrame = false;
        this.searchMenuInitialized = false;
        var browser = this.browser;  // this.browser set in Plugin superclass constructor

        if (! browser.config.helpUrl)  {
	    browser.config.helpUrl = "http://genomearchitect.org/webapollo/docs/help.html";
        }

        // hand the browser object to the feature edge match manager
        FeatureEdgeMatchManager.setBrowser( browser );

	this.featSelectionManager = new FeatureSelectionManager();
	this.annotSelectionManager = new FeatureSelectionManager();

	// setting up selection exclusiveOr --
	//    if selection is made in annot track, any selection in other tracks is deselected, and vice versa,
	//    regardless of multi-select mode etc.
	this.annotSelectionManager.addMutualExclusion(this.featSelectionManager);
	this.featSelectionManager.addMutualExclusion(this.annotSelectionManager);

	FeatureEdgeMatchManager.addSelectionManager(this.featSelectionManager);
	FeatureEdgeMatchManager.addSelectionManager(this.annotSelectionManager);

        // add a global menu option for setting CDS color
        var cds_frame_toggle = new dijitCheckedMenuItem(
                {
                    label: "Color by CDS frame",
                    checked: false,
                    onClick: function(event) {
                        thisB.colorCdsByFrame = cds_frame_toggle.checked;
                        browser.view.redrawTracks();
                    }
                });
        browser.addGlobalMenuItem( 'options', cds_frame_toggle );
        var plus_strand_toggle = new dijitCheckedMenuItem(
                {
                    label: "Show plus strand",
                    checked: true,
                    onClick: function(event) {
                        var plus = plus_strand_toggle.checked;
                        var minus = minus_strand_toggle.checked;
                        console.log("plus: ", plus, " minus: ", minus);
                        if (plus && minus)  {
                            browser.view.featureFilter = browser.view.passAllFilter;
                        }
                        else if (plus)  {
                            browser.view.featureFilter = browser.view.plusStrandFilter;
                        }
                        else if (minus)  {
                            browser.view.featureFilter = browser.view.minusStrandFilter;
                        }
                        else  {
                            browser.view.featureFilter = browser.view.passNoneFilter;
                        }
                        // browser.view.redrawTracks();
                        thisB.redoLayout();
                    }
                });
        browser.addGlobalMenuItem( 'options', plus_strand_toggle );
        var minus_strand_toggle = new dijitCheckedMenuItem(
                {
                    label: "Show minus strand",
                    checked: true,
                    onClick: function(event) {
                        var plus = plus_strand_toggle.checked;
                        var minus = minus_strand_toggle.checked;
                        console.log("plus: ", plus, " minus: ", minus);
                        if (plus && minus)  {
                            browser.view.featureFilter = browser.view.passAllFilter;
                        }
                        else if (plus)  {
                            browser.view.featureFilter = browser.view.plusStrandFilter;
                        }
                        else if (minus)  {
                            browser.view.featureFilter = browser.view.minusStrandFilter;
                        }
                        else  {
                            browser.view.featureFilter = browser.view.passNoneFilter;
                        }
                        // browser.view.redrawTracks();
                        thisB.redoLayout();
                    }
                });
        browser.addGlobalMenuItem( 'options', minus_strand_toggle );

        // register the WebApollo track types with the browser, so
        // that the open-file dialog and other things will have them
        // as options
        browser.registerTrackType({
            type:                 'WebApollo/View/Track/DraggableHTMLFeatures',
            defaultForStoreTypes: [ 'JBrowse/Store/SeqFeature/NCList',
                                    'JBrowse/Store/SeqFeature/GFF3'
                                  ],
            label: 'WebApollo Features'
        });
        browser.registerTrackType({
            type:                 'WebApollo/View/Track/DraggableAlignments',
            defaultForStoreTypes: [
                                    'JBrowse/Store/SeqFeature/BAM',
                                  ],
            label: 'WebApollo Alignments'
        });
        browser.registerTrackType({
            type:                 'WebApollo/View/Track/SequenceTrack',
            defaultForStoreTypes: [ 'JBrowse/Store/Sequence/StaticChunked' ],
            label: 'WebApollo Sequence'
        });

        // put the WebApollo logo in the powered_by place in the main JBrowse bar
        browser.afterMilestone( 'initView', function() {
            browser.poweredByLink.innerHTML = '';
            browser.poweredByLink.href = 'http://www.gmod.org/wiki/WebApollo';
            browser.poweredByLink.target = "_blank";
        });

    },

    // would rather call view.redrawTracks()
    //
    // BUT, view.redrawTracks currently doesn't force relayout
    //     browser.view.redrawTracks();
    // track.changed() forces relayout (at least for HTMLFeatures)
    //    but also call changeCallBack(), which currently is always view.showVisibleBlocks()
    //    thus will needlessly call view.showVisibleBlocks() repeatedly
    // so trying for now to be explicit
    redoLayout: function()  {
        this.browser.view.trackIterate( function(t) {
                                       t.hideAll();
                                       if (t._clearLayout)  {
                                           // console.log("clearing layout for track: " + t.label);
                                           t._clearLayout();
                                       }
                                   }
                                 );
        this.browser.view.showVisibleBlocks(true);
    },

/**
 * hacking addition of a "tools" menu to standard JBrowse menubar,
 *    with a "Search Sequence" dropdown
 */
    initSearchMenu: function()  {
        if (! this.searchMenuInitialized) {
            var webapollo = this;
            this.browser.addGlobalMenuItem( 'tools',
                                            new dijitMenuItem(
                                                {
		                                    label: "Search sequence",
		                                    onClick: function() {
		                                        webapollo.getAnnotTrack().searchSequence();
		                                    }
                                                }) );
            var toolMenu = this.browser.makeGlobalMenu('tools');
            if( toolMenu ) {
                var toolButton = new dijitDropDownButton(
                    { className: 'file',
                      innerHTML: 'Tools',
                      //title: '',
                      dropDown: toolMenu
                    });
                dojo.addClass( toolButton.domNode, 'menu' );
                this.browser.menuBar.appendChild( toolButton.domNode );
            }
        }
        this.searchMenuInitialized = true;
    },

    initLoginMenu: function(username) {
    	var webapollo = this;
    	var loginButton;
    	if (username)  {   // permission only set if permission request succeeded
    		this.browser.addGlobalMenuItem( 'user',
    				new dijitMenuItem(
    						{
    							label: 'Logout',
    							onClick: function()  {
    								webapollo.getAnnotTrack().logout();
    							}
    						})
    		);
    		var userMenu = this.browser.makeGlobalMenu('user');
    		loginButton = new dijitDropDownButton(
    				{ className: 'user',
    					innerHTML: '' + username,
    					title: 'user logged in: UserName',
    					dropDown: userMenu
    				});
    		// if add 'menu' class, button will be placed on left side of menubar instead (because of 'float: left'
    		//     styling in CSS rule for 'menu' class
    		// dojo.addClass( loginButton.domNode, 'menu' );
    	}
    	else  {
    		loginButton = new dijitButton(
    				{ className: 'login',
    					innerHTML: "Login",
    					onClick: function()  {
    						webapollo.getAnnotTrack().login();
    					}
    				});
    	}
    	this.browser.menuBar.appendChild( loginButton.domNode );
    	this.loginMenuInitialized = true;
    },

    getAnnotTrack: function()  {
        if (this.browser && this.browser.view && this.browser.view.tracks)  {
            var tracks = this.browser.view.tracks;
            for (var i = 0; i < tracks.length; i++)  {
	        // should be doing instanceof here, but class setup is not being cooperative
                if (tracks[i].isWebApolloAnnotTrack)  {
                    console.log("annot track refseq: " + tracks[i].refSeq.name);
                    return tracks[i];
                }
            }
        }
        return null;
    }

});

});

});<|endoftext|>


<commit_before>var Stream = require('stream'),
    gm = require('gm'),
    isGmOperationByName = {},
    filterConstructorByOperationName = {};

['PngQuant', 'PngCrush', 'OptiPng', 'JpegTran', 'Inkscape', 'SvgFilter'].forEach(function (constructorName) {
    try {
        filterConstructorByOperationName[constructorName.toLowerCase()] = require(constructorName.toLowerCase());
    } catch (e) {
        // SvgFilter might fail because of failed contextify installation on windows.
        // Dependency chain to contextify: svgfilter --> assetgraph --> jsdom --> contextify
    }
});

Object.keys(gm.prototype).forEach(function (propertyName) {
    if (!/^_|^(?:size|orientation|format|depth|color|res|filesize|identity|write|stream)$/.test(propertyName) &&
        typeof gm.prototype[propertyName] === 'function') {
        isGmOperationByName[propertyName] = true;
    }
});

module.exports = function getFilterInfosAndTargetContentTypeFromQueryString(queryString, rootPath, sourceFilePath) {
    var filterInfos = [],
        gmOperations = [],
        operationNames = [],
        usedQueryStringFragments = [],
        leftOverQueryStringFragments = [],
        targetContentType;

    function flushGmOperations() {
        if (gmOperations.length > 0) {
            var gmOperationsForThisInstance = [].concat(gmOperations);
            operationNames.push('gm');
            filterInfos.push({
                operationName: 'gm',
                usedQueryStringFragments: gmOperations.map(function (gmOperation) {
                    return gmOperation.usedQueryStringFragment;
                }),
                create: function () {
                    // For some reason the gm module doesn't expose itself as a readable/writable stream,
                    // so we need to wrap it into one:

                    var readStream = new Stream();
                    readStream.readable = true;

                    var readWriteStream = new Stream();
                    readWriteStream.readable = readWriteStream.writable = true;
                    var spawned = false;

                    readWriteStream.write = function (chunk) {
                        if (!spawned) {
                            spawned = true;
                            var gmInstance = gm(readStream);
                            gmOperationsForThisInstance.forEach(function (gmOperation) {
                                gmInstance = gmInstance[gmOperation.name].apply(gmInstance, gmOperation.args);
                            });
                            var seenData = false,
                                hasEnded = false;
                            gmInstance.stream(function (err, stdout, stderr) {
                                if (err) {
                                    hasEnded = true;
                                    return readWriteStream.emit('error', err);
                                }
                                stdout.on('data', function (chunk) {
                                    seenData = true;
                                    readWriteStream.emit('data', chunk);
                                }).on('end', function () {
                                    if (!hasEnded) {
                                        if (seenData) {
                                            readWriteStream.emit('end');
                                        } else {
                                            readWriteStream.emit('error', new Error('The gm stream ended without emitting any data'));
                                        }
                                        hasEnded = true;
                                    }
                                });
                            });
                        }
                        readStream.emit('data', chunk);
                    };
                    readWriteStream.end = function (chunk) {
                        if (chunk) {
                            readWriteStream.write(chunk);
                        }
                        readStream.emit('end');
                    };
                    return readWriteStream;
                }
            });
            gmOperations = [];
        }
    }

    queryString.split('&').forEach(function (keyValuePair) {
        var matchKeyValuePair = keyValuePair.match(/^([^=]+)(?:=(.*))?/);
        if (matchKeyValuePair) {
            var operationName = decodeURIComponent(matchKeyValuePair[1]),
                // Split by non-URL encoded comma or plus:
                operationArgs = matchKeyValuePair[2] ? matchKeyValuePair[2].split(/[\+,]/).map(function (arg) {
                    arg = decodeURIComponent(arg);
                    if (/^\d+$/.test(arg)) {
                        return parseInt(arg, 10);
                    } else if (arg === 'true') {
                        return true;
                    } else if (arg === 'false') {
                        return false;
                    } else {
                        return arg;
                    }
                }) : [];

            if (isGmOperationByName[operationName]) {
                if (operationName === 'setFormat' && operationArgs.length > 0) {
                    var targetFormat = operationArgs[0].toLowerCase();
                    if (targetFormat === 'jpg') {
                        targetFormat = 'jpeg';
                    }
                    targetContentType = 'image/' + targetFormat;
                }
                gmOperations.push({name: operationName, args: operationArgs, usedQueryStringFragment: keyValuePair});
                usedQueryStringFragments.push(keyValuePair);
            } else {
                var operationNameLowerCase = operationName.toLowerCase(),
                    FilterConstructor = filterConstructorByOperationName[operationNameLowerCase];
                if (FilterConstructor) {
                    operationNames.push(operationNameLowerCase);
                    flushGmOperations();
                    if (operationNameLowerCase === 'svgfilter' && rootPath && sourceFilePath) {
                        operationArgs.push('--root', 'file://' + rootPath, '--url', 'file://' + sourceFilePath);
                    }
                    filterInfos.push({
                        create: function () {
                            return new FilterConstructor(operationArgs);
                        },
                        operationName: operationNameLowerCase,
                        usedQueryStringFragments: [keyValuePair]
                    });
                    usedQueryStringFragments.push(keyValuePair);
                    if (operationNameLowerCase === 'inkscape') {
                        targetContentType = 'image/' + filterInfos.outputFormat;
                    }
                } else {
                    leftOverQueryStringFragments.push(keyValuePair);
                }
            }
        }
    });
    flushGmOperations();

    return {
        targetContentType: targetContentType,
        operationNames: operationNames,
        filterInfos: filterInfos,
        usedQueryStringFragments: usedQueryStringFragments,
        leftOverQueryStringFragments: leftOverQueryStringFragments
    };
};
<commit_msg>Unbreak targetContentType when inkscape is involved.<commit_after>var Stream = require('stream'),
    gm = require('gm'),
    isGmOperationByName = {},
    filterConstructorByOperationName = {};

['PngQuant', 'PngCrush', 'OptiPng', 'JpegTran', 'Inkscape', 'SvgFilter'].forEach(function (constructorName) {
    try {
        filterConstructorByOperationName[constructorName.toLowerCase()] = require(constructorName.toLowerCase());
    } catch (e) {
        // SvgFilter might fail because of failed contextify installation on windows.
        // Dependency chain to contextify: svgfilter --> assetgraph --> jsdom --> contextify
    }
});

Object.keys(gm.prototype).forEach(function (propertyName) {
    if (!/^_|^(?:size|orientation|format|depth|color|res|filesize|identity|write|stream)$/.test(propertyName) &&
        typeof gm.prototype[propertyName] === 'function') {
        isGmOperationByName[propertyName] = true;
    }
});

module.exports = function getFilterInfosAndTargetContentTypeFromQueryString(queryString, rootPath, sourceFilePath) {
    var filterInfos = [],
        gmOperations = [],
        operationNames = [],
        usedQueryStringFragments = [],
        leftOverQueryStringFragments = [],
        targetContentType;

    function flushGmOperations() {
        if (gmOperations.length > 0) {
            var gmOperationsForThisInstance = [].concat(gmOperations);
            operationNames.push('gm');
            filterInfos.push({
                operationName: 'gm',
                usedQueryStringFragments: gmOperations.map(function (gmOperation) {
                    return gmOperation.usedQueryStringFragment;
                }),
                create: function () {
                    // For some reason the gm module doesn't expose itself as a readable/writable stream,
                    // so we need to wrap it into one:

                    var readStream = new Stream();
                    readStream.readable = true;

                    var readWriteStream = new Stream();
                    readWriteStream.readable = readWriteStream.writable = true;
                    var spawned = false;

                    readWriteStream.write = function (chunk) {
                        if (!spawned) {
                            spawned = true;
                            var gmInstance = gm(readStream);
                            gmOperationsForThisInstance.forEach(function (gmOperation) {
                                gmInstance = gmInstance[gmOperation.name].apply(gmInstance, gmOperation.args);
                            });
                            var seenData = false,
                                hasEnded = false;
                            gmInstance.stream(function (err, stdout, stderr) {
                                if (err) {
                                    hasEnded = true;
                                    return readWriteStream.emit('error', err);
                                }
                                stdout.on('data', function (chunk) {
                                    seenData = true;
                                    readWriteStream.emit('data', chunk);
                                }).on('end', function () {
                                    if (!hasEnded) {
                                        if (seenData) {
                                            readWriteStream.emit('end');
                                        } else {
                                            readWriteStream.emit('error', new Error('The gm stream ended without emitting any data'));
                                        }
                                        hasEnded = true;
                                    }
                                });
                            });
                        }
                        readStream.emit('data', chunk);
                    };
                    readWriteStream.end = function (chunk) {
                        if (chunk) {
                            readWriteStream.write(chunk);
                        }
                        readStream.emit('end');
                    };
                    return readWriteStream;
                }
            });
            gmOperations = [];
        }
    }

    queryString.split('&').forEach(function (keyValuePair) {
        var matchKeyValuePair = keyValuePair.match(/^([^=]+)(?:=(.*))?/);
        if (matchKeyValuePair) {
            var operationName = decodeURIComponent(matchKeyValuePair[1]),
                // Split by non-URL encoded comma or plus:
                operationArgs = matchKeyValuePair[2] ? matchKeyValuePair[2].split(/[\+,]/).map(function (arg) {
                    arg = decodeURIComponent(arg);
                    if (/^\d+$/.test(arg)) {
                        return parseInt(arg, 10);
                    } else if (arg === 'true') {
                        return true;
                    } else if (arg === 'false') {
                        return false;
                    } else {
                        return arg;
                    }
                }) : [];

            if (isGmOperationByName[operationName]) {
                if (operationName === 'setFormat' && operationArgs.length > 0) {
                    var targetFormat = operationArgs[0].toLowerCase();
                    if (targetFormat === 'jpg') {
                        targetFormat = 'jpeg';
                    }
                    targetContentType = 'image/' + targetFormat;
                }
                gmOperations.push({name: operationName, args: operationArgs, usedQueryStringFragment: keyValuePair});
                usedQueryStringFragments.push(keyValuePair);
            } else {
                var operationNameLowerCase = operationName.toLowerCase(),
                    FilterConstructor = filterConstructorByOperationName[operationNameLowerCase];
                if (FilterConstructor) {
                    operationNames.push(operationNameLowerCase);
                    flushGmOperations();
                    if (operationNameLowerCase === 'svgfilter' && rootPath && sourceFilePath) {
                        operationArgs.push('--root', 'file://' + rootPath, '--url', 'file://' + sourceFilePath);
                    }
                    var filterInfo = {
                        create: function () {
                            return new FilterConstructor(operationArgs);
                        },
                        operationName: operationNameLowerCase,
                        usedQueryStringFragments: [keyValuePair]
                    };
                    filterInfos.push(filterInfo);
                    usedQueryStringFragments.push(keyValuePair);
                    if (operationNameLowerCase === 'inkscape') {
                        var filter = filterInfo.create();
                        filterInfo.create = function () {
                            return filter;
                        };
                        targetContentType = 'image/' + filter.outputFormat;
                    }
                } else {
                    leftOverQueryStringFragments.push(keyValuePair);
                }
            }
        }
    });
    flushGmOperations();

    return {
        targetContentType: targetContentType,
        operationNames: operationNames,
        filterInfos: filterInfos,
        usedQueryStringFragments: usedQueryStringFragments,
        leftOverQueryStringFragments: leftOverQueryStringFragments
    };
};
<|endoftext|>


<commit_before>const canvas = document.body.appendChild(document.createElement('canvas'))
const fit = require('canvas-fit')
const regl = require('../regl')({canvas: canvas})
const mat4 = require('gl-mat4')
const vec3 = require('gl-vec3')
window.addEventListener('resize', fit(canvas), false)
const bunny = require('bunny')
const normals = require('angle-normals')
var mp = require('mouse-position')(canvas)
var mb = require('mouse-pressed')(canvas)

var viewMatrix = new Float32Array([1, -0, 0, 0, 0, 0.876966655254364, 0.48055124282836914, 0, -0, -0.48055124282836914, 0.876966655254364, 0, 0, 0, -11.622776985168457, 1])
var projectionMatrix = new Float32Array(16)

var lightDir = [0.39, 0.87, 0.29]

const planeElements = []
var planePosition = []
var planeNormal = []

// A slightly modified version of this code:
// https://github.com/substack/ray-triangle-intersection
// It does intersection between ray and triangle.
function intersectTriangle (out, pt, dir, tri) {
  var EPSILON = 0.000001
  var edge1 = [0, 0, 0]
  var edge2 = [0, 0, 0]
  var tvec = [0, 0, 0]
  var pvec = [0, 0, 0]
  var qvec = [0, 0, 0]

  vec3.subtract(edge1, tri[1], tri[0])
  vec3.subtract(edge2, tri[2], tri[0])

  vec3.cross(pvec, dir, edge2)
  var det = vec3.dot(edge1, pvec)

  if (det < EPSILON) return null
  vec3.subtract(tvec, pt, tri[0])
  var u = vec3.dot(tvec, pvec)
  if (u < 0 || u > det) return null
  vec3.cross(qvec, tvec, edge1)
  var v = vec3.dot(dir, qvec)
  if (v < 0 || u + v > det) return null

  var t = vec3.dot(edge2, qvec) / det
  out[0] = pt[0] + t * dir[0]
  out[1] = pt[1] + t * dir[1]
  out[2] = pt[2] + t * dir[2]
  return t
}

planePosition.push([-0.5, 0.0, -0.5])
planePosition.push([+0.5, 0.0, -0.5])
planePosition.push([-0.5, 0.0, +0.5])
planePosition.push([+0.5, 0.0, +0.5])

planeNormal.push([0.0, 1.0, 0.0])
planeNormal.push([0.0, 1.0, 0.0])
planeNormal.push([0.0, 1.0, 0.0])
planeNormal.push([0.0, 1.0, 0.0])

planeElements.push([3, 1, 0])
planeElements.push([0, 2, 3])

var boxPosition = [
  // side faces
  [-0.5, +0.5, +0.5], [+0.5, +0.5, +0.5], [+0.5, -0.5, +0.5], [-0.5, -0.5, +0.5], // positive z face.
  [+0.5, +0.5, +0.5], [+0.5, +0.5, -0.5], [+0.5, -0.5, -0.5], [+0.5, -0.5, +0.5], // positive x face
  [+0.5, +0.5, -0.5], [-0.5, +0.5, -0.5], [-0.5, -0.5, -0.5], [+0.5, -0.5, -0.5], // negative z face
  [-0.5, +0.5, -0.5], [-0.5, +0.5, +0.5], [-0.5, -0.5, +0.5], [-0.5, -0.5, -0.5], // negative x face.
  [-0.5, +0.5, -0.5], [+0.5, +0.5, -0.5], [+0.5, +0.5, +0.5], [-0.5, +0.5, +0.5],  // top face
  [-0.5, -0.5, -0.5], [+0.5, -0.5, -0.5], [+0.5, -0.5, +0.5], [-0.5, -0.5, +0.5]  // bottom face
]

const boxElements = [
  [2, 1, 0], [2, 0, 3],
  [6, 5, 4], [6, 4, 7],
  [10, 9, 8], [10, 8, 11],
  [14, 13, 12], [14, 12, 15],
  [18, 17, 16], [18, 16, 19],
  [20, 21, 22], [23, 20, 22]
]

// all the normals of a single block.
var boxNormal = [
  // side faces
  [0.0, 0.0, +1.0], [0.0, 0.0, +1.0], [0.0, 0.0, +1.0], [0.0, 0.0, +1.0],
  [+1.0, 0.0, 0.0], [+1.0, 0.0, 0.0], [+1.0, 0.0, 0.0], [+1.0, 0.0, 0.0],
  [0.0, 0.0, -1.0], [0.0, 0.0, -1.0], [0.0, 0.0, -1.0], [0.0, 0.0, -1.0],
  [-1.0, 0.0, 0.0], [-1.0, 0.0, 0.0], [-1.0, 0.0, 0.0], [-1.0, 0.0, 0.0],
  // top
  [0.0, +1.0, 0.0], [0.0, +1.0, 0.0], [0.0, +1.0, 0.0], [0.0, +1.0, 0.0],
  // bottom
  [0.0, -1.0, 0.0], [0.0, -1.0, 0.0], [0.0, -1.0, 0.0], [0.0, -1.0, 0.0]
]

const globalScope = regl({
  uniforms: {
    lightDir: lightDir,
    view: () => viewMatrix,
    projection: ({viewportWidth, viewportHeight}) =>
      mat4.perspective(projectionMatrix,
                       Math.PI / 4,
                       viewportWidth / viewportHeight,
                       0.01,
                       1000)
  }
})

// render the object with lighting, using the previously rendered cubemap
// to render the shadows.
const drawNormal = regl({
  frag: `
  precision mediump float;

  varying vec3 vNormal;
  varying vec3 vPosition;

  uniform float ambientLightAmount;
  uniform float diffuseLightAmount;
  uniform vec3 color;
  uniform vec3 lightDir;

  void main () {
    vec3 ambient = ambientLightAmount * color;
    float cosTheta = dot(vNormal, lightDir);
    vec3 diffuse = diffuseLightAmount * color * clamp(cosTheta , 0.0, 1.0 );

    gl_FragColor = vec4((ambient + diffuse), 1.0);
  }`,
  vert: `
  precision mediump float;

  attribute vec3 position;
  attribute vec3 normal;

  varying vec3 vPosition;
  varying vec3 vNormal;

  uniform mat4 projection, view, model;

  void main() {
    vec4 worldSpacePosition = model * vec4(position, 1);

    vPosition = worldSpacePosition.xyz;
    vNormal = normal;

    gl_Position = projection * view * worldSpacePosition;
  }`
})

const drawOutline = regl({
  frag: `
  precision mediump float;

  void main () {
    gl_FragColor = vec4(vec3(1.0, 1.0, 0.0), 1.0);
  }`,
  vert: `
  precision mediump float;

  attribute vec3 position;
  attribute vec3 normal;

  uniform mat4 projection, view, model;
  uniform bool isRound;

  void main() {
    float s = 0.1;
    vec4 worldSpacePosition = model * vec4(
      isRound ? (position + normal * s) : (position * (0.5*s+1.0)),

      1);
    gl_Position = projection * view * worldSpacePosition;
  }`,

  depth: {
    enable: true,
    mask: false
  }
})

function Mesh (elements, position, normal) {
  this.elements = elements
  this.position = position
  this.normal = normal
}

function createModelMatrix (props) {
  var m = mat4.identity([])

  mat4.translate(m, m, props.translate)

  var s = props.scale
  mat4.scale(m, m, [s, s, s])

  return m
}

Mesh.prototype.draw = regl({
  uniforms: {
    model: (_, props, batchId) => {
      return createModelMatrix(props)
    },
    ambientLightAmount: 0.3,
    diffuseLightAmount: 0.7,
    color: regl.prop('color'),
    isRound: regl.prop('isRound')

  },
  attributes: {
    position: regl.this('position'),
    normal: regl.this('normal')
  },
  elements: regl.this('elements'),
  cull: {
    enable: true
  }
})

var bunnyMesh = new Mesh(bunny.cells, bunny.positions, normals(bunny.cells, bunny.positions))
var boxMesh = new Mesh(boxElements, boxPosition, boxNormal)
var planeMesh = new Mesh(planeElements, planePosition, planeNormal)

var meshes = [
  {scale: 80.0, translate: [0.0, 0.0, 0.0], color: [0.5, 0.5, 0.5], mesh: planeMesh},

  {scale: 0.2, translate: [0.0, 0.0, 0.0], color: [0.6, 0.0, 0.0], mesh: bunnyMesh},

  {scale: 2.0, translate: [4.0, 0.0, 0.0], color: [0.6, 0.0, 0.0], mesh: boxMesh},
  {scale: 1.3, translate: [-3.0, 0.0, -4.0], color: [0.0, 0.6, 0.0], mesh: boxMesh},
  {scale: 0.7, translate: [-3.0, 4.0, 4.0], color: [0.0, 0.0, 0.8], mesh: boxMesh}
]

var selectedMesh = -1

mb.on('down', function () {
  var vp = mat4.multiply([], projectionMatrix, viewMatrix)
  var invVp = mat4.invert([], vp)

  var rayPoint = vec3.transformMat4([], [2.0 * mp.x / canvas.width - 1.0, -2.0 * mp.y / canvas.height + 1.0, 0.0], invVp)

  var rayOrigin = vec3.transformMat4([], [0, 0, 0], mat4.invert([], viewMatrix))

  var rayDir = vec3.normalize([], vec3.subtract([], rayPoint, rayOrigin))

  var minT = 10000000.0
  for (var i = 0; i < meshes.length; i++) {
    var m = meshes[i]

    var modelMatrix = createModelMatrix(m)

    for (var j = 0; j < m.mesh.elements.length; j++) {
      if (m.mesh === planeMesh) {
        continue // we don't allow clicking the plane mesh.
      }
      var f = m.mesh.elements[j]
      var tri =
          [vec3.transformMat4([], m.mesh.position[f[0]], modelMatrix),
           vec3.transformMat4([], m.mesh.position[f[1]], modelMatrix),
           vec3.transformMat4([], m.mesh.position[f[2]], modelMatrix)
      ]
      var res = []
      var t = intersectTriangle(res, rayPoint, rayDir, tri)
      if (t !== null) {
        if (t < minT) {
          // mesh was closer than any object thus far.
          // for the time being, make it the selected object.
          minT = t
          selectedMesh = i
          break
        }
      }
    }
  }
})

regl.frame(({tick}) => {
  regl.clear({
    color: [0, 0, 0, 255],
    depth: 1
  })

  globalScope(() => {
    for (var i = 0; i < meshes.length; i++) {
      var m = meshes[i]

      // draw outline of selected object.
      if (i === selectedMesh) {
        drawOutline(() => {
          m.isRound = (m.mesh !== boxMesh)
          m.mesh.draw(m)
        })
      }

      // then draw object normally.
      drawNormal(() => {
        m.mesh.draw(m)
      })
    }
  })
})
<commit_msg>example/raycast.js: Add simple camera.<commit_after>const canvas = document.body.appendChild(document.createElement('canvas'))
const fit = require('canvas-fit')
const regl = require('../regl')({canvas: canvas})
const mat4 = require('gl-mat4')
const vec3 = require('gl-vec3')
window.addEventListener('resize', fit(canvas), false)
const bunny = require('bunny')
const normals = require('angle-normals')
var mp = require('mouse-position')(canvas)
var mb = require('mouse-pressed')(canvas)

var viewMatrix = new Float32Array([1, -0, 0, 0, 0, 0.876966655254364, 0.48055124282836914, 0, -0, -0.48055124282836914, 0.876966655254364, 0, 0, 0, -11.622776985168457, 1])
var projectionMatrix = new Float32Array(16)

var lightDir = [0.39, 0.87, 0.29]

const planeElements = []
var planePosition = []
var planeNormal = []

// A slightly modified version of this code:
// https://github.com/substack/ray-triangle-intersection
// It does intersection between ray and triangle.
function intersectTriangle (out, pt, dir, tri) {
  var EPSILON = 0.000001
  var edge1 = [0, 0, 0]
  var edge2 = [0, 0, 0]
  var tvec = [0, 0, 0]
  var pvec = [0, 0, 0]
  var qvec = [0, 0, 0]

  vec3.subtract(edge1, tri[1], tri[0])
  vec3.subtract(edge2, tri[2], tri[0])

  vec3.cross(pvec, dir, edge2)
  var det = vec3.dot(edge1, pvec)

  if (det < EPSILON) return null
  vec3.subtract(tvec, pt, tri[0])
  var u = vec3.dot(tvec, pvec)
  if (u < 0 || u > det) return null
  vec3.cross(qvec, tvec, edge1)
  var v = vec3.dot(dir, qvec)
  if (v < 0 || u + v > det) return null

  var t = vec3.dot(edge2, qvec) / det
  out[0] = pt[0] + t * dir[0]
  out[1] = pt[1] + t * dir[1]
  out[2] = pt[2] + t * dir[2]
  return t
}

planePosition.push([-0.5, 0.0, -0.5])
planePosition.push([+0.5, 0.0, -0.5])
planePosition.push([-0.5, 0.0, +0.5])
planePosition.push([+0.5, 0.0, +0.5])

planeNormal.push([0.0, 1.0, 0.0])
planeNormal.push([0.0, 1.0, 0.0])
planeNormal.push([0.0, 1.0, 0.0])
planeNormal.push([0.0, 1.0, 0.0])

planeElements.push([3, 1, 0])
planeElements.push([0, 2, 3])

var boxPosition = [
  // side faces
  [-0.5, +0.5, +0.5], [+0.5, +0.5, +0.5], [+0.5, -0.5, +0.5], [-0.5, -0.5, +0.5], // positive z face.
  [+0.5, +0.5, +0.5], [+0.5, +0.5, -0.5], [+0.5, -0.5, -0.5], [+0.5, -0.5, +0.5], // positive x face
  [+0.5, +0.5, -0.5], [-0.5, +0.5, -0.5], [-0.5, -0.5, -0.5], [+0.5, -0.5, -0.5], // negative z face
  [-0.5, +0.5, -0.5], [-0.5, +0.5, +0.5], [-0.5, -0.5, +0.5], [-0.5, -0.5, -0.5], // negative x face.
  [-0.5, +0.5, -0.5], [+0.5, +0.5, -0.5], [+0.5, +0.5, +0.5], [-0.5, +0.5, +0.5],  // top face
  [-0.5, -0.5, -0.5], [+0.5, -0.5, -0.5], [+0.5, -0.5, +0.5], [-0.5, -0.5, +0.5]  // bottom face
]

const boxElements = [
  [2, 1, 0], [2, 0, 3],
  [6, 5, 4], [6, 4, 7],
  [10, 9, 8], [10, 8, 11],
  [14, 13, 12], [14, 12, 15],
  [18, 17, 16], [18, 16, 19],
  [20, 21, 22], [23, 20, 22]
]

// all the normals of a single block.
var boxNormal = [
  // side faces
  [0.0, 0.0, +1.0], [0.0, 0.0, +1.0], [0.0, 0.0, +1.0], [0.0, 0.0, +1.0],
  [+1.0, 0.0, 0.0], [+1.0, 0.0, 0.0], [+1.0, 0.0, 0.0], [+1.0, 0.0, 0.0],
  [0.0, 0.0, -1.0], [0.0, 0.0, -1.0], [0.0, 0.0, -1.0], [0.0, 0.0, -1.0],
  [-1.0, 0.0, 0.0], [-1.0, 0.0, 0.0], [-1.0, 0.0, 0.0], [-1.0, 0.0, 0.0],
  // top
  [0.0, +1.0, 0.0], [0.0, +1.0, 0.0], [0.0, +1.0, 0.0], [0.0, +1.0, 0.0],
  // bottom
  [0.0, -1.0, 0.0], [0.0, -1.0, 0.0], [0.0, -1.0, 0.0], [0.0, -1.0, 0.0]
]

const globalScope = regl({
  uniforms: {
    lightDir: lightDir,
    view: (context, {eye}) => mat4.lookAt(viewMatrix,
                            eye,
                            [0, 0, 0],
                            [0, 1, 0]),
    projection: ({viewportWidth, viewportHeight}) =>
      mat4.perspective(projectionMatrix,
                       Math.PI / 4,
                       viewportWidth / viewportHeight,
                       0.01,
                       1000)
  }
})

// render the object with lighting, using the previously rendered cubemap
// to render the shadows.
const drawNormal = regl({
  frag: `
  precision mediump float;

  varying vec3 vNormal;
  varying vec3 vPosition;

  uniform float ambientLightAmount;
  uniform float diffuseLightAmount;
  uniform vec3 color;
  uniform vec3 lightDir;

  void main () {
    vec3 ambient = ambientLightAmount * color;
    float cosTheta = dot(vNormal, lightDir);
    vec3 diffuse = diffuseLightAmount * color * clamp(cosTheta , 0.0, 1.0 );

    gl_FragColor = vec4((ambient + diffuse), 1.0);
  }`,
  vert: `
  precision mediump float;

  attribute vec3 position;
  attribute vec3 normal;

  varying vec3 vPosition;
  varying vec3 vNormal;

  uniform mat4 projection, view, model;

  void main() {
    vec4 worldSpacePosition = model * vec4(position, 1);

    vPosition = worldSpacePosition.xyz;
    vNormal = normal;

    gl_Position = projection * view * worldSpacePosition;
  }`
})

const drawOutline = regl({
  frag: `
  precision mediump float;

  void main () {
    gl_FragColor = vec4(vec3(1.0, 1.0, 0.0), 1.0);
  }`,
  vert: `
  precision mediump float;

  attribute vec3 position;
  attribute vec3 normal;

  uniform mat4 projection, view, model;
  uniform bool isRound;

  void main() {
    float s = 0.1;
    vec4 worldSpacePosition = model * vec4(
      isRound ? (position + normal * s) : (position * (0.5*s+1.0)),

      1);
    gl_Position = projection * view * worldSpacePosition;
  }`,

  depth: {
    enable: true,
    mask: false
  }
})

function Mesh (elements, position, normal) {
  this.elements = elements
  this.position = position
  this.normal = normal
}

function createModelMatrix (props) {
  var m = mat4.identity([])

  mat4.translate(m, m, props.translate)

  var s = props.scale
  mat4.scale(m, m, [s, s, s])

  return m
}

Mesh.prototype.draw = regl({
  uniforms: {
    model: (_, props, batchId) => {
      return createModelMatrix(props)
    },
    ambientLightAmount: 0.3,
    diffuseLightAmount: 0.7,
    color: regl.prop('color'),
    isRound: regl.prop('isRound')

  },
  attributes: {
    position: regl.this('position'),
    normal: regl.this('normal')
  },
  elements: regl.this('elements'),
  cull: {
    enable: true
  }
})

var bunnyMesh = new Mesh(bunny.cells, bunny.positions, normals(bunny.cells, bunny.positions))
var boxMesh = new Mesh(boxElements, boxPosition, boxNormal)
var planeMesh = new Mesh(planeElements, planePosition, planeNormal)

var meshes = [
  {scale: 80.0, translate: [0.0, 0.0, 0.0], color: [0.5, 0.5, 0.5], mesh: planeMesh},

  {scale: 0.2, translate: [0.0, 0.0, 0.0], color: [0.6, 0.0, 0.0], mesh: bunnyMesh},

  {scale: 2.0, translate: [4.0, 0.0, 0.0], color: [0.6, 0.0, 0.0], mesh: boxMesh},
  {scale: 1.3, translate: [-3.0, 0.0, -4.0], color: [0.0, 0.6, 0.0], mesh: boxMesh},
  {scale: 0.7, translate: [-3.0, 4.0, 4.0], color: [0.0, 0.0, 0.8], mesh: boxMesh}
]

var selectedMesh = -1

mb.on('down', function () {
  var vp = mat4.multiply([], projectionMatrix, viewMatrix)
  var invVp = mat4.invert([], vp)

  var rayPoint = vec3.transformMat4([], [2.0 * mp.x / canvas.width - 1.0, -2.0 * mp.y / canvas.height + 1.0, 0.0], invVp)

  var rayOrigin = vec3.transformMat4([], [0, 0, 0], mat4.invert([], viewMatrix))

  var rayDir = vec3.normalize([], vec3.subtract([], rayPoint, rayOrigin))

  var minT = 10000000.0
  for (var i = 0; i < meshes.length; i++) {
    var m = meshes[i]

    var modelMatrix = createModelMatrix(m)

    for (var j = 0; j < m.mesh.elements.length; j++) {
      if (m.mesh === planeMesh) {
        continue // we don't allow clicking the plane mesh.
      }
      var f = m.mesh.elements[j]
      var tri =
          [vec3.transformMat4([], m.mesh.position[f[0]], modelMatrix),
           vec3.transformMat4([], m.mesh.position[f[1]], modelMatrix),
           vec3.transformMat4([], m.mesh.position[f[2]], modelMatrix)
      ]
      var res = []
      var t = intersectTriangle(res, rayPoint, rayDir, tri)
      if (t !== null) {
        if (t < minT) {
          // mesh was closer than any object thus far.
          // for the time being, make it the selected object.
          minT = t
          selectedMesh = i
          break
        }
      }
    }
  }
})

var prevMpX = mp.x
var theta = 0

regl.frame(({tick, viewportWidth}) => {
  regl.clear({
    color: [0, 0, 0, 255],
    depth: 1
  })

  if (mb.left) {
    theta += 2.0 * Math.PI * ((mp.x - prevMpX) / viewportWidth)
  }

  prevMpX = mp.x

  globalScope({eye: [
    13 * Math.sin(theta),
    8,
    13 * Math.cos(theta)

  ]}, () => {
    for (var i = 0; i < meshes.length; i++) {
      var m = meshes[i]

      // draw outline of selected object.
      if (i === selectedMesh) {
        drawOutline(() => {
          m.isRound = (m.mesh !== boxMesh)
          m.mesh.draw(m)
        })
      }

      // then draw object normally.
      drawNormal(() => {
        m.mesh.draw(m)
      })
    }
  })
})
<|endoftext|>


<commit_before><commit_msg>Update arena.js<commit_after><|endoftext|>


<commit_before><commit_msg>updated routes to provide contribution statics<commit_after><|endoftext|>


<commit_before><commit_msg>Added Javascript file to signup<commit_after><|endoftext|>