<commit_before>$(window).load(function () {
  setInterval(updateCurrentTime, 100);
  captionTime(0.0);
});

$('#timeStamp').html(' ');

// link timer to timestamp and display caption function
// v2 time on user side, check API time as failsafe near user next caption time.
function updateCurrentTime() {
  if (player.getPlayerState() === 1) {
    // v2 why round? why not just match nonround to last transcript event?
    var rounded = (Math.round(((player.getCurrentTime()) * 2)) / 2);
    $('#timeStamp').html('Timestamp: ' + 'rounded: ' + rounded + ' fixed: ' + player.getCurrentTime().toFixed(1));
    // console.log(player.getCurrentTime());
    // console.log(rounded + ' rounded');
    captionTime(rounded);
  }
}

var transcript = {};

// display captions to page and clear after six seconds
function captionTime(currentTime) {
  if ((transcript[currentTime]) !== undefined) {
    window.hider;
    // $('#captions').html(''); // clear current transcript display
    endAndStartTimer();
    function endAndStartTimer() {
      clearTimeout(window.hider);
      $('#captions').html(transcript[currentTime]).show(); //.delay(6000).fadeOut('fast'); // display transcript
      window.hider = window.setTimeout(function() {$('#captions').fadeOut('fast');}, 6000);
    }
  }
}

//
// todo: get player title and insert into template
// $('.player_title').html(player_title);

// jump timer vid to field time TESTING
/*
$('#timing').on('click', function () {
  player.seekTo((document.getElementById('timing').innerHTML), true); //seekTo argument needs evaluation
});
*/

// load captions on click
$('.transcriptDisplaySelect').click(function () {
  // add click content to transcript var
  $('#captions').html(''); // clear current transcript display
  transcript = $(this).text();
  transcript = $.parseJSON(transcript);
});

// start / stop video control
$('#starp').click(function () {
  if (player.getPlayerState() === 1) {
    player.pauseVideo();
  } else {
    player.playVideo();
  }
});

// skip back control
$('#skipBack').click(function () {
  player.pauseVideo();
  player.seekTo((player.getCurrentTime() - 3), true);
  player.playVideo();
  // console.log(player.getCurrentTime());
});

// append new time and text row to add transcript table on click or focus return
$('#transcripting').on('click', 'a', function(event) {
  event.preventDefault(); // prevent placeholder link from appering in browser URL
  // console.log($(this).html());
  if ($(this).html() === '+') {
    var this_closest_tr = $(this).closest('tr');
    var transcripting_row = $(this_closest_tr).html();
    $(this_closest_tr).after('
' + transcripting_row + '
');
    $(this_closest_tr).next().children('.transcript_cell').children('input').focus();
  }
  else if (($('#transcripting tr').length) > 2) {
    $(this).closest('tr').remove();
  }
});

// create JSON from transcript table
// todo: clear any double blank text field rows after submit
// todo: maybe add JSON parse to only work on already submitted text and not while submitting. do submit in Python
$('#transcripting').on('keyup click', 'input', function() {
  var transcripting_form_values = JSON.stringify($('#transcripting :input, textarea').serializeArray());
  // console.log($('#transcripting :input, textarea').serializeArray());
  var parsed_add = '{';
  for (var i = 0; i < transcripting_form_values.length; i++) {
    var parsed = JSON.parse(transcripting_form_values);
    if (typeof parsed[i] !== 'undefined') {
      if ((parsed[i].name) === ('timestamp_cell')) {
        parsed_add += ('"' + parsed[i].value + '": ');
      }
      else if ((parsed[i].name) === ('transcript_cell')) {
        // replace danger characters with safe equivalent
        var parsed_escaped = parsed[i].value.replace(/\&/gi, '&amps;');
        parsed_escaped = parsed_escaped.replace(/\"/gi, '"');
        parsed_escaped = parsed_escaped.replace(/\\/gi, '\');
        parsed_add += ('\"' + parsed_escaped + '\"' + ', ');
      }
    }
    var parsed_with_slice = (parsed_add.slice(0, -2) + '}');
  }
  $('#id_transcript').val(parsed_with_slice);
  transcript = JSON.parse(parsed_with_slice); // live preview in YT vid
});

// prevent submit on enter from form field
$('#transcripting').on('keyup keypress', 'input', function(event) {
  var keycode = event.keyCode || event.which;
  if (keycode === 13) {
    event.preventDefault();
    return false;
  }
});

// prevent submit without language choice
$('#submit').on('submit click keyup keypress', function(event) {
  if ($('#id_language').eq(0).val() === null) {
    event.preventDefault();
    var transcript_required_error_message = 'Transcript language required.';
    $('#language_message').html('« ' + transcript_required_error_message);
    $('#language_submit_message').html(transcript_required_error_message);
    return false;
  }
});

// prevent submit without all timestamps
$('#submit').on('submit click keyup keypress', function(event) {
  for (var i = 0; i <= $('#transcripting .time_cell').children().length; i++) {
    if ($('.timestamp_input').eq(i).val() === '') {
      event.preventDefault();
      $('.timestamp_input').eq(i).css('border', '2px solid red');
      var transcript_required_error_message = 'Time value(s) required.';
      $('#time_submit_message').html(transcript_required_error_message);
    }
  }
});

// remove error message on langauge select
$('#id_language').on('change', function () {
  if ($(this).eq(0).val() === null) {
    return false;
  }
  else {
    $('#language_message').html('');
    $('#language_submit_message').html('');
  }
});

// remove error message on time select
$('#transcripting').on('keyup click', 'input', function() {
  $('.timestamp_input').each(function() {
    if ($(this).val() === '') {
      return;
    }
    if ($(this).val() !== '') {
      $(this).css('border', '1px solid #454545');
      return;
    }
  });
  for (var i = 0; (i <= $('#transcripting .time_cell').children().length); i++) {
    console.log($('.timestamp_input').eq(i).val());
    if ($('.timestamp_input').eq(i).val() === '') {
      break;
    }
    else {
      if (i === $('#transcripting .time_cell').children().length) {
        $('#time_submit_message').html('');
      }
    }
  }
});

// drop down JSON to human readable format
$('#transcript option').each(function(index) {
  var drop_concat = '';
  if (index !== 0) {
    drop_down_value = JSON.parse(this.value);
    for (var i = 0; i <= 20; i += 0.5 ) {
      if (drop_down_value[i] !== undefined) {
        drop_concat += (drop_down_value[i] + ' ');
        $(this).html(drop_concat);
      }
    }
  }
});

// load drop down selection when chosen
$('#transcript').on('change', function() {
  $('#captions').html(''); // clear current transcript display
  transcript = (JSON.parse(this.value));
  // console.log(JSON.parse(this.value));
});

$('.timestamp_cell').on('keyup', function() {
  $(this).select();
});

/*
$('#transcripting').on('focusin', function() {
  // need logic to only append on last row
  // need logic to stop appending once new blank row is appended
  // jQuery logic to see if next tag is not table so to only append last row
  // tab order needs to be respected
  // add overflow feature in CSS
  console.log('\nworking_________________v\n');
  console.log('1a activeElement val:\t' + $(document.activeElement).val());
  console.log('1b activeElement type:\t' + document.activeElement.type);
*/

  /*
  if ((document.activeElement) === $('#transcripting tr:last')) {
    console.log('last in focus');
  }
  */

  // $('#transcripting tr:last').css('background-color', 'red');
  // $('#transcripting tr:last').prev().css('background-color', 'white');

  // if last row, if text, if !(prev prev row text False and prev row text False).
  // if ((document.activeElement.type === 'text') /*&& (document.activeElement.value === '') && ('test' === 'test')*/) {
    // for maintainability the appended HTML should just make a copy of current tr minus text input and not be hardcoded HTML
    // $('#transcripting').append('
<input cols="100" min="0" type="number" placeholder="time" autocomplete="off" step="0.5"	<input maxlength="100" type="text" placeholder="text" autocomplete="off">	+
');
   // }
// });

// find YT username
// $('#username').html(document.getElementById('yt-masthead-user-displayname').innerHTML);

// find vidID for meta JSON info
// var vidCode = $('#vidCode').html(vidCode);
// var vidCode = /v=11+/g.exec(vidCode); // or val.replace (,);
// $('#returnYT').html('\"<a href=\"//youtube.com/watch?v=\"' + vidCode + 'returnYT');

// return to YouTube button
// $('#returnYT').html('//www.youtube.com/watch?v=' + {{ vidId|escapejs }});
// $('#returnYT').html(player.getVideoUrl());

/*
$('#returnYT').click(function () {
  console.log(player.getVideoUrl());
});
*/

// $('#returnYT').click(function () {
  // return document.URL = ('//www.youtube.com/watch?v=' + {{ vidId|escapejs }});
// });

// best alternative to yt.setConfig search is search page for FEEDBACK_LOCALE_LANGUAGE
//var userLanguage = $('script').text().match(/'FEEDBACK_LOCALE_LANGUAGE': ".*?"/).toString().match(/".*?"/).toString().replace(/\"/g, '');
//$('#' + userLanguage).attr('selected', true);

// not getting latest form edit content
$('#text').keyup(function () {
  var activeForm = (document.getElementById('text').innerHTML);
  console.log(activeForm);
});

// return to YouTube button
// $('#returnYT').html('//www.youtube.com/watch?v=' + {{ vidId|escapejs }});
// $('#returnYT').html(player.getVideoUrl());
// $('#returnYT').click(function () {
  // return document.URL = ('//www.youtube.com/watch?v=' + {{ vidId|escapejs }});
// });

// best alternative to yt.setConfig search is search page for FEEDBACK_LOCALE_LANGUAGE
//var userLanguage = $('script').text().match(/'FEEDBACK_LOCALE_LANGUAGE': ".*?"/).toString().match(/".*?"/).toString().replace(/\"/g, '');
//$('#' + userLanguage).attr('selected', true);

// not getting latest form edit content
$('#text').keyup(function () {
  var activeForm = (document.getElementById('text').innerHTML);
  console.log(activeForm);
});

// sandboxing
//
/*
$(window).load(intervalTimer(faster, 0, 100, 10000));

function intervalTimer(f, start, interval, end) {
  if (!start) start = 0;                // Default to 0 ms
  if (arguments.length <= 2) {          // Single-invocation case
    setTimeout(f, start);               // Single invocation after start ms.
  }
  else {                                // Multiple invocation case
    setTimeout(repeat, start);          // Repetitions begin in start ms
    function repeat() {                 // Invoked by the timeout above
      var h = setInterval(f, interval); // Invoke f every interval ms. And stop invoking after end ms, if end is defined
      if (end) setTimeout(function () { clearInterval(h); }, end);
    }
  }
}
*/

/*
$(window).load(faster);
function faster() {
  for (var i = 0; i < 200; i++) {
    if ((player()) === (-1)) {
      console.log('defined player');
    }
    else {
      if ((player()) !== undefined) {
        console.log('defined player');
      }
    }
  }
};
*/

// delay interval load
// $(window).load(window.setTimeout(funcX, 5500));

/*
remanants of cell traversal testing
console.log('2a this prev prev val:\t' + $(this).prev().prev().val());
console.log('2b this prev val:\t' + $(this).prev().val());
console.log('2c this prev:\t' + $(this).prev());
console.log('2d this siblings:\t' + $(this).siblings());
console.log('2e this next val:\t' + $(this).next().val()); // to check if next item is Submit button
console.log('2f this val:\t' + $(this).val()); // to check if next item is Submit button
console.log('3 this parent parent prev children val:\t' + $(this).parent().parent().prev().children().children().val());
console.log('4 #transcripting next val:\t' + $('#transcripting').next().val());
console.log('5 #transcripting last val:\t' + $('#transcripting').last().val());
console.log('6 last val:\t' + $('#transcripting').last().val());
console.log('7 #transcripting tr:last value:\t' + $('#transcripting tr:last').value);
console.log('8a #transcripting tr:last val:\t' + $('#transcripting tr:last').val());
console.log('8b #transcripting last val:\t' + $('#transcripting input').last().val());
console.log('8c #transcripting last prev val:\t' + $('#transcripting input').last().prev().val());
console.log('9a #transcripting type nth 2:\t' + $('#transcripting input:nth-last-of-type(2)').type);
console.log('9b #transcripting type nth 3:\t' + $('#transcripting input:nth-last-of-type(3)').type);
*/

// v2 caption format?
// [{"time":"3", "time":3, "text":"hello"}, ...]

//var transcript = {
  //"0": "This a test of the CamStudio microphone.",
  //"3": "It\'s just dangling from my ear.",
  //"5": "Um, this is how we learn math.",
  //"10": "Whoops!",
  //"13": "Let\'s click click click delete \"h.\"",
  //"17.5": "Good.",
  //"20": "",
  //"24": "Is it not recording?",
  //"25": ""
//};

// live reload
document.write('


<commit_before>import { createStore, applyMiddleware } from 'redux';
import promise from 'redux-promise-middleware';
import thunk from 'redux-thunk';
import { routerMiddleware, push } from 'react-router-redux';
import { browserHistory } from 'react-router';

//import allreducers from reducers folder
import allReducers from '../reducers'

const browser = routerMiddleware(browserHistory)
const middleware = applyMiddleware(logger(), promise(), thunk, browser);

const store = createStore(allReducers, middleware);

//export store for use in the entry file
export default store;
<commit_msg>remove: logger middleware<commit_after>import { createStore, applyMiddleware } from 'redux';
import promise from 'redux-promise-middleware';
import thunk from 'redux-thunk';
import { routerMiddleware, push } from 'react-router-redux';
import { browserHistory } from 'react-router';

//import allreducers from reducers folder
import allReducers from '../reducers'

const browser = routerMiddleware(browserHistory)
const middleware = applyMiddleware(promise(), thunk, browser);

const store = createStore(allReducers, middleware);

//export store for use in the entry file
export default store;
<|endoftext|>


<commit_before>import React, { Component } from 'react'
import { Alert, AppRegistry, StyleSheet, View, Text, TouchableWithoutFeedback, TouchableOpacity } from 'react-native'
import { numberWithCommas } from '../common'

class PieChart extends React.Component {
  constructor (props) {
    super(props)
    this.drawPie = this.drawPie.bind(this)
    this.initData = this.initData.bind(this)
    this.handleEvent = this.handleEvent.bind(this)
    this.state = {
      // pieSize[i] : size of ith pie, piePos[i] : starting position of ith pie;
      pieSize: [],
      piePos: [],
      oldPiePos: [],
      pieIndex: [],
      dataSum: 0,
      currentPieIdx: -1,
      evtX: 0,
      evtY: 0,
      selectedIndex: -1,
      labels: [],
      colors: []
    }
  }
  // initData!!
  componentDidMount () {
    this.initData(this.props.data)
  }
  componentWillReceiveProps (nextProps) {
    if (nextProps.data !== this.props.data) {
      this.initData(nextProps.data)
    }
  }
  // initialize data
  initData (data) {
    let colors = []
    let labels = []
    if (data[0].color) {
      for (let i = 0; i < data.length; i++) {
        colors[i] = data[i].color
      }
    } else {
      for (let i = 0; i < data.length; i++) {
        colors[i] = this.props.colors[i % this.props.colors.length]
      }
      if (data.length === this.props.colors.length + 1) {
        // 임의로 3으로 지정함 바꿔도 무방
        colors[data.length - 1] = this.props.colors[3]
      }
    }

    if (data[0].label) {
      for (let i = 0; i < data.length; i++) {
        labels[i] = data[i].label
      }
    } else {
      for (let i = 0; i < data.length; i++) {
        labels[i] = null
      }
    }

    // validation
    let sum = 0
    for (let i = 0; i < data.length; i++) {
      sum += data[i].value
    }
    // pieSize에는 각각 라디안값이 들어감
    let pieSize = []
    let pieIndex = []
    let angles = []
    let index = 0
    let tempAngle = 0
    for (let i = 0; i < data.length; i++) {
      let angle = Math.round(data[i].value / sum * 360)
      angles.push(tempAngle + angle)
      tempAngle = tempAngle + angle
      if (data[i].value / sum * 2 * Math.PI > Math.PI) {
        pieIndex.push(index)
        pieIndex.push(index)
        pieSize.push(Math.PI)
        pieSize.push(data[i].value / sum * 2 * Math.PI - Math.PI)
      } else {
        pieIndex.push(index)
        pieSize.push(data[i].value / sum * 2 * Math.PI)
      }
      index++
    }
    // piePos에는 각각 시작위치가 라디안 값으로 들어감
    let piePos = []
    piePos[0] = 0
    for (let i = 1; i < pieSize.length; i++) {
      piePos[i] = piePos[i - 1] + pieSize[i - 1]
    }

    let oldPiePos = []
    for (let i = 0; i < this.props.data.length; i++) {
      oldPiePos[i] = -1
    }
    for (let i = 0; i < piePos.length; i++) {
      if (oldPiePos[pieIndex[i]] === -1) {
        oldPiePos[pieIndex[i]] = piePos[i]
      }
    }

    /*
    for (let i = 0; i < this.props.data.length; i++) {
      console.log(oldPiePos[i])
    }
    */

    /*
    for (let i = 0; i < data.length; i++) {
      console.log(data[i].value)
      console.log(labels[i])
      console.log(colors[i])
    }
    */

    this.setState({
      labels: labels,
      colors: colors,
      pieSize: pieSize,
      piePos: piePos,
      pieIndex: pieIndex,
      angles: angles,
      dataSum: sum,
      oldPiePos: oldPiePos
    })
  }
  handleEventOld (evt) {
    /* this.setState({
      evtX: evt.nativeEvent.pageX,
      evtY: evt.nativeEvent.pageY
    }) */
    let pageX = evt.nativeEvent.pageX
    let pageY = evt.nativeEvent.pageY
    const {angles} = this.state
    const {size} = this.props
    this.refs.test.measure((fx, fy, width, height, px, py) => {
      let evtX = pageX - px
      let evtY = (pageY - py)
      let originX = pageX - px - size / 2
      let originY = size / 2 - (pageY - py)
      let rSquare = Math.pow(originX, 2) + Math.pow(originY, 2)
      let dx = originX
      let dy = -1 * originY
      let rad = Math.atan2(dy, dx)
      let degree = (rad * 180) / Math.PI
      if (degree < 0) degree = 360 + degree
      let inPie = rSquare < Math.pow(size / 2, 2)
      let selectedIndex = -1
      if (inPie) {
        for (let i = 0; i < angles.length; i++) {
          if (degree < angles[i]) {
            selectedIndex = i
            break
          }
        }
      }

      this.setState({
        evtX: evtX,
        evtY: evtY,
        originX: originX,
        originY: originY,
        inPie: inPie,
        selectedAngle: degree,
        selectedRad: rad,
        selectedIndex: selectedIndex
      })
    })
  }

  handleEvent (idx) {
 //   console.log(idx)
    this.setState({
      currentPieIdx: this.state.pieIndex[idx]
    })
  }
  /*
  handleEvent (a, b, c) {
    console.log(a)
    console.log(b)
    console.log(c)
  }
  */

  handleEventBak (evt, idx) {
    /* this.setState({
      evtX: evt.nativeEvent.pageX,
      evtY: evt.nativeEvent.pageY
    }) */

    // console.log(idx)
    let pageX = evt.nativeEvent.pageX
    let pageY = evt.nativeEvent.pageY
    this.setState({
      currentPieIdx: idx
    })
    this.refs.test.measure((fx, fy, width, height, px, py) => {
      this.setState({
        evtX: pageX - px,
        evtY: pageY - py

      })
    })
  }
  drawInfoOld (x, y) {
    const {size} = this.props
    let dist = Math.pow(Math.pow(x, 2) + Math.pow(y, 2), 0.5)
    // console.log('x: ' + x + '  y: ' + y)
    if (dist <= size) {
      let index = -1
      // 중심 y축 기준으로 오른쪽 왼쪽
      let pos = x > 0 ? Math.PI / 2 - Math.asin(y / dist) : Math.PI * 3 / 2 + Math.asin(y / dist)
      // index 값 정하기 piePos piePos 값보다 pos값이 작을 경우 멈춤
      for (let i = 0; i < this.state.piePos.length; i++) {
        if (this.state.piePos[i] > pos) break
        index++
      }
      let marginLeft = x
      let marginTop = -y

      return (
        <View style={{
          width: 100,
          height: 80,
          marginLeft: marginLeft,
          marginTop: marginTop
        }} >
          <Text>{Math.round(this.state.pieSize[index] / (2 * Math.PI) * 10000) / 100 + '\n' + this.state.value[index]} </Text>
        </View>
      )
    }
  }

  drawInfoT (index) {
    const {size} = this.props

    let pos = (this.state.oldPiePos[index] + this.state.oldPiePos[(index + 1) % this.state.oldPiePos.length]) / 2
    if (index + 1 === this.props.data.length) {
      pos += Math.PI
    }
    let x = size / 4 * Math.cos(-pos) + size / 2
    let y = -size / 4 * Math.sin(-pos) + size / 2

    if (index !== -1) {
      let percentage = Math.round((this.props.data[index].value * 1000 / this.state.dataSum)) / 10 + '%'
      let valueStr = numberWithCommas(this.props.data[index].value, false)
      return (
        <View style={{
        }}>
          <View style={{
            marginLeft: x - 30,
            marginTop: y - 40,
            borderWidth: 1,
            borderRadius: 5,
            borderColor: '#ffffff',
            backgroundColor: '#FFFFFF'
          }}>
            <View style={{flexDirection: 'row', paddingLeft: 5, alignItems: 'center'}}>
              <View style={{
                width: 10,
                height: 5,
                marginRight: 3,
                borderRadius: 2,
                backgroundColor: this.state.colors[index]
              }} />
              <Text style={styles.tooltipTitle}>{this.state.labels[index] ? this.state.labels[index] : valueStr}</Text>
            </View>

            <Text style={styles.tooltipValue}>{this.state.labels[index] ? `${valueStr} (${percentage})` : percentage}</Text>

          </View>
        </View>
      )
    }
  }
  drawInfo (index, x, y) {
    const {size} = this.props
    if (index !== -1) {
      return (
        <View style={{
          width: size,
          height: size
        }}>
          <View style={{
            width: 60,
            height: 80,
            marginLeft: x - 30,
            marginTop: y - 40,
            borderWidth: 2,
            borderRadius: 5,
            borderColor: '#e0e0e0',
            backgroundColor: '#FFFFFF'
          }}>

            {this.state.labels[index] !== null &&
              <Text>{this.state.labels[index]}</Text>
            }
            <View style={{flexDirection: 'row', paddingLeft: 5, alignItems: 'center'}}>
              <View style={{
                width: 10,
                height: 5,
                marginRight: 3,
                borderRadius: 2,
                backgroundColor: this.state.colors[index]
              }} />
              <Text style={styles.tooltipValue}>{this.props.data[index].value}</Text>
            </View>

            <Text style={styles.tooltipValue}>{this.props.data[index].value * 100 / this.state.dataSum + '%'}</Text>

          </View>
        </View>
      )
    }
  }

  getTransform (rad, width, add = 0) {
    let x = (0 - width / 2) * Math.cos(rad) - (0 - width / 2) * Math.sin(rad)
    let y = (0 - width / 2) * Math.sin(rad) + (0 - width / 2) * Math.cos(rad)

    return [ {translateX: (-1 * x) - width / 2 + add}, { translateY: (-1 * y) - width / 2 }, { rotate: rad + 'rad' } ]
  }
  drawPie (angle, color, big, idx) {
    const {size} = this.props
    return (
      <View>
        {angle === 0 ? (
          <View />
        ) : (
          angle > (1 / 2 * Math.PI) ? (
            <View>
              <View style={{width: size, height: size / 2}} />
              <View style={{flexDirection: 'row', backgroundColor: 'transparent'}}>

                <View style={{

                  width: size / 2,
                  height: size / 2,

                  transform: this.getTransform(Math.PI / 2, size / 2, size / 2)
                }}>

                  <View style={{
                  }}>
                    {this.drawPie(angle - 1 / 2 * Math.PI, color, false, idx)}
                  </View>

                </View>

                <View style={{
                  opacity: 1,

                  width: size / 2,
                  height: size / 2
                }}>
                  {this.drawPie(1 / 2 * Math.PI, color, false, idx)}
                </View>

              </View>
            </View>

        ) : (
          // big 사용이유
          big ? (
            <View style={{
              width: size,
              height: size
            }}>
              <View style={{width: size, height: size / 2}} />
              <View style={{flexDirection: 'row'}}>
                <View style={{width: size / 2, height: size / 2}} />
                <View style={{
                  width: size / 2,
                  height: size / 2
                }}>

                  <View style={{
                    width: size / 2,
                    height: size / 2,
                    borderBottomRightRadius: size / 2,
                    backgroundColor: color,
                    transform: this.getTransform(angle - Math.PI / 2, size / 2)
                  }} />

                </View>

              </View>
            </View>
          ) : (
            <View style={{
              width: size / 2,
              height: size / 2
            }}>

              <View style={{
                width: size / 2,
                height: size / 2
              }}>

                <View style={{
                  width: size / 2,
                  height: size / 2,
                  borderBottomRightRadius: size / 2,
                  backgroundColor: color,
                  borderWidth: 0,
                  borderColor: color,
                  transform: this.getTransform(angle - Math.PI / 2, size / 2)
                }} />

              </View>

            </View>
          )
        ))}
      </View>
    )
  }
  drawT () {
    let pies = []
    if (this.state.pieSize.length === 0) return null
    for (let i = 0; i < this.state.pieSize.length; i++) {
      pies.push(
        <View key={`t${i}`} style={{
          transform: [{rotate: `${this.state.piePos[i]}rad`}],
          position: 'absolute'

        }}>
          {
            this.drawPie(this.state.pieSize[i], this.state.colors[this.state.pieIndex[i]], true, i)
          }
        </View>
        )
    }
    return (
      pies
    )
  }
  render () {
    const {selectedIndex, locationX, locationY, evtX, evtY, inPie, selectedAngle,
      pieSize, angles} = this.state
    const {size} = this.props
    return (
      <View collapsable={false}>

        <TouchableWithoutFeedback onPress={(e) => {
          // const {locationX, locationY} = e.nativeEvent
          // console.log(locationX, locationY)
          // this.setState({locationX: locationX, locationY: locationY})
          this.handleEventOld(e)
        }}>

          <View ref='test' style={StyleSheet.flatten([styles.container, {
            width: size,
            height: size
          }])}>

            {this.drawT()}
            {this.drawInfoT(this.state.selectedIndex)}
          </View>

        </TouchableWithoutFeedback>
        <Text>selected: {selectedIndex}</Text>
        <Text>({locationX},{locationY})</Text>
        <Text>({evtX},{evtY})</Text>
        <Text>is coordinate In Pie chart: {'' + inPie}</Text>
        <Text>angle: {selectedAngle}</Text>
        <Text>angles: {JSON.stringify(angles)}</Text>
      </View>
    )
  }
}

PieChart.defaultProps = {
  data: [{value: 10}, {value: 20}, {value: 40}, {value: 100}],
  colors: ['green', 'red', 'blue', 'black', 'yellow', 'purple', 'blue', 'orange'],
  size: 200
}
const styles = StyleSheet.create({
  container: {
    overflow: 'hidden',
    marginLeft: 100,
    alignItems: 'center',
    justifyContent: 'center'
  },
  tooltipTitle: {fontSize: 18, fontWeight: 'bold'},
  tooltipValue: {fontSize: 15, marginTop: 5}
})

export default PieChart
<commit_msg>Adopt info position<commit_after>import React, { Component } from 'react'
import { Alert, AppRegistry, StyleSheet, View, Text, TouchableWithoutFeedback, TouchableOpacity } from 'react-native'
import { numberWithCommas } from '../common'

class PieChart extends React.Component {
  constructor (props) {
    super(props)
    this.drawPie = this.drawPie.bind(this)
    this.initData = this.initData.bind(this)
    this.handleEvent = this.handleEvent.bind(this)
    this.state = {
      // pieSize[i] : size of ith pie, piePos[i] : starting position of ith pie;
      pieSize: [],
      piePos: [],
      oldPiePos: [],
      pieIndex: [],
      dataSum: 0,
      currentPieIdx: -1,
      evtX: 0,
      evtY: 0,
      selectedIndex: -1,
      labels: [],
      colors: []
    }
  }
  // initData!!
  componentDidMount () {
    this.initData(this.props.data)
  }
  componentWillReceiveProps (nextProps) {
    if (nextProps.data !== this.props.data) {
      this.initData(nextProps.data)
    }
  }
  // initialize data
  initData (data) {
    let colors = []
    let labels = []
    if (data[0].color) {
      for (let i = 0; i < data.length; i++) {
        colors[i] = data[i].color
      }
    } else {
      for (let i = 0; i < data.length; i++) {
        colors[i] = this.props.colors[i % this.props.colors.length]
      }
      if (data.length === this.props.colors.length + 1) {
        // 임의로 3으로 지정함 바꿔도 무방
        colors[data.length - 1] = this.props.colors[3]
      }
    }

    if (data[0].label) {
      for (let i = 0; i < data.length; i++) {
        labels[i] = data[i].label
      }
    } else {
      for (let i = 0; i < data.length; i++) {
        labels[i] = null
      }
    }

    // validation
    let sum = 0
    for (let i = 0; i < data.length; i++) {
      sum += data[i].value
    }
    // pieSize에는 각각 라디안값이 들어감
    let pieSize = []
    let pieIndex = []
    let angles = []
    let index = 0
    let tempAngle = 0
    for (let i = 0; i < data.length; i++) {
      let angle = Math.round(data[i].value / sum * 360)
      angles.push(tempAngle + angle)
      tempAngle = tempAngle + angle
      if (data[i].value / sum * 2 * Math.PI > Math.PI) {
        pieIndex.push(index)
        pieIndex.push(index)
        pieSize.push(Math.PI)
        pieSize.push(data[i].value / sum * 2 * Math.PI - Math.PI)
      } else {
        pieIndex.push(index)
        pieSize.push(data[i].value / sum * 2 * Math.PI)
      }
      index++
    }
    // piePos에는 각각 시작위치가 라디안 값으로 들어감
    let piePos = []
    piePos[0] = 0
    for (let i = 1; i < pieSize.length; i++) {
      piePos[i] = piePos[i - 1] + pieSize[i - 1]
    }

    let oldPiePos = []
    for (let i = 0; i < this.props.data.length; i++) {
      oldPiePos[i] = -1
    }
    for (let i = 0; i < piePos.length; i++) {
      if (oldPiePos[pieIndex[i]] === -1) {
        oldPiePos[pieIndex[i]] = piePos[i]
      }
    }

    /*
    for (let i = 0; i < this.props.data.length; i++) {
      console.log(oldPiePos[i])
    }
    */

    /*
    for (let i = 0; i < data.length; i++) {
      console.log(data[i].value)
      console.log(labels[i])
      console.log(colors[i])
    }
    */

    this.setState({
      labels: labels,
      colors: colors,
      pieSize: pieSize,
      piePos: piePos,
      pieIndex: pieIndex,
      angles: angles,
      dataSum: sum,
      oldPiePos: oldPiePos
    })
  }
  handleEventOld (evt) {
    /* this.setState({
      evtX: evt.nativeEvent.pageX,
      evtY: evt.nativeEvent.pageY
    }) */
    let pageX = evt.nativeEvent.pageX
    let pageY = evt.nativeEvent.pageY
    const {angles} = this.state
    const {size} = this.props
    this.refs.test.measure((fx, fy, width, height, px, py) => {
      let evtX = pageX - px
      let evtY = (pageY - py)
      let originX = pageX - px - size / 2
      let originY = size / 2 - (pageY - py)
      let rSquare = Math.pow(originX, 2) + Math.pow(originY, 2)
      let dx = originX
      let dy = -1 * originY
      let rad = Math.atan2(dy, dx)
      let degree = (rad * 180) / Math.PI
      if (degree < 0) degree = 360 + degree
      let inPie = rSquare < Math.pow(size / 2, 2)
      let selectedIndex = -1
      if (inPie) {
        for (let i = 0; i < angles.length; i++) {
          if (degree < angles[i]) {
            selectedIndex = i
            break
          }
        }
      }

      this.setState({
        evtX: evtX,
        evtY: evtY,
        originX: originX,
        originY: originY,
        inPie: inPie,
        selectedAngle: degree,
        selectedRad: rad,
        selectedIndex: selectedIndex
      })
    })
  }

  handleEvent (idx) {
 //   console.log(idx)
    this.setState({
      currentPieIdx: this.state.pieIndex[idx]
    })
  }
  /*
  handleEvent (a, b, c) {
    console.log(a)
    console.log(b)
    console.log(c)
  }
  */

  handleEventBak (evt, idx) {
    /* this.setState({
      evtX: evt.nativeEvent.pageX,
      evtY: evt.nativeEvent.pageY
    }) */

    // console.log(idx)
    let pageX = evt.nativeEvent.pageX
    let pageY = evt.nativeEvent.pageY
    this.setState({
      currentPieIdx: idx
    })
    this.refs.test.measure((fx, fy, width, height, px, py) => {
      this.setState({
        evtX: pageX - px,
        evtY: pageY - py

      })
    })
  }
  drawInfoOld (x, y) {
    const {size} = this.props
    let dist = Math.pow(Math.pow(x, 2) + Math.pow(y, 2), 0.5)
    // console.log('x: ' + x + '  y: ' + y)
    if (dist <= size) {
      let index = -1
      // 중심 y축 기준으로 오른쪽 왼쪽
      let pos = x > 0 ? Math.PI / 2 - Math.asin(y / dist) : Math.PI * 3 / 2 + Math.asin(y / dist)
      // index 값 정하기 piePos piePos 값보다 pos값이 작을 경우 멈춤
      for (let i = 0; i < this.state.piePos.length; i++) {
        if (this.state.piePos[i] > pos) break
        index++
      }
      let marginLeft = x
      let marginTop = -y

      return (
        <View style={{
          width: 100,
          height: 80,
          marginLeft: marginLeft,
          marginTop: marginTop
        }} >
          <Text>{Math.round(this.state.pieSize[index] / (2 * Math.PI) * 10000) / 100 + '\n' + this.state.value[index]} </Text>
        </View>
      )
    }
  }

  drawInfoT (index) {
    const {size} = this.props

    let pos = (this.state.oldPiePos[index] + this.state.oldPiePos[(index + 1) % this.state.oldPiePos.length]) / 2
    if (index + 1 === this.props.data.length) {
      pos += Math.PI
    }
    let x = size / 4 * Math.cos(-pos) + size / 2
    let y = -size / 4 * Math.sin(-pos) + size / 2

    if (index !== -1) {
      let percentage = Math.round((this.props.data[index].value * 1000 / this.state.dataSum)) / 10 + '%'
      let valueStr = numberWithCommas(this.props.data[index].value, false)
      return (
        <View style={{
          height: size
        }}>
          <View style={{
            marginLeft: x - 60,
            marginTop: y - 40,
            borderWidth: 1,
            borderRadius: 5,
            borderColor: '#ffffff',
            backgroundColor: '#FFFFFF'
          }}>
            <View style={{flexDirection: 'row', paddingLeft: 5, alignItems: 'center'}}>
              <View style={{
                width: 10,
                height: 5,
                marginRight: 3,
                borderRadius: 2,
                backgroundColor: this.state.colors[index]
              }} />
              <Text style={styles.tooltipTitle}>{this.state.labels[index] ? this.state.labels[index] : valueStr}</Text>
            </View>

            <Text style={styles.tooltipValue}>{this.state.labels[index] ? `${valueStr} (${percentage})` : percentage}</Text>

          </View>
        </View>
      )
    }
  }
  drawInfo (index, x, y) {
    const {size} = this.props
    if (index !== -1) {
      return (
        <View style={{
          width: size,
          height: size
        }}>
          <View style={{
            width: 60,
            height: 80,
            marginLeft: x - 30,
            marginTop: y - 40,
            borderWidth: 2,
            borderRadius: 5,
            borderColor: '#e0e0e0',
            backgroundColor: '#FFFFFF'
          }}>

            {this.state.labels[index] !== null &&
              <Text>{this.state.labels[index]}</Text>
            }
            <View style={{flexDirection: 'row', paddingLeft: 5, alignItems: 'center'}}>
              <View style={{
                width: 10,
                height: 5,
                marginRight: 3,
                borderRadius: 2,
                backgroundColor: this.state.colors[index]
              }} />
              <Text style={styles.tooltipValue}>{this.props.data[index].value}</Text>
            </View>

            <Text style={styles.tooltipValue}>{this.props.data[index].value * 100 / this.state.dataSum + '%'}</Text>

          </View>
        </View>
      )
    }
  }

  getTransform (rad, width, add = 0) {
    let x = (0 - width / 2) * Math.cos(rad) - (0 - width / 2) * Math.sin(rad)
    let y = (0 - width / 2) * Math.sin(rad) + (0 - width / 2) * Math.cos(rad)

    return [ {translateX: (-1 * x) - width / 2 + add}, { translateY: (-1 * y) - width / 2 }, { rotate: rad + 'rad' } ]
  }
  drawPie (angle, color, big, idx) {
    const {size} = this.props
    return (
      <View>
        {angle === 0 ? (
          <View />
        ) : (
          angle > (1 / 2 * Math.PI) ? (
            <View>
              <View style={{width: size, height: size / 2}} />
              <View style={{flexDirection: 'row', backgroundColor: 'transparent'}}>

                <View style={{

                  width: size / 2,
                  height: size / 2,

                  transform: this.getTransform(Math.PI / 2, size / 2, size / 2)
                }}>

                  <View style={{
                  }}>
                    {this.drawPie(angle - 1 / 2 * Math.PI, color, false, idx)}
                  </View>

                </View>

                <View style={{
                  opacity: 1,

                  width: size / 2,
                  height: size / 2
                }}>
                  {this.drawPie(1 / 2 * Math.PI, color, false, idx)}
                </View>

              </View>
            </View>

        ) : (
          // big 사용이유
          big ? (
            <View style={{
              width: size,
              height: size
            }}>
              <View style={{width: size, height: size / 2}} />
              <View style={{flexDirection: 'row'}}>
                <View style={{width: size / 2, height: size / 2}} />
                <View style={{
                  width: size / 2,
                  height: size / 2
                }}>

                  <View style={{
                    width: size / 2,
                    height: size / 2,
                    borderBottomRightRadius: size / 2,
                    backgroundColor: color,
                    transform: this.getTransform(angle - Math.PI / 2, size / 2)
                  }} />

                </View>

              </View>
            </View>
          ) : (
            <View style={{
              width: size / 2,
              height: size / 2
            }}>

              <View style={{
                width: size / 2,
                height: size / 2
              }}>

                <View style={{
                  width: size / 2,
                  height: size / 2,
                  borderBottomRightRadius: size / 2,
                  backgroundColor: color,
                  borderWidth: 0,
                  borderColor: color,
                  transform: this.getTransform(angle - Math.PI / 2, size / 2)
                }} />

              </View>

            </View>
          )
        ))}
      </View>
    )
  }
  drawT () {
    let pies = []
    if (this.state.pieSize.length === 0) return null
    for (let i = 0; i < this.state.pieSize.length; i++) {
      pies.push(
        <View key={`t${i}`} style={{
          transform: [{rotate: `${this.state.piePos[i]}rad`}],
          position: 'absolute'

        }}>
          {
            this.drawPie(this.state.pieSize[i], this.state.colors[this.state.pieIndex[i]], true, i)
          }
        </View>
        )
    }
    return (
      pies
    )
  }
  render () {
    const {selectedIndex, locationX, locationY, evtX, evtY, inPie, selectedAngle,
      pieSize, angles} = this.state
    const {size} = this.props
    return (
      <View collapsable={false}>

        <TouchableWithoutFeedback onPress={(e) => {
          // const {locationX, locationY} = e.nativeEvent
          // console.log(locationX, locationY)
          // this.setState({locationX: locationX, locationY: locationY})
          this.handleEventOld(e)
        }}>

          <View ref='test' style={StyleSheet.flatten([styles.container, {
            width: size,
            height: size
          }])}>

            {this.drawT()}
            {this.drawInfoT(this.state.selectedIndex)}
          </View>

        </TouchableWithoutFeedback>
        <Text>selected: {selectedIndex}</Text>
        <Text>({locationX},{locationY})</Text>
        <Text>({evtX},{evtY})</Text>
        <Text>is coordinate In Pie chart: {'' + inPie}</Text>
        <Text>angle: {selectedAngle}</Text>
        <Text>angles: {JSON.stringify(angles)}</Text>
      </View>
    )
  }
}

PieChart.defaultProps = {
  data: [{value: 10}, {value: 20}, {value: 40}, {value: 100}],
  colors: ['green', 'red', 'blue', 'black', 'yellow', 'purple', 'blue', 'orange'],
  size: 200
}
const styles = StyleSheet.create({
  container: {
    overflow: 'hidden',
    marginLeft: 100,
    alignItems: 'center',
    justifyContent: 'center'
  },
  tooltipTitle: {fontSize: 18, fontWeight: 'bold'},
  tooltipValue: {fontSize: 15, marginTop: 5}
})

export default PieChart
<|endoftext|>


<commit_before>import { polyfill } from 'es6-promise';

import '@styles/main.less';

import React from 'react';
import { render } from 'react-dom';
import { hashHistory } from 'react-router';
import { syncHistoryWithStore } from 'react-router-redux';
import { Root } from '@containers';
import configureStore from '@redux/configureStore';

polyfill();

window.backendUrl = 'http://localhost:3001/api';
window.tokenName = 'react-starter-token';

const store = configureStore();

const history = syncHistoryWithStore(hashHistory, store);

render(
    <Root store={store} history={history} />,
    document.getElementById('root')
);

// TODO - https://github.com/facebookincubator/create-react-app
// TODO - reselect
// TODO - redux saga
// TODO - flow
// TODO - router v4
<commit_msg>Remove saga from todo, I will stick with thunk for now<commit_after>import { polyfill } from 'es6-promise';

import '@styles/main.less';

import React from 'react';
import { render } from 'react-dom';
import { hashHistory } from 'react-router';
import { syncHistoryWithStore } from 'react-router-redux';
import { Root } from '@containers';
import configureStore from '@redux/configureStore';

polyfill();

window.backendUrl = 'http://localhost:3001/api';
window.tokenName = 'react-starter-token';

const store = configureStore();

const history = syncHistoryWithStore(hashHistory, store);

render(
    <Root store={store} history={history} />,
    document.getElementById('root')
);

// TODO - https://github.com/facebookincubator/create-react-app
// TODO - reselect
// TODO - flow
// TODO - router v4
<|endoftext|>


<commit_before>
describe('eventLimit popover', function() {

  var options

  beforeEach(function() {
    affix('#cal')
    options = {
      defaultView: 'month',
      defaultDate: '2014-08-01',
      eventLimit: 3,
      events: [
        { title: 'event1', start: '2014-07-28', end: '2014-07-30', className: 'event1' },
        { title: 'event2', start: '2014-07-29', end: '2014-07-31', className: 'event2' },
        { title: 'event3', start: '2014-07-29', className: 'event3' },
        { title: 'event4', start: '2014-07-29', className: 'event4' }
      ],
      popoverViewportConstrain: false, // because PhantomJS window is small, don't do smart repositioning
      handleWindowResize: false // because showing the popover causes scrollbars and fires resize
    }
  })

  it('closes when user clicks the X and trigger eventDestroy for every render', function() {
    var eventsRendered = {}
    var renderCount = 0
    var activated = false
    options.eventRender = function(eventObject, element, view) {
      if (activated) {
        eventsRendered[eventObject.title] = eventObject
        ++renderCount
      }
    }
    options.eventDestroy = function(eventObject, element, view) {
      delete eventsRendered[eventObject.title]
      --renderCount
    }
    // Create calendar
    $('#cal').fullCalendar(options)
    // Activate flags and pop event limit popover
    activated = true
    $('.fc-more').simulate('click')

    expect($('.fc-more-popover')).toBeVisible()
    $('.fc-more-popover .fc-close')
      .simulate('click')
    expect($('.fc-more-popover')).not.toBeVisible()
    expect(Object.keys(eventsRendered).length).toEqual(0)
    expect(renderCount).toEqual(0)
  })

  it('closes when user clicks outside of the popover and trigger eventDestroy for every render', function() {
    var eventsRendered = {}
    var renderCount = 0
    var activated = false
    options.eventRender = function(eventObject, element, view) {
      if (activated) {
        eventsRendered[eventObject.title] = eventObject
        ++renderCount
      }
    }
    options.eventDestroy = function(eventObject, element, view) {
      delete eventsRendered[eventObject.title]
      --renderCount
    }
    // Create calendar
    $('#cal').fullCalendar(options)
    // Activate flags and pop event limit popover
    activated = true
    $('.fc-more').simulate('click')

    expect($('.fc-more-popover')).toBeVisible()
    $('body').simulate('mousedown').simulate('click')
    expect($('.fc-more-popover')).not.toBeVisible()
    expect(Object.keys(eventsRendered).length).toEqual(0)
    expect(renderCount).toEqual(0)
  })
})
<commit_msg>Update legacy tests<commit_after>describe('eventLimit popover', function() {

  pushOptions({
    defaultView: 'month',
    defaultDate: '2014-08-01',
    eventLimit: 3,
    events: [
      { title: 'event1', start: '2014-07-28', end: '2014-07-30', className: 'event1' },
      { title: 'event2', start: '2014-07-29', end: '2014-07-31', className: 'event2' },
      { title: 'event3', start: '2014-07-29', className: 'event3' },
      { title: 'event4', start: '2014-07-29', className: 'event4' }
    ],
    popoverViewportConstrain: false, // because PhantomJS window is small, don't do smart repositioning
    handleWindowResize: false // because showing the popover causes scrollbars and fires resize
  })

  it('closes when user clicks the X and trigger eventDestroy for every render', function() {
    var eventsRendered = {}
    var renderCount = 0
    var activated = false
    initCalendar({
      eventRender: function(eventObject, element, view) {
        if (activated) {
          eventsRendered[eventObject.title] = eventObject
          ++renderCount
        }
      },
      eventDestroy: function(eventObject, element, view) {
        delete eventsRendered[eventObject.title]
        --renderCount
      }
    })
    // Activate flags and pop event limit popover
    activated = true
    $('.fc-more').simulate('click')

    expect($('.fc-more-popover')).toBeVisible()
    $('.fc-more-popover .fc-close')
      .simulate('click')
    expect($('.fc-more-popover')).not.toBeVisible()
    expect(Object.keys(eventsRendered).length).toEqual(0)
    expect(renderCount).toEqual(0)
  })

  it('closes when user clicks outside of the popover and trigger eventDestroy for every render', function() {
    var eventsRendered = {}
    var renderCount = 0
    var activated = false
    initCalendar({
      eventRender: function(eventObject, element, view) {
        if (activated) {
          eventsRendered[eventObject.title] = eventObject
          ++renderCount
        }
      },
      eventDestroy: function(eventObject, element, view) {
        delete eventsRendered[eventObject.title]
        --renderCount
      }
    })
    // Activate flags and pop event limit popover
    activated = true
    $('.fc-more').simulate('click')

    expect($('.fc-more-popover')).toBeVisible()
    $('body').simulate('mousedown').simulate('click')
    expect($('.fc-more-popover')).not.toBeVisible()
    expect(Object.keys(eventsRendered).length).toEqual(0)
    expect(renderCount).toEqual(0)
  })
})
<|endoftext|>


<commit_before>
JET.ParticleMaterial = function(params) {
	THREE.ShaderMaterial.call(this, params);

	this.attributes = {
		alpha: { type: 'f', value: [] }
	};
	params.color = params.color || 0xffffff;
	this.uniforms = {
		"psColor" : { type: "c", value: new THREE.Color(params.color) },
		"opacity" : { type: "f", value: params.opacity || 1.0 },
		"size" : { type: "f", value: params.size || 1.0 },
		"scale" : { type: "f", value: params.scale || 1.0 },
		"map" : { type: "t", value: params.map || null }
	};
	this.map = params.map;
	this.color = new THREE.Color(params.color);
	//this.sizeAttenuation = params.sizeAttenuation;
	//this.size = params.size;
	this.vertexColors = params.vertexColors;
	this.setValues(params);

	this.vertexShader = [
		"attribute float alpha;",
		"uniform float size;",
		"uniform float scale;",
		THREE.ShaderChunk[ "color_pars_vertex" ],
		"varying float vAlpha;",
		"void main() {",
			THREE.ShaderChunk[ "color_vertex" ],
			"vAlpha = alpha;",
			"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
			"#ifdef USE_SIZEATTENUATION",
				"gl_PointSize = size * ( scale / length( mvPosition.xyz ) );",
			"#else",
				"gl_PointSize = size;",
			"#endif",
			"gl_Position = projectionMatrix * mvPosition;",
			THREE.ShaderChunk[ "worldpos_vertex" ],
		"}"
	].join("\n"),

	this.fragmentShader = [
		"uniform vec3 psColor;",
		"uniform float opacity;",
		THREE.ShaderChunk[ "color_pars_fragment" ],
		THREE.ShaderChunk[ "map_particle_pars_fragment" ],
		"varying float vAlpha;",
		"void main() {",
			"gl_FragColor = vec4( psColor, opacity * vAlpha );",
			THREE.ShaderChunk[ "map_particle_fragment" ],
			THREE.ShaderChunk[ "alphatest_fragment" ],
			THREE.ShaderChunk[ "color_fragment" ],
		"}"
	].join("\n")
}
JET.ParticleMaterial.prototype = Object.create(THREE.ShaderMaterial.prototype);

JET.TextureLib = {
	smoke: THREE.ImageUtils.loadTexture("assets/smoke.png")
}

JET.GradientLib = {
	trail: new JET.ColorGradient(0xcccccc, 0xffaa88),
	explosion: new JET.ColorGradient(0x777777, 0xffff00)
};
JET.GradientLib.trail.add(0.8, 0xbbbbbb);
JET.GradientLib.explosion.add(0.5, 0xbb2200);

JET.Particle = function(index) {
	this.index = index || 0;
	this.alpha = 1.0;
	this.velocity = new THREE.Vector3();
	this.lifeTime = 0;
};

JET.Emitter = function(params) {
	this.maxParticles = params.maxParticles;
	this.spawner = params.spawner;
	this.onBorn = params.onBorn;
	this.onUpdate = params.onUpdate;
	this.geometry = new THREE.Geometry();
	this.particles = new Array(this.maxParticles);
	this.geometry.vertices = new Array(this.maxParticles);
	this.geometry.colors = new Array(this.maxParticles);
	this.geometry.alphas = params.material.attributes.alpha.value = new Array(this.maxParticles);
	for (var i = 0; i < this.maxParticles; ++i) {
		this.geometry.vertices[i] = new THREE.Vector3();
		this.geometry.colors[i] = new THREE.Color();
		this.geometry.alphas[i] = 1.0;
		this.particles[i] = new JET.Particle(i);
	}
	this.particleSystem = new THREE.ParticleSystem(this.geometry, params.material);
	this.particleSystem.sortParticles = params.sortParticles || true;
	if (params.parent) params.parent.add(this.particleSystem);
};

JET.Emitter.prototype.update = function(dt) {
	var spawnAmount = this.spawner(dt);
	for (var i = 0; i < this.maxParticles; ++i) {
		var particle = this.particles[i];
		var position = this.geometry.vertices[i];
		var color = this.geometry.colors[i];
		particle.lifeTime -= dt;
		if (particle.lifeTime <= 0 && spawnAmount > 0) {
			this.onBorn(particle, position, color);
			--spawnAmount;
		} else if (particle.lifeTime > 0)
			this.onUpdate(particle, position, color, dt);
		else particle.alpha = 0;
		this.geometry.alphas[i] = particle.alpha;
	}
	this.geometry.verticesNeedUpdate = true;
	this.geometry.colorsNeedUpdate = true;
	this.particleSystem.material.attributes.alpha.needsUpdate = true;
}

// Create a simple jet trail emitter
JET.createTrail = function(parent) {
	var v = new THREE.Vector3();
	var toCreate = 0;
	var maxLife = 2;
	var emitter = new JET.Emitter({
		parent: scene,
		maxParticles: 400,
		material: new JET.ParticleMaterial({
			color: 0xffffff,
			size: 15,
			map: JET.TextureLib.smoke,
			sizeAttenuation: true,
			vertexColors: true,
			depthTest: false,
			depthWrite: true,
			transparent: true,
			opacity: 0.75,
			alphaTest: 0.1
		}),
		spawner: function(dt) {
			toCreate += 200 * dt;
			var amount = toCreate|0;
			toCreate -= amount;
			return amount;
		},
		onBorn: function(particle, position, color) {
			particle.lifeTime = maxLife;
			var speed = -40 + Math.random() * 20 + parent.speed * 0.7;
			var dx = Math.cos(parent.angle);
			var dy = Math.sin(parent.angle);
			particle.velocity.x = dx * speed + Math.random()*2;
			particle.velocity.y = dy * speed + Math.random()*2;
			position.copy(parent.position);
			var r = 7 + Math.random() * 5;
			position.x -= dx * r;
			position.y -= dy * r;
			position.z -= 2;
			JET.GradientLib.trail.getTo(1.0, color);
		},
		onUpdate: function(particle, position, color, dt) {
			v.copy(particle.velocity).multiplyScalar(dt);
			position.add(v);
			particle.velocity.multiplyScalar(1.0 - 0.5 * dt); // Some drag
			var normalizedLife = particle.lifeTime / maxLife;
			JET.GradientLib.trail.getTo(normalizedLife, color);
			particle.alpha = normalizedLife;
		}
	});
	return emitter;
};

// Spawn a one-shot explosion
JET.createExplosion = function(pos) {
	var maxLife = 1;
	var time = Date.now();
	var emitter = new JET.Emitter({
		parent: scene,
		maxParticles: 5,
		material: new JET.ParticleMaterial({
			color: 0xffffff,
			size: 100,
			map: JET.TextureLib.smoke,
			sizeAttenuation: true,
			vertexColors: true,
			depthTest: false,
			depthWrite: true,
			transparent: true,
			opacity: 0.75,
			alphaTest: 0.1
		}),
		spawner: function(dt) {
			if (Date.now() > time + maxLife * 1000) {
				emitter.done = true;
				return;
			}
			return 100;
		},
		onBorn: function(particle, position, color) {
			particle.lifeTime = 0.5 * (maxLife * Math.random() + maxLife);
			position.copy(pos);
			position.z += 5;
			JET.GradientLib.explosion.getTo(1.0, color);
		},
		onUpdate: function(particle, position, color, dt) {
			var normalizedLife = particle.lifeTime / maxLife;
			JET.GradientLib.explosion.getTo(normalizedLife, color);
			particle.alpha = normalizedLife;
		}
	});
	return emitter;
};

// Particle system initializer for simple particle flames
// TODO: Remove
JET.createParticleSystem = function(nParticles, material) {
	var i, geometry = new THREE.Geometry();
	// Init vertices & colors
	geometry.vertices = new Array(nParticles);
	geometry.colors = new Array(nParticles);
	for (i = 0; i < nParticles; ++i) {
		geometry.vertices[i] = new THREE.Vector3();
		geometry.colors[i] = new THREE.Color();
	}
	// Init particle system
	var particleSystem = new THREE.ParticleSystem(geometry, material);
	particleSystem.sortParticles = true;
	return particleSystem;
};
<commit_msg>Tweak particle counts.<commit_after>
JET.ParticleMaterial = function(params) {
	THREE.ShaderMaterial.call(this, params);

	this.attributes = {
		alpha: { type: 'f', value: [] }
	};
	params.color = params.color || 0xffffff;
	this.uniforms = {
		"psColor" : { type: "c", value: new THREE.Color(params.color) },
		"opacity" : { type: "f", value: params.opacity || 1.0 },
		"size" : { type: "f", value: params.size || 1.0 },
		"scale" : { type: "f", value: params.scale || 1.0 },
		"map" : { type: "t", value: params.map || null }
	};
	this.map = params.map;
	this.color = new THREE.Color(params.color);
	//this.sizeAttenuation = params.sizeAttenuation;
	//this.size = params.size;
	this.vertexColors = params.vertexColors;
	this.setValues(params);

	this.vertexShader = [
		"attribute float alpha;",
		"uniform float size;",
		"uniform float scale;",
		THREE.ShaderChunk[ "color_pars_vertex" ],
		"varying float vAlpha;",
		"void main() {",
			THREE.ShaderChunk[ "color_vertex" ],
			"vAlpha = alpha;",
			"vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
			"#ifdef USE_SIZEATTENUATION",
				"gl_PointSize = size * ( scale / length( mvPosition.xyz ) );",
			"#else",
				"gl_PointSize = size;",
			"#endif",
			"gl_Position = projectionMatrix * mvPosition;",
			THREE.ShaderChunk[ "worldpos_vertex" ],
		"}"
	].join("\n"),

	this.fragmentShader = [
		"uniform vec3 psColor;",
		"uniform float opacity;",
		THREE.ShaderChunk[ "color_pars_fragment" ],
		THREE.ShaderChunk[ "map_particle_pars_fragment" ],
		"varying float vAlpha;",
		"void main() {",
			"gl_FragColor = vec4( psColor, opacity * vAlpha );",
			THREE.ShaderChunk[ "map_particle_fragment" ],
			THREE.ShaderChunk[ "alphatest_fragment" ],
			THREE.ShaderChunk[ "color_fragment" ],
		"}"
	].join("\n")
}
JET.ParticleMaterial.prototype = Object.create(THREE.ShaderMaterial.prototype);

JET.TextureLib = {
	smoke: THREE.ImageUtils.loadTexture("assets/smoke.png")
}

JET.GradientLib = {
	trail: new JET.ColorGradient(0xcccccc, 0xffaa88),
	explosion: new JET.ColorGradient(0x777777, 0xffff00)
};
JET.GradientLib.trail.add(0.8, 0xbbbbbb);
JET.GradientLib.explosion.add(0.5, 0xbb2200);

JET.Particle = function(index) {
	this.index = index || 0;
	this.alpha = 1.0;
	this.velocity = new THREE.Vector3();
	this.lifeTime = 0;
};

JET.Emitter = function(params) {
	this.maxParticles = params.maxParticles;
	this.spawner = params.spawner;
	this.onBorn = params.onBorn;
	this.onUpdate = params.onUpdate;
	this.geometry = new THREE.Geometry();
	this.particles = new Array(this.maxParticles);
	this.geometry.vertices = new Array(this.maxParticles);
	this.geometry.colors = new Array(this.maxParticles);
	this.geometry.alphas = params.material.attributes.alpha.value = new Array(this.maxParticles);
	for (var i = 0; i < this.maxParticles; ++i) {
		this.geometry.vertices[i] = new THREE.Vector3();
		this.geometry.colors[i] = new THREE.Color();
		this.geometry.alphas[i] = 1.0;
		this.particles[i] = new JET.Particle(i);
	}
	this.particleSystem = new THREE.ParticleSystem(this.geometry, params.material);
	this.particleSystem.sortParticles = params.sortParticles || true;
	if (params.parent) params.parent.add(this.particleSystem);
};

JET.Emitter.prototype.update = function(dt) {
	var spawnAmount = this.spawner(dt);
	for (var i = 0; i < this.maxParticles; ++i) {
		var particle = this.particles[i];
		var position = this.geometry.vertices[i];
		var color = this.geometry.colors[i];
		particle.lifeTime -= dt;
		if (particle.lifeTime <= 0 && spawnAmount > 0) {
			this.onBorn(particle, position, color);
			--spawnAmount;
		} else if (particle.lifeTime > 0)
			this.onUpdate(particle, position, color, dt);
		else particle.alpha = 0;
		this.geometry.alphas[i] = particle.alpha;
	}
	this.geometry.verticesNeedUpdate = true;
	this.geometry.colorsNeedUpdate = true;
	this.particleSystem.material.attributes.alpha.needsUpdate = true;
}

// Create a simple jet trail emitter
JET.createTrail = function(parent) {
	var v = new THREE.Vector3();
	var toCreate = 0;
	var maxLife = 2;
	var emitter = new JET.Emitter({
		parent: scene,
		maxParticles: 300,
		material: new JET.ParticleMaterial({
			color: 0xffffff,
			size: 15,
			map: JET.TextureLib.smoke,
			sizeAttenuation: true,
			vertexColors: true,
			depthTest: false,
			depthWrite: true,
			transparent: true,
			opacity: 0.75,
			alphaTest: 0.1
		}),
		spawner: function(dt) {
			toCreate += 150 * dt;
			var amount = toCreate|0;
			toCreate -= amount;
			return amount;
		},
		onBorn: function(particle, position, color) {
			particle.lifeTime = maxLife;
			var speed = -40 + Math.random() * 20 + parent.speed * 0.7;
			var dx = Math.cos(parent.angle);
			var dy = Math.sin(parent.angle);
			particle.velocity.x = dx * speed + Math.random()*2;
			particle.velocity.y = dy * speed + Math.random()*2;
			position.copy(parent.position);
			var r = 7 + Math.random() * 5;
			position.x -= dx * r;
			position.y -= dy * r;
			position.z -= 2;
			JET.GradientLib.trail.getTo(1.0, color);
		},
		onUpdate: function(particle, position, color, dt) {
			v.copy(particle.velocity).multiplyScalar(dt);
			position.add(v);
			particle.velocity.multiplyScalar(1.0 - 0.5 * dt); // Some drag
			var normalizedLife = particle.lifeTime / maxLife;
			JET.GradientLib.trail.getTo(normalizedLife, color);
			particle.alpha = normalizedLife;
		}
	});
	return emitter;
};

// Spawn a one-shot explosion
JET.createExplosion = function(pos) {
	var maxLife = 1.5;
	var time = Date.now();
	var emitter = new JET.Emitter({
		parent: scene,
		maxParticles: 10,
		material: new JET.ParticleMaterial({
			color: 0xffffff,
			size: 100,
			map: JET.TextureLib.smoke,
			sizeAttenuation: true,
			vertexColors: true,
			depthTest: false,
			depthWrite: true,
			transparent: true,
			opacity: 0.75,
			alphaTest: 0.1
		}),
		spawner: function(dt) {
			if (Date.now() > time + maxLife * 1000) {
				emitter.done = true;
				return;
			}
			return 100;
		},
		onBorn: function(particle, position, color) {
			particle.lifeTime = 0.5 * (maxLife * Math.random() + maxLife);
			position.copy(pos);
			position.z += 5;
			JET.GradientLib.explosion.getTo(1.0, color);
		},
		onUpdate: function(particle, position, color, dt) {
			var normalizedLife = particle.lifeTime / maxLife;
			JET.GradientLib.explosion.getTo(normalizedLife, color);
			particle.alpha = normalizedLife;
		}
	});
	return emitter;
};

// Particle system initializer for simple particle flames
// TODO: Remove
JET.createParticleSystem = function(nParticles, material) {
	var i, geometry = new THREE.Geometry();
	// Init vertices & colors
	geometry.vertices = new Array(nParticles);
	geometry.colors = new Array(nParticles);
	for (i = 0; i < nParticles; ++i) {
		geometry.vertices[i] = new THREE.Vector3();
		geometry.colors[i] = new THREE.Color();
	}
	// Init particle system
	var particleSystem = new THREE.ParticleSystem(geometry, material);
	particleSystem.sortParticles = true;
	return particleSystem;
};
<|endoftext|>


<commit_before>dojo.provide("dojox.image.LightboxNano");
dojo.require("dojo.fx");

(function(d){

var getViewport = function(){
		//	summary: Returns the dimensions and scroll position of the viewable area of a browser window
		var scrollRoot = (d.doc.compatMode == "BackCompat") ? d.body() : d.doc.documentElement,
			scroll = dojo._docScroll();
		return { w: scrollRoot.clientWidth, h: scrollRoot.clientHeight, l: scroll.x, t: scroll.y };
	},
	abs = "absolute";

d.declare("dojox.image.LightboxNano", null, {
	//	summary:
	//		A simple "nano" version of the lightbox.
	//
	//	description:
	//		Very lightweight lightbox which only displays a larger image.  There is
	//		no support for a caption or description.  The lightbox can be closed by
	//		clicking any where or pressing any key.  This widget is intended to be
	//		used on  and  tags.  Upon creation, if the domNode is  tag,
	//		then it is wrapped in an  tag, then a
 is placed
	//		inside the  and can be styled to display an icon that the original
	//		can be enlarged.
	//
	//	example:
	//	|
	//
	//	example:
	//	|

	//	href: string
	//		URL to the large image to show in the lightbox.
	href: "",

	//	duration: int
	//		The delay in milliseconds of the LightboxNano open and close animation.
	duration: 500,

	//	preloadDelay: int
	//		The delay in milliseconds after the LightboxNano is created before preloading the larger image.
	preloadDelay: 5000,

	constructor: function(p, n){
		// summary: Initializes the DOM node and connect onload event
		var _this = this;

		d.mixin(_this, p);
		n = dojo.byId(n);

		if(!/a/i.test(n.tagName)){
			var a = d.create("a", { href: _this.href, "class": n.className }, n, "after");
			n.className = "";
			a.appendChild(n);
			n = a;
		}

		d.style(n, {
			display: "block",
			position: "relative"
		});
		_this._createDiv("dojoxEnlarge", n);

		_this._node = n;
		d.setSelectable(n, false);
		_this._onClickEvt = d.connect(n, "onclick", _this, "_load");

		setTimeout(function(){
			(new Image()).src = _this.href;
			_this._hideLoading();
		}, _this.preloadDelay);
	},

	destroy: function(){
		// summary: Destroys the LightboxNano and it's DOM node
		var a = this._connects || [];
		a.push(this._onClickEvt);
		d.forEach(a, d.disconnect);
		d.destroy(this._node);
	},

	_createDiv: function(/*String*/cssClass, /*DomNode*/refNode, /*boolean*/display){
		// summary: Creates a div for the enlarge icon and loading indicator layers
		return d.create("div", { "class": cssClass, style: { position: abs, display: display ? "" : "none" } }, refNode); // DomNode
	},

	_load: function(/*Event*/e){
		// summary: Creates the large image and begins to show it
		var _this = this;

		d.stopEvent(e);

		if(!_this._loading){
			_this._loading = true;
			_this._reset();

			var n = d.query("img", _this._node)[0],
				a = d._abs(n, true),
				c = d.contentBox(n),
				b = d._getBorderExtents(n),
				i = _this._img = d.create("img", {
					style: {
						visibility: "hidden",
						cursor: "pointer",
						position: abs,
						top: 0,
						left: 0,
						zIndex: 9999999
					}
				}, d.body()),
				ln = _this._loadingNode;

			if(ln == null){
				_this._loadingNode = ln = _this._createDiv("dojoxLoading", _this._node, true)
				var l = d.marginBox(ln);
				d.style(ln, {
					left: parseInt((c.w - l.w) / 2) + "px",
					top: parseInt((c.h - l.h) / 2) + "px"
				});
			}

			c.x = a.x - 10 + b.l;
			c.y = a.y - 10 + b.t;
			_this._start = c;

			_this._connects = [d.connect(i, "onload", _this, "_show")];

			i.src = _this.href;
		}
	},

	_hideLoading: function(){
		// summary: Hides the animated loading indicator
		if(this._loadingNode){
			d.style(this._loadingNode, "display", "none");
		}
		this._loadingNode = false;
	},

	_show: function(){
		// summary: The image is now loaded, calculate size and display
		var _this = this,
			vp = getViewport(),
			w = _this._img.width,
			h = _this._img.height,
			vpw = parseInt((vp.w - 20) * 0.9),
			vph = parseInt((vp.h - 20) * 0.9),
			dd = d.doc,
			bg = _this._bg = d.create("div", {
				style: {
					backgroundColor: "#000",
					opacity: 0.0,
					position: abs,
					zIndex: 9999998
				}
			}, d.body()),
			ln = _this._loadingNode;

		if(_this._loadingNode){
			_this._hideLoading();
		}
		d.style(_this._img, {
			border: "10px solid #fff",
			visibility: "visible"
		});
		d.style(_this._node, "visibility", "hidden");

		_this._loading = false;

		_this._connects = _this._connects.concat([
			d.connect(dd, "onmousedown", _this, "_hide"),
			d.connect(dd, "onkeypress", _this, "_key"),
			d.connect(window, "onresize", _this, "_sizeBg")
		]);

		if(w > vpw){
			h = h * vpw / w;
			w = vpw;
		}
		if(h > vph){
			w = w * vph / h;
			h = vph;
		}

		_this._end = {
			x: (vp.w - 20 - w) / 2 + vp.l,
			y: (vp.h - 20 - h) / 2 + vp.t,
			w: w,
			h: h
		};

		_this._sizeBg();

		d.fx.combine([
			_this._anim(_this._img, _this._coords(_this._start, _this._end)),
			_this._anim(bg, { opacity: 0.5 })
		]).play();
	},

	_sizeBg: function(){
		// summary: Resize the background to fill the page
		var dd = d.doc.documentElement;
		d.style(this._bg, {
			top: 0,
			left: 0,
			width: dd.scrollWidth + "px",
			height: dd.scrollHeight + "px"
		});
	},

	_key: function(/*Event*/e){
		// summary: A key was pressed, so hide the lightbox
		d.stopEvent(e);
		this._hide();
	},

	_coords: function(/*Object*/s, /*Object*/e){
		// summary: Returns animation parameters with the start and end coords
		return {
			left:	{ start: s.x, end: e.x },
			top:	{ start: s.y, end: e.y },
			width:	{ start: s.w, end: e.w },
			height:	{ start: s.h, end: e.h }
		}; // object
	},

	_hide: function(){
		// summary: Closes the lightbox
		var _this = this;
		d.forEach(_this._connects, d.disconnect);
		_this._connects = [];
		d.fx.combine([
			_this._anim(_this._img, _this._coords(_this._end, _this._start), "_reset"),
			_this._anim(_this._bg, {opacity:0})
		]).play();
	},

	_reset: function(){
		// summary: Destroys the lightbox
		d.style(this._node, "visibility", "visible");
		d.forEach([this._img, this._bg], function(n){
			d.destroy(n);
			n = null;
		});
		this._node.focus();
	},

	_anim: function(node, args, onEnd){
		// summary: Creates the lightbox open/close and background fadein/out animations
		return d.animateProperty({
			node: node,
			duration: this.duration,
			properties: args,
			onEnd: onEnd ? d.hitch(this, onEnd) : null
		}); // object
	}
});

})(dojo);
<commit_msg>Refs #9029 - Was forcing display:block on the DOM node, but was not needed and didn't play nice when trying to use it on an inline anchor or image tag. !strict<commit_after>dojo.provide("dojox.image.LightboxNano");
dojo.require("dojo.fx");

(function(d){

var getViewport = function(){
		//	summary: Returns the dimensions and scroll position of the viewable area of a browser window
		var scrollRoot = (d.doc.compatMode == "BackCompat") ? d.body() : d.doc.documentElement,
			scroll = dojo._docScroll();
		return { w: scrollRoot.clientWidth, h: scrollRoot.clientHeight, l: scroll.x, t: scroll.y };
	},
	abs = "absolute";

d.declare("dojox.image.LightboxNano", null, {
	//	summary:
	//		A simple "nano" version of the lightbox.
	//
	//	description:
	//		Very lightweight lightbox which only displays a larger image.  There is
	//		no support for a caption or description.  The lightbox can be closed by
	//		clicking any where or pressing any key.  This widget is intended to be
	//		used on  and  tags.  Upon creation, if the domNode is  tag,
	//		then it is wrapped in an  tag, then a
 is placed
	//		inside the  and can be styled to display an icon that the original
	//		can be enlarged.
	//
	//	example:
	//	|
	//
	//	example:
	//	|

	//	href: string
	//		URL to the large image to show in the lightbox.
	href: "",

	//	duration: int
	//		The delay in milliseconds of the LightboxNano open and close animation.
	duration: 500,

	//	preloadDelay: int
	//		The delay in milliseconds after the LightboxNano is created before preloading the larger image.
	preloadDelay: 5000,

	constructor: function(p, n){
		// summary: Initializes the DOM node and connect onload event
		var _this = this;

		d.mixin(_this, p);
		n = dojo.byId(n);

		if(!/a/i.test(n.tagName)){
			var a = d.create("a", { href: _this.href, "class": n.className }, n, "after");
			n.className = "";
			a.appendChild(n);
			n = a;
		}

		d.style(n, "position", "relative");
		_this._createDiv("dojoxEnlarge", n);

		_this._node = n;
		d.setSelectable(n, false);
		_this._onClickEvt = d.connect(n, "onclick", _this, "_load");

		setTimeout(function(){
			(new Image()).src = _this.href;
			_this._hideLoading();
		}, _this.preloadDelay);
	},

	destroy: function(){
		// summary: Destroys the LightboxNano and it's DOM node
		var a = this._connects || [];
		a.push(this._onClickEvt);
		d.forEach(a, d.disconnect);
		d.destroy(this._node);
	},

	_createDiv: function(/*String*/cssClass, /*DomNode*/refNode, /*boolean*/display){
		// summary: Creates a div for the enlarge icon and loading indicator layers
		return d.create("div", { "class": cssClass, style: { position: abs, display: display ? "" : "none" } }, refNode); // DomNode
	},

	_load: function(/*Event*/e){
		// summary: Creates the large image and begins to show it
		var _this = this;

		d.stopEvent(e);

		if(!_this._loading){
			_this._loading = true;
			_this._reset();

			var n = d.query("img", _this._node)[0],
				a = d._abs(n, true),
				c = d.contentBox(n),
				b = d._getBorderExtents(n),
				i = _this._img = d.create("img", {
					style: {
						visibility: "hidden",
						cursor: "pointer",
						position: abs,
						top: 0,
						left: 0,
						zIndex: 9999999
					}
				}, d.body()),
				ln = _this._loadingNode;

			if(ln == null){
				_this._loadingNode = ln = _this._createDiv("dojoxLoading", _this._node, true)
				var l = d.marginBox(ln);
				d.style(ln, {
					left: parseInt((c.w - l.w) / 2) + "px",
					top: parseInt((c.h - l.h) / 2) + "px"
				});
			}

			c.x = a.x - 10 + b.l;
			c.y = a.y - 10 + b.t;
			_this._start = c;

			_this._connects = [d.connect(i, "onload", _this, "_show")];

			i.src = _this.href;
		}
	},

	_hideLoading: function(){
		// summary: Hides the animated loading indicator
		if(this._loadingNode){
			d.style(this._loadingNode, "display", "none");
		}
		this._loadingNode = false;
	},

	_show: function(){
		// summary: The image is now loaded, calculate size and display
		var _this = this,
			vp = getViewport(),
			w = _this._img.width,
			h = _this._img.height,
			vpw = parseInt((vp.w - 20) * 0.9),
			vph = parseInt((vp.h - 20) * 0.9),
			dd = d.doc,
			bg = _this._bg = d.create("div", {
				style: {
					backgroundColor: "#000",
					opacity: 0.0,
					position: abs,
					zIndex: 9999998
				}
			}, d.body()),
			ln = _this._loadingNode;

		if(_this._loadingNode){
			_this._hideLoading();
		}
		d.style(_this._img, {
			border: "10px solid #fff",
			visibility: "visible"
		});
		d.style(_this._node, "visibility", "hidden");

		_this._loading = false;

		_this._connects = _this._connects.concat([
			d.connect(dd, "onmousedown", _this, "_hide"),
			d.connect(dd, "onkeypress", _this, "_key"),
			d.connect(window, "onresize", _this, "_sizeBg")
		]);

		if(w > vpw){
			h = h * vpw / w;
			w = vpw;
		}
		if(h > vph){
			w = w * vph / h;
			h = vph;
		}

		_this._end = {
			x: (vp.w - 20 - w) / 2 + vp.l,
			y: (vp.h - 20 - h) / 2 + vp.t,
			w: w,
			h: h
		};

		_this._sizeBg();

		d.fx.combine([
			_this._anim(_this._img, _this._coords(_this._start, _this._end)),
			_this._anim(bg, { opacity: 0.5 })
		]).play();
	},

	_sizeBg: function(){
		// summary: Resize the background to fill the page
		var dd = d.doc.documentElement;
		d.style(this._bg, {
			top: 0,
			left: 0,
			width: dd.scrollWidth + "px",
			height: dd.scrollHeight + "px"
		});
	},

	_key: function(/*Event*/e){
		// summary: A key was pressed, so hide the lightbox
		d.stopEvent(e);
		this._hide();
	},

	_coords: function(/*Object*/s, /*Object*/e){
		// summary: Returns animation parameters with the start and end coords
		return {
			left:	{ start: s.x, end: e.x },
			top:	{ start: s.y, end: e.y },
			width:	{ start: s.w, end: e.w },
			height:	{ start: s.h, end: e.h }
		}; // object
	},

	_hide: function(){
		// summary: Closes the lightbox
		var _this = this;
		d.forEach(_this._connects, d.disconnect);
		_this._connects = [];
		d.fx.combine([
			_this._anim(_this._img, _this._coords(_this._end, _this._start), "_reset"),
			_this._anim(_this._bg, {opacity:0})
		]).play();
	},

	_reset: function(){
		// summary: Destroys the lightbox
		d.style(this._node, "visibility", "visible");
		d.forEach([this._img, this._bg], function(n){
			d.destroy(n);
			n = null;
		});
		this._node.focus();
	},

	_anim: function(node, args, onEnd){
		// summary: Creates the lightbox open/close and background fadein/out animations
		return d.animateProperty({
			node: node,
			duration: this.duration,
			properties: args,
			onEnd: onEnd ? d.hitch(this, onEnd) : null
		}); // object
	}
});

})(dojo);
<|endoftext|>


<commit_before>//====================================================//
//==== Playlist: enter the file names here: ==========//

var allVideos = {
  "videos": [
  {"title": "video 1",
  "h264": "videos/01-16s_h264.mp4",
  "webm": "videos/01-16s_h264.webm",
  "ogg": "videos/01-16s_h264.ogv",
  "doubleplay": "true",
  "waitmessage": "The video will play again in 5 seconds."
},
{"title": "video 2",
"h264": "videos/02-18s_h264.mp4",
"webm": "videos/02-18s_h264.webm",
"ogg": "videos/02-18s_h264.ogv",
"doubleplay": "true",
"waitmessage": "The video will play again in 5 seconds."
},
{"title": "video 3",
"h264": "videos/03-13s_h264.mp4",
"webm": "videos/03-13s_h264.webm",
"ogg": "videos/03-13s_h264.ogv",
"doubleplay": "true",
"waitmessage": "The video will play again in 5 seconds."
},
{"title": "video 4",
"h264": "videos/04-13s_h264.mp4",
"webm": "videos/04-13s_h264.webm",
"ogg": "videos/04-13s_h264.ogv",
"doubleplay": "true",
"waitmessage": "The video will play again in 5 seconds."
},
{"title": "video 5",
"h264": "videos/05-22s_h264.mp4",
"webm": "videos/05-22s_h264.webm",
"ogg": "videos/05-22s_h264.ogv",
"doubleplay": "true",
"waitmessage": "The video will play again in 5 seconds."
},
{"title": "video 6",
"h264": "videos/magenta_test.mp4",
"doubleplay": "true",
"waitmessage": "The video will play again in 5 seconds."

},
{"title": "video 7",
"h264": "videos/magenta_test2.mp4",
"doubleplay": "true",
"waitmessage": "The video will play again in 5 seconds."
},
]
};

var randomOrder = true; // set to false if playlist order is non-random

//== Playing behavior
var autoplayOn = false;  /// **TODO** -autoplay video upon clicking "next"

//=== UI control toggles - set to false to remove from UI
var playOn = true;
var skipOn = true;
var volumeOn =  true;
var muteOn = true;
var durationOn = true;
var seekOn = true;
var fullscOn = true;

//====================================================//
//===================================================//

// html5 video elements
var vidElements = [];
// display index
var vindex = 0;

console.log("modernizr video h264 support? " + Modernizr.video.h264);
console.log("modernizr video webm support? " + Modernizr.video.webm);
console.log("modernizr video ogg support? " + Modernizr.video.ogg);

// this function will randomly shuffle the list of videos upon page load
// from http://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array
function shuffle(array) {
  var currentIndex = array.length, temporaryValue, randomIndex ;

  // While there remain elements to shuffle...
  while (0 !== currentIndex) {

    // Pick a remaining element...
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex -= 1;

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

  return array;
}

// initialize videojs player and its UI
var vjsPlayer = function(w, h, i){
  // add videojs player to each video
  var options = {
    "width": w,
    "height": h,
    controlBar: {
      playToggle: (playOn ? true : false),
      fullscreenToggle: (fullscOn ? true : false),
      currentTimeDisplay: (durationOn ? true : false),
      durationDisplay: (durationOn ? true : false),
      remainingTimeDisplay: (durationOn ? true : false),
      timeDivider: (durationOn ? true : false),
      progressControl: {
        seekBar: (seekOn ? true : false),
      },
      volumeControl: (volumeOn ? true : false),
      muteToggle: (muteOn ? true : false),
    },
  };

  addVjs("video"+i, options);
  var thisVideo = document.getElementById("video"+vindex);

  if(i == 0){
    console.log("the I is: "+i);
    thisVideo.style.display="block";
    showVjs("video"+i, i);
  }
  else{
    thisVideo.style.display="none";
    hideVjs("video"+i, i);
    showVjs("video"+vindex, vindex);
  }

}; // end vjsplayer function

/// some globals -- todo: refactor this ---//
// video element
var theVideo;
// buttons - so they can access each other
var prevBtn, nextBtn;
// player state
var playing, speed, ffing, rwing, rwinterval;

function skipUI(){

  var theVideo = document.getElementById("video"+vindex);

  $("#video_player").append('<button type="button" class="btn_skip" id="btn_prev" disabled> < < </button>');
  $("#video_player").append('<button type="button" class="btn_skip" id="btn_next" > > > </button>');

  prevBtn = document.getElementById("btn_prev");
  nextBtn = document.getElementById("btn_next");

  // iterate through video playlist backwards
  prevBtn.addEventListener("click", function(){
    console.log("prev click, vindex: ", vindex);
    if(vindex == 0){
      prevBtn.disabled = true;
    }
    else if (vindex == 1) {
      hideVjs("video"+vindex, vindex);
      vindex--;
      showVjs("video"+vindex, vindex);
      prevBtn.disabled = true;
    }
    else{
      hideVjs("video"+vindex, vindex);
      vindex--;
      showVjs("video"+vindex, vindex);
      nextBtn.disabled = false;
    }
  });
  // iterate through video playlist forwards
  nextBtn.addEventListener("click", function(){
    console.log("next click, vindex: ", vindex);
    if(vindex == vidElements.length-1 ){
      nextBtn.disabled = true;
    }
    else if(vindex == vidElements.length-2){
      hideVjs("video"+vindex);
      vindex++;
      showVjs("video"+vindex, vindex);
      prevBtn.disabled = false;
      nextBtn.disabled = true;
    }
    else{
      hideVjs("video"+vindex);
      vindex++;
      showVjs("video"+vindex, vindex);
      prevBtn.disabled = false;
    }

  });

}; // end skipUI function

function addVjs(vidElement, options){
  var thePlayer = videojs(vidElement, options, function(){
    console.log("initializing video js player for "+vidElement);
    this.addClass("vjs-big-play-centered");
    // document.getElementById(vidElement).style.display="none";
  });
}

function showVjs(vidElement, i){
  $("#vid"+vindex).show();
  if($("#vid"+vindex).attr("data-loaded")=="false"){ $("#progress"+vindex).show(); }
  var newVideo = document.getElementById(vidElement+"_html5_api");
  newVideo.style.display="inline block";
  var theVjs = document.getElementById(vidElement);
  theVjs.style.display="block";
  $("#"+vidElement).addClass("video-js vjs-default-skin");
  console.log("showing video js player for "+vidElement);
}

function hideVjs(vidElement, i){
  $("#vid"+i).hide();
  $("#progress"+vindex).hide();
  var oldVideo = document.getElementById(vidElement);
    // reset to beginning
    videojs(oldVideo).pause();
    videojs(oldVideo).currentTime(0);
    oldVideo.style.display="none";
    $("#"+vidElement).removeClass("video-js vjs-default-skin");
    console.log("player hidden for "+vidElement);
  }

// send the appropriate videos to the video loader
var preLoader = function(){
  var vidList = [];
  var vidType ;

  // check browser compatibilty and initiate fall-backs
  if (Modernizr.video.h264){ vidType = "video/mp4";}
  else if (Modernizr.video.webm){ vidType = "video/webm";}
  else if(Modernizr.video.ogg){ vidType = "video/ogg";}
  else { console.log("no html5 video support :( consider upgrading to a modern browser"); }

  // make a list of video files, basd on file type
  for (var i = 0; i < allVideos.videos.length; i++){
    switch(vidType){
      case "video/mp4":
      vidList.push(allVideos.videos[i].h264);
      break;
      case "video/webm":
      vidList.push(allVideos.videos[i].webm);
      break;
      case "video/ogg":
      vidList.push(allVideos.videos[i].ogg);
      break;
      default:
      console.log("no html5 video support :( ");
    }

  }

  // randomize the videos, if necessary
  if (randomOrder){ shuffle(vidList); }

  // start loading the videos
  for (var i = 0; i < vidList.length; i++) {
    // add form elements to track play count
    $('form[name="myForm"]').append('<input type="hidden" name="'+vidList[i]+'" value="0" />');
    // add video & progress element for each video
    $("#videos").append('
<video id="video'+i+'"  > Your browser does not support the video tag. </video><progress id="progress'+i+'" value="2" max="100"></progress>
');
    if (i == 0){ $("#progress"+i).show(); }
    // load the videos into their elements
    console.log("video " +i+ " filename: " + (vidList[i]));
    loadVid(vidList[i], vidType, i);

  }

  // player element w progress bar
  var videoPlayer = document.getElementById("video_player");
  videoPlayer.style.display="table";

};

$(document).ready(preLoader());

// load the videos asynchronously while the user waits
// based on http://stackoverflow.com/questions/18251632/another-force-chrome-to-fully-buffer-mp4-video
function loadVid(vidFile, vidType, i){
	var elementID = "video"+i;
  var xhr = new XMLHttpRequest();
  xhr.onprogress = function(e){
    if(e.lengthComputable){
      var perctload = (e.loaded / e.total) *100;
      $("#progress"+i).val(perctload);
      console.log("computable - "+perctload, vidFile);
      if(perctload == 100){
        $("#progress"+i).hide();
        $("#vid"+i).attr("data-loaded", "true");
      }
    }
    else {
      console.log("unable to compute progress for ",vidFile);
    }
  };
  xhr.open('GET', vidFile, true);
  xhr.responseType = 'blob';
  xhr.onload = function(e) {

    if (this.status == 200) {
      var ltv = loadThisVid(this.response, i);
    }
  };

  function loadThisVid(response, i){
    var myBlob = response;
    var vid = (window.webkitURL ? webkitURL : URL).createObjectURL(myBlob);
      // myBlob is now the blob that the object URL pointed to.
      var video = document.getElementById(elementID);

      console.log("Loading video "+vidFile+" into element video"+i);
      video.src = vid;
      video.type=vidType ;
      video.controls=true;
      // update hidden form with play count
      $(video).on('play', function(){
        var v = parseInt($('input[name="'+vidFile+'"]').val());
        $('input[name="'+vidFile+'"]').val(++v);
      });
      vidElements.push(video);
      video.addEventListener( "loadedmetadata", function (e) {
        var width = this.videoWidth,
        height = this.videoHeight;
        vjsPlayer(width, height, i);
        // initialize skip buttons if its the first video
        if(i === 0 && skipOn){ skipUI(); }
      }, false );
    }

    xhr.send();

  }<commit_msg>disabled context menu in videoPlayer<commit_after>//====================================================//
//==== Playlist: enter the file names here: ==========//

var allVideos = {
  "videos": [
  {"title": "video 1",
  "h264": "videos/01-16s_h264.mp4",
  "webm": "videos/01-16s_h264.webm",
  "ogg": "videos/01-16s_h264.ogv",
  "doubleplay": "true",
  "waitmessage": "The video will play again in 5 seconds."
},
{"title": "video 2",
"h264": "videos/02-18s_h264.mp4",
"webm": "videos/02-18s_h264.webm",
"ogg": "videos/02-18s_h264.ogv",
"doubleplay": "true",
"waitmessage": "The video will play again in 5 seconds."
},
{"title": "video 3",
"h264": "videos/03-13s_h264.mp4",
"webm": "videos/03-13s_h264.webm",
"ogg": "videos/03-13s_h264.ogv",
"doubleplay": "true",
"waitmessage": "The video will play again in 5 seconds."
},
{"title": "video 4",
"h264": "videos/04-13s_h264.mp4",
"webm": "videos/04-13s_h264.webm",
"ogg": "videos/04-13s_h264.ogv",
"doubleplay": "true",
"waitmessage": "The video will play again in 5 seconds."
},
{"title": "video 5",
"h264": "videos/05-22s_h264.mp4",
"webm": "videos/05-22s_h264.webm",
"ogg": "videos/05-22s_h264.ogv",
"doubleplay": "true",
"waitmessage": "The video will play again in 5 seconds."
},
{"title": "video 6",
"h264": "videos/magenta_test.mp4",
"doubleplay": "true",
"waitmessage": "The video will play again in 5 seconds."

},
{"title": "video 7",
"h264": "videos/magenta_test2.mp4",
"doubleplay": "true",
"waitmessage": "The video will play again in 5 seconds."
},
]
};

var randomOrder = true; // set to false if playlist order is non-random

//== Playing behavior
var autoplayOn = false;  /// **TODO** -autoplay video upon clicking "next"

//=== UI control toggles - set to false to remove from UI
var playOn = true;
var skipOn = true;
var volumeOn =  true;
var muteOn = true;
var durationOn = true;
var seekOn = true;
var fullscOn = true;

//====================================================//
//===================================================//

// html5 video elements
var vidElements = [];
// display index
var vindex = 0;

console.log("modernizr video h264 support? " + Modernizr.video.h264);
console.log("modernizr video webm support? " + Modernizr.video.webm);
console.log("modernizr video ogg support? " + Modernizr.video.ogg);

// this function will randomly shuffle the list of videos upon page load
// from http://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array
function shuffle(array) {
  var currentIndex = array.length, temporaryValue, randomIndex ;

  // While there remain elements to shuffle...
  while (0 !== currentIndex) {

    // Pick a remaining element...
    randomIndex = Math.floor(Math.random() * currentIndex);
    currentIndex -= 1;

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

  return array;
}

// initialize videojs player and its UI
var vjsPlayer = function(w, h, i){
  // add videojs player to each video
  var options = {
    "width": w,
    "height": h,
    controlBar: {
      playToggle: (playOn ? true : false),
      fullscreenToggle: (fullscOn ? true : false),
      currentTimeDisplay: (durationOn ? true : false),
      durationDisplay: (durationOn ? true : false),
      remainingTimeDisplay: (durationOn ? true : false),
      timeDivider: (durationOn ? true : false),
      progressControl: {
        seekBar: (seekOn ? true : false),
      },
      volumeControl: (volumeOn ? true : false),
      muteToggle: (muteOn ? true : false),
    },
  };

  addVjs("video"+i, options);
  var thisVideo = document.getElementById("video"+vindex);

  if(i == 0){
    console.log("the I is: "+i);
    thisVideo.style.display="block";
    showVjs("video"+i, i);
  }
  else{
    thisVideo.style.display="none";
    hideVjs("video"+i, i);
    showVjs("video"+vindex, vindex);
  }

}; // end vjsplayer function

/// some globals -- todo: refactor this ---//
// video element
var theVideo;
// buttons - so they can access each other
var prevBtn, nextBtn;
// player state
var playing, speed, ffing, rwing, rwinterval;

function skipUI(){

  var theVideo = document.getElementById("video"+vindex);

  $("#video_player").append('<button type="button" class="btn_skip" id="btn_prev" disabled> < < </button>');
  $("#video_player").append('<button type="button" class="btn_skip" id="btn_next" > > > </button>');

  prevBtn = document.getElementById("btn_prev");
  nextBtn = document.getElementById("btn_next");

  // iterate through video playlist backwards
  prevBtn.addEventListener("click", function(){
    console.log("prev click, vindex: ", vindex);
    if(vindex == 0){
      prevBtn.disabled = true;
    }
    else if (vindex == 1) {
      hideVjs("video"+vindex, vindex);
      vindex--;
      showVjs("video"+vindex, vindex);
      prevBtn.disabled = true;
    }
    else{
      hideVjs("video"+vindex, vindex);
      vindex--;
      showVjs("video"+vindex, vindex);
      nextBtn.disabled = false;
    }
  });
  // iterate through video playlist forwards
  nextBtn.addEventListener("click", function(){
    console.log("next click, vindex: ", vindex);
    if(vindex == vidElements.length-1 ){
      nextBtn.disabled = true;
    }
    else if(vindex == vidElements.length-2){
      hideVjs("video"+vindex);
      vindex++;
      showVjs("video"+vindex, vindex);
      prevBtn.disabled = false;
      nextBtn.disabled = true;
    }
    else{
      hideVjs("video"+vindex);
      vindex++;
      showVjs("video"+vindex, vindex);
      prevBtn.disabled = false;
    }

  });

}; // end skipUI function

function addVjs(vidElement, options){
  var thePlayer = videojs(vidElement, options, function(){
    console.log("initializing video js player for "+vidElement);
    this.addClass("vjs-big-play-centered");
  });
}

function showVjs(vidElement, i){
  $("#vid"+vindex).show();
  if($("#vid"+vindex).attr("data-loaded")=="false"){ $("#progress"+vindex).show(); }
  var newVideo = document.getElementById(vidElement+"_html5_api");
  newVideo.style.display="inline block";
  var theVjs = document.getElementById(vidElement);
  theVjs.style.display="block";
  $("#"+vidElement).addClass("video-js vjs-default-skin");
  console.log("showing video js player for "+vidElement);
}

function hideVjs(vidElement, i){
  $("#vid"+i).hide();
  $("#progress"+vindex).hide();
  var oldVideo = document.getElementById(vidElement);
    // reset to beginning
    videojs(oldVideo).pause();
    videojs(oldVideo).currentTime(0);
    oldVideo.style.display="none";
    $("#"+vidElement).removeClass("video-js vjs-default-skin");
    console.log("player hidden for "+vidElement);
  }

// send the appropriate videos to the video loader
var preLoader = function(){
  var vidList = [];
  var vidType ;

  // check browser compatibilty and initiate fall-backs
  if (Modernizr.video.h264){ vidType = "video/mp4";}
  else if (Modernizr.video.webm){ vidType = "video/webm";}
  else if(Modernizr.video.ogg){ vidType = "video/ogg";}
  else { console.log("no html5 video support :( consider upgrading to a modern browser"); }

  // make a list of video files, basd on file type
  for (var i = 0; i < allVideos.videos.length; i++){
    switch(vidType){
      case "video/mp4":
      vidList.push(allVideos.videos[i].h264);
      break;
      case "video/webm":
      vidList.push(allVideos.videos[i].webm);
      break;
      case "video/ogg":
      vidList.push(allVideos.videos[i].ogg);
      break;
      default:
      console.log("no html5 video support :( ");
    }

  }

  // randomize the videos, if necessary
  if (randomOrder){ shuffle(vidList); }

  // start loading the videos
  for (var i = 0; i < vidList.length; i++) {
    // add form elements to track play count
    $('form[name="myForm"]').append('<input type="hidden" name="'+vidList[i]+'" value="0" />');
    // add video & progress element for each video
    $("#videos").append('
<video id="video'+i+'"  > Your browser does not support the video tag. </video><progress id="progress'+i+'" value="2" max="100"></progress>
');
    // visibilty settings
    if(i != 1){ $("#vid"+i).hide(); }
    if (i == 0){ $("#progress"+i).show(); }
    // load the videos into their elements
    console.log("video " +i+ " filename: " + (vidList[i]));
    loadVid(vidList[i], vidType, i);

  }

  // player element w progress bar
  var videoPlayer = document.getElementById("video_player");
  videoPlayer.style.display="table";
  // disable right-click in the video player
  videoPlayer.addEventListener("contextmenu", function(e){
      e.preventDefault();
    }, false);

};

$(document).ready( preLoader() );

// load the videos asynchronously while the user waits
// based on http://stackoverflow.com/questions/18251632/another-force-chrome-to-fully-buffer-mp4-video
function loadVid(vidFile, vidType, i){
	var elementID = "video"+i;
  var xhr = new XMLHttpRequest();
  xhr.onprogress = function(e){
    if(e.lengthComputable){
      var perctload = (e.loaded / e.total) *100;
      $("#progress"+i).val(perctload);
      console.log("computable - "+perctload, vidFile);
      if(perctload == 100){
        $("#progress"+i).hide();
        $("#vid"+i).attr("data-loaded", "true");
      }
    }
    else {
      console.log("unable to compute progress for ",vidFile);
    }
  };
  xhr.open('GET', vidFile, true);
  xhr.responseType = 'blob';
  xhr.onload = function(e) {

    if (this.status == 200) {
      var ltv = loadThisVid(this.response, i);
    }
  };

  function loadThisVid(response, i){
    var myBlob = response;
    var vid = (window.webkitURL ? webkitURL : URL).createObjectURL(myBlob);
      // myBlob is now the blob that the object URL pointed to.
      var video = document.getElementById(elementID);

      console.log("Loading video "+vidFile+" into element video"+i);
      video.src = vid;
      video.type=vidType ;
      video.controls=true;
      // update hidden form with play count
      $(video).on('play', function(){
        var v = parseInt($('input[name="'+vidFile+'"]').val());
        $('input[name="'+vidFile+'"]').val(++v);
      });
      vidElements.push(video);
      video.addEventListener( "loadedmetadata", function (e) {
        var width = this.videoWidth,
        height = this.videoHeight;
        vjsPlayer(width, height, i);
        // initialize skip buttons if its the first video
        if(i === 0 && skipOn){ skipUI(); }
      }, false );
    }

    xhr.send();

  }<|endoftext|>