<commit_before>
    /*******************************
            Abstracted Functions
    *******************************/

    // Gets data from provided url
    // and updates DOM element (element variable)
    function generate_os_data(url, element){
        $.get( url, function( data ) {
          $(element).text(data);
        } );
    }

    // If dataTable with proved ID (table_id)
    // exists, destroy it. Obliterate it.
    // Leave no evidence.
    function destroy_dataTable(table_id){
        var table = $('#'+table_id);

        var ex = document.getElementById(table_id);
        if ( $.fn.DataTable.fnIsDataTable( ex ) ) {
    		table.hide().dataTable().fnClearTable();
        	table.dataTable( ).fnDestroy();
    	}
    }

    // Function and variables to refresh page data &
    // counter on top right of page
    var page_refresh_counter = $('#page_data_refresh');
    function page_data_refresh_tick(){

        if (Number( page_refresh_counter.text() )==0){
            refresh_all();
            page_refresh_counter.fadeOut().text(20).fadeIn();
            setTimeout(function(){ page_data_refresh_tick(); },1000);
            return false;
        }
        else{
            setTimeout(function(){
                page_refresh_counter.text( Number(page_refresh_counter.text())-1);
                page_data_refresh_tick();
            },1000);
        }
    }

    //DataTables
    //Sort file size data.
    jQuery.extend(jQuery.fn.dataTableExt.oSort, {
        "file-size-pre": function (a) {
            var x = a.substring(0, a.length - 2);
            var x_unit = (a.substring(a.length - 2, a.length) == "M" ?
                1000 : (a.substring(a.length - 2, a.length) == "G" ? 1000000 : 1));

            return parseInt(x * x_unit, 10);
        },

        "file-size-asc": function (a, b) {
            return ((a < b) ? -1 : ((a > b) ? 1 : 0));
        },

        "file-size-desc": function (a, b) {
            return ((a < b) ? 1 : ((a > b) ? -1 : 0));
        }
    });

    //DataTables
    //Sort numeric data which has a percent sign with it.
    jQuery.extend(jQuery.fn.dataTableExt.oSort, {
        "percent-pre": function (a) {
            var x = (a == "-") ? 0 : a.replace(/%/, "");
            return parseFloat(x);
        },

        "percent-asc": function (a, b) {
            return ((a < b) ? -1 : ((a > b) ? 1 : 0));
        },

        "percent-desc": function (a, b) {
            return ((a < b) ? 1 : ((a > b) ? -1 : 0));
        }
    });

    /*******************************
            Data Call Functions
    *******************************/

    function get_ps(){

        $.get( "sh/ps.php", function( data ) {

        	destroy_dataTable('ps_dashboard');
			$('#filter-ps').val('').off('keyup');

            var psTable = $('#ps_dashboard').dataTable({
                "aaData": data,
                "aoColumns": [
                    { "sTitle":"USER","mDataProp":null },
                    { "sTitle":"PID","mDataProp":null },
                    { "sTitle":"%CPU","mDataProp":null },
                    { "sTitle":"%MEM","mDataProp":null },
                    { "sTitle":"VSZ","mDataProp":null },
                    { "sTitle":"RSS","mDataProp":null },
                    { "sTitle":"TTY","mDataProp":null },
                    { "sTitle":"STAT","mDataProp":null },
                    { "sTitle":"START","mDataProp":null },
                    { "sTitle":"TIME","mDataProp":null },
                    { "sTitle":"COMMAND","mDataProp":null }
                ],
                "bPaginate": true,
                "sPaginationType": "full_numbers",
                "bFilter": true,
                "sDom": "lrtip",
                "bAutoWidth": false,
                "bInfo": false
            }).fadeIn();

			$('#filter-ps').on('keyup',function(){
                psTable.fnFilter(this.value);
            });
        }, "json" );

    }

    function get_users(){

        $.get( "sh/users.php", function( data ) {

            destroy_dataTable('users_dashboard');

            $('#users_dashboard').dataTable({
                "aaData": data,
                "aoColumns": [
                    { "sTitle":"Type","mDataProp":null },
                    { "sTitle":"User","mDataProp":null },
                    { "sTitle":"Home","mDataProp":null }
                ],
                 "aaSorting": [[ 0, "desc" ]],
                "iDisplayLength": 5,
                "bPaginate": true,
                "sPaginationType": "full_numbers",
                "bFilter": false,
                "bAutoWidth": false,
                "bInfo": false
            }).fadeIn();

        }, "json" );

     $('select[name="users_dashboard_length"]').val('5');
    }

    function get_online(){

        $.get( "sh/online.php", function( data ) {

            destroy_dataTable('online_dashboard');

            $('#online_dashboard').dataTable({
                "aaData": data,
                "aoColumns": [
                    { "sTitle":"Who","mDataProp":null },
                    { "sTitle":"From","mDataProp":null },
                    { "sTitle":"Login At","mDataProp":null },
                    { "sTitle":"Idle","mDataProp":null }
                ],
                 "aaSorting": [[ 0, "desc" ]],
                "iDisplayLength": 5,
                "bPaginate": true,
                "sPaginationType": "full_numbers",
                "bFilter": false,
                "bAutoWidth": false,
                "bInfo": false
            }).fadeIn();

        }, "json" );

     $('select[name="online_dashboard_length"]').val('5');
    }

    function get_ram(){
        $.get( "sh/mem.php", function( data ) {
          var ram_total = data[1];
          var ram_used = parseInt( (data[2]/ram_total)*100);
          var ram_free = parseInt( (data[3]/ram_total)*100);

          $('#ram-total').text(ram_total);
          $('#ram-used').text(data[2]);
          $('#ram-free').text(data[3]);

          $('#ram-free-per').text( ram_free );
          $('#ram-used-per').text( ram_used );

        }, "json" );
    }

    function get_df(){

        $.get( "sh/df.php", function( data ) {

            var table = $('#df_dashboard');

            var ex = document.getElementById('df_dashboard');
            if ( $.fn.DataTable.fnIsDataTable( ex ) ) {
        		table.hide().dataTable().fnClearTable();
            	table.dataTable( ).fnDestroy();

        	}

            table.dataTable({
                "aaData": data,
                "aoColumns": [
                    { "sTitle":"Filesystem","mDataProp":null },
                    { "sTitle":"Size","mDataProp":null,"sType":"file-size" },
                    { "sTitle":"Used","mDataProp":null ,"sType":"file-size"},
                    { "sTitle":"Avail","mDataProp":null,"sType":"file-size" },
                    { "sTitle":"Use%","mDataProp":null,"sType":"percent" },
                    { "sTitle":"Mounted","mDataProp":null }
                ],
                "bPaginate": false,
                "bFilter": false,
                "bAutoWidth": true,
                "bInfo": false
            }).fadeIn();

        }, "json" );
    }

    function get_whereis(){

        $.get( "sh/whereis.php", function( data ) {

            var table = $('#whereis_dashboard');

            var ex = document.getElementById('whereis_dashboard');
            if ( $.fn.DataTable.fnIsDataTable( ex ) ) {
                table.hide().dataTable().fnClearTable();
                table.dataTable( ).fnDestroy();
            }

            table.dataTable({
                "aaData": data,
                "aoColumns": [
                    { "sTitle":"Software","mDataProp":null },
                    { "sTitle":"Installation","mDataProp":null }
                ],
                "bPaginate": false,
                "bFilter": false,
                "aaSorting": [[ 1, "desc" ]],
                "bAutoWidth": false,
                "bInfo": false
            }).fadeIn();

        }, "json" );
    }

    function get_os_info(){
        generate_os_data('sh/issue.php','#os-info');
        generate_os_data('sh/hostname.php','#os-hostname');
        generate_os_data('sh/uptime.php','#os-uptime');
    }

    function get_ip(){
        $.get( 'sh/ip.php', function( data ) {

            destroy_dataTable('ip_dashboard');

            $('#ip_dashboard').dataTable({
                "aaData": data,
                "aoColumns": [
                    { "sTitle":"Interface","mDataProp":null },
                    { "sTitle":"IP","mDataProp":null }
                ],
                "bPaginate": false,
                "bFilter": false,
                "bAutoWidth": true,
                "bInfo": false
            }).fadeIn();

        }, 'json' );
    }

    function get_ispeed(){
        var rate = $('#ispeed-rate');

        // 0 = MB
        // 1 = KB
        var AS = 1;

        $.get("sh/speed.php?as=" + AS, function (data) {
          rate.text(data);
        });

        var lead = rate.next(".lead");

        if (AS == 0) lead.text("MB/s");
        if (AS == 1) lead.text("KB/s");
    }

    /*
        Function that calls all the other functions which refresh
        each individual widget
    */
   function refresh_all(){
        get_ram();
        get_ps();
        get_df();
        get_os_info();
        get_users();
        get_online();
        get_whereis();
        get_ip();
        get_ispeed();
    }

    // Initialize Page function calls
    refresh_all();
    //page_data_refresh_tick();
    <commit_msg>Fixed sorting of file-size data<commit_after>
    /*******************************
            Abstracted Functions
    *******************************/

    // Gets data from provided url
    // and updates DOM element (element variable)
    function generate_os_data(url, element){
        $.get( url, function( data ) {
          $(element).text(data);
        } );
    }

    // If dataTable with proved ID (table_id)
    // exists, destroy it. Obliterate it.
    // Leave no evidence.
    function destroy_dataTable(table_id){
        var table = $('#'+table_id);

        var ex = document.getElementById(table_id);
        if ( $.fn.DataTable.fnIsDataTable( ex ) ) {
    		table.hide().dataTable().fnClearTable();
        	table.dataTable( ).fnDestroy();
    	}
    }

    // Function and variables to refresh page data &
    // counter on top right of page
    var page_refresh_counter = $('#page_data_refresh');
    function page_data_refresh_tick(){

        if (Number( page_refresh_counter.text() )==0){
            refresh_all();
            page_refresh_counter.fadeOut().text(20).fadeIn();
            setTimeout(function(){ page_data_refresh_tick(); },1000);
            return false;
        }
        else{
            setTimeout(function(){
                page_refresh_counter.text( Number(page_refresh_counter.text())-1);
                page_data_refresh_tick();
            },1000);
        }
    }

    //DataTables
    //Sort file size data.
    jQuery.extend(jQuery.fn.dataTableExt.oSort, {
        "file-size-pre": function (a) {
            var x = a.substring(0, a.length - 1);
            var x_unit = (a.substring(a.length - 1, a.length) == "M" ?
                1000 : (a.substring(a.length - 1, a.length) == "G" ? 1000000 : 1));

            return parseInt(x * x_unit, 10);
        },

        "file-size-asc": function (a, b) {
            return ((a < b) ? -1 : ((a > b) ? 1 : 0));
        },

        "file-size-desc": function (a, b) {
            return ((a < b) ? 1 : ((a > b) ? -1 : 0));
        }
    });

    //DataTables
    //Sort numeric data which has a percent sign with it.
    jQuery.extend(jQuery.fn.dataTableExt.oSort, {
        "percent-pre": function (a) {
            var x = (a == "-") ? 0 : a.replace(/%/, "");
            return parseFloat(x);
        },

        "percent-asc": function (a, b) {
            return ((a < b) ? -1 : ((a > b) ? 1 : 0));
        },

        "percent-desc": function (a, b) {
            return ((a < b) ? 1 : ((a > b) ? -1 : 0));
        }
    });

    /*******************************
            Data Call Functions
    *******************************/

    function get_ps(){

        $.get( "sh/ps.php", function( data ) {

        	destroy_dataTable('ps_dashboard');
			$('#filter-ps').val('').off('keyup');

            var psTable = $('#ps_dashboard').dataTable({
                "aaData": data,
                "aoColumns": [
                    { "sTitle":"USER","mDataProp":null },
                    { "sTitle":"PID","mDataProp":null },
                    { "sTitle":"%CPU","mDataProp":null },
                    { "sTitle":"%MEM","mDataProp":null },
                    { "sTitle":"VSZ","mDataProp":null },
                    { "sTitle":"RSS","mDataProp":null },
                    { "sTitle":"TTY","mDataProp":null },
                    { "sTitle":"STAT","mDataProp":null },
                    { "sTitle":"START","mDataProp":null },
                    { "sTitle":"TIME","mDataProp":null },
                    { "sTitle":"COMMAND","mDataProp":null }
                ],
                "bPaginate": true,
                "sPaginationType": "full_numbers",
                "bFilter": true,
                "sDom": "lrtip",
                "bAutoWidth": false,
                "bInfo": false
            }).fadeIn();

			$('#filter-ps').on('keyup',function(){
                psTable.fnFilter(this.value);
            });
        }, "json" );

    }

    function get_users(){

        $.get( "sh/users.php", function( data ) {

            destroy_dataTable('users_dashboard');

            $('#users_dashboard').dataTable({
                "aaData": data,
                "aoColumns": [
                    { "sTitle":"Type","mDataProp":null },
                    { "sTitle":"User","mDataProp":null },
                    { "sTitle":"Home","mDataProp":null }
                ],
                 "aaSorting": [[ 0, "desc" ]],
                "iDisplayLength": 5,
                "bPaginate": true,
                "sPaginationType": "full_numbers",
                "bFilter": false,
                "bAutoWidth": false,
                "bInfo": false
            }).fadeIn();

        }, "json" );

     $('select[name="users_dashboard_length"]').val('5');
    }

    function get_online(){

        $.get( "sh/online.php", function( data ) {

            destroy_dataTable('online_dashboard');

            $('#online_dashboard').dataTable({
                "aaData": data,
                "aoColumns": [
                    { "sTitle":"Who","mDataProp":null },
                    { "sTitle":"From","mDataProp":null },
                    { "sTitle":"Login At","mDataProp":null },
                    { "sTitle":"Idle","mDataProp":null }
                ],
                 "aaSorting": [[ 0, "desc" ]],
                "iDisplayLength": 5,
                "bPaginate": true,
                "sPaginationType": "full_numbers",
                "bFilter": false,
                "bAutoWidth": false,
                "bInfo": false
            }).fadeIn();

        }, "json" );

     $('select[name="online_dashboard_length"]').val('5');
    }

    function get_ram(){
        $.get( "sh/mem.php", function( data ) {
          var ram_total = data[1];
          var ram_used = parseInt( (data[2]/ram_total)*100);
          var ram_free = parseInt( (data[3]/ram_total)*100);

          $('#ram-total').text(ram_total);
          $('#ram-used').text(data[2]);
          $('#ram-free').text(data[3]);

          $('#ram-free-per').text( ram_free );
          $('#ram-used-per').text( ram_used );

        }, "json" );
    }

    function get_df(){

        $.get( "sh/df.php", function( data ) {

            var table = $('#df_dashboard');

            var ex = document.getElementById('df_dashboard');
            if ( $.fn.DataTable.fnIsDataTable( ex ) ) {
        		table.hide().dataTable().fnClearTable();
            	table.dataTable( ).fnDestroy();

        	}

            table.dataTable({
                "aaData": data,
                "aoColumns": [
                    { "sTitle":"Filesystem","mDataProp":null },
                    { "sTitle":"Size","mDataProp":null,"sType":"file-size" },
                    { "sTitle":"Used","mDataProp":null ,"sType":"file-size"},
                    { "sTitle":"Avail","mDataProp":null,"sType":"file-size" },
                    { "sTitle":"Use%","mDataProp":null,"sType":"percent" },
                    { "sTitle":"Mounted","mDataProp":null }
                ],
                "bPaginate": false,
                "bFilter": false,
                "bAutoWidth": true,
                "bInfo": false
            }).fadeIn();

        }, "json" );
    }

    function get_whereis(){

        $.get( "sh/whereis.php", function( data ) {

            var table = $('#whereis_dashboard');

            var ex = document.getElementById('whereis_dashboard');
            if ( $.fn.DataTable.fnIsDataTable( ex ) ) {
                table.hide().dataTable().fnClearTable();
                table.dataTable( ).fnDestroy();
            }

            table.dataTable({
                "aaData": data,
                "aoColumns": [
                    { "sTitle":"Software","mDataProp":null },
                    { "sTitle":"Installation","mDataProp":null }
                ],
                "bPaginate": false,
                "bFilter": false,
                "aaSorting": [[ 1, "desc" ]],
                "bAutoWidth": false,
                "bInfo": false
            }).fadeIn();

        }, "json" );
    }

    function get_os_info(){
        generate_os_data('sh/issue.php','#os-info');
        generate_os_data('sh/hostname.php','#os-hostname');
        generate_os_data('sh/uptime.php','#os-uptime');
    }

    function get_ip(){
        $.get( 'sh/ip.php', function( data ) {

            destroy_dataTable('ip_dashboard');

            $('#ip_dashboard').dataTable({
                "aaData": data,
                "aoColumns": [
                    { "sTitle":"Interface","mDataProp":null },
                    { "sTitle":"IP","mDataProp":null }
                ],
                "bPaginate": false,
                "bFilter": false,
                "bAutoWidth": true,
                "bInfo": false
            }).fadeIn();

        }, 'json' );
    }

    function get_ispeed(){
        var rate = $('#ispeed-rate');

        // 0 = MB
        // 1 = KB
        var AS = 1;

        $.get("sh/speed.php?as=" + AS, function (data) {
          rate.text(data);
        });

        var lead = rate.next(".lead");

        if (AS == 0) lead.text("MB/s");
        if (AS == 1) lead.text("KB/s");
    }

    /*
        Function that calls all the other functions which refresh
        each individual widget
    */
   function refresh_all(){
        get_ram();
        get_ps();
        get_df();
        get_os_info();
        get_users();
        get_online();
        get_whereis();
        get_ip();
        get_ispeed();
    }

    // Initialize Page function calls
    refresh_all();
    //page_data_refresh_tick();
    <|endoftext|>


<commit_before>/**
 * jQuery Library for Multilingual Press
 *
 * @version        2014.03.24
 * @package		mlp
 *
 * @todo		Doc
 */

jQuery.noConflict();
( function( $ ) {

	/**
	 * Class Holder
	 */
	var multilingual_press = {

		/**
		 * Initial Function
		 */
		init : function () {
			// this.do_blog_checkup();
			this.set_toggle();
			this.copy_post();
		},

		/**
		 * Retrieves all responses from the blog checkup
		 *
		 * @since	0.8
		 */
		do_blog_checkup : function() {
			if ( ! '#multilingual_press_checkup' )
				return;

			$( '#multilingual_press_checkup_link' ).live( 'click', function() {
				var data = {
					action: 'checkup_blogs'
				};

				$.ajax( {
    				url: ajaxurl,
    				data: data,
    				async: true,
    				success: function ( response ) {
    					$( '#multilingual_press_checkup' ).append( response ).delay( 250 ).animate( {
    						backgroundColor: 'lightYellow',
						    borderBottomColor: '#E6DB55',
						    borderLeftColor: '#E6DB55',
    						borderRightColor: '#E6DB55',
    						borderTopColor: '#E6DB55'
    					}, 500 ).delay( 3500 ).slideUp();
    				}
    			} );
			} );
		},

		/**
		 * Toggle handler, show/hide elements with the class 'mlp_toggler'.
		 */
		set_toggle: function () {

			$(document).on("click", ".mlp_toggler", function (event) {

				var toggle_container = $($(this).data('toggle_selector'));

				// buttons and links
				if ('submit' == this.type || 'A' == this.tagName) {
					event.stopPropagation();
					event.preventDefault();
					toggle_container.toggle();
					return false;
				}

				// labels, needs improvements
				if ('LABEL' == this.tagName) {
					var target = $('#' + $(this).attr('for'));

					/* probably not necessary or useful
					 if ( 'radio' != target.attr( 'type' ) )
					 return true;*/

					event.stopPropagation();

					$('input[name="' + target.attr('name') + '"]').change(function () {
						toggle_container.toggle(target.val() == $(this).val());
						return true;
					});

					return true;
				}

				toggle_container.toggle();

				return false;
			});
		},

		/**
		 * Copy post buttons next to media buttons.
		 */
		copy_post: function () {

			$( document ).on( "click", ".mlp_copy_button", function ( event ) {
				event.stopPropagation();
				event.preventDefault();

				// @formatter:off
				var blog_id = $( this ).data( "blog_id" ),
					title   = $( "#title" ).val(),
					content = $( "#content" ).val(),
					prefix  = "mlp_translation_data_" + blog_id,
					mce     = tinyMCE.get( prefix + "_content" );

				if ( title )
					$( "#" + prefix + "_title" ).val( title );

				if ( content ) {
					$( "#" + prefix + "_content" ).val( content );

					if ( mce )
						mce.setContent( content );
				}
				// @formatter:on
			});
		}
	};

	$( document ).ready( function( $ ) {
		multilingual_press.init();
	} );

} )( jQuery );<commit_msg>update js<commit_after>/**
 * jQuery Library for MultilingualPress
 *
 * @version        2014.03.24
 * @package		mlp
 *
 * @todo		Doc
 */

jQuery.noConflict();
( function( $ ) {

	/**
	 * Class Holder
	 */
	var multilingual_press = {

		/**
		 * Initial Function
		 */
		init : function () {
			// this.do_blog_checkup();
			this.set_toggle();
			this.copy_post();
		},

		/**
		 * Retrieves all responses from the blog checkup
		 *
		 * @since	0.8
		 */
		do_blog_checkup : function() {
			if ( ! '#multilingual_press_checkup' )
				return;

			$( '#multilingual_press_checkup_link' ).live( 'click', function() {
				var data = {
					action: 'checkup_blogs'
				};

				$.ajax( {
    				url: ajaxurl,
    				data: data,
    				async: true,
    				success: function ( response ) {
    					$( '#multilingual_press_checkup' ).append( response ).delay( 250 ).animate( {
    						backgroundColor: 'lightYellow',
						    borderBottomColor: '#E6DB55',
						    borderLeftColor: '#E6DB55',
    						borderRightColor: '#E6DB55',
    						borderTopColor: '#E6DB55'
    					}, 500 ).delay( 3500 ).slideUp();
    				}
    			} );
			} );
		},

		/**
		 * Toggle handler, show/hide elements with the class 'mlp_toggler'.
		 */
		set_toggle: function () {

			$(document).on("click", ".mlp_toggler", function (event) {

				var toggle_container = $($(this).data('toggle_selector'));

				// buttons and links
				if ('submit' == this.type || 'A' == this.tagName) {
					event.stopPropagation();
					event.preventDefault();
					toggle_container.toggle();
					return false;
				}

				// labels, needs improvements
				if ('LABEL' == this.tagName) {
					var target = $('#' + $(this).attr('for'));

					/* probably not necessary or useful
					 if ( 'radio' != target.attr( 'type' ) )
					 return true;*/

					event.stopPropagation();

					$('input[name="' + target.attr('name') + '"]').change(function () {
						toggle_container.toggle(target.val() == $(this).val());
						return true;
					});

					return true;
				}

				toggle_container.toggle();

				return false;
			});
		},

		/**
		 * Copy post buttons next to media buttons.
		 */
		copy_post: function () {

			$( document ).on( "click", ".mlp_copy_button", function ( event ) {
				event.preventDefault();

				// @formatter:off
				var blog_id		= $( this ).data( "blog_id" ),
					prefix		= "mlp_translation_data_" + blog_id,
					mce 		= tinyMCE.get( prefix + "_content" ),
					content		= $( '#content' ).val(), // plain content for "text"-view
					title		= $( "#title" ).val()
				;

				if ( title )
					$( "#" + prefix + "_title" ).val( title );

				if ( content ) {
					$( "#" + prefix + "_content" ).val( content );
					mce.setContent( content );
				}
				// @formatter:on
			});
		}
	};

	$( document ).ready( function( $ ) {
		multilingual_press.init();
	} );

} )( jQuery );<|endoftext|>


<commit_before>let $ = require('jquery'),
    _ = require('underscore'),
    React = require('react'),
    Modals = require('../Modals'),
    Actions = require('../../actions/Actions'),
    Header = require('./Header'),
    ValueBinding = require('./values/ValueBinding'),
    NumberValueEditor = require('./values/NumberValueEditor'),
    BooleanValueEditor = require('./values/BooleanValueEditor'),
    ComponentPanel = require('./ComponentPanel'),
    AddComponentPanel = require('./AddComponentPanel'),
    Components = require('../../chaos/Components'),
    ComponentPicker = require('./ComponentPicker');

class SettingsPage extends React.Component {
    render() {
        let component = this.props.component;
        let controls = [];

        if (component && component.type.params) {
            for (let param of component.type.params) {
                controls.push(...this._createControls(component, param));
            }
        }

        return (

                {controls}

        );
    }

    _createControls(component, params) {
        let binding = new ValueBinding(component, params.property);

        let items;
        let hasSubProps;

        switch (params.type) {
            case 'number':
                return [
                    <NumberValueEditor
                        target={component}
                        {...params}
                    />
                ];

            case 'boolean':
                return [
                    <BooleanValueEditor
                        binding={binding}
                        label={params.label}
                        />
                ];

            case 'group':
                items = [];
                items.push();
                for (let prop of params.properties) {
                    items.push(...this._createControls(component, prop));
                }
                return items;

            case 'component':
                hasSubProps = !!binding.val.type.params;

                return [
                    ,
                    <ComponentPanel
                        binding={binding}
                        componentType={params.componentType}
                        showArrow={hasSubProps}
                        icon="icon-more-horiz"
                        onIconClick={this._changeComponent.bind(this)}
                        onPanelClick={this._editComponent.bind(this)}
                        />
                ];

            case 'componentSet':
                let controls = [  ];
                items = binding.val || [];

                for (let index in items) {
                    let componentBinding = new ValueBinding(items, index);
                    hasSubProps = !!componentBinding.val.type.params;
                    controls.push(
                        <ComponentPanel
                            onIconClick={this._removeComponent.bind(this)}
                            onPanelClick={this._editComponent.bind(this)}
                            binding={componentBinding}
                            componentType={params.componentType}
                            showArrow={hasSubProps}
                            icon="icon-delete"
                            />
                    );
                }

                let supportedTypes = Components.findTypes(params.componentType);
                if (items.length < supportedTypes.length) {
                    controls.push(
                        <AddComponentPanel
                            binding={binding}
                            componentType={params.componentType}
                            onClick={this._addComponent.bind(this)}
                            />
                    );
                }

                return controls;
        }

        return [];
    }

    _removeComponent(binding) {
        binding.target.splice(binding.prop, 1);
        this.forceUpdate();
    }

    _addComponent(binding, componentType) {
        let items = binding.val || [];
        let supportedTypes = _.without(Components.findTypes(componentType), items);
        let self = this;

        let valueChanged = TComponent => {
            items.push(new TComponent());
            binding.val = items;
            self.forceUpdate();
        };

        Actions.SHOW_MODAL.invoke(
            <ComponentPicker
                types={supportedTypes}
                onValueChanged={valueChanged}
                />
        );
    }

    _changeComponent(binding, componentType) {
        let supportedTypes = Components.findTypes(componentType);
        let self = this;

        let valueChanged = TComponent => {
            binding.val = new TComponent();
            self.forceUpdate();
        };

        Actions.SHOW_MODAL.invoke(
            <ComponentPicker
                selected={binding.val}
                types={supportedTypes}
                onValueChanged={valueChanged}
                />
        );
    }

    _editComponent(binding) {
        this.props.onEditComponent(binding.val);
    }

    static get propTypes() {
        return {
            onEditComponent: React.PropTypes.func.isRequired,
            component: React.PropTypes.object,
            className: React.PropTypes.string,
            key: React.PropTypes.string
        }
    }
}

module.exports = SettingsPage;<commit_msg>Fix component list type filtering<commit_after>let _ = require('underscore'),
    React = require('react'),
    Modals = require('../Modals'),
    Actions = require('../../actions/Actions'),
    Header = require('./Header'),
    ValueBinding = require('./values/ValueBinding'),
    NumberValueEditor = require('./values/NumberValueEditor'),
    BooleanValueEditor = require('./values/BooleanValueEditor'),
    ComponentPanel = require('./ComponentPanel'),
    AddComponentPanel = require('./AddComponentPanel'),
    Components = require('../../chaos/Components'),
    ComponentPicker = require('./ComponentPicker');

class SettingsPage extends React.Component {
    render() {
        let component = this.props.component;
        let controls = [];

        if (component && component.type.params) {
            for (let param of component.type.params) {
                controls.push(...this._createControls(component, param));
            }
        }

        return (

                {controls}

        );
    }

    _createControls(component, params) {
        let binding = new ValueBinding(component, params.property);

        let items;
        let hasSubProps;

        switch (params.type) {
            case 'number':
                return [
                    <NumberValueEditor
                        target={component}
                        {...params}
                    />
                ];

            case 'boolean':
                return [
                    <BooleanValueEditor
                        binding={binding}
                        label={params.label}
                        />
                ];

            case 'group':
                items = [];
                items.push();
                for (let prop of params.properties) {
                    items.push(...this._createControls(component, prop));
                }
                return items;

            case 'component':
                hasSubProps = !!binding.val.type.params;

                return [
                    ,
                    <ComponentPanel
                        binding={binding}
                        componentType={params.componentType}
                        showArrow={hasSubProps}
                        icon="icon-more-horiz"
                        onIconClick={this._changeComponent.bind(this)}
                        onPanelClick={this._editComponent.bind(this)}
                        />
                ];

            case 'componentSet':
                let controls = [  ];
                items = binding.val || [];

                for (let index in items) {
                    let componentBinding = new ValueBinding(items, index);
                    hasSubProps = !!componentBinding.val.type.params;
                    controls.push(
                        <ComponentPanel
                            onIconClick={this._removeComponent.bind(this)}
                            onPanelClick={this._editComponent.bind(this)}
                            binding={componentBinding}
                            componentType={params.componentType}
                            showArrow={hasSubProps}
                            icon="icon-delete"
                            />
                    );
                }

                let supportedTypes = Components.findTypes(params.componentType);
                if (items.length < supportedTypes.length) {
                    controls.push(
                        <AddComponentPanel
                            binding={binding}
                            componentType={params.componentType}
                            onClick={this._addComponent.bind(this)}
                            />
                    );
                }

                return controls;
        }

        return [];
    }

    _removeComponent(binding) {
        binding.target.splice(binding.prop, 1);
        this.forceUpdate();
    }

    _addComponent(binding, componentType) {
        let items = binding.val || [];
        let itemTypes = _.map(items, item => item.type);
        let supportedTypes = _.difference(Components.findTypes(componentType), itemTypes);
        let self = this;

        let valueChanged = TComponent => {
            items.push(new TComponent());
            binding.val = items;
            self.forceUpdate();
        };

        Actions.SHOW_MODAL.invoke(
            <ComponentPicker
                types={supportedTypes}
                onValueChanged={valueChanged}
                />
        );
    }

    _changeComponent(binding, componentType) {
        let supportedTypes = Components.findTypes(componentType);
        let self = this;

        let valueChanged = TComponent => {
            binding.val = new TComponent();
            self.forceUpdate();
        };

        Actions.SHOW_MODAL.invoke(
            <ComponentPicker
                selected={binding.val}
                types={supportedTypes}
                onValueChanged={valueChanged}
                />
        );
    }

    _editComponent(binding) {
        this.props.onEditComponent(binding.val);
    }

    static get propTypes() {
        return {
            onEditComponent: React.PropTypes.func.isRequired,
            component: React.PropTypes.object,
            className: React.PropTypes.string,
            key: React.PropTypes.string
        }
    }
}

module.exports = SettingsPage;<|endoftext|>


<commit_before>/**
 * Created by irmo on 16/10/11.
 */

'use strict';

import React, {Component, PropTypes} from 'react';
import {
  StyleSheet,
  Text,
  View,
  ListView,
  TouchableHighlight,
  AsyncStorage,
  Image,
  ImageStore,
  formatDate,
  StatusBar,
  AlertIOS,
} from 'react-native';
import dateFormat from 'dateformat';
import Icon from 'react-native-vector-icons/FontAwesome';
import DiaryActions from './../actions';
let ds;

export default class TimelineScene extends Component {
  static propTypes = {
    navigator: PropTypes.object.isRequired,
  };

  constructor(props, context) {
    super(props, context);
    ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    this.state = {
      dataSource: ds.cloneWithRows([]),
      rows: [],
      DIARY_KEY: '@Bref:diaries'
    };
    this.listView = null
    this.listViewItem = []
  }

  componentWillMount() {
    this._refreshData().done();

  }

  _deleteItem(rowID) {

    DiaryActions.deleteDiary(this.state.rows[rowID]);
    delete this.state.rows[rowID];
    this.setState({dataSource: ds.cloneWithRows(this.state.rows)});
  }

  _scrollToTop() {
    if (this.listView != null){
      this.listView.scrollTo({y: 0});
    }
  }

  _scrollToBottom() {

  }

  _deleteStatus(rowID) {

    let ID = 7;
    let ID_y = 0;
    this.listViewItem[ID].measure((x, y, width, height, pageX, pageY) => {
      ID_y = y;
      this.listView.scrollTo({y: ID_y});
      AlertIOS.alert(x.toString() + " " + y.toString() + " " + width.toString() + " " + height.toString() + " " + pageX.toString() + " " + pageY.toString())
    });

    AlertIOS.alert(
      'Confirm deletion?',
      '',
      [
        {text: 'Delete', onPress: () => this._deleteItem(rowID)},
        {
          text: 'Cancel', onPress: () => {
          console.log('Cancel deletion');
          //this.listView.scrollTo({y: 0});
        }
        },
      ],
    );
  }

  _refreshData = async() => {
    let data = await AsyncStorage.getItem(this.state.DIARY_KEY);
    let JSONdata = (JSON.parse(data)).reverse();
    this.setState({dataSource: this.state.dataSource.cloneWithRows(JSONdata)});
    this.setState({rows: JSONdata});

  }

  _renderRow(rowData, sectionID, rowID) {
    let oneImage;
    if (rowData.imageUrl !== null) {
      oneImage = (
        <TouchableHighlight style={ styles.imageContainer }>
          <Image style={ styles.image } source={{uri: rowData.imageUrl}}/>
        </TouchableHighlight>
      );
    }
    let date = new Date(rowData.timeStamp);
    return (
      <View style={styles.item} ref={ref => this.listViewItem[rowID] = ref}>
        <View style={styles.date}>
          <Text style={styles.monthText}>
            {dateFormat(date, 'mmm')}.
          </Text>
          <Text style={styles.dayText}>
            {dateFormat(date, 'dd')}
          </Text>
        </View>

        <View style={styles.card}>
          <View style={{flex: 5}}>
            <Text style={styles.timelineText}>
              {rowData.text}
            </Text>
            <Text style={styles.timelineOthers}>
              <Icon name="clock-o"/>
              {dateFormat(date, 'HH:MM:ss')}
              <Icon name="map-marker"/>
              {rowData.city}
              <TouchableHighlight style={{width: 55, height: 15}}
                                  onPress={() => {
                                    this._deleteStatus(rowID);
                                  }}>
                <Text style={styles.buttonText}>Delete </Text>
              </TouchableHighlight>
            </Text>
          </View>
          <View style={{flex: 1, alignItems: 'flex-end'}}>
            {oneImage}
          </View>
        </View>
      </View>
    )
  }

  render() {

    return (
      <View style={styles.container}>
        <StatusBar backgroundColor="#FFFFFF" barStyle="light-content"/>
        <TouchableHighlight onPress={() => this.props.navigator.pop()}>
          <Text style={styles.backButtonText}>Back</Text>
        </TouchableHighlight>

        <View style={styles.content}>
          <TouchableHighlight
            style={[styles.button, {width: 100, marginLeft: 150}]}
            underlayColor={'gray'}
            activeOpacity={0.5}
            onPress={() => {
              this.props.navigator.pop()
            }}>
            <Text style={styles.buttonText}>Search By Date</Text>
          </TouchableHighlight>

          <TouchableHighlight
            style={[styles.button, {width: 30, marginLeft: 0}]}
            underlayColor={'gray'}
            activeOpacity={0.5}
            onPress={() => this._scrollToTop()}>
            <Text style={styles.buttonText}>Top</Text>
          </TouchableHighlight>

          <TouchableHighlight
            style={[styles.button, {width: 60, marginLeft: 0}]}
            underlayColor={'gray'}
            activeOpacity={0.5}
            onPress={() => this._scrollToTop()}>
            <Text style={styles.buttonText}>Bottom</Text>
          </TouchableHighlight>
        </View>

        <ListView
          ref={ref => this.listView = ref}
          dataSource={this.state.dataSource}
          enableEmptySections={true}
          renderRow={(rowData, sectionID, rowID) => this._renderRow(rowData, sectionID, rowID)}
        />

      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 10,
    backgroundColor: '#202020',
    justifyContent: 'center'
  },
  backButtonText: {
    color: '#FFFFFF',
    fontSize: 50
  },
  item: {
    marginLeft: 10,
    marginRight: 10,
    marginTop: 20,
    flexDirection: 'row',
    alignItems: 'flex-start',
    justifyContent: 'center'
  },
  date: {
    flex: 1,
    justifyContent: 'flex-start',
    alignItems: 'center'
  },
  monthText: {
    color: '#AFAFAF',
    fontSize: 14
  },
  dayText: {
    color: 'white',
    fontSize: 23
  },
  card: {
    padding: 10,
    marginLeft: 10,
    backgroundColor: 'black',
    borderRadius: 2,
    shadowColor: 'black',
    shadowOpacity: 0.5,
    shadowRadius: 1,
    shadowOffset: {
      height: 1,
      width: 0.3,
    },
    flexDirection: 'row',
    flex: 9
  },
  timelineText: {
    color: '#FFFFFF',
    fontSize: 14,
    lineHeight: 16
  },
  timelineOthers: {
    color: '#AFAFAF',
    fontSize: 12,
    marginTop: 5
  },
  imageContainer: {
    marginLeft: 5,
    height: 40,
    width: 40,
    borderRadius: 20,
    backgroundColor: 'grey',
  },
  image: {
    marginRight: 10,
    height: 40,
    width: 40,
    borderRadius: 20,
  },
  buttonText: {
    marginLeft: 0,
    marginTop: 3.5,
    fontSize: 12,
    textAlign: 'center',
    color: '#AFAFAF',
    fontWeight: 'bold'
  },
  button: {
    marginTop: 0,
    height: 25,
    borderTopLeftRadius: 4,
    borderTopRightRadius: 4,
    borderBottomLeftRadius: 4,
    borderBottomRightRadius: 4,
    backgroundColor: '#202020'
  },
  content: {

    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'center'
  }
});<commit_msg>Support Timeline Scroll to Top and Bottom Now<commit_after>/**
 * Created by irmo on 16/10/11.
 */

'use strict';

import React, {Component, PropTypes} from 'react';
import {
  StyleSheet,
  Text,
  View,
  ListView,
  TouchableHighlight,
  AsyncStorage,
  Image,
  ImageStore,
  formatDate,
  StatusBar,
  AlertIOS,
} from 'react-native';
import dateFormat from 'dateformat';
import Icon from 'react-native-vector-icons/FontAwesome';
import DiaryActions from './../actions';
let ds;

export default class TimelineScene extends Component {
  static propTypes = {
    navigator: PropTypes.object.isRequired,
  };

  constructor(props, context) {
    super(props, context);
    ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
    this.state = {
      dataSource: ds.cloneWithRows([]),
      rows: [],
      DIARY_KEY: '@Bref:diaries'
    };
    this.listView = null;
    this.listViewItem = [];
    this.listViewHeight = 0;
    this.t_scrolly = 0;
  }

  componentWillMount() {
    this._refreshData().done();

  }

  _deleteItem(rowID) {

    DiaryActions.deleteDiary(this.state.rows[rowID]);
    delete this.state.rows[rowID];
    this.setState({dataSource: ds.cloneWithRows(this.state.rows)});
  }

  _scrollToTop() {
    if (this.listView != null && this.state.rows.length != 0){
      this.listView.scrollTo({y: 0});
    }
  }

  _scrollToBottom() {
    if (this.listView != null && this.state.rows.length != 0) { //now rows
      let last_row_id = this.state.rows.length - 1;

      this.listViewItem[last_row_id].measure((t_x, t_y, t_width, t_height, t_pageX, t_pageY) => {
        this.t_scroll_y = t_y + t_height - this.listViewHeight;
      });
      this.listView.scrollTo({y: this.t_scroll_y});
    }
  }

  _deleteStatus(rowID) {
    AlertIOS.alert(
      'Confirm deletion?',
      '',
      [
        {text: 'Delete', onPress: () => this._deleteItem(rowID)},
        {text: 'Cancel', onPress: () => console.log('Cancel deletion')},
      ],
    );
  }

  _refreshData = async() => {
    let data = await AsyncStorage.getItem(this.state.DIARY_KEY);
    let JSONdata = (JSON.parse(data)).reverse();
    this.setState({dataSource: this.state.dataSource.cloneWithRows(JSONdata)});
    this.setState({rows: JSONdata});

  }

  _renderRow(rowData, sectionID, rowID) {
    let oneImage;
    if (rowData.imageUrl !== null) {
      oneImage = (
        <TouchableHighlight style={ styles.imageContainer }>
          <Image style={ styles.image } source={{uri: rowData.imageUrl}}/>
        </TouchableHighlight>
      );
    }
    let date = new Date(rowData.timeStamp);
    return (
      <View style={styles.item} ref={ref => this.listViewItem[rowID] = ref}>
        <View style={styles.date}>
          <Text style={styles.monthText}>
            {dateFormat(date, 'mmm')}.
          </Text>
          <Text style={styles.dayText}>
            {dateFormat(date, 'dd')}
          </Text>
        </View>

        <View style={styles.card}>
          <View style={{flex: 5}}>
            <Text style={styles.timelineText}>
              {rowData.text}
            </Text>
            <Text style={styles.timelineOthers}>
              <Icon name="clock-o"/>
              {dateFormat(date, 'HH:MM:ss')}
              <Icon name="map-marker"/>
              {rowData.city}
              <TouchableHighlight style={{width: 55, height: 15}}
                                  onPress={() => {
                                    this._deleteStatus(rowID);
                                  }}>
                <Text style={styles.buttonText}>Delete </Text>
              </TouchableHighlight>
            </Text>
          </View>
          <View style={{flex: 1, alignItems: 'flex-end'}}>
            {oneImage}
          </View>
        </View>
      </View>
    )
  }

  render() {

    return (
      <View style={styles.container }>
        <StatusBar backgroundColor="#FFFFFF" barStyle="light-content"/>
        <TouchableHighlight onPress={() => this.props.navigator.pop()}>
          <Text style={styles.backButtonText}>Back</Text>
        </TouchableHighlight>

        <View style={styles.content}>
          <TouchableHighlight
            style={[styles.button, {width: 100, marginLeft: 150}]}
            underlayColor={'gray'}
            activeOpacity={0.5}
            onPress={() => {
              this.props.navigator.pop()
            }}>
            <Text style={styles.buttonText}>Search By Date</Text>
          </TouchableHighlight>

          <TouchableHighlight
            style={[styles.button, {width: 30, marginLeft: 0}]}
            underlayColor={'gray'}
            activeOpacity={0.5}
            onPress={() => this._scrollToTop()}>
            <Text style={styles.buttonText}>Top</Text>
          </TouchableHighlight>

          <TouchableHighlight
            style={[styles.button, {width: 60, marginLeft: 0}]}
            underlayColor={'gray'}
            activeOpacity={0.5}
            onPress={() => { this._scrollToBottom(); this._scrollToBottom();}}>
            <Text style={styles.buttonText}>Bottom</Text>
          </TouchableHighlight>
        </View>

        <ListView
          ref={ref => this.listView = ref}
          initialListSize={10}
          onLayout={(event) => {
            let layout = event.nativeEvent.layout;
            this.listViewHeight = layout.height;
          }}
          dataSource={this.state.dataSource}
          enableEmptySections={true}
          renderRow={(rowData, sectionID, rowID) => this._renderRow(rowData, sectionID, rowID)}
        />

      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 10,
    backgroundColor: '#202020',
    justifyContent: 'center'
  },
  backButtonText: {
    color: '#FFFFFF',
    fontSize: 50
  },
  item: {
    marginLeft: 10,
    marginRight: 10,
    marginTop: 20,
    flexDirection: 'row',
    alignItems: 'flex-start',
    justifyContent: 'center'
  },
  date: {
    flex: 1,
    justifyContent: 'flex-start',
    alignItems: 'center'
  },
  monthText: {
    color: '#AFAFAF',
    fontSize: 14
  },
  dayText: {
    color: 'white',
    fontSize: 23
  },
  card: {
    padding: 10,
    marginLeft: 10,
    backgroundColor: 'black',
    borderRadius: 2,
    shadowColor: 'black',
    shadowOpacity: 0.5,
    shadowRadius: 1,
    shadowOffset: {
      height: 1,
      width: 0.3,
    },
    flexDirection: 'row',
    flex: 9
  },
  timelineText: {
    color: '#FFFFFF',
    fontSize: 14,
    lineHeight: 16
  },
  timelineOthers: {
    color: '#AFAFAF',
    fontSize: 12,
    marginTop: 5
  },
  imageContainer: {
    marginLeft: 5,
    height: 40,
    width: 40,
    borderRadius: 20,
    backgroundColor: 'grey',
  },
  image: {
    marginRight: 10,
    height: 40,
    width: 40,
    borderRadius: 20,
  },
  buttonText: {
    marginLeft: 0,
    marginTop: 3.5,
    fontSize: 12,
    textAlign: 'center',
    color: '#AFAFAF',
    fontWeight: 'bold'
  },
  button: {
    marginTop: 0,
    height: 25,
    borderTopLeftRadius: 4,
    borderTopRightRadius: 4,
    borderBottomLeftRadius: 4,
    borderBottomRightRadius: 4,
    backgroundColor: '#202020'
  },
  content: {

    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'center'
  }
});<|endoftext|>


<commit_before>/*jslint bitwise: true, plusplus: true, white: true, sub: true, nomen: true*/
/*global timeUtil, console, self:true, _, parseHistory*/

const DEBUG_COLUMNHISTORY = false;

const CATEGORY_DELAY = "Delay";
const CATEGORY_IGNORE = "Ignore";
const CATEGORY_EXECUTION = "Execution";
const CATEGORY_DONE = "Done";

/**
 * Create a new instance of a board design object.
 * Will detect whether the incoming board design is raw data from API or has already been parsed.
 */
function createBoardDesign(boardDesignToParse) {
    let boardDesign;

    if (boardDesignToParse.columnConfig) {
        // Incoming board design is raw data from API and contains the "columnConfig" property.
        delete boardDesignToParse.columnConfig.constraintType; // Not interested in this property
        boardDesign = new BoardDesign(boardDesignToParse.columnConfig, boardDesignToParse.name);
    } else {
        // Incomfing board design has been parsed already and instead stores the column config in a "columns" property.
        boardDesign = new BoardDesign(boardDesignToParse, boardDesignToParse.name);
    }

    return boardDesign;
}

/**
 * Current board structure with columns and their underlying statuses.
 */
function BoardDesign(apiColumnsData, boardName) {
    "use strict";

    let self = _.cloneDeep(apiColumnsData);

    self.getColumnNames = function () {
        let columnNames = [];
        _.forEach(self.columns, function (column) {
            columnNames.push(column.name);
        });
        return columnNames;
    };

    self.getColumnMatchingStatus = function (status) {
        let columnName = null;
        _.forEach(self.columns, function (column) {
            _.forEach(column.statuses, function (columnStatus) {
                if (columnStatus.id === status) {
                    columnName = column.name;
                    return false; // break loop
                }
            });
        });
        return columnName;
    };

    /**
     * Try to guess initial values of the column categories based on the column name.
     */
    self.createColumnCategories = function () {
        let hasCategories;

        hasCategories = _.first(self.columns).hasOwnProperty("category");

        if (!hasCategories) {
            _.forEach(self.columns, function (column) {
                if (column.name.toLowerCase().indexOf("ready") >= 0) {
                    column.category = CATEGORY_DELAY;
                } else if (column.name.toLowerCase().indexOf("backlog") >= 0) {
                    column.category = CATEGORY_IGNORE;
                } else if (column.name.toLowerCase().trim().indexOf("todo") >= 0) {
                    column.category = CATEGORY_IGNORE;
                } else {
                    column.category = CATEGORY_EXECUTION;
                }
            });

            _.first(self.columns).category = CATEGORY_DELAY;
            _.last(self.columns).category = CATEGORY_DONE;
        }

        return self.columns;
    };

    self.getColumnCategory = function (columnName) {
        let category = "";
        _.forEach(self.columns, function (column) {
            if (column.name === columnName) {
                category = column.category;
                return false; // break here, assuming there are no duplicate column names
            }
        });
        return category;
    };

    self.isIncludedInBoardDesign = function (columnName) {
        let result = false;
        _.forEach(self.columns, function (column) {
            if (column.name === columnName) {
                result = true;
                return false; // break here, column match found
            }
        });
        return result;
    };

    self.isIgnoreColumn = function (columnName) {
        return self.getColumnCategory(columnName) === CATEGORY_IGNORE;
    };

    self.isExecutionColumn = function (columnName) {
        return self.getColumnCategory(columnName) === CATEGORY_EXECUTION;
    };

    self.isDelayColumn = function (columnName) {
        return self.getColumnCategory(columnName) === CATEGORY_DELAY;
    };

    self.isDoneColumn = function (columnName) {
        let result = false;
        if (columnName) {
            result = self.getColumnCategory(columnName) === CATEGORY_DONE;
        }
        return result;
    };

    self.name = boardName;
    self.createColumnCategories();

    return self;
}

function Moves(histories, boardDesign) {
    "use strict";

    let moves = [];

    _.forEach(histories, function (event) {
        let moveTime = event.created.substr(0, event.created.indexOf('.'));
        _.forEach(event.items, function (eventItem) {
            if (eventItem.field === "status") {
                let fromColumn = boardDesign.getColumnMatchingStatus(eventItem.from),
                    toColumn = boardDesign.getColumnMatchingStatus(eventItem.to);
                moves.push(new MoveItem(fromColumn, toColumn, moveTime));
            }
        });
    });

    return moves;
}

function MoveItem(fromColumn, toColumn, moveTime) {
    "use strict";

    let self = this;
    self.fromColumn = fromColumn;
    self.toColumn = toColumn;
    self.moveTime = moveTime;
    return self;
}

function ColumnHistoryItem(columnName, enterTime, exitTime) {
    "use strict";

    let self = this;
    self.columnName = columnName;
    self.enterTime = enterTime;
    self.exitTime = exitTime;

    self.timeSpentInColumn = function () {
        return Date.parse(self.exitTime) - Date.parse(self.enterTime);
    };

    return self;
}

/**
 * Narrows down raw api issues data to only the issues as objects in a json array.
 */
function parseMultipleApiIssues(apiIssuesRaw) {
    "use strict";

    return apiIssuesRaw.issues;
}

/**
 * Constructs new issues from an array of issue data.
 */
function createIssuesFromArray(apiIssues, boardDesign) {
    let issues = [];

    _.forEach(apiIssues, function (issue) {
        let parsedIssue = new Issue(issue, boardDesign);
        if (parsedIssue.columnHistory.length > 0) {
            issues.push(parsedIssue);
        }
    });

    return issues;
}

/**
 * Create new issue object containing ID, summary, key, column history.
 */
function Issue(apiIssue, boardDesign, time) {
    "use strict";

    let self = {},
        isAlreadyParsed = apiIssue.changelog == null;

    if (!time) {
        time = timeUtil.getTimestamp();
    }

    function parseCurrentStatus(currentStatus) {
        let status = {};
        status.id = currentStatus.id;
        status.name = currentStatus.name;
        return status;
    }

    function parseApiIssueHistories(apiIssueHistories) {
        return new Moves(apiIssueHistories, boardDesign);
    }

    function getStartColumn(apiIssue) {
        return new ColumnHistoryItem(boardDesign.getColumnMatchingStatus(apiIssue.fields.status.id), self.created, time);
    }

    function createColumnHistoryAlreadyParsed(parsedIssue) {
        let columnHistory = [];

        _.forEach(parsedIssue.columnHistory, function (item) {
            columnHistory.push(new ColumnHistoryItem(item.columnName, item.enterTime, item.exitTime));
        });

        return columnHistory;
    }

    function createColumnHistory(apiIssue) {
        let columnHistory = [],
            issueHasHistory = !(apiIssue.changelog.histories.length === 0),
            issueHasStatusChangeInHistory = false;

        _.forEach(apiIssue.changelog.histories, function (event) {
            _.forEach(event.items, function (item) {
                if (item.field === "status") {
                    //console.log(apiIssue.key + " ||| StatusChange: false | FirstColumn: " + boardDesign.getColumnMatchingStatus(apiIssue.fields.status.id));
                    issueHasStatusChangeInHistory = true;
                    return false;
                }
            });
        });

        if (!issueHasHistory || !issueHasStatusChangeInHistory) {
            //console.log(apiIssue.key + " ||| History: " + issueHasHistory + " | StatusChange: " + issueHasStatusChangeInHistory + " | FirstColumn: " + boardDesign.getColumnMatchingStatus(apiIssue.fields.status.id));
            let startColumn = getStartColumn(apiIssue);
            if (isIncludedInBoardDesign(startColumn.columnName)) {
                columnHistory.push(startColumn);
            }
        } else {
            columnHistory = parseMoves(parseApiIssueHistories(apiIssue.changelog.histories));
        }

        return columnHistory;
    }

    /**
     * Format the parsed history into [COLUMN, ENTER, EXIT].
     * Example: [{"columnName":"Ready to Refine", "enterTime":"2015-09-01T14:42:01", "exitTime":"2015-09-01T14:42:23"}]
     */
    function parseMoves(columnHistory) {
        let i,
            createdTime = apiIssue.fields.created.substr(0, apiIssue.fields.created.indexOf('.')),
            columnsWithEnterExit = [];

        // First item is a special case because we need to set the time which the issue was created as enter time.
        if (isIncludedInBoardDesign(columnHistory[0].fromColumn)) {
            columnsWithEnterExit.push(new ColumnHistoryItem(columnHistory[0].fromColumn, createdTime, columnHistory[0].moveTime));
            if (DEBUG_COLUMNHISTORY) {
                console.log("ITERATION: 0 - " + columnHistory[0].fromColumn + " | " + createdTime + " | " + columnHistory[0].moveTime);
            }
        }

        // Events in the middle.
        for (i = 1; i < columnHistory.length; i++) {
            if (isIncludedInBoardDesign(columnHistory[i].fromColumn)) {
                columnsWithEnterExit.push(new ColumnHistoryItem(columnHistory[i].fromColumn, columnHistory[i - 1].moveTime, columnHistory[i].moveTime));
                if (DEBUG_COLUMNHISTORY) {
                    console.log("ITERATION: " + i + " - " + columnHistory[i].fromColumn + " | " + columnHistory[i - 1].moveTime + " | " + columnHistory[i].moveTime);
                }
            }
        }

        // Last item is a special case because toColumn must become its own object and has no real exit time.
        if (isIncludedInBoardDesign(columnHistory[columnHistory.length - 1].toColumn)) {
            columnsWithEnterExit.push(new ColumnHistoryItem(columnHistory[columnHistory.length - 1].toColumn, columnHistory[columnHistory.length - 1].moveTime, time));
            if (DEBUG_COLUMNHISTORY) {
                console.log("ITERATION: " + columnHistory.length + " - " + columnHistory[columnHistory.length - 1].toColumn + " | " + columnHistory[columnHistory.length - 1].moveTime + " | " + time);
            }
        }

        return columnsWithEnterExit;
    }

    function isIncludedInBoardDesign(columnName) {
        let result = false;
        if (columnName) {
            result = boardDesign.isIncludedInBoardDesign(columnName);
        }
        return result;
    }

    function isExecutionColumn(columnName) {
        return boardDesign.isExecutionColumn(columnName);
    }

    function isDelayColumn(columnName) {
        return boardDesign.isDelayColumn(columnName);
    }

    /**
     * Checks if issue was done and then reopened again. These issues need to be handled differently, as the time spent
     * in the Done column is effectively delay time.
     *
     * @returns {boolean} true if column has been reopened, false otherwise.
     */
    function issueWasReopened () {
        function wasInDoneColumn() {
            let numberOfInstances = 0;
            let columnHistory = _.cloneDeep(self.columnHistory);

            _.forEach(columnHistory, function (columnHistoryItem) {
                if (boardDesign.isDoneColumn(columnHistoryItem.columnName)) {
                    numberOfInstances++;
                }
            });
            return self.isDone ? numberOfInstances > 1 : numberOfInstances > 0;
        }

        return wasInDoneColumn();
    }

    /**
     * Check if issue is in a column which is Done category.
     */
    function issueIsDone () {
        let result = false;
        if (self.columnHistory.length > 0) {
            let columnName = _.last(self.columnHistory).columnName;
            result = boardDesign.isDoneColumn(columnName);
        }
        return result;
    }

    /**
     * Check if issue is in a column which should be ignored in calculations.
     */
    self.isIgnored = function (columnName) {
        return boardDesign.isIgnoreColumn(columnName);
    };

    /**
     * Calculate Cycle Time for the issue.
     */
    function getCycleTime() {
        let cycleTime = 0,
            columnHistory = _.cloneDeep(self.columnHistory);

        if (self.isDone) {
            columnHistory.pop(); // Remove the last Done column from calculation
            _.forEach(columnHistory, function (item) {
                if (!self.isIgnored(item.columnName)) {
                    cycleTime += item.timeSpentInColumn();
                }
            });
            return cycleTime;
        } else {
            return null;
        }
    }

    function getExecutionTime() {
        let executionTime = 0,
            columnHistory = _.cloneDeep(self.columnHistory);

        if (self.isDone) {
            columnHistory.pop(); // Remove done column from calculation
        }

        _.forEach(columnHistory, function (item) {
            if (isExecutionColumn(item.columnName)) {
                executionTime += item.timeSpentInColumn();
            }
        });

        return executionTime;
    }

    function getDelayTime() {
        let delayTime = 0,
            columnHistory = _.cloneDeep(self.columnHistory);

        if (self.isDone) {
            columnHistory.pop();
        }

        _.forEach(columnHistory, function (item) {
            if (isDelayColumn(item.columnName) || self.wasReopened) {
                // When issue has been reopened, all Done columns should also count as delay time.
                delayTime += item.timeSpentInColumn();
            }
        });
        return delayTime;
    }

    function getProcessEfficiency(executionTime, cycleTime) {
        if (cycleTime) {
            return executionTime / cycleTime;
        } else {
            return 0;
        }
    }

    self.isInBetween = function (x, startValue, endValue) {
        return x >= startValue && x <= endValue;
    };

    self.id = apiIssue.id;
    self.key = apiIssue.key;

    if (!isAlreadyParsed) {
        self.summary = apiIssue.fields.summary;
        self.created = apiIssue.fields.created.substr(0, apiIssue.fields.created.indexOf('.'));
        self.currentStatus = parseCurrentStatus(apiIssue.fields.status);
        self.columnHistory = createColumnHistory(apiIssue);
    } else {
        self.summary = apiIssue.summary;
        self.created = apiIssue.created;
        self.currentStatus = apiIssue.currentStatus;
        self.columnHistory = createColumnHistoryAlreadyParsed(apiIssue);
    }

    /**
     * Checks which column this issue was in at a given time.
     */
    self.wasInColumnAtTimeStamp = function (time, column) {
        return self.isInBetween(time, Date.parse(column.enterTime), Date.parse(column.exitTime));
    };

    self.isDone = issueIsDone();
    self.wasReopened = issueWasReopened();
    self.cycleTime = getCycleTime();
    self.executionTime = getExecutionTime();
    self.delayTime = getDelayTime();
    self.processEfficiency = getProcessEfficiency(self.executionTime, self.cycleTime);
    if (self.key === "KAN-40") { console.log(JSON.stringify(self)) }
    return self;
}
<commit_msg>Removed a temporary console log<commit_after>/*jslint bitwise: true, plusplus: true, white: true, sub: true, nomen: true*/
/*global timeUtil, console, self:true, _, parseHistory*/

const DEBUG_COLUMNHISTORY = false;

const CATEGORY_DELAY = "Delay";
const CATEGORY_IGNORE = "Ignore";
const CATEGORY_EXECUTION = "Execution";
const CATEGORY_DONE = "Done";

/**
 * Create a new instance of a board design object.
 * Will detect whether the incoming board design is raw data from API or has already been parsed.
 */
function createBoardDesign(boardDesignToParse) {
    let boardDesign;

    if (boardDesignToParse.columnConfig) {
        // Incoming board design is raw data from API and contains the "columnConfig" property.
        delete boardDesignToParse.columnConfig.constraintType; // Not interested in this property
        boardDesign = new BoardDesign(boardDesignToParse.columnConfig, boardDesignToParse.name);
    } else {
        // Incomfing board design has been parsed already and instead stores the column config in a "columns" property.
        boardDesign = new BoardDesign(boardDesignToParse, boardDesignToParse.name);
    }

    return boardDesign;
}

/**
 * Current board structure with columns and their underlying statuses.
 */
function BoardDesign(apiColumnsData, boardName) {
    "use strict";

    let self = _.cloneDeep(apiColumnsData);

    self.getColumnNames = function () {
        let columnNames = [];
        _.forEach(self.columns, function (column) {
            columnNames.push(column.name);
        });
        return columnNames;
    };

    self.getColumnMatchingStatus = function (status) {
        let columnName = null;
        _.forEach(self.columns, function (column) {
            _.forEach(column.statuses, function (columnStatus) {
                if (columnStatus.id === status) {
                    columnName = column.name;
                    return false; // break loop
                }
            });
        });
        return columnName;
    };

    /**
     * Try to guess initial values of the column categories based on the column name.
     */
    self.createColumnCategories = function () {
        let hasCategories;

        hasCategories = _.first(self.columns).hasOwnProperty("category");

        if (!hasCategories) {
            _.forEach(self.columns, function (column) {
                if (column.name.toLowerCase().indexOf("ready") >= 0) {
                    column.category = CATEGORY_DELAY;
                } else if (column.name.toLowerCase().indexOf("backlog") >= 0) {
                    column.category = CATEGORY_IGNORE;
                } else if (column.name.toLowerCase().trim().indexOf("todo") >= 0) {
                    column.category = CATEGORY_IGNORE;
                } else {
                    column.category = CATEGORY_EXECUTION;
                }
            });

            _.first(self.columns).category = CATEGORY_DELAY;
            _.last(self.columns).category = CATEGORY_DONE;
        }

        return self.columns;
    };

    self.getColumnCategory = function (columnName) {
        let category = "";
        _.forEach(self.columns, function (column) {
            if (column.name === columnName) {
                category = column.category;
                return false; // break here, assuming there are no duplicate column names
            }
        });
        return category;
    };

    self.isIncludedInBoardDesign = function (columnName) {
        let result = false;
        _.forEach(self.columns, function (column) {
            if (column.name === columnName) {
                result = true;
                return false; // break here, column match found
            }
        });
        return result;
    };

    self.isIgnoreColumn = function (columnName) {
        return self.getColumnCategory(columnName) === CATEGORY_IGNORE;
    };

    self.isExecutionColumn = function (columnName) {
        return self.getColumnCategory(columnName) === CATEGORY_EXECUTION;
    };

    self.isDelayColumn = function (columnName) {
        return self.getColumnCategory(columnName) === CATEGORY_DELAY;
    };

    self.isDoneColumn = function (columnName) {
        let result = false;
        if (columnName) {
            result = self.getColumnCategory(columnName) === CATEGORY_DONE;
        }
        return result;
    };

    self.name = boardName;
    self.createColumnCategories();

    return self;
}

function Moves(histories, boardDesign) {
    "use strict";

    let moves = [];

    _.forEach(histories, function (event) {
        let moveTime = event.created.substr(0, event.created.indexOf('.'));
        _.forEach(event.items, function (eventItem) {
            if (eventItem.field === "status") {
                let fromColumn = boardDesign.getColumnMatchingStatus(eventItem.from),
                    toColumn = boardDesign.getColumnMatchingStatus(eventItem.to);
                moves.push(new MoveItem(fromColumn, toColumn, moveTime));
            }
        });
    });

    return moves;
}

function MoveItem(fromColumn, toColumn, moveTime) {
    "use strict";

    let self = this;
    self.fromColumn = fromColumn;
    self.toColumn = toColumn;
    self.moveTime = moveTime;
    return self;
}

function ColumnHistoryItem(columnName, enterTime, exitTime) {
    "use strict";

    let self = this;
    self.columnName = columnName;
    self.enterTime = enterTime;
    self.exitTime = exitTime;

    self.timeSpentInColumn = function () {
        return Date.parse(self.exitTime) - Date.parse(self.enterTime);
    };

    return self;
}

/**
 * Narrows down raw api issues data to only the issues as objects in a json array.
 */
function parseMultipleApiIssues(apiIssuesRaw) {
    "use strict";

    return apiIssuesRaw.issues;
}

/**
 * Constructs new issues from an array of issue data.
 */
function createIssuesFromArray(apiIssues, boardDesign) {
    let issues = [];

    _.forEach(apiIssues, function (issue) {
        let parsedIssue = new Issue(issue, boardDesign);
        if (parsedIssue.columnHistory.length > 0) {
            issues.push(parsedIssue);
        }
    });

    return issues;
}

/**
 * Create new issue object containing ID, summary, key, column history.
 */
function Issue(apiIssue, boardDesign, time) {
    "use strict";

    let self = {},
        isAlreadyParsed = apiIssue.changelog == null;

    if (!time) {
        time = timeUtil.getTimestamp();
    }

    function parseCurrentStatus(currentStatus) {
        let status = {};
        status.id = currentStatus.id;
        status.name = currentStatus.name;
        return status;
    }

    function parseApiIssueHistories(apiIssueHistories) {
        return new Moves(apiIssueHistories, boardDesign);
    }

    function getStartColumn(apiIssue) {
        return new ColumnHistoryItem(boardDesign.getColumnMatchingStatus(apiIssue.fields.status.id), self.created, time);
    }

    function createColumnHistoryAlreadyParsed(parsedIssue) {
        let columnHistory = [];

        _.forEach(parsedIssue.columnHistory, function (item) {
            columnHistory.push(new ColumnHistoryItem(item.columnName, item.enterTime, item.exitTime));
        });

        return columnHistory;
    }

    function createColumnHistory(apiIssue) {
        let columnHistory = [],
            issueHasHistory = !(apiIssue.changelog.histories.length === 0),
            issueHasStatusChangeInHistory = false;

        _.forEach(apiIssue.changelog.histories, function (event) {
            _.forEach(event.items, function (item) {
                if (item.field === "status") {
                    //console.log(apiIssue.key + " ||| StatusChange: false | FirstColumn: " + boardDesign.getColumnMatchingStatus(apiIssue.fields.status.id));
                    issueHasStatusChangeInHistory = true;
                    return false;
                }
            });
        });

        if (!issueHasHistory || !issueHasStatusChangeInHistory) {
            //console.log(apiIssue.key + " ||| History: " + issueHasHistory + " | StatusChange: " + issueHasStatusChangeInHistory + " | FirstColumn: " + boardDesign.getColumnMatchingStatus(apiIssue.fields.status.id));
            let startColumn = getStartColumn(apiIssue);
            if (isIncludedInBoardDesign(startColumn.columnName)) {
                columnHistory.push(startColumn);
            }
        } else {
            columnHistory = parseMoves(parseApiIssueHistories(apiIssue.changelog.histories));
        }

        return columnHistory;
    }

    /**
     * Format the parsed history into [COLUMN, ENTER, EXIT].
     * Example: [{"columnName":"Ready to Refine", "enterTime":"2015-09-01T14:42:01", "exitTime":"2015-09-01T14:42:23"}]
     */
    function parseMoves(columnHistory) {
        let i,
            createdTime = apiIssue.fields.created.substr(0, apiIssue.fields.created.indexOf('.')),
            columnsWithEnterExit = [];

        // First item is a special case because we need to set the time which the issue was created as enter time.
        if (isIncludedInBoardDesign(columnHistory[0].fromColumn)) {
            columnsWithEnterExit.push(new ColumnHistoryItem(columnHistory[0].fromColumn, createdTime, columnHistory[0].moveTime));
            if (DEBUG_COLUMNHISTORY) {
                console.log("ITERATION: 0 - " + columnHistory[0].fromColumn + " | " + createdTime + " | " + columnHistory[0].moveTime);
            }
        }

        // Events in the middle.
        for (i = 1; i < columnHistory.length; i++) {
            if (isIncludedInBoardDesign(columnHistory[i].fromColumn)) {
                columnsWithEnterExit.push(new ColumnHistoryItem(columnHistory[i].fromColumn, columnHistory[i - 1].moveTime, columnHistory[i].moveTime));
                if (DEBUG_COLUMNHISTORY) {
                    console.log("ITERATION: " + i + " - " + columnHistory[i].fromColumn + " | " + columnHistory[i - 1].moveTime + " | " + columnHistory[i].moveTime);
                }
            }
        }

        // Last item is a special case because toColumn must become its own object and has no real exit time.
        if (isIncludedInBoardDesign(columnHistory[columnHistory.length - 1].toColumn)) {
            columnsWithEnterExit.push(new ColumnHistoryItem(columnHistory[columnHistory.length - 1].toColumn, columnHistory[columnHistory.length - 1].moveTime, time));
            if (DEBUG_COLUMNHISTORY) {
                console.log("ITERATION: " + columnHistory.length + " - " + columnHistory[columnHistory.length - 1].toColumn + " | " + columnHistory[columnHistory.length - 1].moveTime + " | " + time);
            }
        }

        return columnsWithEnterExit;
    }

    function isIncludedInBoardDesign(columnName) {
        let result = false;
        if (columnName) {
            result = boardDesign.isIncludedInBoardDesign(columnName);
        }
        return result;
    }

    function isExecutionColumn(columnName) {
        return boardDesign.isExecutionColumn(columnName);
    }

    function isDelayColumn(columnName) {
        return boardDesign.isDelayColumn(columnName);
    }

    /**
     * Checks if issue was done and then reopened again. These issues need to be handled differently, as the time spent
     * in the Done column is effectively delay time.
     *
     * @returns {boolean} true if column has been reopened, false otherwise.
     */
    function issueWasReopened () {
        function wasInDoneColumn() {
            let numberOfInstances = 0;
            let columnHistory = _.cloneDeep(self.columnHistory);

            _.forEach(columnHistory, function (columnHistoryItem) {
                if (boardDesign.isDoneColumn(columnHistoryItem.columnName)) {
                    numberOfInstances++;
                }
            });
            return self.isDone ? numberOfInstances > 1 : numberOfInstances > 0;
        }

        return wasInDoneColumn();
    }

    /**
     * Check if issue is in a column which is Done category.
     */
    function issueIsDone () {
        let result = false;
        if (self.columnHistory.length > 0) {
            let columnName = _.last(self.columnHistory).columnName;
            result = boardDesign.isDoneColumn(columnName);
        }
        return result;
    }

    /**
     * Check if issue is in a column which should be ignored in calculations.
     */
    self.isIgnored = function (columnName) {
        return boardDesign.isIgnoreColumn(columnName);
    };

    /**
     * Calculate Cycle Time for the issue.
     */
    function getCycleTime() {
        let cycleTime = 0,
            columnHistory = _.cloneDeep(self.columnHistory);

        if (self.isDone) {
            columnHistory.pop(); // Remove the last Done column from calculation
            _.forEach(columnHistory, function (item) {
                if (!self.isIgnored(item.columnName)) {
                    cycleTime += item.timeSpentInColumn();
                }
            });
            return cycleTime;
        } else {
            return null;
        }
    }

    function getExecutionTime() {
        let executionTime = 0,
            columnHistory = _.cloneDeep(self.columnHistory);

        if (self.isDone) {
            columnHistory.pop(); // Remove done column from calculation
        }

        _.forEach(columnHistory, function (item) {
            if (isExecutionColumn(item.columnName)) {
                executionTime += item.timeSpentInColumn();
            }
        });

        return executionTime;
    }

    function getDelayTime() {
        let delayTime = 0,
            columnHistory = _.cloneDeep(self.columnHistory);

        if (self.isDone) {
            columnHistory.pop();
        }

        _.forEach(columnHistory, function (item) {
            if (isDelayColumn(item.columnName) || self.wasReopened) {
                // When issue has been reopened, all Done columns should also count as delay time.
                delayTime += item.timeSpentInColumn();
            }
        });
        return delayTime;
    }

    function getProcessEfficiency(executionTime, cycleTime) {
        if (cycleTime) {
            return executionTime / cycleTime;
        } else {
            return 0;
        }
    }

    self.isInBetween = function (x, startValue, endValue) {
        return x >= startValue && x <= endValue;
    };

    self.id = apiIssue.id;
    self.key = apiIssue.key;

    if (!isAlreadyParsed) {
        self.summary = apiIssue.fields.summary;
        self.created = apiIssue.fields.created.substr(0, apiIssue.fields.created.indexOf('.'));
        self.currentStatus = parseCurrentStatus(apiIssue.fields.status);
        self.columnHistory = createColumnHistory(apiIssue);
    } else {
        self.summary = apiIssue.summary;
        self.created = apiIssue.created;
        self.currentStatus = apiIssue.currentStatus;
        self.columnHistory = createColumnHistoryAlreadyParsed(apiIssue);
    }

    /**
     * Checks which column this issue was in at a given time.
     */
    self.wasInColumnAtTimeStamp = function (time, column) {
        return self.isInBetween(time, Date.parse(column.enterTime), Date.parse(column.exitTime));
    };

    self.isDone = issueIsDone();
    self.wasReopened = issueWasReopened();
    self.cycleTime = getCycleTime();
    self.executionTime = getExecutionTime();
    self.delayTime = getDelayTime();
    self.processEfficiency = getProcessEfficiency(self.executionTime, self.cycleTime);
    return self;
}
<|endoftext|>


<commit_before>define(
  'ephox.alloy.ui.common.InputBase',

  [
    'ephox.boulder.api.FieldSchema',
    'ephox.peanut.Fun',
    'ephox.sugar.api.Value'
  ],

  function (FieldSchema, Fun, Value) {
    var schema = [
      FieldSchema.option('data'),
      FieldSchema.defaulted('type', 'input'),
      FieldSchema.defaulted('tag', 'input'),
      FieldSchema.defaulted('hasTabstop', true)
    ];

    var behaviours = function (detail) {
      return {
        representing: {
          store: {
            mode: 'memory',
            initialValue: detail.data().getOr({ value: '', text: '' })
          },

          interactive: {
            event: 'input',
            process: function (input) {
              var v = Value.get(input.element());
              return {
                value: v.toLowerCase(),
                text: v
              };
            }
          },

          onSet: function (input, data) {
            Value.set(input.element(), data.text);
          }
        },

        focusing: {
          onFocus: function (component) {
            var input = component.element();
            var value = Value.get(input);
            input.dom().setSelectionRange(0, value.length);
          }
        },
        // FIX: Undefined
        tabstopping: detail.hasTabstop() ? true : undefined
      };
    };

    var dom = function (detail) {
      return {
        tag: detail.tag(),
        attributes: {
          type: detail.type()
        }
      };
    };

    return {
      schema: Fun.constant(schema),
      behaviours: behaviours,
      dom: dom
    };
  }
);<commit_msg>VAN-44-wheel-hackery-apis-2: placeholder text<commit_after>define(
  'ephox.alloy.ui.common.InputBase',

  [
    'ephox.boulder.api.FieldSchema',
    'ephox.boulder.api.Objects',
    'ephox.peanut.Fun',
    'ephox.sugar.api.Value'
  ],

  function (FieldSchema, Objects, Fun, Value) {
    var schema = [
      FieldSchema.option('data'),
      FieldSchema.defaulted('type', 'input'),
      FieldSchema.defaulted('tag', 'input'),
      FieldSchema.option('placeholder'),
      FieldSchema.defaulted('hasTabstop', true)
    ];

    var behaviours = function (detail) {
      return {
        representing: {
          store: {
            mode: 'memory',
            initialValue: detail.data().getOr({ value: '', text: '' })
          },

          interactive: {
            event: 'input',
            process: function (input) {
              var v = Value.get(input.element());
              return {
                value: v.toLowerCase(),
                text: v
              };
            }
          },

          onSet: function (input, data) {
            Value.set(input.element(), data.text);
          }
        },

        focusing: {
          onFocus: function (component) {
            var input = component.element();
            var value = Value.get(input);
            input.dom().setSelectionRange(0, value.length);
          }
        },
        // FIX: Undefined
        tabstopping: detail.hasTabstop() ? true : undefined
      };
    };

    var dom = function (detail) {
      return {
        tag: detail.tag(),
        attributes: Objects.wrapAll([
          {
            key: 'type',
            value: detail.type()
          }
        ].concat(detail.placeholder().map(function (pc) {
          return {
            key: 'placeholder',
            value: pc
          };
        }).toArray()))
      };
    };

    return {
      schema: Fun.constant(schema),
      behaviours: behaviours,
      dom: dom
    };
  }
);<|endoftext|>