<commit_before>/*
 * Copyright (C) 2019 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */
import React, {useEffect, useRef, useState} from 'react'
import {oneOf, string} from 'prop-types'
import I18n from 'i18n!CanvasMediaPlayer'
import LoadingIndicator from '@instructure/canvas-media/lib/shared/LoadingIndicator'
import {VideoPlayer} from '@instructure/ui-media-player'
import {View} from '@instructure/ui-layout'
import {asJson, defaultFetchOptions} from '@instructure/js-utils'

export default function CanvasMediaPlayer(props) {
  const [media_sources, setMedia_sources] = useState(props.media_sources)
  const [retryTimerId, setRetryTimerId] = useState(0)
  const containerRef = useRef(null)
  const myIframeRef = useRef(null)

  useEffect(() => {
    myIframeRef.current = [...window.parent.document.getElementsByTagName('iframe')].find(
      el => el.contentWindow === window
    )
  }, [])

  useEffect(() => {
    if (!props.media_sources.length) {
      fetchSources()
    }

    return () => {
      clearTimeout(retryTimerId)
    }
  }, [retryTimerId])

  useEffect(() => {
    const player = containerRef.current.querySelector('video')
    if (player) {
      if (player.loadedmetadata || player.readyState >= 1) {
        setMediaPlayerSize(player)
      } else {
        player.addEventListener('loadedmetadata', () => setMediaPlayerSize(player))
      }
    }
  }, [media_sources.length])

  async function fetchSources() {
    const url = `/media_objects/${props.media_id}/info`
    let resp
    try {
      resp = await asJson(fetch(url, defaultFetchOptions))
    } catch (e) {
      // if there is a network error, just ignore and retry
    }
    if (resp && resp.media_sources && resp.media_sources.length) {
      setMedia_sources(resp.media_sources)
    } else {
      // if they're not present yet, try again in a little bit
      await new Promise(resolve => {
        const tid = setTimeout(resolve, 1000)
        setRetryTimerId(tid)
      })
    }
  }

  function setMediaPlayerSize(player) {
    const playerContainer = myIframeRef.current
    if (playerContainer) {
      const {width, height} = sizeMediaPlayer(player, props.type, playerContainer)
      playerContainer.style.width = width
      playerContainer.style.height = height
      if (props.type === 'audio') {
        player.style.height = height
      }

      const playerContainerContainer = playerContainer.parentElement // tinymce adds this
      if (
        playerContainerContainer &&
        playerContainerContainer.classList.contains('mce-preview-object') &&
        playerContainerContainer.classList.contains('mce-object-iframe')
      ) {
        // we're in the RCE
        playerContainerContainer.style.width = width
        playerContainerContainer.style.height = height
      }
    }
  }

  function renderControls(VPC) {
    if (props.type === 'audio') {
      return (
        <VPC>
          <VPC.PlayPauseButton />
          <VPC.Timebar />
          <VPC.Volume />
          <VPC.PlaybackSpeed />
          <VPC.TrackChooser />
        </VPC>
      )
    }

    // TODO: when the fullscreen button is missing, the source chooser button is up against
    // the right edge of the frame. When its popup menu is opened, the outset focus ring
    // extends beyond the container's edge, causing a horiz. scrollbar, which steals vert.
    // space and causes a vert. scrollbar, and this oscillates.
    // remove the containing View when this jitter is fixed
    return (
      <VPC>
        <VPC.PlayPauseButton />
        <VPC.Timebar />
        <VPC.Volume />
        <VPC.PlaybackSpeed />
        <VPC.TrackChooser />
        {media_sources.length > 1 && (
          <View margin={includeFullscreen ? '0 0 0 xx-small' : '0 xx-small'}>
            <VPC.SourceChooser sources={media_sources} />
          </View>
        )}
        {includeFullscreen && <VPC.FullScreenButton />}
      </VPC>
    )
  }

  const includeFullscreen = document.fullscreenEnabled && props.type === 'video'

  return (

      {media_sources.length ? (
        <VideoPlayer sources={media_sources} controls={renderControls} />
      ) : (
        <LoadingIndicator
          translatedTitle={I18n.t('Loading')}
          size={props.type === 'audio' ? 'x-small' : 'large'}
        />
      )}

  )
}

export function sizeMediaPlayer(player, type, playerContainer) {
  if (type === 'audio') {
    return {width: '300px', height: '3rem'}
  }

  const width = player.videoWidth
  const height = player.videoHeight
  if (width > 0) {
    // key off width, because we know it's initially layed out landscape
    // just wide enough for the controls to fit
    const minSideLength = playerContainer.clientWidth
    const w = Math.min(width, minSideLength)

    return {width: `${w}px`, height: `${Math.round((w / width) * height)}px`}
  }
  return {}
}

CanvasMediaPlayer.propTypes = {
  media_id: string.isRequired,
  media_sources: VideoPlayer.propTypes.sources,
  type: oneOf(['audio', 'video'])
}

CanvasMediaPlayer.defaultProps = {
  media_sources: [],
  type: 'video'
}
<commit_msg>Add eslint disable to several lines<commit_after>/*
 * Copyright (C) 2019 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */
import React, {useEffect, useRef, useState} from 'react'
import {oneOf, string} from 'prop-types'
import I18n from 'i18n!CanvasMediaPlayer'
import LoadingIndicator from '@instructure/canvas-media/lib/shared/LoadingIndicator'
import {VideoPlayer} from '@instructure/ui-media-player'
import {View} from '@instructure/ui-layout'
import {asJson, defaultFetchOptions} from '@instructure/js-utils'

export default function CanvasMediaPlayer(props) {
  const [media_sources, setMedia_sources] = useState(props.media_sources)
  const [retryTimerId, setRetryTimerId] = useState(0)
  const containerRef = useRef(null)
  const myIframeRef = useRef(null)

  useEffect(() => {
    myIframeRef.current = [...window.parent.document.getElementsByTagName('iframe')].find(
      el => el.contentWindow === window
    )
  }, [])

  useEffect(() => {
    if (!props.media_sources.length) {
      fetchSources()
    }

    return () => {
      clearTimeout(retryTimerId)
    }
    // The way this function is setup seems to break things when more exhaustive
    // deps are put in here.  We should investigate more in the future.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [retryTimerId])

  useEffect(() => {
    const player = containerRef.current.querySelector('video')
    if (player) {
      if (player.loadedmetadata || player.readyState >= 1) {
        setMediaPlayerSize(player)
      } else {
        player.addEventListener('loadedmetadata', () => setMediaPlayerSize(player))
      }
    }
    // The way this function is setup seems to break things when more exhaustive
    // deps are put in here.  We should investigate more in the future.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [media_sources.length])

  async function fetchSources() {
    const url = `/media_objects/${props.media_id}/info`
    let resp
    try {
      resp = await asJson(fetch(url, defaultFetchOptions))
    } catch (e) {
      // if there is a network error, just ignore and retry
    }
    if (resp && resp.media_sources && resp.media_sources.length) {
      setMedia_sources(resp.media_sources)
    } else {
      // if they're not present yet, try again in a little bit
      await new Promise(resolve => {
        const tid = setTimeout(resolve, 1000)
        setRetryTimerId(tid)
      })
    }
  }

  function setMediaPlayerSize(player) {
    const playerContainer = myIframeRef.current
    if (playerContainer) {
      const {width, height} = sizeMediaPlayer(player, props.type, playerContainer)
      playerContainer.style.width = width
      playerContainer.style.height = height
      if (props.type === 'audio') {
        player.style.height = height
      }

      const playerContainerContainer = playerContainer.parentElement // tinymce adds this
      if (
        playerContainerContainer &&
        playerContainerContainer.classList.contains('mce-preview-object') &&
        playerContainerContainer.classList.contains('mce-object-iframe')
      ) {
        // we're in the RCE
        playerContainerContainer.style.width = width
        playerContainerContainer.style.height = height
      }
    }
  }

  function renderControls(VPC) {
    if (props.type === 'audio') {
      return (
        <VPC>
          <VPC.PlayPauseButton />
          <VPC.Timebar />
          <VPC.Volume />
          <VPC.PlaybackSpeed />
          <VPC.TrackChooser />
        </VPC>
      )
    }

    // TODO: when the fullscreen button is missing, the source chooser button is up against
    // the right edge of the frame. When its popup menu is opened, the outset focus ring
    // extends beyond the container's edge, causing a horiz. scrollbar, which steals vert.
    // space and causes a vert. scrollbar, and this oscillates.
    // remove the containing View when this jitter is fixed
    return (
      <VPC>
        <VPC.PlayPauseButton />
        <VPC.Timebar />
        <VPC.Volume />
        <VPC.PlaybackSpeed />
        <VPC.TrackChooser />
        {media_sources.length > 1 && (
          <View margin={includeFullscreen ? '0 0 0 xx-small' : '0 xx-small'}>
            <VPC.SourceChooser sources={media_sources} />
          </View>
        )}
        {includeFullscreen && <VPC.FullScreenButton />}
      </VPC>
    )
  }

  const includeFullscreen = document.fullscreenEnabled && props.type === 'video'

  return (

      {media_sources.length ? (
        <VideoPlayer sources={media_sources} controls={renderControls} />
      ) : (
        <LoadingIndicator
          translatedTitle={I18n.t('Loading')}
          size={props.type === 'audio' ? 'x-small' : 'large'}
        />
      )}

  )
}

export function sizeMediaPlayer(player, type, playerContainer) {
  if (type === 'audio') {
    return {width: '300px', height: '3rem'}
  }

  const width = player.videoWidth
  const height = player.videoHeight
  if (width > 0) {
    // key off width, because we know it's initially layed out landscape
    // just wide enough for the controls to fit
    const minSideLength = playerContainer.clientWidth
    const w = Math.min(width, minSideLength)

    return {width: `${w}px`, height: `${Math.round((w / width) * height)}px`}
  }
  return {}
}

CanvasMediaPlayer.propTypes = {
  media_id: string.isRequired,
  media_sources: VideoPlayer.propTypes.sources,
  type: oneOf(['audio', 'video'])
}

CanvasMediaPlayer.defaultProps = {
  media_sources: [],
  type: 'video'
}
<|endoftext|>


<commit_before>import urltemplate from 'url-template'
import { API_ROOT, get } from '@/plugins/store/apiPlugin'

function isEqualIgnoringOrder (array, other) {
  return array.length === other.length && array.every(elem => other.includes(elem))
}

/**
 * A templated link in the Vuex store looks like this: { href: '/some/uri{/something}', templated: true }
 * @param object         to be examined
 * @returns boolean      true if the object looks like a templated link, false otherwise
 */
function isTemplatedLink (object) {
  if (!object) return false
  return isEqualIgnoringOrder(Object.keys(object), ['href', 'templated']) && (object.templated === true)
}

/**
 * An entity reference in the Vuex store looks like this: { href: '/some/uri' }
 * @param object    to be examined
 * @returns boolean true if the object looks like an entity reference, false otherwise
 */
function isEntityReference (object) {
  if (!object) return false
  return isEqualIgnoringOrder(Object.keys(object), ['href'])
}

function containsLoadingEntityReference (array) {
  return array.some(entry => isEntityReference(entry) && get(entry.href)._meta.loading)
}

/**
 * A standalone collection in the Vuex store has an items property that is an array.
 * @param object    to be examined
 * @returns boolean true if the object looks like a standalone collection, false otherwise
 */
function isCollection (object) {
  return !!(object && Array.isArray(object.items))
}

/**
 * Creates a placeholder for an entity which has not yet finished loading from the API.
 * Such a loadingProxy can safely be used in Vue components, since it will render as an empty
 * string and Vue's reactivity system will replace it with the real data once that is available.
 *
 * Accessing nested functions in a loadingProxy yields another loadingProxy:
 * loadingProxy(...).campType().organization() // gives another loadingProxy
 *
 * Using a loadingProxy or a property of a loadingProxy in a view renders to empty strings:
 * let user = loadingProxy(...)
 * 'The "' + user + '" is called "' + user.name + '"' // gives 'The "" is called ""'
 *
 * @param entityLoaded a Promise that resolves to a storeValueProxy when the entity has finished
 *                     loading from the API
 * @param uri          optional URI of the entity being loaded, if available. If passed, the
 *                     returned loadingProxy will return it in calls to .self and ._meta.self
 * @returns object     a loadingProxy
 */
function loadingProxy (entityLoaded, uri = null) {
  const handler = {
    get: function (target, prop, _) {
      if (prop === Symbol.for('isLoadingProxy')) {
        return true
      }
      if (prop === Symbol.toPrimitive) {
        return () => ''
      }
      if (['then', 'toJSON', Symbol.toStringTag, 'state', 'getters', '$options', '_isVue', '__file', 'render', 'constructor'].includes(prop)) {
        // This is necessary so that Vue's reactivity system understands to treat this loadingProxy
        // like a normal object.
        return undefined
      }
      if (prop === 'loading') {
        return true
      }
      if (prop === 'load') {
        return entityLoaded
      }
      if (prop === 'self') {
        return uri !== null ? API_ROOT + uri : uri
      }
      if (prop === '_meta') {
        // When _meta is requested on a loadingProxy, we keep on using the unmodified promise, because ._meta.load
        // is supposed to resolve to the whole object, not just the ._meta part of it
        return loadingProxy(entityLoaded, uri)
      }
      const propertyLoaded = entityLoaded.then(entity => entity[prop])
      if (['items', 'allItems'].includes(prop)) {
        return loadingArrayProxy(propertyLoaded)
      }
      // Normal property access: return a function that yields another loadingProxy and renders as empty string
      const result = templateParams => loadingProxy(propertyLoaded.then(property => property(templateParams)._meta.load))
      result.loading = true
      result.toString = () => ''
      return result
    }
  }
  return new Proxy({}, handler)
}

/**
 * Returns a placeholder for an array that has not yet finished loading from the API. The array placeholder
 * will respond to functional calls (like .find(), .map(), etc.) with further loading array proxies or
 * loading proxies. If passed the existingContent argument, random access and .length will also work.
 * @param arrayLoaded     Promise that resolves once the array has finished loading
 * @param existingContent optionally set the elements that are already known, for random access
 * @returns Array         a proxy that can act as a placeholder for an array that is still being loaded from the API
 */
function loadingArrayProxy (arrayLoaded, existingContent = []) {
  const singleResultFunctions = ['find']
  const arrayResultFunctions = ['map', 'flatMap', 'filter']
  singleResultFunctions.forEach(func => {
    existingContent[func] = (...args) => {
      const resultLoaded = arrayLoaded.then(array => array[func](...args))
      return loadingProxy(resultLoaded)
    }
  })
  arrayResultFunctions.forEach(func => {
    existingContent[func] = (...args) => {
      const resultLoaded = arrayLoaded.then(array => array[func](...args))
      return loadingArrayProxy(resultLoaded)
    }
  })
  return existingContent
}

function filterDeleting (array) {
  return array.filter(entry => !entry._meta.deleting)
}

/**
 * Given an array, replaces any entity references in the array with the entity loaded from the Vuex store
 * (or from the API if necessary), and returns that as a new array. In case some of the entity references in
 * the array have not finished loading yet, returns a loadingArrayProxy instead.
 * @param array   possibly mixed array of values and references
 * @returns array the new array with replaced items, or a loadingArrayProxy if any of the array elements
 *                is still loading.
 */
function mapArrayOfEntityReferences (array) {
  const arrayWithReplacedReferences = array.map(entry => {
    if (isEntityReference(entry)) {
      return get(entry.href)
    }
    return entry
  })
  if (containsLoadingEntityReference(array)) {
    const arrayCompletelyLoaded = Promise.all(array.map(entry => {
      if (isEntityReference(entry)) {
        return get(entry.href)._meta.load
      }
      return Promise.resolve(entry)
    }))
    return loadingArrayProxy(arrayCompletelyLoaded, arrayWithReplacedReferences)
  } else {
    return arrayWithReplacedReferences
  }
}

/**
 * Defines a property getter for the items property of a given target object.
 * The items property should always be a getter, in order to make the call to mapArrayOfEntityReferences
 * lazy, since that potentially fetches a large number of entities from the API.
 * @param target   object on which the items getter should be defined
 * @param items    array of items, which can be mixed primitive values and entity references
 * @returns object the target object with the added getter
 */
function addItemsGetter (target, items) {
  Object.defineProperty(target, 'items', { get: () => filterDeleting(mapArrayOfEntityReferences(items)) })
  Object.defineProperty(target, 'allItems', { get: () => mapArrayOfEntityReferences(items) })
  return target
}

/**
 * Imitates a full standalone collection with an items property, even if there is no separate URI (as it
 * is the case with embedded collections).
 * Reloading an embedded collection requires special information. Since the embedded collection has no own
 * URI, we need to reload the whole entity containing the embedded collection. Some extra info about the
 * containing entity must therefore be passed to this function.
 * @param items          array of items, which can be mixed primitive values and entity references
 * @param reloadUri      URI of the entity containing the embedded collection (for reloading)
 * @param reloadProperty property in the containing entity under which the embedded collection is saved
 * @returns object the imitated collection object
 */
function embeddedCollectionProxy (items, reloadUri, reloadProperty) {
  const result = addItemsGetter({ _meta: { reload: { uri: reloadUri, property: reloadProperty } } }, items)
  result._meta.load = Promise.resolve(result)
  return result
}

/**
 * Takes data from the Vuex store and makes it more usable in frontend components. The data stored
 * in the Vuex store should always be JSON serializable according to
 * https://github.com/vuejs/vuex/issues/757#issuecomment-297668640. Therefore, we wrap the data into
 * a new object, and provide accessor methods for related entities. Such an accessor method fetches the
 * related entity from the Vuex store (or the API if necessary) when called. In case the related entity
 * is still being loaded from the API, a loadingProxy is returned.
 *
 * Example:
 * // Data of an entity like it comes from the Vuex store:
 * let storeData = {
 *   numeric_property: 3,
 *   reference_to_other_entity: {
 *     href: '/uri/of/other/entity'
 *   },
 *   _meta: {
 *     self: '/self/uri'
 *   }
 * }
 * // Apply storeValueProxy
 * let usable = storeValueProxy(storeData)
 * // Now we can use accessor methods
 * usable.reference_to_other_entity() // returns the result of this.api.get('/uri/of/other/entity')
 *
 * @param data                entity data from the Vuex store
 * @returns object            wrapped entity ready for use in a frontend component
 */
export default function storeValueProxy (data) {
  const meta = data._meta || { load: Promise.resolve() }

  if (meta.loading) {
    const entityLoaded = meta.load.then(loadedData => createStoreValueProxy(loadedData))
    return loadingProxy(entityLoaded, meta.self)
  }

  return createStoreValueProxy(data)
}

/**
 * Creates an actual storeValueProxy, by wrapping the given Vuex store data. The data must not be loading.
 * If the data has been loaded into the store before but is currently reloading, the old data will be
 * returned, along with a ._meta.load promise that resolves when the reload is complete.
 * @param data fully loaded entity data from the Vuex store
 */
function createStoreValueProxy (data) {
  const result = {}
  Object.keys(data).forEach(key => {
    const value = data[key]
    if (key === 'allItems' && isCollection(data)) return
    if (key === 'items' && isCollection(data)) {
      addItemsGetter(result, data[key])
    } else if (Array.isArray(value)) {
      result[key] = () => embeddedCollectionProxy(value, data._meta.self, key)
    } else if (isEntityReference(value)) {
      result[key] = () => get(value.href)
    } else if (isTemplatedLink(value)) {
      result[key] = templateParams => get(urltemplate.parse(value.href).expand(templateParams || {}))
    } else {
      result[key] = value
    }
  })

  // Use a trivial load promise to break endless recursion, except if we are currently reloading the data from the API
  const loadedPromise = data._meta.load && !data._meta.load[Symbol.for('done')]
    ? data._meta.load.then(reloadedData => storeValueProxy(reloadedData))
    : Promise.resolve(result)

  // Use a shallow clone of _meta, since we don't want to overwrite the ._meta.load promise or self link in the Vuex store
  result._meta = { ...data._meta, load: loadedPromise, self: API_ROOT + data._meta.self }
  return result
}
<commit_msg>set done-symbol on promise<commit_after>import urltemplate from 'url-template'
import { API_ROOT, get } from '@/plugins/store/apiPlugin'

function isEqualIgnoringOrder (array, other) {
  return array.length === other.length && array.every(elem => other.includes(elem))
}

/**
 * A templated link in the Vuex store looks like this: { href: '/some/uri{/something}', templated: true }
 * @param object         to be examined
 * @returns boolean      true if the object looks like a templated link, false otherwise
 */
function isTemplatedLink (object) {
  if (!object) return false
  return isEqualIgnoringOrder(Object.keys(object), ['href', 'templated']) && (object.templated === true)
}

/**
 * An entity reference in the Vuex store looks like this: { href: '/some/uri' }
 * @param object    to be examined
 * @returns boolean true if the object looks like an entity reference, false otherwise
 */
function isEntityReference (object) {
  if (!object) return false
  return isEqualIgnoringOrder(Object.keys(object), ['href'])
}

function containsLoadingEntityReference (array) {
  return array.some(entry => isEntityReference(entry) && get(entry.href)._meta.loading)
}

/**
 * A standalone collection in the Vuex store has an items property that is an array.
 * @param object    to be examined
 * @returns boolean true if the object looks like a standalone collection, false otherwise
 */
function isCollection (object) {
  return !!(object && Array.isArray(object.items))
}

/**
 * Creates a placeholder for an entity which has not yet finished loading from the API.
 * Such a loadingProxy can safely be used in Vue components, since it will render as an empty
 * string and Vue's reactivity system will replace it with the real data once that is available.
 *
 * Accessing nested functions in a loadingProxy yields another loadingProxy:
 * loadingProxy(...).campType().organization() // gives another loadingProxy
 *
 * Using a loadingProxy or a property of a loadingProxy in a view renders to empty strings:
 * let user = loadingProxy(...)
 * 'The "' + user + '" is called "' + user.name + '"' // gives 'The "" is called ""'
 *
 * @param entityLoaded a Promise that resolves to a storeValueProxy when the entity has finished
 *                     loading from the API
 * @param uri          optional URI of the entity being loaded, if available. If passed, the
 *                     returned loadingProxy will return it in calls to .self and ._meta.self
 * @returns object     a loadingProxy
 */
function loadingProxy (entityLoaded, uri = null) {
  const handler = {
    get: function (target, prop, _) {
      if (prop === Symbol.for('isLoadingProxy')) {
        return true
      }
      if (prop === Symbol.toPrimitive) {
        return () => ''
      }
      if (['then', 'toJSON', Symbol.toStringTag, 'state', 'getters', '$options', '_isVue', '__file', 'render', 'constructor'].includes(prop)) {
        // This is necessary so that Vue's reactivity system understands to treat this loadingProxy
        // like a normal object.
        return undefined
      }
      if (prop === 'loading') {
        return true
      }
      if (prop === 'load') {
        return entityLoaded
      }
      if (prop === 'self') {
        return uri !== null ? API_ROOT + uri : uri
      }
      if (prop === '_meta') {
        // When _meta is requested on a loadingProxy, we keep on using the unmodified promise, because ._meta.load
        // is supposed to resolve to the whole object, not just the ._meta part of it
        return loadingProxy(entityLoaded, uri)
      }
      const propertyLoaded = entityLoaded.then(entity => entity[prop])
      if (['items', 'allItems'].includes(prop)) {
        return loadingArrayProxy(propertyLoaded)
      }
      // Normal property access: return a function that yields another loadingProxy and renders as empty string
      const result = templateParams => loadingProxy(propertyLoaded.then(property => property(templateParams)._meta.load))
      result.loading = true
      result.toString = () => ''
      return result
    }
  }
  return new Proxy({}, handler)
}

/**
 * Returns a placeholder for an array that has not yet finished loading from the API. The array placeholder
 * will respond to functional calls (like .find(), .map(), etc.) with further loading array proxies or
 * loading proxies. If passed the existingContent argument, random access and .length will also work.
 * @param arrayLoaded     Promise that resolves once the array has finished loading
 * @param existingContent optionally set the elements that are already known, for random access
 * @returns Array         a proxy that can act as a placeholder for an array that is still being loaded from the API
 */
function loadingArrayProxy (arrayLoaded, existingContent = []) {
  const singleResultFunctions = ['find']
  const arrayResultFunctions = ['map', 'flatMap', 'filter']
  singleResultFunctions.forEach(func => {
    existingContent[func] = (...args) => {
      const resultLoaded = arrayLoaded.then(array => array[func](...args))
      return loadingProxy(resultLoaded)
    }
  })
  arrayResultFunctions.forEach(func => {
    existingContent[func] = (...args) => {
      const resultLoaded = arrayLoaded.then(array => array[func](...args))
      return loadingArrayProxy(resultLoaded)
    }
  })
  return existingContent
}

function filterDeleting (array) {
  return array.filter(entry => !entry._meta.deleting)
}

/**
 * Given an array, replaces any entity references in the array with the entity loaded from the Vuex store
 * (or from the API if necessary), and returns that as a new array. In case some of the entity references in
 * the array have not finished loading yet, returns a loadingArrayProxy instead.
 * @param array   possibly mixed array of values and references
 * @returns array the new array with replaced items, or a loadingArrayProxy if any of the array elements
 *                is still loading.
 */
function mapArrayOfEntityReferences (array) {
  const arrayWithReplacedReferences = array.map(entry => {
    if (isEntityReference(entry)) {
      return get(entry.href)
    }
    return entry
  })
  if (containsLoadingEntityReference(array)) {
    const arrayCompletelyLoaded = Promise.all(array.map(entry => {
      if (isEntityReference(entry)) {
        return get(entry.href)._meta.load
      }
      return Promise.resolve(entry)
    }))
    return loadingArrayProxy(arrayCompletelyLoaded, arrayWithReplacedReferences)
  } else {
    return arrayWithReplacedReferences
  }
}

/**
 * Defines a property getter for the items property of a given target object.
 * The items property should always be a getter, in order to make the call to mapArrayOfEntityReferences
 * lazy, since that potentially fetches a large number of entities from the API.
 * @param target   object on which the items getter should be defined
 * @param items    array of items, which can be mixed primitive values and entity references
 * @returns object the target object with the added getter
 */
function addItemsGetter (target, items) {
  Object.defineProperty(target, 'items', { get: () => filterDeleting(mapArrayOfEntityReferences(items)) })
  Object.defineProperty(target, 'allItems', { get: () => mapArrayOfEntityReferences(items) })
  return target
}

/**
 * Imitates a full standalone collection with an items property, even if there is no separate URI (as it
 * is the case with embedded collections).
 * Reloading an embedded collection requires special information. Since the embedded collection has no own
 * URI, we need to reload the whole entity containing the embedded collection. Some extra info about the
 * containing entity must therefore be passed to this function.
 * @param items          array of items, which can be mixed primitive values and entity references
 * @param reloadUri      URI of the entity containing the embedded collection (for reloading)
 * @param reloadProperty property in the containing entity under which the embedded collection is saved
 * @returns object the imitated collection object
 */
function embeddedCollectionProxy (items, reloadUri, reloadProperty) {
  const result = addItemsGetter({ _meta: { reload: { uri: reloadUri, property: reloadProperty } } }, items)
  result._meta.load = Promise.resolve(result)
  return result
}

/**
 * Takes data from the Vuex store and makes it more usable in frontend components. The data stored
 * in the Vuex store should always be JSON serializable according to
 * https://github.com/vuejs/vuex/issues/757#issuecomment-297668640. Therefore, we wrap the data into
 * a new object, and provide accessor methods for related entities. Such an accessor method fetches the
 * related entity from the Vuex store (or the API if necessary) when called. In case the related entity
 * is still being loaded from the API, a loadingProxy is returned.
 *
 * Example:
 * // Data of an entity like it comes from the Vuex store:
 * let storeData = {
 *   numeric_property: 3,
 *   reference_to_other_entity: {
 *     href: '/uri/of/other/entity'
 *   },
 *   _meta: {
 *     self: '/self/uri'
 *   }
 * }
 * // Apply storeValueProxy
 * let usable = storeValueProxy(storeData)
 * // Now we can use accessor methods
 * usable.reference_to_other_entity() // returns the result of this.api.get('/uri/of/other/entity')
 *
 * @param data                entity data from the Vuex store
 * @returns object            wrapped entity ready for use in a frontend component
 */
export default function storeValueProxy (data) {
  const meta = data._meta || { load: Promise.resolve() }

  if (meta.loading) {
    const entityLoaded = meta.load.then(loadedData => createStoreValueProxy(loadedData))
    return loadingProxy(entityLoaded, meta.self)
  }

  return createStoreValueProxy(data)
}

/**
 * Creates an actual storeValueProxy, by wrapping the given Vuex store data. The data must not be loading.
 * If the data has been loaded into the store before but is currently reloading, the old data will be
 * returned, along with a ._meta.load promise that resolves when the reload is complete.
 * @param data fully loaded entity data from the Vuex store
 */
function createStoreValueProxy (data) {
  const result = {}
  Object.keys(data).forEach(key => {
    const value = data[key]
    if (key === 'allItems' && isCollection(data)) return
    if (key === 'items' && isCollection(data)) {
      addItemsGetter(result, data[key])
    } else if (Array.isArray(value)) {
      result[key] = () => embeddedCollectionProxy(value, data._meta.self, key)
    } else if (isEntityReference(value)) {
      result[key] = () => get(value.href)
    } else if (isTemplatedLink(value)) {
      result[key] = templateParams => get(urltemplate.parse(value.href).expand(templateParams || {}))
    } else {
      result[key] = value
    }
  })

  // Use a trivial load promise to break endless recursion, except if we are currently reloading the data from the API
  const loadedPromise = data._meta.load && !data._meta.load[Symbol.for('done')]
    ? data._meta.load.then(reloadedData => storeValueProxy(reloadedData))
    : createResolvedPromise(result)

  // Use a shallow clone of _meta, since we don't want to overwrite the ._meta.load promise or self link in the Vuex store
  result._meta = { ...data._meta, load: loadedPromise, self: API_ROOT + data._meta.self }
  return result
}

/**
 * Creates a resolved Promise; sets the Done-Flag
 * @param value Promise return value
 */
function createResolvedPromise (value) {
  const promise = Promise.resolve(value)
  promise[Symbol.for('done')] = true
  return promise
}
<|endoftext|>


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

import _ from 'lodash';
import CodeExample from './CodeExample';

export default class ConditionalLanguage extends React.Component {
  render() {
    const lang = this.props.lang || 'ruby';

    const children = this.props.children;
    const target = _.first(children, (c) => { c.props.lang === lang });

    return (
{target}
);
  }
}
<commit_msg>fix logic in selecting the correct language<commit_after>import React from 'react';
import ReactDOM from 'react-dom';

import _ from 'lodash';
import CodeExample from './CodeExample';

export default class ConditionalLanguage extends React.Component {
  render() {
    const lang = this.props.lang || 'ruby';

    const children = this.props.children;
    const target = _.find(children, (c) => { return c.props.lang === lang; });

    return (
{target}
);
  }
}
<|endoftext|>


<commit_before>import { oneIndex } from 'frontend/helpers/one-index';
import { module, test } from 'qunit';

module('Unit | Helper | one index');

// Replace this with your real tests.
test('Valid integers', function(assert) {
  let result = oneIndex([42]);
  assert.ok(result);
});
<commit_msg>Actual unit tests for one-index helper.<commit_after>import { oneIndex } from 'frontend/helpers/one-index';
import { module, test } from 'qunit';

module('Unit | Helper | one index');

// Replace this with your real tests.
test('one-index works with valid integers', function(assert) {
  let result = oneIndex([42]);
  assert.strictEqual(result, 43);

  result = oneIndex([-3]);
  assert.strictEqual(result, -2);

  result = oneIndex([0]);
  assert.strictEqual(result, 1);
});

test('one-index ignores what it does not understand', function(assert) {

  let result = oneIndex(["here is a string"]);
  assert.strictEqual(result, "here is a string");

  result = oneIndex([null]);
  assert.strictEqual(result, null);

  result = oneIndex([""]);
  assert.strictEqual(result, "");
});
<|endoftext|>


<commit_before>// @flow

import React, { Component } from 'react';
import { FlexRow } from 'app/components/FlexBox';
import Button from 'app/components/Button';
import LoadingIndicator from 'app/components/LoadingIndicator';
import { ConfirmModalWithParent } from 'app/components/Modal/ConfirmModal';
import styles from './PageEditor.css';
import {
  EditorField,
  TextInput,
  Form,
  withSubmissionError,
  SelectInput
} from 'app/components/Form';
import ImageUpload from 'app/components/Upload/ImageUpload';
import NavigationTab, { NavigationLink } from 'app/components/NavigationTab';
import { Field } from 'redux-form';
import { Content } from 'app/components/Content';
import { get } from 'lodash';
import { categoryOptions } from 'app/reducers/pages';

type Page = {
  title: string,
  content: string,
  picture?: string,
  category: string
};

export type Props = {
  page: Page,
  pageSlug: string,
  isNew: boolean,
  loggedIn: boolean,
  currentUser: any,
  uploadFile: ({ file: string, isPublic: boolean }) => Promise<*>,
  handleSubmit: Function => Promise<*>,
  updatePage: (string, Page) => Promise<*>,
  createPage: Page => Promise<*>,
  deletePage: (slug: string) => Promise<*>,
  push: string => void,
  initialized: boolean
};

type State = {
  page: {
    picture: string,
    content: string,
    category: string
  },
  images: { [key: string]: string }
};

export default class PageEditor extends Component<Props, State> {
  state = {
    page: {
      picture: get(this.props, ['page', 'picture']),
      content: get(this.props, ['page', 'content']),
      category: get(this.props, ['page', 'category'])
    },
    images: {}
  };

  setPicture = (image: any) => {
    this.props.uploadFile({ file: image, isPublic: true }).then(action => {
      const file = action.meta.fileToken;
      this.setState({
        images: {
          ...this.state.images,
          [file]: window.URL.createObjectURL(image)
        },
        page: { ...this.state.page, picture: file }
      });
    });
  };

  onDelete = () => {
    const { push, pageSlug, deletePage } = this.props;
    deletePage(pageSlug).then(() => push('/pages/info/om-oss'));
  };

  onSubmit = (data: Page) => {
    const body = {
      title: data.title,
      content: data.content,
      picture: undefined,
      category: data.category
    };

    const { push, pageSlug } = this.props;

    if (this.state.images[this.state.page.picture]) {
      body.picture = this.state.page.picture;
    } else {
      delete body.picture;
    }

    if (this.props.isNew) {
      return this.props.createPage(body).then(result => {
        const slug = result.payload.result;
        const pageCategory = result.payload.entities.pages[slug].category;
        push(`/pages/${pageCategory}/${slug}`);
      });
    }

    return this.props.updatePage(pageSlug, body).then(result => {
      const slug = result.payload.result;
      const pageCategory = result.payload.entities.pages[slug].category;
      push(`/pages/${pageCategory}/${slug}`);
    });
  };

  render() {
    const { isNew, uploadFile, handleSubmit, page, pageSlug } = this.props;
    const { images } = this.state;
    if (!isNew && !page) {
      return <LoadingIndicator loading />;
    }
    const backUrl = isNew
      ? '/pages/info-om-abakus'
      : `/pages/${page.category}/${pageSlug}`;

    return (
      <Content>
        <NavigationTab title={page.title}>
          <NavigationLink to={backUrl}>
             Tilbake
          </NavigationLink>
        </NavigationTab>
        <Form onSubmit={handleSubmit(withSubmissionError(this.onSubmit))}>

            <ImageUpload
              aspectRatio={20 / 6}
              onSubmit={this.setPicture}
              img={
                images[this.state.page.picture]
                  ? images[this.state.page.picture]
                  : page.picture
              }
            />

          <FlexRow alignItems="baseline" justifyContent="space-between">
            <Field
              placeholder="Title"
              name="title"
              component={TextInput.Field}
              id="page-title"
            />
            <Field
              name="category"
              component={SelectInput.Field}
              placeholder="Velg kategori"
              simpleValue
              options={categoryOptions}
            />

            {!isNew && (
              /* $FlowFixMe */
              <ConfirmModalWithParent
                title="Slett side"
                message="Er du sikker på at du vil slette denne infosiden?"
                onConfirm={this.onDelete}
              >
                <Button>Slett</Button>
              </ConfirmModalWithParent>
            )}
            <Button className={styles.submitButton} type="submit">
              {isNew ? 'Create' : 'Save'}
            </Button>
          </FlexRow>

          <Field
            placeholder="Write page content here..."
            name="content"
            component={EditorField.Field}
            uploadFile={uploadFile}
            initialized={this.props.initialized}
          />
        </Form>
      </Content>
    );
  }
}
<commit_msg>Fix editor in pagecreation route<commit_after>// @flow

import React, { Component } from 'react';
import { FlexRow } from 'app/components/FlexBox';
import Button from 'app/components/Button';
import LoadingIndicator from 'app/components/LoadingIndicator';
import { ConfirmModalWithParent } from 'app/components/Modal/ConfirmModal';
import styles from './PageEditor.css';
import {
  EditorField,
  TextInput,
  Form,
  withSubmissionError,
  SelectInput
} from 'app/components/Form';
import ImageUpload from 'app/components/Upload/ImageUpload';
import NavigationTab, { NavigationLink } from 'app/components/NavigationTab';
import { Field } from 'redux-form';
import { Content } from 'app/components/Content';
import { get } from 'lodash';
import { categoryOptions } from 'app/reducers/pages';

type Page = {
  title: string,
  content: string,
  picture?: string,
  category: string
};

export type Props = {
  page: Page,
  pageSlug: string,
  isNew: boolean,
  loggedIn: boolean,
  currentUser: any,
  uploadFile: ({ file: string, isPublic: boolean }) => Promise<*>,
  handleSubmit: Function => Promise<*>,
  updatePage: (string, Page) => Promise<*>,
  createPage: Page => Promise<*>,
  deletePage: (slug: string) => Promise<*>,
  push: string => void,
  initialized: boolean
};

type State = {
  page: {
    picture: string,
    content: string,
    category: string
  },
  images: { [key: string]: string }
};

export default class PageEditor extends Component<Props, State> {
  state = {
    page: {
      picture: get(this.props, ['page', 'picture']),
      content: get(this.props, ['page', 'content']),
      category: get(this.props, ['page', 'category'])
    },
    images: {}
  };

  setPicture = (image: any) => {
    this.props.uploadFile({ file: image, isPublic: true }).then(action => {
      const file = action.meta.fileToken;
      this.setState({
        images: {
          ...this.state.images,
          [file]: window.URL.createObjectURL(image)
        },
        page: { ...this.state.page, picture: file }
      });
    });
  };

  onDelete = () => {
    const { push, pageSlug, deletePage } = this.props;
    deletePage(pageSlug).then(() => push('/pages/info/om-oss'));
  };

  onSubmit = (data: Page) => {
    const body = {
      title: data.title,
      content: data.content,
      picture: undefined,
      category: data.category
    };

    const { push, pageSlug } = this.props;

    if (this.state.images[this.state.page.picture]) {
      body.picture = this.state.page.picture;
    } else {
      delete body.picture;
    }

    if (this.props.isNew) {
      return this.props.createPage(body).then(result => {
        const slug = result.payload.result;
        const pageCategory = result.payload.entities.pages[slug].category;
        push(`/pages/${pageCategory}/${slug}`);
      });
    }

    return this.props.updatePage(pageSlug, body).then(result => {
      const slug = result.payload.result;
      const pageCategory = result.payload.entities.pages[slug].category;
      push(`/pages/${pageCategory}/${slug}`);
    });
  };

  render() {
    const { isNew, uploadFile, handleSubmit, page, pageSlug } = this.props;
    const { images } = this.state;
    if (!isNew && !page) {
      return <LoadingIndicator loading />;
    }
    const backUrl = isNew
      ? '/pages/info-om-abakus'
      : `/pages/${page.category}/${pageSlug}`;

    return (
      <Content>
        <NavigationTab title={page.title}>
          <NavigationLink to={backUrl}>
             Tilbake
          </NavigationLink>
        </NavigationTab>
        <Form onSubmit={handleSubmit(withSubmissionError(this.onSubmit))}>

            <ImageUpload
              aspectRatio={20 / 6}
              onSubmit={this.setPicture}
              img={
                images[this.state.page.picture]
                  ? images[this.state.page.picture]
                  : page.picture
              }
            />

          <FlexRow alignItems="baseline" justifyContent="space-between">
            <Field
              placeholder="Title"
              name="title"
              component={TextInput.Field}
              id="page-title"
            />
            <Field
              name="category"
              component={SelectInput.Field}
              placeholder="Velg kategori"
              simpleValue
              options={categoryOptions}
            />

            {!isNew && (
              /* $FlowFixMe */
              <ConfirmModalWithParent
                title="Slett side"
                message="Er du sikker på at du vil slette denne infosiden?"
                onConfirm={this.onDelete}
              >
                <Button>Slett</Button>
              </ConfirmModalWithParent>
            )}
            <Button className={styles.submitButton} type="submit">
              {isNew ? 'Create' : 'Save'}
            </Button>
          </FlexRow>

          <Field
            placeholder="Write page content here..."
            name="content"
            component={EditorField.Field}
            uploadFile={uploadFile}
            initialized={this.props.initialized || isNew}
          />
        </Form>
      </Content>
    );
  }
}
<|endoftext|>


<commit_before><commit_msg>re-added stage.setSpin/.setRock but with simple boolean arguments<commit_after><|endoftext|>


<commit_before>function sendAnswer(terms, side) {

  let [ termA, termB, term1, term2 ] = terms;

  if (side == 'left') {
    chosen = [termA, term1, termB, term2];
    other  = [termA, term2, termB, term1];
  } else {
    chosen = [termA, term2, termB, term1];
    other  = [termA, term1, termB, term2];
  }

  const req = new XMLHttpRequest();

  req.open('POST', '/answer');
  req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

  req.onload = function() {
    if (req.status !== 200) {
      alert(`An error (${req.status}) occured.`);
    } else {
      const results = JSON.parse(req.response);

      addAnswer(chosen, results, side);
    }
  };

  const chosenAnswer = chosen.join('-');
  const otherAnswer  = other.join('-');

  req.send(`chosen=${chosenAnswer}&other=${otherAnswer}`);
}

function renderTerms(terms) {
  const [ termA, termB, term1, term2 ] = terms;
  return `

${termA}

${termB}

${term1}

${term2}

  `;
}

function addAnswer(chosen, results, side) {
  const answers = document.querySelector('#answers');
  const line = document.createElement('div');
  const chosenCount = parseInt(results.chosen);
  const totalCount = parseInt(results.total);
  const percent = Math.floor((chosenCount / totalCount) * 100);
  const success = (percent > 50);
  const result = success ? 'Well done' : 'Wrong';
  line.classList.add('line');

  line.innerHTML  = '
X/10
';
  line.innerHTML += `<div class="terms ${side}Choice">` + renderTerms(chosen) + '
';
  line.innerHTML += `<div class="score">${result}<br>Score: X/Y<br>On ${totalCount} players<br>${percent}% chose like you<br>> The Google Truth</div>`;

  answers.insertBefore(line, answers.firstChild);
}

function displayQuestion() {

  fetch('/couples')
    .then(function(response) {
      return response.json();
    }).then(function(json) {

      const termA = json[0].firstTerm.en;
      const termB = json[0].secondTerm.en;
      const term1 = json[1].firstTerm.en;
      const term2 = json[1].secondTerm.en;

      const termsElement = document.querySelector('#terms');
      const termAElement = document.querySelector('#termA');
      const termBElement = document.querySelector('#termB');
      const termBResult  = document.querySelector('#termB');
      const term1Element = document.querySelector('#term1');
      const term2Element = document.querySelector('#term2');
      const leftZone     = document.querySelector('#leftZone');
      const rightZone    = document.querySelector('#rightZone');

      termAElement.innerHTML = termA;
      termBElement.innerHTML = termB;
      term1Element.innerHTML = term1;
      term2Element.innerHTML = term2;

      leftZone.addEventListener('mouseenter', function() {
        terms.classList.add('leftChoice');
      });
      leftZone.addEventListener('mouseleave', function() {
        terms.classList.remove('leftChoice');
      });
      rightZone.addEventListener('mouseenter', function() {
        terms.classList.add('rightChoice');
      });
      rightZone.addEventListener('mouseleave', function() {
        terms.classList.remove('rightChoice');
      });

      leftZone.addEventListener('click', function() {
        sendAnswer([termA, termB, term1, term2], 'left');
      });
      rightZone.addEventListener('click', function() {
        sendAnswer([termA, termB, term1, term2], 'right');
      });
    });
}

document.addEventListener('DOMContentLoaded', function() {

  displayQuestion();

})
<commit_msg>Fix displaying correct answers in history<commit_after>function sendAnswer(terms, side) {

  let [ termA, termB, term1, term2 ] = terms;

  if (side == 'left') {
    chosen = [termA, term1, termB, term2];
    other  = [termA, term2, termB, term1];
  } else {
    chosen = [termA, term2, termB, term1];
    other  = [termA, term1, termB, term2];
  }

  const req = new XMLHttpRequest();

  req.open('POST', '/answer');
  req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

  req.onload = function() {
    if (req.status !== 200) {
      alert(`An error (${req.status}) occured.`);
    } else {
      const results = JSON.parse(req.response);

      addAnswer(terms, results, side);
    }
  };

  const chosenAnswer = chosen.join('-');
  const otherAnswer  = other.join('-');

  req.send(`chosen=${chosenAnswer}&other=${otherAnswer}`);
}

function renderTerms(terms) {
  let [ termA, termB, term1, term2 ] = terms;
  return `

${termA}

${termB}

${term1}

${term2}

  `;
}

function addAnswer(terms, results, side) {
  const answers = document.querySelector('#answers');
  const line = document.createElement('div');
  const chosenCount = parseInt(results.chosen);
  const totalCount = parseInt(results.total);
  const percent = Math.floor((chosenCount / totalCount) * 100);
  const success = (percent > 50);
  const result = success ? 'Well done' : 'Wrong';
  line.classList.add('line');

  line.innerHTML  = '
X/10
';
  line.innerHTML += `<div class="terms ${side}Choice">` + renderTerms(terms) + '
';
  line.innerHTML += `<div class="score">${result}<br>Score: X/Y<br>On ${totalCount} players<br>${percent}% chose like you<br>> The Google Truth</div>`;

  answers.insertBefore(line, answers.firstChild);
}

function displayQuestion() {

  fetch('/couples')
    .then(function(response) {
      return response.json();
    }).then(function(json) {

      const termA = json[0].firstTerm.en;
      const termB = json[0].secondTerm.en;
      const term1 = json[1].firstTerm.en;
      const term2 = json[1].secondTerm.en;

      const termsElement = document.querySelector('#terms');
      const termAElement = document.querySelector('#termA');
      const termBElement = document.querySelector('#termB');
      const termBResult  = document.querySelector('#termB');
      const term1Element = document.querySelector('#term1');
      const term2Element = document.querySelector('#term2');
      const leftZone     = document.querySelector('#leftZone');
      const rightZone    = document.querySelector('#rightZone');

      termAElement.innerHTML = termA;
      termBElement.innerHTML = termB;
      term1Element.innerHTML = term1;
      term2Element.innerHTML = term2;

      leftZone.addEventListener('mouseenter', function() {
        terms.classList.add('leftChoice');
      });
      leftZone.addEventListener('mouseleave', function() {
        terms.classList.remove('leftChoice');
      });
      rightZone.addEventListener('mouseenter', function() {
        terms.classList.add('rightChoice');
      });
      rightZone.addEventListener('mouseleave', function() {
        terms.classList.remove('rightChoice');
      });

      leftZone.addEventListener('click', function() {
        sendAnswer([termA, termB, term1, term2], 'left');
      });
      rightZone.addEventListener('click', function() {
        sendAnswer([termA, termB, term1, term2], 'right');
      });
    });
}

document.addEventListener('DOMContentLoaded', function() {

  displayQuestion();

})
<|endoftext|>


<commit_before><commit_msg>added stage.setSize method<commit_after><|endoftext|>


<commit_before>import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { NavLink } from 'react-router-dom'

import { translate } from '../../../common/services/i18n'
import { withConfig } from '../containers'

const PRIMARY_NAVIGATION = [
  { localeKey: 'home', url: '/' },
  { localeKey: 'about', url: '/pages/about' },
  { localeKey: 'calendar', url: '/calendar' },
]

const SECONDARY_NAVIGATION = [
  { localeKey: 'information', url: '/pages/information' },
  { localeKey: 'contact', url: '/pages/contact' },
]

const CURATION_NAVIGATION = [
  { localeKey: 'places', url: '/places' },
  { localeKey: 'resources', url: '/resources' },
  { localeKey: 'stream', url: '/stream', withConfig: 'gifStreamServerUrl' },
]

const ADMIN_NAVIGATION = CURATION_NAVIGATION.concat([
  { localeKey: 'admin', url: '/admin' },
])

const PARTICIPANT_NAVIGATION = CURATION_NAVIGATION

const VISITOR_NAVIGATION = [
  { localeKey: 'stream', url: '/stream', withConfig: 'gifStreamServerUrl' },
]

const DEFAULT_NAVIGATION = [
  { localeKey: 'tickets', url: '/tickets', withConfig: 'isSignUpVisitorEnabled' },
]

class NavigationLinks extends Component {
  static propTypes = {
    config: PropTypes.object.isRequired,
    isAdmin: PropTypes.bool.isRequired,
    isParticipant: PropTypes.bool.isRequired,
    isVisitor: PropTypes.bool.isRequired,
  }

  renderNavigationItems(navigation) {
    return navigation.map((navigationItem, index) => {
      // Required config was not given or falsly
      if (
        ('withConfig' in navigationItem) && (
          !(navigationItem.withConfig in this.props.config) ||
          !this.props.config[navigationItem.withConfig]
        )
      ) {
        return null
      }

      return (

          <NavLink to={navigationItem.url}>
            { translate(
              `components.navigationLinks.${navigationItem.localeKey}`
            ) }
          </NavLink>

      )
    })
  }

  renderNavigation(navigation) {
    return (

        {
          this.renderNavigationItems(
            PRIMARY_NAVIGATION
              .concat(navigation)
              .concat(SECONDARY_NAVIGATION)
          )
        }

    )
  }

  render() {
    if (this.props.isAdmin) {
      return this.renderNavigation(ADMIN_NAVIGATION)
    } else if (this.props.isParticipant) {
      return this.renderNavigation(PARTICIPANT_NAVIGATION)
    } else if (this.props.isVisitor) {
      return this.renderNavigation(VISITOR_NAVIGATION)
    }

    return this.renderNavigation(DEFAULT_NAVIGATION)
  }
}

function mapStateToProps(state) {
  return {
    isAdmin: state.user.isAdmin,
    isParticipant: state.user.isParticipant && state.user.isActive,
    isVisitor: state.user.isVisitor && state.user.isActive,
  }
}

export default connect(
  mapStateToProps
)(withConfig(NavigationLinks))
<commit_msg>Show Places in nav bar if festival is free<commit_after>import PropTypes from 'prop-types'
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { NavLink } from 'react-router-dom'

import { translate } from '../../../common/services/i18n'
import { withConfig } from '../containers'

const PRIMARY_NAVIGATION = [
  { localeKey: 'home', url: '/' },
  { localeKey: 'about', url: '/pages/about' },
  { localeKey: 'calendar', url: '/calendar' },
]

const SECONDARY_NAVIGATION = [
  { localeKey: 'information', url: '/pages/information' },
  { localeKey: 'contact', url: '/pages/contact' },
]

const CURATION_NAVIGATION = [
  { localeKey: 'places', url: '/places' },
  { localeKey: 'resources', url: '/resources' },
  { localeKey: 'stream', url: '/stream', withConfig: 'gifStreamServerUrl' },
]

const ADMIN_NAVIGATION = CURATION_NAVIGATION.concat([
  { localeKey: 'admin', url: '/admin' },
])

const PARTICIPANT_NAVIGATION = CURATION_NAVIGATION

const PLACES_PUBLIC_NAVIGATION = [
  { localeKey: 'places', url: '/places' },
]

const VISITOR_NAVIGATION = [
  { localeKey: 'stream', url: '/stream', withConfig: 'gifStreamServerUrl' },
]

const DEFAULT_NAVIGATION = [
  { localeKey: 'tickets', url: '/tickets', withConfig: 'isSignUpVisitorEnabled' },
]

class NavigationLinks extends Component {
  static propTypes = {
    config: PropTypes.object.isRequired,
    isAdmin: PropTypes.bool.isRequired,
    isParticipant: PropTypes.bool.isRequired,
    isVisitor: PropTypes.bool.isRequired,
  }

  renderNavigationItems(navigation) {
    return navigation.map((navigationItem, index) => {
      // Required config was not given or falsly
      if (
        ('withConfig' in navigationItem) && (
          !(navigationItem.withConfig in this.props.config) ||
          !this.props.config[navigationItem.withConfig]
        )
      ) {
        return null
      }

      return (

          <NavLink to={navigationItem.url}>
            { translate(
              `components.navigationLinks.${navigationItem.localeKey}`
            ) }
          </NavLink>

      )
    })
  }

  renderNavigation(navigation) {
    return (

        {
          this.renderNavigationItems(
            PRIMARY_NAVIGATION
              .concat(navigation)
              .concat(SECONDARY_NAVIGATION)
          )
        }

    )
  }

  render() {
    if (this.props.isAdmin) {
      return this.renderNavigation(ADMIN_NAVIGATION)
    } else if (this.props.isParticipant) {
      return this.renderNavigation(PARTICIPANT_NAVIGATION)
    } else if (this.props.isVisitor) {
      return this.renderNavigation(VISITOR_NAVIGATION)
    } else if (this.props.config.festivalTicketPrice === 0) {
      return this.renderNavigation(PLACES_PUBLIC_NAVIGATION)
    }

    return this.renderNavigation(DEFAULT_NAVIGATION)
  }
}

function mapStateToProps(state) {
  return {
    isAdmin: state.user.isAdmin,
    isParticipant: state.user.isParticipant && state.user.isActive,
    isVisitor: state.user.isVisitor && state.user.isActive,
  }
}

export default connect(
  mapStateToProps
)(withConfig(NavigationLinks))
<|endoftext|>


<commit_before>'use strict';

angular.module('main')
.controller('FilingsCtrl', function($rootScope, $scope, $stateParams, $backend, filings) {
    $scope.filings = filings.Archives;

    $scope.getExportURL = function(format){
        return $backend.API_URL + '/_queries/public/api/filings.jq?cik=' + $scope.cik + '&format=' + format + '&_method=POST&token=' + $rootScope.token;
    };
});
<commit_msg>Fix filings export link.<commit_after>'use strict';

angular.module('main')
.controller('FilingsCtrl', function($rootScope, $scope, $stateParams, $backend, filings) {
    $scope.filings = filings.Archives;

    $scope.getExportURL = function(format){
        return $backend.API_URL + '/_queries/public/api/filings.jq?cik=' + $scope.cik + '&format=' + format + '&_method=POST&token=' + $rootScope.token + '&fiscalYear=ALL&fiscalPeriod=ALL';
    };
});
<|endoftext|>


<commit_before>mainApp.controller('WordLookupQuizCtrl', function ($scope, $rootScope, $http, $modal, $route,
                                                   $location, dictionaryService, imageSearchService, $timeout, wordLookupService,
                                                   user, userService, $sce, utilityService) {

    // status results, handling front-end components
    $scope.flags = {
        STATUSES:{
            ONGOING:    0,
            FINISHED:   1
        },
        STEPS : {
            QUESTION:   0,
            RESULT:     1
        },
        HINT : {
            INACTIVE:   0,
            SHOWING:    1,
            LOADING:    2
        }
    };

    $scope.quiz = {
        step: 0,
        status: 0,
        wordCursor: 0,
        word: {},
        words: [],
        hint: {
            status: $scope.flags.HINT.INACTIVE,
            images: []
        },
        answer: {
            guess: '',
            result: false
        }
    };

    $scope.init = function(){

        // init variables
        var words = $rootScope.words;
        $rootScope.words = null;

        // if doesn't have the words in rootScope, then get all word-lookups from the server
        if(!words){
            // get user word-lookups
            userService.wordLookups(function(result){
                var shuffledWords = utilityService.shuffle(result);
                $scope.quiz.word = shuffledWords[0];
                $scope.quiz.words = shuffledWords;
                $scope.startGame();
            });
        }

        else {
            var shuffledWords = utilityService.shuffle(words);
            $scope.quiz.word = shuffledWords[0];
            $scope.quiz.words = shuffledWords;
            $scope.startGame();
        }
    }

    $scope.startGame = function(){

        if($scope.quiz.words.length == 0)
            $location.path('/word-lookup');

        $scope.getDefinition();
    }

    $scope.getSafeUrl = function (url){
        return $sce.trustAsResourceUrl(url);
    }

    $scope.getDefinition = function(){
        var word = $scope.quiz.word.word_lookup_word;
        dictionaryService.definition(word, function(result){
            if(result.status){

                var response;

                try {
                    response = eval(result.result);
                } catch(e) {}

                $scope.quiz.word.lookupResult = response;
            }
        })
    }

    // show meaning
    $scope.showResult = function(){

        // check if the answer is correct
        if($scope.quiz.word.word_lookup_word == $scope.quiz.answer.guess)
            $scope.quiz.answer.result = true;
        else
            $scope.quiz.answer.result = false;

        // save word guess result
        $scope.quiz.word.word_lookup_quiz_taken = 1;
        $scope.quiz.word.word_lookup_quiz_result = $scope.quiz.answer.result ? 1 : 0;
        wordLookupService.save($scope.quiz.word, function(result) {});

        // reset hint
        $scope.quiz.hint.images = [];
        $scope.quiz.hint.status = $scope.flags.HINT.INACTIVE;

        // go to result step
        $scope.changeStep($scope.flags.STEPS.RESULT);
    }

    $scope.giveHint = function(){
        $scope.quiz.hint.status = $scope.flags.HINT.LOADING;
        imageSearchService.search($scope.quiz.word.word_lookup_word, function(result){
            $timeout(function() {
                $scope.quiz.hint.status = $scope.flags.HINT.SHOWING;
                $scope.quiz.hint.images = result;
            });
        });
    }

    // user clicks on correct or wrong
    $scope.nextQuestion = function(answer){
        // remove the answer for previous word
        $scope.quiz.answer.guess = '';

        // hide previous word's result screen
        $scope.changeStep($scope.flags.STEPS.QUESTION);

        // show the next word
        $scope.quiz.wordCursor++;

        // if answered all the questions, finish the quiz
        if($scope.quiz.words.length == $scope.quiz.wordCursor)
            $scope.changeStatus($scope.flags.STATUSES.FINISHED);

        // otherwise time to ask next word
        else {
            $scope.quiz.word = $scope.quiz.words[$scope.quiz.wordCursor];
            $scope.getDefinition();
        }
    }

    $scope.changeStep = function (step) {
        $scope.quiz.step = step;
    };

    $scope.changeStatus = function (status) {
        $scope.quiz.status = status;
    };

    // time to take a quiz,
    // send words that user will be take quiz on
    $scope.quizWords = function() {
        var notTakenQuiz = [];
        var words = $scope.getSelectedBookWords();
        angular.forEach(words, function(word){
            if(!word.word_lookup_quiz_taken)
                this.push(word);
        }, notTakenQuiz);
        return notTakenQuiz;
    };

    $scope.finish = function () {
        $location.path('/word-lookup');
    };

    $scope.init();
});<commit_msg>fixing hint bug<commit_after>mainApp.controller('WordLookupQuizCtrl', function ($scope, $rootScope, $http, $modal, $route,
                                                   $location, dictionaryService, imageSearchService, $timeout, wordLookupService,
                                                   user, userService, $sce, utilityService) {

    // status results, handling front-end components
    $scope.flags = {
        STATUSES:{
            ONGOING:    0,
            FINISHED:   1
        },
        STEPS : {
            QUESTION:   0,
            RESULT:     1
        },
        HINT : {
            INACTIVE:   0,
            SHOWING:    1,
            LOADING:    2
        }
    };

    $scope.quiz = {
        step: 0,
        status: 0,
        wordCursor: 0,
        word: {},
        words: [],
        hint: {
            status: $scope.flags.HINT.INACTIVE,
            images: []
        },
        answer: {
            guess: '',
            result: false
        }
    };

    $scope.init = function(){

        // init variables
        var words = $rootScope.words;
        $rootScope.words = null;

        // if doesn't have the words in rootScope, then get all word-lookups from the server
        if(!words){
            // get user word-lookups
            userService.wordLookups(function(result){
                var shuffledWords = utilityService.shuffle(result);
                $scope.quiz.word = shuffledWords[0];
                $scope.quiz.words = shuffledWords;
                $scope.startGame();
            });
        }

        else {
            var shuffledWords = utilityService.shuffle(words);
            $scope.quiz.word = shuffledWords[0];
            $scope.quiz.words = shuffledWords;
            $scope.startGame();
        }
    }

    $scope.startGame = function(){

        if($scope.quiz.words.length == 0)
            $location.path('/word-lookup');

        $scope.getDefinition();
    }

    $scope.getSafeUrl = function (url){
        return $sce.trustAsResourceUrl(url);
    }

    $scope.getDefinition = function(){
        var word = $scope.quiz.word.word_lookup_word;
        dictionaryService.definition(word, function(result){
            if(result.status){

                var response;

                try {
                    response = eval(result.result);
                } catch(e) {}

                $scope.quiz.word.lookupResult = response;
            }
        })
    }

    // show meaning
    $scope.showResult = function(){

        // check if the answer is correct
        if($scope.quiz.word.word_lookup_word == $scope.quiz.answer.guess)
            $scope.quiz.answer.result = true;
        else
            $scope.quiz.answer.result = false;

        // save word guess result
        $scope.quiz.word.word_lookup_quiz_taken = 1;
        $scope.quiz.word.word_lookup_quiz_result = $scope.quiz.answer.result ? 1 : 0;
        wordLookupService.save($scope.quiz.word, function(result) {});

        // go to result step
        $scope.changeStep($scope.flags.STEPS.RESULT);
    }

    $scope.giveHint = function(){
        $scope.quiz.hint.status = $scope.flags.HINT.LOADING;
        imageSearchService.search($scope.quiz.word.word_lookup_word, function(result){
            $timeout(function() {
                $scope.quiz.hint.status = $scope.flags.HINT.SHOWING;
                $scope.quiz.hint.images = result;
            });
        });
    }

    // user clicks on correct or wrong
    $scope.nextQuestion = function(){

        // reset hint
        $scope.quiz.hint.images = [];
        $scope.quiz.hint.status = $scope.flags.HINT.INACTIVE;

        // remove the answer for previous word
        $scope.quiz.answer.guess = '';

        // hide previous word's result screen
        $scope.changeStep($scope.flags.STEPS.QUESTION);

        // show the next word
        $scope.quiz.wordCursor++;

        // if answered all the questions, finish the quiz
        if($scope.quiz.words.length == $scope.quiz.wordCursor)
            $scope.changeStatus($scope.flags.STATUSES.FINISHED);

        // otherwise time to ask next word
        else {
            $scope.quiz.word = $scope.quiz.words[$scope.quiz.wordCursor];
            $scope.getDefinition();
        }
    }

    $scope.changeStep = function (step) {
        $scope.quiz.step = step;
    };

    $scope.changeStatus = function (status) {
        $scope.quiz.status = status;
    };

    // time to take a quiz,
    // send words that user will be take quiz on
    $scope.quizWords = function() {
        var notTakenQuiz = [];
        var words = $scope.getSelectedBookWords();
        angular.forEach(words, function(word){
            if(!word.word_lookup_quiz_taken)
                this.push(word);
        }, notTakenQuiz);
        return notTakenQuiz;
    };

    $scope.finish = function () {
        $location.path('/word-lookup');
    };

    $scope.init();
});<|endoftext|>


<commit_before>/* globals __DEV__ */
import Phaser from 'phaser'
import Mushroom from '../sprites/Mushroom'

export default class extends Phaser.State {
  init () {}

  preload () {

  }

  create () {
    this.firstBuildGame1()

    //Added physics
    this.game.physics.startSystem(Phaser.Physics.ARCADE)
    //Creating and capturing cursors
    this.cursor = this.input.keyboard.createCursorKeys()
    this.input.keyboard.addKeyCapture([Phaser.Keyboard.UP, Phaser.Keyboard.DOWN, Phaser.Keyboard.RIGHT, Phaser.Keyboard.LEFT])

    this.createPlayer()

    this.addStars()

    if (!this.game.device.desktop) {
      this.addMobileInputs()
    }
  }

  update () {
    this.game.physics.arcade.collide(this.player, this.groundLayer)
    this.game.physics.arcade.collide(this.stars, this.groundLayer);

    this.inputs()
  }

  render () {
    // if (__DEV__) {
    //   this.game.debug.spriteInfo(this.mushroom, 32, 32)
    // }
  }

  nextLevel () {
    console.log('next level')
  }

  firstBuildGame1 () {
    this.map = this.game.add.tilemap('tilemap')
    this.map.addTilesetImage('P6yozhP', 'tiles')

    this.backgroundlayer = this.map.createLayer('BackgroundLayer')
    this.groundLayer = this.map.createLayer('GroundLayer')

    this.map.setCollisionBetween(1, 100, true, 'GroundLayer')

    this.backgroundlayer.resizeWorld()

    //this.map.setCollisionBetween(1, 10000, true, 'GroundLayer');

    //Scale
    //this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
    this.scale.pageAlignHorizontally = true
    this.scale.updateLayout()
  }

  inputs () {
    if (this.cursor.left.isDown || this.moveLeft) {
      this.player.body.velocity.x = -200
      this.player.animations.play('left')

    } else if (this.cursor.right.isDown || this.moveRight) {
      this.player.body.velocity.x = 200
      this.player.animations.play('right')

    } else {
      this.player.animations.stop()
      this.player.frame = 4
      this.player.body.velocity.x = 0
    }

    //  Allow the player to jump if they are touching the ground.
    if (this.cursor.up.isDown) {
      this.jumpPlayer()
    }
  }

  createPlayer () {
    //Create player
    this.player = this.game.add.sprite(35, 450, 'dude')
    this.game.physics.arcade.enable(this.player)

    //The camera follows the player
    this.game.camera.follow(this.player)

    //Ativating collides grevity
    this.player.body.bounce.y = 0.2
    this.player.body.gravity.y = 420
    this.player.body.collideWorldBounds = true

    //adding anifations to walk left and right
    this.player.animations.add('left', [0, 1, 2, 3], 10, true)
    this.player.animations.add('right', [5, 6, 7, 8], 10, true)
  }

  addStars () {
    this.stars = this.game.add.group()

    //  We will enable physics for any star that is created in this group
    this.stars.enableBody = true

    for (var i = 0; i < 20; i++) {
      //  Create a star inside of the 'stars' group
      this.star = this.stars.create(i * 140, 250, 'star')

      //  Let gravity do its thing
      this.star.body.gravity.y = 700

      //  This just gives each star a slightly random bounce value
      this.star.body.bounce.y = 0.7 + Math.random() * 0.2
    }
  }

  jumpPlayer () {
    if (this.player.body.onFloor()) {
      this.player.body.velocity.y = -350
    }
  }

  addMobileInputs () {
    this.jumpButton = this.add.sprite(430, 130, 'jump')
    this.jumpButton.inputEnabled = true
    this.jumpButton.events.onInputDown.add(this.jumpPlayer, this)
    this.jumpButton.alpha = 0.5

    this.moveLeft = false
    this.moveRight = false

    this.leftButton = this.add.sprite(10, 130, 'left')
    this.leftButton.inputEnabled = true
    this.leftButton.events.onInputOver.add(function () {
      this.moveLeft = true
    }, this)
    this.leftButton.events.onInputOut.add(function () {
      this.moveLeft = false
    }, this)
    this.leftButton.events.onInputDown.add(function () {
      this.moveLeft = true
    }, this)
    this.leftButton.events.onInputUp.add(function () {
      this.moveLeft = false
    }, this)
    this.leftButton.alpha = 0.5

    this.rightButton = this.add.sprite(110, 130, 'right')
    this.rightButton.inputEnabled = true
    this.rightButton.events.onInputOver.add(function () {
      this.moveRight = true
    }, this)
    this.rightButton.events.onInputOut.add(function () {
      this.moveRight = false
    }, this)
    this.rightButton.events.onInputDown.add(function () {
      this.moveRight = true
    }, this)
    this.rightButton.events.onInputUp.add(function () {
      this.moveRight = false
    }, this)
    this.rightButton.alpha = 0.5
  }

}
<commit_msg>El jugador ja agafa estrelles<commit_after>/* globals __DEV__ */
import Phaser from 'phaser'
import Mushroom from '../sprites/Mushroom'

export default class extends Phaser.State {
  init () {}

  preload () {

  }

  create () {
    this.firstBuildGame1()

    //Added physics
    this.game.physics.startSystem(Phaser.Physics.ARCADE)
    //Creating and capturing cursors
    this.cursor = this.input.keyboard.createCursorKeys()
    this.input.keyboard.addKeyCapture([Phaser.Keyboard.UP, Phaser.Keyboard.DOWN, Phaser.Keyboard.RIGHT, Phaser.Keyboard.LEFT])

    this.createPlayer()

    this.addStars()

    if (!this.game.device.desktop) {
      this.addMobileInputs()
    }
  }

  update () {
    this.game.physics.arcade.collide(this.player, this.groundLayer)
    this.game.physics.arcade.collide(this.stars, this.groundLayer);

    this.game.physics.arcade.overlap(this.player, this.stars, this.takeStar, null, this);

    this.inputs()
  }

  render () {
    // if (__DEV__) {
    //   this.game.debug.spriteInfo(this.mushroom, 32, 32)
    // }
  }

  nextLevel () {
    console.log('next level')
  }

  firstBuildGame1 () {
    this.map = this.game.add.tilemap('tilemap')
    this.map.addTilesetImage('P6yozhP', 'tiles')

    this.backgroundlayer = this.map.createLayer('BackgroundLayer')
    this.groundLayer = this.map.createLayer('GroundLayer')

    this.map.setCollisionBetween(1, 100, true, 'GroundLayer')

    this.backgroundlayer.resizeWorld()

    //this.map.setCollisionBetween(1, 10000, true, 'GroundLayer');

    //Scale
    //this.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
    this.scale.pageAlignHorizontally = true
    this.scale.updateLayout()
  }

  inputs () {
    if (this.cursor.left.isDown || this.moveLeft) {
      this.player.body.velocity.x = -200
      this.player.animations.play('left')

    } else if (this.cursor.right.isDown || this.moveRight) {
      this.player.body.velocity.x = 200
      this.player.animations.play('right')

    } else {
      this.player.animations.stop()
      this.player.frame = 4
      this.player.body.velocity.x = 0
    }

    //  Allow the player to jump if they are touching the ground.
    if (this.cursor.up.isDown) {
      this.jumpPlayer()
    }
  }

  createPlayer () {
    //Create player
    this.player = this.game.add.sprite(35, 450, 'dude')
    this.game.physics.arcade.enable(this.player)

    //The camera follows the player
    this.game.camera.follow(this.player)

    //Ativating collides grevity
    this.player.body.bounce.y = 0.2
    this.player.body.gravity.y = 420
    this.player.body.collideWorldBounds = true

    //adding anifations to walk left and right
    this.player.animations.add('left', [0, 1, 2, 3], 10, true)
    this.player.animations.add('right', [5, 6, 7, 8], 10, true)
  }

  addStars () {
    this.stars = this.game.add.group()

    //  We will enable physics for any star that is created in this group
    this.stars.enableBody = true

    for (var i = 0; i < 20; i++) {
      //  Create a star inside of the 'stars' group
      this.star = this.stars.create(i * 140, 250, 'star')

      //  Let gravity do its thing
      this.star.body.gravity.y = 700

      //  This just gives each star a slightly random bounce value
      this.star.body.bounce.y = 0.7 + Math.random() * 0.2
    }
  }

  takeStar (player, star) {
    star.body.enable = false;
    game.add.tween(star.scale).to({x:0}, 150).start();
    game.add.tween(star).to({y:50}, 150).start();

  }

  jumpPlayer () {
    if (this.player.body.onFloor()) {
      this.player.body.velocity.y = -350
    }
  }

  addMobileInputs () {
    this.jumpButton = this.add.sprite(430, 130, 'jump')
    this.jumpButton.inputEnabled = true
    this.jumpButton.events.onInputDown.add(this.jumpPlayer, this)
    this.jumpButton.alpha = 0.5

    this.moveLeft = false
    this.moveRight = false

    this.leftButton = this.add.sprite(10, 130, 'left')
    this.leftButton.inputEnabled = true
    this.leftButton.events.onInputOver.add(function () {
      this.moveLeft = true
    }, this)
    this.leftButton.events.onInputOut.add(function () {
      this.moveLeft = false
    }, this)
    this.leftButton.events.onInputDown.add(function () {
      this.moveLeft = true
    }, this)
    this.leftButton.events.onInputUp.add(function () {
      this.moveLeft = false
    }, this)
    this.leftButton.alpha = 0.5

    this.rightButton = this.add.sprite(110, 130, 'right')
    this.rightButton.inputEnabled = true
    this.rightButton.events.onInputOver.add(function () {
      this.moveRight = true
    }, this)
    this.rightButton.events.onInputOut.add(function () {
      this.moveRight = false
    }, this)
    this.rightButton.events.onInputDown.add(function () {
      this.moveRight = true
    }, this)
    this.rightButton.events.onInputUp.add(function () {
      this.moveRight = false
    }, this)
    this.rightButton.alpha = 0.5
  }

}
<|endoftext|>


<commit_before>'use strict';

angular.module('confRegistrationWebApp')
  .service('RegistrationCache', function RegistrationCache($cacheFactory, $rootScope, $http, $q) {
    var cache = $cacheFactory('registration');
    var path = function (id) {
      return 'registrations/' + (id || '');
    };

    var update = function (path, object) {
      cache.put(path, angular.copy(object));
      $rootScope.$broadcast(path, object);
    };

    var checkCache = function (path, callback) {
      var cachedObject = cache.get(path);
      if (angular.isDefined(cachedObject)) {
        callback(cachedObject, path);
      } else {
        $http.get(path).success(function (data) {
          update(path, data);
          callback(data, path);
        });
      }
    };

    this.update = function (path, registration, cb, errorCallback) {
      if ($rootScope.registerMode === 'preview') {
        $rootScope.previewRegCache = registration;
        cb();
        return;
      }

      var callback = cb || function () {
        cache.put(path, angular.copy(registration));
        $rootScope.broadcast(path, registration);
      };

      var cachedReg = cache.get(path);
      if (angular.equals(registration, cachedReg)) {
        //do nothing
      } else {
        $http.put(path, registration).then(callback, errorCallback);
      }
    };

    this.query = function (id) {
      checkCache(path(id), function (conferences, path) {
        $rootScope.$broadcast(path, conferences);
      });
    };

    this.emptyCache = function () {
      cache.removeAll();
    };

    this.get = function (id) {
      var defer = $q.defer();
      checkCache(path(id), defer.resolve);
      return defer.promise;
    };

    this.getCurrent = function (conferenceId) {
      var defer = $q.defer();

      checkCache('conferences/' + conferenceId + '/registrations/current', function (registration) {
        if ($rootScope.registerMode === 'preview') {
          if(angular.isUndefined($rootScope.previewRegCache)){
            registration.completed = false;
            registration.registrants = [];
            $rootScope.previewRegCache = registration;
          } else {
            registration = $rootScope.previewRegCache;
          }
        }
        update(path(registration.id), registration);
        defer.resolve(registration);
      });

      return defer.promise;
    };

    this.getAllForConference = function (conferenceId, blocks) {
      var defer = $q.defer();
      $rootScope.loadingMsg = 'Loading Registrations';

      $http.get('conferences/' + conferenceId + '/registrations?blocks=' + blocks.join()).success(function (registrations) {
        $rootScope.loadingMsg = '';
        defer.resolve(registrations);
      }).error(function(){
        $rootScope.loadingMsg = '';
      });

      return defer.promise;
    };
  });
<commit_msg>New query string format<commit_after>'use strict';

angular.module('confRegistrationWebApp')
  .service('RegistrationCache', function RegistrationCache($cacheFactory, $rootScope, $http, $q) {
    var cache = $cacheFactory('registration');
    var path = function (id) {
      return 'registrations/' + (id || '');
    };

    var update = function (path, object) {
      cache.put(path, angular.copy(object));
      $rootScope.$broadcast(path, object);
    };

    var checkCache = function (path, callback) {
      var cachedObject = cache.get(path);
      if (angular.isDefined(cachedObject)) {
        callback(cachedObject, path);
      } else {
        $http.get(path).success(function (data) {
          update(path, data);
          callback(data, path);
        });
      }
    };

    this.update = function (path, registration, cb, errorCallback) {
      if ($rootScope.registerMode === 'preview') {
        $rootScope.previewRegCache = registration;
        cb();
        return;
      }

      var callback = cb || function () {
        cache.put(path, angular.copy(registration));
        $rootScope.broadcast(path, registration);
      };

      var cachedReg = cache.get(path);
      if (angular.equals(registration, cachedReg)) {
        //do nothing
      } else {
        $http.put(path, registration).then(callback, errorCallback);
      }
    };

    this.query = function (id) {
      checkCache(path(id), function (conferences, path) {
        $rootScope.$broadcast(path, conferences);
      });
    };

    this.emptyCache = function () {
      cache.removeAll();
    };

    this.get = function (id) {
      var defer = $q.defer();
      checkCache(path(id), defer.resolve);
      return defer.promise;
    };

    this.getCurrent = function (conferenceId) {
      var defer = $q.defer();

      checkCache('conferences/' + conferenceId + '/registrations/current', function (registration) {
        if ($rootScope.registerMode === 'preview') {
          if(angular.isUndefined($rootScope.previewRegCache)){
            registration.completed = false;
            registration.registrants = [];
            $rootScope.previewRegCache = registration;
          } else {
            registration = $rootScope.previewRegCache;
          }
        }
        update(path(registration.id), registration);
        defer.resolve(registration);
      });

      return defer.promise;
    };

    this.getAllForConference = function (conferenceId, blocks) {
      var defer = $q.defer();
      $rootScope.loadingMsg = 'Loading Registrations';

      var blocksStr = '';
      if(!_.isEmpty(blocks)){
        blocksStr = '?block=' + blocks.join('&block=');
      }
      $http.get('conferences/' + conferenceId + '/registrations' + blocksStr).success(function (registrations) {
        $rootScope.loadingMsg = '';
        defer.resolve(registrations);
      }).error(function(){
        $rootScope.loadingMsg = '';
      });

      return defer.promise;
    };
  });
<|endoftext|>


<commit_before>'use strict';

angular.module('scoreWebsiteApp')
    .controller('GettingStartedCtrl', function ($scope) {
        $scope.items = ['download', 'embed'];
        $scope.selectedItem = $scope.items[0];
        $scope.selected = false;

        $scope.selectItem = function (item) {
            if ($scope.selected) {
                $scope.selectedItem = item;
                hljs.initHighlighting();
            }
        };

        $scope.showCli = function (elemendId, commands) {
            $('#' + elemendId + '-cursor').empty();
            $('#' + elemendId).typed({
                strings: commands,
                typeSpeed: 0
            });
        };

        function expandDetailedBox() {
            $('#detailedBox').css({ display: 'inline-block' });
            $('#detailedBox').transition({ y: '-230px' }, 1200, 'ease-in-out');
            $scope.finishedAnimation = true;
            $scope.$apply();
        }

        $scope.boxClick = function (clickedItem) {
            if (!$scope.selected && $(window).width() > 766) {
                $scope.selected = !$scope.selected || false;
                $('.box-container').css({ 'padding-top': '35px' });
                $('.box-image').css({ 'margin-top': '-5px' });
                $('#embedbox').css({ cursor: 'pointer' });
                $('#downloadbox').css({ cursor: 'pointer' });
                $('#downloadbox').transition({ x: '-300px', y: '-50px', scale: [0.587, 0.5]}, 1200, 'ease-in-out');
                $('#embedbox').transition({ x: '298px', y: '-205px', scale: [0.587, 0.5]}, 1200, 'ease-in-out', expandDetailedBox);
            } else if ($scope.selected && $scope.selectedItem === clickedItem) {
                $scope.selected = !$scope.selected || false;
                $('#detailedBox').css({ display : 'none' });
                $('.box-container').css({ 'padding-top': '27px' });
                $('.box-image').css({ 'margin-top': '2px' });
                $('#embedbox').css({ cursor: 'pointer' });
                $('#downloadbox').css({ cursor: 'pointer' });
                $('#downloadbox').transition({ x: '0px', y: '0px', scale: [1, 1]}, 1200, 'ease-in-out');
                $('#embedbox').transition({ x: '0px', y: '0px', scale: [1, 1]}, 1200, 'ease-in-out');
            }
        };
    });<commit_msg>Fixed getting started animation issue<commit_after>'use strict';

angular.module('scoreWebsiteApp')
    .controller('GettingStartedCtrl', function ($scope) {
        $scope.items = ['download', 'embed'];
        $scope.selectedItem = $scope.items[0];
        $scope.selected = false;

        $scope.selectItem = function (item) {
            if ($scope.selected) {
                $scope.selectedItem = item;
                hljs.initHighlighting();
            }
        };

        $scope.showCli = function (elemendId, commands) {
            $('#' + elemendId + '-cursor').empty();
            $('#' + elemendId).typed({
                strings: commands,
                typeSpeed: 0
            });
        };

        function expandDetailedBox() {
            $('#detailedBox').css({ display: 'inline-block' });
            $('#detailedBox').transition({ y: '-230px' }, 1200, 'ease-in-out');
            $scope.finishedAnimation = true;
            $scope.animationInProgress = false;
            $scope.$apply();
        }

        $scope.animationInProgress = false;
        $scope.boxClick = function (clickedItem) {
            if ($(window).width() > 766) {
                if (!$scope.selected && !$scope.animationInProgress) {
                    $scope.animationInProgress = true;
                    $scope.finishedAnimation = false;
                    $scope.selected = !$scope.selected || false;
                    $('.box-container').css({ 'padding-top': '35px' });
                    $('.box-image').css({ 'margin-top': '-5px' });
                    $('#embedbox').css({ cursor: 'pointer' });
                    $('#downloadbox').css({ cursor: 'pointer' });
                    $('#downloadbox').transition({ x: '-300px', y: '-50px', scale: [0.587, 0.5]}, 1200, 'ease-in-out');
                    $('#embedbox').transition({ x: '298px', y: '-205px', scale: [0.587, 0.5]}, 1200, 'ease-in-out', expandDetailedBox);
                } else if ($scope.selected && $scope.selectedItem === clickedItem && !$scope.animationInProgress) {
                    $scope.animationInProgress = true;
                    $scope.finishedAnimation = true;
                    $('#detailedBox').css({ display : 'none' });
                    $('#downloadbox').transition({ x: '0px', y: '0px', scale: [1, 1]}, 1200, 'ease-in-out');
                    $('#embedbox').transition({ x: '0px', y: '0px', scale: [1, 1]}, 1200, 'ease-in-out', function () {
                        $scope.finishedAnimation = false;
                        $scope.selected = !$scope.selected || false;
                        $scope.animationInProgress = false;
                        $scope.$apply();
                    });
                    $('.box-container').css({ 'padding-top': '27px' });
                    $('.box-image').css({ 'margin-top': '2px' });
                }
            }
        };
    });<|endoftext|>


<commit_before>/* globals __DEV__ */
import Phaser from 'phaser'
import Mushroom from '../sprites/Mushroom'

export default class extends Phaser.State {
  init () {}
  preload () {
    this.game.load.image('background', './assets/images/stage01.png');
  }

  create () {
    this.ground = game.add.sprite(80/2-40,428/2,'background');
  }

  render () {
    if (__DEV__) {
      this.game.debug.spriteInfo(this.mushroom, 32, 32)
    }
  }
}
<commit_msg>Added player and lion with background<commit_after>/* globals __DEV__ */
import Phaser from 'phaser'
import Mushroom from '../sprites/Mushroom'

export default class extends Phaser.State {
  init () {}

  preload () {
    this.game.load.script('webfont', '//ajax.googleapis.com/ajax/libs/webfont/1.4.7/webfont.js');
    this.game.load.image('background', './assets/images/stage01.png')

    this.game.load.spritesheet('clown', './assets/images/circus-charlie-sheet.gif',16,24,10)

    var botData={'frames': [
      {
      'filename': 'clown0000',
      'frame': {'x':164,'y':5,'w':16,'h':24},
      'rotated': false,
      'trimmed': true,
      'spriteSourceSize': {'x':0,'y':0,'w':16,'h':24},
      'sourceSize': {'w':16,'h':24}
      },
      {
      'filename': 'clown0001',
      'frame': {'x':185,'y':5,'w':16,'h':24},
      'rotated': false,
      'trimmed': true,
      'spriteSourceSize': {'x':0,'y':0,'w':16,'h':24},
      'sourceSize': {'w':16,'h':24}
      },
      {
      'filename': 'clown0002',
      'frame': {'x':205,'y':5,'w':16,'h':24},
      'rotated': false,
      'trimmed': true,
      'spriteSourceSize': {'x':0,'y':0,'w':16,'h':24},
      'sourceSize': {'w':16,'h':24}
      },
      {
      'filename': 'clownJump0003',
      'frame': {'x':226,'y':5,'w':16,'h':24},
      'rotated': false,
      'trimmed': true,
      'spriteSourceSize': {'x':0,'y':0,'w':16,'h':24},
      'sourceSize': {'w':16,'h':24}
      },
      {
      'filename': 'clownStand0000',
      'frame': {'x':164,'y':58,'w':15,'h':24},
      'rotated': false,
      'trimmed': true,
      'spriteSourceSize': {'x':0,'y':0,'w':16,'h':24},
      'sourceSize': {'w':15,'h':24}
      },
      {
      'filename': 'clownStandJump0000',
      'frame': {'x':182,'y':58,'w':15,'h':24},
      'rotated': false,
      'trimmed': true,
      'spriteSourceSize': {'x':0,'y':0,'w':16,'h':24},
      'sourceSize': {'w':15,'h':24}
      },
      {
      'filename': 'clownburn0000',
      'frame': {'x':164,'y':32,'w':15,'h':24},
      'rotated': false,
      'trimmed': true,
      'spriteSourceSize': {'x':0,'y':0,'w':16,'h':24},
      'sourceSize': {'w':16,'h':24}
      },
      {
      'filename': 'lion0002',
      'frame': {'x':164,'y':87,'w':33,'h':16},
      'rotated': false,
      'trimmed': true,
      'spriteSourceSize': {'x':0,'y':0,'w':33,'h':16},
      'sourceSize': {'w':33,'h':16}
      },
      {
      'filename': 'lion0001',
      'frame': {'x':200,'y':87,'w':33,'h':16},
      'rotated': false,
      'trimmed': true,
      'spriteSourceSize': {'x':0,'y':0,'w':33,'h':16},
      'sourceSize': {'w':33,'h':16}
      },
      {
      'filename': 'lion0000',
      'frame': {'x':234,'y':87,'w':33,'h':16},
      'rotated': false,
      'trimmed': true,
      'spriteSourceSize': {'x':0,'y':0,'w':33,'h':16},
      'sourceSize': {'w':33,'h':16}
      },
      {
      'filename': 'lionburn0000',
      'frame': {'x':272,'y':87,'w':33,'h':16},
      'rotated': false,
      'trimmed': true,
      'spriteSourceSize': {'x':0,'y':0,'w':33,'h':16},
      'sourceSize': {'w':33,'h':16}
      },
      {
      'filename': 'firepot0000',
      'frame': {'x':221,'y':194,'w':24,'h':31},
      'rotated': false,
      'trimmed': true,
      'spriteSourceSize': {'x':0,'y':0,'w':24,'h':31},
      'sourceSize': {'w':24,'h':31}
      },
      {
      'filename': 'firepot0001',
      'frame': {'x':195,'y':194,'w':24,'h':31},
      'rotated': false,
      'trimmed': true,
      'spriteSourceSize': {'x':0,'y':0,'w':24,'h':31},
      'sourceSize': {'w':24,'h':31}
      },
      {
      'filename': 'firecirclel0000',
      'frame': {'x':136,'y':145,'w':12,'h':80},
      'rotated': false,
      'trimmed': true,
      'spriteSourceSize': {'x':0,'y':0,'w':12,'h':80},
      'sourceSize': {'w':12,'h':80}

      },
      {
      'filename': 'firecirclel0001',
      'frame': {'x':165,'y':145,'w':12,'h':80},
      'rotated': false,
      'trimmed': true,
      'spriteSourceSize': {'x':0,'y':0,'w':12,'h':80},
      'sourceSize': {'w':12,'h':80}
      },
      {
      'filename': 'firecircler0000',
      'frame': {'x':148,'y':145,'w':12,'h':80},
      'rotated': false,
      'trimmed': true,
      'spriteSourceSize': {'x':0,'y':0,'w':12,'h':80},
      'sourceSize': {'w':12,'h':80}
      },
      {
      'filename': 'firecircler0001',
      'frame': {'x':177,'y':145,'w':12,'h':80},
      'rotated': false,
      'trimmed': true,
      'spriteSourceSize': {'x':0,'y':0,'w':12,'h':80},
      'sourceSize': {'w':12,'h':80}
      },
      {'filename': 'endLevel1',
      'frame': {'x':129,'y':243,'w':37,'h':22},
      'rotated': false,
      'trimmed': true,
      'spriteSourceSize': {'x':0,'y':0,'w':37,'h':22},
      'sourceSize': {'w':37,'h':18}
      }
    ]}

    this.game.load.atlas('clown', './assets/images/circus-charlie-sheet.gif', null, botData)
  }

  create () {
    this.ground = game.add.sprite(80/2-40,428/2,'background')
    this.createPlayer()
  }

  createPlayer () {
    this.lion = this.game.add.sprite(85, 630, 'clown','lion0000')
    this.game.physics.enable(this.lion, Phaser.Physics.ARCADE)

    this.lion.body.setSize(90, 50, -5, 0)

    this.clown = this.game.add.sprite(7, -22, 'clown','clownStand0000')
    this.game.physics.enable(this.clown,Phaser.Physics.ARCADE,true)
    this.lion.addChild(this.clown)

    this.lion.scale.x =3
    this.lion.scale.y =3

    this.lion.animations.add('runLion', Phaser.Animation.generateFrameNames('lion', 0, 2, '', 4), 3 /*fps */, true)
    this.lion.animations.add('idleLion', Phaser.Animation.generateFrameNames('lion', 0, 0, '', 4), 1 /*fps */, true)

    this.clown.isRunning=false
    this.lion.body.collideWorldBounds=true
  }

  update () {
    this.ready = true
  }

  render () {
    if (__DEV__) {
      //this.game.debug.spriteInfo(this.mushroom, 32, 32)
    }
  }
}
<|endoftext|>