<commit_before>'use strict';

let angular = require('angular');

let tabsDirective = function($timeout) {
  'ngInject';

  return {
    template: require('./tabs.html'),
    scope: {
      tabs: '=',
      tabactivate: '=?'
    },
    link(scope, element) {
      let duration = 250;

      let activate = function (tab) {
        let fieldsetContainer = document.querySelector('.np-tabs');
        let tabNodes = fieldsetContainer.querySelectorAll('.np-tab');
        scope.tabs.forEach(function(t, i) {
          let $tab = angular.element(tabNodes[i]);
          if (typeof tab === 'object' || typeof tab === 'number') {
            if ((typeof tab === 'object') ? (t === tab) : (i === tab)) {
              t.active = true;
              $tab.addClass('active');
            } else {
              t.active = false;
              $tab.removeClass('active');
            }
          }
        });
        if (typeof scope.tabactivate === 'function') {
          scope.tabactivate.call({}, tab);
        }
      };

      let animateHorizontalScroll = function (elem, stop) {
        let step = 25;
        let steps = duration / step;
        let start = elem.scrollLeft;
        let length = stop - start;
        let increment = length / steps;
        if (length !== 0) {
          let timer = setInterval(function () {
            if (((length > 0) && (elem.scrollLeft + increment >= stop)) ||
              ((length < 0) && (elem.scrollLeft + increment <= stop))) {
              elem.scrollLeft = stop;
              clearInterval(timer);
            } else {
              elem.scrollLeft += increment;
            }
          }, step);
          setTimeout(function () {
            clearInterval(timer);
          }, duration + 10);
        }
      };

      let slideIntoView = function () {
        let fieldsetContainer = document.querySelector('.np-tabs');
        let active = fieldsetContainer.querySelector('.np-tab.active');
        let move = active.getBoundingClientRect().left - fieldsetContainer.getBoundingClientRect().left;
        fieldsetContainer.style.left = -move + 'px';
      };

      scope.next = function () {
        let scrollContainer = element[0].querySelector('.md-virtual-repeat-scroller');
        let scroll = scrollContainer.scrollLeft;
        animateHorizontalScroll(scrollContainer, scroll + 180);
      };

      scope.back = function () {
        let scrollContainer = element[0].querySelector('.md-virtual-repeat-scroller');
        let scroll = scrollContainer.scrollLeft;
        animateHorizontalScroll(scrollContainer, scroll - 180);
      };

      scope.go = function (ev, tab) {
        let tabsContainer = element[0].querySelector('md-virtual-repeat-container');
        let scrollContainer = element[0].querySelector('.md-virtual-repeat-scroller');
        let scrollPos = scrollContainer.scrollLeft;
        let width = tabsContainer.clientWidth;
        let offset = ev.target.offsetLeft;
        let elemWidth = ev.target.offsetWidth;
        if (scrollPos > offset) {
          animateHorizontalScroll(scrollContainer, offset);
        } else if ((scrollPos + width) < (offset + elemWidth)) {
          animateHorizontalScroll(scrollContainer, offset - width + elemWidth);
        }

        let i = 0, child = ev.target;
        while ((child = child.previousElementSibling)) {
          i++;
        }

        activate(tab);

        slideIntoView();
      };

      activate(scope.tabs[0]);

      // recalc slide if window is resized
      addEventListener('resize', slideIntoView);

      // Hack to dected if scrollbars are added
      // Demo: http://jsfiddle.net/pFaSx/
      // Create an invisible iframe
      var iframe = document.createElement('iframe');
      iframe.id = "hacky-scrollbar-resize-listener";
      iframe.style.cssText = 'height: 0; background-color: transparent; margin: 0; padding: 0; overflow: hidden; border-width: 0; position: absolute; width: 100%;';

      // Register our event when the iframe loads
      iframe.onload = function() {
        // The trick here is that because this iframe has 100% width
        // it should fire a window resize event when anything causes it to
        // resize (even scrollbars on the outer document)
        iframe.contentWindow.addEventListener('resize', function() {
          try {
            var evt = document.createEvent('UIEvents');
            evt.initUIEvent('resize', true, false, window, 0);
            window.dispatchEvent(evt);
          } catch(e) {}
        });
      };

      // Stick the iframe somewhere out of the way
      document.body.appendChild(iframe);

    }
  };
};

module.exports = tabsDirective;
<commit_msg>undef check in tabs<commit_after>'use strict';

let angular = require('angular');

let tabsDirective = function($timeout) {
  'ngInject';

  return {
    template: require('./tabs.html'),
    scope: {
      tabs: '=',
      tabactivate: '=?'
    },
    link(scope, element) {
      let duration = 250;

      let activate = function (tab) {
        let fieldsetContainer = document.querySelector('.np-tabs');
        let tabNodes = fieldsetContainer.querySelectorAll('.np-tab');
        scope.tabs.forEach(function(t, i) {
          let $tab = angular.element(tabNodes[i]);
          if (typeof tab === 'object' || typeof tab === 'number') {
            if ((typeof tab === 'object') ? (t === tab) : (i === tab)) {
              t.active = true;
              $tab.addClass('active');
            } else {
              t.active = false;
              $tab.removeClass('active');
            }
          }
        });
        if (typeof scope.tabactivate === 'function') {
          scope.tabactivate.call({}, tab);
        }
      };

      let animateHorizontalScroll = function (elem, stop) {
        let step = 25;
        let steps = duration / step;
        let start = elem.scrollLeft;
        let length = stop - start;
        let increment = length / steps;
        if (length !== 0) {
          let timer = setInterval(function () {
            if (((length > 0) && (elem.scrollLeft + increment >= stop)) ||
              ((length < 0) && (elem.scrollLeft + increment <= stop))) {
              elem.scrollLeft = stop;
              clearInterval(timer);
            } else {
              elem.scrollLeft += increment;
            }
          }, step);
          setTimeout(function () {
            clearInterval(timer);
          }, duration + 10);
        }
      };

      let slideIntoView = function () {
        let fieldsetContainer = document.querySelector('.np-tabs');
        let active = fieldsetContainer.querySelector('.np-tab.active');
        if (active) {
          let move = active.getBoundingClientRect().left - fieldsetContainer.getBoundingClientRect().left;
          fieldsetContainer.style.left = -move + 'px';
        }
      };

      scope.next = function () {
        let scrollContainer = element[0].querySelector('.md-virtual-repeat-scroller');
        let scroll = scrollContainer.scrollLeft;
        animateHorizontalScroll(scrollContainer, scroll + 180);
      };

      scope.back = function () {
        let scrollContainer = element[0].querySelector('.md-virtual-repeat-scroller');
        let scroll = scrollContainer.scrollLeft;
        animateHorizontalScroll(scrollContainer, scroll - 180);
      };

      scope.go = function (ev, tab) {
        let tabsContainer = element[0].querySelector('md-virtual-repeat-container');
        let scrollContainer = element[0].querySelector('.md-virtual-repeat-scroller');
        let scrollPos = scrollContainer.scrollLeft;
        let width = tabsContainer.clientWidth;
        let offset = ev.target.offsetLeft;
        let elemWidth = ev.target.offsetWidth;
        if (scrollPos > offset) {
          animateHorizontalScroll(scrollContainer, offset);
        } else if ((scrollPos + width) < (offset + elemWidth)) {
          animateHorizontalScroll(scrollContainer, offset - width + elemWidth);
        }

        let i = 0, child = ev.target;
        while ((child = child.previousElementSibling)) {
          i++;
        }

        activate(tab);

        slideIntoView();
      };

      activate(scope.tabs[0]);

      // recalc slide if window is resized
      addEventListener('resize', slideIntoView);

      // Hack to dected if scrollbars are added
      // Demo: http://jsfiddle.net/pFaSx/
      // Create an invisible iframe
      var iframe = document.createElement('iframe');
      iframe.id = "hacky-scrollbar-resize-listener";
      iframe.style.cssText = 'height: 0; background-color: transparent; margin: 0; padding: 0; overflow: hidden; border-width: 0; position: absolute; width: 100%;';

      // Register our event when the iframe loads
      iframe.onload = function() {
        // The trick here is that because this iframe has 100% width
        // it should fire a window resize event when anything causes it to
        // resize (even scrollbars on the outer document)
        iframe.contentWindow.addEventListener('resize', function() {
          try {
            var evt = document.createEvent('UIEvents');
            evt.initUIEvent('resize', true, false, window, 0);
            window.dispatchEvent(evt);
          } catch(e) {}
        });
      };

      // Stick the iframe somewhere out of the way
      document.body.appendChild(iframe);

    }
  };
};

module.exports = tabsDirective;
<|endoftext|>


<commit_before>import React from 'react';
import { FormattedMessage, injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import { push } from 'react-router-redux';
import AppBar from 'material-ui/AppBar';
import Drawer from 'material-ui/Drawer';
import IconButton from 'material-ui/IconButton';
import MenuItem from 'material-ui/MenuItem';
import Divider from 'material-ui/Divider';
import messages from '../../resources/messages';

const localMessages = {
  menuTitle: { id: 'topics.menu.title', defaultMessage: 'Topic Mapper' },
  home: { id: 'topics.menu.items.home', defaultMessage: 'Explore Topics' },
  createNewTopic: { id: 'topics.menu.items.newTopic', defaultMessage: 'Create a New Topic' },
};

class TopicsDrawer extends React.Component {

  constructor(props) {
    super(props);
    this.state = { open: false };
  }

  handleToggle = () => this.setState({ open: !this.state.open });

  close = () => this.setState({ open: false });

  render() {
    const { user, handleMenuItemClick } = this.props;
    const { formatMessage } = this.props.intl;
    // gotta show extra login invivation based on the user state
    let logoutMenuItem = null;
    if (!user.isLoggedIn) {
      logoutMenuItem = (
        <MenuItem onTouchTap={() => { this.close(); handleMenuItemClick('/login'); }}>
          <FormattedMessage {...messages.userLogin} />
        </MenuItem>
      );
    }
    // only show app actions if they are logged in
    let appMenuItems = null;
    if (user.isLoggedIn) {
      appMenuItems = (

          <MenuItem onTouchTap={() => { this.close(); handleMenuItemClick('/home'); }}>
            <FormattedMessage {...localMessages.home} />
          </MenuItem>
          <MenuItem onTouchTap={() => { this.close(); handleMenuItemClick('/topics/create'); }}>
            <FormattedMessage {...localMessages.createNewTopic} />
          </MenuItem>

      );
    }
    // bring it all together in a controlled drawer
    return (

        <IconButton
          iconClassName="material-icons"
          tooltip={formatMessage(messages.menuOpenTooltip)}
          onTouchTap={this.handleToggle}
          style={{ padding: 8, height: 40, width: 40 }}
        >
          menu
        </IconButton>
        <Drawer
          width={300}
          openSecondary
          docked={false}
          open={this.state.open}
          onRequestChange={open => this.setState({ open })}
        >
          <AppBar title={formatMessage(localMessages.menuTitle)} showMenuIconButton={false} />
          {appMenuItems}
          <Divider />
          <MenuItem onTouchTap={() => { this.close(); handleMenuItemClick('/about'); }}>
            <FormattedMessage {...messages.menuAbout} />
          </MenuItem>
          {logoutMenuItem}
        </Drawer>

    );
  }
}

TopicsDrawer.propTypes = {
  // state
  user: React.PropTypes.object.isRequired,
  // from dispatch
  handleMenuItemClick: React.PropTypes.func.isRequired,
  // from context
  intl: React.PropTypes.object.isRequired,
};

const mapStateToProps = state => ({
  user: state.user,
});

const mapDispatchToProps = dispatch => ({
  handleMenuItemClick: (path) => {
    dispatch(push(path));
  },
});

export default
  injectIntl(
    connect(mapStateToProps, mapDispatchToProps)(
      TopicsDrawer
    )
  );
<commit_msg>rename explore to home<commit_after>import React from 'react';
import { FormattedMessage, injectIntl } from 'react-intl';
import { connect } from 'react-redux';
import { push } from 'react-router-redux';
import AppBar from 'material-ui/AppBar';
import Drawer from 'material-ui/Drawer';
import IconButton from 'material-ui/IconButton';
import MenuItem from 'material-ui/MenuItem';
import Divider from 'material-ui/Divider';
import messages from '../../resources/messages';

const localMessages = {
  menuTitle: { id: 'topics.menu.title', defaultMessage: 'Topic Mapper' },
  home: { id: 'topics.menu.items.home', defaultMessage: 'Home' },
  createNewTopic: { id: 'topics.menu.items.newTopic', defaultMessage: 'Create a New Topic' },
};

class TopicsDrawer extends React.Component {

  constructor(props) {
    super(props);
    this.state = { open: false };
  }

  handleToggle = () => this.setState({ open: !this.state.open });

  close = () => this.setState({ open: false });

  render() {
    const { user, handleMenuItemClick } = this.props;
    const { formatMessage } = this.props.intl;
    // gotta show extra login invivation based on the user state
    let logoutMenuItem = null;
    if (!user.isLoggedIn) {
      logoutMenuItem = (
        <MenuItem onTouchTap={() => { this.close(); handleMenuItemClick('/login'); }}>
          <FormattedMessage {...messages.userLogin} />
        </MenuItem>
      );
    }
    // only show app actions if they are logged in
    let appMenuItems = null;
    if (user.isLoggedIn) {
      appMenuItems = (

          <MenuItem onTouchTap={() => { this.close(); handleMenuItemClick('/home'); }}>
            <FormattedMessage {...localMessages.home} />
          </MenuItem>
          <MenuItem onTouchTap={() => { this.close(); handleMenuItemClick('/topics/create'); }}>
            <FormattedMessage {...localMessages.createNewTopic} />
          </MenuItem>

      );
    }
    // bring it all together in a controlled drawer
    return (

        <IconButton
          iconClassName="material-icons"
          tooltip={formatMessage(messages.menuOpenTooltip)}
          onTouchTap={this.handleToggle}
          style={{ padding: 8, height: 40, width: 40 }}
        >
          menu
        </IconButton>
        <Drawer
          width={300}
          openSecondary
          docked={false}
          open={this.state.open}
          onRequestChange={open => this.setState({ open })}
        >
          <AppBar title={formatMessage(localMessages.menuTitle)} showMenuIconButton={false} />
          {appMenuItems}
          <Divider />
          <MenuItem onTouchTap={() => { this.close(); handleMenuItemClick('/about'); }}>
            <FormattedMessage {...messages.menuAbout} />
          </MenuItem>
          {logoutMenuItem}
        </Drawer>

    );
  }
}

TopicsDrawer.propTypes = {
  // state
  user: React.PropTypes.object.isRequired,
  // from dispatch
  handleMenuItemClick: React.PropTypes.func.isRequired,
  // from context
  intl: React.PropTypes.object.isRequired,
};

const mapStateToProps = state => ({
  user: state.user,
});

const mapDispatchToProps = dispatch => ({
  handleMenuItemClick: (path) => {
    dispatch(push(path));
  },
});

export default
  injectIntl(
    connect(mapStateToProps, mapDispatchToProps)(
      TopicsDrawer
    )
  );
<|endoftext|>


<commit_before>import React, { Component, Fragment } from 'react'
import Slider from 'react-slick'
import { remove } from 'lodash'

import api from 'api'
import styled from 'styled-components'
import {
  Box,
  Flex,
  Text,
  Heading,
  Button,
  Loading,
  theme
} from '@hackclub/design-system'

import CarouselProject from 'components/workshops/CarouselProject'
import CarouselSubmissionForm from 'components/workshops/CarouselSubmissionForm'

import 'slick-carousel/slick/slick.css'
import 'slick-carousel/slick/slick-theme.css'

const RehackSlider = styled(Slider)`
  align-self: stretch;
`

const CarouselOuter = styled(Flex).attrs({
  bg: 'smoke',
  pt: [2, 2, 3],
  pb: [3, 3, 4],
  flexDirection: 'column',
  align: 'center'
})``

const StaticWrapper = styled(Flex).attrs({ mb: [3, 3, 4], justify: 'center' })`
  align-self: stretch;
`

const SliderWrapper = styled(Box).attrs({ mb: [3, 3, 4] })`
  align-self: stretch;
`

const ShowAllProjects = styled(Text).attrs({
  fontSize: [1, 2, 3],
  mb: [3, null, 4],
  color: 'muted'
})`
  cursor: pointer;
`

const ShowAllGrid = styled(Flex).attrs({
  justify: 'center',
  flexDirection: 'row',
  wrap: true,
  mb: 1
})``

const LoadingWrapper = styled(Box)`
  position: relative;
  width: 140px;
  height: 140px;
`

const LoadingCarousel = () => (
  <Fragment>
    <Heading.h3>Loading Projects...</Heading.h3>
    <LoadingWrapper>
      <Loading />
    </LoadingWrapper>
  </Fragment>
)

const AllProjects = projects => (
  <ShowAllGrid>
    {projects.map(project => (
      <CarouselProject
        project={project}
        key={project.screenshot.id}
        m={[1, null, 2]}
      />
    ))}
  </ShowAllGrid>
)

const LiveProject = submissionProject => (
  <StaticWrapper>
    <CarouselProject liveFrame project={submissionProject} />
  </StaticWrapper>
)

const EmptyProject = emptyProject => (
  <StaticWrapper>
    <CarouselProject project={emptyProject} />
  </StaticWrapper>
)

const ShortList = projects => (
  <StaticWrapper>
    <CarouselProject project={emptyProject} />
  </StaticWrapper>
)

const sliderSettings = {
  arrows: false,
  speed: 500,
  autoplay: true,
  initialSlide: 0,
  autoplaySpeed: 5000,
  slidesToShow: 1,
  slidesToScroll: 1,
  centerMode: true,
  pauseOnHover: true,
  variableWidth: true,
  focusOnSelect: true
}

const SliderList = projects => (
  <SliderWrapper>
    <RehackSlider {...sliderSettings}>
      {projects.map(project => (
        <CarouselProject project={project} key={project.screenshot.id} />
      ))}
    </RehackSlider>
  </SliderWrapper>
)

const LoadedCarousel = (
  projects,
  projectCount,
  showAll,
  onClickShowAll,
  liveFrameStatus,
  submissionProject,
  emptyProject
) => (
  <Fragment>
    <Heading.h3>
      {projectCount} Rehack
      {projectCount != 1 && 's'}
    </Heading.h3>
    <ShowAllProjects onClick={onClickShowAll} mb={[2, 2, 3]}>
      {showAll ? 'ok stack them back up' : 'Show All'}
    </ShowAllProjects>
    {showAll
      ? AllProjects(projects)
      : liveFrameStatus != 'empty'
        ? LiveProject(submissionProject)
        : projects.length == 0
          ? EmptyProject(emptyProject)
          : projects.length < 3
            ? ShortList(projects)
            : SliderList(projects)}
  </Fragment>
)

class Carousel extends Component {
  state = {
    loading: true,
    submitting: false,
    submissionData: {
      liveUrl: '',
      codeUrl: ''
    },
    authed: false,
    authData: {},
    liveFrameStatus: 'empty',
    liveFrameImage: null,
    showAll: false,
    original: this.emptyProject,
    projects: []
  }
  emptyProject = {
    user: { username: '' },
    empty: true,
    live_url: '',
    code_url: '',
    screenshot: {
      id: -1,
      created_at: '',
      updated_at: '',
      type: '',
      file_path: ''
    }
  }

  componentDidMount() {
    const { slug } = this.props

    api.get(`v1/workshops/${slug}/projects`).then(projects => {
      let original = projects.find(
        project => project.user && project.user.username == 'prophetorpheus'
      )
      if (original) {
        remove(projects, original)
        projects.unshift(original)
      } else original = this.emptyProject

      this.setState({
        loading: false,
        original,
        projects
      })
    })

    api
      .get(`v1/users/current`)
      .then(authData => {
        console.log(
          `User is authorized! Auth data: ${JSON.stringify(authData)}`
        )
        this.setState({ authed: true, authData })
      })
      .catch(error => {
        console.log(`User is not authorized! Error: ${JSON.stringify(error)}`)
        this.setState({ authed: false, authData: {} })
      })
  }

  setLiveFrameStatus(liveFrameStatus) {
    this.setState({ liveFrameStatus })
  }

  setSubmissionData = submissionData => {
    // Disable "Live Frame" feature for now
    // this.setLiveFrameStatus(submissionData.liveUrl == '' ? 'empty' : 'loading')
    this.setState({ submissionData })
  }

  onClickSubmitButton = () => {
    this.setState({ submitting: true })
  }

  onClickShowAll = () => {
    this.setState({ showAll: !this.state.showAll })
  }

  onSignOut = () => {
    console.log('Signing out')
    this.setState({ authed: false, authData: {} })
  }

  render() {
    const { slug } = this.props
    const {
      loading,
      original,
      projects,
      submitting,
      submissionData,
      authed,
      authData,
      liveFrameStatus,
      // liveFrameImage,
      showAll
    } = this.state

    const {
      setSubmissionData,
      onClickSubmitButton,
      onClickShowAll,
      onSignOut,
      emptyProject
    } = this

    const submissionProject = {
      user: authData,
      live_url: submissionData.liveUrl,
      code_url: submissionData.codeUrl,
      screenshot: {}
    }

    const projectCount = projects.length - (original ? 0 : 1)

    return (
      <CarouselOuter>
        {loading
          ? LoadingCarousel()
          : LoadedCarousel(
              projects,
              projectCount,
              showAll,
              onClickShowAll,
              liveFrameStatus,
              submissionProject,
              emptyProject
            )}

        {submitting ? (
          <CarouselSubmissionForm
            authed={authed}
            authData={authData}
            onSignOut={onSignOut}
            workshopSlug={slug}
            submissionData={submissionData}
            setSubmissionData={setSubmissionData}
          />
        ) : (
          <Button
            px={[3, null, 4]}
            py={[2, null, 3]}
            scale
            fontSize={4}
            onClick={onClickSubmitButton}
          >
            I made something
          </Button>
        )}
      </CarouselOuter>
    )
  }
}

export default Carousel
<commit_msg>Fix broken ShortList component<commit_after>import React, { Component, Fragment } from 'react'
import Slider from 'react-slick'
import { remove } from 'lodash'

import api from 'api'
import styled from 'styled-components'
import {
  Box,
  Flex,
  Text,
  Heading,
  Button,
  Loading,
  theme
} from '@hackclub/design-system'

import CarouselProject from 'components/workshops/CarouselProject'
import CarouselSubmissionForm from 'components/workshops/CarouselSubmissionForm'

import 'slick-carousel/slick/slick.css'
import 'slick-carousel/slick/slick-theme.css'

const RehackSlider = styled(Slider)`
  align-self: stretch;
`

const CarouselOuter = styled(Flex).attrs({
  bg: 'smoke',
  pt: [2, 2, 3],
  pb: [3, 3, 4],
  flexDirection: 'column',
  align: 'center'
})``

const StaticWrapper = styled(Flex).attrs({ mb: [3, 3, 4], justify: 'center' })`
  align-self: stretch;
`

const SliderWrapper = styled(Box).attrs({ mb: [3, 3, 4] })`
  align-self: stretch;
`

const ShowAllProjects = styled(Text).attrs({
  fontSize: [1, 2, 3],
  mb: [3, null, 4],
  color: 'muted'
})`
  cursor: pointer;
`

const ShowAllGrid = styled(Flex).attrs({
  justify: 'center',
  flexDirection: 'row',
  wrap: true,
  mb: 1
})``

const LoadingWrapper = styled(Box)`
  position: relative;
  width: 140px;
  height: 140px;
`

const LoadingCarousel = () => (
  <Fragment>
    <Heading.h3>Loading Projects...</Heading.h3>
    <LoadingWrapper>
      <Loading />
    </LoadingWrapper>
  </Fragment>
)

const AllProjects = projects => (
  <ShowAllGrid>
    {projects.map(project => (
      <CarouselProject
        project={project}
        key={project.screenshot.id}
        m={[1, null, 2]}
      />
    ))}
  </ShowAllGrid>
)

const LiveProject = submissionProject => (
  <StaticWrapper>
    <CarouselProject liveFrame project={submissionProject} />
  </StaticWrapper>
)

const EmptyProject = emptyProject => (
  <StaticWrapper>
    <CarouselProject project={emptyProject} />
  </StaticWrapper>
)

const ShortList = projects => (
  <StaticWrapper>
    {projects.map(project => (
      <CarouselProject project={project} key={project.screenshot.id} />
    ))}
  </StaticWrapper>
)

const sliderSettings = {
  arrows: false,
  speed: 500,
  autoplay: true,
  initialSlide: 0,
  autoplaySpeed: 5000,
  slidesToShow: 1,
  slidesToScroll: 1,
  centerMode: true,
  pauseOnHover: true,
  variableWidth: true,
  focusOnSelect: true
}

const SliderList = projects => (
  <SliderWrapper>
    <RehackSlider {...sliderSettings}>
      {projects.map(project => (
        <CarouselProject project={project} key={project.screenshot.id} />
      ))}
    </RehackSlider>
  </SliderWrapper>
)

const LoadedCarousel = (
  projects,
  projectCount,
  showAll,
  onClickShowAll,
  liveFrameStatus,
  submissionProject,
  emptyProject
) => (
  <Fragment>
    <Heading.h3>
      {projectCount} Rehack
      {projectCount != 1 && 's'}
    </Heading.h3>
    <ShowAllProjects onClick={onClickShowAll} mb={[2, 2, 3]}>
      {showAll ? 'ok stack them back up' : 'Show All'}
    </ShowAllProjects>
    {showAll
      ? AllProjects(projects)
      : liveFrameStatus != 'empty'
        ? LiveProject(submissionProject)
        : projects.length == 0
          ? EmptyProject(emptyProject)
          : projects.length < 3
            ? ShortList(projects)
            : SliderList(projects)}
  </Fragment>
)

class Carousel extends Component {
  state = {
    loading: true,
    submitting: false,
    submissionData: {
      liveUrl: '',
      codeUrl: ''
    },
    authed: false,
    authData: {},
    liveFrameStatus: 'empty',
    liveFrameImage: null,
    showAll: false,
    original: this.emptyProject,
    projects: []
  }
  emptyProject = {
    user: { username: '' },
    empty: true,
    live_url: '',
    code_url: '',
    screenshot: {
      id: -1,
      created_at: '',
      updated_at: '',
      type: '',
      file_path: ''
    }
  }

  componentDidMount() {
    const { slug } = this.props

    api.get(`v1/workshops/${slug}/projects`).then(projects => {
      let original = projects.find(
        project => project.user && project.user.username == 'prophetorpheus'
      )
      if (original) {
        remove(projects, original)
        projects.unshift(original)
      } else original = this.emptyProject

      this.setState({
        loading: false,
        original,
        projects
      })
    })

    api
      .get(`v1/users/current`)
      .then(authData => {
        console.log(
          `User is authorized! Auth data: ${JSON.stringify(authData)}`
        )
        this.setState({ authed: true, authData })
      })
      .catch(error => {
        console.log(`User is not authorized! Error: ${JSON.stringify(error)}`)
        this.setState({ authed: false, authData: {} })
      })
  }

  setLiveFrameStatus(liveFrameStatus) {
    this.setState({ liveFrameStatus })
  }

  setSubmissionData = submissionData => {
    // Disable "Live Frame" feature for now
    // this.setLiveFrameStatus(submissionData.liveUrl == '' ? 'empty' : 'loading')
    this.setState({ submissionData })
  }

  onClickSubmitButton = () => {
    this.setState({ submitting: true })
  }

  onClickShowAll = () => {
    this.setState({ showAll: !this.state.showAll })
  }

  onSignOut = () => {
    console.log('Signing out')
    this.setState({ authed: false, authData: {} })
  }

  render() {
    const { slug } = this.props
    const {
      loading,
      original,
      projects,
      submitting,
      submissionData,
      authed,
      authData,
      liveFrameStatus,
      // liveFrameImage,
      showAll
    } = this.state

    const {
      setSubmissionData,
      onClickSubmitButton,
      onClickShowAll,
      onSignOut,
      emptyProject
    } = this

    const submissionProject = {
      user: authData,
      live_url: submissionData.liveUrl,
      code_url: submissionData.codeUrl,
      screenshot: {}
    }

    const projectCount = projects.length - (original ? 0 : 1)

    return (
      <CarouselOuter>
        {loading
          ? LoadingCarousel()
          : LoadedCarousel(
              projects,
              projectCount,
              showAll,
              onClickShowAll,
              liveFrameStatus,
              submissionProject,
              emptyProject
            )}

        {submitting ? (
          <CarouselSubmissionForm
            authed={authed}
            authData={authData}
            onSignOut={onSignOut}
            workshopSlug={slug}
            submissionData={submissionData}
            setSubmissionData={setSubmissionData}
          />
        ) : (
          <Button
            px={[3, null, 4]}
            py={[2, null, 3]}
            scale
            fontSize={4}
            onClick={onClickSubmitButton}
          >
            I made something
          </Button>
        )}
      </CarouselOuter>
    )
  }
}

export default Carousel
<|endoftext|>


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

import axios from 'axios'

class ConfigManager {

    constructor() {
    }

    /**
     * get configurations from server: deployment.yaml
     * @returns {Object}: configuration object
     */
    static getConfigs() {
        return {
            'environments': ConfigRequestMethods.promised_environments()
        };
    }
}

/**
 * @type {{ConstPath: StringPath}} ConfigRequestPaths: Configuration requesting url paths
 */
const ConfigRequestPaths = {
    ENVIRONMENT_CONFIG_PATH: "/configService/environments"
};

/**
 * @type {{'ConstRequestString': (function Request_Method())}} ConfigRequestMethods: Configuration requesting methods
 */
const ConfigRequestMethods = {
    promised_environments() {
        if (ConfigManager._promised_environments) {
            return ConfigManager._promised_environments;
        }
        let origin = window.location.origin;
        let requestUrl = origin + ConfigRequestPaths.ENVIRONMENT_CONFIG_PATH;

        ConfigManager._promised_environments = axios.get(requestUrl);
        return ConfigManager._promised_environments;
    }
};

//List of private promised class variables to preserve single instance
ConfigManager._promised_environments = null;

export default ConfigManager;<commit_msg>Refactor configManager.js in publisher<commit_after>/*
 * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import axios from 'axios'

class ConfigManager {

    /**
     * get promised config and update the configMap
     * @param configPath: Path to read configs from
     * @returns {Promise Object}: promised config
     * @private
     */
    static _getPromisedConfigs(configPath) {
        let promisedConfig = ConfigManager._promisedConfigMap.get(configPath);
        if (promisedConfig) {
            return promisedConfig;
        }
        let origin = window.location.origin;
        let requestUrl = origin + configPath;

        promisedConfig = axios.get(requestUrl);
        ConfigManager._promisedConfigMap.set(configPath, promisedConfig);
        return promisedConfig;
    }

    /**
     * get configurations from server: deployment.yaml
     * @returns {Object}: configuration object
     */
    static getConfigs() {
        return {
            'environments': ConfigManager._getPromisedConfigs(ConfigRequestPaths.ENVIRONMENT_CONFIG_PATH)
        };
    }
}

/**
 * @type {{ConstPath: StringPath}} ConfigRequestPaths: Configuration requesting url paths
 */
const ConfigRequestPaths = {
    ENVIRONMENT_CONFIG_PATH: "/configService/environments"
};

/**
 * The map of single instance promised configs objects
 * {key}: ConfigRequestPaths
 * {value}: promised config
 * @type {Map}
 * @private
 */
ConfigManager._promisedConfigMap = new Map();

export default ConfigManager;<|endoftext|>


<commit_before>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { observable } from 'mobx';
import { observer } from 'mobx-react';
import routerStore from 'stores/routerStore';
import authStore from 'stores/authStore';
import DataStore from 'stores/DataStore';
import { Router } from 'react-router-mobx';
import DocumentTitle from 'react-document-title';
import { initModals } from './initialize';

const styles = {
	container: {
		height: '100%',
	},
};

@observer
export default class AdminContext extends Component {
	static propTypes = {
		appConfig: PropTypes.object,
	};

	static childContextTypes = {
		appConfig: PropTypes.object,
		authStore: PropTypes.object,
		DataStore: PropTypes.func,
	};

	getChildContext() {
		return {
			appConfig: this._appConfig,
			authStore,
			DataStore,
		};
	}

	componentWillMount() {
		this._appConfig = observable(this.props.appConfig);
		authStore.set(this._appConfig);
		DataStore.setup(this._appConfig, authStore);
		initModals(this._appConfig.modals);
	}

	componentWillReceiveProps({ appConfig }) {
		// this._appConfig = appConfig;
		Object.assign(this._appConfig, appConfig);
		authStore.set(this._appConfig);
		// DataStore.hotUpdate(appConfig);
	}

	render() {
		const {
			props: {
				appConfig: { title, navigator: { frame: Frame, login: Login, routes } },
			},
		} = this;

		const handleEnter = async (nextState, replace, next) => {
			const { pathname, search } = nextState.location;
			const isOk = await authStore.auth();
			if (!isOk) {
				replace({
					pathname: '/login',
					query: {
						ref: pathname + search,
					},
				});
			}
			next();
		};
		return (
			<DocumentTitle title={title}>
				<Router component={BrowserRouter} routerStore={routerStore}>

						<Switch>
							<Route path="/login" component={Login} />
							<Route
								onEnter={handleEnter}
								render={() => <Frame>{routes}</Frame>}
							/>
						</Switch>

				</Router>
			</DocumentTitle>
		);
	}
}
<commit_msg>fix route update blocked bug<commit_after>import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { BrowserRouter, Route, Switch } from 'react-router-dom';
import { observable } from 'mobx';
import { observer } from 'mobx-react';
import routerStore from 'stores/routerStore';
import authStore from 'stores/authStore';
import DataStore from 'stores/DataStore';
import { Router } from 'react-router-mobx';
import DocumentTitle from 'react-document-title';
import { initModals } from './initialize';

const styles = {
	container: {
		height: '100%',
	},
};

@observer
export default class AdminContext extends Component {
	static propTypes = {
		appConfig: PropTypes.object,
	};

	static childContextTypes = {
		appConfig: PropTypes.object,
		authStore: PropTypes.object,
		DataStore: PropTypes.func,
	};

	getChildContext() {
		return {
			appConfig: this._appConfig,
			authStore,
			DataStore,
		};
	}

	componentWillMount() {
		this._appConfig = observable(this.props.appConfig);
		authStore.set(this._appConfig);
		DataStore.setup(this._appConfig, authStore);
		initModals(this._appConfig.modals);
	}

	componentWillReceiveProps({ appConfig }) {
		// this._appConfig = appConfig;
		Object.assign(this._appConfig, appConfig);
		authStore.set(this._appConfig);
		// DataStore.hotUpdate(appConfig);
	}

	render() {
		const {
			title,
			navigator: { frame: Frame, login: Login, routes },
		} = this.props.appConfig;

		const handleEnter = async (nextState, replace, next) => {
			const { pathname, search } = nextState.location;
			const isOk = await authStore.auth();
			if (!isOk) {
				replace({
					pathname: '/login',
					query: {
						ref: pathname + search,
					},
				});
			}
			next();
		};
		return (
			<DocumentTitle title={title}>
				<Router component={BrowserRouter} routerStore={routerStore}>

						<Switch>
							<Route path="/login" component={Login} />
							<Route
								onEnter={handleEnter}
								render={({ location }) => (
									<Frame locKey={location.key}>{routes}</Frame>
								)}
							/>
						</Switch>

				</Router>
			</DocumentTitle>
		);
	}
}
<|endoftext|>


<commit_before>
var modules;
var allResults = [];
var current;

registerEnvironment = function(name, mod) {
  environment = name;
  modules = mod;
}

startTests = function() {
  trigger('suite.started', [environment, modules]);
  nextModule();
}

testsFinishedCallback = function(r, time) {
  if(!current) console.info(r, time);
  var data = { module: current.name, results: r, time: time };
  trigger('suite.module_finished', data);
  allResults.push(data);
  nextModule();
}

var nextModule = function() {
  current = modules.shift();
  if(current) {
    loadScripts(current);
  } else {
    modulesFinished();
  }
}

var modulesFinished = function() {
  trigger('suite.finished', [environment, allResults]);
}

var loadScripts = function(module) {
  var loaded = 0, i;
  for(i = 0; i < module.tests.length; i++){
    jQuery.getScript((module.path || '') + module.tests[i], function(){
      loaded++;
      if(loaded == module.tests.length){
        syncTestsFinished();
      }
    });
  }
}

var trigger = function(name, data) {
  var win = getWindow();
  win.jQuery(win.document).trigger(name, data);
}

var getWindow = function() {
  return window.parent !== window && window.parent.jQuery ? window.parent : window;
}
<commit_msg>jQuery getScript not working for some reason<commit_after>
var modules;
var allResults = [];
var current;

registerEnvironment = function(name, mod) {
  environment = name;
  modules = mod;
}

startTests = function() {
  trigger('suite.started', [environment, modules]);
  nextModule();
}

testsFinishedCallback = function(r, time) {
  if(!current) console.info(r, time);
  var data = { module: current.name, results: r, time: time };
  trigger('suite.module_finished', data);
  allResults.push(data);
  nextModule();
}

var nextModule = function() {
  current = modules.shift();
  if(current) {
    loadScripts(current);
  } else {
    modulesFinished();
  }
}

var modulesFinished = function() {
  trigger('suite.finished', [environment, allResults]);
}

var loadScripts = function(module) {
  var loaded = 0, i;
  for(i = 0; i < module.tests.length; i++){
    var script = document.createElement('script');
    script.src = (module.path || '') + module.tests[i];
    script.type = 'text/javascript';
    script.async = true;
    script.onload = function(){
      loaded++;
      if(loaded == module.tests.length){
        syncTestsFinished();
      }
    };
    document.getElementsByTagName('head')[0].appendChild(script);
    /* DUNNO why but this doesn't work in FF2
    jQuery.getScript((module.path || '') + module.tests[i], function(){
      loaded++;
      if(loaded == module.tests.length){
        syncTestsFinished();
      }
    });
    */
  }
}

var trigger = function(name, data) {
  var win = getWindow();
  win.jQuery(win.document).trigger(name, data);
}

var getWindow = function() {
  return window.parent !== window && window.parent.jQuery ? window.parent : window;
}
<|endoftext|>


<commit_before>/*eslint dot-notation: "off"*/
/*eslint no-shadow: "off"*/

import {replace, when} from '../../../../test-helper';

describe.only('the products controller', () => {
  let productsService;
  let ProductsController;
  let productsController;

  let makeProductsControllerWith = (productsService) => {
    ProductsController = require('./products.controller')['ProductsController'];
    productsController = new ProductsController(productsService);
  };

  beforeEach(() => {
    productsService = require('./service/products-service').productsService();
    replace(productsService, 'fetch');
    when(productsService.fetch()).thenResolve(['products']);
  });

  it('has no products', () => {
    makeProductsControllerWith(productsService);
    productsController.products.should.deepEqual([]);
  });

  it('fetches products from the server', () => {
    ProductsController = require('./products.controller')['ProductsController'];
    productsController = new ProductsController(productsService);

    return productsController.fetch().then(() => {
      productsController.products.should.deepEqual(['products']);
    });
  });
});

<commit_msg>refactor: call extracted method<commit_after>/*eslint dot-notation: "off"*/
/*eslint no-shadow: "off"*/

import {replace, when} from '../../../../test-helper';

describe.only('the products controller', () => {
  let productsService;
  let ProductsController;
  let productsController;

  let makeProductsControllerWith = (productsService) => {
    ProductsController = require('./products.controller')['ProductsController'];
    return new ProductsController(productsService);
  };

  beforeEach(() => {
    productsService = require('./service/products-service').productsService();
    replace(productsService, 'fetch');
    when(productsService.fetch()).thenResolve(['products']);
  });

  it('has no products', () => {
    productsController = makeProductsControllerWith(productsService);
    productsController.products.should.deepEqual([]);
  });

  it('fetches products from the server', () => {
    productsController = makeProductsControllerWith(productsService);

    return productsController.fetch().then(() => {
      productsController.products.should.deepEqual(['products']);
    });
  });
});

<|endoftext|>


<commit_before>var Class = require('../../utils/Class');
var BaseSound = require('../BaseSound');
var HTML5AudioSound = new Class({
    Extends: BaseSound,
    initialize: function HTML5AudioSound(manager, key, config) {
        if (config === void 0) { config = {}; }
        BaseSound.call(this, manager, key, config);
    }
});
module.exports = HTML5AudioSound;
<commit_msg>Initializing audio property<commit_after>var Class = require('../../utils/Class');
var BaseSound = require('../BaseSound');
var HTML5AudioSound = new Class({
    Extends: BaseSound,
    initialize: function HTML5AudioSound(manager, key, config) {
        if (config === void 0) { config = {}; }
        this.audio = null;
        BaseSound.call(this, manager, key, config);
    }
});
module.exports = HTML5AudioSound;
<|endoftext|>


<commit_before>/**
 * Created by michal frystacky on 11/27/16.
 */
// @Ref: https://blog.risingstack.com/the-react-way-getting-started-tutorial/
// @Ref: https://www.codementor.io/tamizhvendan/tutorials/beginner-guide-setup-reactjs-environment-npm-babel-6-webpack-du107r9zr
// @Ref: http://survivejs.com/webpack/advanced-techniques/configuring-react/

module.exports = {
    entry: `${__dirname}/src/app.js`,
    output: {
        path: `${__dirname}/dist`,
        filename: 'bundle.js'
    },
    module: {
        loaders: [
            {
                test: /\.js$/,
                exclude: /(node_modules|bower_components)/,
                loader: 'babel',
                query: {
                    presets: ['es2015']
                }
            }
        ]
    }
}<commit_msg>Chore: Updated paths and entry and output for webpack<commit_after>/**
 * Created by michal frystacky on 11/27/16.
 */
// @Ref: https://blog.risingstack.com/the-react-way-getting-started-tutorial/
// @Ref: https://www.codementor.io/tamizhvendan/tutorials/beginner-guide-setup-reactjs-environment-npm-babel-6-webpack-du107r9zr
// @Ref: http://survivejs.com/webpack/advanced-techniques/configuring-react/

module.exports = {
    entry: `${__dirname}/static/js/restaurant.jsx`,
    output: {
        path: `${__dirname}/static/js`,
        filename: 'restaurants.js'
    },
    module: {
        loaders: [
            {
                test: /\.jsx$/,
                exclude: /(node_modules|bower_components)/,
                loader: 'babel',
                query: {
                    presets: ['es2015', 'react']
                }
            }
        ]
    }
}<|endoftext|>


<commit_before>// Copyright 2011 Google Inc. All Rights Reserved.

/**
 * @fileoverview Options page for Chrome OS CIN.
 * @author zork@google.com (Zach Kuznia)
 */

var kTableLoading = "loading";
var kTableMetadataKey = "table_metadata";
var kTableDataKeyPrefix = "table_data-";

function init() {
  writeLocalStorage(kTableLoading, {});
  loadCinTables();
  document.getElementById("cin_table_file_input").addEventListener(
      'change', addTableFile, false);
}

function onAddTableUrl() {
  var url = document.getElementById("cin_table_url_input").value;
  addTableUrl(url);
}

function addTableUrl(url) {
  if (url.replace(/^\s+|s+$/g, "") == "") {
    setAddUrlStatus("URL is empty", true);
    return;
  }

  var table_metadata = readLocalStorage(kTableMetadataKey, {});
  var table_loading = readLocalStorage(kTableLoading, {});

  if (table_loading[url]) {
    setAddUrlStatus("Table is loading", false);
  } else {
    // Write a placeholder value.
    table_loading[url] = true;
    writeLocalStorage(kTableLoading, table_loading);

    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function () {
      if (this.readyState == 4) {
        if (this.status == 200) {
          // Parse the entry
          var parsed_result = parseCin(this.responseText);
          if (parsed_result[0]) {
            var parsed_data = parsed_result[1];
            parsed_data.metadata.url = url;
            if (addCinTable(parsed_data)) {
              // Update the UI
              addCinTableToTable(parsed_data.metadata.ename, url);
              setAddUrlStatus("OK", false);
            } else {
              setAddUrlStatus("Table not added", true);
            }
          } else {
            var msg = parsed_result[1];
            // Update the UI
            setAddUrlStatus("Could not parse cin file. " + msg, true);
          }
        } else {
          // Update the UI
          setAddUrlStatus("Could not read url.  Server returned " + this.status,
                          true);
        }
        table_loading = readLocalStorage(kTableLoading, {});
        delete table_loading[url];
        writeLocalStorage(kTableLoading, table_loading);
      }
    }
    xhr.open("GET", url, true);
    xhr.send(null);
  }
}

function addTableFile(evt) {
  var files = evt.target.files;

  for (var i = 0, file; file = files[i]; i++) {
    var reader = new FileReader();

    reader.onload = (function(file_data) {
      return function(e) {
        // Parse the entry
        var parsed_result = parseCin(e.target.result);
        if (parsed_result[0]) {
          var parsed_data = parsed_result[1];
          if (addCinTable(parsed_data)) {
            // Update the UI
            addCinTableToTable(parsed_data.metadata.ename);
            setAddFileStatus("OK", false);
          } else {
            setAddFileStatus("Table not added", true);
          }
        } else {
          var msg = parsed_result[1];
          // Update the UI
          setAddFileStatus("Could not parse cin file. " + msg, true);
        }
      };
    })(file);

    reader.readAsDataURL(file);
  }
}

function addCinTable(data) {
  // Update the entry in localStorage
  var table_metadata = readLocalStorage(kTableMetadataKey, {});

  if (table_metadata[data.metadata.ename] != undefined) {
    if (!confirm("Do you wish to overwrite " + data.metadata.ename + "?")) {
      return false;
    } else {
      removeCinTableFromTable(data.metadata.ename);
    }
  }
  table_metadata[data.metadata.ename] = data.metadata;
  writeLocalStorage(kTableMetadataKey, table_metadata);
  writeLocalStorage(kTableDataKeyPrefix + data.metadata.ename, data.data);
  return true;
}

function deleteCinTable(name) {
  var table_metadata = readLocalStorage(kTableMetadataKey, {});
  delete table_metadata[name];
  deleteLocalStorage(kTableDataKeyPrefix + name);
  writeLocalStorage(kTableMetadataKey, table_metadata);
}

function setAddUrlStatus(status, error) {
  var status_field = document.getElementById("add_url_status");
  status_field.innerHTML = status;
  if (error) {
    status_field.className = "status_error";
  } else {
    status_field.className = "status_ok";
  }
}

function setAddFileStatus(status, error) {
  var status_field = document.getElementById("add_file_status");
  status_field.innerHTML = status;
  if (error) {
    status_field.className = "status_error";
  } else {
    status_field.className = "status_ok";
  }
}

function addCinTableToTable(name, url) {
  var table = document.getElementById("cin_table_table");

  var row = table.tBodies[0].insertRow(-1);
  var cell = row.insertCell(-1);
  cell.innerHTML = name;
  cell = row.insertCell(-1);
  if (url) {
    cell.innerHTML = url;
  }
  cell = row.insertCell(-1);
  var button = document.createElement('input');
  button.type = 'button';
  button.value = 'Remove';
  button.onclick = function () {
    deleteCinTable(name);
    table.tBodies[0].deleteRow(row.sectionRowIndex);
  }
  cell.appendChild(button);

  cell = row.insertCell(-1);
  if (url) {
    var reload_button = document.createElement('input');
    reload_button.type = 'button';
    reload_button.value = 'Reload';
    reload_button.onclick = function () {
      // dirty hack
      deleteCinTable(name);
      table.tBodies[0].deleteRow(row.sectionRowIndex);
      addTableUrl(url);
    }
    cell.appendChild(reload_button);
  }
}

function removeCinTableFromTable(name, url) {
  var table = document.getElementById("cin_table_table");

  for (var i = 0; i < table.tBodies[0].rows.length; i++) {
    var row = table.tBodies[0].rows[i];
    if (row.cells[0].innerHTML == name) {
      table.tBodies[0].deleteRow(i);
      return;
    }
  }
}

function loadCinTables() {
  var table_metadata = readLocalStorage(kTableMetadataKey);
  if (table_metadata) {
    for (table_name in table_metadata) {
      addCinTableToTable(table_name, table_metadata[table_name].url);
    }
  }
}

function readLocalStorage(key, default_value) {
  var data = localStorage[key];
  if (!data) {
    return default_value;
  }
  return JSON.parse(data);
}

function writeLocalStorage(key, data) {
  localStorage[key] = JSON.stringify(data);
}

function deleteLocalStorage(key) {
  delete localStorage[key];
}
<commit_msg>Read files as text<commit_after>// Copyright 2011 Google Inc. All Rights Reserved.

/**
 * @fileoverview Options page for Chrome OS CIN.
 * @author zork@google.com (Zach Kuznia)
 */

var kTableLoading = "loading";
var kTableMetadataKey = "table_metadata";
var kTableDataKeyPrefix = "table_data-";

function init() {
  writeLocalStorage(kTableLoading, {});
  loadCinTables();
  document.getElementById("cin_table_file_input").addEventListener(
      'change', addTableFile, false);
}

function onAddTableUrl() {
  var url = document.getElementById("cin_table_url_input").value;
  addTableUrl(url);
}

function addTableUrl(url) {
  if (url.replace(/^\s+|s+$/g, "") == "") {
    setAddUrlStatus("URL is empty", true);
    return;
  }

  var table_metadata = readLocalStorage(kTableMetadataKey, {});
  var table_loading = readLocalStorage(kTableLoading, {});

  if (table_loading[url]) {
    setAddUrlStatus("Table is loading", false);
  } else {
    // Write a placeholder value.
    table_loading[url] = true;
    writeLocalStorage(kTableLoading, table_loading);

    var xhr = new XMLHttpRequest();
    xhr.onreadystatechange = function () {
      if (this.readyState == 4) {
        if (this.status == 200) {
          // Parse the entry
          var parsed_result = parseCin(this.responseText);
          if (parsed_result[0]) {
            var parsed_data = parsed_result[1];
            parsed_data.metadata.url = url;
            if (addCinTable(parsed_data)) {
              // Update the UI
              addCinTableToTable(parsed_data.metadata.ename, url);
              setAddUrlStatus("OK", false);
            } else {
              setAddUrlStatus("Table not added", true);
            }
          } else {
            var msg = parsed_result[1];
            // Update the UI
            setAddUrlStatus("Could not parse cin file. " + msg, true);
          }
        } else {
          // Update the UI
          setAddUrlStatus("Could not read url.  Server returned " + this.status,
                          true);
        }
        table_loading = readLocalStorage(kTableLoading, {});
        delete table_loading[url];
        writeLocalStorage(kTableLoading, table_loading);
      }
    }
    xhr.open("GET", url, true);
    xhr.send(null);
  }
}

function addTableFile(evt) {
  var files = evt.target.files;

  for (var i = 0, file; file = files[i]; i++) {
    var reader = new FileReader();

    reader.onload = (function(file_data) {
      return function(e) {
        // Parse the entry
        var parsed_result = parseCin(e.target.result);
        if (parsed_result[0]) {
          var parsed_data = parsed_result[1];
          if (addCinTable(parsed_data)) {
            // Update the UI
            addCinTableToTable(parsed_data.metadata.ename);
            setAddFileStatus("OK", false);
          } else {
            setAddFileStatus("Table not added", true);
          }
        } else {
          var msg = parsed_result[1];
          // Update the UI
          setAddFileStatus("Could not parse cin file. " + msg, true);
        }
      };
    })(file);

    reader.readAsText(file);
  }
}

function addCinTable(data) {
  // Update the entry in localStorage
  var table_metadata = readLocalStorage(kTableMetadataKey, {});

  if (table_metadata[data.metadata.ename] != undefined) {
    if (!confirm("Do you wish to overwrite " + data.metadata.ename + "?")) {
      return false;
    } else {
      removeCinTableFromTable(data.metadata.ename);
    }
  }
  table_metadata[data.metadata.ename] = data.metadata;
  writeLocalStorage(kTableMetadataKey, table_metadata);
  writeLocalStorage(kTableDataKeyPrefix + data.metadata.ename, data.data);
  return true;
}

function deleteCinTable(name) {
  var table_metadata = readLocalStorage(kTableMetadataKey, {});
  delete table_metadata[name];
  deleteLocalStorage(kTableDataKeyPrefix + name);
  writeLocalStorage(kTableMetadataKey, table_metadata);
}

function setAddUrlStatus(status, error) {
  var status_field = document.getElementById("add_url_status");
  status_field.innerHTML = status;
  if (error) {
    status_field.className = "status_error";
  } else {
    status_field.className = "status_ok";
  }
}

function setAddFileStatus(status, error) {
  var status_field = document.getElementById("add_file_status");
  status_field.innerHTML = status;
  if (error) {
    status_field.className = "status_error";
  } else {
    status_field.className = "status_ok";
  }
}

function addCinTableToTable(name, url) {
  var table = document.getElementById("cin_table_table");

  var row = table.tBodies[0].insertRow(-1);
  var cell = row.insertCell(-1);
  cell.innerHTML = name;
  cell = row.insertCell(-1);
  if (url) {
    cell.innerHTML = url;
  }
  cell = row.insertCell(-1);
  var button = document.createElement('input');
  button.type = 'button';
  button.value = 'Remove';
  button.onclick = function () {
    deleteCinTable(name);
    table.tBodies[0].deleteRow(row.sectionRowIndex);
  }
  cell.appendChild(button);

  cell = row.insertCell(-1);
  if (url) {
    var reload_button = document.createElement('input');
    reload_button.type = 'button';
    reload_button.value = 'Reload';
    reload_button.onclick = function () {
      // dirty hack
      deleteCinTable(name);
      table.tBodies[0].deleteRow(row.sectionRowIndex);
      addTableUrl(url);
    }
    cell.appendChild(reload_button);
  }
}

function removeCinTableFromTable(name, url) {
  var table = document.getElementById("cin_table_table");

  for (var i = 0; i < table.tBodies[0].rows.length; i++) {
    var row = table.tBodies[0].rows[i];
    if (row.cells[0].innerHTML == name) {
      table.tBodies[0].deleteRow(i);
      return;
    }
  }
}

function loadCinTables() {
  var table_metadata = readLocalStorage(kTableMetadataKey);
  if (table_metadata) {
    for (table_name in table_metadata) {
      addCinTableToTable(table_name, table_metadata[table_name].url);
    }
  }
}

function readLocalStorage(key, default_value) {
  var data = localStorage[key];
  if (!data) {
    return default_value;
  }
  return JSON.parse(data);
}

function writeLocalStorage(key, data) {
  localStorage[key] = JSON.stringify(data);
}

function deleteLocalStorage(key) {
  delete localStorage[key];
}
<|endoftext|>


<commit_before>/* global jasmine */

/* istanbul ignore if */
if(typeof(jQuery) === "undefined"){
	throw("Unable to find jQuery");
}

/* istanbul ignore else */
if(typeof(window.jasmineTree) === "undefined"){
	window.jasmineTree = {};
}

(function(){
	"use strict";

	jasmineTree.version = "1.0";

	var CONST = {
		CSS_CLASSES: {
			SUMMARY: "jasmine-tree-summary",
			TRIGGER: "jasmine-tree-trigger",
			TOOLBAR: "jasmine-tree-toolbar",
			BUTTON: "jasmine-tree-button",
			NODE_OPENED: "jasmine-tree-opennode"
		},
		SELECTORS: {
			FIRST_CHILD: ":first-child",
			SUMMARY: ".summary,.jasmine-summary",
			ROOT_SUITE: ".summary > .suite,.jasmine-summary > .jasmine-suite",
			NODE_TITLE: "> li.suite-detail,> li.jasmine-suite-detail",
			NODE_SPECS: "> ul.specs,> ul.jasmine-specs",
			NODE_SUITES: "> ul.suite,> ul.jasmine-suite"
		},
		TEXT: {
			COLLAPSE: "Collapse All",
			EXPAND: "Expand All",
			SEPARATOR: " | ",
			PLUS: "+",
			MINUS: "-"
		},
		FILTER_REGEXP: (new RegExp("[?&]spec=([^&]*)"))
	};

	/** @type {Array.<jasmineTree.Suite>} */
	var rootSuites = [];

	/**
	 * Returns the value of the "spec" parameter in the querystring. Null if it's not specified
	 * @return {null|String}
	 */
	jasmineTree.getSpecFilter = function(){
		var match = CONST.FILTER_REGEXP.exec(window.location.search);
		if(match !== null) {
			var filter = decodeURIComponent(match[1].replace(/\+/g, " "));
			if(filter !== "") {
				return filter;
			}
		}
	};

	/**
	 * Collapse all the suites
	 */
	jasmineTree.collapseAll = function(){
		for(var i = 0; i < rootSuites.length; i++){
			rootSuites[i].collapse();
		}
	};

	/**
	 * Expand all the suites
	 */
	jasmineTree.expandAll = function(){
		for(var i = 0; i < rootSuites.length; i++){
			rootSuites[i].expand();
		}
	};

	/**
	 * Add a CSS class to the summary to enable more specific CSS selectors
	 */
	jasmineTree.addRootClass = function(){
		jQuery(CONST.SELECTORS.SUMMARY).addClass(CONST.CSS_CLASSES.SUMMARY);
	};

	/**
	 * Insert toolbar with expand/collapse all buttons
	 */
	jasmineTree.addToolbar = function(){

		var toolbar = jQuery("

").addClass(CONST.CSS_CLASSES.TOOLBAR);
		var collapse = jQuery("").addClass(CONST.CSS_CLASSES.BUTTON).text(CONST.TEXT.COLLAPSE);
		toolbar.append(collapse);
		var separator = jQuery("").text(CONST.TEXT.SEPARATOR);
		toolbar.append(separator);
		var expand = jQuery("").addClass(CONST.CSS_CLASSES.BUTTON).text(CONST.TEXT.EXPAND);
		toolbar.append(expand);

		collapse.click(function(event){
			event.preventDefault();
			jasmineTree.collapseAll();
		});

		expand.click(function(event){
			event.preventDefault();
			jasmineTree.expandAll();
		});

		toolbar.insertBefore(jQuery(CONST.SELECTORS.SUMMARY));
	};

	/**
	 * Check the querystring and expand/collapse suites based on filter criteria (if any)
	 */
	jasmineTree.filterSpec = function(){
		var filter = jasmineTree.getSpecFilter();
		if(filter === undefined){
			return;
		}
		// We have a filter. First collapse all
		jasmineTree.collapseAll();
		// Then expand only the suites that match
		for(var i = 0; i < rootSuites.length; i++){
			if(rootSuites[i].containsPath(filter) === true){
				rootSuites[i].expand();
			}
		}
	};

	/**
	 * @typedef {Object} jasmineTree.Suite.options
	 *
	 * @property {jQuery} rootNode
	 * @property {String} rootPath
	 */

	/**
	 * Wrapper around a suite's HTML node. Adds expand/collapse capabilities
	 * @param {jasmineTree.Suite.options} options
	 * @constructor
	 */
	jasmineTree.Suite = function(options){
		var config = {
			rootNode: null,
			rootPath: ""
		};
		jQuery.extend(config, options);

		/** @type  {jasmineTree.Suite} */
		var self = this;

		/** @type {Array.<jasmineTree.Suite>} */
		var suites = [];
		/** @type {Array.<jQuery>} */
		var specs = [];

		var fullPath = "";
		var expanded = true;
		var triggerNode = jQuery("").text(CONST.TEXT.MINUS).addClass(CONST.CSS_CLASSES.TRIGGER);

		var init = function(){
			config.rootNode.addClass(CONST.CSS_CLASSES.NODE_OPENED);

			var titleNode = config.rootNode.find(CONST.SELECTORS.NODE_TITLE);

			fullPath = config.rootPath + jQuery.trim(titleNode.text());
			triggerNode.insertBefore(titleNode.find(CONST.SELECTORS.FIRST_CHILD));

			config.rootNode.find(CONST.SELECTORS.NODE_SPECS).each(function(index, item){
				specs.push(jQuery(item));
			});
			config.rootNode.find(CONST.SELECTORS.NODE_SUITES).each(function(index, item){
				var childSuite = new jasmineTree.Suite({
					rootNode: jQuery(item),
					rootPath: fullPath + " "
				});
				suites.push(childSuite);
			});
			attachEvents();
		};

		var attachEvents = function(){
			triggerNode.click(function(event){
				event.preventDefault();
				if(expanded === true){
					self.collapse();
				}
				else{
					self.expand();
				}
			});
		};

		var startsWith = function(str, subStr){
			return (str.substring(0, subStr.length) === subStr);
		};

		this.getPath = function(){
			return fullPath;
		};

		/**
		 * Given a filter string, coming from the querystring, search if the suite contains specs that match it
		 * @param {String} path
		 * @return {Boolean}
		 */
		this.containsPath = function(path){
			// Matches the suite
			if(startsWith(self.getPath(), path) === true){
				return true;
			}
			// Search inside child specs
			for(var j = 0; j < specs.length; j++){
				var specPath = self.getPath() + " " + jQuery.trim(specs[j].text());
				if(specPath === path){
					return true;
				}
			}
			// Search inside child suites
			for(var i = 0; i < suites.length; i++){
				if(suites[i].containsPath(path) === true){
					return true;
				}
			}
			return false;
		};

		this.show = function(){
			config.rootNode.show();
		};

		this.hide = function(){
			config.rootNode.hide();
		};

		this.collapse = function(){
			config.rootNode.removeClass(CONST.CSS_CLASSES.NODE_OPENED);
			triggerNode.text(CONST.TEXT.PLUS);
			for(var i = 0; i < suites.length; i++){
				suites[i].collapse();
				suites[i].hide();
			}
			for(var j = 0; j < specs.length; j++){
				specs[j].hide();
			}
			expanded = false;
		};

		this.expand = function(){
			config.rootNode.addClass(CONST.CSS_CLASSES.NODE_OPENED);
			triggerNode.text(CONST.TEXT.MINUS);
			for(var i = 0; i < suites.length; i++){
				suites[i].expand();
				suites[i].show();
			}
			for(var j = 0; j < specs.length; j++){
				specs[j].show();
			}
			expanded = true;
		};

		init();
	};

	/**
	 * This must be invoked after Jasmine finished executing
	 */
	jasmineTree.init = function(){
		jQuery(CONST.SELECTORS.ROOT_SUITE).each(function(index, item){
			var suite = new jasmineTree.Suite({
				rootNode: jQuery(item)
			});
			rootSuites.push(suite);
		});
		jasmineTree.addRootClass();
		jasmineTree.addToolbar();
		jasmineTree.filterSpec();
	};

	jasmine.getEnv().addReporter({
		jasmineDone: function(){
			jasmineTree.init();
		}
	});

}());<commit_msg>Removed old, redundant selectors. Not required for current Jasmine version<commit_after>/* global jasmine */

/* istanbul ignore if */
if(typeof(jQuery) === "undefined"){
	throw("Unable to find jQuery");
}

/* istanbul ignore else */
if(typeof(window.jasmineTree) === "undefined"){
	window.jasmineTree = {};
}

(function(){
	"use strict";

	jasmineTree.version = "1.0";

	var CONST = {
		CSS_CLASSES: {
			SUMMARY: "jasmine-tree-summary",
			TRIGGER: "jasmine-tree-trigger",
			TOOLBAR: "jasmine-tree-toolbar",
			BUTTON: "jasmine-tree-button",
			NODE_OPENED: "jasmine-tree-opennode"
		},
		SELECTORS: {
			FIRST_CHILD: ":first-child",
			SUMMARY: ".jasmine-summary",
			ROOT_SUITE: ".jasmine-summary > .jasmine-suite",
			NODE_TITLE: "> li.jasmine-suite-detail",
			NODE_SPECS: "> ul.jasmine-specs",
			NODE_SUITES: "> ul.jasmine-suite"
		},
		TEXT: {
			COLLAPSE: "Collapse All",
			EXPAND: "Expand All",
			SEPARATOR: " | ",
			PLUS: "+",
			MINUS: "-"
		},
		FILTER_REGEXP: (new RegExp("[?&]spec=([^&]*)"))
	};

	/** @type {Array.<jasmineTree.Suite>} */
	var rootSuites = [];

	/**
	 * Returns the value of the "spec" parameter in the querystring. Null if it's not specified
	 * @return {null|String}
	 */
	jasmineTree.getSpecFilter = function(){
		var match = CONST.FILTER_REGEXP.exec(window.location.search);
		if(match !== null) {
			var filter = decodeURIComponent(match[1].replace(/\+/g, " "));
			if(filter !== "") {
				return filter;
			}
		}
	};

	/**
	 * Collapse all the suites
	 */
	jasmineTree.collapseAll = function(){
		for(var i = 0; i < rootSuites.length; i++){
			rootSuites[i].collapse();
		}
	};

	/**
	 * Expand all the suites
	 */
	jasmineTree.expandAll = function(){
		for(var i = 0; i < rootSuites.length; i++){
			rootSuites[i].expand();
		}
	};

	/**
	 * Add a CSS class to the summary to enable more specific CSS selectors
	 */
	jasmineTree.addRootClass = function(){
		jQuery(CONST.SELECTORS.SUMMARY).addClass(CONST.CSS_CLASSES.SUMMARY);
	};

	/**
	 * Insert toolbar with expand/collapse all buttons
	 */
	jasmineTree.addToolbar = function(){

		var toolbar = jQuery("

").addClass(CONST.CSS_CLASSES.TOOLBAR);
		var collapse = jQuery("").addClass(CONST.CSS_CLASSES.BUTTON).text(CONST.TEXT.COLLAPSE);
		toolbar.append(collapse);
		var separator = jQuery("").text(CONST.TEXT.SEPARATOR);
		toolbar.append(separator);
		var expand = jQuery("").addClass(CONST.CSS_CLASSES.BUTTON).text(CONST.TEXT.EXPAND);
		toolbar.append(expand);

		collapse.click(function(event){
			event.preventDefault();
			jasmineTree.collapseAll();
		});

		expand.click(function(event){
			event.preventDefault();
			jasmineTree.expandAll();
		});

		toolbar.insertBefore(jQuery(CONST.SELECTORS.SUMMARY));
	};

	/**
	 * Check the querystring and expand/collapse suites based on filter criteria (if any)
	 */
	jasmineTree.filterSpec = function(){
		var filter = jasmineTree.getSpecFilter();
		if(filter === undefined){
			return;
		}
		// We have a filter. First collapse all
		jasmineTree.collapseAll();
		// Then expand only the suites that match
		for(var i = 0; i < rootSuites.length; i++){
			if(rootSuites[i].containsPath(filter) === true){
				rootSuites[i].expand();
			}
		}
	};

	/**
	 * @typedef {Object} jasmineTree.Suite.options
	 *
	 * @property {jQuery} rootNode
	 * @property {String} rootPath
	 */

	/**
	 * Wrapper around a suite's HTML node. Adds expand/collapse capabilities
	 * @param {jasmineTree.Suite.options} options
	 * @constructor
	 */
	jasmineTree.Suite = function(options){
		var config = {
			rootNode: null,
			rootPath: ""
		};
		jQuery.extend(config, options);

		/** @type  {jasmineTree.Suite} */
		var self = this;

		/** @type {Array.<jasmineTree.Suite>} */
		var suites = [];
		/** @type {Array.<jQuery>} */
		var specs = [];

		var fullPath = "";
		var expanded = true;
		var triggerNode = jQuery("").text(CONST.TEXT.MINUS).addClass(CONST.CSS_CLASSES.TRIGGER);

		var init = function(){
			config.rootNode.addClass(CONST.CSS_CLASSES.NODE_OPENED);

			var titleNode = config.rootNode.find(CONST.SELECTORS.NODE_TITLE);

			fullPath = config.rootPath + jQuery.trim(titleNode.text());
			triggerNode.insertBefore(titleNode.find(CONST.SELECTORS.FIRST_CHILD));

			config.rootNode.find(CONST.SELECTORS.NODE_SPECS).each(function(index, item){
				specs.push(jQuery(item));
			});
			config.rootNode.find(CONST.SELECTORS.NODE_SUITES).each(function(index, item){
				var childSuite = new jasmineTree.Suite({
					rootNode: jQuery(item),
					rootPath: fullPath + " "
				});
				suites.push(childSuite);
			});
			attachEvents();
		};

		var attachEvents = function(){
			triggerNode.click(function(event){
				event.preventDefault();
				if(expanded === true){
					self.collapse();
				}
				else{
					self.expand();
				}
			});
		};

		var startsWith = function(str, subStr){
			return (str.substring(0, subStr.length) === subStr);
		};

		this.getPath = function(){
			return fullPath;
		};

		/**
		 * Given a filter string, coming from the querystring, search if the suite contains specs that match it
		 * @param {String} path
		 * @return {Boolean}
		 */
		this.containsPath = function(path){
			// Matches the suite
			if(startsWith(self.getPath(), path) === true){
				return true;
			}
			// Search inside child specs
			for(var j = 0; j < specs.length; j++){
				var specPath = self.getPath() + " " + jQuery.trim(specs[j].text());
				if(specPath === path){
					return true;
				}
			}
			// Search inside child suites
			for(var i = 0; i < suites.length; i++){
				if(suites[i].containsPath(path) === true){
					return true;
				}
			}
			return false;
		};

		this.show = function(){
			config.rootNode.show();
		};

		this.hide = function(){
			config.rootNode.hide();
		};

		this.collapse = function(){
			config.rootNode.removeClass(CONST.CSS_CLASSES.NODE_OPENED);
			triggerNode.text(CONST.TEXT.PLUS);
			for(var i = 0; i < suites.length; i++){
				suites[i].collapse();
				suites[i].hide();
			}
			for(var j = 0; j < specs.length; j++){
				specs[j].hide();
			}
			expanded = false;
		};

		this.expand = function(){
			config.rootNode.addClass(CONST.CSS_CLASSES.NODE_OPENED);
			triggerNode.text(CONST.TEXT.MINUS);
			for(var i = 0; i < suites.length; i++){
				suites[i].expand();
				suites[i].show();
			}
			for(var j = 0; j < specs.length; j++){
				specs[j].show();
			}
			expanded = true;
		};

		init();
	};

	/**
	 * This must be invoked after Jasmine finished executing
	 */
	jasmineTree.init = function(){
		jQuery(CONST.SELECTORS.ROOT_SUITE).each(function(index, item){
			var suite = new jasmineTree.Suite({
				rootNode: jQuery(item)
			});
			rootSuites.push(suite);
		});
		jasmineTree.addRootClass();
		jasmineTree.addToolbar();
		jasmineTree.filterSpec();
	};

	jasmine.getEnv().addReporter({
		jasmineDone: function(){
			jasmineTree.init();
		}
	});

}());<|endoftext|>


<commit_before>//= require "webgl"
//= require "events"

/**
 * class Jax.Context
 * The highest level of operation in Jax. Initialization is simple:
 *
 *     var context = new Jax.Context(canvas);
 *
 * where _canvas_ is a reference to an HTML5 Canvas element.
 *
 * If there is a root route set up, then at this point you are done.
 * If not, there's an additional step:
 *
 *     context.redirect_to("controller_name/action_name");
 *
 * where *controller_name* is the short name of the controller to start at,
 * and *action_name* is the action to fire within that controller.
 *
 * Note that the specified redirect path must be registered in the route set.
 *
 * Jax will automatically initialize and maintain a WebGL rendering bridge,
 * and WebGL methods can be called directly on this context by prefixing
 * the name of the method with "gl". For example:
 *
 *     context.glClear(GL_COLOR_BIT | GL_DEPTH_BUFFER_BIT);
 *
 **/
Jax.Context = (function() {
  function setupContext(self) {
    try { self.gl = self.canvas.getContext(WEBGL_CONTEXT_NAME, WEBGL_CONTEXT_OPTIONS); } catch(e) { }
    if (!self.gl) throw new Error("WebGL could not be initialized!");
  }

  function startRendering(self) {
    function render() {
      if (self.current_view) {
        reloadMatrices(self);
        self.glViewport(0, 0, self.canvas.width, self.canvas.height);
        self.current_view.render();
        self.render_interval = requestAnimFrame(render, self.canvas);
      }
      else {
        clearTimeout(self.render_interval);
        self.render_interval = null;
      }
    }

    self.render_interval = setTimeout(render, Jax.render_speed);
  }

  function stopRendering(self) {
    clearTimeout(self.render_interval);
  }

  function startUpdating(self) {
    function updateFunc() {
      if (!self.lastUpdate) self.lastUpdate = new Date();
      var now = new Date();
      var timechange = (now - self.lastUpdate) / 1000.0;
      self.lastUpdate = now;

      self.update(timechange);
      self.update_interval = setTimeout(updateFunc, Jax.update_speed);
    }
    updateFunc();
  }

  function stopUpdating(self) {
    clearTimeout(self.update_interval);
  }

  function setupView(self, view) {
    view.context = self;
    view.world = self.world;
    view.player = self.player;
    for (var i in self) {
      if (i.indexOf("gl") == 0) {
        /* it's a WebGL method */
        view[i] = eval("(function() { return this.context."+i+".apply(this.context, arguments); })");
      }
    }
    /* TODO we should set up helpers, etc. here too */
  }

  function reloadMatrices(self) {
    mat4.set(self.player.camera.getModelViewMatrix(), self.getModelViewMatrix());
    mat4.set(self.player.camera.getProjectionMatrix(), self.getProjectionMatrix());

    // the modelview matrix is actually the inverse of the model (world space transform) matrix.
    // TODO rename the modelview functions accordingly, since they're really just model matrices
    mat4.inverse(self.matrices.mv[self.matrix_depth]);
    mat4.identity(self.matrices.world[self.matrix_depth]);
  }

  return Jax.Class.create({
    initialize: function(canvas) {
      this.id = ++Jax.Context.identifier;
      this.canvas = canvas;
      setupContext(this);
      this.setupEventListeners();
      this.render_interval = null;
      this.glClearColor(0.0, 0.0, 0.0, 1.0);
      this.glClearDepth(1.0);
      this.glEnable(GL_DEPTH_TEST);
      this.glDepthFunc(GL_LEQUAL);
      this.glEnable(GL_BLEND);
      this.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      this.checkForRenderErrors();
      this.world = new Jax.World(this);
      this.player = {camera: new Jax.Camera()};
      this.player.camera.perspective({width:canvas.width, height:canvas.height});

//      this.matrices = [mat4.create()];
      this.matrices = { mv: [mat4.create()], world: [mat4.create()], p: [mat4.create()] };
      mat4.set(this.player.camera.getModelViewMatrix(), this.matrices.mv[0]);
      mat4.set(this.player.camera.getProjectionMatrix(), this.matrices.p[0]);
      this.matrix_depth = 0;

      if (Jax.routes.isRouted("/"))
        this.redirectTo("/");

      startUpdating(this);
    },

    hasStencil: function() {
      return !!this.gl.stencil;
    },

    /**
     * Jax.Context#redirectTo(path) -> Controller
     * - path (String): the path to redirect to
     *
     * Redirects to the specified route, and then returns the Jax.Controller that
     * was just redirected to. The act of redirecting will dispose of the current
     * World, so be prepared to initialize a new scene.
     **/
    redirectTo: function(path) {
      stopRendering(this);
      this.world.dispose();
      this.player.camera.reset();
      this.current_controller = Jax.routes.dispatch(path, this);
      if (!this.current_controller.view_key)
        throw new Error("Controller '"+this.current_controller.getControllerName()+"' did not produce a renderable result");
      this.current_view = Jax.views.find(this.current_controller.view_key);
      setupView(this, this.current_view);
      if (!this.isRendering()) startRendering(this);

      return this.current_controller;
    },

    update: function(timechange) {
      if (this.current_controller && this.current_controller.update)
        this.current_controller.update(timechange);
      this.world.update(timechange);
    },

    /**
     * Jax.Context#isRendering() -> Boolean
     * Returns true if this Jax.Context currently has a render interval set.
     * This will be false by default if there is no root route; otherwise
     * it will be true. It is also enabled automatically upon the first
     * valid redirect.
     **/
    isRendering: function() {
      return this.render_interval != null;
    },

    /**
     * Jax.Context#dispose() -> undefined
     *
     * Permanently disposes of this context, freeing the resources it is using.
     * This stops actions from running, closes any open rendering contexts, and
     * clears all other timeouts and intervals.
     *
     * A Jax.Context cannot be used once it is disposed.
     **/
    dispose: function() {
      this.disposed = true;
      stopRendering(this);
      stopUpdating(this);
    },

    /**
     * Jax.Context#isDisposed() -> Boolean
     * Returns true if this context has been disposed.
     **/
    isDisposed: function() {
      return !!this.disposed;
    },

    pushMatrix: function(yield_to) {
      var current_mv = this.getModelViewMatrix();
      var current_ws = this.getWorldSpaceMatrix();
      var current_pr = this.getProjectionMatrix();
      this.matrix_depth++;
      if (!this.matrices.mv[this.matrix_depth]) {
        this.matrices.mv[this.matrix_depth] = mat4.create();
        this.matrices.world[this.matrix_depth] = mat4.create();
        this.matrices.p[this.matrix_depth] = mat4.create();
      }
      mat4.set(current_mv, this.matrices.mv[this.matrix_depth]);
      mat4.set(current_ws, this.matrices.world[this.matrix_depth]);
      mat4.set(current_pr, this.matrices.p[this.matrix_depth]);
      yield_to();
      this.matrix_depth--;
    },

    multMatrix: function(matr) {
      mat4.multiply(this.getModelViewMatrix(), matr);
      mat4.multiply(this.getWorldSpaceMatrix(), mat4.inverse(matr, mat4.create()));
    },

    /**
     * Jax.Context#getWorldSpaceMatrix() -> mat4
     * Returns the world space matrix. A vector multiplied by this matrix will be transformed
     * into world space regardless of the current matrix transformations.
     **/
    getWorldSpaceMatrix: function(matr) {
      return this.matrices.world[this.matrix_depth];
    },

    /**
     * Jax.Context#getFrustum() -> Jax.Scene.Frustum
     * Returns the frustum for the player's camera. Equivalent to calling
     *
     *     context.player.camera.getFrustum()
     *
     * Note that changes to the matrix via #multMatrix will not be represented
     * in this Jax.Scene.Frustum.
     **/
    getFrustum: function() { return this.player.camera.frustum; },

    /**
     * Jax.Context#getModelViewMatrix() -> Matrix
     * Returns the current modelview matrix.
     **/
    getModelViewMatrix: function() { return this.matrices.mv[this.matrix_depth]; },

    /**
     * Jax.Context#getProjectionMatrix() -> Matrix
     * Returns the current projection matrix.
     **/
    getProjectionMatrix: function() { return this.matrices.p[this.matrix_depth]; },

    /**
     * Jax.Context#getNormalMatrix() -> Matrix
     * Returns the current normal matrix.
     **/
    getNormalMatrix: function() {
      // TODO: optimize
      var mat = mat3.create();
      mat4.toInverseMat3(this.getModelViewMatrix(), mat);
      mat3.transpose(mat);
      return mat;
    },

    checkForRenderErrors: function() {
      /* Error checking is slow, so don't do it in production mode */
      if (Jax.environment == "production") return; /* TODO expose Jax.environment to application */

      var error = this.glGetError();
      if (error != GL_NO_ERROR)
      {
        var str = "GL error in "+this.canvas.id+": "+error;
        error = new Error(str);
        var message = error;
        if (error.stack)
        {
          var stack = error.stack.split("\n");
          stack.shift();
          message += "\n\n"+stack.join("\n");
        }

        throw error;
      }
    },

    handleRenderError: function(method_name, args, err) {
      throw err;
    }
  });
})();

Jax.Context.identifier = 0;
Jax.Context.addMethods(GL_METHODS);
Jax.Context.addMethods(Jax.EVENT_METHODS);
<commit_msg>Jax.Context can optionally be initialized with the ID of a canvas element in place of the canvas element itself<commit_after>//= require "webgl"
//= require "events"

/**
 * class Jax.Context
 * The highest level of operation in Jax. Initialization is simple:
 *
 *     var context = new Jax.Context(canvas);
 *
 * where _canvas_ is a reference to an HTML5 Canvas element.
 *
 * If there is a root route set up, then at this point you are done.
 * If not, there's an additional step:
 *
 *     context.redirect_to("controller_name/action_name");
 *
 * where *controller_name* is the short name of the controller to start at,
 * and *action_name* is the action to fire within that controller.
 *
 * Note that the specified redirect path must be registered in the route set.
 *
 * Jax will automatically initialize and maintain a WebGL rendering bridge,
 * and WebGL methods can be called directly on this context by prefixing
 * the name of the method with "gl". For example:
 *
 *     context.glClear(GL_COLOR_BIT | GL_DEPTH_BUFFER_BIT);
 *
 **/
Jax.Context = (function() {
  function setupContext(self) {
    try { self.gl = self.canvas.getContext(WEBGL_CONTEXT_NAME, WEBGL_CONTEXT_OPTIONS); } catch(e) { }
    if (!self.gl) throw new Error("WebGL could not be initialized!");
  }

  function startRendering(self) {
    function render() {
      if (self.current_view) {
        reloadMatrices(self);
        self.glViewport(0, 0, self.canvas.width, self.canvas.height);
        self.current_view.render();
        self.render_interval = requestAnimFrame(render, self.canvas);
      }
      else {
        clearTimeout(self.render_interval);
        self.render_interval = null;
      }
    }

    self.render_interval = setTimeout(render, Jax.render_speed);
  }

  function stopRendering(self) {
    clearTimeout(self.render_interval);
  }

  function startUpdating(self) {
    function updateFunc() {
      if (!self.lastUpdate) self.lastUpdate = new Date();
      var now = new Date();
      var timechange = (now - self.lastUpdate) / 1000.0;
      self.lastUpdate = now;

      self.update(timechange);
      self.update_interval = setTimeout(updateFunc, Jax.update_speed);
    }
    updateFunc();
  }

  function stopUpdating(self) {
    clearTimeout(self.update_interval);
  }

  function setupView(self, view) {
    view.context = self;
    view.world = self.world;
    view.player = self.player;
    for (var i in self) {
      if (i.indexOf("gl") == 0) {
        /* it's a WebGL method */
        view[i] = eval("(function() { return this.context."+i+".apply(this.context, arguments); })");
      }
    }
    /* TODO we should set up helpers, etc. here too */
  }

  function reloadMatrices(self) {
    mat4.set(self.player.camera.getModelViewMatrix(), self.getModelViewMatrix());
    mat4.set(self.player.camera.getProjectionMatrix(), self.getProjectionMatrix());

    // the modelview matrix is actually the inverse of the model (world space transform) matrix.
    // TODO rename the modelview functions accordingly, since they're really just model matrices
    mat4.inverse(self.matrices.mv[self.matrix_depth]);
    mat4.identity(self.matrices.world[self.matrix_depth]);
  }

  return Jax.Class.create({
    initialize: function(canvas) {
      if (typeof(canvas) == "string") canvas = document.getElementById(canvas);
      if (!canvas) throw new Error("Can't initialize a WebGL context without a canvas!");
      this.id = ++Jax.Context.identifier;
      this.canvas = canvas;
      setupContext(this);
      this.setupEventListeners();
      this.render_interval = null;
      this.glClearColor(0.0, 0.0, 0.0, 1.0);
      this.glClearDepth(1.0);
      this.glEnable(GL_DEPTH_TEST);
      this.glDepthFunc(GL_LEQUAL);
      this.glEnable(GL_BLEND);
      this.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      this.checkForRenderErrors();
      this.world = new Jax.World(this);
      this.player = {camera: new Jax.Camera()};
      this.player.camera.perspective({width:canvas.width, height:canvas.height});

//      this.matrices = [mat4.create()];
      this.matrices = { mv: [mat4.create()], world: [mat4.create()], p: [mat4.create()] };
      mat4.set(this.player.camera.getModelViewMatrix(), this.matrices.mv[0]);
      mat4.set(this.player.camera.getProjectionMatrix(), this.matrices.p[0]);
      this.matrix_depth = 0;

      if (Jax.routes.isRouted("/"))
        this.redirectTo("/");

      startUpdating(this);
    },

    hasStencil: function() {
      return !!this.gl.stencil;
    },

    /**
     * Jax.Context#redirectTo(path) -> Controller
     * - path (String): the path to redirect to
     *
     * Redirects to the specified route, and then returns the Jax.Controller that
     * was just redirected to. The act of redirecting will dispose of the current
     * World, so be prepared to initialize a new scene.
     **/
    redirectTo: function(path) {
      stopRendering(this);
      this.world.dispose();
      this.player.camera.reset();
      this.current_controller = Jax.routes.dispatch(path, this);
      if (!this.current_controller.view_key)
        throw new Error("Controller '"+this.current_controller.getControllerName()+"' did not produce a renderable result");
      this.current_view = Jax.views.find(this.current_controller.view_key);
      setupView(this, this.current_view);
      if (!this.isRendering()) startRendering(this);

      return this.current_controller;
    },

    update: function(timechange) {
      if (this.current_controller && this.current_controller.update)
        this.current_controller.update(timechange);
      this.world.update(timechange);
    },

    /**
     * Jax.Context#isRendering() -> Boolean
     * Returns true if this Jax.Context currently has a render interval set.
     * This will be false by default if there is no root route; otherwise
     * it will be true. It is also enabled automatically upon the first
     * valid redirect.
     **/
    isRendering: function() {
      return this.render_interval != null;
    },

    /**
     * Jax.Context#dispose() -> undefined
     *
     * Permanently disposes of this context, freeing the resources it is using.
     * This stops actions from running, closes any open rendering contexts, and
     * clears all other timeouts and intervals.
     *
     * A Jax.Context cannot be used once it is disposed.
     **/
    dispose: function() {
      this.disposed = true;
      stopRendering(this);
      stopUpdating(this);
    },

    /**
     * Jax.Context#isDisposed() -> Boolean
     * Returns true if this context has been disposed.
     **/
    isDisposed: function() {
      return !!this.disposed;
    },

    pushMatrix: function(yield_to) {
      var current_mv = this.getModelViewMatrix();
      var current_ws = this.getWorldSpaceMatrix();
      var current_pr = this.getProjectionMatrix();
      this.matrix_depth++;
      if (!this.matrices.mv[this.matrix_depth]) {
        this.matrices.mv[this.matrix_depth] = mat4.create();
        this.matrices.world[this.matrix_depth] = mat4.create();
        this.matrices.p[this.matrix_depth] = mat4.create();
      }
      mat4.set(current_mv, this.matrices.mv[this.matrix_depth]);
      mat4.set(current_ws, this.matrices.world[this.matrix_depth]);
      mat4.set(current_pr, this.matrices.p[this.matrix_depth]);
      yield_to();
      this.matrix_depth--;
    },

    multMatrix: function(matr) {
      mat4.multiply(this.getModelViewMatrix(), matr);
      mat4.multiply(this.getWorldSpaceMatrix(), mat4.inverse(matr, mat4.create()));
    },

    /**
     * Jax.Context#getWorldSpaceMatrix() -> mat4
     * Returns the world space matrix. A vector multiplied by this matrix will be transformed
     * into world space regardless of the current matrix transformations.
     **/
    getWorldSpaceMatrix: function(matr) {
      return this.matrices.world[this.matrix_depth];
    },

    /**
     * Jax.Context#getFrustum() -> Jax.Scene.Frustum
     * Returns the frustum for the player's camera. Equivalent to calling
     *
     *     context.player.camera.getFrustum()
     *
     * Note that changes to the matrix via #multMatrix will not be represented
     * in this Jax.Scene.Frustum.
     **/
    getFrustum: function() { return this.player.camera.frustum; },

    /**
     * Jax.Context#getModelViewMatrix() -> Matrix
     * Returns the current modelview matrix.
     **/
    getModelViewMatrix: function() { return this.matrices.mv[this.matrix_depth]; },

    /**
     * Jax.Context#getProjectionMatrix() -> Matrix
     * Returns the current projection matrix.
     **/
    getProjectionMatrix: function() { return this.matrices.p[this.matrix_depth]; },

    /**
     * Jax.Context#getNormalMatrix() -> Matrix
     * Returns the current normal matrix.
     **/
    getNormalMatrix: function() {
      // TODO: optimize
      var mat = mat3.create();
      mat4.toInverseMat3(this.getModelViewMatrix(), mat);
      mat3.transpose(mat);
      return mat;
    },

    checkForRenderErrors: function() {
      /* Error checking is slow, so don't do it in production mode */
      if (Jax.environment == "production") return; /* TODO expose Jax.environment to application */

      var error = this.glGetError();
      if (error != GL_NO_ERROR)
      {
        var str = "GL error in "+this.canvas.id+": "+error;
        error = new Error(str);
        var message = error;
        if (error.stack)
        {
          var stack = error.stack.split("\n");
          stack.shift();
          message += "\n\n"+stack.join("\n");
        }

        throw error;
      }
    },

    handleRenderError: function(method_name, args, err) {
      throw err;
    }
  });
})();

Jax.Context.identifier = 0;
Jax.Context.addMethods(GL_METHODS);
Jax.Context.addMethods(Jax.EVENT_METHODS);
<|endoftext|>