<commit_before>/*! core 2014-04-08 */
!function(scope){function ver(a,b,c,d){var e=a.exec(ua);return null===e||"object"!=typeof e||"number"!=typeof e.length?!0:"number"==typeof e.length&&e.length>=b+1?c&&d?e[b].replace(c,d):e[b]:!0}if(scope.core||(scope.core={}),!scope.core.selector)try{scope.core.selector="undefined"!=typeof jQuery?jQuery:"undefined"!=typeof $?$:document.querySelectorAll||null}catch(err){scope.core.selector=document.querySelectorAll||null}if("function"!=typeof String.prototype.trim&&(String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g,"")}),Function.prototype.bind||(Function.prototype.bind=function(a){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var b=Array.prototype.slice.call(arguments,1),c=this,d=function(){},e=function(){return c.apply(this instanceof d?this:a||window,b.concat(Array.prototype.slice.call(arguments)))};return d.prototype=this.prototype,e.prototype=new d,e}),Function.prototype.inherits=function(a){var b=function(){};b.prototype=new a({__inheriting__:!0}),this.prototype=b.prototype,this.prototype.constructor=this,b=null},Function.prototype.augment=function(a){for(var b in a)this.prototype[b]=a[b]},"undefined"!=typeof navigator){var N=navigator.appName,ua=navigator.userAgent,tem,M=ua.match(/(opera|chrome|safari|firefox|msie|trident)\/?\s*(\.?\d+(\.\d+)*)/i);M&&null!=(tem=ua.match(/version\/([\.\d]+)/i))&&(M[2]=tem[1]),M=M?[M[1],M[2]]:[N,navigator.appVersion,"-?"];var br={mobile:!1,ios:!1,iphone:!1,ipad:!1,android:!1,webos:!1,mac:!1,windows:!1,other:!0,name:"trident"==M[0].toLowerCase()?"msie":M[0].toLowerCase(),version:M[1].split(".")[0],touch:"ontouchstart"in window,full_version:M[1]};/mobile/i.test(ua)&&(br.mobile=!0),/like Mac OS X/.test(ua)&&(br.ios=ver(/CPU( iPhone)? OS ([0-9\._]+) like Mac OS X/,2,/_/g,"."),br.iphone=/iPhone/.test(ua),br.ipad=/iPad/.test(ua)),/Android/.test(ua)&&(br.android=ver(/Android ([0-9\.]+)[\);]/,1)),/webOS\//.test(ua)&&(br.webos=ver(/webOS\/([0-9\.]+)[\);]/,1)),/(Intel|PPC) Mac OS X/.test(ua)&&(br.mac=ver(/(Intel|PPC) Mac OS X ?([0-9\._]*)[\)\;]/,2,/_/g,".")),/Windows NT/.test(ua)&&(br.windows=ver(/Windows NT ([0-9\._]+)[\);]/,1));for(var key in br)"Other"!==key&&"Mobile"!==key&&br[key]!==!1&&(br.other=!1);scope.core.browser=br,scope.core.browser["trident"==M[0].toLowerCase()?"msie":M[0].toLowerCase()]={version:M[1].split(".")[0]}}scope.core.GUID=function(){for(var a=[],b="0123456789abcdef",c=0;36>c;c++)a[c]=b.substr(Math.floor(16*Math.random()),1);a[14]="4",a[19]=b.substr(3&a[19]|8,1),a[8]=a[13]=a[18]=a[23]="-";var d=a.join("");return d},scope.core.rect=function(a){return a instanceof Array?a[0].getBoundingClientRect():a.getBoundingClientRect()},scope.core.registerNamespace=function(a,b){var c=a.split("."),d=c.shift();scope[d]||(scope[d]={});for(var e=scope[d];c.length>1;){var f=c.shift();e[f]||(e[f]={}),e=e[f]}var g=c.shift();e[g]=b||{}},scope.core.import=function(a){for(var b=a.split("."),c=scope;b.length;){var d=b.shift();if(!c[d])return null;c=c[d]}return c},scope.JSON||(scope.JSON={}),function(){function f(a){return 10>a?"0"+a:a}function quote(a){return escapable.lastIndex=0,escapable.test(a)?'"'+a.replace(escapable,function(a){var b=meta[a];return"string"==typeof b?b:"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+a+'"'}function str(a,b){var c,d,e,f,g,h=gap,i=b[a];switch(i&&"object"==typeof i&&"function"==typeof i.toJSON&&(i=i.toJSON(a)),"function"==typeof rep&&(i=rep.call(b,a,i)),typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";if(gap+=indent,g=[],"[object Array]"===Object.prototype.toString.apply(i)){for(f=i.length,c=0;f>c;c+=1)g[c]=str(c,i)||"null";return e=0===g.length?"[]":gap?"[\n"+gap+g.join(",\n"+gap)+"\n"+h+"]":"["+g.join(",")+"]",gap=h,e}if(rep&&"object"==typeof rep)for(f=rep.length,c=0;f>c;c+=1)d=rep[c],"string"==typeof d&&(e=str(d,i),e&&g.push(quote(d)+(gap?": ":":")+e));else for(d in i)Object.hasOwnProperty.call(i,d)&&(e=str(d,i),e&&g.push(quote(d)+(gap?": ":":")+e));return e=0===g.length?"{}":gap?"{\n"+gap+g.join(",\n"+gap)+"\n"+h+"}":"{"+g.join(",")+"}",gap=h,e}}"function"!=typeof Date.prototype.toJSON&&(Date.prototype.toJSON=function(){return isFinite(this.valueOf())?this.getUTCFullYear()+"-"+f(this.getUTCMonth()+1)+"-"+f(this.getUTCDate())+"T"+f(this.getUTCHours())+":"+f(this.getUTCMinutes())+":"+f(this.getUTCSeconds())+"Z":null},String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(){return this.valueOf()});var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={"\b":"\\b","	":"\\t","\n":"\\n","\f":"\\f","\r":"\\r",'"':'\\"',"\\":"\\\\"},rep;"function"!=typeof JSON.stringify&&(JSON.stringify=function(a,b,c){var d;if(gap="",indent="","number"==typeof c)for(d=0;c>d;d+=1)indent+=" ";else"string"==typeof c&&(indent=c);if(rep=b,b&&"function"!=typeof b&&("object"!=typeof b||"number"!=typeof b.length))throw new Error("JSON.stringify");return str("",{"":a})}),"function"!=typeof JSON.parse&&(JSON.parse=function(text,reviver){function walk(a,b){var c,d,e=a[b];if(e&&"object"==typeof e)for(c in e)Object.hasOwnProperty.call(e,c)&&(d=walk(e,c),void 0!==d?e[c]=d:delete e[c]);return reviver.call(a,b,e)}var j;if(text=String(text),cx.lastIndex=0,cx.test(text)&&(text=text.replace(cx,function(a){return"\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4)})),/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return j=eval("("+text+")"),"function"==typeof reviver?walk({"":j},""):j;throw new SyntaxError("JSON.parse")})}()}("undefined"!=typeof process&&void 0!==process.arch?GLOBAL:window),console||(console={log:function(){},warn:function(){},trace:function(){}}),function(a){function b(b){if(!b||!b.__inheriting__){b&&b.el&&(this.el="string"!=typeof b.el||"undefined"==typeof jQuery&&"undefined"==typeof a?"string"==typeof b.el&&"undefined"!=typeof jQuery&&"undefined"!=typeof a?document.querySelectorAll(b.el):b.el:a(b.el)),this.proxyHandlers={},this.construct(b);var c=this;setTimeout(function(){c.delayedConstruct&&c.delayedConstruct(b)},0)}}b.prototype._=b.prototype.getProxyHandler=function(a){return this.proxyHandlers[a]||("function"==typeof this[a]?this.proxyHandlers[a]=this[a].bind(this):console.warn("Warning: attempt to create non existing method as proxy "+a)),this.proxyHandlers[a]},b.prototype.clearProxyHandler=function(a){var b=this.proxyHandlers[a];return null===b&&console.warn("Warning: attempt to clear a non-existing proxy - "+a),this.proxyHandlers[a]=null,delete this.proxyHandlers[a],b},b.prototype.construct=function(){},b.prototype.delayedConstruct=function(){},b.prototype.dispose=function(){this.el=null;for(var a in this.proxyHandlers)this.proxyHandlers[a]=null,delete this.proxyHandlers[a]},core.registerNamespace("core.Core",b)}(core.selector,"undefined"!=typeof process&&void 0!==process.arch?GLOBAL:window),function(){function a(a){b.call(this,a)}var b=core.Core,c=b.prototype;a.inherits(b);var d=a.prototype;d.construct=function(a){c.construct.call(this,a),this.events={}},d.dispose=function(){c.dispose.call(this),this.removeAll(),this.events=null};var e=function(a,b){for(var c=a.length,d=0;c>d;d++)if(a[d].scope===b)return a[d];return b.__signal__id=core.GUID(),-1},f=function(a,b,c,d){var f=null,g=e.call(this,this.events[a+(d?"_once":"")],b);-1===g&&b.dispose?(f=b.dispose,b.dispose=function(){var a=Array.prototype.shift.call(arguments),c=Array.prototype.shift.call(arguments);return c.removeScope(this,b),c=null,a=null,f.apply(this,arguments)}.bind(b,c,this),this.events[a+(d?"_once":"")].push({method:c,scope:b,dispose_orig:f})):-1!==g?g.method!=c&&this.events[a+(d?"_once":"")].push({method:c,scope:g.scope,dispose_orig:g.dispose_orig}):this.events[a+(d?"_once":"")].push({method:c,scope:b,dispose_orig:null})};d.on=d.add=function(a,b,c){this.events[a]||(this.events[a]=[]),f.call(this,a,b,c)},d.onceOn=d.addOnce=function(a,b,c){this.events[a+"_once"]||(this.events[a+"_once"]=[]),f.call(this,a,b,c,!0)};var g=function(a,b,c){if(this.events[a]){for(var d=this.events[a].length;d--;)if(this.events[a][d].scope===b&&this.events[a][d].method===c){var e=this.events[a].splice(d,1).pop();e.scope.dispose&&e.dispose_orig&&(e.scope.dispose=e.dispose_orig),delete e.scope.__signal__id,e.scope=null,e.dispose_orig=null,delete e.dispose_orig,e=null}0===this.events[a].length&&delete this.events[a]}};d.off=d.remove=function(a,b,c){g.call(this,a,b,c),g.call(this,a+"_once",b,c)},d.removeScope=function(a){for(var b in this.events){for(var c=this.events[b].length;c--;)if(this.events[b][c].scope===a){var d=this.events[b].splice(c,1).pop();d.scope.dispose&&d.dispose_orig&&(d.scope.dispose=d.dispose_orig),delete d.scope.__signal__id,d=null}0===this.events[b].length&&delete this.events[b]}},d.removeAll=function(){for(var a in this.events){for(this.events[a].length;this.events[a].length;){var b=this.events[a].shift();b.scope.dispose&&b.dispose_orig&&(b.scope.dispose=b.dispose_orig),delete b.__signal__id,b=null}0===this.events[a].length&&delete this.events[a]}this.events={}},d.dispatch=function(a,b){var c=b||{};if(c.type||(c.type=a),this.events&&this.events[a])for(var d=this.events[a],e=d.length,f=0;e>f;f++){var g=d[f];g.scope[g.method].call(g.scope,c),g=null}if(this.events&&this.events[a+"_once"]){for(var h=this.events[a+"_once"];h.length;){var g=h.shift();g.scope[g.method].call(g.scope,c),g=null}h.length||delete this.events[a+"_once"]}c=null},core.registerNamespace("core.events.Signal",a)}("undefined"!=typeof process&&void 0!==process.arch?GLOBAL:window),function(){function a(a){a&&a.__inheriting__||c.call(this,a)}var b=null,c=core.events.Signal,d=c.prototype;a.inherits(c);var e=a.prototype;e.construct=function(a){d.construct.call(this,a)},e.dispose=function(){d.dispose.call(this)};var f={init:function(){return null==b&&(b=new a),b}};f.instance=f.init,core.registerNamespace("core.events.EventChannel",f)}(core.selector,"undefined"!=typeof process&&void 0!==process.arch?GLOBAL:window),function(){function a(a){a&&a.__inheriting__||c.call(this,a)}var b=null,c=core.Core,d=c.prototype,e=function(a){for(a=0;4>a;a++)try{return a?new ActiveXObject([,"Msxml2","Msxml3","Microsoft"][a]+".XMLHTTP"):new XMLHttpRequest}catch(b){}};a.inherits(c);var f=a.prototype;f.construct=function(a){d.construct.call(this,a),this.settingsCache={}},f.dispose=function(){b=null,this.settingsCache=null,delete this.settingsCache,d.dispose.call(this)};var g=function(a,b){if("json"===b)try{return JSON.parse(a)}catch(c){return null}return"html"===b?a.trim():a},h=function(a){for(var b in this.settingsCache)a[b]||(a[b]=this.settingsCache[b]);return a};f.setConfig=function(a){this.settingsCache=a},f.request=function(a){if(void 0==a)throw new Error("XHR:Invalid parameters");a=h.call(this,a);var b=e(),c=a.method||"get";a.location=a.url||a.location;var d=a.async="undefined"!=typeof a.async?a.async:!0;b.queryString=a.data||null,b.open(c,a.location+(a.nocache?"?cache="+(new Date).getTime():""),d);try{b.setRequestHeader("X-Requested-With","XMLHttpRequest")}catch(f){throw new Error("XHR: "+f)}var i=a.dataType||a.format||"plain";"post"===c.toLowerCase()&&b.setRequestHeader("Content-Type",a.contentType||"application/x-www-form-urlencoded");var j=function(){if(4===b.readyState){if((0===b.status||200===b.status)&&a.callback(g.call(this,b.responseText,i)),/^[45]/.test(b.status))try{a.error()}catch(c){}b=null,a=null}else switch(b.readyState){case 1:try{a.requestSetup()}catch(c){}break;case 2:try{a.requestSent()}catch(c){}break;case 3:try{a.requestInProcess()}catch(c){}}};d&&(b.onreadystatechange=j,d||j());try{b.send(a.data||null)}catch(f){a.error(f),a=null,b=null}};var i={init:function(){return null==b&&(b=new a),b}};i.instance=i.init,core.registerNamespace("core.net.XHR",i)}(core.selector,"undefined"!=typeof process&&void 0!==process.arch?GLOBAL:window),function(a,b){function c(a){a&&a.__inheriting__||e.call(this,a)}function d(a,b){var c=!1,d=!0,e=a.document,f=e.documentElement,g=e.addEventListener?"addEventListener":"attachEvent",h=e.addEventListener?"removeEventListener":"detachEvent",i=e.addEventListener?"":"on",j=function(d){("readystatechange"!=d.type||"complete"==e.readyState)&&(("load"==d.type?a:e)[h](i+d.type,j,!1),!c&&(c=!0)&&b.call(a,d.type||d))},k=function(){try{f.doScroll("left")}catch(a){return void setTimeout(k,50)}j("poll")};if("complete"==e.readyState)b.call(a,"lazy");else{if(e.createEventObject&&f.doScroll){try{d=!a.frameElement}catch(l){}d&&k()}e[g](i+"DOMContentLoaded",j,!1),e[g](i+"readystatechange",j,!1),a[g](i+"load",j,!1)}}var e=core.import("core.Core"),f=e.prototype;c.inherits(e);var g=c.prototype;g.construct=function(a){f.construct.call(this,a),"undefined"!=typeof document&&d(window,this.getProxyHandler("onDocumentReady"))},g.dispose=function(){f.dispose.call(this)};var h=function(){var a=document.body;if(a.hasAttribute("core-app")||a.hasAttribute("data-root")){var c=Function.apply(b,["return "+a.getAttribute(a.hasAttribute("core-app")?"core-app":"data-root")])(),d=a.getAttribute("data-params")?JSON.parse(a.getAttribute("data-params")):{};d.el=a,window.__coreapp__=new c(d)}else i=null};g.onDocumentReady=function(a){a&&h.call(this)};var i=new c}(core.selector,"undefined"!=typeof process&&void 0!==process.arch?GLOBAL:window),function(a,b){function c(a){a&&a.__inheriting__||e.call(this,a)}function d(c){var d=function(c){for(var e,f,g=-1,h=c.length-1;g++<h;){var i=c[g];1==i.nodeType&&(i.getAttribute("core-module")&&i.getAttribute("id")&&!this[i.getAttribute("id")]?(e=Function.apply(b,["return "+i.getAttribute("core-module")])(),f=i.getAttribute("data-params")?JSON.parse(i.getAttribute("data-params")):{},f.el="undefined"!=typeof jQuery?a(i):i,this[i.getAttribute("id")]=new e(f)):i.getAttribute("core-module")&&!i.getAttribute("id")?(e=Function.apply(b,["return "+i.getAttribute("core-module")])(),f=i.getAttribute("data-params")?JSON.parse(i.getAttribute("data-params")):{},f.el="undefined"!=typeof jQuery?a(i):i,new e(f)):i.hasChildNodes()&&d(i.childNodes))}};d(c.childNodes)}var e=core.Core,f=e.prototype;c.inherits(e);var g=c.prototype;g.delayedConstruct=function(a){d.call(this,this.el),this.initialized(a)},g.dispose=function(){f.dispose.call(this)},g.find=function(a){if("querySelectorAll"in document)return document.querySelectorAll(a);var b=function(a){for(var c=-1,d=a.length-1;c++<d;){var e=a[c];1==e.nodeType,e.hasChildNodes()&&b(e.childNodes)}};b(this.el instanceof Array&&"undefined"!=typeof jQuery?this.el[0].childNodes:this.el.childNodes)},core.registerNamespace("core.delegates.Module",c)}(core.selector,"undefined"!=typeof process&&void 0!==process.arch?GLOBAL:window),function(a){for(var b=0,c=["webkit","moz"],d=0;d<c.length&&!a.requestAnimationFrame;++d)a.requestAnimationFrame=a[c[d]+"RequestAnimationFrame"],a.cancelAnimationFrame=a[c[d]+"CancelAnimationFrame"]||a[c[d]+"CancelRequestAnimationFrame"];a.requestAnimationFrame||(a.requestAnimationFrame=function(c){var d=(new Date).getTime(),e=Math.max(0,16-(d-b)),f=a.setTimeout(function(){c(d+e)},e);return b=d+e,f}),a.cancelAnimationFrame||(a.cancelAnimationFrame=function(a){clearTimeout(a)}),Math.randomFloat||(Math.randomFloat=function(a,b){return Math.random()*(b-a)+a}),Math.randomInt||(Math.randomInt=function(a,b){return Math.min(b,Math.floor(Math.random()*(1+b-a))+a)})}("undefined"!=typeof process&&void 0!==process.arch?GLOBAL:window);<commit_msg>Delete core.min.js<commit_after><|endoftext|>


<commit_before>'use strict';

/* Environment Vars
..............................*/
require('dotenv').load();

/* Packages
..............................*/
const async       = require('async');
const express     = require('express');
const session     = require('express-session');
const cookie      = require('cookie-parser');
const parser      = require('body-parser');
const uuid        = require('uuid');
const template    = require('../views/templates');
const vhx         = require('vhx')(process.env.VHX_API_KEY);
const app         = express();

/* Express App
..............................*/
app.set('port', process.env.PORT);
app.set('view engine', 'hbs');
app.use(express.static('public'));
app.use(parser.json());
app.use(parser.urlencoded({ extended: true }));

/* Express App - Session
..............................*/
app.use(cookie());
app.use(session({
  resave: false,
  saveUninitialized: false,
  genid: function() {
    return uuid.v4();
  },
  secret: process.env.SESSION_SECRET
}));

/* Routes
..................................... */
app.get('/', function(req, res) {

  let data = [];

  /* VHX > List Collections
  .....................................
  a call to vhx.collections to get back our collections
  http://dev.vhx.tv/docs/api/?javascript#collection-list
  ..................................... */
  vhx.collections.all({}, function(err, all_collections) {

    async.eachSeries(all_collections._embedded.collections, function(collection, callback) {

      /* VHX > List Collection Items
      .....................................
      We then interate over each collection and use its href to get
      back our collection items. Using async's eachSeries method
      allows us to do this sychronously until we trigger the next
      iteration by via the provided callback
      http://dev.vhx.tv/docs/api/?javascript#collection-items-list
      ..................................... */
      vhx.collections.listItems({
        collection: collection._links.self.href,
        product: 'https://api.vhx.tv/products/14444'
      }, function(err, items) {
        data.push({
          name: collection.name,
          items: items._embedded.items
        });

        callback();
      });

    /* DEMO > Complete Callback
    .....................................
    Render the collections data received back
    from the series of VHX API calls into the
    view using handlebars (see templates.js)
    ..................................... */
    }, function() {
      template(req, res, {
        layout: 'layout',
        yield: 'home/home',
        data: {
          collections: data
        }
      });
    });
  });

});

app.post('/join', function(req, res) {
  let redirect = req.body.redirect ? req.body.redirect : '/';

  // maybe prevent some spam (field should be empty)
  if (req.body.important.length > 0) {
    res.redirect('/');
    return;
  }

  /* DEMO > Payment and Account Authorization
  .....................................
  For the sake of this demo site, no actual payment information is
  is collected or authorized. For payments, we recommend using Stripe's
  payment api (http://stripe.com). We also mock login the user by
  creating a session with the customer vhx href. You'd save this href in
  your DB, along with any other user account info (i.e. username/password).
  For user account authorization we recommend http://passportjs.org/.
  ..................................... */

  if (req.body.customer) {

    /* VHX > Create Customer
    .....................................
    On post of the join form we can create a VHX Customer, associating
    the customer with our product, which gives the customer access to
    that project by enabling us to make authorization calls (see below)
    on line 165 in the /watch route
    http://http://dev.vhx.tv/docs/api/?javascript#customer-create
    ..................................... */
    vhx.customers.create({
      name: req.body.customer.name,
      email: req.body.customer.email,
      product: 'https://api.vhx.tv/products/14444'
    }, function(err, customer) {
      req.session.customer_href = 'https://api.vhx.tv/customers/' + customer.id;
      res.redirect(redirect);
    });
  }
});

app.post('/login', function(req, res) {

  // maybe prevent some spam (field should be empty)
  if (req.body.important.length > 0) {
    res.redirect('/');
    return;
  }

  /* DEMO > User Account Authorization
  .....................................
  For the sake of this demo site, no actual user authorization
  is implemented. You can authorize your users however you want.
  We recommend http://passportjs.org/.

  On POST to this route (/login), we mock login the user
  by creating a session with the customer vhx href. The customer href
  would be something you store in your own DB on account creation.
  ..................................... */
  req.session.customer_href = 'https://api.vhx.tv/customers/2041092';
  req.session.customer_email = req.body.customer.email;

  let url = req.query.redirect ? req.query.redirect : '/';
  res.redirect(url);
});

app.get('/logout', function(req, res) {
  req.session.destroy(function() {
    res.redirect('/');
  });
});

app.get('/watch/:video_id', function(req, res) {

  if (!req.params.video_id) {
    res.send('Not Found');
    return;
  }

  if (!req.session.customer_href) {
    template(req, res, {
      layout: 'layout',
      yield: 'watch/unauthorized'
    });
    return;
  }

  /* VHX > Authorize Customer
  .....................................
  a call to vhx.collections to get back our collection items
  http://dev.vhx.tv/docs/api/?javascript#authorizations-create
  ..................................... */
  vhx.authorizations.create({
    customer: req.session.customer_href,
    video: 'https://api.vhx.tv/videos/' + req.params.video_id
  }, function(err, authorization) {
    if (err) {
      template(req, res, {
        layout: 'layout',
        yield: 'watch/unauthorized'
      });
    } else {
      template(req, res, {
        layout: 'layout',
        yield: 'watch/player',
        data: {
          authorization: authorization
        }
      });
    }
  });
});

app.listen(process.env.PORT, function () {
  console.log("Web server listening on port " + process.env.PORT);
});
<commit_msg>wrap return of iterator in try catch to prevent random errors on async issues<commit_after>'use strict';

/* Environment Vars
..............................*/
require('dotenv').load();

/* Packages
..............................*/
const async       = require('async');
const express     = require('express');
const session     = require('express-session');
const cookie      = require('cookie-parser');
const parser      = require('body-parser');
const uuid        = require('uuid');
const template    = require('../views/templates');
const vhx         = require('vhx')(process.env.VHX_API_KEY);
const app         = express();

/* Express App
..............................*/
app.set('port', process.env.PORT);
app.set('view engine', 'hbs');
app.use(express.static('public'));
app.use(parser.json());
app.use(parser.urlencoded({ extended: true }));

/* Express App - Session
..............................*/
app.use(cookie());
app.use(session({
  resave: false,
  saveUninitialized: false,
  genid: function() {
    return uuid.v4();
  },
  secret: process.env.SESSION_SECRET
}));

/* Routes
..................................... */
app.get('/', function(req, res) {

  let data = [];

  /* VHX > List Collections
  .....................................
  a call to vhx.collections to get back our collections
  http://dev.vhx.tv/docs/api/?javascript#collection-list
  ..................................... */
  vhx.collections.all({}, function(err, all_collections) {

    async.eachSeries(all_collections._embedded.collections, function(collection, iterator) {

      /* VHX > List Collection Items
      .....................................
      We then interate over each collection and use its href to get
      back our collection items. Using async's eachSeries method
      allows us to do this sychronously until we trigger the next
      iteration via the provided callback
      http://dev.vhx.tv/docs/api/?javascript#collection-items-list
      ..................................... */
      vhx.collections.listItems({
        collection: collection._links.self.href,
        product: 'https://api.vhx.tv/products/14444'
      }, function(err, items) {
        data.push({
          name: collection.name,
          items: items._embedded.items
        });
        try {
          return iterator();
        } catch (e) {};
      });

    /* DEMO > Complete Callback
    .....................................
    Render the collections data received back
    from the series of VHX API calls into the
    view using handlebars (see templates.js)
    ..................................... */
    }, function() {
      template(req, res, {
        layout: 'layout',
        yield: 'home/home',
        data: {
          collections: data
        }
      });
    });
  });

});

app.post('/join', function(req, res) {
  let redirect = req.body.redirect ? req.body.redirect : '/';

  // maybe prevent some spam (field should be empty)
  if (req.body.important.length > 0) {
    res.redirect('/');
    return;
  }

  /* DEMO > Payment and Account Authorization
  .....................................
  For the sake of this demo site, no actual payment information is
  is collected or authorized. For payments, we recommend using Stripe's
  payment api (http://stripe.com). We also mock login the user by
  creating a session with the customer vhx href. You'd save this href in
  your DB, along with any other user account info (i.e. username/password).
  For user account authorization we recommend http://passportjs.org/.
  ..................................... */

  if (req.body.customer) {

    /* VHX > Create Customer
    .....................................
    On post of the join form we can create a VHX Customer, associating
    the customer with our product, which gives the customer access to
    that project by enabling us to make authorization calls (see below)
    on line 165 in the /watch route
    http://http://dev.vhx.tv/docs/api/?javascript#customer-create
    ..................................... */
    vhx.customers.create({
      name: req.body.customer.name,
      email: req.body.customer.email,
      product: 'https://api.vhx.tv/products/14444'
    }, function(err, customer) {
      req.session.customer_href = 'https://api.vhx.tv/customers/' + customer.id;
      res.redirect(redirect);
    });
  }
});

app.post('/login', function(req, res) {

  // maybe prevent some spam (field should be empty)
  if (req.body.important.length > 0) {
    res.redirect('/');
    return;
  }

  /* DEMO > User Account Authorization
  .....................................
  For the sake of this demo site, no actual user authorization
  is implemented. You can authorize your users however you want.
  We recommend http://passportjs.org/.

  On POST to this route (/login), we mock login the user
  by creating a session with the customer vhx href. The customer href
  would be something you store in your own DB on account creation.
  ..................................... */
  req.session.customer_href = 'https://api.vhx.tv/customers/2041092';
  req.session.customer_email = req.body.customer.email;

  let url = req.query.redirect ? req.query.redirect : '/';
  res.redirect(url);
});

app.get('/logout', function(req, res) {
  req.session.destroy(function() {
    res.redirect('/');
  });
});

app.get('/watch/:video_id', function(req, res) {

  if (!req.params.video_id) {
    res.send('Not Found');
    return;
  }

  if (!req.session.customer_href) {
    template(req, res, {
      layout: 'layout',
      yield: 'watch/unauthorized'
    });
    return;
  }

  /* VHX > Authorize Customer
  .....................................
  a call to vhx.collections to get back our collection items
  http://dev.vhx.tv/docs/api/?javascript#authorizations-create
  ..................................... */
  vhx.authorizations.create({
    customer: req.session.customer_href,
    video: 'https://api.vhx.tv/videos/' + req.params.video_id
  }, function(err, authorization) {
    if (err) {
      template(req, res, {
        layout: 'layout',
        yield: 'watch/unauthorized'
      });
    } else {
      template(req, res, {
        layout: 'layout',
        yield: 'watch/player',
        data: {
          authorization: authorization
        }
      });
    }
  });
});

app.listen(process.env.PORT, function () {
  console.log("Web server listening on port " + process.env.PORT);
});
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';

function topPosition(domElt) {
  if (!domElt) {
    return 0;
  }
  return domElt.offsetTop + topPosition(domElt.offsetParent);
}

var component;

export default class InfiniteScroll extends React.Component {
  constructor(props) {
    super(props);

    component = this;
  }
  componentDidMount() {
    this.pageLoaded = this.props.pageStart;
    this.attachScrollListener();
  }
  componentDidUpdate() {
    this.attachScrollListener();
  }
  render() {
    var props = this.props;
    return React.DOM.div(null, props.children, props.hasMore && (props.loader || this._defaultLoader));
  }
  scrollListener() {
    var el = ReactDOM.findDOMNode(component);
    var scrollEl = window;

    var offset;
    if(component.props.useWindow == true) {
      var scrollTop = (scrollEl.pageYOffset !== undefined) ? scrollEl.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
      offset = topPosition(el) + el.offsetHeight - scrollTop - window.innerHeight;
    } else {
      offset = el.offsetHeight - el.parentNode.scrollTop - el.parentNode.clientHeight;
    }

    if (offset < Number(component.props.threshold)) {
      component.detachScrollListener();
      // call loadMore after detachScrollListener to allow
      // for non-async loadMore functions
      component.props.loadMore(component.pageLoaded += 1);
    }
  }
  attachScrollListener() {
    if (!component.props.hasMore) {
      return;
    }

    var scrollEl = window;
    if(component.props.useWindow == false) {
      scrollEl = ReactDOM.findDOMNode(this).parentNode;
    }

    scrollEl.addEventListener('scroll', this.scrollListener);
    scrollEl.addEventListener('resize', this.scrollListener);
    this.scrollListener();
  }
  detachScrollListener() {
    var scrollEl = window;
    if(component.props.useWindow == false) {
      scrollEl = ReactDOM.findDOMNode(this).parentNode;
    }

    scrollEl.removeEventListener('scroll', this.scrollListener);
    scrollEl.removeEventListener('resize', this.scrollListener);
  }
  componentWillUnmount() {
    this.detachScrollListener();
  }
  setDefaultLoader(loader) {
    this._defaultLoader = loader;
  }
}
InfiniteScroll.PropTypes = {
  pageStart: React.PropTypes.number,
  hasMore: React.PropTypes.bool,
  loadMore: React.PropTypes.func.isRequired,
  threshold: React.PropTypes.number,
  useWindow: React.PropTypes.bool
}
InfiniteScroll.defaultProps = {
  pageStart: 0,
  hasMore: false,
  loadMore: function () {},
  threshold: 250,
  useWindow: true
};
<commit_msg>Bound listener functions. Fixes #13<commit_after>import React from 'react';
import ReactDOM from 'react-dom';

function topPosition(domElt) {
  if (!domElt) {
    return 0;
  }
  return domElt.offsetTop + topPosition(domElt.offsetParent);
}

export default class InfiniteScroll extends React.Component {
  constructor() {
    this.scrollListener = this.scrollListener.bind(this);
  }
  componentDidMount() {
    this.pageLoaded = this.props.pageStart;
    this.attachScrollListener();
  }
  componentDidUpdate() {
    this.attachScrollListener();
  }
  render() {
    var props = this.props;
    return React.DOM.div(null, props.children, props.hasMore && (props.loader || this._defaultLoader));
  }
  scrollListener() {
    var el = ReactDOM.findDOMNode(this);
    var scrollEl = window;

    var offset;
    if(this.props.useWindow == true) {
      var scrollTop = (scrollEl.pageYOffset !== undefined) ? scrollEl.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
      offset = topPosition(el) + el.offsetHeight - scrollTop - window.innerHeight;
    } else {
      offset = el.offsetHeight - el.parentNode.scrollTop - el.parentNode.clientHeight;
    }

    if (offset < Number(this.props.threshold)) {
      this.detachScrollListener();
      // call loadMore after detachScrollListener to allow
      // for non-async loadMore functions
      this.props.loadMore(this.pageLoaded += 1);
    }
  }
  attachScrollListener() {
    if (!this.props.hasMore) {
      return;
    }

    var scrollEl = window;
    if(this.props.useWindow == false) {
      scrollEl = ReactDOM.findDOMNode(this).parentNode;
    }

    scrollEl.addEventListener('scroll', this.scrollListener);
    scrollEl.addEventListener('resize', this.scrollListener);
    this.scrollListener();
  }
  detachScrollListener() {
    var scrollEl = window;
    if(this.props.useWindow == false) {
      scrollEl = ReactDOM.findDOMNode(this).parentNode;
    }

    scrollEl.removeEventListener('scroll', this.scrollListener);
    scrollEl.removeEventListener('resize', this.scrollListener);
  }
  componentWillUnmount() {
    this.detachScrollListener();
  }
  setDefaultLoader(loader) {
    this._defaultLoader = loader;
  }
}
InfiniteScroll.PropTypes = {
  pageStart: React.PropTypes.number,
  hasMore: React.PropTypes.bool,
  loadMore: React.PropTypes.func.isRequired,
  threshold: React.PropTypes.number,
  useWindow: React.PropTypes.bool
}
InfiniteScroll.defaultProps = {
  pageStart: 0,
  hasMore: false,
  loadMore: function () {},
  threshold: 250,
  useWindow: true
};
<|endoftext|>


<commit_before>import LeaderLine from 'leader-line';
import React from 'react';

function shimPointAnchor (point) {
  return point instanceof Element
    ? LeaderLine.pointAnchor( point, { x: 0, y: 0 } )
    : LeaderLine.pointAnchor( document.body, point )
    ;
}

export default class Line extends React.Component {
  componentWillUnmount () {
    if (this.line) {
      const { svg } = this.line.getProps();
      const { parentNode } = svg;

      if (parentNode)
        parentNode.removeChild(svg);

      this.line.remove();
    }
  }

  initLine() {
    const { start, end } = this.props;
    const startPoint = shimPointAnchor(start);
    const endPoint = shimPointAnchor(end);

    this.line = new LeaderLine({
      start: startPoint,
      end: endPoint,
      path: 'fluid',
      dash: true,
    });

    const { svg } = this.line.getProps();
    const { parentNode } = this.mockSvg;
    svg.leaderLine = this.line;

    if (parentNode)
      parentNode.appendChild(svg);

    if (typeof(this.props.onLoad) === 'function')
      this.props.onLoad(this.line);
  }

  shouldComponentUpdate (nextProps) {
    if (!this.line && this.mockSvg && this.props.start && this.props.end) {
      this.initLine();
      return false;
    }

    if (this.line) {

      if (this.props.start != nextProps.start) {
        this.line.start = shimPointAnchor(nextProps.start);
      }

      if (this.props.end != nextProps.end) {
        this.line.end = shimPointAnchor(nextProps.end);
      }

      this.line.position(false);
    }

    return false;
  }

  render() {
    return <svg ref={ ref => this.mockSvg = ref } />;
  }
}<commit_msg>Use disableViewBox option<commit_after>import LeaderLine from 'leader-line';
import React from 'react';

function shimPointAnchor (point) {
  return point instanceof Element
    ? LeaderLine.pointAnchor( point, { x: 0, y: 0 } )
    : LeaderLine.pointAnchor( document.body, point )
    ;
}

export default class Line extends React.Component {
  componentWillUnmount () {
    if (this.line) {
      const { svg } = this.line.getProps();
      const { parentNode } = svg;

      if (parentNode)
        parentNode.removeChild(svg);

      this.line.remove();
    }
  }

  initLine() {
    const { start, end } = this.props;
    const startPoint = shimPointAnchor(start);
    const endPoint = shimPointAnchor(end);

    this.line = new LeaderLine({
      start: startPoint,
      end: endPoint,
      path: 'fluid',
      dash: true,
      disableViewBox: true,
    });

    const { svg } = this.line.getProps();
    const { parentNode } = this.mockSvg;
    svg.leaderLine = this.line;

    if (parentNode)
      parentNode.appendChild(svg);

    if (typeof(this.props.onLoad) === 'function')
      this.props.onLoad(this.line);
  }

  shouldComponentUpdate (nextProps) {
    if (!this.line && this.mockSvg && this.props.start && this.props.end) {
      this.initLine();
      return false;
    }

    if (this.line) {

      if (this.props.start != nextProps.start) {
        this.line.start = shimPointAnchor(nextProps.start);
      }

      if (this.props.end != nextProps.end) {
        this.line.end = shimPointAnchor(nextProps.end);
      }

      this.line.position();
    }

    return false;
  }

  render() {
    return <svg ref={ ref => this.mockSvg = ref } />;
  }
}<|endoftext|>


<commit_before>import {notification} from 'antd';

// In production, we register a service worker to serve assets from local cache.

// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on the "N+1" visit to a page, since previously
// cached resources are updated in the background.

// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
// This link also includes instructions on opting out of this behavior.

const isLocalhost = Boolean(
    window.location.hostname === 'localhost' ||
    // [::1] is the IPv6 localhost address.
    window.location.hostname === '[::1]' ||
    // 127.0.0.1/8 is considered localhost for IPv4.
    window.location.hostname.match(
        /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
    )
);

export default function register() {
    if ( process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator ) {
        // The URL constructor is available in all browsers that support SW.
        const publicUrl = new URL( process.env.PUBLIC_URL, window.location );
        if ( publicUrl.origin !== window.location.origin ) {
            // Our service worker won't work if PUBLIC_URL is on a different origin
            // from what our page is served on. This might happen if a CDN is used to
            // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
            return;
        }

        window.addEventListener( 'load', () => {
            const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;

            if ( !isLocalhost ) {
                // Is not local host. Just register service worker
                registerValidSW( swUrl );
            } else {
                // This is running on localhost. Lets check if a service worker still exists or not.
                checkValidServiceWorker( swUrl );
            }
        } );
    }
}

function registerValidSW( swUrl ) {
    navigator.serviceWorker
        .register( swUrl )
        .then( registration => {
            registration.onupdatefound = () => {
                const installingWorker = registration.installing;
                installingWorker.onstatechange = () => {
                    if ( installingWorker.state === 'installed' ) {
                        if ( navigator.serviceWorker.controller ) {
                            // At this point, the old content will have been purged and
                            // the fresh content will have been added to the cache.
                            // It's the perfect time to display a "New content is
                            // available; please refresh." message in your web app.
                            console.log( 'New content is available; please refresh.' );
                            notification.info( {
                                                   message: 'New Update',
                                                   description: 'New update is available. Please refresh.',
                                                   placement: 'bottomRight',
                                                   duration: 0
                                               } );
                        } else {
                            // At this point, everything has been precached.
                            // It's the perfect time to display a
                            // "Content is cached for offline use." message.
                            console.log( 'Content is cached for offline use.' );
                            notification.info( {
                                                   message: 'Content cached',
                                                   description: 'Content is cached for offline use.',
                                                   placement: 'bottomRight',
                                                   duration: 0
                                               } );
                        }
                    }
                };
            };
        } )
        .catch( error => {
            console.error( 'Error during service worker registration:', error );
            toastr.warning( "Error during service worker registration", error );
        } );
}

function checkValidServiceWorker( swUrl ) {
    // Check if the service worker can be found. If it can't reload the page.
    fetch( swUrl )
        .then( response => {
            // Ensure service worker exists, and that we really are getting a JS file.
            if (
                response.status === 404 ||
                response.headers.get( 'content-type' ).indexOf( 'javascript' ) === -1
            ) {
                // No service worker found. Probably a different app. Reload the page.
                navigator.serviceWorker.ready.then( registration => {
                    registration.unregister().then( () => {
                        window.location.reload();
                    } );
                } );
            } else {
                // Service worker found. Proceed as normal.
                registerValidSW( swUrl );
            }
        } )
        .catch( () => {
            console.log( 'No internet connection found. App is running in offline mode.' );
            notification.info( {
                                   message: 'Offline Mode',
                                   description: 'No internet connection found. App is running in offline mode.',
                                   placement: 'bottomRight',
                                   duration: 0
                               } );
        } );
}

export function unregister() {
    if ( 'serviceWorker' in navigator ) {
        navigator.serviceWorker.ready.then( registration => {
            registration.unregister();
        } );
    }
}
<commit_msg>- used antd to create notification in service worker<commit_after>import {notification} from 'antd';

// In production, we register a service worker to serve assets from local cache.

// This lets the app load faster on subsequent visits in production, and gives
// it offline capabilities. However, it also means that developers (and users)
// will only see deployed updates on the "N+1" visit to a page, since previously
// cached resources are updated in the background.

// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
// This link also includes instructions on opting out of this behavior.

const isLocalhost = Boolean(
    window.location.hostname === 'localhost' ||
    // [::1] is the IPv6 localhost address.
    window.location.hostname === '[::1]' ||
    // 127.0.0.1/8 is considered localhost for IPv4.
    window.location.hostname.match(
        /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
    )
);

export default function register() {
    if ( process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator ) {
        // The URL constructor is available in all browsers that support SW.
        const publicUrl = new URL( process.env.PUBLIC_URL, window.location );
        if ( publicUrl.origin !== window.location.origin ) {
            // Our service worker won't work if PUBLIC_URL is on a different origin
            // from what our page is served on. This might happen if a CDN is used to
            // serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
            return;
        }

        window.addEventListener( 'load', () => {
            const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;

            if ( !isLocalhost ) {
                // Is not local host. Just register service worker
                registerValidSW( swUrl );
            } else {
                // This is running on localhost. Lets check if a service worker still exists or not.
                checkValidServiceWorker( swUrl );
            }
        } );
    }
}

function registerValidSW( swUrl ) {
    navigator.serviceWorker
        .register( swUrl )
        .then( registration => {
            registration.onupdatefound = () => {
                const installingWorker = registration.installing;
                installingWorker.onstatechange = () => {
                    if ( installingWorker.state === 'installed' ) {
                        if ( navigator.serviceWorker.controller ) {
                            // At this point, the old content will have been purged and
                            // the fresh content will have been added to the cache.
                            // It's the perfect time to display a "New content is
                            // available; please refresh." message in your web app.
                            console.log( 'New content is available; please refresh.' );
                            notification.info( {
                                                   message: 'New Update',
                                                   description: 'New update is available. Please refresh.',
                                                   placement: 'bottomRight',
                                                   duration: 0
                                               } );
                        } else {
                            // At this point, everything has been precached.
                            // It's the perfect time to display a
                            // "Content is cached for offline use." message.
                            console.log( 'Content is cached for offline use.' );
                            notification.info( {
                                                   message: 'Content cached',
                                                   description: 'Content is cached for offline use.',
                                                   placement: 'bottomRight',
                                                   duration: 0
                                               } );
                        }
                    }
                };
            };
        } )
        .catch( error => {
            console.error( 'Error during service worker registration:', error );
        } );
}

function checkValidServiceWorker( swUrl ) {
    // Check if the service worker can be found. If it can't reload the page.
    fetch( swUrl )
        .then( response => {
            // Ensure service worker exists, and that we really are getting a JS file.
            if (
                response.status === 404 ||
                response.headers.get( 'content-type' ).indexOf( 'javascript' ) === -1
            ) {
                // No service worker found. Probably a different app. Reload the page.
                navigator.serviceWorker.ready.then( registration => {
                    registration.unregister().then( () => {
                        window.location.reload();
                    } );
                } );
            } else {
                // Service worker found. Proceed as normal.
                registerValidSW( swUrl );
            }
        } )
        .catch( () => {
            console.log( 'No internet connection found. App is running in offline mode.' );
            notification.info( {
                                   message: 'Offline Mode',
                                   description: 'No internet connection found. App is running in offline mode.',
                                   placement: 'bottomRight',
                                   duration: 0
                               } );
        } );
}

export function unregister() {
    if ( 'serviceWorker' in navigator ) {
        navigator.serviceWorker.ready.then( registration => {
            registration.unregister();
        } );
    }
}
<|endoftext|>


<commit_before>// master object for the TETROID board
var board = (function() {
  // layout board space
  var rows = 20;
  var cols = 10;
  // grab window size, calculate number of pixels per block
  var maxH = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
  var maxW = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
  // leave a small border, for safety
  maxH -= 10;
  maxW -= 10;
  var scale = (maxH / (rows + 2)) < (maxW / (cols + 2)) ? parseInt(maxH / (rows + 2)) : parseInt(maxW / (cols + 2));
  var canvas = document.getElementById('boardCanvas');
  canvas.height = scale * (rows + 2);
  canvas.width = scale * (cols + 2);
  // get context for working with the canvas later
  var context = canvas.getContext('2d');
  // find preview canvas
  var scalePreview = 20;
  var canPreview = document.getElementById('preview');
  canPreview.height = scalePreview * 2;
  canPreview.width = scalePreview * 4;
  var conPreview = canPreview.getContext('2d');
  // define color palette
  var WHITE = "#FFFFFF";
  var GRAY = "#777777";
  var GHOST = '#E3E3E3';
  var PURPLE = "#6C0089"; // Temporary: Explosive block color
  var BLUE = "#003B6F";
  var RED = "#AB1400";
  var GREEN = "#008022";
  var YELLOW = "#AB6400";
  var colors = [BLUE, RED, GREEN, YELLOW];
  // map colors to their lighter shades
  var shades = {
    "#003B6F": "#015EB2",
    "#AB1400": "#FF1E00",
    "#008022": "#00F841",
    "#AB6400": "#FF9500",
    "#777777": "#A3A3A3",
    "#6C0089": "#BB33DF"
  };
  var BACKGROUND = WHITE;
  var BOOM = PURPLE;
  // tile array for game area
  var board = [];
  for (var r = 0; r < rows; r++) {
    board[r] = [];
    for (var c = 0; c < cols; c++) {
      board[r][c] = false;
    }
  }

  return {
    resize: function() {
      // helper function to resize game board on the fly
      maxH = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
      maxW = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
      // leave a small border, for safety
      maxH -= 10;
      maxW -= 10;
      scale = (maxH / (rows + 2)) < (maxW / (cols + 2)) ? parseInt(maxH / (rows + 2)) : parseInt(maxW / (cols + 2));
      canvas.height = scale * (rows + 2);
      canvas.width = scale * (cols + 2);
      this.reset();
    },
    reset: function() {
      // tile array for game area
      board = [];
      for (var r = 0; r < rows; r++) {
        board[r] = [];
        for (var c = 0; c < cols; c++) {
          board[r][c] = false;
        }
      }
    },
    wipePreview: function() {
      // draw over preview pane
      conPreview.beginPath();
      conPreview.rect(0, 0, scalePreview * 4, scalePreview * 2);
      conPreview.fillStyle = BACKGROUND;
      conPreview.fill();
    },
    drawPreview: function(posiArray) {
      // draw the next game piece
      this.wipePreview();
      for (var i = 0; i < posiArray.length; i++) {
        var posi = posiArray[i];
        conPreview.beginPath();
        conPreview.rect(posi[0] * scalePreview, posi[1] * scalePreview, scalePreview, scalePreview);
        conPreview.fillStyle = "#A3A3A3";
        conPreview.fill();
      }
    },
    drawBackground: function() {
      // fill in background for entire board
      context.beginPath();
      context.rect(scale, scale, scale * cols, scale * rows);
      context.fillStyle = BACKGROUND;
      context.fill();
    },
    drawBoxSmall: function(xmin, ymin, color) {
      // draw very small boxes (<20px)
      context.beginPath();
      context.rect(xmin, ymin, scale, scale);
      context.fillStyle = color;
      context.fill();
    },
    drawBoxMedium: function(xmin, ymin, color) {
      // draw medium boxes (< 50px but > 20px) (WARNING: Breaks if < 11px!)
      context.beginPath();
      context.rect(xmin + 3, ymin + 3, scale - 6, scale - 6);
      context.fillStyle = shades[color];
      context.fill();
      context.lineWidth = 6;
      context.lineJoin = "round";
      context.strokeStyle = color;
      context.stroke();
    },
    drawBoxLarge: function(xmin, ymin, color) {
      // draw a box if the sizes are bigger than 50px
      var half = parseInt(scale / 2);
      var radGrad = context.createRadialGradient(xmin + half, ymin + half, 0, xmin + half, ymin + half, half);
      radGrad.addColorStop(0, 'white');
      radGrad.addColorStop(1, color);

      context.beginPath();
      context.rect(xmin + 5, ymin + 5, scale - 10, scale - 10);
      context.fillStyle = radGrad;
      context.fill();
      context.lineWidth = 10;
      context.lineJoin = "round";
      context.strokeStyle = color;
      context.stroke();
    },
    drawBox: function(x, y, c) {
      scale < 21 ? this.drawBoxSmall(x, y, c) : (scale < 50 ? this.drawBoxMedium(x, y, c) : this.drawBoxLarge(x, y, c));
    },
    drawWalls: function() {
      // draw the bounding box of the game board
      // draw sides
      for (var i = 0; i < rows + 2; i++) {
        this.drawBox(0, scale * i, GRAY, scale);
        this.drawBox(scale * (cols + 1), scale * i, GRAY, scale);
      }
      // draw top and bottom
      for (var i = 1; i < cols + 1; i++) {
        this.drawBox(scale * i, 0, GRAY, scale);
        this.drawBox(scale * i, scale * (rows + 1), GRAY, scale);
      }
    },
    drawBoard: function() {
      this.drawBackground();
      // draw the board tiles
      for (var r = 0; r < rows; r++) {
        for (var c = 0; c < cols; c++) {
          if (board[r][c]) {
            this.drawBox(c * scale + scale, r * scale + scale, board[r][c]);
          }
        }
      }
    },
    drawPiece: function(posiArray, plusX, plusY, color) {
      // draw a single game piece as it is falling
      this.drawGhostPiece(posiArray, plusX, plusY);

      for (var i = 0; i < posiArray.length; i++) {
        posi = posiArray[i];
        if ((posi[1] + plusY) >= 0) {
          this.drawBox((posi[0] + plusX) * scale + scale, (posi[1] + plusY) * scale + scale, color);
        }
      }
    },
    drawGhostPiece: function(posiArray, plusX, plusY) {
      // draw a ghost piece, to prviews
      // determine how far down the ghost piece will be
      var yShift = 0;
      var hitBottom = false;
      var y = 0;
      for (var j = 0; j < (rows - plusY); j++) {
        yShift += 1;
        for (var i = 0; i < posiArray.length; i++) {
          posi = posiArray[i];
          y = posi[1] + plusY + yShift;
          if (y >= rows) {
            hitBottom = true;
            break;
          } else if (y > -1 && board[y][posi[0] + plusX]) {
            hitBottom = true;
            break;
          }
        }

        if (hitBottom) {
          break;
        }
      }

      // do we even need a ghost piece?
      yShift -= 1;
      if (yShift <= 0) {
        return;
      }

      // draw the ghost piece
      for (var i = 0; i < posiArray.length; i++) {
        posi = posiArray[i];
        this.drawBoxSmall((posi[0] + plusX) * scale + scale, (posi[1] + plusY + yShift) * scale + scale, GHOST);
      }
    },
    getRandomColor: function() {
      return colors[Math.floor(Math.random() * colors.length)];
    },
    getBoomColor: function() {
      return BOOM;
    },
    setB: function(x, y, c) {
      // set a tile on the board
      // c can be a color string or false
      board[y][x] = c;
    },
    getB: function(x, y) {
      // get tile on board
      if (board[y]) {
        return board[y][x];
      } else {
        return false;
      }
    },
    isRowComplete: function(r) {
      // test if a line is complete
      if (r < 0 || r >= rows) {
        return false;
      }
      for (var c = 0; c < cols; c++) {
        if (!board[r][c]) {
          return false;
        }
      }
      return true;
    },
    getRows: function() {
      return rows;
    },
    getCols: function() {
      return cols;
    },
    write: function(words) {
      // write words on the canvas: START, PAUSED, GAME OVER
      context.fillStyle = "black";
      context.font = "bold " + parseInt(1.5 * scale).toString() + "px Arial";
      context.textAlign = 'center';
      context.fillText(words, canvas.width / 2, canvas.height / 2);
    }
  };
}());

// TETROID game logic
var tetroid = (function() {
  var pieces = [
    // Pieces are made of "points" from 0 to 7
    // 0123
    // 4567

    // 1. Bar
    // ----
    // XXXX
    [4, 5, 6, 7],
    // 2. L (flipped)
    // X---
    // XXX-
    [0, 4, 5, 6],
    // 3. L
    // --X-
    // XXX-
    [2, 4, 5, 6],
    // 4. Square
    // -XX-
    // -XX-
    [1, 2, 5, 6],
    // 5. S
    // -XX-
    // XX--
    [1, 2, 4, 5],
    // 6. Triangle
    // -X--
    // XXX-
    [1, 4, 5, 6],
    // 7. S (flipped)
    // XX--
    // -XX-
    [0, 1, 5, 6]
  ].map(function(points) {
    return points.map(function(i) {
      var x = i % 4;
      var y = Math.floor(i / 4);
      return [x, y];
    });
  });

  // Gameplay area
  var board = null;
  var sizeX = 10; // should come from board at runtime
  var sizeY = 20; // should come from board at runtime
  var progress = 0;
  var score = 0;
  var boomValue = 8;
  var linesPerLevel = 12;
  var levelProgress = 0;
  var winScore = 99999;
  var gameState = false;
  var paused = false;

  // Current piece: array of points and position
  var current;
  var currentId;
  var currentX;
  var currentY;
  var currentColor;
  var nextExplodes = false;
  var currentExplodes = false;
  var nextPieces = [];

  return {
    shuffle: function(arr) {
      // randomly re-order an array
      var currentIndex = arr.length,
        temporaryValue, randomIndex;

      // While there remain elements to shuffle
      while (0 !== currentIndex) {
        // Pick a remaining element
        randomIndex = Math.floor(Math.random() * currentIndex);
        currentIndex -= 1;

        // And swap it with the current element.
        temporaryValue = arr[currentIndex];
        arr[currentIndex] = arr[randomIndex];
        arr[randomIndex] = temporaryValue;
      }

      return arr;
    },
    bagDrawOrder: function() {
      // return a random shuffle of 7 elements
      return this.shuffle([0, 1, 2, 3, 4, 5, 6]);
    },
    gamePlayStart: function() {
      // start or re-start each game
      progress = 0;
      score = 0;
      levelProgress = 0;
      nextExplodes = false;
      currentExplodes = false;
      this.printScore();
      this.getNextPiece();
      this.previewNext();
      this.render();
      this.flipSwitch();
      gameState = true;
    },
    unpause: function() {
      this.render();
      this.flipSwitch();
      paused = false;
    },
    pause: function() {
      this.flipSwitch();
      paused = true;
      board.write("PAUSED");
    },
    isPaused: function() {
      return paused;
    },
    flipSwitch: function() {
      // pause or unpause
      var now = document.getElementById("startPause").innerHTML;
      now = now === "Pause" ? "Start" : "Pause";
      document.getElementById("startPause").innerHTML = now;
    },
    moveIfFree: function(vectorX, vectorY) {
      // Check if the place for moved piece's position is free
      var isAreaFree = current.every(function(point) {
        var x = point[0] + currentX + vectorX;
        var y = point[1] + currentY + vectorY;
        return (x >= 0 && x <= sizeX - 1 && y <= sizeY - 1 && !board.getB(x, y))
      });

      // Move current piece if possible
      if (isAreaFree) {
        currentX += vectorX;
        currentY += vectorY;
      }

      return isAreaFree;
    },
    getNextPiece: function() {
      // mock up a quick queue of pieces
      if (nextPieces.length == 0) {
        nextPieces = this.bagDrawOrder();
      }
      currentId = nextPieces.shift();
      if (nextPieces.length == 0) {
        nextPieces = this.bagDrawOrder();
      }

      // identify piece
      current = pieces[currentId];
      currentColor = board.getRandomColor();
      currentX = Math.floor(Math.random() * (sizeX - 3));
      currentY = -2;
      // rotate piece
      var rot = Math.floor(Math.random() * 3);
      for (var i = 0; i < rot; i++) {
        this.rotate();
      }
    },
    peakNextPiece: function() {
      return nextPieces[0];
    },
    previewNext: function() {
      var next = pieces[this.peakNextPiece()];
      board.drawPreview(next);
    },
    step: function() {
      // If piece can't move unit down
      if (!this.moveIfFree(0, 1)) {
        // If piece is stuck at the top, the game is lost
        if (current.some(function(point) {
            return point[1] + currentY < 1;
          })) {
          this.endGame("GAME OVER");
        } else { // the piece is at the bottom and should stay there
          // Attach current object to the game area
          current.forEach(function(point) {
            board.setB(point[0] + currentX, point[1] + currentY, currentColor);
          });

          // Create new piece
          if (currentExplodes) {
            var lastPiece = current.slice();
            var lastX = parseInt(currentX);
            var lastY = parseInt(currentY);
          }
          this.getNextPiece();
          this.previewNext();

          // Check for full horizontal lines in game area
          var lines = [];
          var line;
          for (var i = 0; i < sizeY; i++) {
            line = board.isRowComplete(i);
            if (line) {
              lines.unshift(i);
            }
          }

          if (currentExplodes) {
            this.handleExplosion(lastPiece, lastX, lastY);
          }

          var num = lines.length;
          if (lines.length) {
            this.removeLines(lines);
            this.handleHanging(num);
          }
        }
      } else {
        this.render();
      }
    },
    removeLines: function(lines) { // Remove horizontal lines, if there are any
      // update score and check for end game
      progress += lines.length;
      score += sizeX * Math.pow(lines.length, 2);
      this.testEndGame("You Win!");
      // level progress
      levelProgress += lines.length;
      if (levelProgress >= linesPerLevel) {
        levelProgress %= linesPerLevel;
        nextExplodes = true;
        if (nextExplodes) {
          currentColor = board.getBoomColor();
        }
        this.previewNext();
        nextExplodes = false;
        currentExplodes = true;
      }
      this.printScore();

      // Iterate through all lines and shift by "levels" units
      var levels = 1;
      for (i = lines.shift() - 1; i >= 0; i--) {
        if (lines.length) {
          if (lines[0] === i) {
            lines.shift();
            levels++;
            continue;
          }
        }

        // Move line down by "levels" unit
        for (j = 0; j < sizeX; j++) {
          board.setB(j, i + levels, board.getB(j, i));
        }
      }
    },
    handleExplosion: function(lastPiece, lastX, lastY) {
      // Handle block explosions
      lastPiece.forEach(function(point) {
        [-1, 0, 1].forEach(function(offsetX) {
          var xx = point[0] + lastX + offsetX;
          [-3, -2, -1, 0, 1].forEach(function(offsetY) {
            var yy = point[1] + lastY + offsetY;
            if (yy < sizeY && yy > -1 && xx > -1 && xx < sizeX) {
              if (board.getB(xx, yy)) {
                board.setB(xx, yy, false);
                score += boomValue;
              }
            }
          });
        });
      });
      this.handleHanging(boomValue);
      currentExplodes = false;
      board.write("Boom!");
      this.testEndGame("You Win!");
    },
    handleHanging: function(val) {
      // post-explosion, remove any magical floating tiles
      var r = 0;
      var c = 0;
      var stable = this.stabilityArray();
      for (r = 0; r < sizeY; r++) {
        for (c = 0; c < sizeX; c++) {
          if (!stable[r][c] && board.getB(c, r)) {
            board.setB(c, r, false);
            score += val;
          }
        }
      }
    },
    stabilityArray: function() {
      // determine which pieces are gravitationally stable
      var r, c, cc;
      // fill stability array
      var stable = [];
      for (r = 0; r < sizeY; r++) {
        stable[r] = [];
        for (c = 0; c < sizeX; c++) {
          stable[r][c] = false;
        }
      }
      // loop through grid and find unstable elements
      for (r = sizeY - 1; r > -1; r--) {
        for (cc = 0; cc < (2 * sizeX - 1); cc++) {
          if (cc < sizeX) {
            c = cc;
          } else {
            c = 2 * sizeX - cc - 2;
          }
          // if no piece on the board, unstable
          if (!board.getB(c, r)) {
            continue;
          }
          // if on bottom row, stable. or if adj to stable
          if (r == (sizeY - 1)) {
            stable[r][c] = true;
          } else if (r < (sizeY - 1) && stable[r + 1][c]) {
            stable[r][c] = true;
          } else if (c > 0 && stable[r][c - 1]) {
            stable[r][c] = true;
          } else if (c < (sizeX - 1) && stable[r][c + 1]) {
            stable[r][c] = true;
          } else if (r > 0 && stable[r - 1][c]) {
            stable[r][c] = true;
          }
          // if now stable, set adj to stable
          if (!stable[r][c]) {
            continue;
          }
          if (r < (sizeY - 1) && board.getB(c, r + 1)) {
            stable[r + 1][c] = true;
          } else if (r > 0 && board.getB(c, r - 1)) {
            stable[r - 1][c] = true;
          } else if (c < (sizeX - 1) && board.getB(c + 1, r)) {
            stable[r][c + 1] = true;
          } else if (c > 0 && board.getB(c - 1, r)) {
            stable[r][c - 1] = true;
          }
        }
      }
      return stable;
    },
    testEndGame: function(txt) {
      if (score >= winScore) {
        score = winScore;
        this.printScore();
        this.endGame("You Win!");
      }
    },
    endGame: function(txt) {
      gameState = false;
      board.reset();
      this.flipSwitch();
      board.write(txt);
    },
    printScore: function() {
      document.getElementById('lines').innerHTML = progress;
      document.getElementById('score').innerHTML = score;
    },
    render: function() {
      // master graphical UI rendered
      board.drawBoard();
      board.drawPiece(current, currentX, currentY, currentColor);
    },
    rotate: function() {
      // If current piece is a square
      if (currentId === 3) {
        return;
      }

      var newCurrent = [];
      var canRotate = true;

      current.forEach(function(point) {
        var x = 2 - point[1];
        var y = point[0];

        // If current piece is a bar and it is vertical
        if (currentId === 0 && current[0][0] == current[1][0]) {
          x += 1;
        }

        if (x + currentX < 0 || y + currentY < 0 || x + currentX >= sizeX || y + currentY >= sizeY) {
          canRotate = false;
        } else if (board.getB(x + currentX, y + currentY)) {
          canRotate = false;
        }

        newCurrent.push([x, y]);
      });

      if (canRotate) {
        current = newCurrent;
      }
    },
    loop: function() {
      // If gameState === 1 then step()
      gameState && (!paused && this.step());
    },
    getGameState: function() {
      return gameState;
    },
    setGameState: function(g) {
      gameState = g;
    },
    loadBoard: function(b) {
      board = b;
      sizeX = board.getCols();
      sizeY = board.getRows();
      board.drawWalls();
    }
  };
}());

// load the board controller into the game
tetroid.loadBoard(board);

// handle keyboard events
document.onkeydown = function(e) {
  var keyMap = {
    ArrowLeft: tetroid.moveIfFree.bind(null, -1, 0), // Move left
    ArrowRight: tetroid.moveIfFree.bind(null, 1, 0), // Move right
    ArrowDown: tetroid.moveIfFree.bind(null, 0, 1), // Move bottom (speed up)
    ArrowUp: tetroid.rotate // Rotate current piece
  };
  // If the game is being played
  if (tetroid.getGameState()) {
    // If the action exists for the key pressed
    if (e.key in keyMap) {
      // Run an action based on key code
      keyMap[e.key]();
      tetroid.render();
    }
  }
};

// activate Start/Pause button
document.getElementById("startPause").addEventListener("click", function(e) {
  if (!tetroid.getGameState()) {
    tetroid.gamePlayStart();
  } else if (tetroid.isPaused()) {
    tetroid.unpause();
  } else {
    tetroid.pause();
  }
});

// game loop meta function, to deal with scope
function looper() {
  tetroid.loop();
}

// start the game
setInterval(looper, 400);
<commit_msg>#74 cleaning up Tetroid draft<commit_after><|endoftext|>


<commit_before>// Get webview element
var webview = document.getElementById('webview');

// Get find box elements
var findBox = document.getElementById('find-box');
var findInput = document.getElementById('find-text');

// Get dialog box elements
var dialogBox = document.getElementById("dialog-box");
var dialogOk = document.getElementById("dialog-box-ok");
var dialogText = document.getElementById("dialog-box-text");
var dialogInput = document.getElementById("dialog-box-input");
var dialogCancel = document.getElementById("dialog-box-cancel");

// Initial page zoom factor
var zoomFactor = 1.0;

// Listen to keydown event
window.onkeydown = function (e) {
    // Check whether CTRL on Windows or CMD on Mac is pressed
    var modifierActive = (navigator.platform.startsWith('Mac')) ? e.metaKey : e.ctrlKey;
    var altModifierActive = (navigator.platform.startsWith('Mac')) ? e.ctrlKey : e.altKey;

    // Enter full screen mode (CMD/ALT + CTRL + F)
    if (modifierActive && altModifierActive && e.keyCode == 'F'.charCodeAt(0)) {
        // Get current focused window
        var window = chrome.app.window.current();

        // Check if currently full screen
        if (!window.isFullscreen()) {
            // Enter full screen mode
            window.fullscreen();
        }
        else {
            // Exit full screen mode
            window.restore();
        }

        // Prevent other shortcut checks
        return;
    }

    // Refresh the page (CTRL/CMD + R)
    if (modifierActive && e.keyCode == 'R'.charCodeAt(0)) {
        webview.reload();
    }

    // Find in page (CTRL/CMD + F)
    if (modifierActive && e.keyCode == 'F'.charCodeAt(0)) {
        // Show the find box
        findBox.style.display = 'block';

        // Focus the find input
        findInput.focus();

        // Select all existing text (if any)
        findInput.select();
    }

    // Copy URL (CTRL/CMD + L)
    if (modifierActive && e.keyCode == 'L'.charCodeAt(0)) {
        // Copy webview source to clipboard
        copyToClipboard(webview.src, 'text/plain');
    }

    // Print (CTRL/CMD + P)
    if (modifierActive && e.keyCode == 'P'.charCodeAt(0)) {
        webview.print();
    }

    // Zoom in (CTRL/CMD +)
    if (modifierActive && e.keyCode == 187) {
        zoomFactor += 0.1;
        webview.setZoom(zoomFactor);
    }

    // Zoom out (CTRL/CMD -)
    if (modifierActive && e.keyCode == 189) {
        zoomFactor -= 0.1;

        // Don't let zoom drop below 0.2
        if (zoomFactor <= 0.2) {
            zoomFactor = 0.2;
        }

        webview.setZoom(zoomFactor);
    }

    // Reset zoom (CTRL/CMD + 0)
    if (modifierActive && e.keyCode == '0'.charCodeAt(0)) {
        zoomFactor = 1.0;
        webview.setZoom(zoomFactor);
    }
};

// Listen for webview load event
webview.addEventListener('contentload', function () {
    // Execute JS script within webview
    webview.executeScript({
        // Send a Chrome runtime message every time the keydown event is fired within webview
        code: `window.addEventListener('keydown', function (e) {
            chrome.runtime.sendMessage({ event: 'keydown', params: { ctrlKey: e.ctrlKey, metaKey: e.metaKey, altKey: e.altKey, keyCode: e.keyCode } });
        });`});
});

// Listen for Chrome runtime messages
chrome.runtime.onMessage.addListener(
    function (request, sender, sendResponse) {
        // Check for keydown event
        if (request.event === 'keydown') {
            // Invoke the local window's keydown event handler
            window.onkeydown(request.params);
        }
    }
);

// Find input: listen to keydown event
findInput.addEventListener('keyup', function (e) {
    // Search for current input text
    webview.find(findInput.value, { matchCase: false });

    // Escape key
    if (e.keyCode === 27) {
        webview.stopFinding();
        findBox.style.display = 'none';
    }
});

function copyToClipboard(str, mimetype) {
    // Listen for 'oncopy' event
    document.oncopy = function (event) {
        event.clipboardData.setData(mimetype, str);
        event.preventDefault();
    };

    // Execute browser command 'Copy'
    document.execCommand("Copy", false, null);
}

// Custom alert dialog popup
var dialogController;

// Listen for dialog cancellation button click
dialogCancel.addEventListener('click', function () {
    // Send cancellation
    dialogController.cancel();

    // Hide dialog box
    dialogBox.style.display = 'none';
});

// Listen for dialog OK button click
dialogOk.addEventListener('click', function () {
    // Send OK value
    dialogController.ok(dialogInput.value);

    // Hide dialog box
    dialogBox.style.display = 'none';
});

// Listen for dialog event on webview for new alert dialogs
webview.addEventListener('dialog', function (e) {
    // Prevent default logic
    e.preventDefault();

    // Extract dialog type and text
    var text = e.messageText;
    var dialogType = e.messageType;

    // Keep a reference to dialog object
    dialogController = e.dialog;

    // Set dialog text
    dialogText.innerHTML = text;

    // Reset dialog input
    dialogInput.value = '';

    // Hide it by default
    dialogInput.style.display = 'none';

    // Alert?
    if (dialogType == 'alert') {
        // Hide cancel button
        dialogCancel.style.display = 'none';
    }
    else {
        // Another type of dialog, show cancel button
        dialogCancel.style.display = 'block';

        // Prompt?
        if (dialogType == 'prompt') {
            // Show text input field
            dialogInput.style.display = 'block';
        }
    }

    // Show dialog box
    dialogBox.style.display = 'block';
});<commit_msg>Use window.addEventListener() to support multiple keydown listeners<commit_after>// Get webview element
var webview = document.getElementById('webview');

// Get find box elements
var findBox = document.getElementById('find-box');
var findInput = document.getElementById('find-text');

// Get dialog box elements
var dialogBox = document.getElementById("dialog-box");
var dialogOk = document.getElementById("dialog-box-ok");
var dialogText = document.getElementById("dialog-box-text");
var dialogInput = document.getElementById("dialog-box-input");
var dialogCancel = document.getElementById("dialog-box-cancel");

// Initial page zoom factor
var zoomFactor = 1.0;

// Listen to keydown event
window.addEventListener('keydown', function (e) {
    // Check whether CTRL on Windows or CMD on Mac is pressed
    var modifierActive = (navigator.platform.startsWith('Mac')) ? e.metaKey : e.ctrlKey;
    var altModifierActive = (navigator.platform.startsWith('Mac')) ? e.ctrlKey : e.altKey;

    // Enter full screen mode (CMD/ALT + CTRL + F)
    if (modifierActive && altModifierActive && e.keyCode == 'F'.charCodeAt(0)) {
        // Get current focused window
        var window = chrome.app.window.current();

        // Check if currently full screen
        if (!window.isFullscreen()) {
            // Enter full screen mode
            window.fullscreen();
        }
        else {
            // Exit full screen mode
            window.restore();
        }

        // Prevent other shortcut checks
        return;
    }

    // Refresh the page (CTRL/CMD + R)
    if (modifierActive && e.keyCode == 'R'.charCodeAt(0)) {
        webview.reload();
    }

    // Find in page (CTRL/CMD + F)
    if (modifierActive && e.keyCode == 'F'.charCodeAt(0)) {
        // Show the find box
        findBox.style.display = 'block';

        // Focus the find input
        findInput.focus();

        // Select all existing text (if any)
        findInput.select();
    }

    // Copy URL (CTRL/CMD + L)
    if (modifierActive && e.keyCode == 'L'.charCodeAt(0)) {
        // Copy webview source to clipboard
        copyToClipboard(webview.src, 'text/plain');
    }

    // Print (CTRL/CMD + P)
    if (modifierActive && e.keyCode == 'P'.charCodeAt(0)) {
        webview.print();
    }

    // Zoom in (CTRL/CMD +)
    if (modifierActive && e.keyCode == 187) {
        zoomFactor += 0.1;
        webview.setZoom(zoomFactor);
    }

    // Zoom out (CTRL/CMD -)
    if (modifierActive && e.keyCode == 189) {
        zoomFactor -= 0.1;

        // Don't let zoom drop below 0.2
        if (zoomFactor <= 0.2) {
            zoomFactor = 0.2;
        }

        webview.setZoom(zoomFactor);
    }

    // Reset zoom (CTRL/CMD + 0)
    if (modifierActive && e.keyCode == '0'.charCodeAt(0)) {
        zoomFactor = 1.0;
        webview.setZoom(zoomFactor);
    }
});

// Listen for webview load event
webview.addEventListener('contentload', function () {
    // Execute JS script within webview
    webview.executeScript({
        // Send a Chrome runtime message every time the keydown event is fired within webview
        code: `window.addEventListener('keydown', function (e) {
            chrome.runtime.sendMessage({ event: 'keydown', params: { ctrlKey: e.ctrlKey, metaKey: e.metaKey, altKey: e.altKey, keyCode: e.keyCode } });
        });`});
});

// Listen for Chrome runtime messages
chrome.runtime.onMessage.addListener(
    function (request, sender, sendResponse) {
        // Check for keydown event
        if (request.event === 'keydown') {
            // Invoke the local window's keydown event handler
            window.onkeydown(request.params);
        }
    }
);

// Find input: listen to keydown event
findInput.addEventListener('keyup', function (e) {
    // Search for current input text
    webview.find(findInput.value, { matchCase: false });

    // Escape key
    if (e.keyCode === 27) {
        webview.stopFinding();
        findBox.style.display = 'none';
    }
});

function copyToClipboard(str, mimetype) {
    // Listen for 'oncopy' event
    document.oncopy = function (event) {
        event.clipboardData.setData(mimetype, str);
        event.preventDefault();
    };

    // Execute browser command 'Copy'
    document.execCommand("Copy", false, null);
}

// Custom alert dialog popup
var dialogController;

// Listen for dialog cancellation button click
dialogCancel.addEventListener('click', function () {
    // Send cancellation
    dialogController.cancel();

    // Hide dialog box
    dialogBox.style.display = 'none';
});

// Listen for dialog OK button click
dialogOk.addEventListener('click', function () {
    // Send OK value
    dialogController.ok(dialogInput.value);

    // Hide dialog box
    dialogBox.style.display = 'none';
});

// Listen for dialog event on webview for new alert dialogs
webview.addEventListener('dialog', function (e) {
    // Prevent default logic
    e.preventDefault();

    // Extract dialog type and text
    var text = e.messageText;
    var dialogType = e.messageType;

    // Keep a reference to dialog object
    dialogController = e.dialog;

    // Set dialog text
    dialogText.innerHTML = text;

    // Reset dialog input
    dialogInput.value = '';

    // Hide it by default
    dialogInput.style.display = 'none';

    // Alert?
    if (dialogType == 'alert') {
        // Hide cancel button
        dialogCancel.style.display = 'none';
    }
    else {
        // Another type of dialog, show cancel button
        dialogCancel.style.display = 'block';

        // Prompt?
        if (dialogType == 'prompt') {
            // Show text input field
            dialogInput.style.display = 'block';
        }
    }

    // Show dialog box
    dialogBox.style.display = 'block';
});<|endoftext|>


<commit_before>var serverNumber = parseInt(process.argv[2]),
    port = [1337,1338,1339][serverNumber];
// Downgrade from root
if (process.getuid() === 0)
    require('fs').stat(__filename, function(err, stats) {
            if (err)
                return console.log(err);
            process.setuid(stats.uid);
        });
console.log("Started physics server "+serverNumber);
var io = require('socket.io').listen(port),
    redis = require('redis').createClient(),
    GameLogic = require('./public/javascripts/game_logic.js').GameLogic,
    GameDef = require('./public/javascripts/game_def.js').GameDef;

redis.on("error", function (err) {
        console.log("Redis connection error to " + redis.host + ":" + redis.port + " - " + err);
    });

var games = {},
    gameBySocketId = {},
    redisKeys = {},
    gamesPerProcess = 4;

io.sockets.on('connection',function(socket){
        socket.on('subscribe',function(type,gameId,redisKey){
                var room = games[gameId];
                if(typeof room === 'undefined'){
                    socket.emit('error',"Bad room id");
                }
                if(room)
                    if(room.join(socket,type)){
                        gameBySocketId[socket.id] = gameId;
                        redisKeys[socket.id] = redisKey;
                        socket.emit('update',room.physics.getState());
                    }else{
                        socket.emit('error',"This game room is full");
                    }
            });
        socket.on('disconnect',function(){
                var gameId, game;
                if(gameId = gameBySocketId[socket.id]){
                    if(game=games[gameId]){
                        game.remove(socket.id);
                    }
                    delete gameBySocketId[socket.id];
                }
                delete redisKeys[socket.id];
            });
    });
Game = function(id){
    this.gameId = id;
    //Initialize a new physics object here
    this.physics = new GameLogic(GameDef);
    this.c0 = null;
    this.c1 = null;
    this.running = false;
    this.viewers = 0;
    this.controllers = 0;
    this.all_sockets = id+'-all';
    this.controller_sockets = id+'-control';
};
Game.prototype.join = function(socket,type){
    if(type == 'viewer'){
        //Probably can manage 4 viewers and 4 controllers?
        if(this.viewers < 4){
            socket.join(this.all_sockets);
            this.viewers++;
            return true;
        }
    }else if(type == 'controller'){
        if(this.controllers < 4){
            socket.join(this.all_sockets);
            socket.join(this.controller_sockets);
            this.controllers++;
            return true;
        }
    }
    return false;
};
Game.prototype.empty = function(){
    return (this.viewers == 0 && this.controllers == 0);
};
Game.prototype.remove = function(id){
    var controller_room = io.sockets.manager.rooms['/'+this.controller_sockets] || [];
    if(-1 != controller_room.indexOf(id+'')){
        // If this is c1 or c0, the other one wins.
        if(id == this.c0){
            this.c0 = null;
            this.end(1);
        }else if(id == this.c1){
            this.c1 = null;
            this.end(0);
        }
        this.controllers--;
    }else
        this.viewers--;
};
Game.prototype.end = function(winner){
    // for both controller sockets (if they exist) start ignoring the paddle data
    this.running = false;
    if(this.c0){
        var s = io.sockets.socket(this.c0);
        s.removeAllListeners('paddle');
        s.removeAllListeners('dbl');
    }
    if(this.c1){
        var s = io.sockets.socket(this.c1);
        s.removeAllListeners('paddle');
        s.removeAllListeners('dbl');
    }
    winner = [this.c0,this.c1][winner];
    (function(game){
        redis.get(redisKeys[winner],function(err,data){
                var fail = {};
                io.sockets.in(game.all_sockets).emit('win',data || fail);
            });
    })(this);
};
Game.prototype.begin = function(){
    var controllers = io.sockets.manager.rooms['/'+this.controller_sockets];
    (function(game){
        chooseControllers(controllers,function(a,b){
                game.c0 = a;
                game.c1 = b;
                console.log(a);
                console.log(b);
                io.sockets.socket(game.c0).emit('nominate',0);
                io.sockets.socket(game.c1).emit('nominate',1);
                io.sockets.in(game.all_sockets).emit('time',15);
                setTimeout(function(){
                        if(game.running){
                            game.physics.reset();
                            io.sockets.in(game.all_sockets).emit('start',game.physics.getState());
                            io.sockets.socket(game.c0).on('paddle',function(dat){
                                    game.physics.setTouchPoints(0,dat);
                                });
                            io.sockets.socket(game.c1).on('paddle',function(dat){
                                    game.physics.setTouchPoints(1,dat);
                                });
                            io.sockets.socket(game.c0).on('dbl',function(){
                                    game.physics.doubleTouch(0);
                                });
                            io.sockets.socket(game.c1).on('dbl',function(dat){
                                    game.physics.doubleTouch(1);
                                });
                            setupPhysics(game);
                        }
                    },15*1000);
            });
    })(this);
    this.running = true;
};
Game.prototype.redisRecord = function(){
    return {name:this.gameId,viewers:this.viewers,controllers:this.controllers,port:port};
};

for(var i = 0;i<gamesPerProcess;i++){
    games[i] = new Game(i+gamesPerProcess*serverNumber);
}

// Takes an array of possible controller ids and chooses two. Can map controller id to redis keys.
function chooseControllers(set,cont){
    // It turns out that writing the comparison predicate for the data is just a pain in the ass. Randomly schedule, for now.
    /*  var keys = set.map(function(i){return redisKeys[i];});
    redis.mget(keys,function(err,res){
            if(err){
                console.log("Redis error, with keys :"+keys);
                cont(set[0],set[1]);//Just choose the first two...
            }else{
                res = res.map(function(obj,index){return {obj:obj,ctrl:set[index]};});
                res.sort(function(a,b){
                        //Some predicate for the epicness.

                        return Math.random()-0.5;
                    });
                cont(res[0].ctrl,res[1].ctrl);
            }
        });
    */
    set.sort(function(a,b){return Math.random()-0.5;});
    cont(set[0],set[1]);
}
// Sets up the game's physics loops
function setupPhysics(game){
    var loop = function(){
        // Needs some logic to terminate, however
        if(game.physics.over())
            game.end();
        if(game.running){
            // Update the physics here
            var state = game.physics.step(1/30);
            io.sockets.in(game.all_sockets).volatile.emit('update',state);
            setTimeout(loop,1/30)
        }
    };
    loop();
}
// Update the game room data on redis every n seconds.
setInterval(function(){
        var rooms = {};
        for(var i in games){
            i = games[i];
            rooms[i.gameId] = JSON.stringify(i.redisRecord());
        }
        redis.hmset('rooms',rooms,function(err,_){
                if(err)
                    console.log('Redis-error:'+err);
            });
    },5000);

//This is the server's real main-loop. Checks if games can be started, and then starts them
setInterval(function(){
        for(var i in games){
            i = games[i];
            if(!i.running && i.controllers > 1)
                i.begin();
        }
    },250);<commit_msg>Let's run that 1000 times slower<commit_after>var serverNumber = parseInt(process.argv[2]),
    port = [1337,1338,1339][serverNumber];
// Downgrade from root
if (process.getuid() === 0)
    require('fs').stat(__filename, function(err, stats) {
            if (err)
                return console.log(err);
            process.setuid(stats.uid);
        });
console.log("Started physics server "+serverNumber);
var io = require('socket.io').listen(port),
    redis = require('redis').createClient(),
    GameLogic = require('./public/javascripts/game_logic.js').GameLogic,
    GameDef = require('./public/javascripts/game_def.js').GameDef;

redis.on("error", function (err) {
        console.log("Redis connection error to " + redis.host + ":" + redis.port + " - " + err);
    });

var games = {},
    gameBySocketId = {},
    redisKeys = {},
    gamesPerProcess = 4;

io.sockets.on('connection',function(socket){
        socket.on('subscribe',function(type,gameId,redisKey){
                var room = games[gameId];
                if(typeof room === 'undefined'){
                    socket.emit('error',"Bad room id");
                }
                if(room)
                    if(room.join(socket,type)){
                        gameBySocketId[socket.id] = gameId;
                        redisKeys[socket.id] = redisKey;
                        socket.emit('update',room.physics.getState());
                    }else{
                        socket.emit('error',"This game room is full");
                    }
            });
        socket.on('disconnect',function(){
                var gameId, game;
                if(gameId = gameBySocketId[socket.id]){
                    if(game=games[gameId]){
                        game.remove(socket.id);
                    }
                    delete gameBySocketId[socket.id];
                }
                delete redisKeys[socket.id];
            });
    });
Game = function(id){
    this.gameId = id;
    //Initialize a new physics object here
    this.physics = new GameLogic(GameDef);
    this.c0 = null;
    this.c1 = null;
    this.running = false;
    this.viewers = 0;
    this.controllers = 0;
    this.all_sockets = id+'-all';
    this.controller_sockets = id+'-control';
};
Game.prototype.join = function(socket,type){
    if(type == 'viewer'){
        //Probably can manage 4 viewers and 4 controllers?
        if(this.viewers < 4){
            socket.join(this.all_sockets);
            this.viewers++;
            return true;
        }
    }else if(type == 'controller'){
        if(this.controllers < 4){
            socket.join(this.all_sockets);
            socket.join(this.controller_sockets);
            this.controllers++;
            return true;
        }
    }
    return false;
};
Game.prototype.empty = function(){
    return (this.viewers == 0 && this.controllers == 0);
};
Game.prototype.remove = function(id){
    var controller_room = io.sockets.manager.rooms['/'+this.controller_sockets] || [];
    if(-1 != controller_room.indexOf(id+'')){
        // If this is c1 or c0, the other one wins.
        if(id == this.c0){
            this.c0 = null;
            this.end(1);
        }else if(id == this.c1){
            this.c1 = null;
            this.end(0);
        }
        this.controllers--;
    }else
        this.viewers--;
};
Game.prototype.end = function(winner){
    // for both controller sockets (if they exist) start ignoring the paddle data
    this.running = false;
    if(this.c0){
        var s = io.sockets.socket(this.c0);
        s.removeAllListeners('paddle');
        s.removeAllListeners('dbl');
    }
    if(this.c1){
        var s = io.sockets.socket(this.c1);
        s.removeAllListeners('paddle');
        s.removeAllListeners('dbl');
    }
    winner = [this.c0,this.c1][winner];
    (function(game){
        redis.get(redisKeys[winner],function(err,data){
                var fail = {};
                io.sockets.in(game.all_sockets).emit('win',data || fail);
            });
    })(this);
};
Game.prototype.begin = function(){
    var controllers = io.sockets.manager.rooms['/'+this.controller_sockets];
    (function(game){
        chooseControllers(controllers,function(a,b){
                game.c0 = a;
                game.c1 = b;
                console.log(a);
                console.log(b);
                io.sockets.socket(game.c0).emit('nominate',0);
                io.sockets.socket(game.c1).emit('nominate',1);
                io.sockets.in(game.all_sockets).emit('time',15);
                setTimeout(function(){
                        if(game.running){
                            game.physics.reset();
                            io.sockets.in(game.all_sockets).emit('start',game.physics.getState());
                            io.sockets.socket(game.c0).on('paddle',function(dat){
                                    game.physics.setTouchPoints(0,dat);
                                });
                            io.sockets.socket(game.c1).on('paddle',function(dat){
                                    game.physics.setTouchPoints(1,dat);
                                });
                            io.sockets.socket(game.c0).on('dbl',function(){
                                    game.physics.doubleTouch(0);
                                });
                            io.sockets.socket(game.c1).on('dbl',function(dat){
                                    game.physics.doubleTouch(1);
                                });
                            setupPhysics(game);
                        }
                    },15*1000);
            });
    })(this);
    this.running = true;
};
Game.prototype.redisRecord = function(){
    return {name:this.gameId,viewers:this.viewers,controllers:this.controllers,port:port};
};

for(var i = 0;i<gamesPerProcess;i++){
    games[i] = new Game(i+gamesPerProcess*serverNumber);
}

// Takes an array of possible controller ids and chooses two. Can map controller id to redis keys.
function chooseControllers(set,cont){
    // It turns out that writing the comparison predicate for the data is just a pain in the ass. Randomly schedule, for now.
    /*  var keys = set.map(function(i){return redisKeys[i];});
    redis.mget(keys,function(err,res){
            if(err){
                console.log("Redis error, with keys :"+keys);
                cont(set[0],set[1]);//Just choose the first two...
            }else{
                res = res.map(function(obj,index){return {obj:obj,ctrl:set[index]};});
                res.sort(function(a,b){
                        //Some predicate for the epicness.

                        return Math.random()-0.5;
                    });
                cont(res[0].ctrl,res[1].ctrl);
            }
        });
    */
    set.sort(function(a,b){return Math.random()-0.5;});
    cont(set[0],set[1]);
}
// Sets up the game's physics loops
function setupPhysics(game){
    var loop = function(){
        // Needs some logic to terminate, however
        if(game.physics.over())
            game.end();
        if(game.running){
            // Update the physics here
            var state = game.physics.step(1000/30);
            io.sockets.in(game.all_sockets).volatile.emit('update',state);
            setTimeout(loop,1000/30)
        }
    };
    loop();
}
// Update the game room data on redis every n seconds.
setInterval(function(){
        var rooms = {};
        for(var i in games){
            i = games[i];
            rooms[i.gameId] = JSON.stringify(i.redisRecord());
        }
        redis.hmset('rooms',rooms,function(err,_){
                if(err)
                    console.log('Redis-error:'+err);
            });
    },5000);

//This is the server's real main-loop. Checks if games can be started, and then starts them
setInterval(function(){
        for(var i in games){
            i = games[i];
            if(!i.running && i.controllers > 1)
                i.begin();
        }
    },250);<|endoftext|>