<commit_before>/**
 * @author mrdoob / http://mrdoob.com/
 */

var OpenSimToolbar = function ( editor ) {

	var signals = editor.signals;

	var container = new UI.Panel();
	container.setId( 'toolbar' );

	var buttons = new UI.Panel();
	container.add( buttons );

	var camera = editor.camera;
    // -X
	var viewminx = new UI.Button('-X').onClick(function () {
		viewfromMinusX();
	});
	buttons.add(viewminx);
	function viewfromMinusX() {
	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.y - bbox.min.y, bbox.max.z - bbox.min.z) / 2;
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.x = bbox.min.x - distance;
	    editor.updateCamera(newpos, center);
	    editor.signals.defaultCameraApplied.dispatch(center);
    };
    // +X
	var viewx = new UI.Button('+X').onClick(function () {

	    viewfromPlusX();

	});
	buttons.add(viewx);

	function viewfromPlusX() {
	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    //var helper = new THREE.BoundingBoxHelper(modelObject, 0xff0000);
	    //helper.update();
	    // If you want a visible bounding box
	    //editor.scene.add(helper);
	    // create a sphere at new CameraPos
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.y - bbox.min.y, bbox.max.z - bbox.min.z) / 2;
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.x = bbox.max.x + distance;
	    editor.updateCamera(newpos, center);
	    editor.signals.defaultCameraApplied.dispatch(center);
    };
    // -Y
	var viewminy = new UI.Button('-Y').onClick(function () {
	    viewfromMinusY();
	});
	buttons.add(viewminy);
	function viewfromMinusY() {
	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.x - bbox.min.x, bbox.max.z - bbox.min.z) / 2;
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.y = bbox.min.y - distance;
	    editor.updateCamera(newpos, center);
	    editor.signals.defaultCameraApplied.dispatch(center);
    };
    // +Y
	var viewplusy = new UI.Button('+Y').onClick(function () {
	    viewfromPlusY();
	});
	buttons.add(viewplusy);
	function viewfromPlusY() {
	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.x - bbox.min.x, bbox.max.z - bbox.min.z) / 2;
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.y = bbox.max.y + distance;
	    editor.updateCamera(newpos, center);
	    editor.signals.defaultCameraApplied.dispatch(center);
    };
    // -Z
	var viewminz = new UI.Button('-Z').onClick(function () {
	    viewfromMinusZ();
	});
	buttons.add(viewminz);
	function viewfromMinusZ() {

	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.x - bbox.min.x, bbox.max.y - bbox.min.y)/2;
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.z = bbox.min.z - distance;
	    editor.updateCamera(newpos, center);
	    editor.signals.defaultCameraApplied.dispatch(center);
	};
    // +Z
	var viewplusz = new UI.Button('+Z').onClick(function () {
	    viewfromPlusZ();
	});
	buttons.add(viewplusz);
	function viewfromPlusZ() {

	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.x - bbox.min.x, bbox.max.y - bbox.min.y)/2;
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.z = bbox.max.z + distance;
	    editor.updateCamera(newpos, center);
	    editor.signals.defaultCameraApplied.dispatch(center);
    };

	var view_zoomin = new UI.Button('+').onClick(function () {
	    editor.viewZoom(+100);
	});
	buttons.add(view_zoomin);
	var view_zoomout = new UI.Button('-').onClick(function () {
	    editor.viewZoom(-100);
	});
	buttons.add(view_zoomout);
	var view_refit = new UI.Button('R').onClick(function () {
	    //var modelObject = editor.scene.getObjectByName('OpenSimModel');
	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    //var newpos = new THREE.Vector3().copy(camera.location);
	    var fov = camera.fov * (Math.PI / 180);
	    var objectSize = Math.max(bbox.max.x - bbox.min.x, bbox.max.y - bbox.min.y, bbox.max.z - bbox.min.z);
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    //var helper = new THREE.BoundingBoxHelper(modelObject, 0xff0000);
	    //helper.update();
	    //editor.scene.add(helper);
	    // Zoom out only if model is outside view, also zoom in if too small
	    var curDistance = center.distanceTo(camera.position);
	    editor.viewZoom(curDistance-distance);

	});
	buttons.add(view_refit);

	var snapshot = new UI.Button('Snap').onClick(function () {
            saveAsImage();
	});
	buttons.add(snapshot);

	function computeModelBbox() {
	    var modelObject = editor.scene.getObjectByName('OpenSimModel');
	    return (new THREE.Box3().setFromObject(modelObject));
	};
        function saveAsImage() {
            var canvas = document.getElementById("viewport");
            getImageData = true;
            //canvas.children[1].render();
	    var img    = canvas.children[1].toDataURL("image/jpeg");
	    saveFile(img, "opensim_snapshot.jpg");
	};
        // Support saving image to file
        var saveFile = function (strData, filename) {
            var link = document.createElement('a');
            if (typeof link.download === 'string') {
                document.body.appendChild(link); //Firefox requires the link to be in the body
                link.download = filename;
                link.href = strData;
                link.click();
                document.body.removeChild(link); //remove the link when done
            } else {
                location.replace(uri);
            }
        };

	return container;

}
<commit_msg>Repeat the fix for "Refit" button<commit_after>/**
 * @author mrdoob / http://mrdoob.com/
 */

var OpenSimToolbar = function ( editor ) {

	var signals = editor.signals;

	var container = new UI.Panel();
	container.setId( 'toolbar' );

	var buttons = new UI.Panel();
	container.add( buttons );

	var camera = editor.camera;
    // -X
	var viewminx = new UI.Button('-X').onClick(function () {
		viewfromMinusX();
	});
	buttons.add(viewminx);
	function viewfromMinusX() {
	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.y - bbox.min.y, bbox.max.z - bbox.min.z) / 2;
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.x = bbox.min.x - distance;
	    editor.updateCamera(newpos, center);
	    editor.signals.defaultCameraApplied.dispatch(center);
    };
    // +X
	var viewx = new UI.Button('+X').onClick(function () {

	    viewfromPlusX();

	});
	buttons.add(viewx);

	function viewfromPlusX() {
	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    //var helper = new THREE.BoundingBoxHelper(modelObject, 0xff0000);
	    //helper.update();
	    // If you want a visible bounding box
	    //editor.scene.add(helper);
	    // create a sphere at new CameraPos
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.y - bbox.min.y, bbox.max.z - bbox.min.z) / 2;
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.x = bbox.max.x + distance;
	    editor.updateCamera(newpos, center);
	    editor.signals.defaultCameraApplied.dispatch(center);
    };
    // -Y
	var viewminy = new UI.Button('-Y').onClick(function () {
	    viewfromMinusY();
	});
	buttons.add(viewminy);
	function viewfromMinusY() {
	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.x - bbox.min.x, bbox.max.z - bbox.min.z) / 2;
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.y = bbox.min.y - distance;
	    editor.updateCamera(newpos, center);
	    editor.signals.defaultCameraApplied.dispatch(center);
    };
    // +Y
	var viewplusy = new UI.Button('+Y').onClick(function () {
	    viewfromPlusY();
	});
	buttons.add(viewplusy);
	function viewfromPlusY() {
	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.x - bbox.min.x, bbox.max.z - bbox.min.z) / 2;
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.y = bbox.max.y + distance;
	    editor.updateCamera(newpos, center);
	    editor.signals.defaultCameraApplied.dispatch(center);
    };
    // -Z
	var viewminz = new UI.Button('-Z').onClick(function () {
	    viewfromMinusZ();
	});
	buttons.add(viewminz);
	function viewfromMinusZ() {

	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.x - bbox.min.x, bbox.max.y - bbox.min.y)/2;
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.z = bbox.min.z - distance;
	    editor.updateCamera(newpos, center);
	    editor.signals.defaultCameraApplied.dispatch(center);
	};
    // +Z
	var viewplusz = new UI.Button('+Z').onClick(function () {
	    viewfromPlusZ();
	});
	buttons.add(viewplusz);
	function viewfromPlusZ() {

	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    var newpos = new THREE.Vector3().copy(center);
	    var fov = editor.camera.fov * (Math.PI / 180);
	    // Calculate the camera distance
	    var objectSize = Math.max(bbox.max.x - bbox.min.x, bbox.max.y - bbox.min.y)/2;
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    newpos.z = bbox.max.z + distance;
	    editor.updateCamera(newpos, center);
	    editor.signals.defaultCameraApplied.dispatch(center);
    };

	var view_zoomin = new UI.Button('+').onClick(function () {
	    editor.viewZoom(+100);
	});
	buttons.add(view_zoomin);
	var view_zoomout = new UI.Button('-').onClick(function () {
	    editor.viewZoom(-100);
	});
	buttons.add(view_zoomout);
	var view_refit = new UI.Button('R').onClick(function () {
	    //var modelObject = editor.scene.getObjectByName('OpenSimModel');
	    var bbox = computeModelBbox();
	    var center = new THREE.Vector3();
	    bbox.center(center);
	    //var newpos = new THREE.Vector3().copy(camera.location);
	    var fov = camera.fov * (Math.PI / 180);
	    var objectSize = Math.max(bbox.max.x - bbox.min.x, bbox.max.y - bbox.min.y, bbox.max.z - bbox.min.z);
	    var distance = Math.abs(objectSize / Math.sin(fov / 2));
	    //var helper = new THREE.BoundingBoxHelper(modelObject, 0xff0000);
	    //helper.update();
	    //editor.scene.add(helper);
	    // Zoom out only if model is outside view, also zoom in if too small
	    var curDistance = center.distanceTo(camera.position);
	    editor.viewZoom(curDistance - distance);
	    editor.signals.defaultCameraApplied.dispatch(center);

	});
	buttons.add(view_refit);

	var snapshot = new UI.Button('Snap').onClick(function () {
            saveAsImage();
	});
	buttons.add(snapshot);

	function computeModelBbox() {
	    var modelObject = editor.scene.getObjectByName('OpenSimModel');
	    return (new THREE.Box3().setFromObject(modelObject));
	};
        function saveAsImage() {
            var canvas = document.getElementById("viewport");
            getImageData = true;
            //canvas.children[1].render();
	    var img    = canvas.children[1].toDataURL("image/jpeg");
	    saveFile(img, "opensim_snapshot.jpg");
	};
        // Support saving image to file
        var saveFile = function (strData, filename) {
            var link = document.createElement('a');
            if (typeof link.download === 'string') {
                document.body.appendChild(link); //Firefox requires the link to be in the body
                link.download = filename;
                link.href = strData;
                link.click();
                document.body.removeChild(link); //remove the link when done
            } else {
                location.replace(uri);
            }
        };

	return container;

}
<|endoftext|>


<commit_before>import Ember from 'ember';

const { getOwner } = Ember;

var templateOptionsKey = null;

function clearIfHasProperty (obj, propertyName) {
  if (obj && Object.hasOwnProperty.call(obj, propertyName)) {
    obj[propertyName] = undefined;
  }
}

function clear (context, owner, name) {
  var environment = owner.lookup('service:-glimmer-environment');
  if (environment) { // Glimmer2
    environment._definitionCache && environment._definitionCache.store && environment._definitionCache.store.clear();
  }
  if (templateOptionsKey) { // Ember v3.1.1
    var templateOptions = owner.lookup(templateOptionsKey);
    var compileTimeLookup = templateOptions.resolver;
    var runtimeResolver = compileTimeLookup.resolver;
    runtimeResolver.componentDefinitionCache.clear();
  }
  if (owner.__container__) {
    clearIfHasProperty(owner.__container__.cache, name);
    clearIfHasProperty(owner.__container__.factoryCache, name);
    clearIfHasProperty(owner.__container__.factoryManagerCache, name);
    clearIfHasProperty(owner.__registry__._resolveCache, name);
    clearIfHasProperty(owner.__registry__._failCache, name);

    clearIfHasProperty(owner.base.__container__.cache, name);
    clearIfHasProperty(owner.base.__container__.factoryCache, name);
    clearIfHasProperty(owner.base.__container__.factoryManagerCache, name);
    clearIfHasProperty(owner.base.__registry__._resolveCache, name);
    clearIfHasProperty(owner.base.__registry__._failCache, name);
  } else {
    clearIfHasProperty(context.container.cache, name);
    clearIfHasProperty(context.container.factoryCache, name);
    clearIfHasProperty(context.container.factoryManagerCache, name);
    clearIfHasProperty(context.container._registry._resolveCache, name);
    clearIfHasProperty(context.container._registry._failCache, name);
    // NOTE: the app's __container__ is the same as context.container. Not needed:
    // clearIfHasProperty(window.Dummy.__container__.cache, name);
    // clearIfHasProperty(window.Dummy.__container__.factoryCache, name);
    // NOTE: the app's registry, is different than container._registry. We may need this
    // clearIfHasProperty(window.Dummy.registry._resolveCache, name);
    // clearIfHasProperty(window.Dummy.registry._failCache, name);
  }
}

var regex = new RegExp('template-options:main-(.*)');
export function captureTemplateOptions(parsedName) {
  if (templateOptionsKey) {
    return;
  }
  var name = parsedName.fullName || '';
  var matched = name.match(regex);
  if (matched && matched.length > 0) {
    templateOptionsKey = name;
  }
}

export default function clearContainerCache(context, componentName) {
  const componentFullName = `component:${componentName}`;
  const templateFullName = `template:components/${componentName}`;
  const owner = getOwner(context);

  clear(context, owner, componentFullName);
  clear(context, owner, templateFullName);
}
<commit_msg>Break private resolver template cache for ember v3.2<commit_after>import Ember from 'ember';

const { getOwner } = Ember;

var templateOptionsKey = null;
var templateCompilerKey = null;

function clearIfHasProperty (obj, propertyName) {
  if (obj && Object.hasOwnProperty.call(obj, propertyName)) {
    obj[propertyName] = undefined;
  }
}

function clear (context, owner, name) {
  var environment = owner.lookup('service:-glimmer-environment');
  if (environment) { // Glimmer2
    environment._definitionCache && environment._definitionCache.store && environment._definitionCache.store.clear();
  }
  if (templateOptionsKey) { // Ember v3.1.1
    var templateOptions = owner.lookup(templateOptionsKey);
    var optionsTimeLookup = templateOptions.resolver;
    var optionsRuntimeResolver = optionsTimeLookup.resolver;
    optionsRuntimeResolver.componentDefinitionCache.clear();
  }
  if (templateCompilerKey) { // Ember v3.2
    var templateCompiler = owner.lookup(templateCompilerKey);
    var compileTimeLookup = templateCompiler.resolver;
    var compileRuntimeResolver = compileTimeLookup.resolver;
    compileRuntimeResolver.componentDefinitionCache.clear();
  }
  if (owner.__container__) {
    clearIfHasProperty(owner.__container__.cache, name);
    clearIfHasProperty(owner.__container__.factoryCache, name);
    clearIfHasProperty(owner.__container__.factoryManagerCache, name);
    clearIfHasProperty(owner.__registry__._resolveCache, name);
    clearIfHasProperty(owner.__registry__._failCache, name);

    clearIfHasProperty(owner.base.__container__.cache, name);
    clearIfHasProperty(owner.base.__container__.factoryCache, name);
    clearIfHasProperty(owner.base.__container__.factoryManagerCache, name);
    clearIfHasProperty(owner.base.__registry__._resolveCache, name);
    clearIfHasProperty(owner.base.__registry__._failCache, name);
  } else {
    clearIfHasProperty(context.container.cache, name);
    clearIfHasProperty(context.container.factoryCache, name);
    clearIfHasProperty(context.container.factoryManagerCache, name);
    clearIfHasProperty(context.container._registry._resolveCache, name);
    clearIfHasProperty(context.container._registry._failCache, name);
    // NOTE: the app's __container__ is the same as context.container. Not needed:
    // clearIfHasProperty(window.Dummy.__container__.cache, name);
    // clearIfHasProperty(window.Dummy.__container__.factoryCache, name);
    // NOTE: the app's registry, is different than container._registry. We may need this
    // clearIfHasProperty(window.Dummy.registry._resolveCache, name);
    // clearIfHasProperty(window.Dummy.registry._failCache, name);
  }
}

var optionsRegex = new RegExp('template-options:main-(.*)');
var compilerRegex = new RegExp('template-compiler:main-(.*)');
export function captureTemplateOptions(parsedName) {
  if (templateCompilerKey || templateOptionsKey) {
    return;
  }
  var name = parsedName.fullName || '';
  var optionsMatch = name.match(optionsRegex);
  if (optionsMatch && optionsMatch.length > 0) {
    templateOptionsKey = name;
  }
  var compilerMatch = name.match(compilerRegex);
  if (compilerMatch && compilerMatch.length > 0) {
    templateCompilerKey = name;
  }
}

export default function clearContainerCache(context, componentName) {
  const componentFullName = `component:${componentName}`;
  const templateFullName = `template:components/${componentName}`;
  const owner = getOwner(context);

  clear(context, owner, componentFullName);
  clear(context, owner, templateFullName);
}
<|endoftext|>


<commit_before>import React from 'react'
import { connect } from 'react-redux'
import { getCurrentCommunity, getFollowedTags } from '../models/community'
import { connectedListProps } from '../util/caching'
import { closeModal } from '../actions'
import { fetchTags, followTag } from '../actions/tags'
import Icon from '../components/Icon'
import A from '../components/A'
import { Modal } from '../components/Modal'
import { isEmpty, some } from 'lodash'
import { find } from 'lodash/fp'
import { same } from '../models'
import { denormalizedCurrentUser, newestMembership } from '../models/currentUser'
import { humanDate } from '../util/text'
import { tagUrl, userUrl } from '../routes'
import { trackEvent, FOLLOWED_TOPIC } from '../util/analytics'
const { array, bool, func, number, object, string } = React.PropTypes

const subject = 'community'

@connect((state, props) => {
  const community = props.community || getCurrentCommunity(state) ||
    newestMembership(denormalizedCurrentUser(state)).community
  return {
    community,
    ...connectedListProps(state, {subject, id: community.slug}, 'tags'),
    followedTags: getFollowedTags(community, state)
  }
})
export default class BrowseTopicsModal extends React.Component {
  static propTypes = {
    dispatch: func,
    tags: array,
    community: object,
    pending: bool,
    total: number,
    followedTags: array,
    onCancel: func,
    onboarding: bool,
    nextUrl: string
  }

  componentDidMount () {
    const { dispatch, community } = this.props
    dispatch(fetchTags({subject, limit: 10, id: community.slug, sort: 'popularity'}))
  }

  render () {
    const {
      tags, community, dispatch, pending, total, followedTags, onCancel,
      onboarding, nextUrl
    } = this.props
    const offset = tags.length
    const title = onboarding
      ? 'What are you interested in?'
      : (total ? `Browse all ${total} topics` : 'Browse all topics')

    const subtitle = onboarding
      ? `Follow the topics you're interested in to join the conversation with other members.`
      : null

    const loadMore = !pending && offset < total &&
      (() => dispatch(fetchTags({subject, id: community.slug, offset, sort: 'popularity'})))

    return <Modal {...{title, subtitle}} id='browse-all-topics' onCancel={onCancel}
      standalone={onboarding}>
      {isEmpty(tags) ?
Loading...

        :

            {tags.map(tag => {
              const followed = some(followedTags, same('name', tag))
              return <TagRow tag={tag} community={community} key={tag.id}
                followed={followed} onboarding={onboarding}/>
            })}
            {loadMore &&

                Showing {tags.length} of {total}.
                Show more

}

}
      {onboarding &&

        Next

}
    </Modal>
  }
}

const TagRow = ({ tag, community, followed, onboarding }, { isMobile, dispatch }) => {
  const { id, name, post_type } = tag
  const { slug } = community
  const membership = find(m => same('id', community), tag.memberships)
  const { description, follower_count, owner, created_at } = membership
  const close = () => dispatch(closeModal())
  const controls = <TagRowControls {...{follower_count, slug, followed, name, onboarding}}/>

  return

    {!isMobile && controls}
    # {name}
    {(description || post_type) &&

      {description || post_type}

}
    {!isEmpty(owner) &&
      created by {owner.name} {humanDate(created_at)}
    }
    {isMobile && controls}

}
TagRow.contextTypes = {isMobile: bool, dispatch: func}

const TagRowControls = ({ followed, follower_count, slug, name, onboarding }, { dispatch }) => {
  const follow = () => {
    if (!followed) {
      trackEvent(FOLLOWED_TOPIC, {
        tag: name,
        context: onboarding ? 'onboarding' : 'modal'
      })
    }
    return dispatch(followTag(slug, name))
  }
  return

      <Icon name='Users'/>
      {follower_count}

    {followed
      ?
          Unfollow ×

      :
          + Follow
        }

}
TagRowControls.contextTypes = {dispatch: func}
<commit_msg>add missing space to browse topics modal<commit_after>import React from 'react'
import { connect } from 'react-redux'
import { getCurrentCommunity, getFollowedTags } from '../models/community'
import { connectedListProps } from '../util/caching'
import { closeModal } from '../actions'
import { fetchTags, followTag } from '../actions/tags'
import Icon from '../components/Icon'
import A from '../components/A'
import { Modal } from '../components/Modal'
import { isEmpty, some } from 'lodash'
import { find } from 'lodash/fp'
import { same } from '../models'
import { denormalizedCurrentUser, newestMembership } from '../models/currentUser'
import { humanDate } from '../util/text'
import { tagUrl, userUrl } from '../routes'
import { trackEvent, FOLLOWED_TOPIC } from '../util/analytics'
const { array, bool, func, number, object, string } = React.PropTypes

const subject = 'community'

@connect((state, props) => {
  const community = props.community || getCurrentCommunity(state) ||
    newestMembership(denormalizedCurrentUser(state)).community
  return {
    community,
    ...connectedListProps(state, {subject, id: community.slug}, 'tags'),
    followedTags: getFollowedTags(community, state)
  }
})
export default class BrowseTopicsModal extends React.Component {
  static propTypes = {
    dispatch: func,
    tags: array,
    community: object,
    pending: bool,
    total: number,
    followedTags: array,
    onCancel: func,
    onboarding: bool,
    nextUrl: string
  }

  componentDidMount () {
    const { dispatch, community } = this.props
    dispatch(fetchTags({subject, limit: 10, id: community.slug, sort: 'popularity'}))
  }

  render () {
    const {
      tags, community, dispatch, pending, total, followedTags, onCancel,
      onboarding, nextUrl
    } = this.props
    const offset = tags.length
    const title = onboarding
      ? 'What are you interested in?'
      : (total ? `Browse all ${total} topics` : 'Browse all topics')

    const subtitle = onboarding
      ? `Follow the topics you're interested in to join the conversation with other members.`
      : null

    const loadMore = !pending && offset < total &&
      (() => dispatch(fetchTags({subject, id: community.slug, offset, sort: 'popularity'})))

    return <Modal {...{title, subtitle}} id='browse-all-topics' onCancel={onCancel}
      standalone={onboarding}>
      {isEmpty(tags) ?
Loading...

        :

            {tags.map(tag => {
              const followed = some(followedTags, same('name', tag))
              return <TagRow tag={tag} community={community} key={tag.id}
                followed={followed} onboarding={onboarding} />
            })}
            {loadMore &&

                Showing {tags.length} of {total}.
                Show more

}

}
      {onboarding &&

        Next

}
    </Modal>
  }
}

const TagRow = ({ tag, community, followed, onboarding }, { isMobile, dispatch }) => {
  const { id, name, post_type } = tag
  const { slug } = community
  const membership = find(m => same('id', community), tag.memberships)
  const { description, follower_count, owner, created_at } = membership
  const close = () => dispatch(closeModal())
  const controls = <TagRowControls {...{follower_count, slug, followed, name, onboarding}} />

  return

    {!isMobile && controls}
    # {name}
    {(description || post_type) &&

      {description || post_type}

}
    {!isEmpty(owner) &&
      created by {owner.name} {humanDate(created_at)}
    }
    {isMobile && controls}

}
TagRow.contextTypes = {isMobile: bool, dispatch: func}

const TagRowControls = ({ followed, follower_count, slug, name, onboarding }, { dispatch }) => {
  const follow = () => {
    if (!followed) {
      trackEvent(FOLLOWED_TOPIC, {
        tag: name,
        context: onboarding ? 'onboarding' : 'modal'
      })
    }
    return dispatch(followTag(slug, name))
  }
  return

      <Icon name='Users' />
      {follower_count}

    {followed
      ?
          Unfollow ×

      :
          + Follow
        }

}
TagRowControls.contextTypes = {dispatch: func}
<|endoftext|>


<commit_before>import styles  from '../Style/style.js'
import Helper from '../Lib/HomeMapHelperFns.js'
import React, { Component } from 'react'
import MapView from 'react-native-maps'
import { View, StyleSheet, TextInput, Alert, Image, Text, ActivityIndicator, TouchableHighlight } from 'react-native'
import CreateLocation from './CreateLocation'
import ProfileView from './ProfileView'
import LocationListView from './LocationListView'
import LocationMarker from './LocationMarker'
import LocationMarkerPaid from './LocationMarkerPaid'
import LocationMarkerCallout from './LocationMarkerCallout'
import LocationMarkerPaidCallout from './LocationMarkerPaidCallout'
import ReturnToUser from './ReturnToUser'
import Button from 'apsl-react-native-button'

export default class HomeMap extends Component {
  constructor(props) {
    super(props)
    this.state = {
      currentUser: this.props.currentUser,
      currentLocation: {
        latitude: 30.2689941,
        longitude: -97.7405441,
        latitudeDelta: 0.1,
        longitudeDelta: 0.1
      },
      userLocation: {},
      currentTime: '',
      nearbyLocations: [],
      nearbyPaidLocations: [],
      searchText: 'Search for spots...',
      lastPosition: {},
      address: '',
      addButtonPress: false,
      redoButtonPress: false
    }

    this.addLocation = this.addLocation.bind(this)
    this.onRegionChange = this.onRegionChange.bind(this)
    this.returnToUser = this.returnToUser.bind(this)
    this.getPinsForCoords = this.getPinsForCoords.bind(this)
  }

  componentDidMount () {
    // if we don't have a user fetch the user belonging to current access token
    if (!this.state.currentUser._id) {
      this.getUserInfo()
    }
    // grab users location and nearby pins
    this.setUserLocation()

    // Probably not needed for now
    // this.watchID = navigator.geolocation.watchPosition((position) => {
    //   let lastPosition = JSON.stringify(position);
    //   this.setState({lastPosition});
    // })
    // console.log('watchID: ',this.watchID)
  }

  searchLocationSubmit = Helper.searchLocationSubmit

  // grabs user on mounting if component doesn't have one
  getUserInfo = Helper.getUserInfo

  // finds users location and grabs nearby pins
  setUserLocation = Helper.setUserLocation

  getPinsForCoords = Helper.getPinsForCoords

  getPaidPinsForCoords = Helper.getPaidPinsForCoords

  getAddressByCoords = Helper.getAddressByCoords

  //adds a pins to the map if the user opens the create location form
  addLocation = Helper.addLocation

  //Removes the pin from the map if the user does not save the new location
  cancelLocationAdd = Helper.cancelLocationAdd

  onRegionChange = Helper.onRegionChange

  returnToUser = Helper.returnToUser

  createLocationNav = Helper.createLocationNav

  createProfileNav = Helper.createProfileNav

  setAddButtonStyle (style) {
    this.setState({
      addButtonPress: style
    })
  }

  setRedoButtonStyle (style) {
    this.setState({
      redoButtonPress: style
    })
  }

  render () {
    let addButtonStyle = this.state.addButtonPress ? styles.addLocationButtonPress : styles.addLocationButton
    let redoSearchButton = this.state.redoButtonPress ? styles.redoSearchButtonPress : styles.redoSearchButton

    return (
      <View style={styles.homeContainer}>
        <View style={styles.searchBarContainer}>
          <TextInput
            returnKeyType="search"
            style={styles.searchBar}
            onChangeText={(text) => this.setState({text})}
            placeholder={this.state.searchText}
            onSubmitEditing={(event) => this.searchLocationSubmit(event)}
            clearButtonMode={'while-editing'}
          />
        </View>

        <TouchableHighlight
          onPress={() => {
            this.createProfileNav()
          }}
        >
          <Image
            style={styles.profileViewImageButton}
            source={{uri: this.state.currentUser.photo}}
          />
        </TouchableHighlight>

        <ReturnToUser backToUser={this.returnToUser} />

        <View style={styles.homeMapContainer}>
          <MapView
            style={styles.homeMapView}
            region={this.state.currentLocation}
            onRegionChange={this.onRegionChange}
            showsUserLocation={true}
            ref="map"
          >
            {this.state.nearbyLocations.map((marker, key) => (
              <MapView.Marker
                key={marker.id}
                id={marker.id}
                coordinate={marker.coordinate}
                title={marker.title}
                description={marker.description}
                onPress={(evt) => console.log('pressed ', evt.nativeEvent)}
                centerOffset={{x: 0, y: -20}}
              >
                <LocationMarker {...marker} />
                <MapView.Callout style={styles.locationMarkerCallout}>
                  <LocationMarkerCallout
                    {...marker}
                    navigator={this.props.navigator}
                    currentUser={this.state.currentUser}
                    userToken={this.props.userToken}
                  />
                </MapView.Callout>
              </MapView.Marker>
            ))}

            {this.state.nearbyPaidLocations.map((marker, key) => (
              <MapView.Marker
                key={marker.id}
                id={marker.id}
                coordinate={marker.coordinate}
                title={marker.title}
                description={marker.description}
                price={marker.price}
                onPress={(evt) => console.log('pressed ', evt.nativeEvent)}
                centerOffset={{x: 0, y: -20}}

              >
                <LocationMarkerPaid {...marker} />
                <MapView.Callout style={styles.locationMarkerCallout}>
                  <LocationMarkerPaidCallout  {...marker} navigator={this.props.navigator} />
                </MapView.Callout>
              </MapView.Marker>
            ))}
          </MapView>

          <View style = {styles.mapCenterMarkerView}>
            <Image
              style={styles.mapCenterMarker}
              source={require('../Public/centerMap.png')}
            />
          </View>
        </View>
          <LocationListView
            nearbyLocations={this.state.nearbyLocations}
            navigator={this.props.navigator}
          />
          <Button
            style={redoSearchButton}
            textStyle={styles.reviewButtonText}
            onPress={() => (
              this.getPinsForCoords(this.state.currentLocation.longitude, this.state.currentLocation.latitude)
            )}
            onPressIn={() => this.setRedoButtonStyle(!this.state.redoButtonPress)}
            onPressOut={() => this.setRedoButtonStyle(!this.state.redoButtonPress)}
            activeOpacity={1}
          >
            Redo Search
          </Button>

          <Button
            onPress={() => {
              this.createLocationNav()
            }}
            onPressIn={() => this.setAddButtonStyle(!this.state.addButtonPress)}
            onPressOut={() => this.setAddButtonStyle(!this.state.addButtonPress)}
            style={addButtonStyle}
            textStyle={styles.addLocationButtonText}
            activeOpacity={1}
          >
            +
          </Button>
      </View>
    )
  }
}<commit_msg>minor stuff<commit_after>import styles  from '../Style/style.js'
import Helper from '../Lib/HomeMapHelperFns.js'
import React, { Component } from 'react'
import MapView from 'react-native-maps'
import { View, StyleSheet, TextInput, Alert, Image, Text, ActivityIndicator,
 TouchableHighlight, TouchableWithoutFeedback, Keyboard } from 'react-native'
import CreateLocation from './CreateLocation'
import ProfileView from './ProfileView'
import LocationListView from './LocationListView'
import LocationMarker from './LocationMarker'
import LocationMarkerPaid from './LocationMarkerPaid'
import LocationMarkerCallout from './LocationMarkerCallout'
import LocationMarkerPaidCallout from './LocationMarkerPaidCallout'
import ReturnToUser from './ReturnToUser'
import Button from 'apsl-react-native-button'

export default class HomeMap extends Component {
  constructor(props) {
    super(props)
    this.state = {
      currentUser: this.props.currentUser,
      currentLocation: {
        latitude: 30.2689941,
        longitude: -97.7405441,
        latitudeDelta: 0.1,
        longitudeDelta: 0.1
      },
      userLocation: {},
      currentTime: '',
      nearbyLocations: [],
      nearbyPaidLocations: [],
      searchText: 'Search for spots...',
      lastPosition: {},
      address: '',
      addButtonPress: false,
      redoButtonPress: false
    }

    this.addLocation = this.addLocation.bind(this)
    this.onRegionChange = this.onRegionChange.bind(this)
    this.returnToUser = this.returnToUser.bind(this)
    this.getPinsForCoords = this.getPinsForCoords.bind(this)
  }

  componentDidMount () {
    // if we don't have a user fetch the user belonging to current access token
    if (!this.state.currentUser._id) {
      this.getUserInfo()
    }
    // grab users location and nearby pins
    this.setUserLocation()

    // Probably not needed for now
    // this.watchID = navigator.geolocation.watchPosition((position) => {
    //   let lastPosition = JSON.stringify(position);
    //   this.setState({lastPosition});
    // })
    // console.log('watchID: ',this.watchID)
  }

  searchLocationSubmit = Helper.searchLocationSubmit

  // grabs user on mounting if component doesn't have one
  getUserInfo = Helper.getUserInfo

  // finds users location and grabs nearby pins
  setUserLocation = Helper.setUserLocation

  getPinsForCoords = Helper.getPinsForCoords

  getPaidPinsForCoords = Helper.getPaidPinsForCoords

  getAddressByCoords = Helper.getAddressByCoords

  //adds a pins to the map if the user opens the create location form
  addLocation = Helper.addLocation

  //Removes the pin from the map if the user does not save the new location
  cancelLocationAdd = Helper.cancelLocationAdd

  onRegionChange = Helper.onRegionChange

  returnToUser = Helper.returnToUser

  createLocationNav = Helper.createLocationNav

  createProfileNav = Helper.createProfileNav

  setAddButtonStyle (style) {
    this.setState({
      addButtonPress: style
    })
  }

  setRedoButtonStyle (style) {
    this.setState({
      redoButtonPress: style
    })
  }

  render () {
    let addButtonStyle = this.state.addButtonPress ? styles.addLocationButtonPress : styles.addLocationButton
    let redoSearchButton = this.state.redoButtonPress ? styles.redoSearchButtonPress : styles.redoSearchButton

    return (
      <View style={styles.homeContainer}>
        <View style={styles.searchBarContainer}>
          <TextInput
            returnKeyType="search"
            style={styles.searchBar}
            onChangeText={(text) => this.setState({text})}
            placeholder={this.state.searchText}
            onSubmitEditing={(event) => this.searchLocationSubmit(event)}
            clearButtonMode={'while-editing'}
          />
        </View>

        <TouchableHighlight
          onPress={() => {
            this.createProfileNav()
          }}
        >
          <Image
            style={styles.profileViewImageButton}
            source={{uri: this.state.currentUser.photo}}
          />
        </TouchableHighlight>

        <ReturnToUser backToUser={this.returnToUser} />

        <View style={styles.homeMapContainer}>
          <MapView
            style={styles.homeMapView}
            region={this.state.currentLocation}
            onRegionChange={this.onRegionChange}
            showsUserLocation={true}
            ref="map"
          >
            {this.state.nearbyLocations.map((marker, key) => (
              <MapView.Marker
                key={marker.id}
                id={marker.id}
                coordinate={marker.coordinate}
                title={marker.title}
                description={marker.description}
                onPress={(evt) => console.log('pressed ', evt.nativeEvent)}
                centerOffset={{x: 0, y: -20}}
              >
                <LocationMarker {...marker} />
                <MapView.Callout style={styles.locationMarkerCallout}>
                  <LocationMarkerCallout
                    {...marker}
                    navigator={this.props.navigator}
                    currentUser={this.state.currentUser}
                    userToken={this.props.userToken}
                  />
                </MapView.Callout>
              </MapView.Marker>
            ))}

            {this.state.nearbyPaidLocations.map((marker, key) => (
              <MapView.Marker
                key={marker.id}
                id={marker.id}
                coordinate={marker.coordinate}
                title={marker.title}
                description={marker.description}
                price={marker.price}
                onPress={(evt) => console.log('pressed ', evt.nativeEvent)}
                centerOffset={{x: 0, y: -20}}

              >
                <LocationMarkerPaid {...marker} />
                <MapView.Callout style={styles.locationMarkerCallout}>
                  <LocationMarkerPaidCallout  {...marker} navigator={this.props.navigator} />
                </MapView.Callout>
              </MapView.Marker>
            ))}
          </MapView>

          <View style = {styles.mapCenterMarkerView}>
            <Image
              style={styles.mapCenterMarker}
              source={require('../Public/centerMap.png')}
            />
          </View>
        </View>
          <LocationListView
            nearbyLocations={this.state.nearbyLocations}
            navigator={this.props.navigator}
          />
          <Button
            style={redoSearchButton}
            textStyle={styles.reviewButtonText}
            onPress={() => (
              this.getPinsForCoords(this.state.currentLocation.longitude, this.state.currentLocation.latitude)
            )}
            onPressIn={() => this.setRedoButtonStyle(!this.state.redoButtonPress)}
            onPressOut={() => this.setRedoButtonStyle(!this.state.redoButtonPress)}
            activeOpacity={1}
          >
            Redo Search
          </Button>

          <Button
            onPress={() => {
              this.createLocationNav()
            }}
            onPressIn={() => this.setAddButtonStyle(!this.state.addButtonPress)}
            onPressOut={() => this.setAddButtonStyle(!this.state.addButtonPress)}
            style={addButtonStyle}
            textStyle={styles.addLocationButtonText}
            activeOpacity={1}
          >
            +
          </Button>
      </View>
    )
  }
}<|endoftext|>


<commit_before>function toArray (obj) {
  return Array.prototype.slice.apply(obj);
}

function $ (selector, parent) {
  parent = parent || document;
  return parent.querySelector(selector);
}

function $$ (selector, parent) {
  parent = parent || document;
  return toArray(parent.querySelectorAll(selector));
}

function resizeHandler (e) {
  $$('.size-to-window').forEach(function (el) {
    el.style.width = window.innerWidth + 'px';
    el.style.height = window.innerHeight + 'px';
  });
}

window.addEventListener('resize', resizeHandler);
resizeHandler();

// Adding a class so we can disable certain :hover styles on touch.
// NOTE: Not using classList for IE compatibility.
document.body.className += 'ontouchstart' in window ? ' has-touch' : ' lacks-touch';

// And a class for WebVR support.
var isVr = 'getVRDevices' in navigator;
document.body.className += isVr ? ' vr' : ' novr';

// menu tabs
function Tabs() {
  var menuTabs = document.body.querySelectorAll('.menu--tabs');
  this.activeSection = null;
  this.activeTab = null;

  // hide content panels
  var menuContent = Array.from(document.body.querySelectorAll('.menu--content'));
  menuContent.forEach(function(content) {
    content.style.display = 'none';
  })

  for (var i = 0; i < menuTabs.length; i++) {
    var menuTab = menuTabs[i];
    var tabs = Array.from(menuTab.querySelectorAll('li'));

    tabs.forEach(function(tab) {
      var link = tab.querySelector('a[href]');

      link.addEventListener('click', function(e) {
        e.preventDefault();
        var id = link.getAttribute('href');
        var section = document.querySelector(id);
        if (!section) { return; }
        if (this.activeSection && this.activeTab) {
          this.activeSection.style.display = 'none';
          this.activeSection = null;
          this.activeTab.classList.remove('active');
          this.activeTab = null;
        }
        this.activeTab = tab;
        this.activeTab.classList.add('active');
        section.style.display = 'block';
        this.activeSection = section;
      }.bind(this))
    })
  }
}

Tabs();

// select tab based on mobile or desktop visitor.
var clickEl = null;
if (isMobile()) {
  clickEl = document.querySelector('a[href="#mobile"]');
} else {
  clickEl = document.querySelector('a[href="#rift"]');
}

clickEl.dispatchEvent(new MouseEvent('click', {
  'view': window,
  'bubbles': true,
  'cancelable': true
}));

// ismobile
function isMobile() {
  var check = false;
  (function (a) {
    if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) {
      check = true;
    }
  })(navigator.userAgent || navigator.vendor || window.opera);
  return check;
};

if (isMobile()) {
  document.body.className += ' isMobile';
}

function initGoogleAnalytics (id) {
  (function(c, v, a, n) {
    c.GoogleAnalyticsObject = n;

    c[n] = c[n] || function() {
      (c[n].q = c[n].q || []).push(arguments);
    }, c[n].l = 1 * new Date();

    var s = v.createElement('script');
    s.async = true;
    s.src = a;

    var m = v.getElementsByTagName('script')[0];
    m.parentNode.insertBefore(s, m);
  })(window, document, 'https://www.google-analytics.com/analytics.js', 'ga');
  ga('create', id, 'auto');
  ga('send', 'pageview');
}

initGoogleAnalytics('UA-24056643-3');
<commit_msg>remove unused resizeHandler<commit_after>function toArray (obj) {
  return Array.prototype.slice.apply(obj);
}

function $ (selector, parent) {
  parent = parent || document;
  return parent.querySelector(selector);
}

function $$ (selector, parent) {
  parent = parent || document;
  return toArray(parent.querySelectorAll(selector));
}

// Adding a class so we can disable certain :hover styles on touch.
// NOTE: Not using classList for IE compatibility.
document.body.className += 'ontouchstart' in window ? ' has-touch' : ' lacks-touch';

// And a class for WebVR support.
var isVr = 'getVRDevices' in navigator;
document.body.className += isVr ? ' vr' : ' novr';

// menu tabs
function Tabs() {
  var menuTabs = document.body.querySelectorAll('.menu--tabs');
  this.activeSection = null;
  this.activeTab = null;

  // hide content panels
  var menuContent = Array.from(document.body.querySelectorAll('.menu--content'));
  menuContent.forEach(function(content) {
    content.style.display = 'none';
  })

  for (var i = 0; i < menuTabs.length; i++) {
    var menuTab = menuTabs[i];
    var tabs = Array.from(menuTab.querySelectorAll('li'));

    tabs.forEach(function(tab) {
      var link = tab.querySelector('a[href]');

      link.addEventListener('click', function(e) {
        e.preventDefault();
        var id = link.getAttribute('href');
        var section = document.querySelector(id);
        if (!section) { return; }
        if (this.activeSection && this.activeTab) {
          this.activeSection.style.display = 'none';
          this.activeSection = null;
          this.activeTab.classList.remove('active');
          this.activeTab = null;
        }
        this.activeTab = tab;
        this.activeTab.classList.add('active');
        section.style.display = 'block';
        this.activeSection = section;
      }.bind(this))
    })
  }
}

Tabs();

// select tab based on mobile or desktop visitor.
var clickEl = null;
if (isMobile()) {
  clickEl = document.querySelector('a[href="#mobile"]');
} else {
  clickEl = document.querySelector('a[href="#rift"]');
}

clickEl.dispatchEvent(new MouseEvent('click', {
  'view': window,
  'bubbles': true,
  'cancelable': true
}));

// ismobile
function isMobile() {
  var check = false;
  (function (a) {
    if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) {
      check = true;
    }
  })(navigator.userAgent || navigator.vendor || window.opera);
  return check;
};

if (isMobile()) {
  document.body.className += ' isMobile';
}

function initGoogleAnalytics (id) {
  (function(c, v, a, n) {
    c.GoogleAnalyticsObject = n;

    c[n] = c[n] || function() {
      (c[n].q = c[n].q || []).push(arguments);
    }, c[n].l = 1 * new Date();

    var s = v.createElement('script');
    s.async = true;
    s.src = a;

    var m = v.getElementsByTagName('script')[0];
    m.parentNode.insertBefore(s, m);
  })(window, document, 'https://www.google-analytics.com/analytics.js', 'ga');
  ga('create', id, 'auto');
  ga('send', 'pageview');
}

initGoogleAnalytics('UA-24056643-3');
<|endoftext|>


<commit_before>(function()
{
    var loader = cns("sfme.internals");
    var log = cns("sfme.log");
    var this_ = this;
    this.ready = false;
    var shaderPrograms = [];
    this.activeShader = null;
    var gl = null;

    this.init = function(gl_)
    {
        gl = gl_;

        return new Promise(function(resolve,reject)
        {
            this_.loadShadersFromFile(["sfme/shaders/standard.vs", "sfme/shaders/standard.fs"],
                ["x-shader/x-vertex","x-shader/x-fragment"]).then(
                function(values)
                {
                    log.debug("Shaders loaded");
                    shaderPrograms["standard"] = createProgram(values[0],values[1]);
                    useProgram("standard");
                    this_.enableShader("standard");
                    log.debug("standard shader:"+this_.activeShader);

                    this_.ready = true;
                    resolve();
                },
                function ()
                {
                    reject();
                });
        });
    };

    this.enableShader = function(id)
    {
        this_.activeShader = shaderPrograms[id] || null;
    };

    this.activateVertexShader = function(obj)
    {
        if (this_.activeShader)
        {
            gl.vertexAttribPointer(this_.activeShader.vertexPositionAttribute, obj.vertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
        }
    };

    this.activateFragmentShader = function(obj)
    {
        if (this_.activeShader)
        {
            gl.vertexAttribPointer(this_.activeShader.vertexColorAttribute, obj.vertexColorBuffer.itemSize, gl.FLOAT, false, 0, 0);
        }
    };

    this.setUniforms = function(pMatrix,mvMatrix)
    {
        if (this_.activeShader)
        {
            gl.uniformMatrix4fv(this_.activeShader.pMatrixUniform, false, pMatrix);
            gl.uniformMatrix4fv(this_.activeShader.mvMatrixUniform, false, mvMatrix);
        }
    };

    function createProgram(vertexShader,fragmentShader)
    {
        var shaderProgram = gl.createProgram();
        gl.attachShader(shaderProgram, vertexShader);
        gl.attachShader(shaderProgram, fragmentShader);
        gl.linkProgram(shaderProgram);

        if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
            alert("Could not initialise shaders");
        }
        return shaderProgram;
    }

    function useProgram(programIndex)
    {
        var shaderProgram = shaderPrograms[programIndex];
        gl.useProgram(shaderProgram);

        shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
        gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

        shaderProgram.vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor");
        gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute);

        shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
        shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
    }

    this.loadShadersFromFile = function(fNameList,typeList)
    {
        var prArray = [];

        for (var i=0;i<fNameList.length;++i)
        {
            prArray.push(this.loadShaderFromFile(fNameList[i],typeList[i]));
        }

        return Promise.all(prArray);
    };

    this.loadShaderFromFile = function(fName,type)
    {
        return new Promise(function(resolve, reject)
        {
            loader.loadFile(fName).then(function(shaderSource)
            {
                log.debug(shaderSource);
                var shader = this_.loadShaderFromSource(type,shaderSource);
                resolve(shader);
            },
            function() { reject(); });
        });
    };

    function createFragmentShader()
    {
        var shader  = gl.createShader(gl.FRAGMENT_SHADER);
        return shader;
    }

    function createVertexShader()
    {
        var shader = gl.createShader(gl.VERTEX_SHADER);
        return shader;
    }

    function createShaderFromSource(shader,source)
    {
        gl.shaderSource(shader, source);
        gl.compileShader(shader);

        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS))
        {
            alert(gl.getShaderInfoLog(shader));
            return null;
        }
        return shader;
    }

    this.loadShaderFromDocument = function(id)
    {
        var shaderScript = document.getElementById(id);
        if (!shaderScript)
        {
            return null;
        }

        var str = "";
        var k = shaderScript.firstChild;
        while (k)
        {
            if (k.nodeType === 3)
            {
                str += k.textContent;
            }
            k = k.nextSibling;
        }

        return this.loadShaderFromSource(shaderScript.type,str);
    };

    this.loadShaderFromSource = function(type,sourceCode)
    {
        var shader;
        if (type === "x-shader/x-fragment")
        {
            shader = createFragmentShader();
        }
        else if (type === "x-shader/x-vertex")
        {
            shader = createVertexShader();
        }
        else
        {
            return null;
        }

        return createShaderFromSource(shader,sourceCode);
    };
}
).apply(cns("sfme.internals.shaderManager"));
<commit_msg>Hidden more variables.<commit_after>(function()
{
    "use strict";
    var loader = cns("sfme.internals");
    var log = cns("sfme.log");
    var this_ = this;
    var ready = false;
    var shaderPrograms = [];
    this.activeShader = null;
    var gl = null;

    this.init = function(gl_)
    {
        gl = gl_;

        return new Promise(function(resolve,reject)
        {
            loadShadersFromFile(["sfme/shaders/standard.vs", "sfme/shaders/standard.fs"],
                ["x-shader/x-vertex","x-shader/x-fragment"]).then(
                function(values)
                {
                    log.debug("Shaders loaded");
                    shaderPrograms["standard"] = createProgram(values[0],values[1]);
                    useProgram("standard");
                    enableShader("standard");
                    log.debug("standard shader:"+this_.activeShader);

                    ready = true;
                    resolve();
                },
                function ()
                {
                    reject();
                });
        });
    };

    function enableShader(id)
    {
        this_.activeShader = shaderPrograms[id] || null;
    };

    function activateVertexShader(obj)
    {
        if (this_.activeShader)
        {
            gl.vertexAttribPointer(this_.activeShader.vertexPositionAttribute, obj.vertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
        }
    };

    function activateFragmentShader(obj)
    {
        if (this_.activeShader)
        {
            gl.vertexAttribPointer(this_.activeShader.vertexColorAttribute, obj.vertexColorBuffer.itemSize, gl.FLOAT, false, 0, 0);
        }
    };

    function setUniforms(pMatrix,mvMatrix)
    {
        if (this_.activeShader)
        {
            gl.uniformMatrix4fv(this_.activeShader.pMatrixUniform, false, pMatrix);
            gl.uniformMatrix4fv(this_.activeShader.mvMatrixUniform, false, mvMatrix);
        }
    };

    function createProgram(vertexShader,fragmentShader)
    {
        var shaderProgram = gl.createProgram();
        gl.attachShader(shaderProgram, vertexShader);
        gl.attachShader(shaderProgram, fragmentShader);
        gl.linkProgram(shaderProgram);

        if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
            alert("Could not initialise shaders");
        }
        return shaderProgram;
    }

    function useProgram(programIndex)
    {
        var shaderProgram = shaderPrograms[programIndex];
        gl.useProgram(shaderProgram);

        shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
        gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);

        shaderProgram.vertexColorAttribute = gl.getAttribLocation(shaderProgram, "aVertexColor");
        gl.enableVertexAttribArray(shaderProgram.vertexColorAttribute);

        shaderProgram.pMatrixUniform = gl.getUniformLocation(shaderProgram, "uPMatrix");
        shaderProgram.mvMatrixUniform = gl.getUniformLocation(shaderProgram, "uMVMatrix");
    }

    function loadShadersFromFile(fNameList,typeList)
    {
        var prArray = [];

        for (var i=0;i<fNameList.length;++i)
        {
            prArray.push(loadShaderFromFile(fNameList[i],typeList[i]));
        }

        return Promise.all(prArray);
    };

    function loadShaderFromFile(fName,type)
    {
        return new Promise(function(resolve, reject)
        {
            loader.loadFile(fName).then(function(shaderSource)
            {
                log.debug(shaderSource);
                var shader = loadShaderFromSource(type,shaderSource);
                resolve(shader);
            },
            function() { reject(); });
        });
    }

    function createFragmentShader()
    {
        var shader  = gl.createShader(gl.FRAGMENT_SHADER);
        return shader;
    }

    function createVertexShader()
    {
        var shader = gl.createShader(gl.VERTEX_SHADER);
        return shader;
    }

    function createShaderFromSource(shader,source)
    {
        gl.shaderSource(shader, source);
        gl.compileShader(shader);

        if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS))
        {
            alert(gl.getShaderInfoLog(shader));
            return null;
        }
        return shader;
    }

    function loadShaderFromDocument(id)
    {
        var shaderScript = document.getElementById(id);
        if (!shaderScript)
        {
            return null;
        }

        var str = "";
        var k = shaderScript.firstChild;
        while (k)
        {
            if (k.nodeType === 3)
            {
                str += k.textContent;
            }
            k = k.nextSibling;
        }

        return loadShaderFromSource(shaderScript.type,str);
    }

    function loadShaderFromSource(type,sourceCode)
    {
        var shader;
        if (type === "x-shader/x-fragment")
        {
            shader = createFragmentShader();
        }
        else if (type === "x-shader/x-vertex")
        {
            shader = createVertexShader();
        }
        else
        {
            return null;
        }

        return createShaderFromSource(shader,sourceCode);
    }

    this.activateVertexShader = activateVertexShader;
    this.activateFragmentShader = activateFragmentShader;
    this.setUniforms = setUniforms;

}
).apply(cns("sfme.internals.shaderManager"));
<|endoftext|>


<commit_before>/* global Kiwi, _, helixPi */
'use strict';

var HelixPiEditor = HelixPiEditor || {};

HelixPiEditor.Editor = new Kiwi.State('Editor');

HelixPiEditor.Editor.create = function () {
  this.game.huds.defaultHUD.removeAllWidgets();

  this.events = {};

  this.timeline = HelixPiEditor.timeline(this);

  this.participants = [];

  this.game.input.keyboard.onKeyDown.add(
    this.onPress,
    this
  );

  this.mouse = this.game.input.mouse;
  this.mouse.onDown.add(this.handleClick, this);
  this.mouse.onUp.add(this.handleClickRelease, this);

  if (this.frameText) {
    this.frameText.destroy();
  }

  this.frameText = new Kiwi.GameObjects.TextField(this, 'test', 10, 10, '#FFF');

  this.addChild(this.frameText);

  this.currentFrame = 0;
  this.highestFrame = 0;

  this.scenarios = HelixPiEditor.scenarios();

  this.addScenarioButton = HelixPiEditor.buttons.create(
    this,
    'Add Scenario',
    5,
    55
  );

  this.addScenarioButton.input.onDown.add(this.addScenario, this);

  this.scenarioButtons = [];

  this.progressIndicator = {
    destroy: function () {}
  };

  this.line = new Kiwi.Plugins.Primitives.Line({
    state: this,
    points: [],

    strokeColor: [1, 1, 1],
    strokeWidth: 4
  });
  this.addChild(this.line);

  if (this.scenarios.length === 0) {
    this.addScenario();
  } else {
    this.scenarios.forEach(function (scenario, index) {
      this.createScenarioButton(index);
    }.bind(this));

    this.loadScenario(0);
  };

  this.progressIndicator = {destroy: function () {}};

  this.addKeyFrameButton = HelixPiEditor.buttons.create(
    this,
    'Add 60 frames',
    this.game.stage.width - 130,
    5
  );

  this.playProgramButton = HelixPiEditor.buttons.create(
    this,
    'Play Program',
    this.game.stage.width - 130,
    45
  );

  this.addInputButton = HelixPiEditor.buttons.create(
    this,
    'Add input',
    5,
    this.game.stage.height - 130
  );

  this.addKeyFrameButton.input.onDown.add(this.addKeyFrame, this);
  this.playProgramButton.input.onDown.add(this.playProgram, this);
  this.addInputButton.input.onDown.add(this.addInput, this);

  this.addingInput = false;
  this.input = this.input || [];
  this.input.forEach(this.renderInput.bind(this));

  this.api = function (entity, getButtonDown) {
    var self = {};

    function declareApiCall(options, f) {
      f.takes = options.takes;
      f.returns = options.returns;
      return f;
    }

    self.move = declareApiCall({
      takes: {x: 0, y: 0},
      returns: null
    }, function (coordinates) {
      entity.x += coordinates.x;
      entity.y += coordinates.y;
    });

    self.checkButtonDown = declareApiCall({
      takes: ['right', 'left', 'up', 'down'],
      returns: [true, false]
    }, getButtonDown);

    self.getPosition = declareApiCall({
      takes: [],
      returns: {x: 0, y: 0}
    }, function () {
      return {
        x: entity.x,
        y: entity.y
      };
    });

    return self;
  };
};

HelixPiEditor.Editor.update = function () {
  Kiwi.State.prototype.update.call(this);

  this.frameText.text = ['Frame: ', this.currentFrame].join('');

  this.timeline.tick(this.handleTimelineTick.bind(this));
};

HelixPiEditor.Editor.handleTimelineTick = function (ratio, updateCharacterPosition) {
  this.currentFrame = Math.round(this.lastFrame() * ratio);
  this.displayProgressIndicator(ratio);
  if (updateCharacterPosition) {
    this.participants.forEach(function(participant) { this.moveEntityInTime(participant, ratio) }.bind(this));
  };
}

HelixPiEditor.Editor.lastFrame = function () {
  return this.highestFrame;
};

HelixPiEditor.Editor.displayProgressIndicator = function (progress) {
  var indicatorHeight = 60;
  this.progressIndicator.destroy();

  this.progressIndicator = new Kiwi.Plugins.Primitives.Line({
    state: this,
    points: [
      [this.game.stage.width * progress, this.game.stage.height - indicatorHeight],
      [this.game.stage.width * progress, this.game.stage.height]
    ],
    strokeColor: [1, 1, 1],
    strokeWidth: 4
  });

  this.addChild(this.progressIndicator);
};

HelixPiEditor.Editor.createScenario = function () {
  // TODO - support multiple participants
  return {
    participants: _.map(this.participants, 'name'),

    scenarios: this.scenarios.map(function (scenario) {
      var positions = scenario.positions;

      var initialPositions = _.chain(positions)
        .map(function (participantPositions, participant) {
          return [participant, participantPositions[0]];
      }).object()
        .value();

      var expectedPositions = _.chain(positions)
        .map(function (participantPositions, participant) {
          return [participant, participantPositions.slice(1)];
      }).object()
        .value();

      var input = scenario.input;

      return {
        participants: _.uniq(Object.keys(positions)),

        initialPositions: initialPositions,

        startPosition: function (name) {
          return initialPositions[name];
        },

        expectedPositions: expectedPositions,
        input: input
      };
    }),

    fitness: function (expectedPosition, entity) {
      var distance = {
        x: Math.abs(expectedPosition.x - entity.x),
        y: Math.abs(expectedPosition.y - entity.y)
      };

      return 1000 - Math.pow((distance.x + distance.y), 1.4);
    }
  };
};

HelixPiEditor.Editor.savePosition = function (participant) {
  var participantGameObject = this.findParticipant(participant.name).gameObject;

  this.createPosition({
    participant: participant,
    x: participantGameObject.x + participantGameObject.width / 2,
    y: participantGameObject.y + participantGameObject.height / 2,
    frame: this.currentFrame,
  });
};

HelixPiEditor.Editor.createPosition = function (position) {
  var participantPositions = this.positions[position.participant.name];
  var existingPositionForFrameIndex = participantPositions.findIndex(function (existingPosition) {
    return existingPosition.frame == position.frame;
  });

  if (existingPositionForFrameIndex != -1) {
    participantPositions.splice(existingPositionForFrameIndex, 1);
  }

  participantPositions.push(position);
  participantPositions = participantPositions.sort(function (a, b) {
    return a.frame > b.frame;
  });

  this.updatePath(position.participant);
};

HelixPiEditor.Editor.updatePath = function (participant) {
  this.line.destroy();
  this.line = new Kiwi.Plugins.Primitives.Line({
    state: this,
    points: this.positions[participant.name]
      .map(function (position) { return [position.x, position.y]; }),

    strokeColor: [1, 1, 1],
    strokeWidth: 4
  });
  this.addChild(this.line);
};

HelixPiEditor.Editor.addKeyFrame = function () {
  this.currentFrame += 60;
  this.highestFrame = _.max([this.currentFrame, this.highestFrame]);
};

HelixPiEditor.Editor.onPress = function (keyCode) {
  if (keyCode === Kiwi.Input.Keycodes.R) {
    this.createProgram();
  }
};

HelixPiEditor.Editor.droppedEntity = function (participant) {
  this.savePosition(participant);
  this.updatePath(participant);
};

HelixPiEditor.Editor.createProgram = function () {
  this.results = helixPi(this.createScenario(), this.api, 500, 32, HelixPiEditor.results());

  HelixPiEditor.results(this.results);
};

HelixPiEditor.Editor.playProgram = function () {
  this.createProgram();
  HelixPiEditor.results(this.results);
  this.game.states.switchState('Play');
};

HelixPiEditor.Editor.moveEntityInTime = function (participant, ratio) {
  // TODO - fix stuff being moved to top left for some reason
  var participantGameObject = this.findParticipant(participant.name).gameObject;
  var lerp = function (startPosition, endPosition, ratio) {
    return {
      x: startPosition.x + (endPosition.x - startPosition.x) * ratio,
      y: startPosition.y + (endPosition.y - startPosition.y) * ratio
    };
  };

  var firstPosition = _.first(this.positions[participant.name]);

  if (ratio === 0 && firstPosition) {
    participantGameObject.x = firstPosition.x - participantGameObject.width / 2;
    participantGameObject.y = firstPosition.y - participantGameObject.height / 2;
    return;
  }

  var that = this;
  var getPositionAt = function (positions, ratio) {
    var totalFrames = that.lastFrame();
    var frameToFind = totalFrames * ratio;

    if (frameToFind > totalFrames) {
      return false;
    }

    // ugh a for loop
    // TODO - make this functional and nice
    for (var positionIndex = 0; positionIndex < positions.length; positionIndex++) {
      var position = positions[positionIndex];
      var nextPosition = positions[positionIndex + 1];

      if (nextPosition === undefined) {
        continue;
      }

      if (frameToFind >= position.frame && frameToFind < nextPosition.frame) {
        // if you read this code I am a bit sorry
        var startPositionRatio = position.frame / totalFrames;
        var nextPositionRatio = nextPosition.frame / totalFrames;

        var duration = nextPositionRatio - startPositionRatio;

        return lerp(position, nextPosition, (ratio - startPositionRatio) / duration);
      }
    }

    return false;
  };

  var newPosition = getPositionAt(this.positions[participant.name], ratio);

  // TODO - make entity centered
  if (newPosition) {
    participantGameObject.x = newPosition.x - participantGameObject.width / 2;
    participantGameObject.y = newPosition.y - participantGameObject.height / 2;
  }
};

HelixPiEditor.Editor.addInput = function () {
  this.addingInput = true;
  this.firstClickAfterAddingInput = true; // TODO - WOW SUC HACK
}

HelixPiEditor.Editor.handleClick = function () {
  if (this.addingInput && !this.firstClickAfterAddingInput) {
    this.inputStartX = this.mouse.x;
  }
}

HelixPiEditor.Editor.handleClickRelease = function () {
  if (this.addingInput) {
    if (this.firstClickAfterAddingInput) {
      this.firstClickAfterAddingInput = false;
      return;
    }

    this.addingInput = false;
    this.createInput(
      this.inputStartX,
      this.mouse.x,
      prompt('Key?')
    );
  }
}

HelixPiEditor.Editor.createInput = function (startX, endX, key) {
  var totalFrames = this.lastFrame();

  var input = {
    startFrame: totalFrames * startX / this.game.stage.width,
    endFrame: totalFrames * endX / this.game.stage.width,
    key: key,
    startX: startX,
    endX: endX
  };

  this.input.push(input);
  this.renderInput(input);
}

HelixPiEditor.Editor.renderInput = function(input) {
  var newInput = new Kiwi.Plugins.Primitives.Rectangle({
    state: this,
    x: input.startX,
    y: this.game.stage.height - 100,
    width: input.endX - input.startX,
    height: 30,
    color: [0.5, 0.5, 0.5],
  })

  this.addChild(newInput);
  var text = new Kiwi.GameObjects.TextField(this, input.key, newInput.x + 5, newInput.y + 5, '#FFF');
  this.addChild(text);

  this.on('changeScenario', function () {
    newInput.destroy();
    text.destroy();
  });
}

HelixPiEditor.Editor.addScenario = function () {
  var newScenario = {
    participants: [
      {
        name: 'Eevee',
        sprite: this.textures.paddle
      },

      {
        name: 'Greg',
        sprite: this.textures.ball
      },

      {
        name: 'Stan',
        sprite: this.textures.paddle
      }
    ],
    positions: {
      'Eevee': [
        {
          x: 200,
          y: 250,
          frame: 0
        }
      ],
      'Greg': [
        {
          x: 400,
          y: 270,
          frame: 0
        }
      ],
      'Stan': [
        {
          x: 600,
          y: 250,
          frame: 0
        }
      ],
    },

    input: [
    ]
  };

  this.scenarios.push(newScenario);
  HelixPiEditor.scenarios(this.scenarios);

  var scenarioIndex = this.scenarios.length - 1;

  this.createScenarioButton(scenarioIndex);

  this.loadScenario(scenarioIndex);
};

HelixPiEditor.Editor.loadScenario = function (scenarioIndex) {
  this.line.destroy();
  this.line = {destroy: function () {}};

  this.participants.forEach(function (participant) {
    participant.gameObject.destroy();
  });

  this.participants = [];

  this.trigger('changeScenario');

  var scenario = this.scenarios[scenarioIndex];
  this.positions = scenario.positions;
  this.input = scenario.input;
  this.input.forEach(this.renderInput.bind(this));
  scenario.participants.forEach(this.addParticipant.bind(this));
  this.handleTimelineTick(0, true);
  this.reflowScenarioButtons();
};

HelixPiEditor.Editor.reflowScenarioButtons = function () {
  var yOffset = 55;
  var yDistance = 40;

  this.scenarioButtons.forEach(function (button, index) {
    button.x = 5;
    button.y = index + yOffset + index * yDistance;
  });

  if (this.scenarioButtons.length === 0) {
    this.addScenarioButton.y = yOffset;
  } else {
    this.addScenarioButton.y = _.last(this.scenarioButtons).y + yDistance;
  }
};

HelixPiEditor.Editor.createScenarioButton = function (scenarioIndex) {
  var newScenarioButton = HelixPiEditor.buttons.create(
    this,
    'Scenario #' + (scenarioIndex + 1),
    5,
    55
  );

  newScenarioButton.input.onDown.add(function () {
    this.loadScenario(scenarioIndex); // Oh yeah sweet potential off by one error
  }.bind(this));

  this.scenarioButtons.push(newScenarioButton);
};

HelixPiEditor.Editor.findParticipant = function (participantName) {
  return this.participants.find(function(participant) {
    return participant.name == participantName;
  });
}

HelixPiEditor.Editor.addParticipant = function (participant) {
  // TODO - start at the initial position

  // TODO - handle having no positions
  var startPosition = _.first(this.positions[participant.name]);
  if (startPosition === undefined) {
    startPosition = {x: 250, y: 250};
  }

  participant.gameObject = new Kiwi.GameObjects.Sprite(
    this,
    participant.sprite,
    startPosition.x,
    startPosition.y,
    true
  );

  this.addChild(participant.gameObject);
  this.participants.push(participant);

  participant.gameObject.input.enableDrag();
  participant.gameObject.input.onDragStopped.add(function () { this.droppedEntity(participant) }.bind(this));
};

HelixPiEditor.Editor.eventHandlers = function (eventName) {
  if (this.events[eventName] === undefined) {
    this.events[eventName] = [];
  }

  return this.events[eventName];
};

HelixPiEditor.Editor.on = function (eventName, callback) {
  this.eventHandlers(eventName).push(callback.bind(this));
};

HelixPiEditor.Editor.trigger = function (eventName) {
  this.eventHandlers(eventName).forEach(function (handler) {
    handler();
  });
};

<commit_msg>Set highestFrame correctly when transitioning between scenarios, editor is now mostly usable<commit_after>/* global Kiwi, _, helixPi */
'use strict';

var HelixPiEditor = HelixPiEditor || {};

HelixPiEditor.Editor = new Kiwi.State('Editor');

HelixPiEditor.Editor.create = function () {
  this.game.huds.defaultHUD.removeAllWidgets();

  this.events = {};

  this.timeline = HelixPiEditor.timeline(this);

  this.participants = [];

  this.game.input.keyboard.onKeyDown.add(
    this.onPress,
    this
  );

  this.mouse = this.game.input.mouse;
  this.mouse.onDown.add(this.handleClick, this);
  this.mouse.onUp.add(this.handleClickRelease, this);

  if (this.frameText) {
    this.frameText.destroy();
  }

  this.frameText = new Kiwi.GameObjects.TextField(this, 'test', 10, 10, '#FFF');

  this.addChild(this.frameText);

  this.currentFrame = 0;
  this.highestFrame = 0;

  this.scenarios = HelixPiEditor.scenarios();

  this.addScenarioButton = HelixPiEditor.buttons.create(
    this,
    'Add Scenario',
    5,
    55
  );

  this.addScenarioButton.input.onDown.add(this.addScenario, this);

  this.scenarioButtons = [];

  this.progressIndicator = {
    destroy: function () {}
  };

  this.line = new Kiwi.Plugins.Primitives.Line({
    state: this,
    points: [],

    strokeColor: [1, 1, 1],
    strokeWidth: 4
  });
  this.addChild(this.line);

  if (this.scenarios.length === 0) {
    this.addScenario();
  } else {
    this.scenarios.forEach(function (scenario, index) {
      this.createScenarioButton(index);
    }.bind(this));

    this.loadScenario(0);
  };

  this.progressIndicator = {destroy: function () {}};

  this.addKeyFrameButton = HelixPiEditor.buttons.create(
    this,
    'Add 60 frames',
    this.game.stage.width - 130,
    5
  );

  this.playProgramButton = HelixPiEditor.buttons.create(
    this,
    'Play Program',
    this.game.stage.width - 130,
    45
  );

  this.addInputButton = HelixPiEditor.buttons.create(
    this,
    'Add input',
    5,
    this.game.stage.height - 130
  );

  this.addKeyFrameButton.input.onDown.add(this.addKeyFrame, this);
  this.playProgramButton.input.onDown.add(this.playProgram, this);
  this.addInputButton.input.onDown.add(this.addInput, this);

  this.addingInput = false;
  this.input = this.input || [];
  this.input.forEach(this.renderInput.bind(this));

  this.on('changeScenario', function () {
    this.highestFrame = _.max(_.map(this.positions, function (positionsPerParticipant) {
      return _.max(positionsPerParticipant.map(function (position) {
        return position.frame;
      }))
    }))
  });

  this.api = function (entity, getButtonDown) {
    var self = {};

    function declareApiCall(options, f) {
      f.takes = options.takes;
      f.returns = options.returns;
      return f;
    }

    self.move = declareApiCall({
      takes: {x: 0, y: 0},
      returns: null
    }, function (coordinates) {
      entity.x += coordinates.x;
      entity.y += coordinates.y;
    });

    self.checkButtonDown = declareApiCall({
      takes: ['right', 'left', 'up', 'down'],
      returns: [true, false]
    }, getButtonDown);

    self.getPosition = declareApiCall({
      takes: [],
      returns: {x: 0, y: 0}
    }, function () {
      return {
        x: entity.x,
        y: entity.y
      };
    });

    return self;
  };
};

HelixPiEditor.Editor.update = function () {
  Kiwi.State.prototype.update.call(this);

  this.frameText.text = ['Frame: ', this.currentFrame].join('');

  this.timeline.tick(this.handleTimelineTick.bind(this));
};

HelixPiEditor.Editor.handleTimelineTick = function (ratio, updateCharacterPosition) {
  this.currentFrame = Math.round(this.lastFrame() * ratio);
  this.displayProgressIndicator(ratio);
  if (updateCharacterPosition) {
    this.participants.forEach(function(participant) { this.moveEntityInTime(participant, ratio) }.bind(this));
  };
}

HelixPiEditor.Editor.lastFrame = function () {
  return this.highestFrame;
};

HelixPiEditor.Editor.displayProgressIndicator = function (progress) {
  var indicatorHeight = 60;
  this.progressIndicator.destroy();

  this.progressIndicator = new Kiwi.Plugins.Primitives.Line({
    state: this,
    points: [
      [this.game.stage.width * progress, this.game.stage.height - indicatorHeight],
      [this.game.stage.width * progress, this.game.stage.height]
    ],
    strokeColor: [1, 1, 1],
    strokeWidth: 4
  });

  this.addChild(this.progressIndicator);
};

HelixPiEditor.Editor.createScenario = function () {
  // TODO - support multiple participants
  return {
    participants: _.map(this.participants, 'name'),

    scenarios: this.scenarios.map(function (scenario) {
      var positions = scenario.positions;

      var initialPositions = _.chain(positions)
        .map(function (participantPositions, participant) {
          return [participant, participantPositions[0]];
      }).object()
        .value();

      var expectedPositions = _.chain(positions)
        .map(function (participantPositions, participant) {
          return [participant, participantPositions.slice(1)];
      }).object()
        .value();

      var input = scenario.input;

      return {
        participants: _.uniq(Object.keys(positions)),

        initialPositions: initialPositions,

        startPosition: function (name) {
          return initialPositions[name];
        },

        expectedPositions: expectedPositions,
        input: input
      };
    }),

    fitness: function (expectedPosition, entity) {
      var distance = {
        x: Math.abs(expectedPosition.x - entity.x),
        y: Math.abs(expectedPosition.y - entity.y)
      };

      return 1000 - Math.pow((distance.x + distance.y), 1.4);
    }
  };
};

HelixPiEditor.Editor.savePosition = function (participant) {
  var participantGameObject = this.findParticipant(participant.name).gameObject;

  this.createPosition({
    participant: participant,
    x: participantGameObject.x + participantGameObject.width / 2,
    y: participantGameObject.y + participantGameObject.height / 2,
    frame: this.currentFrame,
  });
};

HelixPiEditor.Editor.createPosition = function (position) {
  var participantPositions = this.positions[position.participant.name];
  var existingPositionForFrameIndex = participantPositions.findIndex(function (existingPosition) {
    return existingPosition.frame == position.frame;
  });

  if (existingPositionForFrameIndex != -1) {
    participantPositions.splice(existingPositionForFrameIndex, 1);
  }

  participantPositions.push(position);
  participantPositions = participantPositions.sort(function (a, b) {
    return a.frame > b.frame;
  });

  this.updatePath(position.participant);
};

HelixPiEditor.Editor.updatePath = function (participant) {
  this.line.destroy();
  this.line = new Kiwi.Plugins.Primitives.Line({
    state: this,
    points: this.positions[participant.name]
      .map(function (position) { return [position.x, position.y]; }),

    strokeColor: [1, 1, 1],
    strokeWidth: 4
  });
  this.addChild(this.line);
};

HelixPiEditor.Editor.addKeyFrame = function () {
  this.currentFrame += 60;
  this.highestFrame = _.max([this.currentFrame, this.highestFrame]);
};

HelixPiEditor.Editor.onPress = function (keyCode) {
  if (keyCode === Kiwi.Input.Keycodes.R) {
    this.createProgram();
  }
};

HelixPiEditor.Editor.droppedEntity = function (participant) {
  this.savePosition(participant);
  this.updatePath(participant);
};

HelixPiEditor.Editor.createProgram = function () {
  this.results = helixPi(this.createScenario(), this.api, 500, 32, HelixPiEditor.results());

  HelixPiEditor.results(this.results);
};

HelixPiEditor.Editor.playProgram = function () {
  this.createProgram();
  HelixPiEditor.results(this.results);
  this.game.states.switchState('Play');
};

HelixPiEditor.Editor.moveEntityInTime = function (participant, ratio) {
  // TODO - fix stuff being moved to top left for some reason
  var participantGameObject = this.findParticipant(participant.name).gameObject;
  var lerp = function (startPosition, endPosition, ratio) {
    return {
      x: startPosition.x + (endPosition.x - startPosition.x) * ratio,
      y: startPosition.y + (endPosition.y - startPosition.y) * ratio
    };
  };

  var firstPosition = _.first(this.positions[participant.name]);

  if (ratio === 0 && firstPosition) {
    participantGameObject.x = firstPosition.x - participantGameObject.width / 2;
    participantGameObject.y = firstPosition.y - participantGameObject.height / 2;
    return;
  }

  var that = this;
  var getPositionAt = function (positions, ratio) {
    var totalFrames = that.lastFrame();
    var frameToFind = totalFrames * ratio;

    if (frameToFind > totalFrames) {
      return false;
    }

    // ugh a for loop
    // TODO - make this functional and nice
    for (var positionIndex = 0; positionIndex < positions.length; positionIndex++) {
      var position = positions[positionIndex];
      var nextPosition = positions[positionIndex + 1];

      if (nextPosition === undefined) {
        continue;
      }

      if (frameToFind >= position.frame && frameToFind < nextPosition.frame) {
        // if you read this code I am a bit sorry
        var startPositionRatio = position.frame / totalFrames;
        var nextPositionRatio = nextPosition.frame / totalFrames;

        var duration = nextPositionRatio - startPositionRatio;

        return lerp(position, nextPosition, (ratio - startPositionRatio) / duration);
      }
    }

    return false;
  };

  var newPosition = getPositionAt(this.positions[participant.name], ratio);

  // TODO - make entity centered
  if (newPosition) {
    participantGameObject.x = newPosition.x - participantGameObject.width / 2;
    participantGameObject.y = newPosition.y - participantGameObject.height / 2;
  }
};

HelixPiEditor.Editor.addInput = function () {
  this.addingInput = true;
  this.firstClickAfterAddingInput = true; // TODO - WOW SUC HACK
}

HelixPiEditor.Editor.handleClick = function () {
  if (this.addingInput && !this.firstClickAfterAddingInput) {
    this.inputStartX = this.mouse.x;
  }
}

HelixPiEditor.Editor.handleClickRelease = function () {
  if (this.addingInput) {
    if (this.firstClickAfterAddingInput) {
      this.firstClickAfterAddingInput = false;
      return;
    }

    this.addingInput = false;
    this.createInput(
      this.inputStartX,
      this.mouse.x,
      prompt('Key?')
    );
  }
}

HelixPiEditor.Editor.createInput = function (startX, endX, key) {
  var totalFrames = this.lastFrame();

  var input = {
    startFrame: totalFrames * startX / this.game.stage.width,
    endFrame: totalFrames * endX / this.game.stage.width,
    key: key,
    startX: startX,
    endX: endX
  };

  this.input.push(input);
  this.renderInput(input);
}

HelixPiEditor.Editor.renderInput = function(input) {
  var newInput = new Kiwi.Plugins.Primitives.Rectangle({
    state: this,
    x: input.startX,
    y: this.game.stage.height - 100,
    width: input.endX - input.startX,
    height: 30,
    color: [0.5, 0.5, 0.5],
  })

  this.addChild(newInput);
  var text = new Kiwi.GameObjects.TextField(this, input.key, newInput.x + 5, newInput.y + 5, '#FFF');
  this.addChild(text);

  this.on('changeScenario', function () {
    newInput.destroy();
    text.destroy();
  });
}

HelixPiEditor.Editor.addScenario = function () {
  var newScenario = {
    participants: [
      {
        name: 'Eevee',
        sprite: this.textures.paddle
      },

      {
        name: 'Greg',
        sprite: this.textures.ball
      },

      {
        name: 'Stan',
        sprite: this.textures.paddle
      }
    ],
    positions: {
      'Eevee': [
        {
          x: 200,
          y: 250,
          frame: 0
        }
      ],
      'Greg': [
        {
          x: 400,
          y: 270,
          frame: 0
        }
      ],
      'Stan': [
        {
          x: 600,
          y: 250,
          frame: 0
        }
      ],
    },

    input: [
    ]
  };

  this.scenarios.push(newScenario);
  HelixPiEditor.scenarios(this.scenarios);

  var scenarioIndex = this.scenarios.length - 1;

  this.createScenarioButton(scenarioIndex);

  this.loadScenario(scenarioIndex);
};

HelixPiEditor.Editor.loadScenario = function (scenarioIndex) {
  this.line.destroy();
  this.line = {destroy: function () {}};

  this.participants.forEach(function (participant) {
    participant.gameObject.destroy();
  });

  this.participants = [];

  var scenario = this.scenarios[scenarioIndex];
  this.positions = scenario.positions;
  this.input = scenario.input;
  this.input.forEach(this.renderInput.bind(this));
  scenario.participants.forEach(this.addParticipant.bind(this));
  this.handleTimelineTick(0, true);
  this.reflowScenarioButtons();

  this.trigger('changeScenario');
};

HelixPiEditor.Editor.reflowScenarioButtons = function () {
  var yOffset = 55;
  var yDistance = 40;

  this.scenarioButtons.forEach(function (button, index) {
    button.x = 5;
    button.y = index + yOffset + index * yDistance;
  });

  if (this.scenarioButtons.length === 0) {
    this.addScenarioButton.y = yOffset;
  } else {
    this.addScenarioButton.y = _.last(this.scenarioButtons).y + yDistance;
  }
};

HelixPiEditor.Editor.createScenarioButton = function (scenarioIndex) {
  var newScenarioButton = HelixPiEditor.buttons.create(
    this,
    'Scenario #' + (scenarioIndex + 1),
    5,
    55
  );

  newScenarioButton.input.onDown.add(function () {
    this.loadScenario(scenarioIndex); // Oh yeah sweet potential off by one error
  }.bind(this));

  this.scenarioButtons.push(newScenarioButton);
};

HelixPiEditor.Editor.findParticipant = function (participantName) {
  return this.participants.find(function(participant) {
    return participant.name == participantName;
  });
}

HelixPiEditor.Editor.addParticipant = function (participant) {
  // TODO - start at the initial position

  // TODO - handle having no positions
  var startPosition = _.first(this.positions[participant.name]);
  if (startPosition === undefined) {
    startPosition = {x: 250, y: 250};
  }

  participant.gameObject = new Kiwi.GameObjects.Sprite(
    this,
    participant.sprite,
    startPosition.x,
    startPosition.y,
    true
  );

  this.addChild(participant.gameObject);
  this.participants.push(participant);

  participant.gameObject.input.enableDrag();
  participant.gameObject.input.onDragStopped.add(function () { this.droppedEntity(participant) }.bind(this));
};

HelixPiEditor.Editor.eventHandlers = function (eventName) {
  if (this.events[eventName] === undefined) {
    this.events[eventName] = [];
  }

  return this.events[eventName];
};

HelixPiEditor.Editor.on = function (eventName, callback) {
  this.eventHandlers(eventName).push(callback.bind(this));
};

HelixPiEditor.Editor.trigger = function (eventName) {
  this.eventHandlers(eventName).forEach(function (handler) {
    handler();
  });
};

<|endoftext|>