<commit_before>$(document).ready(function () {
    /*
    * DEEPZOOM FUNCTIONALITY
    * First checks if flash is enabled, if so use OpenZoomViewer.swf.
    * If that fails use Seadragon.js
    * And if that fails, view a regular image
    */
    var zoomUrls = $('#deepZoomUrls a'),
        imageUrls = $('#imageUrls a'),
        imageNavThumbs = $('#thumbnails .img img'),
        imageViewContainer = '#image-viewer',
        zoomViewContainer = '#zoom-viewer';

    var activateDeepZoom = function(el) {

        var index = imageNavThumbs.index(el),
            deepZoomElement = $(zoomUrls).get(index),
            deepZoomUrl = $(deepZoomElement).attr("href");
        if (hasFlash) {
            var flashEmbed = '<embed class="flash-viewer-window" bgcolor="#ffffff" width="100%" height="100%" flashvars="source=' + deepZoomUrl + '" src="/assets/flash/OpenZoomViewer.swf" menu="false" wmode="opaque" allowFullScreen="true" pluginspage="http://www.adobe.com/go/getflashplayer" type="application/x-shockwave-flash"></embed>';
            $(zoomViewContainer).html(flashEmbed);
        }
        else if (!isIE) {
            // seadragon Zoom
            if (typeof deepZoomElement !== 'undefined') {
                if(typeof viewer === 'undefined') {
                    $.getScript('/assets/common/javascripts/seadragon/seadragon-min.js', function(){
                        Seadragon.Config.imagePath = "/assets/common/javascripts/seadragon/img/";
                        Seadragon.Config.autoHideControls = false;
                        var viewer = new Seadragon.Viewer(zoomViewContainer.replace('#',''));
                        viewer.addEventListener("error",function(){
                            // if viewer fails, then revert back to a regular image view
                            zoomNav = false;
                            var rImage = $(imageUrls).first().attr('href');
                            // change zoom-viewer id to regular image viewer for navigation and image replacement
                            $(zoomViewContainer).attr('id',imageViewContainer.replace('#',''));
                            // load the first image
                            $(imageViewContainer).html('

');
                        });
                        viewer.openDzi(deepZoomUrl);
                    });
                }else {
                    viewer.openDzi(deepZoomUrl);
                }
            }
        }
    };

    var zoomNav = !!$(zoomViewContainer).length;
    // VIEW DEEP ZOOM IMAGE
    if (zoomNav) {
        // activate deepzoom functionality for first image
        activateDeepZoom(0)
    }

    // hide thumb nav if there is only one image to view
    if( imageNavThumbs.size() <= 1 ){
        $('#thumbnails').hide();
    }
    // or  make thumbs clickable
    else {
        imageNavThumbs.each(function(index, el) {
            // switch image src onclick
            $(el).on("click", function() {
                if (zoomNav == false) {
                    var index = $(imageNavThumbs).index(el);
                    var imageSrc = $(imageUrls).get(index);
                    if(typeof imageSrc !== 'undefined') {
//                        $(viewedImageSource).attr("src", imageSrc);
                        $("#image-viewer img").attr("src", imageSrc);
                    }
                }
                else {
                    activateDeepZoom(el);
                }

            });
        });
    }

    /*
     * RIGHTS ICONS
     * Append the corresponding icon image to the rights URL
     */
    if ($('#rightsUrl').length) {
        var rights = stripTrailingSlash($('#rightsUrl').find('a').attr('href')),
            icon='',
            img = new Image();

        if (rights != 'undefined') {
            switch (rights) {
                case 'http://creativecommons.org/publicdomain/mark/1.0':
                    icon = 'cc_publicdomain_mark.png';
                    break;
                case 'http://creativecommons.org/publicdomain/zero/1.0':
                    icon = 'cc_publicdomain_zero.png';
                    break;
                case 'http://creativecommons.org/licenses/by/3.0':
                    icon = 'cc_by.png';
                    break;
                case 'http://creativecommons.org/licenses/by-nd/3.0':
                    icon = 'cc_by-nd-3.0.png';
                    break;
                case 'http://creativecommons.org/licenses/by-nc-sa/3.0':
                    icon = 'cc_by-nc-sa-3.0.png';
                    break;
                case 'http://creativecommons.org/licenses/by-sa/3.0/nl':
                    icon = 'cc_by-sa-3.0.png';
                    break;
                case 'http://creativecommons.org/licenses/by-sa/3.0':
                    icon = 'cc_by-sa-3.0.png';
                    break;
                case 'http://creativecommons.org/licenses/by-nc/3.0':
                    icon = 'cc_by-nc-3.0.png';
                    break;
                case 'http://creativecommons.org/licenses/by-nc/3.0/nl':
                    icon = 'cc_by-nc-3.0.png';
                    break;
                case 'http://creativecommons.org/licenses/by-nc-nd/3.0':
                    icon = 'cc_by-nc-nd-3.0.png';
                    break;
                case 'http://www.europeana.eu/rights/rr-f':
                    icon = 'eu_free_access.jpg';
                    break;
                case 'http://www.europeana.eu/rights/rr-p':
                    icon = 'eu_paid_access.jpg';
                    break;
                case 'http://www.europeana.eu/rights/rr-r':
                    icon = 'eu_restricted_access.jpg';
                    break;
                default:
                    icon = '';
            }
            if (icon != '') {
                img.src = '/assets/common/images/rights/'+icon;
                $('#rightsUrl').find('a').append(img);
            }
        }
    }

    if(currentSchema && (currentSchema.indexOf('icn') > -1)){
        // Dimcon specific temp fix: hide thumb added by fieldmarker
        $('#thumbnails img').each(function() {
            if($(this).attr('src').match(/(\/dimcon\/geldmuseum\/).*(\/220)/)){
                $(this).hide();
            }
        });
    }
});

/*
 * RELATED ITEMS, AKA 'MORE LIKE THIS'
 * Loads related items via ajax call
 */
function renderRelatedItems() {

    // Endpoint to retrieve related items for this object
    var mltEndpoint = '/api/search?id=' + Thing.hubId + '&format=json&mlt=true';

    if(jsLabels.objTitle.length && $('#object-title-big').length) {
        $('#object-title-big').html(jsLabels.objTitle);
    }

    // Make the request for the related objects. If this is a Collection or a Museum, then this request will fail.
    // Use the bad request to change the layout to accomodate a Museum or Collection view definition
    try {
    $.ajax({
        type: "GET",
        url: mltEndpoint,
        success: function(data) {
            if (data.result && data.result.relatedItems) {
                var rItems = data.result.relatedItems.item, html = '', tmp, org, owner, id, uri, title;
                if (rItems) {

                    $('.object-title').toggleClass('hide');
                    html = '
' + jsLabels.relatedItems + '
';
                    $.each(rItems, function (i, item) {

                        tmp = item.fields['delving_hubId'].split('_');
                        org = tmp[0];
                        owner = tmp[1];
                        id = tmp[2];
                        uri = "/" + org + "/" + owner + "/" + id + "?mlt=true";
                        // clean up title since sometimes it contains html
                        title = item.fields['delving_title'].replace(/<\/?[a-z][a-z0-9]*[^<>]*>/ig, "");

                        html += '
';
                        html += '';
                        html += '

'+ title.trunc(40) +'
';
                        if (item.fields['dc_creator']) {
                            html += '
'+jsLabels.creator+': '+item.fields['dc_creator']+'
';
                        }
                        if (item.fields['europeana_collectionTitle']) {
                            html += '
'+jsLabels.collection+': '+item.fields['europeana_collectionTitle']+'
';
                        }
                        html += '

';
                    });
                    html += "
";
                    $('#related-items').html(html);
                }
                else {
                     $('#related-items').hide();
                }
            }
        }
    });
    } catch(e) {
        //..do nothing and fail gracefully
    }
}

/*
 * PAGE UTILITES
 */

function stripTrailingSlash(str) {
    if(str.substr(-1) == '/') {
        return str.substr(0, str.length - 1);
    }
    return str;
}

var isIE = (navigator.appName.indexOf("Microsoft") != -1 && navigator.appVersion.indexOf("Windows") > -1)+1-1;

var hasFlash = function(){
    var nRequiredVersion = 8;
    if(isIE){
        document.write(' \n');
        if(window.hasFlash != null){ return window.hasFlash;};
    };
    if(navigator.mimeTypes && navigator.mimeTypes["application/x-shockwave-flash"] && navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin){
        var flashDescription = (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]).description;
        var vr = parseInt(flashDescription.charAt(flashDescription.indexOf(".") - 1)); if (vr < 4) vr += 10;
        return vr >= nRequiredVersion;
    };
    return false;
}();
<commit_msg>better dom check for rights url<commit_after>$(document).ready(function () {
    /*
    * DEEPZOOM FUNCTIONALITY
    * First checks if flash is enabled, if so use OpenZoomViewer.swf.
    * If that fails use Seadragon.js
    * And if that fails, view a regular image
    */
    var zoomUrls = $('#deepZoomUrls a'),
        imageUrls = $('#imageUrls a'),
        imageNavThumbs = $('#thumbnails .img img'),
        imageViewContainer = '#image-viewer',
        zoomViewContainer = '#zoom-viewer';

    var activateDeepZoom = function(el) {

        var index = imageNavThumbs.index(el),
            deepZoomElement = $(zoomUrls).get(index),
            deepZoomUrl = $(deepZoomElement).attr("href");
        if (hasFlash) {
            var flashEmbed = '<embed class="flash-viewer-window" bgcolor="#ffffff" width="100%" height="100%" flashvars="source=' + deepZoomUrl + '" src="/assets/flash/OpenZoomViewer.swf" menu="false" wmode="opaque" allowFullScreen="true" pluginspage="http://www.adobe.com/go/getflashplayer" type="application/x-shockwave-flash"></embed>';
            $(zoomViewContainer).html(flashEmbed);
        }
        else if (!isIE) {
            // seadragon Zoom
            if (typeof deepZoomElement !== 'undefined') {
                if(typeof viewer === 'undefined') {
                    $.getScript('/assets/common/javascripts/seadragon/seadragon-min.js', function(){
                        Seadragon.Config.imagePath = "/assets/common/javascripts/seadragon/img/";
                        Seadragon.Config.autoHideControls = false;
                        var viewer = new Seadragon.Viewer(zoomViewContainer.replace('#',''));
                        viewer.addEventListener("error",function(){
                            // if viewer fails, then revert back to a regular image view
                            zoomNav = false;
                            var rImage = $(imageUrls).first().attr('href');
                            // change zoom-viewer id to regular image viewer for navigation and image replacement
                            $(zoomViewContainer).attr('id',imageViewContainer.replace('#',''));
                            // load the first image
                            $(imageViewContainer).html('

');
                        });
                        viewer.openDzi(deepZoomUrl);
                    });
                }else {
                    viewer.openDzi(deepZoomUrl);
                }
            }
        }
    };

    var zoomNav = !!$(zoomViewContainer).length;
    // VIEW DEEP ZOOM IMAGE
    if (zoomNav) {
        // activate deepzoom functionality for first image
        activateDeepZoom(0)
    }

    // hide thumb nav if there is only one image to view
    if( imageNavThumbs.size() <= 1 ){
        $('#thumbnails').hide();
    }
    // or  make thumbs clickable
    else {
        imageNavThumbs.each(function(index, el) {
            // switch image src onclick
            $(el).on("click", function() {
                if (zoomNav == false) {
                    var index = $(imageNavThumbs).index(el);
                    var imageSrc = $(imageUrls).get(index);
                    if(typeof imageSrc !== 'undefined') {
//                        $(viewedImageSource).attr("src", imageSrc);
                        $("#image-viewer img").attr("src", imageSrc);
                    }
                }
                else {
                    activateDeepZoom(el);
                }

            });
        });
    }

    /*
     * RIGHTS ICONS
     * Append the corresponding icon image to the rights URL
     */
    if ($('#rightsUrl').find('a').length) {
        var rights = stripTrailingSlash($('#rightsUrl').find('a').attr('href')),
            icon='',
            img = new Image();

        if (rights != 'undefined') {
            switch (rights) {
                case 'http://creativecommons.org/publicdomain/mark/1.0':
                    icon = 'cc_publicdomain_mark.png';
                    break;
                case 'http://creativecommons.org/publicdomain/zero/1.0':
                    icon = 'cc_publicdomain_zero.png';
                    break;
                case 'http://creativecommons.org/licenses/by/3.0':
                    icon = 'cc_by.png';
                    break;
                case 'http://creativecommons.org/licenses/by-nd/3.0':
                    icon = 'cc_by-nd-3.0.png';
                    break;
                case 'http://creativecommons.org/licenses/by-nc-sa/3.0':
                    icon = 'cc_by-nc-sa-3.0.png';
                    break;
                case 'http://creativecommons.org/licenses/by-sa/3.0/nl':
                    icon = 'cc_by-sa-3.0.png';
                    break;
                case 'http://creativecommons.org/licenses/by-sa/3.0':
                    icon = 'cc_by-sa-3.0.png';
                    break;
                case 'http://creativecommons.org/licenses/by-nc/3.0':
                    icon = 'cc_by-nc-3.0.png';
                    break;
                case 'http://creativecommons.org/licenses/by-nc/3.0/nl':
                    icon = 'cc_by-nc-3.0.png';
                    break;
                case 'http://creativecommons.org/licenses/by-nc-nd/3.0':
                    icon = 'cc_by-nc-nd-3.0.png';
                    break;
                case 'http://www.europeana.eu/rights/rr-f':
                    icon = 'eu_free_access.jpg';
                    break;
                case 'http://www.europeana.eu/rights/rr-p':
                    icon = 'eu_paid_access.jpg';
                    break;
                case 'http://www.europeana.eu/rights/rr-r':
                    icon = 'eu_restricted_access.jpg';
                    break;
                default:
                    icon = '';
            }
            if (icon != '') {
                img.src = '/assets/common/images/rights/'+icon;
                $('#rightsUrl').find('a').append(img);
            }
        }
    }

    if(currentSchema && (currentSchema.indexOf('icn') > -1)){
        // Dimcon specific temp fix: hide thumb added by fieldmarker
        $('#thumbnails img').each(function() {
            if($(this).attr('src').match(/(\/dimcon\/geldmuseum\/).*(\/220)/)){
                $(this).hide();
            }
        });
    }
});

/*
 * RELATED ITEMS, AKA 'MORE LIKE THIS'
 * Loads related items via ajax call
 */
function renderRelatedItems() {

    // Endpoint to retrieve related items for this object
    var mltEndpoint = '/api/search?id=' + Thing.hubId + '&format=json&mlt=true';

    if(jsLabels.objTitle.length && $('#object-title-big').length) {
        $('#object-title-big').html(jsLabels.objTitle);
    }

    // Make the request for the related objects. If this is a Collection or a Museum, then this request will fail.
    // Use the bad request to change the layout to accomodate a Museum or Collection view definition
    try {
    $.ajax({
        type: "GET",
        url: mltEndpoint,
        success: function(data) {
            if (data.result && data.result.relatedItems) {
                var rItems = data.result.relatedItems.item, html = '', tmp, org, owner, id, uri, title;
                if (rItems) {

                    $('.object-title').toggleClass('hide');
                    html = '
' + jsLabels.relatedItems + '
';
                    $.each(rItems, function (i, item) {

                        tmp = item.fields['delving_hubId'].split('_');
                        org = tmp[0];
                        owner = tmp[1];
                        id = tmp[2];
                        uri = "/" + org + "/" + owner + "/" + id + "?mlt=true";
                        // clean up title since sometimes it contains html
                        title = item.fields['delving_title'].replace(/<\/?[a-z][a-z0-9]*[^<>]*>/ig, "");
                        html += '
';
                        html += '';
                        html += '

'+ title.trunc(40) +'
';
                        if (item.fields['dc_creator']) {
                            html += '
'+jsLabels.creator+': '+item.fields['dc_creator']+'
';
                        }
                        if (item.fields['europeana_collectionTitle']) {
                            html += '
'+jsLabels.collection+': '+item.fields['europeana_collectionTitle']+'
';
                        }
                        html += '

';
                    });
                    html += "
";
                    $('#related-items').html(html);
                }
                else {
                     $('#related-items').hide();
                }
            }
        }
    });
    } catch(e) {
        //..do nothing and fail gracefully
    }
}

/*
 * PAGE UTILITES
 */

function stripTrailingSlash(str) {
    //TODO: check if there' actually a str being passed to the function!
    if(str.substr(-1) == '/') {
        return str.substr(0, str.length - 1);
    }
    return str;
}

var isIE = (navigator.appName.indexOf("Microsoft") != -1 && navigator.appVersion.indexOf("Windows") > -1)+1-1;

var hasFlash = function(){
    var nRequiredVersion = 8;
    if(isIE){
        document.write(' \n');
        if(window.hasFlash != null){ return window.hasFlash;};
    };
    if(navigator.mimeTypes && navigator.mimeTypes["application/x-shockwave-flash"] && navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin){
        var flashDescription = (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]).description;
        var vr = parseInt(flashDescription.charAt(flashDescription.indexOf(".") - 1)); if (vr < 4) vr += 10;
        return vr >= nRequiredVersion;
    };
    return false;
}();
<|endoftext|>


<commit_before>import {clamp, compose, identity, isNil, map, reject, tap} from 'ramda'
import {Observable} from 'rx'
import React, {PropTypes} from 'react'
import THREE from 'three'
import {randomMesh} from '../../utils/webGLHelpers'

const {fromEvent, merge} = Observable

const cameraZ = 16
const minZ = -128
const sideLength = 1
const maxDepth = 3 * sideLength

let currentXYRatios = null
let controlPadElement = null
let renderLoopActive = null
let token = null
let renderer = null
let camera = null
let scene = null
let mouseInputEnabled = false
let controlPadActive = false

const setRendererSize = _ => {
  const rendererSize = window.innerWidth < window.innerHeight
    ? window.innerWidth
    : window.innerHeight * 0.8
  renderer.setSize(rendererSize, rendererSize)
}

const renderLoop = _ => {
  if (!renderLoopActive) return
  window.requestAnimationFrame(renderLoop)
  const controlPadHasNotBeenUsed = isNil(currentXYRatios)
  const {z} = token.position
  if (controlPadHasNotBeenUsed) return
  if (!controlPadActive) {
    if (z > minZ - maxDepth) token.position.z -= 1
    renderer.render(scene, camera)
    return
  }
  const {xRatio, yRatio} = currentXYRatios
  const xMod = xRatio < 0.5
    ? -(xRatio - 0.5) ** 2
    : (xRatio - 0.5) ** 2
  const yMod = yRatio < 0.5
    ? -(yRatio - 0.5) ** 2
    : (yRatio - 0.5) ** 2
  const rotationBaseAmount = 0.01
  const rotationVelocityComponent = 0.8
  token.rotation.x += rotationBaseAmount + rotationVelocityComponent * xMod
  token.rotation.y += rotationBaseAmount + rotationVelocityComponent * yMod
  token.rotation.z += rotationBaseAmount + rotationVelocityComponent * xMod * yMod
  token.position.x = (xRatio - 0.5) * cameraZ
  token.position.y = (0.5 - yRatio) * cameraZ
  const returnVelocity = 8
  if (z < 0) token.position.z += z > -returnVelocity ? -z : returnVelocity
  renderer.render(scene, camera)
}

const validRatio = clamp(0, 1 - Number.EPSILON)

const calculateXAndYRatio = e => {
  const {top, right, bottom, left} = e.target.getBoundingClientRect()
  const [width, height] = [right - left, bottom - top]
  const {clientX, clientY} = e.changedTouches && e.changedTouches[0] || e
  const [x, y] = [clientX - left, clientY - top]
  return {xRatio: validRatio(x / width), yRatio: validRatio(y / height)}
}

export default class extends React.Component {
  static propTypes = {
    inputEndTransducer: PropTypes.func,
    inputTransducer: PropTypes.func
  };
  componentDidMount () {
    const {inputEndTransducer, inputTransducer} = this.props
    controlPadElement = document.querySelector('.control-pad')
    const input$ = merge(
      fromEvent(controlPadElement, 'touchstart'),
      fromEvent(controlPadElement, 'touchmove'),
      fromEvent(controlPadElement, 'mousedown'),
      fromEvent(controlPadElement, 'mousemove')
    )
    const endInput$ = merge(
      fromEvent(controlPadElement, 'touchend'),
      fromEvent(controlPadElement, 'mouseup')
    )

    input$
      .transduce(compose(
        map(tap(e => mouseInputEnabled = e.type === 'mousedown'
          ? true
          : mouseInputEnabled)),
        reject(e => e instanceof window.MouseEvent && !mouseInputEnabled),
        map(tap(_ => controlPadActive = true)),
        map(e => currentXYRatios = calculateXAndYRatio(e))
      ))
      .transduce(inputTransducer)
      .subscribe(identity, ::console.error)

    endInput$
      .transduce(compose(
        map(tap(_ => controlPadActive = false)),
        map(tap(_ => mouseInputEnabled = false)),
        map(e => currentXYRatios = calculateXAndYRatio(e))
      ))
      .transduce(inputEndTransducer)
      .subscribe(identity, ::console.error)

    renderLoopActive = true
    scene = new THREE.Scene()
    camera = new THREE.PerspectiveCamera(
      54,
      1,
      cameraZ - maxDepth,
      cameraZ - minZ
    )
    renderer = new THREE.WebGLRenderer({alpha: true, canvas: controlPadElement})
    const directionalLight = new THREE.DirectionalLight(0xffffff)
    token = randomMesh()
    token.position.z = minZ - maxDepth

    directionalLight.position.set(16, 16, 24).normalize()
    scene.add(new THREE.AmbientLight(0x333333))
      .add(directionalLight)
      .add(token)
    camera.position.z = cameraZ

    setRendererSize()
    window.onresize = setRendererSize

    controlPadElement.oncontextmenu = e => e.preventDefault()

    renderLoop()
  }

  componentWillUnmount () {
    renderLoopActive = false
    window.onresize = null
  }

  render () {
    return <canvas width='768' height='768' className='control-pad'></canvas>
  }
}
<commit_msg>feat(control-pad) tweaks to control-pad visuals<commit_after>import {clamp, compose, identity, isNil, map, reject, tap} from 'ramda'
import {Observable} from 'rx'
import React, {PropTypes} from 'react'
import THREE from 'three'
import {randomMesh} from '../../utils/webGLHelpers'

const {fromEvent, merge} = Observable

const cameraZ = 16
const fallAwayVelocity = 1
const minZ = -160
const returnVelocity = 12

const sideLength = 1
const maxDepth = 3 * sideLength

let currentXYRatios = null
let controlPadElement = null
let renderLoopActive = null
let token = null
let renderer = null
let camera = null
let scene = null
let mouseInputEnabled = false
let controlPadActive = false

const setRendererSize = _ => {
  const rendererSize = window.innerWidth < window.innerHeight
    ? window.innerWidth
    : window.innerHeight * 0.8
  renderer.setSize(rendererSize, rendererSize)
}

const renderLoop = _ => {
  if (!renderLoopActive) return
  window.requestAnimationFrame(renderLoop)
  const controlPadHasNotBeenUsed = isNil(currentXYRatios)
  const {z} = token.position
  if (controlPadHasNotBeenUsed) return
  if (!controlPadActive) {
    if (z > minZ - maxDepth) token.position.z -= fallAwayVelocity
    renderer.render(scene, camera)
    return
  }
  const {xRatio, yRatio} = currentXYRatios
  const xMod = xRatio < 0.5
    ? -(xRatio - 0.5) ** 2
    : (xRatio - 0.5) ** 2
  const yMod = yRatio < 0.5
    ? -(yRatio - 0.5) ** 2
    : (yRatio - 0.5) ** 2
  const rotationBaseAmount = 0.01
  const rotationVelocityComponent = 0.8
  token.rotation.x += rotationBaseAmount + rotationVelocityComponent * xMod
  token.rotation.y += rotationBaseAmount + rotationVelocityComponent * yMod
  token.rotation.z += rotationBaseAmount + rotationVelocityComponent * xMod * yMod
  token.position.x = (xRatio - 0.5) * cameraZ
  token.position.y = (0.5 - yRatio) * cameraZ
  if (z < 0) token.position.z += z > -returnVelocity ? -z : returnVelocity
  renderer.render(scene, camera)
}

const validRatio = clamp(0, 1 - Number.EPSILON)

const calculateXAndYRatio = e => {
  const {top, right, bottom, left} = e.target.getBoundingClientRect()
  const [width, height] = [right - left, bottom - top]
  const {clientX, clientY} = e.changedTouches && e.changedTouches[0] || e
  const [x, y] = [clientX - left, clientY - top]
  return {xRatio: validRatio(x / width), yRatio: validRatio(y / height)}
}

export default class extends React.Component {
  static propTypes = {
    inputEndTransducer: PropTypes.func,
    inputTransducer: PropTypes.func
  };
  componentDidMount () {
    const {inputEndTransducer, inputTransducer} = this.props
    controlPadElement = document.querySelector('.control-pad')
    const input$ = merge(
      fromEvent(controlPadElement, 'touchstart'),
      fromEvent(controlPadElement, 'touchmove'),
      fromEvent(controlPadElement, 'mousedown'),
      fromEvent(controlPadElement, 'mousemove')
    )
    const endInput$ = merge(
      fromEvent(controlPadElement, 'touchend'),
      fromEvent(controlPadElement, 'mouseup')
    )

    input$
      .transduce(compose(
        map(tap(e => mouseInputEnabled = e.type === 'mousedown'
          ? true
          : mouseInputEnabled)),
        reject(e => e instanceof window.MouseEvent && !mouseInputEnabled),
        map(tap(_ => controlPadActive = true)),
        map(e => currentXYRatios = calculateXAndYRatio(e))
      ))
      .transduce(inputTransducer)
      .subscribe(identity, ::console.error)

    endInput$
      .transduce(compose(
        map(tap(_ => controlPadActive = false)),
        map(tap(_ => mouseInputEnabled = false)),
        map(e => currentXYRatios = calculateXAndYRatio(e))
      ))
      .transduce(inputEndTransducer)
      .subscribe(identity, ::console.error)

    renderLoopActive = true
    scene = new THREE.Scene()
    camera = new THREE.PerspectiveCamera(
      54,
      1,
      cameraZ - maxDepth,
      cameraZ - minZ
    )
    renderer = new THREE.WebGLRenderer({alpha: true, canvas: controlPadElement})
    const directionalLight = new THREE.DirectionalLight(0xffffff)
    token = randomMesh()
    token.position.z = minZ - maxDepth

    directionalLight.position.set(16, 16, 24).normalize()
    scene.add(new THREE.AmbientLight(0x333333))
      .add(directionalLight)
      .add(token)
    camera.position.z = cameraZ

    setRendererSize()
    window.onresize = setRendererSize

    controlPadElement.oncontextmenu = e => e.preventDefault()

    renderLoop()
  }

  componentWillUnmount () {
    renderLoopActive = false
    window.onresize = null
  }

  render () {
    return <canvas width='768' height='768' className='control-pad'></canvas>
  }
}
<|endoftext|>


<commit_before>import React from 'react'
import { Link } from 'react-router'
import Avatar from '../assets/Avatar'
import TextRegion from '../posts/regions/TextRegion'
import ImageRegion from '../posts/regions/ImageRegion'

let models = {}

function textRegion(region, key, isGridLayout, postDetailPath) {
  return (
    <TextRegion
      content={ region.data }
      isGridLayout={ isGridLayout }
      key={ key }
      postDetailPath={ postDetailPath }
    />
  )
}

function imageRegion(region, key, isGridLayout, postDetailPath, isNotification, isComment) {
  return (
    <ImageRegion
      assets={ models.assets }
      content={ region.data }
      isComment={ isComment }
      isGridLayout={ isGridLayout }
      isNotification={ isNotification }
      key={ key }
      links={ region.links }
      postDetailPath={ postDetailPath }
    />
  )
}

function embedRegion(region, key) {
  const data = {}
  data[`data-${region.data.service}-id`] = region.data.id
  return (

      <Link className="EmbedRegionContent" to={ region.data.url }>

      </Link>

  )
}

export function regionItemsForNotifications(content, postDetailPath = null) {
  const assets = []
  const texts = []

  content.forEach((region, i) => {
    switch (region.kind) {
      case 'text':
        texts.push(textRegion(region, `TextRegion_${i}`, false, postDetailPath))
        break
      case 'image':
        assets.push(imageRegion(region, `ImageRegion_${i}`, true, postDetailPath, true))
        break
      case 'embed':
        assets.push(embedRegion(region, `EmbedRegion_${i}`))
        break
      case 'rule':
        texts.push(<hr className="NotificationRule" key={`NotificationRule_${i}`} />)
        break
      default:
        break
    }
  })
  return { assets, texts }
}

// TODO: Wrap all of these function arguments in an object and destructure so order doesn't matter.
/* eslint-disable max-len */
export function regionItems(content, only = null, isGridLayout = true, postDetailPath = null, isNotification = false, isComment = false) {
  return content.map((region, i) => {
    if (!only || only === region.kind) {
      switch (region.kind) {
        case 'text':
          return textRegion(region, `TextRegion_${i}`, isGridLayout, postDetailPath)
        case 'image':
          return imageRegion(region, `ImageRegion_${i}`, isGridLayout, postDetailPath, isNotification, isComment)
        case 'embed':
          return embedRegion(region, `EmbedRegion_${i}`)
        default:
          return null
      }
    }
    return null
  })
}

export function body(content, id, isGridLayout, postDetailPath = null, isComment = false) {
  return (
    <div className="PostBody" key={ `PostBody_${id}` }>
      { regionItems(content, null, isGridLayout, postDetailPath, isComment) }

  )
}

export function repostedBody(author, content, id, isGridLayout, postDetailPath = null) {
  return (
    <div className="PostBody RepostedBody" key={ `RepostedBody_${id}` }>
      <Avatar
        priority={ author.relationshipPriority }
        sources={ author.avatar }
        to={ `/${author.username}` }
        userId={ `${author.id}` }
        username={ author.username }
      />
      { regionItems(content, null, isGridLayout, postDetailPath) }

  )
}

export function setModels(newModels) {
  models = newModels
}

<commit_msg>Wrap the embed in a div to fix size issue.<commit_after>import React from 'react'
import { Link } from 'react-router'
import Avatar from '../assets/Avatar'
import TextRegion from '../posts/regions/TextRegion'
import ImageRegion from '../posts/regions/ImageRegion'

let models = {}

function textRegion(region, key, isGridLayout, postDetailPath) {
  return (
    <TextRegion
      content={ region.data }
      isGridLayout={ isGridLayout }
      key={ key }
      postDetailPath={ postDetailPath }
    />
  )
}

function imageRegion(region, key, isGridLayout, postDetailPath, isNotification, isComment) {
  return (
    <ImageRegion
      assets={ models.assets }
      content={ region.data }
      isComment={ isComment }
      isGridLayout={ isGridLayout }
      isNotification={ isNotification }
      key={ key }
      links={ region.links }
      postDetailPath={ postDetailPath }
    />
  )
}

function embedRegion(region, key) {
  const data = {}
  data[`data-${region.data.service}-id`] = region.data.id
  return (

        <Link className="EmbedRegionContent" to={ region.data.url }>

        </Link>

  )
}

export function regionItemsForNotifications(content, postDetailPath = null) {
  const assets = []
  const texts = []

  content.forEach((region, i) => {
    switch (region.kind) {
      case 'text':
        texts.push(textRegion(region, `TextRegion_${i}`, false, postDetailPath))
        break
      case 'image':
        assets.push(imageRegion(region, `ImageRegion_${i}`, true, postDetailPath, true))
        break
      case 'embed':
        assets.push(embedRegion(region, `EmbedRegion_${i}`))
        break
      case 'rule':
        texts.push(<hr className="NotificationRule" key={`NotificationRule_${i}`} />)
        break
      default:
        break
    }
  })
  return { assets, texts }
}

// TODO: Wrap all of these function arguments in an object and destructure so order doesn't matter.
/* eslint-disable max-len */
export function regionItems(content, only = null, isGridLayout = true, postDetailPath = null, isNotification = false, isComment = false) {
  return content.map((region, i) => {
    if (!only || only === region.kind) {
      switch (region.kind) {
        case 'text':
          return textRegion(region, `TextRegion_${i}`, isGridLayout, postDetailPath)
        case 'image':
          return imageRegion(region, `ImageRegion_${i}`, isGridLayout, postDetailPath, isNotification, isComment)
        case 'embed':
          return embedRegion(region, `EmbedRegion_${i}`)
        default:
          return null
      }
    }
    return null
  })
}

export function body(content, id, isGridLayout, postDetailPath = null, isComment = false) {
  return (
    <div className="PostBody" key={ `PostBody_${id}` }>
      { regionItems(content, null, isGridLayout, postDetailPath, isComment) }

  )
}

export function repostedBody(author, content, id, isGridLayout, postDetailPath = null) {
  return (
    <div className="PostBody RepostedBody" key={ `RepostedBody_${id}` }>
      <Avatar
        priority={ author.relationshipPriority }
        sources={ author.avatar }
        to={ `/${author.username}` }
        userId={ `${author.id}` }
        username={ author.username }
      />
      { regionItems(content, null, isGridLayout, postDetailPath) }

  )
}

export function setModels(newModels) {
  models = newModels
}

<|endoftext|>


<commit_before>window.requestAnimFrame = (function () {
  return  window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame    ||
    window.oRequestAnimationFrame      ||
    window.msRequestAnimationFrame     ||
    function(/* function */ callback, /* DOMElement */ element){
      window.setTimeout(callback, 1000 / 60);
    };
})();

LEFT = 37;
RIGHT = 39;
FIRE = 32;

function getRandom(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

function AssetManager() {
  this.successCount = 0;
  this.errorCount = 0;
  this.cache = {};
  this.downloadQueue = [];
  this.soundsQueue = [];
}

AssetManager.prototype.queueDownload = function (path) {
  this.downloadQueue.push(path);
}

AssetManager.prototype.queueSound = function (id, path) {
  this.soundsQueue.push({id: id, path: path});
}

AssetManager.prototype.downloadAll = function (callback) {
  if (this.downloadQueue.length === 0 && this.soundsQueue.length === 0) {
    callback();
  }

  //this.downloadSounds(callback);

  for (var i = 0; i < this.downloadQueue.length; i++) {
    var path = this.downloadQueue[i];
    var img = new Image();
    var that = this;
    img.addEventListener("load", function () {
      console.log(this.src + ' is loaded');
      that.successCount += 1;
      if (that.isDone()) {
          callback();
      }
    }, false);
    img.addEventListener("error", function () {
      that.errorCount += 1;
      if (that.isDone()) {
          callback();
      }
    }, false);
    img.src = path;
    this.cache[path] = img;
  }
}

AssetManager.prototype.downloadSounds = function (callback) {
  var that = this;
  soundManager.onready(function() {
    console.log('soundManager ready');
    for (var i = 0; i < that.soundsQueue.length; i++) {
      that.downloadSound(that.soundsQueue[i].id, that.soundsQueue[i].path, callback);
    }
  });
  soundManager.ontimeout(function() {
    console.log('SM2 did not start');
  });
}

AssetManager.prototype.downloadSound = function (id, path, callback) {
  var that = this;
  this.cache[path] = soundManager.createSound({
    id: id,
    autoLoad: true,
    url: path,
    onload: function() {
      console.log(this.url + ' is loaded');
      that.successCount += 1;
      if (that.isDone()) {
        callback();
      }
    }
  });
}

AssetManager.prototype.getSound = function (path) {
  return this.cache[path];
}

AssetManager.prototype.getAsset = function (path) {
  return this.cache[path];
}

AssetManager.prototype.isDone = function () {
  return ((this.downloadQueue.length + this.soundsQueue.length) == this.successCount + this.errorCount);
}

function Timer() {
  this.gameTime = 0;
  this.maxStep = 0.05;
  this.wallLastTimestamp = 0;
}

Timer.prototype.tick = function () {
  var wallCurrent = Date.now();
  var wallDelta = (wallCurrent - this.wallLastTimestamp) / 1000;
  this.wallLastTimestamp = wallCurrent;

  var gameDelta = Math.min(wallDelta, this.maxStep);
  this.gameTime += gameDelta;
  return gameDelta;
}

function GameEngine() {
  this.entities = [];
  this.key = null;
  this.timer = new Timer();
  this.surfaceWidth = null;
  this.surfaceHeight = null;
  this.halfSurfaceWidth = null;
  this.halfSurfaceHeight = null;
  this.stop = false;
  this.keyFire = false;
}

GameEngine.prototype.init = function (ctx) {
  console.log('game initialized');
  this.ctx = ctx;
  this.surfaceWidth = this.ctx.canvas.width;
  this.surfaceHeight = this.ctx.canvas.height;
  this.halfSurfaceWidth = this.surfaceWidth/2;
  this.halfSurfaceHeight = this.surfaceHeight/2;
  this.startInput();
}

GameEngine.prototype.start = function () {
  console.log("starting game");
  var that = this;
  (function gameLoop () {
    if (that.stop) return;
    that.loop();
    requestAnimFrame(gameLoop, that.ctx.canvas);
  })();
}

GameEngine.prototype.startInput = function () {
  var that = this;
  document.addEventListener("keydown", function (e) {
    var code = e.keyCode;
    if ([LEFT, RIGHT, FIRE].indexOf(code) !== -1) {
      that.key = code;
      e.stopPropagation();
      e.preventDefault();
    }
  });

  document.addEventListener("keyup", function (e) {
    var code = e.keyCode;
    if (code === FIRE) {
      that.keyFire = true;
      e.stopPropagation();
      e.preventDefault();
    }
  });
}

GameEngine.prototype.addEntity = function (entity) {
    this.entities.push(entity);
}

GameEngine.prototype.draw = function (callback) {
  this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
  for (var i = 0; i < this.entities.length; i++) {
    this.entities[i].draw(this.ctx);
  }
  if (callback) {
    callback(this);
  }
}

GameEngine.prototype.update = function () {
  var entitiesCount = this.entities.length;

  for (var i = 0; i < entitiesCount; i++) {
    var entity = this.entities[i];
    if (!entity.removeFromWorld) {
      entity.update();
    }
  }

  for (var i = this.entities.length-1; i >= 0; --i) {
    if (this.entities[i].removeFromWorld) {
      this.entities.splice(i, 1);
    }
  }
}

GameEngine.prototype.loop = function () {
  this.clockTick = this.timer.tick();
  this.update();
  this.draw();
  this.key = null;
  this.keyFire = false;
}

function Entity(game, x, y) {
  this.game = game;
  this.x = x;
  this.y = y;
  this.removeFromWorld = false;
}

Entity.prototype.update = function() {
}

Entity.prototype.draw = function(ctx) {
  // if (this.game.showOutlines && this.radius) {
  //   ctx.beginPath();
  //   ctx.strokeStyle = "green";
  //   ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, false);
  //   ctx.stroke();
  //   ctx.closePath();
  // }
}

Entity.prototype.drawSpriteCentered = function(ctx) {
  if (this.sprite && this.x && this.y) {
    var x = this.x - this.sprite.width/2;
    var y = this.y - this.sprite.height/2;
    ctx.drawImage(this.sprite, x, y);
  }
}

Entity.prototype.outsideScreen = function() {
  return (this.x > this.game.surfaceWidth || this.x < 0 ||
    this.y > this.game.surfaceHeight || this.y < 0);
}

function Player(game) {
  var asset = ASSET_MANAGER.getAsset('img/player.png')
  Entity.call(this, game, game.surfaceWidth/2, game.surfaceHeight - asset.height/ 2  - 10);
  this.sprite = asset;
  this.speed = 30;
}
Player.prototype = new Entity();
Player.prototype.constructor = Player;

Player.prototype.update = function () {
  var key = this.game.key;
  if (key === LEFT) {
    this.x -= this.speed; //* this.game.clockTick;
  } else if (key === RIGHT) {
    this.x += this.speed; //* this.game.clockTick;
  }
  if (this.x - this.sprite.width / 2 < 0) {
    this.x = 1 + this.sprite.width / 2;
  }
  if (this.x + this.sprite.width / 2 > this.game.surfaceWidth) {
    this.x = this.game.surfaceWidth - 1 - this.sprite.width / 2;
  }
  if (this.game.keyFire) {
    this.shoot();
  }
  Entity.prototype.update.call(this);
}

Player.prototype.draw = function (ctx) {
  this.drawSpriteCentered(ctx);
  Entity.prototype.draw.call(this, ctx);
}

Player.prototype.shoot = function () {
  var bullet = new Bullet(this.game, this.x, this.y - this.sprite.height);
  this.game.addEntity(bullet);
}

function Bullet(game, x, y) {
  Entity.call(this, game, x, y);
  this.sprite = ASSET_MANAGER.getAsset('img/laserRed.png');
  this.speed = 580;
}
Bullet.prototype = new Entity();
Bullet.prototype.constructor = Bullet;

Bullet.prototype.update = function () {
  if (this.outsideScreen()) {
    this.removeFromWorld = true;
  } else {
    this.y -= this.speed * this.game.clockTick;
  }
  Entity.prototype.update.call(this);
}

Bullet.prototype.draw = function (ctx) {
  ctx.drawImage(this.sprite, this.x, this.y);
  Entity.prototype.draw.call(this, ctx);
}

function Asteriod(game) {
  Entity.call(this, game);
  if (getRandom(1, 10) > 5) {
    this.sprite = ASSET_MANAGER.getAsset('img/asteroidBig.png');
  } else {
    this.sprite = ASSET_MANAGER.getAsset('img/asteroidSmall.png');
  }
  this.speed = getRandom(40, 100);
  this.setCoords();
  this.m = Math.floor((this.game.player.y - this.y) / (this.game.player.x - this.x));
  if (this.m == 0) this.m = 1;
  else if (this.m > 0 && this.m > 5) this.m = 5;
  else if (this.m < 0 && this.m < -5) this.m = -5;
}
Asteriod.prototype = new Entity();
Asteriod.prototype.constructor = Asteriod;

Asteriod.prototype.setCoords = function () {
  this.y = -this.sprite.height;
  this.x = getRandom(0, game.surfaceWidth);
}

Asteriod.prototype.draw = function (ctx) {
  this.drawSpriteCentered(ctx);
  Entity.prototype.draw.call(this, ctx);
}

Asteriod.prototype.update = function () {
  this.x += this.m  * this.speed/2 * this.game.clockTick;
  this.y += Math.abs(this.m) * this.speed * this.game.clockTick;
  if (this.outsideScreen()) {
    this.removeFromWorld = true;
  }
}

Asteriod.prototype.outsideScreen = function () {
  return (this.x > this.game.surfaceWidth || this.x < 0 ||
    this.y > this.game.surfaceHeight);
}

function FallingAsteroids() {
  GameEngine.call(this);
}
FallingAsteroids.prototype = new GameEngine();
FallingAsteroids.prototype.constructor = FallingAsteroids;

FallingAsteroids.prototype.start = function () {
  this.player = new Player(this);
  this.addEntity(this.player);
  this.addEntity(new Asteriod(this));
  GameEngine.prototype.start.call(this);
}

FallingAsteroids.prototype.update = function () {
  if (this.lastAsteroidAddedAt == null || (this.timer.gameTime - this.lastAsteroidAddedAt) > 1.5) {
    this.addEntity(new Asteriod(this));
    this.lastAsteroidAddedAt = this.timer.gameTime;
  }
  GameEngine.prototype.update.call(this);
}

FallingAsteroids.prototype.draw = function() {
  GameEngine.prototype.draw.call(this);
}

var canvas = document.getElementById('surface');
var ctx = canvas.getContext('2d');
var game = new FallingAsteroids();
var ASSET_MANAGER = new AssetManager();

ASSET_MANAGER.queueDownload('img/asteroidBig.png');
ASSET_MANAGER.queueDownload('img/asteroidSmall.png');
ASSET_MANAGER.queueDownload('img/enemyShip.png');
ASSET_MANAGER.queueDownload('img/laserGreen.png');
ASSET_MANAGER.queueDownload('img/laserGreenShot.png');
ASSET_MANAGER.queueDownload('img/laserRed.png');
ASSET_MANAGER.queueDownload('img/laserRedShot.png');
ASSET_MANAGER.queueDownload('img/life.png');
ASSET_MANAGER.queueDownload('img/player.png');

ASSET_MANAGER.downloadAll(function() {
  console.log('complete');
  game.init(ctx);
  game.start();
});<commit_msg>Debug lines. Check collitions<commit_after>window.requestAnimFrame = (function () {
  return  window.requestAnimationFrame ||
    window.webkitRequestAnimationFrame ||
    window.mozRequestAnimationFrame    ||
    window.oRequestAnimationFrame      ||
    window.msRequestAnimationFrame     ||
    function(/* function */ callback, /* DOMElement */ element){
      window.setTimeout(callback, 1000 / 60);
    };
})();

LEFT = 37;
RIGHT = 39;
FIRE = 32;

function getRandom(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

function AssetManager() {
  this.successCount = 0;
  this.errorCount = 0;
  this.cache = {};
  this.downloadQueue = [];
  this.soundsQueue = [];
}

AssetManager.prototype.queueDownload = function (path) {
  this.downloadQueue.push(path);
}

AssetManager.prototype.queueSound = function (id, path) {
  this.soundsQueue.push({id: id, path: path});
}

AssetManager.prototype.downloadAll = function (callback) {
  if (this.downloadQueue.length === 0 && this.soundsQueue.length === 0) {
    callback();
  }

  //this.downloadSounds(callback);

  for (var i = 0; i < this.downloadQueue.length; i++) {
    var path = this.downloadQueue[i];
    var img = new Image();
    var that = this;
    img.addEventListener("load", function () {
      console.log(this.src + ' is loaded');
      that.successCount += 1;
      if (that.isDone()) {
          callback();
      }
    }, false);
    img.addEventListener("error", function () {
      that.errorCount += 1;
      if (that.isDone()) {
          callback();
      }
    }, false);
    img.src = path;
    this.cache[path] = img;
  }
}

AssetManager.prototype.downloadSounds = function (callback) {
  var that = this;
  soundManager.onready(function() {
    console.log('soundManager ready');
    for (var i = 0; i < that.soundsQueue.length; i++) {
      that.downloadSound(that.soundsQueue[i].id, that.soundsQueue[i].path, callback);
    }
  });
  soundManager.ontimeout(function() {
    console.log('SM2 did not start');
  });
}

AssetManager.prototype.downloadSound = function (id, path, callback) {
  var that = this;
  this.cache[path] = soundManager.createSound({
    id: id,
    autoLoad: true,
    url: path,
    onload: function() {
      console.log(this.url + ' is loaded');
      that.successCount += 1;
      if (that.isDone()) {
        callback();
      }
    }
  });
}

AssetManager.prototype.getSound = function (path) {
  return this.cache[path];
}

AssetManager.prototype.getAsset = function (path) {
  return this.cache[path];
}

AssetManager.prototype.isDone = function () {
  return ((this.downloadQueue.length + this.soundsQueue.length) == this.successCount + this.errorCount);
}

function Timer() {
  this.gameTime = 0;
  this.maxStep = 0.05;
  this.wallLastTimestamp = 0;
}

Timer.prototype.tick = function () {
  var wallCurrent = Date.now();
  var wallDelta = (wallCurrent - this.wallLastTimestamp) / 1000;
  this.wallLastTimestamp = wallCurrent;

  var gameDelta = Math.min(wallDelta, this.maxStep);
  this.gameTime += gameDelta;
  return gameDelta;
}

function GameEngine() {
  this.entities = [];
  this.key = null;
  this.timer = new Timer();
  this.surfaceWidth = null;
  this.surfaceHeight = null;
  this.halfSurfaceWidth = null;
  this.halfSurfaceHeight = null;
  this.stop = false;
  this.keyFire = false;
}

GameEngine.prototype.init = function (ctx) {
  console.log('game initialized');
  this.ctx = ctx;
  this.surfaceWidth = this.ctx.canvas.width;
  this.surfaceHeight = this.ctx.canvas.height;
  this.halfSurfaceWidth = this.surfaceWidth/2;
  this.halfSurfaceHeight = this.surfaceHeight/2;
  this.startInput();
}

GameEngine.prototype.start = function () {
  console.log("starting game");
  var that = this;
  (function gameLoop () {
    if (that.stop) return;
    that.loop();
    requestAnimFrame(gameLoop, that.ctx.canvas);
  })();
}

GameEngine.prototype.startInput = function () {
  var that = this;
  document.addEventListener("keydown", function (e) {
    var code = e.keyCode;
    if ([LEFT, RIGHT, FIRE].indexOf(code) !== -1) {
      that.key = code;
      e.stopPropagation();
      e.preventDefault();
    }
  });

  document.addEventListener("keyup", function (e) {
    var code = e.keyCode;
    if (code === FIRE) {
      that.keyFire = true;
      e.stopPropagation();
      e.preventDefault();
    }
  });
}

GameEngine.prototype.addEntity = function (entity) {
    this.entities.push(entity);
}

GameEngine.prototype.draw = function (callback) {
  this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height);
  for (var i = 0; i < this.entities.length; i++) {
    this.entities[i].draw(this.ctx);
  }
  if (callback) {
    callback(this);
  }
}

GameEngine.prototype.update = function () {
  var entitiesCount = this.entities.length;

  for (var i = 0; i < entitiesCount; i++) {
    var entity = this.entities[i];
    if (!entity.removeFromWorld) {
      entity.update();
    }
  }

  for (var i = this.entities.length-1; i >= 0; --i) {
    if (this.entities[i].removeFromWorld) {
      this.entities.splice(i, 1);
    }
  }
}

GameEngine.prototype.loop = function () {
  this.clockTick = this.timer.tick();
  this.update();
  this.draw();
  this.key = null;
  this.keyFire = false;
}

function Entity(game, x, y) {
  this.game = game;
  this.x = x;
  this.y = y;
  this.removeFromWorld = false;
}

Entity.prototype.update = function() {
}

Entity.prototype.draw = function(ctx) {
  if (this.game.showOutlines) {
    var halfWidth = this.sprite.width / 2;
    var halfHeight = this.sprite.height / 2;
    ctx.beginPath();
    ctx.rect(this.x - halfWidth, this.y - halfHeight, this.sprite.width, this.sprite.height);
    ctx.strokeStyle = "green";
    ctx.stroke();
    ctx.closePath();
  }
}

Entity.prototype.drawSpriteCentered = function(ctx) {
  if (this.sprite && this.x && this.y) {
    var x = this.x - this.sprite.width/2;
    var y = this.y - this.sprite.height/2;
    ctx.drawImage(this.sprite, x, y);
  }
}

Entity.prototype.outsideScreen = function() {
  return (this.x > this.game.surfaceWidth || this.x < 0 ||
    this.y > this.game.surfaceHeight || this.y < 0);
}

function Player(game) {
  var asset = ASSET_MANAGER.getAsset('img/player.png')
  Entity.call(this, game, game.surfaceWidth/2, game.surfaceHeight - asset.height/ 2  - 10);
  this.sprite = asset;
  this.speed = 30;
}
Player.prototype = new Entity();
Player.prototype.constructor = Player;

Player.prototype.update = function () {
  var key = this.game.key;
  if (key === LEFT) {
    this.x -= this.speed; //* this.game.clockTick;
  } else if (key === RIGHT) {
    this.x += this.speed; //* this.game.clockTick;
  }
  if (this.x - this.sprite.width / 2 < 0) {
    this.x = 1 + this.sprite.width / 2;
  }
  if (this.x + this.sprite.width / 2 > this.game.surfaceWidth) {
    this.x = this.game.surfaceWidth - 1 - this.sprite.width / 2;
  }
  if (this.game.keyFire) {
    this.shoot();
  }
  Entity.prototype.update.call(this);
}

Player.prototype.draw = function (ctx) {
  this.drawSpriteCentered(ctx);
  Entity.prototype.draw.call(this, ctx);
}

Player.prototype.shoot = function () {
  var bullet = new Bullet(this.game, this.x, this.y - this.sprite.height);
  this.game.addEntity(bullet);
}

function Bullet(game, x, y) {
  Entity.call(this, game, x, y);
  this.sprite = ASSET_MANAGER.getAsset('img/laserRed.png');
  this.speed = 580;
}
Bullet.prototype = new Entity();
Bullet.prototype.constructor = Bullet;

Bullet.prototype.update = function () {
  if (this.outsideScreen()) {
    this.removeFromWorld = true;
  } else {
    this.y -= this.speed * this.game.clockTick;
  }

  for (var i = 0; i < this.game.entities.length; i++) {
    var asteroid = this.game.entities[i];
    if (asteroid instanceof Asteriod && this.collidesWith(asteroid)) {
      console.log('hit!');
      this.removeFromWorld = true;
    }
  }

  Entity.prototype.update.call(this);
}

Bullet.prototype.draw = function (ctx) {
  ctx.drawImage(this.sprite, this.x, this.y);
  Entity.prototype.draw.call(this, ctx);
}

Bullet.prototype.collidesWith = function (asteroid) {
  var aX = asteroid.x - asteroid.sprite.width/2;
  var aY = asteroid.y - asteroid.sprite.height/2;
  var aW = asteroid.sprite.width;
  var aH = asteroid.sprite.height;
  return this.x < aX + aW &&
    this.x + this.sprite.width > aX &&
    this.y < aY + aH &&
    this.y + this.sprite.height > aY;
}

function Asteriod(game) {
  Entity.call(this, game);
  if (getRandom(1, 10) > 5) {
    this.sprite = ASSET_MANAGER.getAsset('img/asteroidBig.png');
  } else {
    this.sprite = ASSET_MANAGER.getAsset('img/asteroidSmall.png');
  }
  this.speed = getRandom(40, 100);
  this.setCoords();
  this.m = Math.floor((this.game.player.y - this.y) / (this.game.player.x - this.x));
  if (this.m == 0) this.m = 1;
  else if (this.m > 0 && this.m > 5) this.m = 5;
  else if (this.m < 0 && this.m < -5) this.m = -5;
}
Asteriod.prototype = new Entity();
Asteriod.prototype.constructor = Asteriod;

Asteriod.prototype.setCoords = function () {
  this.y = -this.sprite.height;
  this.x = getRandom(0, game.surfaceWidth);
}

Asteriod.prototype.draw = function (ctx) {
  this.drawSpriteCentered(ctx);
  Entity.prototype.draw.call(this, ctx);
}

Asteriod.prototype.update = function () {
  this.x += this.m  * this.speed/2 * this.game.clockTick;
  this.y += Math.abs(this.m) * this.speed * this.game.clockTick;
  if (this.outsideScreen()) {
    this.removeFromWorld = true;
  }
}

Asteriod.prototype.outsideScreen = function () {
  return (this.x > this.game.surfaceWidth || this.x < 0 ||
    this.y > this.game.surfaceHeight);
}

function FallingAsteroids() {
  GameEngine.call(this);
  this.showOutlines = true;
}
FallingAsteroids.prototype = new GameEngine();
FallingAsteroids.prototype.constructor = FallingAsteroids;

FallingAsteroids.prototype.start = function () {
  this.player = new Player(this);
  this.addEntity(this.player);
  this.addEntity(new Asteriod(this));
  GameEngine.prototype.start.call(this);
}

FallingAsteroids.prototype.update = function () {
  if (this.lastAsteroidAddedAt == null || (this.timer.gameTime - this.lastAsteroidAddedAt) > 1.5) {
    this.addEntity(new Asteriod(this));
    this.lastAsteroidAddedAt = this.timer.gameTime;
  }
  GameEngine.prototype.update.call(this);
}

FallingAsteroids.prototype.draw = function() {
  GameEngine.prototype.draw.call(this);
}

var canvas = document.getElementById('surface');
var ctx = canvas.getContext('2d');
var game = new FallingAsteroids();
var ASSET_MANAGER = new AssetManager();

ASSET_MANAGER.queueDownload('img/asteroidBig.png');
ASSET_MANAGER.queueDownload('img/asteroidSmall.png');
ASSET_MANAGER.queueDownload('img/enemyShip.png');
ASSET_MANAGER.queueDownload('img/laserGreen.png');
ASSET_MANAGER.queueDownload('img/laserGreenShot.png');
ASSET_MANAGER.queueDownload('img/laserRed.png');
ASSET_MANAGER.queueDownload('img/laserRedShot.png');
ASSET_MANAGER.queueDownload('img/life.png');
ASSET_MANAGER.queueDownload('img/player.png');

ASSET_MANAGER.downloadAll(function() {
  console.log('complete');
  game.init(ctx);
  game.start();
});<|endoftext|>


<commit_before>/*
Copyright 2015, 2016 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict';

var React = require('react');
var ReactDOM = require("react-dom");
var classNames = require('classnames');
var dis = require("../../../dispatcher");
var MatrixClientPeg = require('../../../MatrixClientPeg');
import DMRoomMap from '../../../utils/DMRoomMap';
var sdk = require('../../../index');
var ContextualMenu = require('../../structures/ContextualMenu');
var RoomNotifs = require('../../../RoomNotifs');
var FormattingUtils = require('../../../utils/FormattingUtils');
import AccessibleButton from '../elements/AccessibleButton';
var UserSettingsStore = require('../../../UserSettingsStore');

module.exports = React.createClass({
    displayName: 'RoomTile',

    propTypes: {
        connectDragSource: React.PropTypes.func,
        connectDropTarget: React.PropTypes.func,
        onClick: React.PropTypes.func,
        isDragging: React.PropTypes.bool,

        room: React.PropTypes.object.isRequired,
        collapsed: React.PropTypes.bool.isRequired,
        selected: React.PropTypes.bool.isRequired,
        unread: React.PropTypes.bool.isRequired,
        highlight: React.PropTypes.bool.isRequired,
        isInvite: React.PropTypes.bool.isRequired,
        incomingCall: React.PropTypes.object,
    },

    getDefaultProps: function() {
        return {
            isDragging: false,
        };
    },

    getInitialState: function() {
        return({
            hover : false,
            badgeHover : false,
            notificationTagMenu: false,
            roomTagMenu: false,
            notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId),
        });
    },

    _shouldShowNotifBadge: function() {
        const showBadgeInStates = [RoomNotifs.ALL_MESSAGES, RoomNotifs.ALL_MESSAGES_LOUD];
        return showBadgeInStates.indexOf(this.state.notifState) > -1;
    },

    _shouldShowMentionBadge: function() {
        return this.state.notifState != RoomNotifs.MUTE;
    },

    _isDirectMessageRoom: function(roomId) {
        var dmRooms = DMRoomMap.shared().getUserIdForRoomId(roomId);
        if (dmRooms) {
            return true;
        } else {
            return false;
        }
    },

    onAccountData: function(accountDataEvent) {
        if (accountDataEvent.getType() == 'm.push_rules') {
            this.setState({
                notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId),
            });
        }
    },

    componentWillMount: function() {
        MatrixClientPeg.get().on("accountData", this.onAccountData);
    },

    componentWillUnmount: function() {
        var cli = MatrixClientPeg.get();
        if (cli) {
            MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
        }
    },

    onClick: function() {
        if (this.props.onClick) {
            this.props.onClick(this.props.room.roomId);
        }
    },

    onMouseEnter: function() {
        this.setState( { hover : true });
        this.badgeOnMouseEnter();
    },

    onMouseLeave: function() {
        this.setState( { hover : false });
        this.badgeOnMouseLeave();
    },

    badgeOnMouseEnter: function() {
        // Only allow non-guests to access the context menu
        // and only change it if it needs to change
        if (!MatrixClientPeg.get().isGuest() && !this.state.badgeHover) {
            this.setState( { badgeHover : true } );
        }
    },

    badgeOnMouseLeave: function() {
        this.setState( { badgeHover : false } );
    },

    onBadgeClicked: function(e) {
        // Only allow none guests to access the context menu
        if (!MatrixClientPeg.get().isGuest()) {

            // If the badge is clicked, then no longer show tooltip
            if (this.props.collapsed) {
                this.setState({ hover: false });
            }

            var NotificationStateMenu = sdk.getComponent('context_menus.NotificationStateContextMenu');
            var elementRect = e.target.getBoundingClientRect();
            // The window X and Y offsets are to adjust position when zoomed in to page
            var x = elementRect.right + window.pageXOffset + 3;
            var y = (elementRect.top + (elementRect.height / 2) + window.pageYOffset) - 53;
            var self = this;
            ContextualMenu.createMenu(NotificationStateMenu, {
                menuWidth: 188,
                menuHeight: 126,
                chevronOffset: 45,
                left: x,
                top: y,
                room: this.props.room,
                onFinished: function() {
                    self.setState({ notificationTagMenu: false });
                    self.props.refreshSubList();
                }
            });
            this.setState({ notificationTagMenu: true });
        }
        // Prevent the RoomTile onClick event firing as well
        e.stopPropagation();
    },

    onAvatarClicked: function(e) {
        // Only allow none guests to access the context menu
        if (!MatrixClientPeg.get().isGuest() && !this.props.collapsed) {

            // If the badge is clicked, then no longer show tooltip
            if (this.props.collapsed) {
                this.setState({ hover: false });
            }

            var RoomTagMenu = sdk.getComponent('context_menus.RoomTagContextMenu');
            var elementRect = e.target.getBoundingClientRect();
            // The window X and Y offsets are to adjust position when zoomed in to page
            var x = elementRect.right + window.pageXOffset + 3;
            var y = (elementRect.top + (elementRect.height / 2) + window.pageYOffset) - 19;
            var self = this;
            ContextualMenu.createMenu(RoomTagMenu, {
                chevronOffset: 10,
                // XXX: fix horrid hardcoding
                menuColour: UserSettingsStore.getSyncedSettings().theme === 'dark' ? "#2d2d2d" : "#FFFFFF",
                left: x,
                top: y,
                room: this.props.room,
                onFinished: function() {
                    self.setState({ roomTagMenu: false });
                }
            });
            this.setState({ roomTagMenu: true });
            // Prevent the RoomTile onClick event firing as well
            e.stopPropagation();
        }
    },

    render: function() {
        var myUserId = MatrixClientPeg.get().credentials.userId;
        var me = this.props.room.currentState.members[myUserId];

        var notificationCount = this.props.room.getUnreadNotificationCount();
        // var highlightCount = this.props.room.getUnreadNotificationCount("highlight");

        const notifBadges = notificationCount > 0 && this._shouldShowNotifBadge();
        const mentionBadges = this.props.highlight && this._shouldShowMentionBadge();
        const badges = notifBadges || mentionBadges;

        var classes = classNames({
            'mx_RoomTile': true,
            'mx_RoomTile_selected': this.props.selected,
            'mx_RoomTile_unread': this.props.unread,
            'mx_RoomTile_unreadNotify': notifBadges,
            'mx_RoomTile_highlight': mentionBadges,
            'mx_RoomTile_invited': (me && me.membership == 'invite'),
            'mx_RoomTile_notificationTagMenu': this.state.notificationTagMenu,
            'mx_RoomTile_noBadges': !badges,
        });

        var avatarClasses = classNames({
            'mx_RoomTile_avatar': true,
        });

        var avatarContainerClasses = classNames({
            'mx_RoomTile_avatar_container': true,
            'mx_RoomTile_avatar_roomTagMenu': this.state.roomTagMenu,
        });

        var badgeClasses = classNames({
            'mx_RoomTile_badge': true,
            'mx_RoomTile_badgeButton': this.state.badgeHover || this.state.notificationTagMenu,
        });

        // XXX: We should never display raw room IDs, but sometimes the
        // room name js sdk gives is undefined (cannot repro this -- k)
        var name = this.props.room.name || this.props.room.roomId;
        name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon

        var badge;
        var badgeContent;

        if (this.state.badgeHover || this.state.notificationTagMenu) {
            badgeContent = "\u00B7\u00B7\u00B7";
        } else if (badges) {
            var limitedCount = FormattingUtils.formatCount(notificationCount);
            badgeContent = notificationCount ? limitedCount : '!';
        } else {
            badgeContent = '\u200B';
        }

        badge =
{ badgeContent }
;

        const EmojiText = sdk.getComponent('elements.EmojiText');
        var label;
        var tooltip;
        if (!this.props.collapsed) {
            var nameClasses = classNames({
                'mx_RoomTile_name': true,
                'mx_RoomTile_invite': this.props.isInvite,
                'mx_RoomTile_badgeShown': badges || this.state.badgeHover || this.state.notificationTagMenu,
            });

            if (this.props.selected) {
                let nameSelected = <EmojiText>{name}</EmojiText>;

                label =
{ nameSelected }
;
            } else {
                label = <EmojiText element="div" title={ name } className={ nameClasses }>{name}</EmojiText>;
            }
        } else if (this.state.hover) {
            var RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
            tooltip = <RoomTooltip className="mx_RoomTile_tooltip" room={this.props.room} />;
        }

        //var incomingCallBox;
        //if (this.props.incomingCall) {
        //    var IncomingCallBox = sdk.getComponent("voip.IncomingCallBox");
        //    incomingCallBox = <IncomingCallBox incomingCall={ this.props.incomingCall }/>;
        //}

        var RoomAvatar = sdk.getComponent('avatars.RoomAvatar');

        var directMessageIndicator;
        if (this._isDirectMessageRoom(this.props.room.roomId)) {
         directMessageIndicator = ;
        }

        // These props are injected by React DnD,
        // as defined by your `collect` function above:
        var isDragging = this.props.isDragging;
        var connectDragSource = this.props.connectDragSource;
        var connectDropTarget = this.props.connectDropTarget;

        let ret = (

 { /* Only native elements can be wrapped in a DnD object. */}
            <AccessibleButton className={classes} tabIndex="0" onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>

                            <RoomAvatar room={this.props.room} width={24} height={24} />
                            {directMessageIndicator}

                    { label }
                    { badge }

                {/* { incomingCallBox } */}
                { tooltip }
            </AccessibleButton>

        );

        if (connectDropTarget) ret = connectDropTarget(ret);
        if (connectDragSource) ret = connectDragSource(ret);

        return ret;
    }
});
<commit_msg>Remove dis as a dep in RoomTile<commit_after>/*
Copyright 2015, 2016 OpenMarket Ltd

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict';

var React = require('react');
var ReactDOM = require("react-dom");
var classNames = require('classnames');
var MatrixClientPeg = require('../../../MatrixClientPeg');
import DMRoomMap from '../../../utils/DMRoomMap';
var sdk = require('../../../index');
var ContextualMenu = require('../../structures/ContextualMenu');
var RoomNotifs = require('../../../RoomNotifs');
var FormattingUtils = require('../../../utils/FormattingUtils');
import AccessibleButton from '../elements/AccessibleButton';
var UserSettingsStore = require('../../../UserSettingsStore');

module.exports = React.createClass({
    displayName: 'RoomTile',

    propTypes: {
        connectDragSource: React.PropTypes.func,
        connectDropTarget: React.PropTypes.func,
        onClick: React.PropTypes.func,
        isDragging: React.PropTypes.bool,

        room: React.PropTypes.object.isRequired,
        collapsed: React.PropTypes.bool.isRequired,
        selected: React.PropTypes.bool.isRequired,
        unread: React.PropTypes.bool.isRequired,
        highlight: React.PropTypes.bool.isRequired,
        isInvite: React.PropTypes.bool.isRequired,
        incomingCall: React.PropTypes.object,
    },

    getDefaultProps: function() {
        return {
            isDragging: false,
        };
    },

    getInitialState: function() {
        return({
            hover : false,
            badgeHover : false,
            notificationTagMenu: false,
            roomTagMenu: false,
            notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId),
        });
    },

    _shouldShowNotifBadge: function() {
        const showBadgeInStates = [RoomNotifs.ALL_MESSAGES, RoomNotifs.ALL_MESSAGES_LOUD];
        return showBadgeInStates.indexOf(this.state.notifState) > -1;
    },

    _shouldShowMentionBadge: function() {
        return this.state.notifState != RoomNotifs.MUTE;
    },

    _isDirectMessageRoom: function(roomId) {
        var dmRooms = DMRoomMap.shared().getUserIdForRoomId(roomId);
        if (dmRooms) {
            return true;
        } else {
            return false;
        }
    },

    onAccountData: function(accountDataEvent) {
        if (accountDataEvent.getType() == 'm.push_rules') {
            this.setState({
                notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId),
            });
        }
    },

    componentWillMount: function() {
        MatrixClientPeg.get().on("accountData", this.onAccountData);
    },

    componentWillUnmount: function() {
        var cli = MatrixClientPeg.get();
        if (cli) {
            MatrixClientPeg.get().removeListener("accountData", this.onAccountData);
        }
    },

    onClick: function() {
        if (this.props.onClick) {
            this.props.onClick(this.props.room.roomId);
        }
    },

    onMouseEnter: function() {
        this.setState( { hover : true });
        this.badgeOnMouseEnter();
    },

    onMouseLeave: function() {
        this.setState( { hover : false });
        this.badgeOnMouseLeave();
    },

    badgeOnMouseEnter: function() {
        // Only allow non-guests to access the context menu
        // and only change it if it needs to change
        if (!MatrixClientPeg.get().isGuest() && !this.state.badgeHover) {
            this.setState( { badgeHover : true } );
        }
    },

    badgeOnMouseLeave: function() {
        this.setState( { badgeHover : false } );
    },

    onBadgeClicked: function(e) {
        // Only allow none guests to access the context menu
        if (!MatrixClientPeg.get().isGuest()) {

            // If the badge is clicked, then no longer show tooltip
            if (this.props.collapsed) {
                this.setState({ hover: false });
            }

            var NotificationStateMenu = sdk.getComponent('context_menus.NotificationStateContextMenu');
            var elementRect = e.target.getBoundingClientRect();
            // The window X and Y offsets are to adjust position when zoomed in to page
            var x = elementRect.right + window.pageXOffset + 3;
            var y = (elementRect.top + (elementRect.height / 2) + window.pageYOffset) - 53;
            var self = this;
            ContextualMenu.createMenu(NotificationStateMenu, {
                menuWidth: 188,
                menuHeight: 126,
                chevronOffset: 45,
                left: x,
                top: y,
                room: this.props.room,
                onFinished: function() {
                    self.setState({ notificationTagMenu: false });
                    self.props.refreshSubList();
                }
            });
            this.setState({ notificationTagMenu: true });
        }
        // Prevent the RoomTile onClick event firing as well
        e.stopPropagation();
    },

    onAvatarClicked: function(e) {
        // Only allow none guests to access the context menu
        if (!MatrixClientPeg.get().isGuest() && !this.props.collapsed) {

            // If the badge is clicked, then no longer show tooltip
            if (this.props.collapsed) {
                this.setState({ hover: false });
            }

            var RoomTagMenu = sdk.getComponent('context_menus.RoomTagContextMenu');
            var elementRect = e.target.getBoundingClientRect();
            // The window X and Y offsets are to adjust position when zoomed in to page
            var x = elementRect.right + window.pageXOffset + 3;
            var y = (elementRect.top + (elementRect.height / 2) + window.pageYOffset) - 19;
            var self = this;
            ContextualMenu.createMenu(RoomTagMenu, {
                chevronOffset: 10,
                // XXX: fix horrid hardcoding
                menuColour: UserSettingsStore.getSyncedSettings().theme === 'dark' ? "#2d2d2d" : "#FFFFFF",
                left: x,
                top: y,
                room: this.props.room,
                onFinished: function() {
                    self.setState({ roomTagMenu: false });
                }
            });
            this.setState({ roomTagMenu: true });
            // Prevent the RoomTile onClick event firing as well
            e.stopPropagation();
        }
    },

    render: function() {
        var myUserId = MatrixClientPeg.get().credentials.userId;
        var me = this.props.room.currentState.members[myUserId];

        var notificationCount = this.props.room.getUnreadNotificationCount();
        // var highlightCount = this.props.room.getUnreadNotificationCount("highlight");

        const notifBadges = notificationCount > 0 && this._shouldShowNotifBadge();
        const mentionBadges = this.props.highlight && this._shouldShowMentionBadge();
        const badges = notifBadges || mentionBadges;

        var classes = classNames({
            'mx_RoomTile': true,
            'mx_RoomTile_selected': this.props.selected,
            'mx_RoomTile_unread': this.props.unread,
            'mx_RoomTile_unreadNotify': notifBadges,
            'mx_RoomTile_highlight': mentionBadges,
            'mx_RoomTile_invited': (me && me.membership == 'invite'),
            'mx_RoomTile_notificationTagMenu': this.state.notificationTagMenu,
            'mx_RoomTile_noBadges': !badges,
        });

        var avatarClasses = classNames({
            'mx_RoomTile_avatar': true,
        });

        var avatarContainerClasses = classNames({
            'mx_RoomTile_avatar_container': true,
            'mx_RoomTile_avatar_roomTagMenu': this.state.roomTagMenu,
        });

        var badgeClasses = classNames({
            'mx_RoomTile_badge': true,
            'mx_RoomTile_badgeButton': this.state.badgeHover || this.state.notificationTagMenu,
        });

        // XXX: We should never display raw room IDs, but sometimes the
        // room name js sdk gives is undefined (cannot repro this -- k)
        var name = this.props.room.name || this.props.room.roomId;
        name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon

        var badge;
        var badgeContent;

        if (this.state.badgeHover || this.state.notificationTagMenu) {
            badgeContent = "\u00B7\u00B7\u00B7";
        } else if (badges) {
            var limitedCount = FormattingUtils.formatCount(notificationCount);
            badgeContent = notificationCount ? limitedCount : '!';
        } else {
            badgeContent = '\u200B';
        }

        badge =
{ badgeContent }
;

        const EmojiText = sdk.getComponent('elements.EmojiText');
        var label;
        var tooltip;
        if (!this.props.collapsed) {
            var nameClasses = classNames({
                'mx_RoomTile_name': true,
                'mx_RoomTile_invite': this.props.isInvite,
                'mx_RoomTile_badgeShown': badges || this.state.badgeHover || this.state.notificationTagMenu,
            });

            if (this.props.selected) {
                let nameSelected = <EmojiText>{name}</EmojiText>;

                label =
{ nameSelected }
;
            } else {
                label = <EmojiText element="div" title={ name } className={ nameClasses }>{name}</EmojiText>;
            }
        } else if (this.state.hover) {
            var RoomTooltip = sdk.getComponent("rooms.RoomTooltip");
            tooltip = <RoomTooltip className="mx_RoomTile_tooltip" room={this.props.room} />;
        }

        //var incomingCallBox;
        //if (this.props.incomingCall) {
        //    var IncomingCallBox = sdk.getComponent("voip.IncomingCallBox");
        //    incomingCallBox = <IncomingCallBox incomingCall={ this.props.incomingCall }/>;
        //}

        var RoomAvatar = sdk.getComponent('avatars.RoomAvatar');

        var directMessageIndicator;
        if (this._isDirectMessageRoom(this.props.room.roomId)) {
         directMessageIndicator = ;
        }

        // These props are injected by React DnD,
        // as defined by your `collect` function above:
        var isDragging = this.props.isDragging;
        var connectDragSource = this.props.connectDragSource;
        var connectDropTarget = this.props.connectDropTarget;

        let ret = (

 { /* Only native elements can be wrapped in a DnD object. */}
            <AccessibleButton className={classes} tabIndex="0" onClick={this.onClick} onMouseEnter={this.onMouseEnter} onMouseLeave={this.onMouseLeave}>

                            <RoomAvatar room={this.props.room} width={24} height={24} />
                            {directMessageIndicator}

                    { label }
                    { badge }

                {/* { incomingCallBox } */}
                { tooltip }
            </AccessibleButton>

        );

        if (connectDropTarget) ret = connectDropTarget(ret);
        if (connectDragSource) ret = connectDragSource(ret);

        return ret;
    }
});
<|endoftext|>


<commit_before>const $ = require('jquery');
const logger = require('logger');

function init() {
	logger.log('configuration.js: Initializing');

	GM_config.init({
		'id': 'FateConfig',
		'fields': {
			'rollDataTSV': {
				'label': 'Weapon Rolls Tab-Separated Values',
				'type': 'text',
				'default': 'https://docs.google.com/spreadsheets/d/e/2PACX-1vQ06pCDSdvu2nQzgHMXl22ci-6pO9rTTmvZmlKXaiBrIHVhl1X1awIaHEOagZcs4ME4X9ZMEghBP9NE/pub?gid=1131147082&single=true&output=tsv'
			},
			'armorRollTSV': {
				'label': 'Armor Rolls Tab-Separated Values',
				'type': 'text',
				'default': 'https://docs.google.com/spreadsheets/d/e/2PACX-1vQ06pCDSdvu2nQzgHMXl22ci-6pO9rTTmvZmlKXaiBrIHVhl1X1awIaHEOagZcs4ME4X9ZMEghBP9NE/pub?gid=1332329724&single=true&output=tsv'
			},
			'shaderDataTSV': {
				'label': 'Shader Data Tab-Separated Values',
				'type': 'text',
				'default': 'https://docs.google.com/spreadsheets/d/e/2PACX-1vQ06pCDSdvu2nQzgHMXl22ci-6pO9rTTmvZmlKXaiBrIHVhl1X1awIaHEOagZcs4ME4X9ZMEghBP9NE/pub?gid=1194152043&single=true&output=tsv'
      },
      'maxLightLevelTracking': {
        'label': 'Display per-slot max light level overlay',
        'type': 'checkbox',
        'default': false
      }
		},
		'events': {
			'save': function() {
				fateBus.subscribe(module, 'fate.refresh', init);
			}
		}
	});

	fateBus.publish(module, 'fate.configurationLoaded', {
		rollDataTSV: GM_config.get('rollDataTSV'),
		armorRollTSV: GM_config.get('armorRollTSV'),
    shaderDataTSV: GM_config.get('shaderDataTSV'),
    maxLightLevelTracking: GM_config.get('maxLightLevelTracking')
	});
}

function install() {
	if ($('.fate-config').length > 0) {
		return;
	}

	$('.header-links').prepend("FATE Config");
	$('.fate-config').on('click', function() {
		GM_config.open();
	});
}

fateBus.subscribe(module, 'fate.init', init);
fateBus.subscribe(module, 'fate.refresh', install);
<commit_msg>Rejigger init logic to use GM_config itself<commit_after>const $ = require('jquery');
const logger = require('logger');

function init() {
	logger.log('configuration.js: Initializing');

	GM_config.init({
		'id': 'FateConfig',
		'fields': {
			'rollDataTSV': {
				'label': 'Weapon Rolls Tab-Separated Values',
				'type': 'text',
				'default': 'https://docs.google.com/spreadsheets/d/e/2PACX-1vQ06pCDSdvu2nQzgHMXl22ci-6pO9rTTmvZmlKXaiBrIHVhl1X1awIaHEOagZcs4ME4X9ZMEghBP9NE/pub?gid=1131147082&single=true&output=tsv'
			},
			'armorRollTSV': {
				'label': 'Armor Rolls Tab-Separated Values',
				'type': 'text',
				'default': 'https://docs.google.com/spreadsheets/d/e/2PACX-1vQ06pCDSdvu2nQzgHMXl22ci-6pO9rTTmvZmlKXaiBrIHVhl1X1awIaHEOagZcs4ME4X9ZMEghBP9NE/pub?gid=1332329724&single=true&output=tsv'
			},
			'shaderDataTSV': {
				'label': 'Shader Data Tab-Separated Values',
				'type': 'text',
				'default': 'https://docs.google.com/spreadsheets/d/e/2PACX-1vQ06pCDSdvu2nQzgHMXl22ci-6pO9rTTmvZmlKXaiBrIHVhl1X1awIaHEOagZcs4ME4X9ZMEghBP9NE/pub?gid=1194152043&single=true&output=tsv'
      },
      'maxLightLevelTracking': {
        'label': 'Display per-slot max light level overlay',
        'type': 'checkbox',
        'default': false
      }
		},
		'events': {
			'save': function() {
				fateBus.subscribe(module, 'fate.refresh', init);
      },
      'init': function() {
        fateBus.publish(module, 'fate.configurationLoaded', {
          rollDataTSV: GM_config.get('rollDataTSV'),
          armorRollTSV: GM_config.get('armorRollTSV'),
          shaderDataTSV: GM_config.get('shaderDataTSV'),
          maxLightLevelTracking: GM_config.get('maxLightLevelTracking')
        });
      }
		}
	});
}

// "Why don't you use GM_config's 'init' callback above to do this?"
//
// When GM_config is built, DIM hasn't finished loading, so we would have no
// DOM navigation structure to key off of.
function install() {
	if ($('.fate-config').length > 0) {
		return;
	}

	$('.header-links').prepend("FATE Config");
	$('.fate-config').on('click', function() {
		GM_config.open();
	});
}

fateBus.subscribe(module, 'fate.init', init);
fateBus.subscribe(module, 'fate.refresh', install);
<|endoftext|>


<commit_before>import keyMirror from 'keyMirror'

const ApplicationConstants ={

  ActionTypes: keyMirror({
    LOAD_ENTRIES: null
  })
}

export default ApplicationConstants
<commit_msg>Fix module loading failure on case-sensitive systems (#43)<commit_after>import keyMirror from 'keymirror'

const ApplicationConstants ={

  ActionTypes: keyMirror({
    LOAD_ENTRIES: null
  })
}

export default ApplicationConstants
<|endoftext|>