<commit_before>var tweets = {
    1: {
        name: 'Preethi Kasireddy',
        username: '@iam_preethi',
        text: 'When you hear a dogmatic opinion about some code, best practice or tool.. question it. Most things in programming are not so black & white.',
        time: '6:49 PM - 30 Oct 2016',
        retweets: 120,
        likes: 207,
        replies: []
    },
    2: {
        name: 'Preethi Kasireddy',
        username: '@iam_preethi',
        text: 'My favorite thing about programming is the never-ending, "mind-blown", euphoric moments you have from learning or realizing something new',
        time: '11:14 PM - 15 Oct 2016',
        retweets: 95,
        likes: 259,
        replies: []
    },
    3: {
        name: 'Preethi Kasireddy',
        username: '@iam_preethi',
        text: 'One of the hardest things as a programmer is knowing which rabbit holes are worth descending into',
        time: '5:21 AM - 7 Oct 2016',
        retweets: 521,
        likes: 945,
        replies: []
    },
    4: {
        name: 'Preethi Kasireddy',
        username: '@iam_preethi',
        text: 'If you\'re worried about what other people will think of you for doing X or Y, dont be. Most people are too busy thinking about themselves',
        time: '7:15 PM - 29 Jun 2016',
        retweets: 30,
        likes: 60,
        replies: []
    },
    5: {
        name: 'Sebastian Markbåge',
        username: '@sebmarkbage',
        text: 'My main goal with React isn\'t for the library/code to "win" but for the lessons learned not to go forgotten. That\'s surprisingly difficult.',
        time: '6:45 PM - 20 Oct 2016',
        retweets: 52,
        likes: 192,
        replies: []
    },
    6: {
        name: 'Sebastian Markbåge',
        username: '@sebmarkbage',
        text: 'Each pattern by itself can\'t be said to be "best practice" without the context of other patterns it needs to work with.',
        time: '8:23 PM - 20 Sep 2016',
        retweets: 10,
        likes: 24,
        replies: []
    },
    7: {
        name: 'Sebastian Markbåge',
        username: '@sebmarkbage',
        text: 'Server-side only doesn\'t work because high latency/fragile updates. Downloading JS on-demand doesn\'t work because slow to download+compile.',
        time: '5:37 AM - 16 Sep 2016',
        retweets: 2,
        likes: 13,
        replies: []
    },
    8: {
        name: 'Sebastian Markbåge',
        username: '@sebmarkbage',
        text: 'Don\'t confuse identifying a problem with solving a problem. There\'s a very long tail that follows.',
        time: '8:15 AM - 30 Aug 2016',
        retweets: 15,
        likes: 40,
        replies: []
    },
    9: {
        name: 'Dan Abramov',
        username: '@dan_abramov',
        text: 'Code is not just a static snapshot of the requirements and tradeoffs, but a living collaborative document pulled into many directions.',
        time: '5:29 PM - 31 Oct 2016',
        retweets: 27,
        likes: 63,
        replies: []
    },
    10: {
        name: 'Dan Abramov',
        username: '@dan_abramov',
        text: 'Don\'t write "flexible" modules. No matter how you plan, you\'ll miss some future requirements. Write modules that are easy to delete.',
        time: '5:15 PM - 31 Oct 2016',
        retweets: 339,
        likes: 534,
        replies: []
    },
    11: {
        name: 'Dan Abramov',
        username: '@dan_abramov',
        text: 'I like reaching that point when tests no longer express my wishes but help me discover and understand the system.',
        time: '4:06 PM - 28 Oct 2016',
        retweets: 9,
        likes: 70,
        replies: []
    },
    12: {
        name: 'Dan Abramov',
        username: '@dan_abramov',
        text: 'I feel very anxious when I approach new code so I add logging everywhere. This helps so much.',
        time: '10:37 PM - 13 Oct 2016',
        retweets: 16,
        likes: 112,
        replies: []
    },
};<commit_msg>Populated dummy replies<commit_after>var tweets = {
    1: {
        name: 'Preethi Kasireddy',
        username: '@iam_preethi',
        text: 'When you hear a dogmatic opinion about some code, best practice or tool.. question it. Most things in programming are not so black & white.',
        time: '6:49 PM - 30 Oct 2016',
        retweets: 120,
        likes: 207,
        replies: [4,8,5]
    },
    2: {
        name: 'Preethi Kasireddy',
        username: '@iam_preethi',
        text: 'My favorite thing about programming is the never-ending, "mind-blown", euphoric moments you have from learning or realizing something new',
        time: '11:14 PM - 15 Oct 2016',
        retweets: 95,
        likes: 259,
        replies: [9,3]
    },
    3: {
        name: 'Preethi Kasireddy',
        username: '@iam_preethi',
        text: 'One of the hardest things as a programmer is knowing which rabbit holes are worth descending into',
        time: '5:21 AM - 7 Oct 2016',
        retweets: 521,
        likes: 945,
        replies: [6]
    },
    4: {
        name: 'Preethi Kasireddy',
        username: '@iam_preethi',
        text: 'If you\'re worried about what other people will think of you for doing X or Y, dont be. Most people are too busy thinking about themselves',
        time: '7:15 PM - 29 Jun 2016',
        retweets: 30,
        likes: 60,
        replies: [10,7,11,2]
    },
    5: {
        name: 'Sebastian Markbåge',
        username: '@sebmarkbage',
        text: 'My main goal with React isn\'t for the library/code to "win" but for the lessons learned not to go forgotten. That\'s surprisingly difficult.',
        time: '6:45 PM - 20 Oct 2016',
        retweets: 52,
        likes: 192,
        replies: [1,12,8]
    },
    6: {
        name: 'Sebastian Markbåge',
        username: '@sebmarkbage',
        text: 'Each pattern by itself can\'t be said to be "best practice" without the context of other patterns it needs to work with.',
        time: '8:23 PM - 20 Sep 2016',
        retweets: 10,
        likes: 24,
        replies: [4,3]
    },
    7: {
        name: 'Sebastian Markbåge',
        username: '@sebmarkbage',
        text: 'Server-side only doesn\'t work because high latency/fragile updates. Downloading JS on-demand doesn\'t work because slow to download+compile.',
        time: '5:37 AM - 16 Sep 2016',
        retweets: 2,
        likes: 13,
        replies: [6]
    },
    8: {
        name: 'Sebastian Markbåge',
        username: '@sebmarkbage',
        text: 'Don\'t confuse identifying a problem with solving a problem. There\'s a very long tail that follows.',
        time: '8:15 AM - 30 Aug 2016',
        retweets: 15,
        likes: 40,
        replies: [10,9,7,2]
    },
    9: {
        name: 'Dan Abramov',
        username: '@dan_abramov',
        text: 'Code is not just a static snapshot of the requirements and tradeoffs, but a living collaborative document pulled into many directions.',
        time: '5:29 PM - 31 Oct 2016',
        retweets: 27,
        likes: 63,
        replies: [7]
    },
    10: {
        name: 'Dan Abramov',
        username: '@dan_abramov',
        text: 'Don\'t write "flexible" modules. No matter how you plan, you\'ll miss some future requirements. Write modules that are easy to delete.',
        time: '5:15 PM - 31 Oct 2016',
        retweets: 339,
        likes: 534,
        replies: [2,1,6,8]
    },
    11: {
        name: 'Dan Abramov',
        username: '@dan_abramov',
        text: 'I like reaching that point when tests no longer express my wishes but help me discover and understand the system.',
        time: '4:06 PM - 28 Oct 2016',
        retweets: 9,
        likes: 70,
        replies: [4,10]
    },
    12: {
        name: 'Dan Abramov',
        username: '@dan_abramov',
        text: 'I feel very anxious when I approach new code so I add logging everywhere. This helps so much.',
        time: '10:37 PM - 13 Oct 2016',
        retweets: 16,
        likes: 112,
        replies: [9,5]
    },
};<|endoftext|>


<commit_before>import React from 'react';
import {StyleSheet, ScrollView, Text, View, TouchableHighlight} from 'react-native';
import {NavigationContext} from 'navigation-react';

const colors = [
  'maroon', 'red', 'crimson', 'orange', 'brown', 'sienna', 'olive',
  'purple', 'fuchsia', 'indigo', 'green', 'navy', 'blue', 'teal', 'black'
];

export default () => (
  <NavigationContext>
    {({stateNavigator}) => (
      <View style={styles.grid}>
        <ScrollView>
          <View style={styles.colors}>
            {colors.map(color => (
              <TouchableHighlight
                style={[
                  {backgroundColor: color},
                  styles.color
                ]}
                underlayColor={color}
                onPress={() => {
                  stateNavigator.navigate('detail', {color});
                }}>
                <Text style={styles.text}>{color}</Text>
              </TouchableHighlight>
            ))}
          </View>
        </ScrollView>
      </View>
    )}
  </NavigationContext>
);

const styles = StyleSheet.create({
  grid: {
    flex: 1,
    backgroundColor: '#fff',
  },
  colors: {
    flexDirection: 'row',
    flexWrap: 'wrap',
    justifyContent: 'center',
    marginTop: 50,
  },
  color: {
    width: 100,
    height: 150,
    marginLeft: 10,
    marginRight: 10,
    marginBottom: 20,
    justifyContent: 'center',
  },
  text: {
    color: '#fff',
    fontSize: 20,
    textAlign: 'center',
    fontWeight: 'bold',
  }
});<commit_msg>Added key prop<commit_after>import React from 'react';
import {StyleSheet, ScrollView, Text, View, TouchableHighlight} from 'react-native';
import {NavigationContext} from 'navigation-react';

const colors = [
  'maroon', 'red', 'crimson', 'orange', 'brown', 'sienna', 'olive',
  'purple', 'fuchsia', 'indigo', 'green', 'navy', 'blue', 'teal', 'black'
];

export default () => (
  <NavigationContext>
    {({stateNavigator}) => (
      <View style={styles.grid}>
        <ScrollView>
          <View style={styles.colors}>
            {colors.map(color => (
              <TouchableHighlight
                style={[
                  {backgroundColor: color},
                  styles.color
                ]}
                underlayColor={color}
                key={color}
                onPress={() => {
                  stateNavigator.navigate('detail', {color});
                }}>
                <Text style={styles.text}>{color}</Text>
              </TouchableHighlight>
            ))}
          </View>
        </ScrollView>
      </View>
    )}
  </NavigationContext>
);

const styles = StyleSheet.create({
  grid: {
    flex: 1,
    backgroundColor: '#fff',
  },
  colors: {
    flexDirection: 'row',
    flexWrap: 'wrap',
    justifyContent: 'center',
    marginTop: 50,
  },
  color: {
    width: 100,
    height: 150,
    marginLeft: 10,
    marginRight: 10,
    marginBottom: 20,
    justifyContent: 'center',
  },
  text: {
    color: '#fff',
    fontSize: 20,
    textAlign: 'center',
    fontWeight: 'bold',
  }
});<|endoftext|>


<commit_before>var fs = require("fs");
var path = require("path");
var cache = {};
function readdir(fullpath) {
    return new Promise(function (ok, oh) {
        fs.readdir(fullpath, { withFileTypes: true }, function (err, names) {
            if (err) return oh(err);
            var map = {};
            var rest = [];
            for (var n of names) {
                map[n.name] = n;
                if (/\-/.test(n.name)) rest.push(n);
            }
            for (var r of rest) {
                var n = r.name.replace(/\-([\s\S])/g, (_, a) => a.toUpperCase());
                if (!(n in map)) {
                    map[n] = r;
                }
            }
            ok(map);
        })
    })
}

async function seek(fullpath, search) {
    search = search.split(/[\\\/]/);
    var pathlist = [fullpath];
    while (search.length) {
        var s = search.shift();
        var p = path.join(...pathlist);
        var map;
        if (p in cache) {
            map = await cache[p];
        }
        else {
            cache[p] = readdir(p);
            map = await readdir(p);
            cache[p] = map;
        }
        if (s in map) {
            pathlist.push(map[s].name);
            continue;
        }
        return null;
    }
    return path.join(...pathlist);
}
async function getFromPath(dirs, name, extt) {
    if (!(dirs instanceof Array)) dirs = [dirs];
    if (!(extt instanceof Array)) extt = [extt];
    for (var d of dirs) {
        for (var e of extt) {
            var p = await seek(d, name + e);
            if (p) return p;
        }
    }
}
module.exports = getFromPath;<commit_msg>修复有view组件的项目编译后无法访问的问题<commit_after>var fs = require("fs");
var path = require("path");
var cache = Object.create(null);
function readdir(fullpath) {
    return new Promise(function (ok, oh) {
        fs.readdir(fullpath, { withFileTypes: true }, function (err, names) {
            if (err) return oh(err);
            var map = {};
            var rest = [];
            for (var n of names) {
                map[n.name] = n;
                if (/\-/.test(n.name)) rest.push(n);
            }
            for (var r of rest) {
                var n = r.name.replace(/\-([\s\S])/g, (_, a) => a.toUpperCase());
                if (!(n in map)) {
                    map[n] = r;
                }
            }
            ok(map);
        })
    })
}

async function seek(fullpath, search) {
    search = search.split(/[\\\/]/);
    var pathlist = [fullpath];
    while (search.length) {
        var s = search.shift();
        var p = path.join(...pathlist);
        var map;
        if (p in cache) {
            map = await cache[p];
        }
        else {
            cache[p] = readdir(p);
            map = await readdir(p);
            cache[p] = map;
        }
        if (s in map) {
            pathlist.push(map[s].name);
            if (map[s].isFile()) break;
            continue;
        }
        return null;
    }
    if (search.length) return null;
    return path.join(...pathlist);
}
async function getFromPath(dirs, name, extt) {
    if (!(dirs instanceof Array)) dirs = [dirs];
    if (!(extt instanceof Array)) extt = [extt];
    var dist = [];
    for (var d of dirs) {
        for (var e of extt) {
            var p = await seek(d, name + e);
            if (p) dist.push(p);
        }
    }
    return dist;
}
module.exports = getFromPath;<|endoftext|>


<commit_before>'use strict';

var mongoose = require('mongoose'),
    async = require('async'),
    pretty = require('pretty-time'),
    articleModel = require('./article.model.js');

module.exports.connect = function (next) {
    var uri = 'mongodb://localhost/paginate-benchmark';
    var options = {
        user: '',
        pass: ''
    };
    return mongoose.connect(uri, options, next);
};

module.exports.remove = function (next) {
    return mongoose.model('Article').remove({}, next);
};

module.exports.seed = function (commandResult, next) {

    var articles = [];
    for (var i = 0; i < 10000; i++) {
        articles.push({
            title: 'Dummy title ' + i,
            content: '1234567890 1234567890 1234567890 1234567890 1234567890'
        });
    }

    return mongoose.model('Article').insertMany(articles, next);
};

module.exports.timing = function (f, next) {
    var start = process.hrtime();
    return f(function () {
        var time = process.hrtime(start);
        var prettyTiming = pretty(time);
        return next(null, prettyTiming);
    });
};

module.exports.strategy1 = function (next) {
    return module.exports.timing(function (cb) {
        return mongoose.model('Article').find({}).skip(5000).limit(20).exec(function (err, c) {
            return cb();
        })
    }, next);
};

module.exports.strategy2 = function (next) {
    return module.exports.timing(function (cb) {
        // to do.. dummy waiting
        setTimeout(function() {
            return cb();
        }, 3000);
    }, next);
};

module.exports.benchmark = function (docs, next) {
    return async.series(
        {
            "strategy1": module.exports.strategy1,
            "strategy2": module.exports.strategy2
        }, next);
};

module.exports.report = function (benchmarks, next) {
    var names = Object.keys(benchmarks);
    for (var i = 0; i < names.length; i++) {
        var name = names[i];
        var result = benchmarks[name];
        console.log(name + ' : ' + result);
    }
    return next();
};

module.exports.start = function (next) {
    return async.waterfall(
        [
            module.exports.connect,
            module.exports.remove,
            module.exports.seed,
            module.exports.benchmark,
            module.exports.report
        ], next
    );
};

module.exports.start(function(err) {
    if (err) {
        console.log(err);
        return process.exit(1);
    }
    return process.exit();
});<commit_msg>Strategy 2 Benchmarks<commit_after>'use strict';

var mongoose = require('mongoose'),
    async = require('async'),
    pretty = require('pretty-time'),
    articleModel = require('./article.model.js');

var _totalArticlesToSeed = 10000;
var _totalArticlesToQuery = 10000;
var _pageIndex = (250); // zero based index for use with Mongoose's .skip() method
var _take = 20;

module.exports.connect = function (next) {
    var uri = 'mongodb://localhost/paginate-benchmark';
    var options = {
        user: '',
        pass: ''
    };
    return mongoose.connect(uri, options, next);
};

module.exports.remove = function (next) {
    return mongoose.model('Article').remove({}, next);
};

module.exports.seed = function (commandResult, next) {

    var articles = [];
    for (var i = 0; i < _totalArticlesToSeed; i++) {
        articles.push({
            title: 'Dummy title ' + i,
            content: '1234567890 1234567890 1234567890 1234567890 1234567890'
        });
    }

    return mongoose.model('Article').insertMany(articles, next);
};

module.exports.timing = function (f, next) {
    var start = process.hrtime();
    return f(function () {
        var time = process.hrtime(start);
        var prettyTiming = pretty(time);
        return next(null, prettyTiming);
    });
};

module.exports.strategy1 = function (next) {
    return module.exports.timing(function (cb) {
        return mongoose.model('Article').find({}).skip(_pageIndex * _take).limit(_take).exec(function (err, c) {
            return cb();
        })
    }, next);
};

module.exports.strategy2 = function (next) {
    return module.exports.timing(function (cb) {
        var query = mongoose.model('Article').find({}).limit(_totalArticlesToQuery);

        return query.count(function (err, count) {
            query.skip(_pageIndex * _take).limit(_take).exec('find', function (err, articles) {
                return cb();
            });
        })
    }, next);
};

module.exports.strategy2_withSortOnIndexedField = function (next) {
    return module.exports.timing(function (cb) {
        var query = mongoose.model('Article').find({}).limit(_totalArticlesToQuery).sort('_id');

        return query.count(function (err, count) {
            query.skip(_pageIndex * _take).limit(_take).exec('find', function (err, articles) {
                return cb();
            });
        })
    }, next);
};

module.exports.benchmark = function (docs, next) {
    return async.series(
        {
            "strategy1": module.exports.strategy1,
            "strategy2": module.exports.strategy2,
            "strategy2_withSortOnIndexedField": module.exports.strategy2_withSortOnIndexedField
        }, next);
};

module.exports.report = function (benchmarks, next) {
    var names = Object.keys(benchmarks);
    for (var i = 0; i < names.length; i++) {
        var name = names[i];
        var result = benchmarks[name];
        console.log(name + ' : ' + result);
    }
    return next();
};

module.exports.start = function (next) {
    return async.waterfall(
        [
            module.exports.connect,
            module.exports.remove,
            module.exports.seed,
            module.exports.benchmark,
            module.exports.report
        ], next
    );
};

module.exports.start(function(err) {
    if (err) {
        console.log(err);
        return process.exit(1);
    }
    return process.exit();
});<|endoftext|>


<commit_before>/**
 * Copyright 2016 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

function log(args) {
  var var_args = Array.prototype.slice.call(arguments, 0);
  var_args.unshift('[SHELL]');
  console/*OK*/.log.apply(console, var_args);
}

function startsWith(string, prefix) {
  return string.lastIndexOf(prefix, 0) == 0;
}

class Shell {
  constructor(win, useStreaming) {
    /** @private @const {!Window} */
    this.win = win;

    /** @private @const {boolean} */
    this.useStreaming_ = useStreaming;

    /** @private @const {!AmpViewer} */
    this.ampViewer_ = new AmpViewer(win,
        win.document.getElementById('doc-container'));

    /** @private {string} */
    this.currentPage_ = win.location.pathname;

    this.sidebarCloseButton_ = document.querySelector('#sidebarClose');

    win.addEventListener('popstate', this.handlePopState_.bind(this));
    win.document.documentElement.addEventListener('click',
        this.handleNavigate_.bind(this));

    log('Shell created');

    if (this.currentPage_ && !isShellUrl(this.currentPage_)) {
      this.navigateTo(this.currentPage_);
    } else if (this.win.location.hash) {
      const hashParams = parseQueryString(this.win.location.hash);
      const href = hashParams['href'];
      if (href) {
        this.currentPage_ = href;
        this.navigateTo(href);
      }
    }

    // Install service worker
    this.registerServiceWorker_();
  }

  registerServiceWorker_() {
    if ('serviceWorker' in navigator) {
      log('Register service worker');
      navigator.serviceWorker.register('/pwa/pwa-sw.js').then(reg => {
        log('Service worker registered: ', reg);
      }).catch(err => {
        log('Service worker registration failed: ', err);
      });
    }
  }

  unregisterServiceWorker_() {
    if ('serviceWorker' in navigator) {
      log('Register service worker');
      navigator.serviceWorker.getRegistration('/pwa/pwa-sw.js').then(reg => {
        log('Service worker found: ', reg);
        reg.unregister();
        log('Service worker unregistered');
      });
    }
  }

  /**
   * @param {!Event} e
   */
  handleNavigate_(e) {
    if (e.defaultPrevented) {
      return false;
    }
    if (e.button) {
      return false;
    }
    let a = e.target;
    while (a) {
      if (a.tagName == 'A' && a.href) {
        break;
      }
      a = a.parentElement;
    }
    if (a) {
      const url = new URL(a.href);
      const location = this.win.location;
      if (url.origin == location.origin &&
          startsWith(url.pathname, '/pwa/') &&
          url.pathname.indexOf('amp.html') != -1) {
        e.preventDefault();
        const newPage = url.pathname + location.search;
        log('Internal link to: ', newPage);
        if (newPage != this.currentPage_) {
          this.closeSidebar().then(() => this.navigateTo(newPage));
        }
      }
    }
  }

  /**
   */
  handlePopState_() {
    const newPage = this.win.location.pathname;
    log('Pop state: ', newPage, this.currentPage_);
    if (newPage != this.currentPage_) {
      this.navigateTo(newPage);
    }
  }

  /**
   * @param {string} path
   * @return {!Promise}
   */
  navigateTo(path) {
    log('Navigate to: ', path);
    const oldPage = this.currentPage_;
    this.currentPage_ = path;

    // Update URL.
    const push = !isShellUrl(path) && isShellUrl(oldPage);
    if (path != this.win.location.pathname) {
      if (push) {
        this.win.history.pushState(null, '', path);
      } else {
        this.win.history.replaceState(null, '', path);
      }
    }

    if (isShellUrl(path)) {
      log('Back to shell');
      this.ampViewer_.clear();
      return Promise.resolve();
    }

    // Fetch.
    const url = this.resolveUrl_(path);
    log('Fetch and render doc:', path, url);
    // TODO(dvoytenko, #9490): Make `streamDocument` the only used API once
    // streaming is graduated out of experimental.
    if (this.useStreaming_) {
      log('Streaming started: ', url);
      return this.ampViewer_.showAsStream(url).then(
          shadowDoc => streamDocument(url, shadowDoc.writer));
    }
    return fetchDocument(url).then(doc => {
      log('Fetch complete: ', doc);
      return this.ampViewer_.show(doc, url);
    });
  }

  /**
   * @param {string} url
   * @return {string}
   */
  resolveUrl_(url) {
    if (!this.a_) {
      this.a_ = this.win.document.createElement('a');
    }
    this.a_.href = url;
    return this.a_.href;
  }

  closeSidebar() {
    if (this.sidebarCloseButton_) {
      return new Promise(resolve => {
        this.sidebarCloseButton_.click();
        // TODO implement a better method to detect when
        // closing sidebar has finished
        setTimeout(() => resolve(), 100);
      });
    } else {
      return Promise.resolve();
    }
  }
}

class AmpViewer {

  constructor(win, container) {
    /** @private @const {!Window} */
    this.win = win;
    /** @private @const {!Element} */
    this.container = container;

    win.AMP_SHADOW = true;
    const ampReadyPromise = new Promise(resolve => {
      (window.AMP = window.AMP || []).push(resolve);
    });
    ampReadyPromise.then(AMP => {
      log('AMP LOADED:', AMP);
    });

    const isShadowDomSupported = (
      Element.prototype.attachShadow ||
      Element.prototype.createShadowRoot
    );
    const shadowDomReadyPromise = new Promise((resolve, reject) => {
      if (isShadowDomSupported) {
        resolve();
      } else if (this.win.document.querySelector('script[src*=webcomponents]')) {
        this.win.addEventListener('WebComponentsReady', resolve);
      } else {
        // AMP polyfills small part of SD spec. It's functional, but some things
        // (e.g. slots) are not available.
        resolve();
      }
    });

    this.readyPromise_ = Promise.all([
      ampReadyPromise,
      shadowDomReadyPromise,
    ]).then(results => results[0]);

    /** @private @const {string} */
    this.baseUrl_ = null;
    /** @private @const {?Element} */
    this.host_ = null;
    /** @private @const {...} */
    this.amp_ = null;

    // Immediately install amp-shadow.js.
    try {
      // Install compiled version if it exists
      this.installScript_('/dist/shadow-v0.js')
    } catch (ex) {
      this.installScript_('/dist/amp-shadow.js');
    }
  }

  /**
   */
  clear() {
    if (this.amp_) {
      this.amp_.close();
      this.amp_ = null;
    }
    this.container.textContent = '';
  }

  /**
   * @param {!Document} doc
   * @param {string} url
   */
  show(doc, url) {
    log('Show document:', doc, url);

    // Cleanup the existing document if any.
    this.clear();

    this.baseUrl_ = url;

    this.host_ = this.win.document.createElement('div');
    this.host_.classList.add('amp-doc-host');

    const hostTemplate = this.win.document.getElementById('amp-slot-template');
    if (hostTemplate) {
      this.host_.appendChild(hostTemplate.content.cloneNode(true));
    }

    this.container.appendChild(this.host_);

    return this.readyPromise_.then(AMP => {
      this.amp_ = AMP.attachShadowDoc(this.host_, doc, url, {});
      this.win.document.title = this.amp_.title || '';
      this.amp_.onMessage(this.onMessage_.bind(this));
      this.amp_.setVisibilityState('visible');
    });
  }

  /**
   * @param {string} url
   * @return {!Promise<!ShadowDoc>}
   */
  showAsStream(url) {
    log('Show stream document:', url);

    // Cleanup the existing document if any.
    this.clear();

    this.baseUrl_ = url;

    this.host_ = this.win.document.createElement('div');
    this.host_.classList.add('amp-doc-host');

    const hostTemplate = this.win.document.getElementById('amp-slot-template');
    if (hostTemplate) {
      this.host_.appendChild(hostTemplate.content.cloneNode(true));
    }

    this.container.appendChild(this.host_);

    return this.readyPromise_.then(AMP => {
      this.amp_ = AMP.attachShadowDocAsStream(this.host_, url, {});
      this.win.document.title = this.amp_.title || '';
      this.amp_.onMessage(this.onMessage_.bind(this));
      this.amp_.setVisibilityState('visible');
      return this.amp_;
    });
  }

  /**
   * @param {string} src
   * @param {string=} customElement
   * @param {string=} customTemplate
   */
  installScript_(src, customElement, customTemplate) {
    const doc = this.win.document;
    const el = doc.createElement('script');
    el.setAttribute('src', src);
    if (customElement) {
      el.setAttribute('custom-element', customElement);
    }
    if (customTemplate) {
      el.setAttribute('custom-template', customTemplate);
    }
    doc.head.appendChild(el);
    log('- script added: ', src, el);
  }

  /**
   * @param {string} url
   * @return {string}
   */
  resolveUrl_(relativeUrlString) {
    return new URL(relativeUrlString, this.baseUrl_).toString();
  }

  /**
   * @param {string} url
   * @return {string}
   */
  getOrigin_(relativeUrlString) {
    return new URL(relativeUrlString, this.baseUrl_).origin;
  }

  /**
   */
  onMessage_(type, data, rsvp) {
  }
}

/**
 * @param {string} url
 * @return {boolean}
 */
function isShellUrl(url) {
  return (url == '/pwa' || url == '/pwa/');
}

/**
 * @param {string} url
 * @return {!Promise<!Document>}
 */
function fetchDocument(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.responseType = 'document';
    xhr.setRequestHeader('Accept', 'text/html');
    xhr.onreadystatechange = () => {
      if (xhr.readyState < /* STATUS_RECEIVED */ 2) {
        return;
      }
      if (xhr.status < 100 || xhr.status > 599) {
        xhr.onreadystatechange = null;
        reject(new Error(`Unknown HTTP status ${xhr.status}`));
        return;
      }
      if (xhr.readyState == /* COMPLETE */ 4) {
        if (xhr.responseXML) {
          resolve(xhr.responseXML);
        } else {
          reject(new Error(`No xhr.responseXML`));
        }
      }
    };
    xhr.onerror = () => {
      reject(new Error('Network failure'));
    };
    xhr.onabort = () => {
      reject(new Error('Request aborted'));
    };
    xhr.send();
  });
}

/**
 * @param {string} url
 * @param {!WritableStreamDefaultWriter} writer
 * @return {!Promise}
 */
function streamDocument(url, writer) {
  // Try native first.
  if (window.fetch && window.TextDecoder && window.ReadableStream) {
    return fetch(url).then(response => {
      // This should be a lot simpler with transforming streams and pipes,
      // but, TMK, these are not supported anywhere yet.
      const /** !ReadableStreamDefaultReader */ reader = response.body
          .getReader();
      const decoder = new TextDecoder();
      function readChunk(chunk) {
        const text = decoder.decode(
            chunk.value || new Uint8Array(),
            {stream: !chunk.done});
        if (text) {
          writer.write(text);
        }
        if (chunk.done) {
          writer.close();
        } else {
          return reader.read().then(readChunk);
        }
      }
      return reader.read().then(readChunk);
    });
  }

  // Polyfill via XHR.
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.setRequestHeader('Accept', 'text/html');
    let pos = 0;
    xhr.onreadystatechange = () => {
      if (xhr.readyState < /* STATUS_RECEIVED */ 2) {
        return;
      }
      if (xhr.status < 100 || xhr.status > 599) {
        xhr.onreadystatechange = null;
        reject(new Error(`Unknown HTTP status ${xhr.status}`));
        return;
      }
      if (xhr.readyState == /* LOADING */ 3 ||
          xhr.readyState == /* COMPLETE */ 4) {
        const s = xhr.responseText;
        const chunk = s.substring(pos);
        pos = s.length;
        writer.write(chunk);
        if (xhr.readyState == /* COMPLETE */ 4) {
          writer.close().then(resolve);
        }
      }
    };
    xhr.onerror = () => {
      reject(new Error('Network failure'));
    };
    xhr.onabort = () => {
      reject(new Error('Request aborted'));
    };
    xhr.send();
  });
}

/**
 * Parses the query string of an URL. This method returns a simple key/value
 * map. If there are duplicate keys the latest value is returned.
 * @param {string} queryString
 * @return {!Object<string>}
 */
function parseQueryString(queryString) {
  const params = Object.create(null);
  if (!queryString) {
    return params;
  }
  if (startsWith(queryString, '?') || startsWith(queryString, '#')) {
    queryString = queryString.substr(1);
  }
  const pairs = queryString.split('&');
  for (let i = 0; i < pairs.length; i++) {
    const pair = pairs[i];
    const eqIndex = pair.indexOf('=');
    let name;
    let value;
    if (eqIndex != -1) {
      name = decodeURIComponent(pair.substring(0, eqIndex)).trim();
      value = decodeURIComponent(pair.substring(eqIndex + 1)).trim();
    } else {
      name = decodeURIComponent(pair).trim();
      value = '';
    }
    if (name) {
      params[name] = value;
    }
  }
  return params;
}

var shell = new Shell(window, /* useStreaming */ true);
<commit_msg>Fix PWA example script (#23705)<commit_after>/**
 * Copyright 2016 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

function log(args) {
  var var_args = Array.prototype.slice.call(arguments, 0);
  var_args.unshift('[SHELL]');
  console/*OK*/.log.apply(console, var_args);
}

function startsWith(string, prefix) {
  return string.lastIndexOf(prefix, 0) == 0;
}

class Shell {
  constructor(win, useStreaming) {
    /** @private @const {!Window} */
    this.win = win;

    /** @private @const {boolean} */
    this.useStreaming_ = useStreaming;

    /** @private @const {!AmpViewer} */
    this.ampViewer_ = new AmpViewer(win,
        win.document.getElementById('doc-container'));

    /** @private {string} */
    this.currentPage_ = win.location.pathname;

    this.sidebarCloseButton_ = document.querySelector('#sidebarClose');

    win.addEventListener('popstate', this.handlePopState_.bind(this));
    win.document.documentElement.addEventListener('click',
        this.handleNavigate_.bind(this));

    log('Shell created');

    if (this.currentPage_ && !isShellUrl(this.currentPage_)) {
      this.navigateTo(this.currentPage_);
    } else if (this.win.location.hash) {
      const hashParams = parseQueryString(this.win.location.hash);
      const href = hashParams['href'];
      if (href) {
        this.currentPage_ = href;
        this.navigateTo(href);
      }
    }

    // Install service worker
    this.registerServiceWorker_();
  }

  registerServiceWorker_() {
    if ('serviceWorker' in navigator) {
      log('Register service worker');
      navigator.serviceWorker.register('/pwa/pwa-sw.js').then(reg => {
        log('Service worker registered: ', reg);
      }).catch(err => {
        log('Service worker registration failed: ', err);
      });
    }
  }

  unregisterServiceWorker_() {
    if ('serviceWorker' in navigator) {
      log('Register service worker');
      navigator.serviceWorker.getRegistration('/pwa/pwa-sw.js').then(reg => {
        log('Service worker found: ', reg);
        reg.unregister();
        log('Service worker unregistered');
      });
    }
  }

  /**
   * @param {!Event} e
   */
  handleNavigate_(e) {
    if (e.defaultPrevented) {
      return false;
    }
    if (e.button) {
      return false;
    }
    let a = e.target;
    while (a) {
      if (a.tagName == 'A' && a.href) {
        break;
      }
      a = a.parentElement;
    }
    if (a) {
      const url = new URL(a.href);
      const location = this.win.location;
      if (url.origin == location.origin &&
          startsWith(url.pathname, '/pwa/') &&
          url.pathname.indexOf('amp.html') != -1) {
        e.preventDefault();
        const newPage = url.pathname + location.search;
        log('Internal link to: ', newPage);
        if (newPage != this.currentPage_) {
          this.closeSidebar().then(() => this.navigateTo(newPage));
        }
      }
    }
  }

  /**
   */
  handlePopState_() {
    const newPage = this.win.location.pathname;
    log('Pop state: ', newPage, this.currentPage_);
    if (newPage != this.currentPage_) {
      this.navigateTo(newPage);
    }
  }

  /**
   * @param {string} path
   * @return {!Promise}
   */
  navigateTo(path) {
    log('Navigate to: ', path);
    const oldPage = this.currentPage_;
    this.currentPage_ = path;

    // Update URL.
    const push = !isShellUrl(path) && isShellUrl(oldPage);
    if (path != this.win.location.pathname) {
      if (push) {
        this.win.history.pushState(null, '', path);
      } else {
        this.win.history.replaceState(null, '', path);
      }
    }

    if (isShellUrl(path)) {
      log('Back to shell');
      this.ampViewer_.clear();
      return Promise.resolve();
    }

    // Fetch.
    const url = this.resolveUrl_(path);
    log('Fetch and render doc:', path, url);
    // TODO(dvoytenko, #9490): Make `streamDocument` the only used API once
    // streaming is graduated out of experimental.
    if (this.useStreaming_) {
      log('Streaming started: ', url);
      return this.ampViewer_.showAsStream(url).then(
          shadowDoc => streamDocument(url, shadowDoc.writer));
    }
    return fetchDocument(url).then(doc => {
      log('Fetch complete: ', doc);
      return this.ampViewer_.show(doc, url);
    });
  }

  /**
   * @param {string} url
   * @return {string}
   */
  resolveUrl_(url) {
    if (!this.a_) {
      this.a_ = this.win.document.createElement('a');
    }
    this.a_.href = url;
    return this.a_.href;
  }

  closeSidebar() {
    if (this.sidebarCloseButton_) {
      return new Promise(resolve => {
        this.sidebarCloseButton_.click();
        // TODO implement a better method to detect when
        // closing sidebar has finished
        setTimeout(() => resolve(), 100);
      });
    } else {
      return Promise.resolve();
    }
  }
}

class AmpViewer {

  constructor(win, container) {
    /** @private @const {!Window} */
    this.win = win;
    /** @private @const {!Element} */
    this.container = container;

    win.AMP_SHADOW = true;
    const ampReadyPromise = new Promise(resolve => {
      (window.AMP = window.AMP || []).push(resolve);
    });
    ampReadyPromise.then(AMP => {
      log('AMP LOADED:', AMP);
    });

    const isShadowDomSupported = (
      Element.prototype.attachShadow ||
      Element.prototype.createShadowRoot
    );
    const shadowDomReadyPromise = new Promise((resolve, reject) => {
      if (isShadowDomSupported) {
        resolve();
      } else if (this.win.document.querySelector('script[src*=webcomponents]')) {
        this.win.addEventListener('WebComponentsReady', resolve);
      } else {
        // AMP polyfills small part of SD spec. It's functional, but some things
        // (e.g. slots) are not available.
        resolve();
      }
    });

    this.readyPromise_ = Promise.all([
      ampReadyPromise,
      shadowDomReadyPromise,
    ]).then(results => results[0]);

    /** @private @const {string} */
    this.baseUrl_ = null;
    /** @private @const {?Element} */
    this.host_ = null;
    /** @private @const {...} */
    this.amp_ = null;

    // Immediately install amp-shadow.js.
    this.installScript_('/dist/shadow-v0.js', '/dist/amp-shadow.js');
  }

  /**
   */
  clear() {
    if (this.amp_) {
      this.amp_.close();
      this.amp_ = null;
    }
    this.container.textContent = '';
  }

  /**
   * @param {!Document} doc
   * @param {string} url
   */
  show(doc, url) {
    log('Show document:', doc, url);

    // Cleanup the existing document if any.
    this.clear();

    this.baseUrl_ = url;

    this.host_ = this.win.document.createElement('div');
    this.host_.classList.add('amp-doc-host');

    const hostTemplate = this.win.document.getElementById('amp-slot-template');
    if (hostTemplate) {
      this.host_.appendChild(hostTemplate.content.cloneNode(true));
    }

    this.container.appendChild(this.host_);

    return this.readyPromise_.then(AMP => {
      this.amp_ = AMP.attachShadowDoc(this.host_, doc, url, {});
      this.win.document.title = this.amp_.title || '';
      this.amp_.onMessage(this.onMessage_.bind(this));
      this.amp_.setVisibilityState('visible');
    });
  }

  /**
   * @param {string} url
   * @return {!Promise<!ShadowDoc>}
   */
  showAsStream(url) {
    log('Show stream document:', url);

    // Cleanup the existing document if any.
    this.clear();

    this.baseUrl_ = url;

    this.host_ = this.win.document.createElement('div');
    this.host_.classList.add('amp-doc-host');

    const hostTemplate = this.win.document.getElementById('amp-slot-template');
    if (hostTemplate) {
      this.host_.appendChild(hostTemplate.content.cloneNode(true));
    }

    this.container.appendChild(this.host_);

    return this.readyPromise_.then(AMP => {
      this.amp_ = AMP.attachShadowDocAsStream(this.host_, url, {});
      this.win.document.title = this.amp_.title || '';
      this.amp_.onMessage(this.onMessage_.bind(this));
      this.amp_.setVisibilityState('visible');
      return this.amp_;
    });
  }

  /**
   * @param {string} src
   * @param {string=} fallbackSrc
   * @param {string=} customElement
   * @param {string=} customTemplate
   */
  installScript_(src, fallbackSrc, customElement, customTemplate) {
    const doc = this.win.document;
    const el = doc.createElement('script');
    el.setAttribute('src', src);
    if (customElement) {
      el.setAttribute('custom-element', customElement);
    }
    if (customTemplate) {
      el.setAttribute('custom-template', customTemplate);
    }
    el.onload = () => {
      log('- script added: ', src, el);
    };
    el.onerror = () => {
      log('- script failed to load: ', src, el);
      doc.head.removeChild(el);
      if (fallbackSrc) {
        this.installScript_(fallbackSrc, undefined, customElement, customTemplate);
      }
    };
    doc.head.appendChild(el);
  }

  /**
   * @param {string} url
   * @return {string}
   */
  resolveUrl_(relativeUrlString) {
    return new URL(relativeUrlString, this.baseUrl_).toString();
  }

  /**
   * @param {string} url
   * @return {string}
   */
  getOrigin_(relativeUrlString) {
    return new URL(relativeUrlString, this.baseUrl_).origin;
  }

  /**
   */
  onMessage_(type, data, rsvp) {
  }
}

/**
 * @param {string} url
 * @return {boolean}
 */
function isShellUrl(url) {
  return (url == '/pwa' || url == '/pwa/');
}

/**
 * @param {string} url
 * @return {!Promise<!Document>}
 */
function fetchDocument(url) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.responseType = 'document';
    xhr.setRequestHeader('Accept', 'text/html');
    xhr.onreadystatechange = () => {
      if (xhr.readyState < /* STATUS_RECEIVED */ 2) {
        return;
      }
      if (xhr.status < 100 || xhr.status > 599) {
        xhr.onreadystatechange = null;
        reject(new Error(`Unknown HTTP status ${xhr.status}`));
        return;
      }
      if (xhr.readyState == /* COMPLETE */ 4) {
        if (xhr.responseXML) {
          resolve(xhr.responseXML);
        } else {
          reject(new Error(`No xhr.responseXML`));
        }
      }
    };
    xhr.onerror = () => {
      reject(new Error('Network failure'));
    };
    xhr.onabort = () => {
      reject(new Error('Request aborted'));
    };
    xhr.send();
  });
}

/**
 * @param {string} url
 * @param {!WritableStreamDefaultWriter} writer
 * @return {!Promise}
 */
function streamDocument(url, writer) {
  // Try native first.
  if (window.fetch && window.TextDecoder && window.ReadableStream) {
    return fetch(url).then(response => {
      // This should be a lot simpler with transforming streams and pipes,
      // but, TMK, these are not supported anywhere yet.
      const /** !ReadableStreamDefaultReader */ reader = response.body
          .getReader();
      const decoder = new TextDecoder();
      function readChunk(chunk) {
        const text = decoder.decode(
            chunk.value || new Uint8Array(),
            {stream: !chunk.done});
        if (text) {
          writer.write(text);
        }
        if (chunk.done) {
          writer.close();
        } else {
          return reader.read().then(readChunk);
        }
      }
      return reader.read().then(readChunk);
    });
  }

  // Polyfill via XHR.
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);
    xhr.setRequestHeader('Accept', 'text/html');
    let pos = 0;
    xhr.onreadystatechange = () => {
      if (xhr.readyState < /* STATUS_RECEIVED */ 2) {
        return;
      }
      if (xhr.status < 100 || xhr.status > 599) {
        xhr.onreadystatechange = null;
        reject(new Error(`Unknown HTTP status ${xhr.status}`));
        return;
      }
      if (xhr.readyState == /* LOADING */ 3 ||
          xhr.readyState == /* COMPLETE */ 4) {
        const s = xhr.responseText;
        const chunk = s.substring(pos);
        pos = s.length;
        writer.write(chunk);
        if (xhr.readyState == /* COMPLETE */ 4) {
          writer.close().then(resolve);
        }
      }
    };
    xhr.onerror = () => {
      reject(new Error('Network failure'));
    };
    xhr.onabort = () => {
      reject(new Error('Request aborted'));
    };
    xhr.send();
  });
}

/**
 * Parses the query string of an URL. This method returns a simple key/value
 * map. If there are duplicate keys the latest value is returned.
 * @param {string} queryString
 * @return {!Object<string>}
 */
function parseQueryString(queryString) {
  const params = Object.create(null);
  if (!queryString) {
    return params;
  }
  if (startsWith(queryString, '?') || startsWith(queryString, '#')) {
    queryString = queryString.substr(1);
  }
  const pairs = queryString.split('&');
  for (let i = 0; i < pairs.length; i++) {
    const pair = pairs[i];
    const eqIndex = pair.indexOf('=');
    let name;
    let value;
    if (eqIndex != -1) {
      name = decodeURIComponent(pair.substring(0, eqIndex)).trim();
      value = decodeURIComponent(pair.substring(eqIndex + 1)).trim();
    } else {
      name = decodeURIComponent(pair).trim();
      value = '';
    }
    if (name) {
      params[name] = value;
    }
  }
  return params;
}

var shell = new Shell(window, /* useStreaming */ true);
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router';
import ReactTooltip from 'react-tooltip';

import SPECS from 'common/SPECS';
import ROLES from 'common/ROLES';

import makeAnalyzerUrl from './makeAnalyzerUrl';

class PlayerSelectionList extends Component {
  static propTypes = {
    report: PropTypes.shape({
      code: PropTypes.string.isRequired,
      title: PropTypes.string.isRequired,
      friendlies: PropTypes.arrayOf(PropTypes.shape({
        id: PropTypes.number.isRequired,
        type: PropTypes.string.isRequired,
        name: PropTypes.string.isRequired,
      })),
    }).isRequired,
    fightId: PropTypes.number.isRequired,
    combatants: PropTypes.arrayOf(PropTypes.shape({

    })).isRequired,
  };

  componentWillUnmount() {
    ReactTooltip.hide();
  }

  renderRoleIcon(spec) {
    let icon;
    switch (spec.role) {
      case ROLES.TANK: icon = 'tank'; break;
      case ROLES.HEALER: icon = 'healer'; break;
      case ROLES.DPS.MELEE: icon = 'dps'; break;
      case ROLES.DPS.RANGED: icon = 'dps.ranged'; break;
      default: break;
    }

    return (
      <img src={`/roles/${icon}.jpg`} alt="Role icon" style={{ borderRadius: '50%', marginLeft: -5, marginRight: 10 }} />
    );
  }
  renderSpecIcon(spec) {
    return (
      <img src={`/specs/${spec.className.replace(' ', '')}-${spec.specName.replace(' ', '')}.jpg`} alt="Spec logo" />
    );
  }

  render() {
    const { report, fightId, combatants } = this.props;

    return (

        {combatants.length === 0 && (

            Could not find any players in this report. Make sure the log is recorded with Advanced Combat Logging enabled. You can enable this in-game in the network settings.

        )}
        {
          report.friendlies
            .map(friendly => ({
              friendly,
              combatant: combatants.find(combatant => combatant.sourceID === friendly.id),
            }))
            .filter(player => !!player.combatant)
            .sort((a, b) => {
              // The combatlog can error out while would cause the combatant to not have a spec specified, in that case sort them at the bottom.
              const aSpec = SPECS[a.combatant.specID] || { role: 10 };
              const bSpec = SPECS[b.combatant.specID] || { role: 10 };
              if (aSpec.role > bSpec.role) {
                return 1;
              } else if (aSpec.role < bSpec.role) {
                return -1;
              }
              if (a.friendly.name > b.friendly.name) {
                return 1;
              } else if (a.friendly.name  {
              const spec = SPECS[combatant.specID];

              if (!spec) {
                // Spec might not be found if the combatantinfo errored, this happens extremely rarely. Example report: CJBdLf3c2zQXkPtg/13-Heroic+Kil'jaeden+-+Kill+(7:40)
                return (

                     alert('The combatlog did not give us any information about this player. This player can not be analyzed.')}>
                      {friendly.name} (Error - Spec unknown)

                );
              }

              return (

                  <Link to={makeAnalyzerUrl(report, fightId, friendly.name)}>
                    {this.renderRoleIcon(spec)}{' '}
                    {this.renderSpecIcon(spec)}{' '}
                    {friendly.name} ({spec.specName} {spec.className})
                  </Link>

              );
            })
        }

    );
  }
}

export default PlayerSelectionList;
<commit_msg>Fix eslint warning<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router';
import ReactTooltip from 'react-tooltip';

import SPECS from 'common/SPECS';
import ROLES from 'common/ROLES';

import makeAnalyzerUrl from './makeAnalyzerUrl';

class PlayerSelectionList extends Component {
  static propTypes = {
    report: PropTypes.shape({
      code: PropTypes.string.isRequired,
      title: PropTypes.string.isRequired,
      friendlies: PropTypes.arrayOf(PropTypes.shape({
        id: PropTypes.number.isRequired,
        type: PropTypes.string.isRequired,
        name: PropTypes.string.isRequired,
      })),
    }).isRequired,
    fightId: PropTypes.number.isRequired,
    combatants: PropTypes.arrayOf(PropTypes.shape({

    })).isRequired,
  };

  componentWillUnmount() {
    ReactTooltip.hide();
  }

  renderRoleIcon(spec) {
    let icon;
    switch (spec.role) {
      case ROLES.TANK: icon = 'tank'; break;
      case ROLES.HEALER: icon = 'healer'; break;
      case ROLES.DPS.MELEE: icon = 'dps'; break;
      case ROLES.DPS.RANGED: icon = 'dps.ranged'; break;
      default: break;
    }

    return (
      <img src={`/roles/${icon}.jpg`} alt="Role icon" style={{ borderRadius: '50%', marginLeft: -5, marginRight: 10 }} />
    );
  }
  renderSpecIcon(spec) {
    return (
      <img src={`/specs/${spec.className.replace(' ', '')}-${spec.specName.replace(' ', '')}.jpg`} alt="Spec logo" />
    );
  }

  render() {
    const { report, fightId, combatants } = this.props;

    return (

        {combatants.length === 0 && (

            Could not find any players in this report. Make sure the log is recorded with Advanced Combat Logging enabled. You can enable this in-game in the network settings.

        )}
        {
          report.friendlies
            .map(friendly => ({
              friendly,
              combatant: combatants.find(combatant => combatant.sourceID === friendly.id),
            }))
            .filter(player => !!player.combatant)
            .sort((a, b) => {
              // The combatlog can error out while would cause the combatant to not have a spec specified, in that case sort them at the bottom.
              const aSpec = SPECS[a.combatant.specID] || { role: 10 };
              const bSpec = SPECS[b.combatant.specID] || { role: 10 };
              if (aSpec.role > bSpec.role) {
                return 1;
              } else if (aSpec.role < bSpec.role) {
                return -1;
              }
              if (a.friendly.name > b.friendly.name) {
                return 1;
              } else if (a.friendly.name  {
              const spec = SPECS[combatant.specID];

              if (!spec) {
                // Spec might not be found if the combatantinfo errored, this happens extremely rarely. Example report: CJBdLf3c2zQXkPtg/13-Heroic+Kil'jaeden+-+Kill+(7:40)
                return (

                    <Link
                      to={makeAnalyzerUrl(report, fightId, friendly.name)}
                      onClick={e => {
                        e.preventDefault();
                        alert('The combatlog did not give us any information about this player. This player can not be analyzed.');
                      }}
                    >
                      {friendly.name} (Error - Spec unknown)
                    </Link>

                );
              }

              return (

                  <Link to={makeAnalyzerUrl(report, fightId, friendly.name)}>
                    {this.renderRoleIcon(spec)}{' '}
                    {this.renderSpecIcon(spec)}{' '}
                    {friendly.name} ({spec.specName} {spec.className})
                  </Link>

              );
            })
        }

    );
  }
}

export default PlayerSelectionList;
<|endoftext|>


<commit_before>// utils
// var debug = require('debug').enable('*')
// var extend = require('extend')

// sys
// var createWindow = require('./sys/createWindow')
// var loadImage = require('./sys/loadImage')
// var Time = require('./sys/Time')
// var Platform = require('./sys/Platform')

// glu
// var Program = require('./glu/Program')
// var VertexArray = require('./glu/VertexArray')
// var Context = require('./glu/Context')
// var ClearCommand = require('./glu/ClearCommand')
// var DrawCommand = require('./glu/DrawCommand')
// var TextureCube = require('./glu/TextureCube')
// var Framebuffer = require('./glu/Framebuffer')
// var Texture2D = require('./glu/Texture2D')
// var toVertexArray = require('./glu/createVertexArrayFromGeometry')

// geom
// var createCube = require('./agen/createCube')
// var createFSQ = require('./vgen/createFullScreenQuad')
const createCube = require('primitive-cube')
const bunny = require('bunny')
const normals = require('normals')
const centerAndNormalize = require('geom-center-and-normalize')
const Vec3 = require('pex-math/Vec3')
const SimplexNoise = require('simplex-noise')
const flatten = require('flatten')

// math
// var createMat4 = require('gl-mat4/create')
// var lookAt = require('gl-mat4/lookAt')
// var perspective = require('gl-mat4/perspective')
// var translate = require('gl-mat4/translate')
// var copy3 = require('gl-vec3/copy')

// shaders
// var glslify = require('glslify-promise')

const createContext = require('../../pex-context')
const raf = require('raf')
const createCamera = require('pex-cam/perspective')
const createOrbiter = require('pex-cam/orbiter')
const Mat4 = require('pex-math/Mat4')
// const load = require('pex-io/load')
const glsl = require('glslify')
// const isBrowser = require('is-browser')

const ctx = createContext()

let elapsedSeconds = 0
let prevTime = Date.now()
const noise = new SimplexNoise()

const camera = createCamera({
  fov: 45, // TODO: change fov to radians
  aspect: ctx.gl.canvas.width / ctx.gl.canvas.height,
  position: [3, 0.5, 3],
  target: [0, 0, 0]
})

createOrbiter({ camera: camera, distance: 10 })

const lightCamera = createCamera({
  fov: 45, // TODO: change fov to radians,
  aspect: 1,
  near: 1,
  far: 50,
  position: [7, 4, 7],
  target: [0, 0, 0]
})

const depthMapSize = 512
const depthMap = ctx.texture2D({
  width: depthMapSize, height: depthMapSize,
  format: ctx.PixelFormat.Depth
})
const colorMap = ctx.texture2D({ width: depthMapSize, height: depthMapSize })

// TODO: i could probably replace framebuffer with color, depth, stencil attachments props
// same way we don't declare vertex array, fbo would be created on demand?
const depthPassCmd = {
  name: 'depthPass',
  pass: ctx.pass({
    color: [ colorMap ],
    depth: depthMap,
    clearColor: [0, 0, 0, 1],
    clearDepth: 1
  })
}

const showNormalsVert = glsl(__dirname + '/glsl/show-normals.vert')
const showNormalsFrag = glsl(__dirname + '/glsl/show-normals.frag')
const shadowMappedVert = glsl(__dirname + '/glsl/shadow-mapped.vert')
const shadowMappedFrag = glsl(__dirname + '/glsl/shadow-mapped.frag')
// BlitVert: glslify(__dirname + '/sh/materials/Blit.vert'),
// BlitFrag: glslify(__dirname + '/sh/materials/Blit.frag')

const clearCmd = {
  pass: ctx.pass({
    clearColor: [0.5, 0.5, 0.5, 1.0],
    clearDepth: 1
  })
}

const floor = createCube(5, 0.1, 5)
const floorPositionsBuf = ctx.vertexBuffer(floor.positions)
const floorNormalsBuf = ctx.vertexBuffer(floor.normals)

const shadowMappedPipeline = ctx.pipeline({
  vert: shadowMappedVert,
  frag: shadowMappedFrag,
  depthEnabled: true
})

const drawDepthPipeline = ctx.pipeline({
  vert: showNormalsVert,
  frag: showNormalsFrag,
  depthEnabled: true
})

const drawFloorCmd = {
  name: 'drawFloor',
  pipeline: shadowMappedPipeline,
  uniforms: {
    uProjectionMatrix: camera.projectionMatrix,
    uViewMatrix: camera.viewMatrix,
    uModelMatrix: Mat4.create(),
    uWrap: 0,
    uLightNear: lightCamera.near,
    uLightFar: lightCamera.far,
    uLightProjectionMatrix: lightCamera.projectionMatrix,
    uLightViewMatrix: lightCamera.viewMatrix,
    uLightPos: lightCamera.position,
    uDepthMap: depthMap,
    uAmbientColor: [0, 0, 0, 1],
    uDiffuseColor: [1, 1, 1, 1]
  },
  attributes: {
    aPosition: {
      buffer: floorPositionsBuf
    },
    aNormal: {
      buffer: floorNormalsBuf
    }
  },
  indices: {
    buffer: ctx.indexBuffer(floor.cells)
  }
}

const drawFloorDepthCmd = {
  name: 'drawFloorDepth',
  pipeline: drawDepthPipeline,
  uniforms: {
    uProjectionMatrix: lightCamera.projectionMatrix,
    uViewMatrix: lightCamera.viewMatrix,
    uModelMatrix: Mat4.create()
  },
  attributes: {
    aPosition: {
      buffer: floorPositionsBuf
    },
    aNormal: {
      buffer: floorNormalsBuf
    }
  },
  indices: {
    buffer: ctx.indexBuffer(floor.cells)
  }
}

const bunnyBaseVertices = centerAndNormalize(bunny.positions).map((p) => Vec3.scale(p, 2))
const bunnyBaseNormals = normals.vertexNormals(bunny.cells, bunny.positions)
const bunnyNoiseVertices = centerAndNormalize(bunny.positions).map((p) => Vec3.scale(p, 2))

const bunnyPositionBuffer = ctx.vertexBuffer(bunnyBaseVertices)
const bunnyNormalBuffer = ctx.vertexBuffer(bunnyBaseNormals)

const drawBunnyCmd = {
  name: 'drawBunny',
  pipeline: shadowMappedPipeline,
  uniforms: {
    uProjectionMatrix: camera.projectionMatrix,
    // FIXME: because we pass by reference this matrix will keep updating without us
    // doing anything, is that but or a feature? Should i cache and force uViewMatrix: () => camera.viewMatrix
    // to mark the uniform as "dynamic" ?
    uViewMatrix: camera.viewMatrix,
    uModelMatrix: Mat4.translate(Mat4.create(), [0, 1, 0]),
    uWrap: 0,
    uLightNear: lightCamera.near,
    uLightFar: lightCamera.far,
    uLightProjectionMatrix: lightCamera.projectionMatrix,
    uLightViewMatrix: lightCamera.viewMatrix,
    uLightPos: lightCamera.position,
    uDepthMap: depthMap,
    uAmbientColor: [0, 0, 0, 1],
    uDiffuseColor: [1, 1, 1, 1]
  },
  attributes: {
    aPosition: {
      buffer: bunnyPositionBuffer
    },
    aNormal: {
      buffer: bunnyNormalBuffer
    }
  },
  indices: {
    buffer: ctx.indexBuffer(bunny.cells)
  }
}

const drawBunnyDepthCmd = {
  name: 'drawBunnyDepth',
  pipeline: drawDepthPipeline,
  uniforms: {
    uProjectionMatrix: lightCamera.projectionMatrix,
    uViewMatrix: lightCamera.viewMatrix,
    uModelMatrix: Mat4.translate(Mat4.create(), [0, 1, 0])
  },
  attributes: {
    aPosition: {
      buffer: bunnyPositionBuffer
    },
    aNormal: {
      buffer: bunnyNormalBuffer
    }
  },
  // FIXME: rename this to indexBuffer?
  indices: {
    buffer: ctx.indexBuffer(bunny.cells)
  }
}

function updateTime () {
  const now = Date.now()
  const deltaTime = (now - prevTime) / 1000
  elapsedSeconds += deltaTime
  prevTime = now
}

function updateCamera () {
  const t = elapsedSeconds / 10
  const x = 6 * Math.cos(Math.PI * t)
  const y = 3
  const z = 6 * Math.sin(Math.PI * t)
  camera({ position: [x, y, z] })
}

function updateBunny (ctx) {
  const noiseFrequency = 1
  const noiseScale = 0.1
  for (let i = 0; i < bunnyBaseVertices.length; i++) {
    var v = bunnyNoiseVertices[i]
    var n = bunnyBaseNormals[i]
    Vec3.set(v, bunnyBaseVertices[i])
    var f = noise.noise3D(v[0] * noiseFrequency, v[1] * noiseFrequency, v[2] * noiseFrequency + elapsedSeconds)
    v[0] += n[0] * noiseScale * (f + 1)
    v[1] += n[1] * noiseScale * (f + 1)
    v[2] += n[2] * noiseScale * (f + 1)
  }

  // FIXME: pre-allocate buffer
  // FIXME: add update command
  const positionData = new Float32Array(flatten(bunnyNoiseVertices))
  // bunnyPositionBuffer.bufferData(positionData)
  ctx.update(bunnyPositionBuffer, { data: positionData })

  // Update options:
  // 1) direct update buffer
  // bunnyPositionBuffer.bufferData(positionData)
  //
  // 2) direct update via ctx
  // ctx.update(bunnyPositionBuffer, { data: positionData })
  //
  // 3) update command
  // const updateCommand = ctx.update({ target: bunnyPositionBuffer, data: positionData })
  // ctx.submit(updatePositions)

  // FIXME: pre-allocate buffer
  // FIXME: add update command
  // What are the update patterns in other APIs?
  const normalData = new Float32Array(flatten(normals.vertexNormals(bunny.cells, bunnyNoiseVertices)))
  // bunnyNormalBuffer.bufferData(normalData)
  ctx.update(bunnyNormalBuffer, { data: normalData })
}

const drawFullscreenQuadCmd = {
  name: 'drawFullscreenQuad',
  pipeline: ctx.pipeline({
    vert: glsl(__dirname + '/glsl/screen-image.vert'),
    frag: glsl(__dirname + '/glsl/screen-image.frag'),
    depthEnabled: false
  }),
  attributes: {
    // aPosition: { buffer: ctx.vertexBuffer(new Float32Array(flatten([[-1, -1], [1, -1], [1, 1], [-1, 1]]))) },
    aPosition: { buffer: ctx.vertexBuffer([[-1, -1], [-2 / 4, -1], [-2 / 4, -1 / 3], [-1, -1 / 3]]) },
    aTexCoord0: { buffer: ctx.vertexBuffer([[0, 0], [1, 0], [1, 1], [ 0, 1]]) }
  },
  indices: {
    buffer: ctx.indexBuffer([[0, 1, 2], [0, 2, 3]])
  },
  uniforms: {
    uTexture: colorMap
  }
}

// console.time('frame')

let frameNumber = 0

raf(function frame () {
  // console.timeEnd('frame')
  // console.time('frame')
  updateTime()
  updateCamera()
  updateBunny(ctx)
  ctx.debug((++frameNumber) === 1)
  ctx.submit(depthPassCmd, () => {
    ctx.submit(drawFloorDepthCmd)
    ctx.submit(drawBunnyDepthCmd)
  })
  ctx.submit(clearCmd, () => {
    ctx.submit(drawFloorCmd)
    ctx.submit(drawBunnyCmd)
    ctx.submit(drawFullscreenQuadCmd)
  })
  raf(frame)
})
<commit_msg>Update shadows example with frame callback<commit_after>// utils
// var debug = require('debug').enable('*')
// var extend = require('extend')

// sys
// var createWindow = require('./sys/createWindow')
// var loadImage = require('./sys/loadImage')
// var Time = require('./sys/Time')
// var Platform = require('./sys/Platform')

// glu
// var Program = require('./glu/Program')
// var VertexArray = require('./glu/VertexArray')
// var Context = require('./glu/Context')
// var ClearCommand = require('./glu/ClearCommand')
// var DrawCommand = require('./glu/DrawCommand')
// var TextureCube = require('./glu/TextureCube')
// var Framebuffer = require('./glu/Framebuffer')
// var Texture2D = require('./glu/Texture2D')
// var toVertexArray = require('./glu/createVertexArrayFromGeometry')

// geom
// var createCube = require('./agen/createCube')
// var createFSQ = require('./vgen/createFullScreenQuad')
const createCube = require('primitive-cube')
const bunny = require('bunny')
const normals = require('normals')
const centerAndNormalize = require('geom-center-and-normalize')
const Vec3 = require('pex-math/Vec3')
const SimplexNoise = require('simplex-noise')
const flatten = require('flatten')

// math
// var createMat4 = require('gl-mat4/create')
// var lookAt = require('gl-mat4/lookAt')
// var perspective = require('gl-mat4/perspective')
// var translate = require('gl-mat4/translate')
// var copy3 = require('gl-vec3/copy')

// shaders
// var glslify = require('glslify-promise')

const createContext = require('../../pex-context')
const createCamera = require('pex-cam/perspective')
const createOrbiter = require('pex-cam/orbiter')
const Mat4 = require('pex-math/Mat4')
// const load = require('pex-io/load')
const glsl = require('glslify')
// const isBrowser = require('is-browser')

const ctx = createContext()

let elapsedSeconds = 0
let prevTime = Date.now()
const noise = new SimplexNoise()

const camera = createCamera({
  fov: 45, // TODO: change fov to radians
  aspect: ctx.gl.canvas.width / ctx.gl.canvas.height,
  position: [3, 0.5, 3],
  target: [0, 0, 0]
})

createOrbiter({ camera: camera, distance: 10 })

const lightCamera = createCamera({
  fov: 45, // TODO: change fov to radians,
  aspect: 1,
  near: 1,
  far: 50,
  position: [7, 4, 7],
  target: [0, 0, 0]
})

const depthMapSize = 512
const depthMap = ctx.texture2D({
  width: depthMapSize, height: depthMapSize,
  format: ctx.PixelFormat.Depth
})
const colorMap = ctx.texture2D({ width: depthMapSize, height: depthMapSize })

// TODO: i could probably replace framebuffer with color, depth, stencil attachments props
// same way we don't declare vertex array, fbo would be created on demand?
const depthPassCmd = {
  name: 'depthPass',
  pass: ctx.pass({
    color: [ colorMap ],
    depth: depthMap,
    clearColor: [0, 0, 0, 1],
    clearDepth: 1
  })
}

const showNormalsVert = glsl(__dirname + '/glsl/show-normals.vert')
const showNormalsFrag = glsl(__dirname + '/glsl/show-normals.frag')
const shadowMappedVert = glsl(__dirname + '/glsl/shadow-mapped.vert')
const shadowMappedFrag = glsl(__dirname + '/glsl/shadow-mapped.frag')
// BlitVert: glslify(__dirname + '/sh/materials/Blit.vert'),
// BlitFrag: glslify(__dirname + '/sh/materials/Blit.frag')

const clearCmd = {
  pass: ctx.pass({
    clearColor: [0.5, 0.5, 0.5, 1.0],
    clearDepth: 1
  })
}

const floor = createCube(5, 0.1, 5)
const floorPositionsBuf = ctx.vertexBuffer(floor.positions)
const floorNormalsBuf = ctx.vertexBuffer(floor.normals)

const shadowMappedPipeline = ctx.pipeline({
  vert: shadowMappedVert,
  frag: shadowMappedFrag,
  depthEnabled: true
})

const drawDepthPipeline = ctx.pipeline({
  vert: showNormalsVert,
  frag: showNormalsFrag,
  depthEnabled: true
})

const drawFloorCmd = {
  name: 'drawFloor',
  pipeline: shadowMappedPipeline,
  uniforms: {
    uProjectionMatrix: camera.projectionMatrix,
    uViewMatrix: camera.viewMatrix,
    uModelMatrix: Mat4.create(),
    uWrap: 0,
    uLightNear: lightCamera.near,
    uLightFar: lightCamera.far,
    uLightProjectionMatrix: lightCamera.projectionMatrix,
    uLightViewMatrix: lightCamera.viewMatrix,
    uLightPos: lightCamera.position,
    uDepthMap: depthMap,
    uAmbientColor: [0, 0, 0, 1],
    uDiffuseColor: [1, 1, 1, 1]
  },
  attributes: {
    aPosition: {
      buffer: floorPositionsBuf
    },
    aNormal: {
      buffer: floorNormalsBuf
    }
  },
  indices: {
    buffer: ctx.indexBuffer(floor.cells)
  }
}

const drawFloorDepthCmd = {
  name: 'drawFloorDepth',
  pipeline: drawDepthPipeline,
  uniforms: {
    uProjectionMatrix: lightCamera.projectionMatrix,
    uViewMatrix: lightCamera.viewMatrix,
    uModelMatrix: Mat4.create()
  },
  attributes: {
    aPosition: {
      buffer: floorPositionsBuf
    },
    aNormal: {
      buffer: floorNormalsBuf
    }
  },
  indices: {
    buffer: ctx.indexBuffer(floor.cells)
  }
}

const bunnyBaseVertices = centerAndNormalize(bunny.positions).map((p) => Vec3.scale(p, 2))
const bunnyBaseNormals = normals.vertexNormals(bunny.cells, bunny.positions)
const bunnyNoiseVertices = centerAndNormalize(bunny.positions).map((p) => Vec3.scale(p, 2))

const bunnyPositionBuffer = ctx.vertexBuffer(bunnyBaseVertices)
const bunnyNormalBuffer = ctx.vertexBuffer(bunnyBaseNormals)

const drawBunnyCmd = {
  name: 'drawBunny',
  pipeline: shadowMappedPipeline,
  uniforms: {
    uProjectionMatrix: camera.projectionMatrix,
    // FIXME: because we pass by reference this matrix will keep updating without us
    // doing anything, is that but or a feature? Should i cache and force uViewMatrix: () => camera.viewMatrix
    // to mark the uniform as "dynamic" ?
    uViewMatrix: camera.viewMatrix,
    uModelMatrix: Mat4.translate(Mat4.create(), [0, 1, 0]),
    uWrap: 0,
    uLightNear: lightCamera.near,
    uLightFar: lightCamera.far,
    uLightProjectionMatrix: lightCamera.projectionMatrix,
    uLightViewMatrix: lightCamera.viewMatrix,
    uLightPos: lightCamera.position,
    uDepthMap: depthMap,
    uAmbientColor: [0, 0, 0, 1],
    uDiffuseColor: [1, 1, 1, 1]
  },
  attributes: {
    aPosition: {
      buffer: bunnyPositionBuffer
    },
    aNormal: {
      buffer: bunnyNormalBuffer
    }
  },
  indices: {
    buffer: ctx.indexBuffer(bunny.cells)
  }
}

const drawBunnyDepthCmd = {
  name: 'drawBunnyDepth',
  pipeline: drawDepthPipeline,
  uniforms: {
    uProjectionMatrix: lightCamera.projectionMatrix,
    uViewMatrix: lightCamera.viewMatrix,
    uModelMatrix: Mat4.translate(Mat4.create(), [0, 1, 0])
  },
  attributes: {
    aPosition: {
      buffer: bunnyPositionBuffer
    },
    aNormal: {
      buffer: bunnyNormalBuffer
    }
  },
  // FIXME: rename this to indexBuffer?
  indices: {
    buffer: ctx.indexBuffer(bunny.cells)
  }
}

function updateTime () {
  const now = Date.now()
  const deltaTime = (now - prevTime) / 1000
  elapsedSeconds += deltaTime
  prevTime = now
}

function updateCamera () {
  const t = elapsedSeconds / 10
  const x = 6 * Math.cos(Math.PI * t)
  const y = 3
  const z = 6 * Math.sin(Math.PI * t)
  camera({ position: [x, y, z] })
}

function updateBunny (ctx) {
  const noiseFrequency = 1
  const noiseScale = 0.1
  for (let i = 0; i < bunnyBaseVertices.length; i++) {
    var v = bunnyNoiseVertices[i]
    var n = bunnyBaseNormals[i]
    Vec3.set(v, bunnyBaseVertices[i])
    var f = noise.noise3D(v[0] * noiseFrequency, v[1] * noiseFrequency, v[2] * noiseFrequency + elapsedSeconds)
    v[0] += n[0] * noiseScale * (f + 1)
    v[1] += n[1] * noiseScale * (f + 1)
    v[2] += n[2] * noiseScale * (f + 1)
  }

  // FIXME: pre-allocate buffer
  // FIXME: add update command
  const positionData = new Float32Array(flatten(bunnyNoiseVertices))
  // bunnyPositionBuffer.bufferData(positionData)
  ctx.update(bunnyPositionBuffer, { data: positionData })

  // Update options:
  // 1) direct update buffer
  // bunnyPositionBuffer.bufferData(positionData)
  //
  // 2) direct update via ctx
  // ctx.update(bunnyPositionBuffer, { data: positionData })
  //
  // 3) update command
  // const updateCommand = ctx.update({ target: bunnyPositionBuffer, data: positionData })
  // ctx.submit(updatePositions)

  // FIXME: pre-allocate buffer
  // FIXME: add update command
  // What are the update patterns in other APIs?
  const normalData = new Float32Array(flatten(normals.vertexNormals(bunny.cells, bunnyNoiseVertices)))
  // bunnyNormalBuffer.bufferData(normalData)
  ctx.update(bunnyNormalBuffer, { data: normalData })
}

const drawFullscreenQuadCmd = {
  name: 'drawFullscreenQuad',
  pipeline: ctx.pipeline({
    vert: glsl(__dirname + '/glsl/screen-image.vert'),
    frag: glsl(__dirname + '/glsl/screen-image.frag'),
    depthEnabled: false
  }),
  attributes: {
    // aPosition: { buffer: ctx.vertexBuffer(new Float32Array(flatten([[-1, -1], [1, -1], [1, 1], [-1, 1]]))) },
    aPosition: { buffer: ctx.vertexBuffer([[-1, -1], [-2 / 4, -1], [-2 / 4, -1 / 3], [-1, -1 / 3]]) },
    aTexCoord0: { buffer: ctx.vertexBuffer([[0, 0], [1, 0], [1, 1], [ 0, 1]]) }
  },
  indices: {
    buffer: ctx.indexBuffer([[0, 1, 2], [0, 2, 3]])
  },
  uniforms: {
    uTexture: colorMap
  }
}

// console.time('frame')

let frameNumber = 0

ctx.frame(() => {
  // console.timeEnd('frame')
  // console.time('frame')
  updateTime()
  updateCamera()
  updateBunny(ctx)
  ctx.debug((++frameNumber) === 1)
  ctx.submit(depthPassCmd, () => {
    ctx.submit(drawFloorDepthCmd)
    ctx.submit(drawBunnyDepthCmd)
  })
  ctx.submit(clearCmd, () => {
    ctx.submit(drawFloorCmd)
    ctx.submit(drawBunnyCmd)
    ctx.submit(drawFullscreenQuadCmd)
  })
})
<|endoftext|>


<commit_before>/* eslint react/prop-types: 0 */

var React = require('react');
var Select = require('react-select');
var GravatarOption = require('./CustomOption');
var GravatarValue = require('./CustomSingleValue');

var STATES = require('./data/states');
var USERS = require('./data/users');
var id = 0;

function logChange(value) {
	console.log('Select value changed: ' + value);
}

var CountrySelect = React.createClass({
	onClick: function() {
		this.props.onSelect(this.props.value);
	},
	render: function() {
		var className = this.props.value === this.props.selected ? 'active' : 'link';
		return {this.props.children};
	}
});

var UsersField = React.createClass({
	getDefaultProps: function () {
		return {
			searchable: true,
			label: 'Users: (with custom option and value component)'
		};
	},
	render: function() {

		return (

				{this.props.label}
				<Select
					onOptionLabelClick={this.onLabelClick}
					placeholder="Select user"
					optionComponent={GravatarOption}
					singleValueComponent={GravatarValue}
					options={USERS.users}/>

		);
	}
});

var StatesField = React.createClass({
	getDefaultProps: function () {
		return {
			searchable: true,
			label: 'States:'
		};
	},
	getInitialState: function() {
		return {
			country: 'AU',
			disabled: false,
			id: ++id,
			selectValue: 'new-south-wales'
		};
	},
	switchCountry: function(newCountry) {
		console.log('Country changed to ' + newCountry);
		this.setState({
			country: newCountry,
			selectValue: null
		});
	},
	updateValue: function(newValue) {
		logChange('State changed to ' + newValue);
		this.setState({
			selectValue: newValue || null
		});
	},
	focusStateSelect: function() {
		this.refs.stateSelect.focus();
	},
	toggleDisabled: function(e) {
		this.setState({ disabled: e.target.checked });
	},
	render: function() {
		var ops = STATES[this.state.country];
		return (

				{this.props.label}
				<Select ref="stateSelect" options={ops} disabled={this.state.disabled} value={this.state.selectValue} onChange={this.updateValue} searchable={this.props.searchable} />

					Country:
					<CountrySelect value="AU" selected={this.state.country} onSelect={this.switchCountry}>Australia</CountrySelect>
					<CountrySelect value="US" selected={this.state.country} onSelect={this.switchCountry}>US</CountrySelect>
					  <button type="button" onClick={this.focusStateSelect}>Focus Select</button>
					  <input type="checkbox" checked={this.state.disabled} id={'disable-states-' + this.state.id} onChange={this.toggleDisabled}/>
					Disable

		);
	}
});

var RemoteSelectField = React.createClass({
	loadOptions: function(input, callback) {
		input = input.toLowerCase();
		var rtn = {
			options: [
				{ label: 'One', value: 'one' },
				{ label: 'Two', value: 'two' },
				{ label: 'Three', value: 'three' }
			],
			complete: true
		};
		if (input.slice(0, 1) === 'a') {
			if (input.slice(0, 2) === 'ab') {
				rtn = {
					options: [
						{ label: 'AB', value: 'ab' },
						{ label: 'ABC', value: 'abc' },
						{ label: 'ABCD', value: 'abcd' }
					],
					complete: true
				};
			} else {
				rtn = {
					options: [
						{ label: 'A', value: 'a' },
						{ label: 'AA', value: 'aa' },
						{ label: 'AB', value: 'ab' }
					],
					complete: false
				};
			}
		} else if (!input.length) {
			rtn.complete = false;
		}

		setTimeout(function() {
			callback(null, rtn);
		}, 500);
	},
	render: function() {
		return (

				{this.props.label}
				<Select asyncOptions={this.loadOptions} className="remote-example" />

		);
	}
});

var MultiSelectField = React.createClass({
	getInitialState: function() {
		return {
			disabled: false,
			value: []
		};
	},
	handleSelectChange: function(value, values) {
		logChange('New value:', value, 'Values:', values);
		this.setState({ value: value });
	},
	toggleDisabled: function(e) {
		this.setState({ 'disabled': e.target.checked });
	},
	render: function() {
		var ops = [
			{ label: 'Chocolate', value: 'chocolate' },
			{ label: 'Vanilla', value: 'vanilla' },
			{ label: 'Strawberry', value: 'strawberry' },
			{ label: 'Caramel', value: 'caramel' },
			{ label: 'Cookies and Cream', value: 'cookiescream' },
			{ label: 'Peppermint', value: 'peppermint' }
		];
		return (

					{this.props.label}
					<Select multi={true} disabled={this.state.disabled} value={this.state.value} placeholder="Select your favourite(s)" options={ops} onChange={this.handleSelectChange} />

					<input type="checkbox" checked={this.state.disabled} id="disable-multiselect" onChange={this.toggleDisabled}/>
					Disable

		);
	}
});

var SelectedValuesField = React.createClass({
	onLabelClick: function (data, event) {
		console.log(data, event);
	},
	render: function() {
		var ops = [
			{ label: 'Chocolate', value: 'chocolate' },
			{ label: 'Vanilla', value: 'vanilla' },
			{ label: 'Strawberry', value: 'strawberry' },
			{ label: 'Caramel', value: 'caramel' },
			{ label: 'Cookies and Cream', value: 'cookiescream' },
			{ label: 'Peppermint', value: 'peppermint' }
		];
		return (

				{this.props.label}
				<Select
					onOptionLabelClick={this.onLabelClick}
					value="chocolate,vanilla,strawberry"
					multi={true}
					placeholder="Select your favourite(s)"
					options={ops}
					onChange={logChange} />

		);
	}
});

var SelectedValuesFieldDisabled = React.createClass({
	onLabelClick: function (data, event) {
		console.log(data, event);
	},
	render: function() {
		var ops = [
			{ label: 'Chocolate', value: 'chocolate' },
			{ label: 'Vanilla', value: 'vanilla' },
			{ label: 'Strawberry', value: 'strawberry' },
			{ label: 'Caramel (You don\'t like it, apparently)', value: 'caramel', disabled: true },
			{ label: 'Cookies and Cream', value: 'cookiescream' },
			{ label: 'Peppermint', value: 'peppermint' }
		];
		return (

				{this.props.label}
				<Select
					onOptionLabelClick={this.onLabelClick}
					value="chocolate,vanilla,strawberry"
					multi={true}
					placeholder="Select your favourite(s)"
					options={ops}
					onChange={logChange} />

		);
	}
});

var SelectedValuesFieldCreate = React.createClass({
	onLabelClick: function (data, event) {
		console.log(data, event);
	},
	render: function() {
		var ops = [
			{ label: 'First Option', value: 'first' },
			{ label: 'Second Option', value: 'second' },
			{ label: 'Third Option', value: 'third' }
		];
		return (

				{this.props.label}
				<Select
					value="first"
					delimiter=","
					multi={true}
					allowCreate={true}
					placeholder="Select your favourite(s)"
					options={ops}
					onChange={logChange} />

		);
	}
});

var CustomRenderField = React.createClass({
	onLabelClick: function (data, event) {
		console.log(data, event);
	},
	renderOption: function(option) {
		return {option.label} ({option.hex});

	},
	renderValue: function(option) {
		return {option.label};
	},
	render: function() {
		var ops = [
			{ label: 'Red', value: 'red', hex: '#EC6230' },
			{ label: 'Green', value: 'green', hex: '#4ED84E' },
			{ label: 'Blue', value: 'blue', hex: '#6D97E2' }
		];
		return (

				{this.props.label}
				<Select
					allowCreate={true}
					placeholder="Select your favourite"
					options={ops}
					optionRenderer={this.renderOption}
					valueRenderer={this.renderValue}
					onChange={logChange} />

		);
	}
});

var CustomRenderMultiField = React.createClass({
	onLabelClick: function (data, event) {
		console.log(data, event);
	},
	renderOption: function(option) {
		return {option.label} ({option.hex});

	},
	renderValue: function(option) {
		return {option.label};
	},
	render: function() {
		var ops = [
			{ label: 'Red', value: 'red', hex: '#EC6230' },
			{ label: 'Green', value: 'green', hex: '#4ED84E' },
			{ label: 'Blue', value: 'blue', hex: '#6D97E2' }
		];
		return (

				{this.props.label}
				<Select
					delimiter=","
					multi={true}
					allowCreate={true}
					placeholder="Select your favourite(s)"
					options={ops}
					optionRenderer={this.renderOption}
					valueRenderer={this.renderValue}
					onChange={logChange} />

		);
	}
});

React.render(

		<StatesField />
		<StatesField label="States (non-searchable):" searchable={false} />
		<UsersField />
		<MultiSelectField label="Multiselect:"/>
		<SelectedValuesField label="Clickable labels (labels as links):" />
		<SelectedValuesFieldDisabled label="Disabled option:" />
		<SelectedValuesFieldCreate label="Option Creation (tags mode):" />
		<CustomRenderField label="Custom rendering for options and values:" />
		<CustomRenderMultiField label="Custom rendering for multiple options and values:" />
		<RemoteSelectField label="Remote Options:"/>

,
	document.getElementById('example')
);
<commit_msg>Add examples for values as numbers<commit_after>/* eslint react/prop-types: 0 */

var React = require('react');
var Select = require('react-select');
var GravatarOption = require('./CustomOption');
var GravatarValue = require('./CustomSingleValue');

var STATES = require('./data/states');
var USERS = require('./data/users');
var id = 0;

function logChange() {
	console.log.apply(console, [].concat(['Select value changed: '], Array.prototype.slice.apply(arguments)));
}

var CountrySelect = React.createClass({
	onClick: function() {
		this.props.onSelect(this.props.value);
	},
	render: function() {
		var className = this.props.value === this.props.selected ? 'active' : 'link';
		return {this.props.children};
	}
});

var UsersField = React.createClass({
	getDefaultProps: function () {
		return {
			searchable: true,
			label: 'Users: (with custom option and value component)'
		};
	},
	render: function() {

		return (

				{this.props.label}
				<Select
					onOptionLabelClick={this.onLabelClick}
					placeholder="Select user"
					optionComponent={GravatarOption}
					singleValueComponent={GravatarValue}
					options={USERS.users}/>

		);
	}
});

var ValuesAsNumbersField = React.createClass({

	getInitialState: function () {
		return {
			options: [
				{ value: 10, label: 'Ten' },
				{ value: 11, label: 'Eleven' },
				{ value: 12, label: 'Twelve' },
				{ value: 23, label: 'Twenty-three' },
				{ value: 24, label: 'Twenty-three' }
			],
			matchPos: 'any',
			matchValue: true,
			matchLabel: true,
			value: null,
			multi: false
		};
	},

	onChangeMatchStart(event) {
		this.setState({
			matchPos: event.target.checked ? 'start' : 'any'
		});
	},

	onChangeMatchValue(event) {
		this.setState({
			matchValue: event.target.checked
		});
	},

	onChangeMatchLabel(event) {
		this.setState({
			matchLabel: event.target.checked
		});
	},

	onChange(value, values) {
		this.setState({
			value: value
		});
		logChange(value, values);
	},

	onChangeMulti(event) {
		this.setState({
			multi: event.target.checked
		});
	},

	render: function () {

		var matchProp = 'any';

		if (this.state.matchLabel && !this.state.matchValue) {
			matchProp = 'label';
		}

		if (!this.state.matchLabel && this.state.matchValue) {
			matchProp = 'value';
		}

		return (

				{this.props.label}
				<Select
					searchable={true}
					matchProp={matchProp}
					matchPos={this.state.matchPos}
					options={this.state.options}
					onChange={this.onChange}
					value={this.state.value}
					multi={this.state.multi}
					/>

					Multi-Select?
					<input type="checkbox" id="values-as-numbers-multi" checked={this.state.multi} onChange={this.onChangeMulti} />
					Match only at start?
					<input type="checkbox" id="values-as-numbers-matchstart" checked={this.state.matchPos === 'start'} onChange={this.onChangeMatchStart} />
					Match value?
					<input type="checkbox" id="values-as-numbers-matchvalue" checked={this.state.matchValue} onChange={this.onChangeMatchValue} />
					Match label?
					<input type="checkbox" id="values-as-numbers-matchlabel" checked={this.state.matchLabel} onChange={this.onChangeMatchLabel} />

		);
	}
});

var StatesField = React.createClass({
	getDefaultProps: function () {
		return {
			searchable: true,
			label: 'States:'
		};
	},
	getInitialState: function() {
		return {
			country: 'AU',
			disabled: false,
			id: ++id,
			selectValue: 'new-south-wales'
		};
	},
	switchCountry: function(newCountry) {
		console.log('Country changed to ' + newCountry);
		this.setState({
			country: newCountry,
			selectValue: null
		});
	},
	updateValue: function(newValue) {
		logChange('State changed to ' + newValue);
		this.setState({
			selectValue: newValue || null
		});
	},
	focusStateSelect: function() {
		this.refs.stateSelect.focus();
	},
	toggleDisabled: function(e) {
		this.setState({ disabled: e.target.checked });
	},
	render: function() {
		var ops = STATES[this.state.country];
		return (

				{this.props.label}
				<Select ref="stateSelect" options={ops} disabled={this.state.disabled} value={this.state.selectValue} onChange={this.updateValue} searchable={this.props.searchable} />

					Country:
					<CountrySelect value="AU" selected={this.state.country} onSelect={this.switchCountry}>Australia</CountrySelect>
					<CountrySelect value="US" selected={this.state.country} onSelect={this.switchCountry}>US</CountrySelect>
					  <button type="button" onClick={this.focusStateSelect}>Focus Select</button>
					  <input type="checkbox" checked={this.state.disabled} id={'disable-states-' + this.state.id} onChange={this.toggleDisabled}/>
					Disable

		);
	}
});

var RemoteSelectField = React.createClass({
	loadOptions: function(input, callback) {
		input = input.toLowerCase();
		var rtn = {
			options: [
				{ label: 'One', value: 'one' },
				{ label: 'Two', value: 'two' },
				{ label: 'Three', value: 'three' }
			],
			complete: true
		};
		if (input.slice(0, 1) === 'a') {
			if (input.slice(0, 2) === 'ab') {
				rtn = {
					options: [
						{ label: 'AB', value: 'ab' },
						{ label: 'ABC', value: 'abc' },
						{ label: 'ABCD', value: 'abcd' }
					],
					complete: true
				};
			} else {
				rtn = {
					options: [
						{ label: 'A', value: 'a' },
						{ label: 'AA', value: 'aa' },
						{ label: 'AB', value: 'ab' }
					],
					complete: false
				};
			}
		} else if (!input.length) {
			rtn.complete = false;
		}

		setTimeout(function() {
			callback(null, rtn);
		}, 500);
	},
	render: function() {
		return (

				{this.props.label}
				<Select asyncOptions={this.loadOptions} className="remote-example" />

		);
	}
});

var MultiSelectField = React.createClass({
	getInitialState: function() {
		return {
			disabled: false,
			value: []
		};
	},
	handleSelectChange: function(value, values) {
		logChange('New value:', value, 'Values:', values);
		this.setState({ value: value });
	},
	toggleDisabled: function(e) {
		this.setState({ 'disabled': e.target.checked });
	},
	render: function() {
		var ops = [
			{ label: 'Chocolate', value: 'chocolate' },
			{ label: 'Vanilla', value: 'vanilla' },
			{ label: 'Strawberry', value: 'strawberry' },
			{ label: 'Caramel', value: 'caramel' },
			{ label: 'Cookies and Cream', value: 'cookiescream' },
			{ label: 'Peppermint', value: 'peppermint' }
		];
		return (

					{this.props.label}
					<Select multi={true} disabled={this.state.disabled} value={this.state.value} placeholder="Select your favourite(s)" options={ops} onChange={this.handleSelectChange} />

					<input type="checkbox" checked={this.state.disabled} id="disable-multiselect" onChange={this.toggleDisabled}/>
					Disable

		);
	}
});

var SelectedValuesField = React.createClass({
	onLabelClick: function (data, event) {
		console.log(data, event);
	},
	render: function() {
		var ops = [
			{ label: 'Chocolate', value: 'chocolate' },
			{ label: 'Vanilla', value: 'vanilla' },
			{ label: 'Strawberry', value: 'strawberry' },
			{ label: 'Caramel', value: 'caramel' },
			{ label: 'Cookies and Cream', value: 'cookiescream' },
			{ label: 'Peppermint', value: 'peppermint' }
		];
		return (

				{this.props.label}
				<Select
					onOptionLabelClick={this.onLabelClick}
					value="chocolate,vanilla,strawberry"
					multi={true}
					placeholder="Select your favourite(s)"
					options={ops}
					onChange={logChange} />

		);
	}
});

var SelectedValuesFieldDisabled = React.createClass({
	onLabelClick: function (data, event) {
		console.log(data, event);
	},
	render: function() {
		var ops = [
			{ label: 'Chocolate', value: 'chocolate' },
			{ label: 'Vanilla', value: 'vanilla' },
			{ label: 'Strawberry', value: 'strawberry' },
			{ label: 'Caramel (You don\'t like it, apparently)', value: 'caramel', disabled: true },
			{ label: 'Cookies and Cream', value: 'cookiescream' },
			{ label: 'Peppermint', value: 'peppermint' }
		];
		return (

				{this.props.label}
				<Select
					onOptionLabelClick={this.onLabelClick}
					value="chocolate,vanilla,strawberry"
					multi={true}
					placeholder="Select your favourite(s)"
					options={ops}
					onChange={logChange} />

		);
	}
});

var SelectedValuesFieldCreate = React.createClass({
	onLabelClick: function (data, event) {
		console.log(data, event);
	},
	render: function() {
		var ops = [
			{ label: 'First Option', value: 'first' },
			{ label: 'Second Option', value: 'second' },
			{ label: 'Third Option', value: 'third' }
		];
		return (

				{this.props.label}
				<Select
					value="first"
					delimiter=","
					multi={true}
					allowCreate={true}
					placeholder="Select your favourite(s)"
					options={ops}
					onChange={logChange} />

		);
	}
});

var CustomRenderField = React.createClass({
	onLabelClick: function (data, event) {
		console.log(data, event);
	},
	renderOption: function(option) {
		return {option.label} ({option.hex});

	},
	renderValue: function(option) {
		return {option.label};
	},
	render: function() {
		var ops = [
			{ label: 'Red', value: 'red', hex: '#EC6230' },
			{ label: 'Green', value: 'green', hex: '#4ED84E' },
			{ label: 'Blue', value: 'blue', hex: '#6D97E2' }
		];
		return (

				{this.props.label}
				<Select
					allowCreate={true}
					placeholder="Select your favourite"
					options={ops}
					optionRenderer={this.renderOption}
					valueRenderer={this.renderValue}
					onChange={logChange} />

		);
	}
});

var CustomRenderMultiField = React.createClass({
	onLabelClick: function (data, event) {
		console.log(data, event);
	},
	renderOption: function(option) {
		return {option.label} ({option.hex});

	},
	renderValue: function(option) {
		return {option.label};
	},
	render: function() {
		var ops = [
			{ label: 'Red', value: 'red', hex: '#EC6230' },
			{ label: 'Green', value: 'green', hex: '#4ED84E' },
			{ label: 'Blue', value: 'blue', hex: '#6D97E2' }
		];
		return (

				{this.props.label}
				<Select
					delimiter=","
					multi={true}
					allowCreate={true}
					placeholder="Select your favourite(s)"
					options={ops}
					optionRenderer={this.renderOption}
					valueRenderer={this.renderValue}
					onChange={logChange} />

		);
	}
});

React.render(

		<StatesField />
		<StatesField label="States (non-searchable):" searchable={false} />
		<UsersField />
		<ValuesAsNumbersField label="Values as numbers" />
		<MultiSelectField label="Multiselect:"/>
		<SelectedValuesField label="Clickable labels (labels as links):" />
		<SelectedValuesFieldDisabled label="Disabled option:" />
		<SelectedValuesFieldCreate label="Option Creation (tags mode):" />
		<CustomRenderField label="Custom rendering for options and values:" />
		<CustomRenderMultiField label="Custom rendering for multiple options and values:" />
		<RemoteSelectField label="Remote Options:"/>

,
	document.getElementById('example')
);
<|endoftext|>