<commit_before>// Copyright (c) 2011, Centrum Cyfrowe
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Centrum Cyfrowe nor the names of its contributors
//     may be used to endorse or promote products derived from this software
//     without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

var _tools = (function () {

//  P U B L I C   I N T E R F A C E
    var that = {};

    // TODO refactor this !!!!
    that.sort = function ( data, sett ) {
        var id = "idef";
        // TODO what is it for?!
        var new_table_data_object = {};
        _algorithms.prepare_sorting_setting( sett, id );
        _algorithms.sort( data, sett );
    };

    that.prepare_tools = function () {
        prepare_sorting_interface();
        prepare_filtering_interface();
        prepare_snapshot_interface();
    };

    that.get_full_parents = function ( id ) {
        var par_ids = [];

    };

    return that;

//  P R I V A T E   I N T E R F A C E

    function add_sort_key() {
        var i, key;
        var html = [];
        var schema = _store.basic_schema();
        var columns = schema
                        .filter( function ( e ) {
                            return e['processable'];
                        })
                        .map( function ( e ) {
                            return {
                                name: e['key'],
                                label: e['label']
                            };
                        });

        key = $('#sort-form > div').length;

        if( key === 2 ) {
            $('#add-sort-key' ).remove();
        }

        html.push( '
' );
        // columns select list
        html.push( '<select name="columns" class="key-', key, '">' );
        html.push( '<option value="null" class="key-', key, '" selected>' );
        html.push( 'Wybierz kolumnę</option>' );

        // TODO don't include already selected colmuns
        for( i = 0; i < columns.length; ++i ) {
            html.push( '<option value="', columns[i]['name'], '" class="key-', key, '">' );
            html.push( columns[i]['label'], '</option>' );
        }

        html.push( '</select>' );
        // ascending/descending radio buttons
        html.push( 'Rosnąco <input class="radio key-', key, '" type="radio" ' );
        html.push( 'name="key-', key, '-order" value="-1" checked />' );
        html.push( 'Malejąco <input class="radio key-', key, '" type="radio" ' );
        html.push( 'name="key-', key, '-order" value="1" />' );

        if( key === 0 ) {
            html.push( '
+
' );
            html.push( '<input type="submit" value="Sortuj" />' );
        }
        html.push( '
' );

        $('#sort-form').append( $( html.join('') ));

        if( key === 0 ) {
            $('#add-sort-key').click( function () {
                add_sort_key();
            });
        }
    }

    function add_filter_key() {
        var i, key;
        var html = [];
        var filter_part;
        var selected_column;
        var type;
        var schema = _store.basic_schema();
        var columns = schema
                        .filter( function ( e ) {
                            return e['processable'];
                        })
                        .map( function ( e ) {
                            return {
                                name: e['key'],
                                label: e['label']
                            };
                        });

        key = $('#filter-form > div').length;

        columns.unshift({
            name: 'null',
            label: 'Wybierz jedną z kolumn'
        });

        html.push( '<select id="filter-', key, '-columns"' );
        html.push( ' name="columns">');
        for ( i = 0; i < columns.length; i += 1 ) {
            html.push( '<option value="', columns[i]['key']);
            html.push( '" class="filter-', key, '" >"');
            html.push( columns[i]['label'], '</option>' );
        }
        html.push( '</select>' );

        html.push( '<select id="filter-', key, '-operations"' );
        html.push( ' name="null-operation">' );
        // not needed because column has not been selected yet

        html.push( '</select>' );

        html.push( '
' );

        if ( key === 0 ) {
            html.push( '
+
' );
            html.push( '<input type="submit" value="Filtruj" />' );
        }

        $('#filter-form').append( $( html.join('') );

        if( key === 0 ) {
            $('#add-filter-key').click( function () {
                add_filter_key();
            });
        }

        filter_part = $('filter-', key, '-columns');
        filter_part.change( function() {
            selected_column = $(this).val();
            // schema[0] is for empty column
            for ( i = 0; i < schema.length; i += 1 ) {
                if ( schema[i]['key'] === selected_column ) {
                    type = schema[i]['type'];
                    var filter_ops = $('#filter-', key, '-operations');
                    var filter_keys;

                    var new_select_type = type + '-operation';
                    filter_ops.attr('name', type);
                    if ( schema[i]['type'] === 'number' ) {
                        html = [ '<select>' ];
                        html.push( '<option value="null" class="filter-', key, '" selected>Wybierz operację</option>' );
                        html.push( '<option value="gt" class="filter-', key, '">></option>' );
                        html.push( '<option value="eq" class="filter-', key, '">=</option>' );
                        html.push( '<option value="lt" class="filter-', key, '"><</option>' );
                        html.push( '</select' );
                    } else {
                        html = [ '<select>' ];
                        html.push( '<option value="null" class="filter-', key, '" selected>Wybierz operację</option>' );
                        html.push( '<option value="cnt" class="filter-', key, '">></option>' );
                        html.push( '<option value="st" class="filter-', key, '">></option>' );
                        html.push( '<option value="ncnt" class="filter-', key, '">></option>' );
                        html.push( '<option value="nst" class="filter-', key, '">></option>' );
                        html.push( '</select>' );
                    }

                    html.push( '<input type="text" name="query" id="filter-', key, '-query" />' );
                }
            }
            //alert('Handler for .change() called.');
        });
    }

    /*for ( i = 0; i < schema.length; i += 1 ) {
            html.push( '<option value="', schema[i]['key']);
            html.push( '" class="filter-', m key, '" >"');
            html.push( schema[i]['label'], '</option>' );
        }*/
    // TODO don't include already selected colmuns
        /*for( i = 0; i < columns.length; ++i ) {
            html.push( '<option value="', columns[i]['name'], '" class="key-', key, '">' );
            html.push( columns[i]['label'], '</option>' );
        }*/

    function prepare_sorting_interface() {

        $('#sort-button')
            .click( function () {
                $('#filter-form').hide();
                $('#sort-form').html('').toggle();
                add_sort_key();
            });

        $('#sort-form')
            .submit( function () {

//                _utils.create_preloader('Sortowanie');

                var column, order;
                var settings = [];
                var i, len = $('#sort-form select').length;

                for( i = 0; i < len; i += 1 ) {
                    column = $( '.key-'+ i +' option:selected' ).val();
                    // TODO what does this condition mean?!
                    if( column === "null" ) {
                        if( i === 1 ) {
                            $(this).hide();
                            return false;
                        }
                        else {
                            break;
                        }
                    }
                    order = parseInt( $('.key-'+ i +':radio:checked').val() );

                    settings.push(
                        {
                            "pref": order,
                            "name": column
                        }
                    );
                }

                that.sort( _store.active_rows(), settings );

                _table.clean_table();
                _table.init_table();
                $(this).hide();

                // TODO why false?!
                return false;
            });

        var i;
        for( i = 1; i <= $('#sort-form select').length; ++i ) {
            if( i > 1 ) {
                $('#key-'+i).hide();
            }

            (function (i) {
                $('#sort-form select.key-'+i).change( function () {
                    if( $('.key-'+i+' option:selected').val() !== "null" ) {
                        $('#key-'+(i+1)).show();
                    }
                });
            })(i);
        }
    };

    function prepare_filtering_interface() {

        $('#filter-button')
            .click( function () {
                $('#filter-form').html('').toggle();
                $('#sort-form').hide();
                add_filter_key();
            });

        $('#filter-form')
            .submit( function () {

                var column, operation, query;
                var mask = [];
                var i, len = $('#filter-form select').length / 2;
                var tmp, type;
                var new_sheet;
                for( i = 1; i < len; ++i ) {
                    column = $('#filter-'+i+'-columns option:selected').val();
                    if( column === "null" ) {
                        if( i === 1 ) {
                            $(this).hide();
                            return false;
                        }
                        else {
                            break;
                        }
                    }

                    type = _store.active_columns().filter( function ( e ) {
                        return e['key'] === column;
                    })[0]['type'];

                    operation = $('#filter-'+i+'-operations option:selected').val();;
                    query = $('#filter-'+i+'-query').val();

                    tmp = parseInt( query, 10 );
                    // if the column if numeric - check if the query is so
                    if( type === 'number' ) {
                        // crazy way to check if tmp is a number!
                        if( !!tmp === true || tmp === 0 ) {
                            query = tmp;
                        }
                        else {
                            // characters for numeric column -- do nothing & quit
                            $(this).hide();
                            return false;
                        }
                    }

                    mask.push(
                        {
                           name: column,
                           pref: operation,
                           value: query
                        }
                    );
                }

                new_sheet = {};
                $.extend( true, new_sheet, _store.active_sheet() );
                new_sheet['rows'] = _algorithms.filter( new_sheet['rows'], mask );
                //new_sheet['filtered'] = true;

                _sheet.create_new_sheet( new_sheet, "Arkusz", true );

                _table.clean_table();
                _table.init_table( true );

                $(this).hide();

                return false;
           });
    };

    function prepare_snapshot_interface() {

        $('#save-snapshot')
            .click( function () {
                _sheet.create_new_sheet( _store.active_sheet(), "Arkusz" );
            });
    }

}) ();

<commit_msg>Typo fixed<commit_after>// Copyright (c) 2011, Centrum Cyfrowe
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Centrum Cyfrowe nor the names of its contributors
//     may be used to endorse or promote products derived from this software
//     without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

var _tools = (function () {

//  P U B L I C   I N T E R F A C E
    var that = {};

    // TODO refactor this !!!!
    that.sort = function ( data, sett ) {
        var id = "idef";
        // TODO what is it for?!
        var new_table_data_object = {};
        _algorithms.prepare_sorting_setting( sett, id );
        _algorithms.sort( data, sett );
    };

    that.prepare_tools = function () {
        prepare_sorting_interface();
        prepare_filtering_interface();
        prepare_snapshot_interface();
    };

    that.get_full_parents = function ( id ) {
        var par_ids = [];

    };

    return that;

//  P R I V A T E   I N T E R F A C E

    function add_sort_key() {
        var i, key;
        var html = [];
        var schema = _store.basic_schema();
        var columns = schema
                        .filter( function ( e ) {
                            return e['processable'];
                        })
                        .map( function ( e ) {
                            return {
                                name: e['key'],
                                label: e['label']
                            };
                        });

        key = $('#sort-form > div').length;

        if( key === 2 ) {
            $('#add-sort-key' ).remove();
        }

        html.push( '
' );
        // columns select list
        html.push( '<select name="columns" class="key-', key, '">' );
        html.push( '<option value="null" class="key-', key, '" selected>' );
        html.push( 'Wybierz kolumnę</option>' );

        // TODO don't include already selected colmuns
        for( i = 0; i < columns.length; ++i ) {
            html.push( '<option value="', columns[i]['name'], '" class="key-', key, '">' );
            html.push( columns[i]['label'], '</option>' );
        }

        html.push( '</select>' );
        // ascending/descending radio buttons
        html.push( 'Rosnąco <input class="radio key-', key, '" type="radio" ' );
        html.push( 'name="key-', key, '-order" value="-1" checked />' );
        html.push( 'Malejąco <input class="radio key-', key, '" type="radio" ' );
        html.push( 'name="key-', key, '-order" value="1" />' );

        if( key === 0 ) {
            html.push( '
+
' );
            html.push( '<input type="submit" value="Sortuj" />' );
        }
        html.push( '
' );

        $('#sort-form').append( $( html.join('') ));

        if( key === 0 ) {
            $('#add-sort-key').click( function () {
                add_sort_key();
            });
        }
    }

    function add_filter_key() {
        var i, key;
        var html = [];
        var filter_part;
        var selected_column;
        var type;
        var schema = _store.basic_schema();
        var columns = schema
                        .filter( function ( e ) {
                            return e['processable'];
                        })
                        .map( function ( e ) {
                            return {
                                name: e['key'],
                                label: e['label']
                            };
                        });

        key = $('#filter-form > div').length;

        columns.unshift({
            name: 'null',
            label: 'Wybierz jedną z kolumn'
        });

        html.push( '<select id="filter-', key, '-columns"' );
        html.push( ' name="columns">');
        for ( i = 0; i < columns.length; i += 1 ) {
            html.push( '<option value="', columns[i]['key']);
            html.push( '" class="filter-', key, '" >"');
            html.push( columns[i]['label'], '</option>' );
        }
        html.push( '</select>' );

        html.push( '<select id="filter-', key, '-operations"' );
        html.push( ' name="null-operation">' );
        // not needed because column has not been selected yet

        html.push( '</select>' );

        html.push( '
' );

        if ( key === 0 ) {
            html.push( '
+
' );
            html.push( '<input type="submit" value="Filtruj" />' );
        }

        $('#filter-form').append( $( html.join('') ) );

        if( key === 0 ) {
            $('#add-filter-key').click( function () {
                add_filter_key();
            });
        }

        filter_part = $('filter-', key, '-columns');
        filter_part.change( function() {
            selected_column = $(this).val();
            // schema[0] is for empty column
            for ( i = 0; i < schema.length; i += 1 ) {
                if ( schema[i]['key'] === selected_column ) {
                    type = schema[i]['type'];
                    var filter_ops = $('#filter-', key, '-operations');
                    var filter_keys;

                    var new_select_type = type + '-operation';
                    filter_ops.attr('name', type);
                    if ( schema[i]['type'] === 'number' ) {
                        html = [ '<select>' ];
                        html.push( '<option value="null" class="filter-', key, '" selected>Wybierz operację</option>' );
                        html.push( '<option value="gt" class="filter-', key, '">></option>' );
                        html.push( '<option value="eq" class="filter-', key, '">=</option>' );
                        html.push( '<option value="lt" class="filter-', key, '"><</option>' );
                        html.push( '</select' );
                    } else {
                        html = [ '<select>' ];
                        html.push( '<option value="null" class="filter-', key, '" selected>Wybierz operację</option>' );
                        html.push( '<option value="cnt" class="filter-', key, '">></option>' );
                        html.push( '<option value="st" class="filter-', key, '">></option>' );
                        html.push( '<option value="ncnt" class="filter-', key, '">></option>' );
                        html.push( '<option value="nst" class="filter-', key, '">></option>' );
                        html.push( '</select>' );
                    }

                    html.push( '<input type="text" name="query" id="filter-', key, '-query" />' );
                }
            }
            //alert('Handler for .change() called.');
        });
    }

    /*for ( i = 0; i < schema.length; i += 1 ) {
            html.push( '<option value="', schema[i]['key']);
            html.push( '" class="filter-', m key, '" >"');
            html.push( schema[i]['label'], '</option>' );
        }*/
    // TODO don't include already selected colmuns
        /*for( i = 0; i < columns.length; ++i ) {
            html.push( '<option value="', columns[i]['name'], '" class="key-', key, '">' );
            html.push( columns[i]['label'], '</option>' );
        }*/

    function prepare_sorting_interface() {

        $('#sort-button')
            .click( function () {
                $('#filter-form').hide();
                $('#sort-form').html('').toggle();
                add_sort_key();
            });

        $('#sort-form')
            .submit( function () {

//                _utils.create_preloader('Sortowanie');

                var column, order;
                var settings = [];
                var i, len = $('#sort-form select').length;

                for( i = 0; i < len; i += 1 ) {
                    column = $( '.key-'+ i +' option:selected' ).val();
                    // TODO what does this condition mean?!
                    if( column === "null" ) {
                        if( i === 1 ) {
                            $(this).hide();
                            return false;
                        }
                        else {
                            break;
                        }
                    }
                    order = parseInt( $('.key-'+ i +':radio:checked').val() );

                    settings.push(
                        {
                            "pref": order,
                            "name": column
                        }
                    );
                }

                that.sort( _store.active_rows(), settings );

                _table.clean_table();
                _table.init_table();
                $(this).hide();

                // TODO why false?!
                return false;
            });

        var i;
        for( i = 1; i <= $('#sort-form select').length; ++i ) {
            if( i > 1 ) {
                $('#key-'+i).hide();
            }

            (function (i) {
                $('#sort-form select.key-'+i).change( function () {
                    if( $('.key-'+i+' option:selected').val() !== "null" ) {
                        $('#key-'+(i+1)).show();
                    }
                });
            })(i);
        }
    };

    function prepare_filtering_interface() {

        $('#filter-button')
            .click( function () {
                $('#filter-form').html('').toggle();
                $('#sort-form').hide();
                add_filter_key();
            });

        $('#filter-form')
            .submit( function () {

                var column, operation, query;
                var mask = [];
                var i, len = $('#filter-form select').length / 2;
                var tmp, type;
                var new_sheet;
                for( i = 1; i < len; ++i ) {
                    column = $('#filter-'+i+'-columns option:selected').val();
                    if( column === "null" ) {
                        if( i === 1 ) {
                            $(this).hide();
                            return false;
                        }
                        else {
                            break;
                        }
                    }

                    type = _store.active_columns().filter( function ( e ) {
                        return e['key'] === column;
                    })[0]['type'];

                    operation = $('#filter-'+i+'-operations option:selected').val();;
                    query = $('#filter-'+i+'-query').val();

                    tmp = parseInt( query, 10 );
                    // if the column if numeric - check if the query is so
                    if( type === 'number' ) {
                        // crazy way to check if tmp is a number!
                        if( !!tmp === true || tmp === 0 ) {
                            query = tmp;
                        }
                        else {
                            // characters for numeric column -- do nothing & quit
                            $(this).hide();
                            return false;
                        }
                    }

                    mask.push(
                        {
                           name: column,
                           pref: operation,
                           value: query
                        }
                    );
                }

                new_sheet = {};
                $.extend( true, new_sheet, _store.active_sheet() );
                new_sheet['rows'] = _algorithms.filter( new_sheet['rows'], mask );
                //new_sheet['filtered'] = true;

                _sheet.create_new_sheet( new_sheet, "Arkusz", true );

                _table.clean_table();
                _table.init_table( true );

                $(this).hide();

                return false;
           });
    };

    function prepare_snapshot_interface() {

        $('#save-snapshot')
            .click( function () {
                _sheet.create_new_sheet( _store.active_sheet(), "Arkusz" );
            });
    }

}) ();

<|endoftext|>


<commit_before>const ADD_STEP_TYPE = 'scenarioEditor/ADD_STEP_TYPE';
const DELETE_STEP = 'scenarioEditor/DELETE_STEP';
const DUPLICATE_STEP = 'scenarioEditor/DUPLICATE_STEP';
const MOVE_STEP = 'scenarioEditor/MOVE_STEP';
const UPDATE_STEP = 'scenarioEditor/UPDATE_STEP';
const SELECT_STEP = 'scenarioEditor/SELECT_STEP';
const UPDATE_TITLE = 'scenarioEditor/UPDATE_TITLE';
const UPDATE_DESCRIPTION = 'scenarioEditor/UPDATE_DESCRIPTION';
const SUBMIT = 'scenarioEditor/SUBMIT';
const SUBMIT_SUCCESS = 'scenarioEditor/SUBMIT_SUCCESS';
const SUBMIT_FAIL = 'scenarioEditor/SUBMIT_FAIL';

import clone from 'lodash';
import cloneDeep from 'lodash';

const initialState = {
  selectedStep: 0,
  title: '',
  description: '',
  steps: [
    {// 0
      type: 'dialog',
      speaker: '',
      goTo: 0,
      text: ''
    }
  ],
  submitting: false
};

export default function reducer(state = initialState, action = {}) {
  let newState;
  switch (action.type) {
    case ADD_STEP_TYPE:
      newState = clone(state).__wrapped__;
      newState.selectedStep = action.index + 1;
      if (action.stepType === 'dialog') {
        if (action.index === state.steps.length - 1) {
          newState.steps.splice(action.index + 1, 0, {
            type: 'dialog',
            speaker: '',
            goTo: 0,
            text: ''
          });
        } else {
          newState.steps.splice(action.index + 1, 0, {
            type: 'dialog',
            speaker: '',
            goTo: action.index,
            text: ''
          });
        }
      }
      if (action.stepType === 'choice') {
        newState.steps.splice(action.index + 1, 0, {
          type: 'choice',
          choices: [{
            text: '',
            canImprove: '',
            doneWell: ''
          }]
        });
      }
      newState.steps.forEach((step, index) => {
        if (step.type === 'dialog') {
          if (index === state.steps.length - 2 && action.index === state.steps.length - 2) {
            step.goTo = state.steps.length - 1;
          } else if (index === action.index + 1 && action.index !== state.steps.length - 2) {
            step.goTo += 2;
          } else if (step.goTo > action.index) {
            step.goTo++;
          }
        } else if (step.type === 'choice') {
          step.choices.forEach((choice) => {
            if (choice.goTo > action.index) {
              choice.goTo++;
            }
          });
        }
      });
      return {
        ...state,
        selectedStep: newState.selectedStep,
        steps: newState.steps
      };
    case DELETE_STEP:
      newState = clone(state).__wrapped__;
      if (action.index === state.steps.length - 1) {
        newState.steps[action.index - 1].goTo = 0;
      }
      if (action.index === state.steps.length - 1) {
        newState.selectedStep = action.index - 1;
      }
      newState.steps.splice(action.index, 1);
      newState.steps.forEach((step) => {
        if (step.type === 'dialog' && step.goTo > action.index) {
          step.goTo--;
        } else if (step.type === 'choice') {
          step.choices.forEach((choice) => {
            if (choice.goTo > action.index) {
              choice.goTo--;
            }
          });
        }
      });
      return {
        ...state,
        steps: newState.steps
      };
    case DUPLICATE_STEP:
      newState = cloneDeep(state).__wrapped__;
      console.log(cloneDeep(state));
      newState.selectedStep = action.index + 1;
      newState.steps.splice(action.index + 1, 0, {
        ...newState.steps[action.index]
      });
      newState.steps.forEach((step, index) => {
        if (step.type === 'dialog') {
          if (step.goTo > action.index) {
            step.goTo++;
          } else if (index === action.index && step.goTo === 0) {
            step.goTo++;
          }
        } else if (step.type === 'choice') {
          step.choices.forEach((choice) => {
            if (choice.goTo > action.index) {
              choice.goTo++;
            }
          });
        }
      });
      return {
        ...state,
        selectedStep: newState.selectedStep,
        steps: newState.steps
      };
    case MOVE_STEP:
      newState = clone(state).__wrapped__;
      if (action.upDown === 'up') {
        newState.selectedStep = action.index - 1;
        const temp = newState.steps[action.index];
        newState.steps[action.index] = newState.steps[action.index - 1];
        newState.steps[action.index - 1] = temp;
        newState.steps.forEach((step) => {
          if (step.type === 'dialog' && step.goTo === action.index) {
            step.goTo--;
          } else if (step.type === 'dialog' && step.goTo === action.index - 1) {
            step.goTo++;
          } else if (step.type === 'choice') {
            step.choices.forEach((choice) => {
              if (choice.goTo === action.index) {
                choice.goTo--;
              } else if (choice.goTo === action.index - 1) {
                choice.goTo++;
              }
            });
          }
        });
      }
      if (action.upDown === 'down') {
        const temp = newState.steps[action.index];
        newState.selectedStep = action.index + 1;
        newState.steps[action.index] = newState.steps[action.index + 1];
        newState.steps[action.index + 1] = temp;
        newState.steps.forEach((step) => {
          if (step.type === 'dialog' && step.goTo === action.index) {
            step.goTo++;
          } else if (step.type === 'dialog' && step.goTo === action.index + 1) {
            step.goTo--;
          } else if (step.type === 'choice') {
            step.choices.forEach((choice) => {
              if (choice.goTo === action.index) {
                choice.goTo++;
              } else if (choice.goTo === action.index + 1) {
                choice.goTo--;
              }
            });
          }
        });
      }
      return {
        ...state,
        selectedStep: newState.selectedStep,
        steps: newState.steps
      };
    case UPDATE_STEP:
      newState = clone(state).__wrapped__;
      newState.steps[action.index] = action.obj;
      return {
        ...state,
        steps: newState.steps
      };
    case SELECT_STEP:
      return {
        ...state,
        selectedStep: action.index
      };
    case UPDATE_TITLE:
      return {
        ...state,
        title: action.text
      };
    case UPDATE_DESCRIPTION:
      return {
        ...state,
        description: action.text
      };
    case SUBMIT:
      return {
        ...state,
        submitting: true
      };
    case SUBMIT_SUCCESS:
      return {
        ...state,
        submitting: false
      };
    case SUBMIT_FAIL:
      return {
        ...state,
        submitting: false
      };
    default:
      return state;
  }
}

export function add(index, stepType) {
  return {
    type: ADD_STEP_TYPE,
    index,
    stepType
  };
}

export function remove(index) {
  return {
    type: DELETE_STEP,
    index
  };
}

export function duplicate(index) {
  return {
    type: DUPLICATE_STEP,
    index
  };
}

export function move(index, upDown) {
  return {
    type: MOVE_STEP,
    index,
    upDown
  };
}

export function updateStep(index, obj) {
  return {
    type: UPDATE_STEP,
    index,
    obj
  };
}

export function selectStep(index) {
  return {
    type: SELECT_STEP,
    index
  };
}

export function updateTitle(text) {
  return {
    type: UPDATE_TITLE,
    text
  };
}

export function updateDescription(text) {
  return {
    type: UPDATE_DESCRIPTION,
    text
  };
}

export function submitScenario(json) {
  console.log(json);
  return {
    types: [SUBMIT, SUBMIT_SUCCESS, SUBMIT_FAIL],
    promise: (client) => client.post('/scenario', {
      data: json
    })
  };
}
<commit_msg>Fixed duplication bug<commit_after>const ADD_STEP_TYPE = 'scenarioEditor/ADD_STEP_TYPE';
const DELETE_STEP = 'scenarioEditor/DELETE_STEP';
const DUPLICATE_STEP = 'scenarioEditor/DUPLICATE_STEP';
const MOVE_STEP = 'scenarioEditor/MOVE_STEP';
const UPDATE_STEP = 'scenarioEditor/UPDATE_STEP';
const SELECT_STEP = 'scenarioEditor/SELECT_STEP';
const UPDATE_TITLE = 'scenarioEditor/UPDATE_TITLE';
const UPDATE_DESCRIPTION = 'scenarioEditor/UPDATE_DESCRIPTION';
const SUBMIT = 'scenarioEditor/SUBMIT';
const SUBMIT_SUCCESS = 'scenarioEditor/SUBMIT_SUCCESS';
const SUBMIT_FAIL = 'scenarioEditor/SUBMIT_FAIL';

import {clone} from 'lodash';
import {cloneDeep} from 'lodash';

const initialState = {
  selectedStep: 0,
  title: '',
  description: '',
  steps: [
    {// 0
      type: 'dialog',
      speaker: '',
      goTo: 0,
      text: ''
    }
  ],
  submitting: false
};

export default function reducer(state = initialState, action = {}) {
  let newState;
  switch (action.type) {
    case ADD_STEP_TYPE:
      newState = clone(state);
      newState.selectedStep = action.index + 1;
      if (action.stepType === 'dialog') {
        if (action.index === state.steps.length - 1) {
          newState.steps.splice(action.index + 1, 0, {
            type: 'dialog',
            speaker: '',
            goTo: 0,
            text: ''
          });
        } else {
          newState.steps.splice(action.index + 1, 0, {
            type: 'dialog',
            speaker: '',
            goTo: action.index,
            text: ''
          });
        }
      }
      if (action.stepType === 'choice') {
        newState.steps.splice(action.index + 1, 0, {
          type: 'choice',
          choices: [{
            text: '',
            canImprove: '',
            doneWell: ''
          }]
        });
      }
      newState.steps.forEach((step, index) => {
        if (step.type === 'dialog') {
          if (index === state.steps.length - 2 && action.index === state.steps.length - 2) {
            step.goTo = state.steps.length - 1;
          } else if (index === action.index + 1 && action.index !== state.steps.length - 2) {
            step.goTo += 2;
          } else if (step.goTo > action.index) {
            step.goTo++;
          }
        } else if (step.type === 'choice') {
          step.choices.forEach((choice) => {
            if (choice.goTo > action.index) {
              choice.goTo++;
            }
          });
        }
      });
      return {
        ...state,
        selectedStep: newState.selectedStep,
        steps: newState.steps
      };
    case DELETE_STEP:
      newState = clone(state);
      if (action.index === state.steps.length - 1) {
        newState.steps[action.index - 1].goTo = 0;
      }
      if (action.index === state.steps.length - 1) {
        newState.selectedStep = action.index - 1;
      }
      newState.steps.splice(action.index, 1);
      newState.steps.forEach((step) => {
        if (step.type === 'dialog' && step.goTo > action.index) {
          step.goTo--;
        } else if (step.type === 'choice') {
          step.choices.forEach((choice) => {
            if (choice.goTo > action.index) {
              choice.goTo--;
            }
          });
        }
      });
      return {
        ...state,
        steps: newState.steps
      };
    case DUPLICATE_STEP:
      newState = cloneDeep(state);
      console.log(cloneDeep(state));
      newState.selectedStep = action.index + 1;
      newState.steps.splice(action.index + 1, 0, cloneDeep(newState.steps[action.index]));
      console.log(newState.steps);
      console.log(newState.steps[1] === newState.steps[2]);
      newState.steps.forEach((step, index) => {
        if (step.type === 'dialog') {
          if (step.goTo > action.index) {
            step.goTo++;
          } else if (index === action.index && step.goTo === 0) {
            step.goTo++;
          }
        } else if (step.type === 'choice') {
          step.choices.forEach((choice) => {
            if (choice.goTo > action.index) {
              choice.goTo++;
            }
          });
        }
      });
      return {
        ...state,
        selectedStep: newState.selectedStep,
        steps: newState.steps
      };
    case MOVE_STEP:
      newState = clone(state);
      if (action.upDown === 'up') {
        newState.selectedStep = action.index - 1;
        const temp = newState.steps[action.index];
        newState.steps[action.index] = newState.steps[action.index - 1];
        newState.steps[action.index - 1] = temp;
        newState.steps.forEach((step) => {
          if (step.type === 'dialog' && step.goTo === action.index) {
            step.goTo--;
          } else if (step.type === 'dialog' && step.goTo === action.index - 1) {
            step.goTo++;
          } else if (step.type === 'choice') {
            step.choices.forEach((choice) => {
              if (choice.goTo === action.index) {
                choice.goTo--;
              } else if (choice.goTo === action.index - 1) {
                choice.goTo++;
              }
            });
          }
        });
      }
      if (action.upDown === 'down') {
        const temp = newState.steps[action.index];
        newState.selectedStep = action.index + 1;
        newState.steps[action.index] = newState.steps[action.index + 1];
        newState.steps[action.index + 1] = temp;
        newState.steps.forEach((step) => {
          if (step.type === 'dialog' && step.goTo === action.index) {
            step.goTo++;
          } else if (step.type === 'dialog' && step.goTo === action.index + 1) {
            step.goTo--;
          } else if (step.type === 'choice') {
            step.choices.forEach((choice) => {
              if (choice.goTo === action.index) {
                choice.goTo++;
              } else if (choice.goTo === action.index + 1) {
                choice.goTo--;
              }
            });
          }
        });
      }
      return {
        ...state,
        selectedStep: newState.selectedStep,
        steps: newState.steps
      };
    case UPDATE_STEP:
      newState = clone(state);
      newState.steps[action.index] = action.obj;
      return {
        ...state,
        steps: newState.steps
      };
    case SELECT_STEP:
      return {
        ...state,
        selectedStep: action.index
      };
    case UPDATE_TITLE:
      return {
        ...state,
        title: action.text
      };
    case UPDATE_DESCRIPTION:
      return {
        ...state,
        description: action.text
      };
    case SUBMIT:
      return {
        ...state,
        submitting: true
      };
    case SUBMIT_SUCCESS:
      return {
        ...state,
        submitting: false
      };
    case SUBMIT_FAIL:
      return {
        ...state,
        submitting: false
      };
    default:
      return state;
  }
}

export function add(index, stepType) {
  return {
    type: ADD_STEP_TYPE,
    index,
    stepType
  };
}

export function remove(index) {
  return {
    type: DELETE_STEP,
    index
  };
}

export function duplicate(index) {
  return {
    type: DUPLICATE_STEP,
    index
  };
}

export function move(index, upDown) {
  return {
    type: MOVE_STEP,
    index,
    upDown
  };
}

export function updateStep(index, obj) {
  return {
    type: UPDATE_STEP,
    index,
    obj
  };
}

export function selectStep(index) {
  return {
    type: SELECT_STEP,
    index
  };
}

export function updateTitle(text) {
  return {
    type: UPDATE_TITLE,
    text
  };
}

export function updateDescription(text) {
  return {
    type: UPDATE_DESCRIPTION,
    text
  };
}

export function submitScenario(json) {
  console.log(json);
  return {
    types: [SUBMIT, SUBMIT_SUCCESS, SUBMIT_FAIL],
    promise: (client) => client.post('/scenario', {
      data: json
    })
  };
}
<|endoftext|>


<commit_before>THREE.WebGLProgram = ( function () {

	var programIdCount = 0;

	function generateDefines( defines ) {

		var chunks = [];

		for ( var name in defines ) {

			var value = defines[ name ];

			if ( value === false ) continue;

			chunks.push( '#define ' + name + ' ' + value );

		}

		return chunks.join( '\n' );

	}

	function fetchUniformLocations( gl, program, identifiers ) {

		var uniforms = {};

		var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS );

		for ( var i = 0; i < n; i ++ ) {

			var info = gl.getActiveUniform( program , i );
			var name = info.name;
			var location = gl.getUniformLocation( program, name );

			//console.log("THREE.WebGLProgram: ACTIVE UNIFORM:", name);

			var suffixPos = name.lastIndexOf( '[0]' );
			if ( suffixPos !== -1 && suffixPos === name.length - 3 ) {

				uniforms[ name.substr( 0, suffixPos ) ] = location;

			}

			uniforms[ name ] = location;

		}

		return uniforms;

	}

	function fetchAttributeLocations( gl, program, identifiers ) {

		var attributes = {};

		var n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES );

		for ( var i = 0; i < n; i ++ ) {

			var info = gl.getActiveAttrib( program , i );
			var name = info.name;

			//console.log("THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name);

			attributes[ name ] = gl.getAttribLocation( program, name );

		}

		return attributes;

	}

	function filterEmptyLine( string ) {

		return string !== '';

	}

	return function ( renderer, code, material, parameters ) {

		var gl = renderer.context;

		var defines = material.defines;
		var uniforms = material.__webglShader.uniforms;
		var attributes = material.attributes;

		var vertexShader = material.__webglShader.vertexShader;
		var fragmentShader = material.__webglShader.fragmentShader;

		var index0AttributeName = material.index0AttributeName;

		/*
		if ( index0AttributeName === undefined && parameters.morphTargets === true ) {

			// programs with morphTargets displace position out of attribute 0

			index0AttributeName = 'position';

		}
		*/

		var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC';

		if ( parameters.shadowMapType === THREE.PCFShadowMap ) {

			shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF';

		} else if ( parameters.shadowMapType === THREE.PCFSoftShadowMap ) {

			shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT';

		}

		var envMapTypeDefine = 'ENVMAP_TYPE_CUBE';
		var envMapModeDefine = 'ENVMAP_MODE_REFLECTION';
		var envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';

		if ( parameters.envMap ) {

			switch ( material.envMap.mapping ) {

				case THREE.CubeReflectionMapping:
				case THREE.CubeRefractionMapping:
					envMapTypeDefine = 'ENVMAP_TYPE_CUBE';
					break;

				case THREE.EquirectangularReflectionMapping:
				case THREE.EquirectangularRefractionMapping:
					envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC';
					break;

				case THREE.SphericalReflectionMapping:
					envMapTypeDefine = 'ENVMAP_TYPE_SPHERE';
					break;

			}

			switch ( material.envMap.mapping ) {

				case THREE.CubeRefractionMapping:
				case THREE.EquirectangularRefractionMapping:
					envMapModeDefine = 'ENVMAP_MODE_REFRACTION';
					break;

			}

			switch ( material.combine ) {

				case THREE.MultiplyOperation:
					envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';
					break;

				case THREE.MixOperation:
					envMapBlendingDefine = 'ENVMAP_BLENDING_MIX';
					break;

				case THREE.AddOperation:
					envMapBlendingDefine = 'ENVMAP_BLENDING_ADD';
					break;

			}

		}

		var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0;

		// console.log( 'building new program ' );

		//

		var customDefines = generateDefines( defines );

		//

		var program = gl.createProgram();

		var prefixVertex, prefixFragment;

		if ( material instanceof THREE.RawShaderMaterial ) {

			prefixVertex = '';
			prefixFragment = '';

		} else {

			prefixVertex = [

				'precision ' + parameters.precision + ' float;',
				'precision ' + parameters.precision + ' int;',

				'#define SHADER_NAME ' + material.__webglShader.name,

				customDefines,

				parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '',

				renderer.gammaInput ? '#define GAMMA_INPUT' : '',
				renderer.gammaOutput ? '#define GAMMA_OUTPUT' : '',
				'#define GAMMA_FACTOR ' + gammaFactorDefine,

				'#define MAX_DIR_LIGHTS ' + parameters.maxDirLights,
				'#define MAX_POINT_LIGHTS ' + parameters.maxPointLights,
				'#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights,
				'#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights,

				'#define MAX_SHADOWS ' + parameters.maxShadows,

				'#define MAX_BONES ' + parameters.maxBones,

				parameters.map ? '#define USE_MAP' : '',
				parameters.envMap ? '#define USE_ENVMAP' : '',
				parameters.envMap ? '#define ' + envMapModeDefine : '',
				parameters.lightMap ? '#define USE_LIGHTMAP' : '',
				parameters.aoMap ? '#define USE_AOMAP' : '',
				parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',
				parameters.bumpMap ? '#define USE_BUMPMAP' : '',
				parameters.normalMap ? '#define USE_NORMALMAP' : '',
				parameters.specularMap ? '#define USE_SPECULARMAP' : '',
				parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
				parameters.vertexColors ? '#define USE_COLOR' : '',

				parameters.flatShading ? '#define FLAT_SHADED': '',

				parameters.skinning ? '#define USE_SKINNING' : '',
				parameters.useVertexTexture ? '#define BONE_TEXTURE' : '',

				parameters.morphTargets ? '#define USE_MORPHTARGETS' : '',
				parameters.morphNormals ? '#define USE_MORPHNORMALS' : '',
				parameters.doubleSided ? '#define DOUBLE_SIDED' : '',
				parameters.flipSided ? '#define FLIP_SIDED' : '',

				parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
				parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',
				parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '',
				parameters.shadowMapCascade ? '#define SHADOWMAP_CASCADE' : '',

				parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '',

				parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',
				parameters.logarithmicDepthBuffer && renderer.extensions.get('EXT_frag_depth') ? '#define USE_LOGDEPTHBUF_EXT' : '',

				'uniform mat4 modelMatrix;',
				'uniform mat4 modelViewMatrix;',
				'uniform mat4 projectionMatrix;',
				'uniform mat4 viewMatrix;',
				'uniform mat3 normalMatrix;',
				'uniform vec3 cameraPosition;',

				'attribute vec3 position;',
				'attribute vec3 normal;',
				'attribute vec2 uv;',

				'#ifdef USE_COLOR',

				'	attribute vec3 color;',

				'#endif',

				'#ifdef USE_MORPHTARGETS',

				'	attribute vec3 morphTarget0;',
				'	attribute vec3 morphTarget1;',
				'	attribute vec3 morphTarget2;',
				'	attribute vec3 morphTarget3;',

				'	#ifdef USE_MORPHNORMALS',

				'		attribute vec3 morphNormal0;',
				'		attribute vec3 morphNormal1;',
				'		attribute vec3 morphNormal2;',
				'		attribute vec3 morphNormal3;',

				'	#else',

				'		attribute vec3 morphTarget4;',
				'		attribute vec3 morphTarget5;',
				'		attribute vec3 morphTarget6;',
				'		attribute vec3 morphTarget7;',

				'	#endif',

				'#endif',

				'#ifdef USE_SKINNING',

				'	attribute vec4 skinIndex;',
				'	attribute vec4 skinWeight;',

				'#endif',

				'\n'

			].filter( filterEmptyLine ).join( '\n' );

			prefixFragment = [

				( parameters.bumpMap || parameters.normalMap || parameters.flatShading || material.derivatives ) ? '#extension GL_OES_standard_derivatives : enable' : '',

				'precision ' + parameters.precision + ' float;',
				'precision ' + parameters.precision + ' int;',

				'#define SHADER_NAME ' + material.__webglShader.name,

				customDefines,

				'#define MAX_DIR_LIGHTS ' + parameters.maxDirLights,
				'#define MAX_POINT_LIGHTS ' + parameters.maxPointLights,
				'#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights,
				'#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights,

				'#define MAX_SHADOWS ' + parameters.maxShadows,

				parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest : '',

				renderer.gammaInput ? '#define GAMMA_INPUT' : '',
				renderer.gammaOutput ? '#define GAMMA_OUTPUT' : '',
				'#define GAMMA_FACTOR ' + gammaFactorDefine,

				( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '',
				( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '',

				parameters.map ? '#define USE_MAP' : '',
				parameters.envMap ? '#define USE_ENVMAP' : '',
				parameters.envMap ? '#define ' + envMapTypeDefine : '',
				parameters.envMap ? '#define ' + envMapModeDefine : '',
				parameters.envMap ? '#define ' + envMapBlendingDefine : '',
				parameters.lightMap ? '#define USE_LIGHTMAP' : '',
				parameters.aoMap ? '#define USE_AOMAP' : '',
				parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',
				parameters.bumpMap ? '#define USE_BUMPMAP' : '',
				parameters.normalMap ? '#define USE_NORMALMAP' : '',
				parameters.specularMap ? '#define USE_SPECULARMAP' : '',
				parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
				parameters.vertexColors ? '#define USE_COLOR' : '',

				parameters.flatShading ? '#define FLAT_SHADED': '',

				parameters.metal ? '#define METAL' : '',
				parameters.doubleSided ? '#define DOUBLE_SIDED' : '',
				parameters.flipSided ? '#define FLIP_SIDED' : '',

				parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
				parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',
				parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '',
				parameters.shadowMapCascade ? '#define SHADOWMAP_CASCADE' : '',

				parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',
				parameters.logarithmicDepthBuffer && renderer.extensions.get('EXT_frag_depth') ? '#define USE_LOGDEPTHBUF_EXT' : '',

				'uniform mat4 viewMatrix;',
				'uniform vec3 cameraPosition;',

				'\n'

			].filter( filterEmptyLine ).join( '\n' );

		}

		var vertexGlsl = prefixVertex + vertexShader;
		var fragmentGlsl = prefixFragment + fragmentShader;

		var glVertexShader = new THREE.WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl );
		var glFragmentShader = new THREE.WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl );

		gl.attachShader( program, glVertexShader );
		gl.attachShader( program, glFragmentShader );

		if ( index0AttributeName !== undefined ) {

			// Force a particular attribute to index 0.
			// because potentially expensive emulation is done by browser if attribute 0 is disabled.
			// And, color, for example is often automatically bound to index 0 so disabling it

			gl.bindAttribLocation( program, 0, index0AttributeName );

		}

		gl.linkProgram( program );

		var programLogInfo = gl.getProgramInfoLog( program );
		var vertexErrorLogInfo = gl.getShaderInfoLog( glVertexShader );
		var fragmentErrorLogInfo = gl.getShaderInfoLog( glFragmentShader );

		if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) {

			console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), 'gl.VALIDATE_STATUS', gl.getProgramParameter( program, gl.VALIDATE_STATUS ), 'gl.getProgramInfoLog', programLogInfo, vertexErrorLogInfo, fragmentErrorLogInfo );

		}

		else if ( programLogInfo !== '' ) {

			console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLogInfo );

		}

		// clean up

		gl.deleteShader( glVertexShader );
		gl.deleteShader( glFragmentShader );

		// set up caching for uniform locations

		var getUniforms = function() { return this._cachedUniforms; };

		this.getUniforms = function() {

			// fetch, cache, and next time just use a dumb accessor
			var uniforms = fetchUniformLocations( gl, program );
			this._cachedUniforms = uniforms;
			this.getUniforms = getUniforms;
			return uniforms;

		};

		// set up caching for attribute locations

		var getAttributes = function() { return this._cachedAttributes; };

		this.getAttributes = function() {

			var attributes = fetchAttributeLocations( gl, program );
			this._cachedAttributes = attributes;
			this.getAttributes = getAttributes;
			return attributes;

		};

		// DEPRECATED

		Object.defineProperties( this, {

			uniforms: {
				get: function() {

					console.warn( 'THREE.WebGLProgram: .uniforms is now .getUniforms().' );
					return this.getUniforms();

				}
			},

			attributes: {
				get: function() {

					console.warn( 'THREE.WebGLProgram: .attributes is now .getAttributes().' );
					return this.getAttributes();

				}
			}

		});

		//

		this.id = programIdCount ++;
		this.code = code;
		this.usedTimes = 1;
		this.program = program;
		this.vertexShader = glVertexShader;
		this.fragmentShader = glFragmentShader;

		return this;

	};

} )();
<commit_msg>Removed new operator calling THREE.WebGLShader (it's a function).<commit_after>THREE.WebGLProgram = ( function () {

	var programIdCount = 0;

	function generateDefines( defines ) {

		var chunks = [];

		for ( var name in defines ) {

			var value = defines[ name ];

			if ( value === false ) continue;

			chunks.push( '#define ' + name + ' ' + value );

		}

		return chunks.join( '\n' );

	}

	function fetchUniformLocations( gl, program, identifiers ) {

		var uniforms = {};

		var n = gl.getProgramParameter( program, gl.ACTIVE_UNIFORMS );

		for ( var i = 0; i < n; i ++ ) {

			var info = gl.getActiveUniform( program , i );
			var name = info.name;
			var location = gl.getUniformLocation( program, name );

			//console.log("THREE.WebGLProgram: ACTIVE UNIFORM:", name);

			var suffixPos = name.lastIndexOf( '[0]' );
			if ( suffixPos !== -1 && suffixPos === name.length - 3 ) {

				uniforms[ name.substr( 0, suffixPos ) ] = location;

			}

			uniforms[ name ] = location;

		}

		return uniforms;

	}

	function fetchAttributeLocations( gl, program, identifiers ) {

		var attributes = {};

		var n = gl.getProgramParameter( program, gl.ACTIVE_ATTRIBUTES );

		for ( var i = 0; i < n; i ++ ) {

			var info = gl.getActiveAttrib( program , i );
			var name = info.name;

			//console.log("THREE.WebGLProgram: ACTIVE VERTEX ATTRIBUTE:", name);

			attributes[ name ] = gl.getAttribLocation( program, name );

		}

		return attributes;

	}

	function filterEmptyLine( string ) {

		return string !== '';

	}

	return function ( renderer, code, material, parameters ) {

		var gl = renderer.context;

		var defines = material.defines;
		var uniforms = material.__webglShader.uniforms;
		var attributes = material.attributes;

		var vertexShader = material.__webglShader.vertexShader;
		var fragmentShader = material.__webglShader.fragmentShader;

		var index0AttributeName = material.index0AttributeName;

		/*
		if ( index0AttributeName === undefined && parameters.morphTargets === true ) {

			// programs with morphTargets displace position out of attribute 0

			index0AttributeName = 'position';

		}
		*/

		var shadowMapTypeDefine = 'SHADOWMAP_TYPE_BASIC';

		if ( parameters.shadowMapType === THREE.PCFShadowMap ) {

			shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF';

		} else if ( parameters.shadowMapType === THREE.PCFSoftShadowMap ) {

			shadowMapTypeDefine = 'SHADOWMAP_TYPE_PCF_SOFT';

		}

		var envMapTypeDefine = 'ENVMAP_TYPE_CUBE';
		var envMapModeDefine = 'ENVMAP_MODE_REFLECTION';
		var envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';

		if ( parameters.envMap ) {

			switch ( material.envMap.mapping ) {

				case THREE.CubeReflectionMapping:
				case THREE.CubeRefractionMapping:
					envMapTypeDefine = 'ENVMAP_TYPE_CUBE';
					break;

				case THREE.EquirectangularReflectionMapping:
				case THREE.EquirectangularRefractionMapping:
					envMapTypeDefine = 'ENVMAP_TYPE_EQUIREC';
					break;

				case THREE.SphericalReflectionMapping:
					envMapTypeDefine = 'ENVMAP_TYPE_SPHERE';
					break;

			}

			switch ( material.envMap.mapping ) {

				case THREE.CubeRefractionMapping:
				case THREE.EquirectangularRefractionMapping:
					envMapModeDefine = 'ENVMAP_MODE_REFRACTION';
					break;

			}

			switch ( material.combine ) {

				case THREE.MultiplyOperation:
					envMapBlendingDefine = 'ENVMAP_BLENDING_MULTIPLY';
					break;

				case THREE.MixOperation:
					envMapBlendingDefine = 'ENVMAP_BLENDING_MIX';
					break;

				case THREE.AddOperation:
					envMapBlendingDefine = 'ENVMAP_BLENDING_ADD';
					break;

			}

		}

		var gammaFactorDefine = ( renderer.gammaFactor > 0 ) ? renderer.gammaFactor : 1.0;

		// console.log( 'building new program ' );

		//

		var customDefines = generateDefines( defines );

		//

		var program = gl.createProgram();

		var prefixVertex, prefixFragment;

		if ( material instanceof THREE.RawShaderMaterial ) {

			prefixVertex = '';
			prefixFragment = '';

		} else {

			prefixVertex = [

				'precision ' + parameters.precision + ' float;',
				'precision ' + parameters.precision + ' int;',

				'#define SHADER_NAME ' + material.__webglShader.name,

				customDefines,

				parameters.supportsVertexTextures ? '#define VERTEX_TEXTURES' : '',

				renderer.gammaInput ? '#define GAMMA_INPUT' : '',
				renderer.gammaOutput ? '#define GAMMA_OUTPUT' : '',
				'#define GAMMA_FACTOR ' + gammaFactorDefine,

				'#define MAX_DIR_LIGHTS ' + parameters.maxDirLights,
				'#define MAX_POINT_LIGHTS ' + parameters.maxPointLights,
				'#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights,
				'#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights,

				'#define MAX_SHADOWS ' + parameters.maxShadows,

				'#define MAX_BONES ' + parameters.maxBones,

				parameters.map ? '#define USE_MAP' : '',
				parameters.envMap ? '#define USE_ENVMAP' : '',
				parameters.envMap ? '#define ' + envMapModeDefine : '',
				parameters.lightMap ? '#define USE_LIGHTMAP' : '',
				parameters.aoMap ? '#define USE_AOMAP' : '',
				parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',
				parameters.bumpMap ? '#define USE_BUMPMAP' : '',
				parameters.normalMap ? '#define USE_NORMALMAP' : '',
				parameters.specularMap ? '#define USE_SPECULARMAP' : '',
				parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
				parameters.vertexColors ? '#define USE_COLOR' : '',

				parameters.flatShading ? '#define FLAT_SHADED': '',

				parameters.skinning ? '#define USE_SKINNING' : '',
				parameters.useVertexTexture ? '#define BONE_TEXTURE' : '',

				parameters.morphTargets ? '#define USE_MORPHTARGETS' : '',
				parameters.morphNormals ? '#define USE_MORPHNORMALS' : '',
				parameters.doubleSided ? '#define DOUBLE_SIDED' : '',
				parameters.flipSided ? '#define FLIP_SIDED' : '',

				parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
				parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',
				parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '',
				parameters.shadowMapCascade ? '#define SHADOWMAP_CASCADE' : '',

				parameters.sizeAttenuation ? '#define USE_SIZEATTENUATION' : '',

				parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',
				parameters.logarithmicDepthBuffer && renderer.extensions.get('EXT_frag_depth') ? '#define USE_LOGDEPTHBUF_EXT' : '',

				'uniform mat4 modelMatrix;',
				'uniform mat4 modelViewMatrix;',
				'uniform mat4 projectionMatrix;',
				'uniform mat4 viewMatrix;',
				'uniform mat3 normalMatrix;',
				'uniform vec3 cameraPosition;',

				'attribute vec3 position;',
				'attribute vec3 normal;',
				'attribute vec2 uv;',

				'#ifdef USE_COLOR',

				'	attribute vec3 color;',

				'#endif',

				'#ifdef USE_MORPHTARGETS',

				'	attribute vec3 morphTarget0;',
				'	attribute vec3 morphTarget1;',
				'	attribute vec3 morphTarget2;',
				'	attribute vec3 morphTarget3;',

				'	#ifdef USE_MORPHNORMALS',

				'		attribute vec3 morphNormal0;',
				'		attribute vec3 morphNormal1;',
				'		attribute vec3 morphNormal2;',
				'		attribute vec3 morphNormal3;',

				'	#else',

				'		attribute vec3 morphTarget4;',
				'		attribute vec3 morphTarget5;',
				'		attribute vec3 morphTarget6;',
				'		attribute vec3 morphTarget7;',

				'	#endif',

				'#endif',

				'#ifdef USE_SKINNING',

				'	attribute vec4 skinIndex;',
				'	attribute vec4 skinWeight;',

				'#endif',

				'\n'

			].filter( filterEmptyLine ).join( '\n' );

			prefixFragment = [

				( parameters.bumpMap || parameters.normalMap || parameters.flatShading || material.derivatives ) ? '#extension GL_OES_standard_derivatives : enable' : '',

				'precision ' + parameters.precision + ' float;',
				'precision ' + parameters.precision + ' int;',

				'#define SHADER_NAME ' + material.__webglShader.name,

				customDefines,

				'#define MAX_DIR_LIGHTS ' + parameters.maxDirLights,
				'#define MAX_POINT_LIGHTS ' + parameters.maxPointLights,
				'#define MAX_SPOT_LIGHTS ' + parameters.maxSpotLights,
				'#define MAX_HEMI_LIGHTS ' + parameters.maxHemiLights,

				'#define MAX_SHADOWS ' + parameters.maxShadows,

				parameters.alphaTest ? '#define ALPHATEST ' + parameters.alphaTest : '',

				renderer.gammaInput ? '#define GAMMA_INPUT' : '',
				renderer.gammaOutput ? '#define GAMMA_OUTPUT' : '',
				'#define GAMMA_FACTOR ' + gammaFactorDefine,

				( parameters.useFog && parameters.fog ) ? '#define USE_FOG' : '',
				( parameters.useFog && parameters.fogExp ) ? '#define FOG_EXP2' : '',

				parameters.map ? '#define USE_MAP' : '',
				parameters.envMap ? '#define USE_ENVMAP' : '',
				parameters.envMap ? '#define ' + envMapTypeDefine : '',
				parameters.envMap ? '#define ' + envMapModeDefine : '',
				parameters.envMap ? '#define ' + envMapBlendingDefine : '',
				parameters.lightMap ? '#define USE_LIGHTMAP' : '',
				parameters.aoMap ? '#define USE_AOMAP' : '',
				parameters.emissiveMap ? '#define USE_EMISSIVEMAP' : '',
				parameters.bumpMap ? '#define USE_BUMPMAP' : '',
				parameters.normalMap ? '#define USE_NORMALMAP' : '',
				parameters.specularMap ? '#define USE_SPECULARMAP' : '',
				parameters.alphaMap ? '#define USE_ALPHAMAP' : '',
				parameters.vertexColors ? '#define USE_COLOR' : '',

				parameters.flatShading ? '#define FLAT_SHADED': '',

				parameters.metal ? '#define METAL' : '',
				parameters.doubleSided ? '#define DOUBLE_SIDED' : '',
				parameters.flipSided ? '#define FLIP_SIDED' : '',

				parameters.shadowMapEnabled ? '#define USE_SHADOWMAP' : '',
				parameters.shadowMapEnabled ? '#define ' + shadowMapTypeDefine : '',
				parameters.shadowMapDebug ? '#define SHADOWMAP_DEBUG' : '',
				parameters.shadowMapCascade ? '#define SHADOWMAP_CASCADE' : '',

				parameters.logarithmicDepthBuffer ? '#define USE_LOGDEPTHBUF' : '',
				parameters.logarithmicDepthBuffer && renderer.extensions.get('EXT_frag_depth') ? '#define USE_LOGDEPTHBUF_EXT' : '',

				'uniform mat4 viewMatrix;',
				'uniform vec3 cameraPosition;',

				'\n'

			].filter( filterEmptyLine ).join( '\n' );

		}

		var vertexGlsl = prefixVertex + vertexShader;
		var fragmentGlsl = prefixFragment + fragmentShader;

		var glVertexShader = THREE.WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl );
		var glFragmentShader = THREE.WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl );

		gl.attachShader( program, glVertexShader );
		gl.attachShader( program, glFragmentShader );

		if ( index0AttributeName !== undefined ) {

			// Force a particular attribute to index 0.
			// because potentially expensive emulation is done by browser if attribute 0 is disabled.
			// And, color, for example is often automatically bound to index 0 so disabling it

			gl.bindAttribLocation( program, 0, index0AttributeName );

		}

		gl.linkProgram( program );

		var programLogInfo = gl.getProgramInfoLog( program );
		var vertexErrorLogInfo = gl.getShaderInfoLog( glVertexShader );
		var fragmentErrorLogInfo = gl.getShaderInfoLog( glFragmentShader );

		if ( gl.getProgramParameter( program, gl.LINK_STATUS ) === false ) {

			console.error( 'THREE.WebGLProgram: shader error: ', gl.getError(), 'gl.VALIDATE_STATUS', gl.getProgramParameter( program, gl.VALIDATE_STATUS ), 'gl.getProgramInfoLog', programLogInfo, vertexErrorLogInfo, fragmentErrorLogInfo );

		}

		else if ( programLogInfo !== '' ) {

			console.warn( 'THREE.WebGLProgram: gl.getProgramInfoLog()', programLogInfo );

		}

		// clean up

		gl.deleteShader( glVertexShader );
		gl.deleteShader( glFragmentShader );

		// set up caching for uniform locations

		var getUniforms = function() { return this._cachedUniforms; };

		this.getUniforms = function() {

			// fetch, cache, and next time just use a dumb accessor
			var uniforms = fetchUniformLocations( gl, program );
			this._cachedUniforms = uniforms;
			this.getUniforms = getUniforms;
			return uniforms;

		};

		// set up caching for attribute locations

		var getAttributes = function() { return this._cachedAttributes; };

		this.getAttributes = function() {

			var attributes = fetchAttributeLocations( gl, program );
			this._cachedAttributes = attributes;
			this.getAttributes = getAttributes;
			return attributes;

		};

		// DEPRECATED

		Object.defineProperties( this, {

			uniforms: {
				get: function() {

					console.warn( 'THREE.WebGLProgram: .uniforms is now .getUniforms().' );
					return this.getUniforms();

				}
			},

			attributes: {
				get: function() {

					console.warn( 'THREE.WebGLProgram: .attributes is now .getAttributes().' );
					return this.getAttributes();

				}
			}

		});

		//

		this.id = programIdCount ++;
		this.code = code;
		this.usedTimes = 1;
		this.program = program;
		this.vertexShader = glVertexShader;
		this.fragmentShader = glFragmentShader;

		return this;

	};

} )();
<|endoftext|>


<commit_before>import {bindable} from 'aurelia-framework';
import {Field} from './abstract/field';
import {parseJSON} from '../jsonparser';

export class Typefield extends Field {
  @bindable selectedType = '';
  valueKey = '';
  showType = true;
  copyValue = false;
  child = undefined;
  types = {};

  /** @inheritdoc */
  init(id = '', args = {}) {
    args = Object.assign({
      valueKey: '',
      showType: true,
      copyValue: false,
      types: { 'null': { 'type': 'text' } }
    }, args);
    this.types = args.types;
    this.valueKey = args.valueKey;
    this.showType = args.showType;
    this.copyValue = args.copyValue;
    this.selectedType = args.defaultType || Object.keys(this.types)[0];
    this.selectedTypeChanged(this.selectedType);
    return super.init(id, args);
  }

  selectedTypeChanged(newType) {
    const newSchema = this.types[newType];
    const value = this.copyValue ? this.getValue() : undefined;
    let newChild;
    if (newSchema.hasOwnProperty('$ref')) {
      newChild = this.resolveRef(newSchema.$ref).clone();
    } else {
      newChild = parseJSON(newType, newSchema);
    }
    if (newChild) {
      newChild.parent = this;
      if (value) {
        this.setValue(value);
      }
      this.child = newChild;
    }
  }

  getValue() {
    if (!this.child) {
      return undefined;
    }

    let value = this.child.getValue();
    if (!this.showType) {
      return value;
    }
    if (this.valueKey || typeof value !== 'object' || Array.isArray(value)) {
      const valueKey = this.valueKey || 'value';
      value = {
        type: this.selectedType,
        [valueKey]: value
      };
    } else {
      value.type = this.selectedType;
    }
    return value;
  }

  setValue(value) {
    if (typeof value === 'object' && !Array.isArray(value)) {
      delete value.type;
      this.child.setValue(value);
    } else {
      this.child.setValue(value[this.valueKey]);
    }
  }

  setType(type) {
    this.selectedType = type;
  }

  getType() {
    return this.selectedType;
  }

  get possibleTypes() {
    return Object.keys(this.types);
  }
}
<commit_msg>Add JSDocs and @containerless to Typefield<commit_after>import {containerless, bindable} from 'aurelia-framework';
import {Field} from './abstract/field';
import {parseJSON} from '../jsonparser';

/**
 * Typefield is a {@link Field} that shows different child forms depending on
 * the type the user chooses.
 *
 * This allows for theoretically infinite nesting because it uses lazy
 * evaluation and supports $refs in type options.
 */
@containerless
export class Typefield extends Field {
  /**
   * The type that is currently selected.
   * @type {String}
   */
  @bindable selectedType = '';
  /**
   * The key to set the value of the child field to in the output of this field.
   * @type {String}
   */
  valueKey = '';
  /**
   * Whether or not to show the chosen type in the value of this field.
   * @type {Boolean}
   */
  showType = true;
  /**
   * Whether or not to copy the value over to the new child when switching types.
   *
   * N.B. Absolutely nothing is done to ensure the data ends up in the right
   * place. Only use this if most of the fields in different types are in nearly
   * the same places.
   * @type {Boolean}
   */
  copyValue = false;
  /**
   * The current child field.
   * @type {Field}
   * @private
   */
  child = undefined;
  /**
   * The schemas for the available types.
   * @type {Object}
   */
  types = {};

  /** @inheritdoc */
  init(id = '', args = {}) {
    args = Object.assign({
      valueKey: '',
      showType: true,
      copyValue: false,
      types: { 'null': { 'type': 'text' } }
    }, args);
    this.types = args.types;
    this.valueKey = args.valueKey;
    this.showType = args.showType;
    this.copyValue = args.copyValue;
    this.selectedType = args.defaultType || Object.keys(this.types)[0];
    this.selectedTypeChanged(this.selectedType);
    return super.init(id, args);
  }

  /**
   * Called by Aurelia when the selected type changes (e.g. from the dropdown)
   * @param {String} newType The new type.
   */
  selectedTypeChanged(newType) {
    const newSchema = this.types[newType];
    const value = this.copyValue ? this.getValue() : undefined;
    let newChild;
    if (newSchema.hasOwnProperty('$ref')) {
      newChild = this.resolveRef(newSchema.$ref).clone();
    } else {
      newChild = parseJSON(newType, newSchema);
    }
    if (newChild) {
      newChild.parent = this;
      if (value) {
        this.setValue(value);
      }
      this.child = newChild;
    }
  }

  /**
   * Get the value of this field.
   *
   * If the child is undefined, this will return undefined.
   * If {@link #showType} is {@linkplain false}, the raw value of the child is
   * returned.
   * If {@link #showType} is {@linkplain true} and {@link #valueKey} is defined
   * OR the raw value of the child is not an object, the raw value of the child
   * will be placed in an object with {@link #valueKey} or {@linkplain value}
   * as the key.
   * If {@link #showType} is {@linkplain true}, {@link #valueKey} is not
   * defined and the raw value of the child is an object, the type will simply
   * be added to the raw value of the child.
   *
   * @return {Object} The value.
   */
  getValue() {
    if (!this.child) {
      return undefined;
    }

    let value = this.child.getValue();
    if (!this.showType) {
      return value;
    }
    if (this.valueKey || typeof value !== 'object' || Array.isArray(value)) {
      const valueKey = this.valueKey || 'value';
      value = {
        type: this.selectedType,
        [valueKey]: value
      };
    } else {
      value.type = this.selectedType;
    }
    return value;
  }

  setValue(value) {
    if (!this.showType) {
      this.child.setValue(value);
    } else if (typeof value === 'object' && !Array.isArray(value)) {
      delete value.type;
      this.child.setValue(value);
    } else {
      this.child.setValue(value[this.valueKey || 'value']);
    }
  }

  setType(type) {
    this.selectedType = type;
  }

  getType() {
    return this.selectedType;
  }

  /**
   * Get all the possible type names.
   * @return {String[]} The names of the possible types.
   */
  get possibleTypes() {
    return Object.keys(this.types);
  }
}
<|endoftext|>


<commit_before>const PLAYER_PLAY = 'player/PLAYER_PLAY'
const PLAYER_PAUSE = 'player/PLAYER_PAUSE'
const PLAYER_STATUS = 'player/PLAYER_STATUS'

// can be emitted after a PLAYER_NEXT_REQUEST
const PLAYER_QUEUE_END = 'player/PLAYER_QUEUE_END'

// Request Play Next
const PLAYER_NEXT_REQUEST = 'server/PLAYER_NEXT'
export function requestPlayNext() {
  return (dispatch, getState) => {
    const curId = getState().queue.curId

    dispatch({
      type: PLAYER_NEXT_REQUEST,
      payload: curId
    })
  }
}

// ------------------------------------
// dispatched mostly for informational purposes
// by provider players
// ------------------------------------
export const GET_MEDIA = 'player/GET_MEDIA'
export const GET_MEDIA_SUCCESS = 'player/GET_MEDIA_SUCCESS'

export function getMedia(url) {
  return {
    type: GET_MEDIA,
    payload: url
  }
}

export function getMediaSuccess() {
  return {
    type: GET_MEDIA_SUCCESS,
    payload: null
  }
}

// signals we should move to next song
export const MEDIA_END = 'player/MEDIA_END'

export function mediaEnd() {
  return (dispatch, getState) => {
    dispatch({
      type: MEDIA_END,
      payload: getState().player.queueId
    })

    dispatch(requestPlayNext())
  }
}

const PLAYER_EMIT_ERROR = 'server/PLAYER_EMIT_ERROR'
export function mediaError(queueId, message) {
  return (dispatch, getState) => {
    // informational: have server emit player error to room
    dispatch({
      type: PLAYER_EMIT_ERROR,
      payload: {queueId, message}
    })

    // move to next song if we haven't already
    // if (!getState().player.errors[id]) {
      dispatch(requestPlayNext())
    // }

  }
}

// have server emit player status to room
const PLAYER_EMIT_STATUS = 'server/PLAYER_EMIT_STATUS'
export function status(payload) {
  return {
    type: PLAYER_EMIT_STATUS,
    payload
  }
}

// ------------------------------------
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = {
  [PLAYER_PAUSE]: (state, {payload}) => ({
    ...state,
    isPlaying: false,
  }),
  [PLAYER_PLAY]: (state, {payload}) => ({
    ...state,
    isPlaying: true,
  }),
  [PLAYER_QUEUE_END]: (state, {payload}) => ({
    ...state,
    isPlaying: false,
  }),
  [GET_MEDIA]: (state, {payload}) => ({
    ...state,
    isFetching: true,
  }),
  [GET_MEDIA_SUCCESS]: (state, {payload}) => ({
    ...state,
    isFetching: false,
    isPlaying: true, // all media is loaded
  }),
}

// ------------------------------------
// Reducer
// ------------------------------------
const initialState = {
  isPlaying: false,
  isFetching: false,
  errorMessage: null,
}

export default function playerReducer (state = initialState, action) {
  const handler = ACTION_HANDLERS[action.type]

  return handler ? handler(state, action) : state
}
<commit_msg>Simplify action<commit_after>const PLAYER_PLAY = 'player/PLAYER_PLAY'
const PLAYER_PAUSE = 'player/PLAYER_PAUSE'
const PLAYER_STATUS = 'player/PLAYER_STATUS'

// can be emitted after a PLAYER_NEXT_REQUEST
const PLAYER_QUEUE_END = 'player/PLAYER_QUEUE_END'

// Request Play Next
const PLAYER_NEXT_REQUEST = 'server/PLAYER_NEXT'
export function requestPlayNext() {
  return (dispatch, getState) => {
    dispatch({
      type: PLAYER_NEXT_REQUEST,
      payload: getState().queue.curId
    })
  }
}

// ------------------------------------
// dispatched mostly for informational purposes
// by provider players
// ------------------------------------
export const GET_MEDIA = 'player/GET_MEDIA'
export const GET_MEDIA_SUCCESS = 'player/GET_MEDIA_SUCCESS'

export function getMedia(url) {
  return {
    type: GET_MEDIA,
    payload: url
  }
}

export function getMediaSuccess() {
  return {
    type: GET_MEDIA_SUCCESS,
    payload: null
  }
}

// signals we should move to next song
export const MEDIA_END = 'player/MEDIA_END'

export function mediaEnd() {
  return (dispatch, getState) => {
    dispatch({
      type: MEDIA_END,
      payload: getState().player.queueId
    })

    dispatch(requestPlayNext())
  }
}

const PLAYER_EMIT_ERROR = 'server/PLAYER_EMIT_ERROR'
export function mediaError(queueId, message) {
  return (dispatch, getState) => {
    // informational: have server emit player error to room
    dispatch({
      type: PLAYER_EMIT_ERROR,
      payload: {queueId, message}
    })

    // move to next song if we haven't already
    // if (!getState().player.errors[id]) {
      dispatch(requestPlayNext())
    // }

  }
}

// have server emit player status to room
const PLAYER_EMIT_STATUS = 'server/PLAYER_EMIT_STATUS'
export function status(payload) {
  return {
    type: PLAYER_EMIT_STATUS,
    payload
  }
}

// ------------------------------------
// Action Handlers
// ------------------------------------
const ACTION_HANDLERS = {
  [PLAYER_PAUSE]: (state, {payload}) => ({
    ...state,
    isPlaying: false,
  }),
  [PLAYER_PLAY]: (state, {payload}) => ({
    ...state,
    isPlaying: true,
  }),
  [PLAYER_QUEUE_END]: (state, {payload}) => ({
    ...state,
    isPlaying: false,
  }),
  [GET_MEDIA]: (state, {payload}) => ({
    ...state,
    isFetching: true,
  }),
  [GET_MEDIA_SUCCESS]: (state, {payload}) => ({
    ...state,
    isFetching: false,
    isPlaying: true, // all media is loaded
  }),
}

// ------------------------------------
// Reducer
// ------------------------------------
const initialState = {
  isPlaying: false,
  isFetching: false,
  errorMessage: null,
}

export default function playerReducer (state = initialState, action) {
  const handler = ACTION_HANDLERS[action.type]

  return handler ? handler(state, action) : state
}
<|endoftext|>


<commit_before>/*!
 * ${copyright}
 */
sap.ui.define([
	"./BaseHeader",
	"sap/m/library",
	"sap/f/library",
	"sap/m/Text",
	"sap/f/Avatar",
	"sap/f/cards/HeaderRenderer",
	"sap/ui/core/Core",
	"sap/ui/core/InvisibleText"
], function (
	BaseHeader,
	mLibrary,
	library,
	Text,
	Avatar,
	HeaderRenderer,
	Core,
	InvisibleText
) {
	"use strict";

	var AvatarShape = mLibrary.AvatarShape;
	var AvatarColor = mLibrary.AvatarColor;

	/**
	 * Constructor for a new Header.
	 *
	 * @param {string} [sId] ID for the new control, generated automatically if no ID is given
	 * @param {object} [mSettings] Initial settings for the new control
	 *
	 * @class
	 * Displays general information in the header of the {@link sap.f.Card}.
	 *
	 * You can configure the title, subtitle, status text and icon, using the provided properties.
	 *
	 * Notes:
	 *

	 *
You should always set a title.

	 *
To show a KPI or any numeric information, use {@link sap.f.cards.NumericHeader} instead.

	 *

	 *
	 * @extends sap.f.cards.BaseHeader
	 * @implements sap.f.cards.IHeader
	 *
	 * @author SAP SE
	 * @version ${version}
	 *
	 * @constructor
	 * @public
	 * @since 1.64
	 * @alias sap.f.cards.Header
	 * @ui5-metamodel This control/element also will be described in the UI5 (legacy) designtime metamodel
	 */
	var Header = BaseHeader.extend("sap.f.cards.Header", {
		metadata: {
			library: "sap.f",
			interfaces: ["sap.f.cards.IHeader"],
			properties: {

				/**
				 * Defines the title.
				 */
				title: { type: "string", defaultValue: "" },

				/**
				 * Defines the subtitle.
				 */
				subtitle: { type: "string", defaultValue: "" },

				/**
				 * Defines the status text.
				 */
				statusText: { type: "string", defaultValue: "" },

				/**
				 * Defines the shape of the icon.
				 */
				iconDisplayShape: { type: "sap.m.AvatarShape", defaultValue: AvatarShape.Circle },

				/**
				 * Defines the icon source.
				 */
				iconSrc: { type: "sap.ui.core.URI", defaultValue: "" },

				/**
				 * Defines the initials of the icon.
				 */
				iconInitials: { type: "string", defaultValue: "" },

				/**
				 * Defines an alt text for the avatar or icon.
				 *
				 * @experimental Since 1.81 this feature is experimental and the API may change.
				 */
				iconAlt: { type: "string", defaultValue: "" },

				/**
				 * Defines a background color for the avatar or icon.
				 *
				 * @experimental Since 1.83 this feature is experimental and the API may change.
				 */
				iconBackgroundColor: { type: "sap.m.AvatarColor", defaultValue: AvatarColor.Transparent }
			},
			aggregations: {

				/**
				 * Defines the inner title control.
				 */
				_title: { type: "sap.m.Text", multiple: false, visibility: "hidden" },

				/**
				 * Defines the inner subtitle control.
				 */
				_subtitle: { type: "sap.m.Text", multiple: false, visibility: "hidden" },

				/**
				 * Defines the inner avatar control.
				 */
				_avatar: { type: "sap.f.Avatar", multiple: false, visibility: "hidden" }
			},
			events: {

				/**
				 * Fires when the user presses the control.
				 */
				press: {}
			}
		},
		renderer: HeaderRenderer
	});

	/**
	 * Initialization hook.
	 * @private
	 */
	Header.prototype.init = function () {
		BaseHeader.prototype.init.apply(this, arguments);

		this._oRb = Core.getLibraryResourceBundle("sap.f");
		this.data("sap-ui-fastnavgroup", "true", true); // Define group for F6 handling

		this._oAriaAvatarText = new InvisibleText({id: this.getId() + "-ariaAvatarText"});
		this._oAriaAvatarText.setText(this._oRb.getText("ARIA_HEADER_AVATAR_TEXT"));

	};

	Header.prototype.exit = function () {
		BaseHeader.prototype.exit.apply(this, arguments);

		if (this._oAriaAvatarText) {
			this._oAriaAvatarText.destroy();
			this._oAriaAvatarText = null;
		}
		this._oRb = null;
	};

	/**
	 * Lazily creates a title and returns it.
	 * @private
	 * @returns {sap.m.Text} The inner title aggregation
	 */
	Header.prototype._getTitle = function () {
		var oTitle = this.getAggregation("_title");
		if (!oTitle) {
			oTitle = new Text({
				maxLines: 3
			}).addStyleClass("sapFCardTitle");
			this.setAggregation("_title", oTitle);
		}
		return oTitle;
	};

	/**
	 * Lazily creates a subtitle and returns it.
	 * @private
	 * @returns {sap.m.Text} The inner subtitle aggregation
	 */
	Header.prototype._getSubtitle = function () {
		var oSubtitle = this.getAggregation("_subtitle");
		if (!oSubtitle) {
			oSubtitle = new Text({
				maxLines: 2
			}).addStyleClass("sapFCardSubtitle");
			this.setAggregation("_subtitle", oSubtitle);
		}
		return oSubtitle;
	};

	/**
	 * Lazily creates an avatar control and returns it.
	 * @private
	 * @returns {sap.f.Avatar} The inner avatar aggregation
	 */
	Header.prototype._getAvatar = function () {
		var oAvatar = this.getAggregation("_avatar");
		if (!oAvatar) {
			oAvatar = new Avatar().addStyleClass("sapFCardIcon");
			this.setAggregation("_avatar", oAvatar);
		}
		return oAvatar;
	};

	/**
	 * Called before the control is rendered.
	 * @private
	 */
	Header.prototype.onBeforeRendering = function () {
		BaseHeader.prototype.onBeforeRendering.apply(this, arguments);

		var oAvatar = this._getAvatar();

		this._getTitle().setText(this.getTitle());
		this._getSubtitle().setText(this.getSubtitle());

		oAvatar.setDisplayShape(this.getIconDisplayShape());
		oAvatar.setSrc(this.getIconSrc());
		oAvatar.setInitials(this.getIconInitials());
		oAvatar.setTooltip(this.getIconAlt());
		oAvatar.setBackgroundColor(this.getIconBackgroundColor());

		this._setAccessibilityAttributes();
	};

	/**
	 * Helper function used to create aria-labelledby attribute.
	 *
	 * @private
	 * @returns {string} IDs of controls
	 */
	Header.prototype._getHeaderAccessibility = function () {
		var sSubtitleId = this.getSubtitle() ? this._getSubtitle().getId() : "",
			sStatusTextId = this.getStatusText() ? this.getId() + "-status" : "",
			sAvatarId = this.getIconSrc() || this.getIconInitials() ? this.getId() + "-ariaAvatarText " : "",
			sIds = sSubtitleId + " " + sStatusTextId + " " + sAvatarId;

		// remove whitespace from both sides
		// and merge the consecutive whitespaces into one
		return sIds.replace(/ {2,}/g, ' ').trim();
	};

	/**
	 * Fires the sap.f.cards.Header press event.
	 */
	Header.prototype.ontap = function (oEvent) {
		var srcControl = oEvent.srcControl;
		if (srcControl && srcControl.getId().indexOf("overflowButton") > -1) { // better way?
			return;
		}

		this.firePress();
	};

	/**
	 * Fires the sap.f.cards.Header press event.
	 */
	Header.prototype.onsapselect = function () {
		this.firePress();
	};

	/**
	 * Sets accessibility to the header to the header.
	 *
	 * @private
	 */
	Header.prototype._setAccessibilityAttributes = function () {
		if (this.hasListeners("press")) {
			this._sAriaRole = "button";
			this._sAriaHeadingLevel = undefined;
			this._sAriaRoleDescritoion = this._oRb.getText("ARIA_ROLEDESCRIPTION_INTERACTIVE_CARD_HEADER");
		} else {
			this._sAriaRole = "heading";
			this._sAriaHeadingLevel = "3";
			this._sAriaRoleDescritoion = this._oRb.getText("ARIA_ROLEDESCRIPTION_CARD_HEADER");
		}
	};

	Header.prototype.isLoading = function () {
		return false;
	};

	Header.prototype.attachPress = function () {
		var aMyArgs = Array.prototype.slice.apply(arguments);
		aMyArgs.unshift("press");

		BaseHeader.prototype.attachEvent.apply(this, aMyArgs);

		this.invalidate();

		return this;
	};

	Header.prototype.detachPress = function() {
		var aMyArgs = Array.prototype.slice.apply(arguments);
		aMyArgs.unshift("press");

		BaseHeader.prototype.detachEvent.apply(this, aMyArgs);

		this.invalidate();

		return this;
	};

	/**
	 * Returns if the control is inside a sap.f.GridContainer
	 *
	 * @private
	 */
	Header.prototype._isInsideGridContainer = function() {
		var oParent = this.getParent();
		if (!oParent) {
			return false;
		}

		oParent = oParent.getParent();
		if (!oParent) {
			return false;
		}

		return oParent.isA("sap.f.GridContainer");
	};

	return Header;
});
<commit_msg>[INTERNAL] sap.f.cards.Header: Use sap/m/Avatar instead of sap/f/Avatar<commit_after>/*!
 * ${copyright}
 */
sap.ui.define([
	"./BaseHeader",
	"sap/m/library",
	"sap/f/library",
	"sap/m/Text",
	"sap/m/Avatar",
	"sap/f/cards/HeaderRenderer",
	"sap/ui/core/Core",
	"sap/ui/core/InvisibleText"
], function (
	BaseHeader,
	mLibrary,
	library,
	Text,
	Avatar,
	HeaderRenderer,
	Core,
	InvisibleText
) {
	"use strict";

	var AvatarShape = mLibrary.AvatarShape;
	var AvatarColor = mLibrary.AvatarColor;

	/**
	 * Constructor for a new Header.
	 *
	 * @param {string} [sId] ID for the new control, generated automatically if no ID is given
	 * @param {object} [mSettings] Initial settings for the new control
	 *
	 * @class
	 * Displays general information in the header of the {@link sap.f.Card}.
	 *
	 * You can configure the title, subtitle, status text and icon, using the provided properties.
	 *
	 * Notes:
	 *

	 *
You should always set a title.

	 *
To show a KPI or any numeric information, use {@link sap.f.cards.NumericHeader} instead.

	 *

	 *
	 * @extends sap.f.cards.BaseHeader
	 * @implements sap.f.cards.IHeader
	 *
	 * @author SAP SE
	 * @version ${version}
	 *
	 * @constructor
	 * @public
	 * @since 1.64
	 * @alias sap.f.cards.Header
	 * @ui5-metamodel This control/element also will be described in the UI5 (legacy) designtime metamodel
	 */
	var Header = BaseHeader.extend("sap.f.cards.Header", {
		metadata: {
			library: "sap.f",
			interfaces: ["sap.f.cards.IHeader"],
			properties: {

				/**
				 * Defines the title.
				 */
				title: { type: "string", defaultValue: "" },

				/**
				 * Defines the subtitle.
				 */
				subtitle: { type: "string", defaultValue: "" },

				/**
				 * Defines the status text.
				 */
				statusText: { type: "string", defaultValue: "" },

				/**
				 * Defines the shape of the icon.
				 */
				iconDisplayShape: { type: "sap.m.AvatarShape", defaultValue: AvatarShape.Circle },

				/**
				 * Defines the icon source.
				 */
				iconSrc: { type: "sap.ui.core.URI", defaultValue: "" },

				/**
				 * Defines the initials of the icon.
				 */
				iconInitials: { type: "string", defaultValue: "" },

				/**
				 * Defines an alt text for the avatar or icon.
				 *
				 * @experimental Since 1.81 this feature is experimental and the API may change.
				 */
				iconAlt: { type: "string", defaultValue: "" },

				/**
				 * Defines a background color for the avatar or icon.
				 *
				 * @experimental Since 1.83 this feature is experimental and the API may change.
				 */
				iconBackgroundColor: { type: "sap.m.AvatarColor", defaultValue: AvatarColor.Transparent }
			},
			aggregations: {

				/**
				 * Defines the inner title control.
				 */
				_title: { type: "sap.m.Text", multiple: false, visibility: "hidden" },

				/**
				 * Defines the inner subtitle control.
				 */
				_subtitle: { type: "sap.m.Text", multiple: false, visibility: "hidden" },

				/**
				 * Defines the inner avatar control.
				 */
				_avatar: { type: "sap.m.Avatar", multiple: false, visibility: "hidden" }
			},
			events: {

				/**
				 * Fires when the user presses the control.
				 */
				press: {}
			}
		},
		renderer: HeaderRenderer
	});

	/**
	 * Initialization hook.
	 * @private
	 */
	Header.prototype.init = function () {
		BaseHeader.prototype.init.apply(this, arguments);

		this._oRb = Core.getLibraryResourceBundle("sap.f");
		this.data("sap-ui-fastnavgroup", "true", true); // Define group for F6 handling

		this._oAriaAvatarText = new InvisibleText({id: this.getId() + "-ariaAvatarText"});
		this._oAriaAvatarText.setText(this._oRb.getText("ARIA_HEADER_AVATAR_TEXT"));

	};

	Header.prototype.exit = function () {
		BaseHeader.prototype.exit.apply(this, arguments);

		if (this._oAriaAvatarText) {
			this._oAriaAvatarText.destroy();
			this._oAriaAvatarText = null;
		}
		this._oRb = null;
	};

	/**
	 * Lazily creates a title and returns it.
	 * @private
	 * @returns {sap.m.Text} The inner title aggregation
	 */
	Header.prototype._getTitle = function () {
		var oTitle = this.getAggregation("_title");
		if (!oTitle) {
			oTitle = new Text({
				maxLines: 3
			}).addStyleClass("sapFCardTitle");
			this.setAggregation("_title", oTitle);
		}
		return oTitle;
	};

	/**
	 * Lazily creates a subtitle and returns it.
	 * @private
	 * @returns {sap.m.Text} The inner subtitle aggregation
	 */
	Header.prototype._getSubtitle = function () {
		var oSubtitle = this.getAggregation("_subtitle");
		if (!oSubtitle) {
			oSubtitle = new Text({
				maxLines: 2
			}).addStyleClass("sapFCardSubtitle");
			this.setAggregation("_subtitle", oSubtitle);
		}
		return oSubtitle;
	};

	/**
	 * Lazily creates an avatar control and returns it.
	 * @private
	 * @returns {sap.m.Avatar} The inner avatar aggregation
	 */
	Header.prototype._getAvatar = function () {
		var oAvatar = this.getAggregation("_avatar");
		if (!oAvatar) {
			oAvatar = new Avatar().addStyleClass("sapFCardIcon");
			this.setAggregation("_avatar", oAvatar);
		}
		return oAvatar;
	};

	/**
	 * Called before the control is rendered.
	 * @private
	 */
	Header.prototype.onBeforeRendering = function () {
		BaseHeader.prototype.onBeforeRendering.apply(this, arguments);

		var oAvatar = this._getAvatar();

		this._getTitle().setText(this.getTitle());
		this._getSubtitle().setText(this.getSubtitle());

		oAvatar.setDisplayShape(this.getIconDisplayShape());
		oAvatar.setSrc(this.getIconSrc());
		oAvatar.setInitials(this.getIconInitials());
		oAvatar.setTooltip(this.getIconAlt());
		oAvatar.setBackgroundColor(this.getIconBackgroundColor());

		this._setAccessibilityAttributes();
	};

	/**
	 * Helper function used to create aria-labelledby attribute.
	 *
	 * @private
	 * @returns {string} IDs of controls
	 */
	Header.prototype._getHeaderAccessibility = function () {
		var sSubtitleId = this.getSubtitle() ? this._getSubtitle().getId() : "",
			sStatusTextId = this.getStatusText() ? this.getId() + "-status" : "",
			sAvatarId = this.getIconSrc() || this.getIconInitials() ? this.getId() + "-ariaAvatarText " : "",
			sIds = sSubtitleId + " " + sStatusTextId + " " + sAvatarId;

		// remove whitespace from both sides
		// and merge the consecutive whitespaces into one
		return sIds.replace(/ {2,}/g, ' ').trim();
	};

	/**
	 * Fires the sap.f.cards.Header press event.
	 */
	Header.prototype.ontap = function (oEvent) {
		var srcControl = oEvent.srcControl;
		if (srcControl && srcControl.getId().indexOf("overflowButton") > -1) { // better way?
			return;
		}

		this.firePress();
	};

	/**
	 * Fires the sap.f.cards.Header press event.
	 */
	Header.prototype.onsapselect = function () {
		this.firePress();
	};

	/**
	 * Sets accessibility to the header to the header.
	 *
	 * @private
	 */
	Header.prototype._setAccessibilityAttributes = function () {
		if (this.hasListeners("press")) {
			this._sAriaRole = "button";
			this._sAriaHeadingLevel = undefined;
			this._sAriaRoleDescritoion = this._oRb.getText("ARIA_ROLEDESCRIPTION_INTERACTIVE_CARD_HEADER");
		} else {
			this._sAriaRole = "heading";
			this._sAriaHeadingLevel = "3";
			this._sAriaRoleDescritoion = this._oRb.getText("ARIA_ROLEDESCRIPTION_CARD_HEADER");
		}
	};

	Header.prototype.isLoading = function () {
		return false;
	};

	Header.prototype.attachPress = function () {
		var aMyArgs = Array.prototype.slice.apply(arguments);
		aMyArgs.unshift("press");

		BaseHeader.prototype.attachEvent.apply(this, aMyArgs);

		this.invalidate();

		return this;
	};

	Header.prototype.detachPress = function() {
		var aMyArgs = Array.prototype.slice.apply(arguments);
		aMyArgs.unshift("press");

		BaseHeader.prototype.detachEvent.apply(this, aMyArgs);

		this.invalidate();

		return this;
	};

	/**
	 * Returns if the control is inside a sap.f.GridContainer
	 *
	 * @private
	 */
	Header.prototype._isInsideGridContainer = function() {
		var oParent = this.getParent();
		if (!oParent) {
			return false;
		}

		oParent = oParent.getParent();
		if (!oParent) {
			return false;
		}

		return oParent.isA("sap.f.GridContainer");
	};

	return Header;
});
<|endoftext|>