<commit_before>(function($)
{
    'use strict';
    /**
     * @param options
     * @returns {$.fn.ModTable}
     * @constructor
     */
    $.fn.ModTable = function(options)
    {

        var Table;

        // This is the easiest way to have default options.
        var settings = $.extend(true, {
            // These are the defaults.
            "language": {
                "sEmptyTable": "Keine Daten in der Tabelle vorhanden",
                "sInfo": "_START_ bis _END_ von _TOTAL_ Einträgen",
                "sInfoEmpty": "0 bis 0 von 0 Einträgen",
                "sInfoFiltered": "(gefiltert von _MAX_ Einträgen)",
                "sInfoPostFix": "",
                "sInfoThousands": ".",
                "sLengthMenu": "_MENU_ Einträge anzeigen",
                "sLoadingRecords": "Wird geladen...",
                "sProcessing": "Bitte warten...",
                "sSearch": "Suchen",
                "sZeroRecords": "Keine Einträge vorhanden.",
                "oPaginate": {
                    "sFirst": "Erste",
                    "sPrevious": "Zurück",
                    "sNext": "Nächste",
                    "sLast": "Letzte"
                },
                "oAria": {
                    "sSortAscending": ": aktivieren, um Spalte aufsteigend zu sortieren",
                    "sSortDescending": ": aktivieren, um Spalte absteigend zu sortieren"
                }
            },
            "lengthChange": true,
            "lengthMenu": [[10, 25, 50, 100, -1], [10, 25, 50, 100, 'Alle']],
            "pageLength": 10,
            "dom": "<'row'<'col-sm-5 hidden-xs'li><'col-sm-7 hidden-xs'fp>>" +
            "<'row'<'col-sm-12'tr>>" +
            "<'row'<'col-sm-5'i><'col-sm-7'p>>",
            stateSave: true,
            responsive: true,
            autoWidth: false,
            // Setup RowReorder Extension
            ExtensionRowReorder: {
                Enabled: false,
                Url: '/Api/',
                Event: {
                    Success: function(Data)
                    {
                        try {
                            Data = $.parseJSON(Data);
                        } catch (e) {
                            Data = { 'Error': [], 'Data': '{Empty Response}' }
                        }
                        Table.processing(false);
                        $.notifyClose();
                        $.notify({
                            // options
                            message: Data.Error[0] ? Data.Error[0].Description : Data.Data
                        }, {
                            // settings
                            newest_on_top: true,
                            type: Data.Error[0] ? 'danger' : 'success',
                            delay: Data.Error[0] ? 5000 : 1000,
                            placement: {
                                from: "top",
                                align: "center"
                            }
                        });
                    },
                    Error: function(Data)
                    {
                        try {
                            Data = $.parseJSON(Data);
                        } catch (e) {
                            Data = { 'Error': [], 'Data': '{Empty Response}' }
                        }
                        Table.processing(false);
                        $.notifyClose();
                        $.notify({
                            // options
                            message: 'Die Aktion konnte nicht ausgeführt werden.'
                        }, {
                            // settings
                            newest_on_top: true,
                            type: 'danger',
                            placement: {
                                from: "top",
                                align: "center"
                            }
                        });
                    }
                },
                Data: {
                    // User-Data (additional)
                }
            },
            ExtensionRowExchange: {
                Enabled: false,
                Url: '/Api/',
                Handler: {
                    From: 'SourceHandlerClass',
                    To: 'TargetHandlerClass',
                    All: null
                },
                Connect: {
                    From: 'SourceTableClass',
                    To: 'TargetTableClass'
                },
                Event: {
                    Success: function(Data)
                    {
                        try {
                            Data = $.parseJSON(Data);
                        } catch (e) {
                            Data = { 'Error': [], 'Data': '{Empty Response}' }
                        }
                        Table.processing(false);
                        $.notifyClose();
                        $.notify({
                            // options
                            message: Data.Error[0] ? Data.Error[0].Description : Data.Data
                        }, {
                            // settings
                            newest_on_top: true,
                            type: Data.Error[0] ? 'danger' : 'success',
                            delay: Data.Error[0] ? 5000 : 1000,
                            placement: {
                                from: "top",
                                align: "center"
                            }
                        });
                    },
                    Error: function(Data)
                    {
                        try {
                            Data = $.parseJSON(Data);
                        } catch (e) {
                            Data = { 'Error': [], 'Data': '{Empty Response}' }
                        }
                        Table.processing(false);
                        $.notifyClose();
                        $.notify({
                            // options
                            message: 'Die Aktion konnte nicht ausgeführt werden.'
                        }, {
                            // settings
                            newest_on_top: true,
                            type: 'danger',
                            placement: {
                                from: "top",
                                align: "center"
                            }
                        });
                    }
                },
                Data: {
                    // User-Data (additional)
                }
            }
        }, options);

        /**
         * Register: Processing Api
         *
         * Table.processing(true) - Show processing message
         * Table.processing(false) - Hide processing message
         */
        jQuery.fn.dataTable.Api.register('processing()', function(show)
        {
            return this.iterator('table', function(ctx)
            {
                ctx.oApi._fnProcessingDisplay(ctx, show);
            });
        });

        // Rewrite Custom-Settings to Api-Settings
        if (settings.ExtensionRowReorder.Enabled) {
            settings.processing = true;

            // Find FIRST Column Name (.column(0).dataSrc()) for Sequence
            var dataSrc = 0;
            if( settings.aoColumns ) {
                dataSrc = settings.aoColumns[0];
            }
            if( settings.columns ) {
                dataSrc = settings.columns[0].data;
            }

            if (settings.responsive) {
                settings.rowReorder = {
                    dataSrc: dataSrc ? dataSrc: 0,
                    selector: 'td:nth-child(2)',
                    snapX: 0
                }
            } else {
                settings.rowReorder = {
                    dataSrc: dataSrc ? dataSrc: 0,
                    snapX: 0
                };
            }

            if (settings.columnDefs) {
                settings.columnDefs = settings.columnDefs.concat([
                    {orderable: true, targets: 0},
                    {orderable: false, targets: '_all'},
                    {className: 'reorder', targets: settings.responsive ? 1 : 0}
                ]);
            } else {
                settings.columnDefs = [
                    {orderable: true, targets: 0},
                    {orderable: false, targets: '_all'},
                    {className: 'reorder', targets: settings.responsive ? 1 : 0}
                ];
            }

            if (options.ExtensionRowReorder.Event) {
                if (options.ExtensionRowReorder.Event.Success) {
                    settings.ExtensionRowReorder.Event.Success = new Function(
                        options.ExtensionRowReorder.Event.Success
                    )
                }
                if (options.ExtensionRowReorder.Event.Error) {
                    settings.ExtensionRowReorder.Event.Error = new Function(
                        options.ExtensionRowReorder.Event.Error
                    )
                }
            }
        }

        // Rewrite Custom-Settings to Api-Settings
        if (settings.ExtensionRowExchange.Enabled) {
            settings.processing = true;
            settings.responsive = false;

            if (options.ExtensionRowExchange.Event) {
                if (options.ExtensionRowExchange.Event.Success) {
                    settings.ExtensionRowExchange.Event.Success = new Function(
                        options.ExtensionRowExchange.Event.Success
                    )
                }
                if (options.ExtensionRowExchange.Event.Error) {
                    settings.ExtensionRowExchange.Event.Error = new Function(
                        options.ExtensionRowExchange.Event.Error
                    )
                }
            }
        }

        /**
         * Activate: DataTable
         */
        Table = this.DataTable(settings);

        /**
         * Register: RowReorder-Extension
         */
        if (settings.ExtensionRowReorder.Enabled) {

            Table.on('row-reorder', function(Event, Diff)
            {
                Table.processing(true);
                var postData = {};
                for (var i = 0, ien = Diff.length; i < ien; i++) {
                    if( Diff[i].oldData ) {
                        postData[i] = {
                            pre: Diff[i].oldData,
                            post: Diff[i].newData
                        }
                    } else {
                        postData[i] = {
                            pre: Diff[i].oldPosition +1,
                            post: Diff[i].newPosition +1
                        }
                    }
                }
                if (settings.ExtensionRowReorder.Url) {
                    $.post(settings.ExtensionRowReorder.Url,
                        /**
                         * @deprecated Reorder
                         * @see Data
                         */
                        {'Reorder': postData, 'Data': postData, 'Additional': settings.ExtensionRowReorder.Data},
                        "json")
                        .fail(settings.ExtensionRowReorder.Event.Error)
                        .fail(function()
                        {
                            Table.processing(false);
                        })
                        .success(settings.ExtensionRowReorder.Event.Success)
                        .done(function()
                        {
                            Table.processing(false);
                        })
                }
            });
        }

        /**
         * Register: RowExchange-Extension
         */
        if (settings.ExtensionRowExchange.Enabled) {

            $(this).addClass(settings.ExtensionRowExchange.Connect.From);
            var $Table = $(this);

            if (settings.ExtensionRowExchange.Handler.All) {
                $('span.' + settings.ExtensionRowExchange.Handler.All).on('click', function()
                {
                    Table.processing(true);

                    var ExchangeTarget = $('table.' + settings.ExtensionRowExchange.Connect.To).DataTable();

                    var SourceRows = Table.rows()[0];
                    $(SourceRows).each(function(Index)
                    {

                        var SourceRow = $Table.find('tbody tr:eq(' + Index + ')');
                        var Payload = SourceRow.find('td span.ExchangeData').html();
                        var Handler = SourceRow.find('td span.' + settings.ExtensionRowExchange.Handler.From);

                        if (Payload) {

                            Handler.removeClass(
                                settings.ExtensionRowExchange.Handler.From
                            ).addClass(
                                settings.ExtensionRowExchange.Handler.To
                            );

                            var PostData = $.parseJSON(Payload);
                            var TargetRow = Table.row(SourceRow);

                            if (settings.ExtensionRowExchange.Url) {
                                $.post(settings.ExtensionRowExchange.Url,
                                    {
                                        'Direction': settings.ExtensionRowExchange.Connect,
                                        'Data': PostData,
                                        'Additional': settings.ExtensionRowExchange.Data
                                    }, "json")
                                    .fail(settings.ExtensionRowExchange.Event.Error)
                                    .fail(function()
                                    {
                                        Table.processing(false);
                                        ExchangeTarget.processing(false);
                                    })
                                    .success(settings.ExtensionRowExchange.Event.Success)
                                    .success(function()
                                    {
                                        ExchangeTarget.row.add(SourceRow).draw();
                                        TargetRow.remove().draw();
                                    })
                                    .done(function()
                                    {
                                        Table.processing(false);
                                        ExchangeTarget.processing(false);
                                    })
                            }
                        }
                    })
                })
            }

            Table.on('click', 'tbody tr .' + settings.ExtensionRowExchange.Handler.From, function()
            {

                Table.processing(true);

                var ExchangeTarget = $('table.' + settings.ExtensionRowExchange.Connect.To).DataTable();
                var SourceRow = $(this).closest('tr');
                var Payload = $(this).closest('td').find('span.ExchangeData').html();
                var PostData = $.parseJSON(Payload);

                $(this).removeClass(
                    settings.ExtensionRowExchange.Handler.From
                ).addClass(
                    settings.ExtensionRowExchange.Handler.To
                );

                var TargetRow = Table.row(SourceRow);
                ExchangeTarget.row.add(SourceRow).draw();
                TargetRow.remove().draw();
                if (settings.ExtensionRowExchange.Url) {
                    $.post(settings.ExtensionRowExchange.Url,
                        {
                            'Direction': settings.ExtensionRowExchange.Connect,
                            'Data': PostData,
                            'Additional': settings.ExtensionRowExchange.Data
                        }, "json")
                        .fail(settings.ExtensionRowExchange.Event.Error)
                        .fail(function()
                        {
                            Table.processing(false);
                            ExchangeTarget.processing(false);
                        })
                        .success(settings.ExtensionRowExchange.Event.Success)
                        .done(function()
                        {
                            Table.processing(false);
                            ExchangeTarget.processing(false);
                        })
                }
            }).on('mouseover', 'tbody tr .' + settings.ExtensionRowExchange.Handler.From, function()
            {
                $(this).css('cursor', 'pointer');
            });
        }
        return this;
    };

}(jQuery));
<commit_msg>Fix: Click-Area RowExchange<commit_after>(function($)
{
    'use strict';
    /**
     * @param options
     * @returns {$.fn.ModTable}
     * @constructor
     */
    $.fn.ModTable = function(options)
    {

        var Table;

        // This is the easiest way to have default options.
        var settings = $.extend(true, {
            // These are the defaults.
            "language": {
                "sEmptyTable": "Keine Daten in der Tabelle vorhanden",
                "sInfo": "_START_ bis _END_ von _TOTAL_ Einträgen",
                "sInfoEmpty": "0 bis 0 von 0 Einträgen",
                "sInfoFiltered": "(gefiltert von _MAX_ Einträgen)",
                "sInfoPostFix": "",
                "sInfoThousands": ".",
                "sLengthMenu": "_MENU_ Einträge anzeigen",
                "sLoadingRecords": "Wird geladen...",
                "sProcessing": "Bitte warten...",
                "sSearch": "Suchen",
                "sZeroRecords": "Keine Einträge vorhanden.",
                "oPaginate": {
                    "sFirst": "Erste",
                    "sPrevious": "Zurück",
                    "sNext": "Nächste",
                    "sLast": "Letzte"
                },
                "oAria": {
                    "sSortAscending": ": aktivieren, um Spalte aufsteigend zu sortieren",
                    "sSortDescending": ": aktivieren, um Spalte absteigend zu sortieren"
                }
            },
            "lengthChange": true,
            "lengthMenu": [[10, 25, 50, 100, -1], [10, 25, 50, 100, 'Alle']],
            "pageLength": 10,
            "dom": "<'row'<'col-sm-5 hidden-xs'li><'col-sm-7 hidden-xs'fp>>" +
            "<'row'<'col-sm-12'tr>>" +
            "<'row'<'col-sm-5'i><'col-sm-7'p>>",
            stateSave: true,
            responsive: true,
            autoWidth: false,
            // Setup RowReorder Extension
            ExtensionRowReorder: {
                Enabled: false,
                Url: '/Api/',
                Event: {
                    Success: function(Data)
                    {
                        try {
                            Data = $.parseJSON(Data);
                        } catch (e) {
                            Data = { 'Error': [], 'Data': '{Empty Response}' }
                        }
                        Table.processing(false);
                        $.notifyClose();
                        $.notify({
                            // options
                            message: Data.Error[0] ? Data.Error[0].Description : Data.Data
                        }, {
                            // settings
                            newest_on_top: true,
                            type: Data.Error[0] ? 'danger' : 'success',
                            delay: Data.Error[0] ? 5000 : 1000,
                            placement: {
                                from: "top",
                                align: "center"
                            }
                        });
                    },
                    Error: function(Data)
                    {
                        try {
                            Data = $.parseJSON(Data);
                        } catch (e) {
                            Data = { 'Error': [], 'Data': '{Empty Response}' }
                        }
                        Table.processing(false);
                        $.notifyClose();
                        $.notify({
                            // options
                            message: 'Die Aktion konnte nicht ausgeführt werden.'
                        }, {
                            // settings
                            newest_on_top: true,
                            type: 'danger',
                            placement: {
                                from: "top",
                                align: "center"
                            }
                        });
                    }
                },
                Data: {
                    // User-Data (additional)
                }
            },
            ExtensionRowExchange: {
                Enabled: false,
                Url: '/Api/',
                Handler: {
                    From: 'SourceHandlerClass',
                    To: 'TargetHandlerClass',
                    All: null
                },
                Connect: {
                    From: 'SourceTableClass',
                    To: 'TargetTableClass'
                },
                Event: {
                    Success: function(Data)
                    {
                        try {
                            Data = $.parseJSON(Data);
                        } catch (e) {
                            Data = { 'Error': [], 'Data': '{Empty Response}' }
                        }
                        Table.processing(false);
                        $.notifyClose();
                        $.notify({
                            // options
                            message: Data.Error[0] ? Data.Error[0].Description : Data.Data
                        }, {
                            // settings
                            newest_on_top: true,
                            type: Data.Error[0] ? 'danger' : 'success',
                            delay: Data.Error[0] ? 5000 : 1000,
                            placement: {
                                from: "top",
                                align: "center"
                            }
                        });
                    },
                    Error: function(Data)
                    {
                        try {
                            Data = $.parseJSON(Data);
                        } catch (e) {
                            Data = { 'Error': [], 'Data': '{Empty Response}' }
                        }
                        Table.processing(false);
                        $.notifyClose();
                        $.notify({
                            // options
                            message: 'Die Aktion konnte nicht ausgeführt werden.'
                        }, {
                            // settings
                            newest_on_top: true,
                            type: 'danger',
                            placement: {
                                from: "top",
                                align: "center"
                            }
                        });
                    }
                },
                Data: {
                    // User-Data (additional)
                }
            }
        }, options);

        /**
         * Register: Processing Api
         *
         * Table.processing(true) - Show processing message
         * Table.processing(false) - Hide processing message
         */
        jQuery.fn.dataTable.Api.register('processing()', function(show)
        {
            return this.iterator('table', function(ctx)
            {
                ctx.oApi._fnProcessingDisplay(ctx, show);
            });
        });

        // Rewrite Custom-Settings to Api-Settings
        if (settings.ExtensionRowReorder.Enabled) {
            settings.processing = true;

            // Find FIRST Column Name (.column(0).dataSrc()) for Sequence
            var dataSrc = 0;
            if( settings.aoColumns ) {
                dataSrc = settings.aoColumns[0];
            }
            if( settings.columns ) {
                dataSrc = settings.columns[0].data;
            }

            if (settings.responsive) {
                settings.rowReorder = {
                    dataSrc: dataSrc ? dataSrc: 0,
                    selector: 'td:nth-child(2)',
                    snapX: 0
                }
            } else {
                settings.rowReorder = {
                    dataSrc: dataSrc ? dataSrc: 0,
                    snapX: 0
                };
            }

            if (settings.columnDefs) {
                settings.columnDefs = settings.columnDefs.concat([
                    {orderable: true, targets: 0},
                    {orderable: false, targets: '_all'},
                    {className: 'reorder', targets: settings.responsive ? 1 : 0}
                ]);
            } else {
                settings.columnDefs = [
                    {orderable: true, targets: 0},
                    {orderable: false, targets: '_all'},
                    {className: 'reorder', targets: settings.responsive ? 1 : 0}
                ];
            }

            if (options.ExtensionRowReorder.Event) {
                if (options.ExtensionRowReorder.Event.Success) {
                    settings.ExtensionRowReorder.Event.Success = new Function(
                        options.ExtensionRowReorder.Event.Success
                    )
                }
                if (options.ExtensionRowReorder.Event.Error) {
                    settings.ExtensionRowReorder.Event.Error = new Function(
                        options.ExtensionRowReorder.Event.Error
                    )
                }
            }
        }

        // Rewrite Custom-Settings to Api-Settings
        if (settings.ExtensionRowExchange.Enabled) {
            settings.processing = true;
            settings.responsive = false;

            if (options.ExtensionRowExchange.Event) {
                if (options.ExtensionRowExchange.Event.Success) {
                    settings.ExtensionRowExchange.Event.Success = new Function(
                        options.ExtensionRowExchange.Event.Success
                    )
                }
                if (options.ExtensionRowExchange.Event.Error) {
                    settings.ExtensionRowExchange.Event.Error = new Function(
                        options.ExtensionRowExchange.Event.Error
                    )
                }
            }
        }

        /**
         * Activate: DataTable
         */
        Table = this.DataTable(settings);

        /**
         * Register: RowReorder-Extension
         */
        if (settings.ExtensionRowReorder.Enabled) {

            Table.on('row-reorder', function(Event, Diff)
            {
                Table.processing(true);
                var postData = {};
                for (var i = 0, ien = Diff.length; i < ien; i++) {
                    if( Diff[i].oldData ) {
                        postData[i] = {
                            pre: Diff[i].oldData,
                            post: Diff[i].newData
                        }
                    } else {
                        postData[i] = {
                            pre: Diff[i].oldPosition +1,
                            post: Diff[i].newPosition +1
                        }
                    }
                }
                if (settings.ExtensionRowReorder.Url) {
                    $.post(settings.ExtensionRowReorder.Url,
                        /**
                         * @deprecated Reorder
                         * @see Data
                         */
                        {'Reorder': postData, 'Data': postData, 'Additional': settings.ExtensionRowReorder.Data},
                        "json")
                        .fail(settings.ExtensionRowReorder.Event.Error)
                        .fail(function()
                        {
                            Table.processing(false);
                        })
                        .success(settings.ExtensionRowReorder.Event.Success)
                        .done(function()
                        {
                            Table.processing(false);
                        })
                }
            });
        }

        /**
         * Register: RowExchange-Extension
         */
        if (settings.ExtensionRowExchange.Enabled) {

            $(this).addClass(settings.ExtensionRowExchange.Connect.From);
            var $Table = $(this);

            if (settings.ExtensionRowExchange.Handler.All) {
                $('span.' + settings.ExtensionRowExchange.Handler.All).on('click', function()
                {
                    Table.processing(true);

                    var ExchangeTarget = $('table.' + settings.ExtensionRowExchange.Connect.To).DataTable();

                    var SourceRows = Table.rows()[0];
                    $(SourceRows).each(function(Index)
                    {

                        var SourceRow = $Table.find('tbody tr:eq(' + Index + ')');
                        var Payload = SourceRow.find('td span.ExchangeData').html();
                        var Handler = SourceRow.find('td span.' + settings.ExtensionRowExchange.Handler.From);

                        if (Payload) {

                            Handler.removeClass(
                                settings.ExtensionRowExchange.Handler.From
                            ).addClass(
                                settings.ExtensionRowExchange.Handler.To
                            );

                            var PostData = $.parseJSON(Payload);
                            var TargetRow = Table.row(SourceRow);

                            if (settings.ExtensionRowExchange.Url) {
                                $.post(settings.ExtensionRowExchange.Url,
                                    {
                                        'Direction': settings.ExtensionRowExchange.Connect,
                                        'Data': PostData,
                                        'Additional': settings.ExtensionRowExchange.Data
                                    }, "json")
                                    .fail(settings.ExtensionRowExchange.Event.Error)
                                    .fail(function()
                                    {
                                        Table.processing(false);
                                        ExchangeTarget.processing(false);
                                    })
                                    .success(settings.ExtensionRowExchange.Event.Success)
                                    .success(function()
                                    {
                                        ExchangeTarget.row.add(SourceRow).draw();
                                        TargetRow.remove().draw();
                                    })
                                    .done(function()
                                    {
                                        Table.processing(false);
                                        ExchangeTarget.processing(false);
                                    })
                            }
                        }
                    })
                })
            }

            Table.on('click', 'tbody tr span.btn:has(span.' + settings.ExtensionRowExchange.Handler.From+')', function()
            {

                Table.processing(true);

                var ExchangeTarget = $('table.' + settings.ExtensionRowExchange.Connect.To).DataTable();
                var SourceRow = $(this).closest('tr');
                var Payload = $(this).closest('td').find('span.ExchangeData').html();
                var PostData = $.parseJSON(Payload);

                if( $(this).hasClass('btn') ) {
                    $(this).find('span').removeClass(
                        settings.ExtensionRowExchange.Handler.From
                    ).addClass(
                        settings.ExtensionRowExchange.Handler.To
                    );
                } else {
                    $(this).removeClass(
                        settings.ExtensionRowExchange.Handler.From
                    ).addClass(
                        settings.ExtensionRowExchange.Handler.To
                    );
                }

                var TargetRow = Table.row(SourceRow);
                ExchangeTarget.row.add(SourceRow).draw();
                TargetRow.remove().draw();
                if (settings.ExtensionRowExchange.Url) {
                    $.post(settings.ExtensionRowExchange.Url,
                        {
                            'Direction': settings.ExtensionRowExchange.Connect,
                            'Data': PostData,
                            'Additional': settings.ExtensionRowExchange.Data
                        }, "json")
                        .fail(settings.ExtensionRowExchange.Event.Error)
                        .fail(function()
                        {
                            Table.processing(false);
                            ExchangeTarget.processing(false);
                        })
                        .success(settings.ExtensionRowExchange.Event.Success)
                        .done(function()
                        {
                            Table.processing(false);
                            ExchangeTarget.processing(false);
                        })
                }
            }).on('mouseover', 'tbody tr .' + settings.ExtensionRowExchange.Handler.From, function()
            {
                $(this).css('cursor', 'pointer');
            });
        }
        return this;
    };

}(jQuery));
<|endoftext|>


<commit_before>let Job = require('../Job');
let JobTree = require('../JobTree');

describe('JobTree', function () {

  describe('#constructor', function () {

    beforeEach(function () {
      this.instance = new JobTree({
        id: '/group',
        items: [
          new JobTree({id: '/group/foo', items: []}),
          {id: '/group/bar', items: []},
          {id: '/group/alpha'},
          new Job({id: '/group/beta'}),
          {id: '/group/wibble/wobble'}
        ],
      });
    });

    it('defaults id to root tree (groups) id', function () {
      let tree = new JobTree({items: []});
      expect(tree.getId()).toEqual('/');
    });

    it('sets correct tree (groups) id', function () {
      expect(this.instance.getId()).toEqual('/group');
    });

    it('should throw error if the provided id doesn\'t start with a slash',
      function () {
        expect(function () {
          new JobTree({id: 'malformed/id'});
        }).toThrow();
      }
    );

    it('should throw error if the provided id ends with a slash',
      function () {
        expect(function () {
          new JobTree({id: '/malformed/id/'});
        }).toThrow();
      }
    );

    it('should not throw error if the provided id is only a slash (root id)',
      function () {
        expect(function () {
          new JobTree({id: '/'});
        }).not.toThrow();
      }
    );

    it('accepts nested trees (groups)', function () {
      expect(this.instance.getItems()[0] instanceof JobTree).toEqual(true);
    });

    it('converts tree like items into instances of JobTree', function () {
      expect(this.instance.getItems()[1] instanceof JobTree).toEqual(true);
    });

    it('converts items into instances of Job', function () {
      expect(this.instance.getItems()[2] instanceof Job).toEqual(true);
    });

  });

  describe('#add', function () {

    beforeEach(function () {
      this.instance = new JobTree();
    });

    it('adds a job to the tree', function () {
      this.instance.add(new Job({id: '/alpha'}));

      expect(this.instance.getItems()[0].getId()).toEqual('/alpha');
    });

    it('adds nested items at the correct location based on id/path matching',
      function () {
        this.instance.add(new Job({id: '/group/foo/bar'}));

        expect(this.instance.getItems()[0].getId()).toEqual('/group');
        expect(this.instance.getItems()[0].getItems()[0].getId())
          .toEqual('/group/foo');
        expect(this.instance.getItems()[0].getItems()[0].getItems()[0].getId())
          .toEqual('/group/foo/bar');
      }
    );

    it('should throw error if item is neither an instance of Job nor JobTree',
      function () {
        expect(function () {
          this.instance.add({})
        }).toThrow();
        expect(function () {
          this.instance.add([])
        }).toThrow();
        expect(function () {
          this.instance.add(NaN)
        }).toThrow();
        expect(function () {
          this.instance.add()
        }).toThrow();
      }
    );

  });

  describe('#findItemById', function () {

    beforeEach(function () {
      this.instance = new JobTree({
        id: '/',
        items: [
          {id: '/foo', items: []},
          {id: '/alpha'},
          {id: '/wibble/wobble'}
        ],
      });
    });

    it('should find matching item', function () {
      expect(this.instance.findItemById('/alpha').getId()).toEqual('/alpha');
    });

    it('should find matching subtree item', function () {
      expect(this.instance.findItemById('/wibble/wobble').getId())
        .toEqual('/wibble/wobble');
    });

    it('should find matching subtree', function () {
      expect(this.instance.findItemById('/foo').getId()).toEqual('/foo');
    });

  });

});
<commit_msg>Improve test descriptions<commit_after>let Job = require('../Job');
let JobTree = require('../JobTree');

describe('JobTree', function () {

  describe('#constructor', function () {

    beforeEach(function () {
      this.instance = new JobTree({
        id: '/group',
        items: [
          new JobTree({id: '/group/foo', items: []}),
          {id: '/group/bar', items: []},
          {id: '/group/alpha'},
          new Job({id: '/group/beta'}),
          {id: '/group/wibble/wobble'}
        ],
      });
    });

    it('defaults id to slash (root group id)', function () {
      let tree = new JobTree({items: []});
      expect(tree.getId()).toEqual('/');
    });

    it('sets correct tree id', function () {
      expect(this.instance.getId()).toEqual('/group');
    });

    it('should throw error if the provided id doesn\'t start with a slash',
      function () {
        expect(function () {
          new JobTree({id: 'malformed/id'});
        }).toThrow();
      }
    );

    it('should throw error if the provided id ends with a slash',
      function () {
        expect(function () {
          new JobTree({id: '/malformed/id/'});
        }).toThrow();
      }
    );

    it('should not throw error if the provided id is only a slash (root id)',
      function () {
        expect(function () {
          new JobTree({id: '/'});
        }).not.toThrow();
      }
    );

    it('accepts nested trees (groups)', function () {
      expect(this.instance.getItems()[0] instanceof JobTree).toEqual(true);
    });

    it('converts tree like items into instances of JobTree', function () {
      expect(this.instance.getItems()[1] instanceof JobTree).toEqual(true);
    });

    it('converts items into instances of Job', function () {
      expect(this.instance.getItems()[2] instanceof Job).toEqual(true);
    });

  });

  describe('#add', function () {

    beforeEach(function () {
      this.instance = new JobTree();
    });

    it('adds a job to the tree', function () {
      this.instance.add(new Job({id: '/alpha'}));

      expect(this.instance.getItems()[0].getId()).toEqual('/alpha');
    });

    it('adds nested items at the correct location based on id/path matching',
      function () {
        this.instance.add(new Job({id: '/group/foo/bar'}));

        expect(this.instance.getItems()[0].getId()).toEqual('/group');
        expect(this.instance.getItems()[0].getItems()[0].getId())
          .toEqual('/group/foo');
        expect(this.instance.getItems()[0].getItems()[0].getItems()[0].getId())
          .toEqual('/group/foo/bar');
      }
    );

    it('should throw error if item is neither an instance of Job nor JobTree',
      function () {
        expect(function () {
          this.instance.add({})
        }).toThrow();
        expect(function () {
          this.instance.add([])
        }).toThrow();
        expect(function () {
          this.instance.add(NaN)
        }).toThrow();
        expect(function () {
          this.instance.add()
        }).toThrow();
      }
    );

  });

  describe('#findItemById', function () {

    beforeEach(function () {
      this.instance = new JobTree({
        id: '/',
        items: [
          {id: '/foo', items: []},
          {id: '/alpha'},
          {id: '/wibble/wobble'}
        ],
      });
    });

    it('should find matching item', function () {
      expect(this.instance.findItemById('/alpha').getId()).toEqual('/alpha');
    });

    it('should find matching subtree item', function () {
      expect(this.instance.findItemById('/wibble/wobble').getId())
        .toEqual('/wibble/wobble');
    });

    it('should find matching subtree', function () {
      expect(this.instance.findItemById('/foo').getId()).toEqual('/foo');
    });

  });

});
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies.
 */
var express = require('express'),
	http = require('http'),
	morgan = require('morgan'),
	bodyParser = require('body-parser'),
	session = require('express-session'),
	compress = require('compression'),
	methodOverride = require('method-override'),
	cookieParser = require('cookie-parser'),
	helmet = require('helmet'),
	passport = require('passport'),
	mongoStore = require('connect-mongo')({
		session: session
	}),
	twitter = require('ntwitter'),
	flash = require('connect-flash'),
	config = require('./config'),
	consolidate = require('consolidate'),
	path = require('path');

module.exports = function(db) {
	// Initialize express app
	var app = express();
	var server = http.createServer(app);
	var io = require('socket.io').listen(server);

	// Globbing model files
	config.getGlobbedFiles('./app/models/**/*.js').forEach(function(modelPath) {
		require(path.resolve(modelPath));
	});

	// Setting application local variables
	app.locals.title = config.app.title;
	app.locals.description = config.app.description;
	app.locals.keywords = config.app.keywords;
	app.locals.facebookAppId = config.facebook.clientID;
	app.locals.jsFiles = config.getJavaScriptAssets();
	app.locals.cssFiles = config.getCSSAssets();

	// Passing the request url to environment locals
	app.use(function(req, res, next) {
		res.locals.url = req.protocol + ':// ' + req.headers.host + req.url;
		next();
	});

	// Should be placed before express.static
	app.use(compress({
		filter: function(req, res) {
			return (/json|text|javascript|css/).test(res.getHeader('Content-Type'));
		},
		level: 9
	}));

	// Showing stack errors
	app.set('showStackError', true);

	// Set swig as the template engine
	app.engine('server.view.html', consolidate[config.templateEngine]);

	// Set views path and view engine
	app.set('view engine', 'server.view.html');
	app.set('views', './app/views');

	// Environment dependent middleware
	if (process.env.NODE_ENV === 'development') {
		// Enable logger (morgan)
		app.use(morgan('dev'));

		// Disable views cache
		app.set('view cache', false);
	} else if (process.env.NODE_ENV === 'production') {
		app.locals.cache = 'memory';
	}

	// Request body parsing middleware should be above methodOverride
	app.use(bodyParser.urlencoded());
	app.use(bodyParser.json());
	app.use(methodOverride());

	// Enable jsonp
	app.enable('jsonp callback');

	// CookieParser should be above session
	app.use(cookieParser());

	// Express MongoDB session storage
	app.use(session({
		secret: config.sessionSecret,
		store: new mongoStore({
			db: db.connection.db,
			collection: config.sessionCollection
		})
	}));

	// use passport session
	app.use(passport.initialize());
	app.use(passport.session());

	// connect flash for flash messages
	app.use(flash());

	// Use helmet to secure Express headers
	app.use(helmet.xframe());
	app.use(helmet.iexss());
	app.use(helmet.contentTypeOptions());
	app.use(helmet.ienoopen());
	app.disable('x-powered-by');

	// Setting the app router and static folder
	app.use(express.static(path.resolve('./public')));

	// Globbing routing files
	config.getGlobbedFiles('./app/routes/**/*.js').forEach(function(routePath) {
		require(path.resolve(routePath))(app);
	});

	// Assume 'not found' in the error msgs is a 404. this is somewhat silly, but valid, you can do whatever you like, set properties, use instanceof etc.
	app.use(function(err, req, res, next) {
		// If the error object doesn't exists
		if (!err) return next();

		// Log it
		console.error(err.stack);

		// Error page
		res.status(500).render('500', {
			error: err.stack
		});
	});

	// Assume 404 since no middleware responded
	app.use(function(req, res) {
		res.status(404).render('404', {
			url: req.originalUrl,
			error: 'Not Found'
		});
	});

    // TODO - Put all these in environment variables
    var twitterClient =  new twitter({
                  consumer_key: process.env.TWITTER_KEY || 'UNDEFINED',
                  consumer_secret: process.env.TWITTER_SECRET || 'UNDEFINED',
                  access_token_key: process.env.TWITTER_ACCESS_KEY || 'UNDEFINED',
                  access_token_secret: process.env.TWITTER_ACCESS_SECRET || 'UNDEFINED'
              }),
        stream = null,
        users = [];

    io.on('connection', function (socket) {
        console.log('user connected');

        // The user it's added to the array if it doesn't exist
        if(users.indexOf(socket.id) === -1) {
            users.push(socket.id);
        }

        socket.on('start stream', function() {
            // The stream will be started only when the 1st user arrives
            if(stream === null) {
                twitterClient.stream('statuses/filter', {
                    //'locations':'38.46,-85.11,41.88,-80.37' // ohio
                    'locations' : '-83.1226,39.868,-82.8808,40.12' // NYC/NE
                }, function(s) {
                    stream = s;
                    stream.on('data', function(data) {
                        // only broadcast when users are online
                        if(users.length > 0) {
                            // This emits the signal to all users but the one
                            // that started the stream
                            socket.broadcast.emit('new tweet', data);
                            // This emits the signal to the user that started
                            // the stream
                            socket.emit('new tweet', data);
                        }
                        else {
                            // If there are no users connected we destroy the stream.
                            // Why would we keep it running for nobody?
                            console.log('users have all disconnected');
                            stream.destroy();
                            stream = null;
                        }
                    });

                    stream.on('error', function(type, code, description) {
                        console.log(type + ' ' + code + ': ' + description);
                    });
                });
            }
        });

        // This handles when a user is disconnected
        socket.on('disconnect', function(o) {
            console.log('user disconnected');
            // find the user in the array
            var index = users.indexOf(socket.id);
            if(index != -1) {
                // Eliminates the user from the array
                users.splice(index, 1);
            }

            console.log('number of users connected: ' + users.length);
        });

        // Emits signal when the user is connected sending
        // the tracking words the app it's using
        socket.emit('connected', {
            tracking: ''
        });
  	});

	return server;
};
<commit_msg>Trying new config for heroku socketio<commit_after>'use strict';

/**
 * Module dependencies.
 */
var express = require('express'),
	http = require('http'),
	morgan = require('morgan'),
	bodyParser = require('body-parser'),
	session = require('express-session'),
	compress = require('compression'),
	methodOverride = require('method-override'),
	cookieParser = require('cookie-parser'),
	helmet = require('helmet'),
	passport = require('passport'),
	mongoStore = require('connect-mongo')({
		session: session
	}),
	twitter = require('ntwitter'),
	flash = require('connect-flash'),
	config = require('./config'),
	consolidate = require('consolidate'),
	path = require('path');

module.exports = function(db) {
	// Initialize express app
	var app = express();
	var server = http.createServer(app);
	var io = require('socket.io')({
            "transports":["xhr-polling"],
            "polling duration":10
        }).listen(server);

	// Globbing model files
	config.getGlobbedFiles('./app/models/**/*.js').forEach(function(modelPath) {
		require(path.resolve(modelPath));
	});

	// Setting application local variables
	app.locals.title = config.app.title;
	app.locals.description = config.app.description;
	app.locals.keywords = config.app.keywords;
	app.locals.facebookAppId = config.facebook.clientID;
	app.locals.jsFiles = config.getJavaScriptAssets();
	app.locals.cssFiles = config.getCSSAssets();

	// Passing the request url to environment locals
	app.use(function(req, res, next) {
		res.locals.url = req.protocol + ':// ' + req.headers.host + req.url;
		next();
	});

	// Should be placed before express.static
	app.use(compress({
		filter: function(req, res) {
			return (/json|text|javascript|css/).test(res.getHeader('Content-Type'));
		},
		level: 9
	}));

	// Showing stack errors
	app.set('showStackError', true);

	// Set swig as the template engine
	app.engine('server.view.html', consolidate[config.templateEngine]);

	// Set views path and view engine
	app.set('view engine', 'server.view.html');
	app.set('views', './app/views');

	// Environment dependent middleware
	if (process.env.NODE_ENV === 'development') {
		// Enable logger (morgan)
		app.use(morgan('dev'));

		// Disable views cache
		app.set('view cache', false);
	} else if (process.env.NODE_ENV === 'production') {
		app.locals.cache = 'memory';
	}

	// Request body parsing middleware should be above methodOverride
	app.use(bodyParser.urlencoded());
	app.use(bodyParser.json());
	app.use(methodOverride());

	// Enable jsonp
	app.enable('jsonp callback');

	// CookieParser should be above session
	app.use(cookieParser());

	// Express MongoDB session storage
	app.use(session({
		secret: config.sessionSecret,
		store: new mongoStore({
			db: db.connection.db,
			collection: config.sessionCollection
		})
	}));

	// use passport session
	app.use(passport.initialize());
	app.use(passport.session());

	// connect flash for flash messages
	app.use(flash());

	// Use helmet to secure Express headers
	app.use(helmet.xframe());
	app.use(helmet.iexss());
	app.use(helmet.contentTypeOptions());
	app.use(helmet.ienoopen());
	app.disable('x-powered-by');

	// Setting the app router and static folder
	app.use(express.static(path.resolve('./public')));

	// Globbing routing files
	config.getGlobbedFiles('./app/routes/**/*.js').forEach(function(routePath) {
		require(path.resolve(routePath))(app);
	});

	// Assume 'not found' in the error msgs is a 404. this is somewhat silly, but valid, you can do whatever you like, set properties, use instanceof etc.
	app.use(function(err, req, res, next) {
		// If the error object doesn't exists
		if (!err) return next();

		// Log it
		console.error(err.stack);

		// Error page
		res.status(500).render('500', {
			error: err.stack
		});
	});

	// Assume 404 since no middleware responded
	app.use(function(req, res) {
		res.status(404).render('404', {
			url: req.originalUrl,
			error: 'Not Found'
		});
	});

    // TODO - Put all these in environment variables
    var twitterClient =  new twitter({
                  consumer_key: process.env.TWITTER_KEY || 'UNDEFINED',
                  consumer_secret: process.env.TWITTER_SECRET || 'UNDEFINED',
                  access_token_key: process.env.TWITTER_ACCESS_KEY || 'UNDEFINED',
                  access_token_secret: process.env.TWITTER_ACCESS_SECRET || 'UNDEFINED'
              }),
        stream = null,
        users = [];

    io.on('connection', function (socket) {
        console.log('user connected');

        // The user it's added to the array if it doesn't exist
        if(users.indexOf(socket.id) === -1) {
            users.push(socket.id);
        }

        socket.on('start stream', function() {
            // The stream will be started only when the 1st user arrives
            if(stream === null) {
                twitterClient.stream('statuses/filter', {
                    //'locations':'38.46,-85.11,41.88,-80.37' // ohio
                    'locations' : '-83.1226,39.868,-82.8808,40.12' // NYC/NE
                }, function(s) {
                    stream = s;
                    stream.on('data', function(data) {
                        // only broadcast when users are online
                        if(users.length > 0) {
                            // This emits the signal to all users but the one
                            // that started the stream
                            socket.broadcast.emit('new tweet', data);
                            // This emits the signal to the user that started
                            // the stream
                            socket.emit('new tweet', data);
                        }
                        else {
                            // If there are no users connected we destroy the stream.
                            // Why would we keep it running for nobody?
                            console.log('users have all disconnected');
                            stream.destroy();
                            stream = null;
                        }
                    });

                    stream.on('error', function(type, code, description) {
                        console.log(type + ' ' + code + ': ' + description);
                    });
                });
            }
        });

        // This handles when a user is disconnected
        socket.on('disconnect', function(o) {
            console.log('user disconnected');
            // find the user in the array
            var index = users.indexOf(socket.id);
            if(index != -1) {
                // Eliminates the user from the array
                users.splice(index, 1);
            }

            console.log('number of users connected: ' + users.length);
        });

        // Emits signal when the user is connected sending
        // the tracking words the app it's using
        socket.emit('connected', {
            tracking: ''
        });
  	});

	return server;
};
<|endoftext|>


<commit_before>(function() {
  'use strict';

  /**
   * @namespace   Services
   */
  angular
    .module('badgeFrontend.core.auth.services')
    .factory('UserService', UserService);

  //////////

  /**
   * @description UserService.
   * @namespace   UserService
   * @memberOf    Services
   * @ngInject
   *
   * @param {*} $localStorage
   * @param {*} jwtHelper
   * @returns {{
   *    user: Services.UserService.user
   *  }}
   * @constructor
   */
  function UserService(
    $localStorage,
    jwtHelper
  ) {
    return {
      user: user
    };

    //////////

    /**
     * @name      user
     * @memberOf  Services.UserService
     *
     * @returns   {*}
     */
    function user() {
      return $localStorage.token ? jwtHelper.decodeToken($localStorage.token) : {};
    }
  }
}());
<commit_msg>Changed default value of user data getter.<commit_after>(function() {
  'use strict';

  /**
   * @namespace   Services
   */
  angular
    .module('badgeFrontend.core.auth.services')
    .factory('UserService', UserService);

  //////////

  /**
   * @description UserService.
   * @namespace   UserService
   * @memberOf    Services
   * @ngInject
   *
   * @param {*} $localStorage
   * @param {*} jwtHelper
   * @returns {{
   *    user: Services.UserService.user
   *  }}
   * @constructor
   */
  function UserService(
    $localStorage,
    jwtHelper
  ) {
    return {
      user: user
    };

    //////////

    /**
     * @name      user
     * @memberOf  Services.UserService
     *
     * @returns   {*}
     */
    function user() {
      return $localStorage.token ? jwtHelper.decodeToken($localStorage.token) : false;
    }
  }
}());
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import * as firebase from 'firebase';
import './index.css';
var crit = require("./functions/Crit.js");
var rolldice = require("./functions/Roll.js");

var channel = window.location.pathname.slice(1).toLowerCase(),
    user = window.location.search.slice(1),
    diceOrder = ['yellow', 'green', 'blue', 'red', 'purple', 'black', 'white'],
    symbols = ['success', 'advantage', 'triumph', 'fail', 'threat', 'despair', 'lightside', 'darkside'],
    symbolOrder = ['s', 'a', '!', 'f', 't', 'd', 'l', 'n'];

class Dice extends Component {
  constructor(props) {
    super(props);
    this.state = {
      diceRoll: {},
      rollRef: firebase.database().ref().child(`${channel}`).child('roll'),
      message: {},
      messageRef: firebase.database().ref().child(`${channel}`).child('message'),
      showOptions: 'none',
      optionsRef: firebase.database().ref().child(`${channel}`).child('options'),
      destinyRef: firebase.database().ref().child(`${channel}`).child('destiny')
    };
  }

  componentDidMount() {
    this.reset();
    this.state.rollRef.on('value', snap => {
      this.setState({
        diceRoll: snap.val()
        });
      });
    this.state.optionsRef.on('value', snap => {
      this.setState({
        showOptions: snap.val()
        });
      });
  }

  addDie(diceColor) {
    this.state.rollRef.child(diceColor).set(this.state.diceRoll[diceColor]+1);
  }
  removeDie(diceColor) {
    if (this.state.diceRoll[diceColor] > 0) {
      this.state.rollRef.child(diceColor).set(this.state.diceRoll[diceColor]-1);
    }
  }
  reset() {
    this.state.rollRef.set({yellow:0, green:0, blue:0, red:0, purple:0, black:0, white:0, polyhedral:0});
    diceOrder = ['yellow', 'green', 'blue', 'red', 'purple', 'black', 'white'];
    this.state.optionsRef.set('none');
    this.refs.caption.value = '';
  }
  expandExtras() {
    if (diceOrder.length < 8) {
      diceOrder.push('success', 'advantage', 'triumph', 'fail', 'threat', 'despair', 'lightside', 'darkside');
      this.state.rollRef.set({yellow:this.state.diceRoll['yellow'], green:this.state.diceRoll['green'], blue:this.state.diceRoll['blue'], red:this.state.diceRoll['red'], purple:this.state.diceRoll['purple'], black:this.state.diceRoll['black'], white:this.state.diceRoll['white'], polyhedral:this.state.diceRoll['polyhedral'], success:0, advantage:0, triumph:0, fail:0, threat:0, despair:0, lightside:0, darkside:0});
    } else {
      this.reset();
    }
  }

  dropMenu() {
    if (this.state.showOptions !== 'none'){
      this.state.optionsRef.set('none');
    } else {
      this.state.optionsRef.set('inline-block');
    }
  }

  critical(critical, stop) {
    stop.preventDefault();
    var critRoll = crit.d100(this.refs.modifier.value);
    var critText = ''
    if (critical === 'critical') {
      critText = crit.crit(critRoll[0]);
    } else {
      critText = crit.shipcrit(critRoll[0]);
    }
    critText = user + ' ' + critRoll[1] + `<p>` + critText + `</p>`
    this.state.messageRef.push().set(critText);
    this.refs.modifier.value = '';

  }

  roll() {
      var message = rolldice.roll(this.state.diceRoll, this.refs.polyhedral.value, this.refs.caption.value, diceOrder, symbols, symbolOrder, user)[0];
      if (message !== undefined) {
        this.state.messageRef.push().set(message);
      }
      if (this.refs.resetCheck.checked === false){
        this.reset()
      }
    }

  destinyRoll(){
    var destinyResult = rolldice.roll({white:1}, this.refs.polyhedral.value, diceOrder, symbols, symbolOrder, user);
    var message = destinyResult[0] + `<br/> Adding to the Destiny Pool`;
    destinyResult = destinyResult[1]['white'][0];
    switch(destinyResult) {
      case 'l':
        this.state.destinyRef.push().set('lightside');
        break;
      case 'll':
        this.state.destinyRef.push().set('lightside');
        this.state.destinyRef.push().set('lightside');
        break;
      case "n":
        this.state.destinyRef.push().set('darkside');
        break;
      case 'nn':
        this.state.destinyRef.push().set('darkside');
        this.state.destinyRef.push().set('darkside');
        break;
      default:
        break;
    }
    this.state.messageRef.push().set(message);
  }

  render() {
    return (

      {diceOrder.map((diceColor) =>

            <button className='btnAdd' onClick={this.addDie.bind(this, diceColor)}>↑</button>
            <button className='btnAdd' onClick={this.removeDie.bind(this, diceColor)}>↓</button>

 {this.state.diceRoll[diceColor]}

              <img
              className='dice'
              key={diceColor}
              style={{float: 'left', marginLeft: 15}}
              onClick={this.addDie.bind(this, diceColor)}
              src={`/images/${diceColor}.png`}
              alt={`${diceColor}`} />

      )}

          <button className='btnAdd' onClick={this.addDie.bind(this, 'polyhedral')}>↑</button>
          <button className='btnAdd' onClick={this.removeDie.bind(this, 'polyhedral')}>↓</button>

 {this.state.diceRoll.polyhedral}

          <input className='textinput' style={{float: 'left', marginLeft: 15, width: '3em', textAlign: 'center', margin: '15px 0px 15px 25px'}} ref='polyhedral' defaultValue='100' />

      <input type='button' ref='extras' className='lrgButton' style={{verticalAlign:'bottom', margin: '5px'}} onClick={this.expandExtras.bind(this)} value='Symbols' />

        <input type='button' ref='roll' className='lrgButton' onClick={this.roll.bind(this)} value='Roll' />
        <input className='textinput' ref='caption' name='caption' placeholder='caption' style={{width: '70px', paddingLeft: '5px'}}/>
        <input type='button' ref='reset' className='lrgButton' style={{background: '#9e9e9e'}} onClick={this.reset.bind(this)} value='Reset' />
        <input type='button' ref='specialRollsDropDown' onClick={this.dropMenu.bind(this)} className='lrgButton' style={{width: '100px'}} value='Special Rolls'/>
        <input type="checkbox" ref='resetCheck' />Save dice pool

        <form onSubmit={this.critical.bind(this, 'critical')}>
          <button className='lrgButton' style={{width: '100px'}}>Critical</button>
          <button className='lrgButton' style={{width: '100px'}}>Ship Critical</button>
          <input className='textinput' ref='modifier' name='modifier' placeholder='modifier' style={{width: '70px', paddingLeft: '5px'}}/>
        </form>
          <input type='button' style={{width: '100px'}} ref='destinyRoll' className='lrgButton' onClick={this.destinyRoll.bind(this)} value='Roll Destiny' />

    );
  }
}
  export default Dice;
<commit_msg>fixed destiny roll<commit_after>import React, { Component } from 'react';
import * as firebase from 'firebase';
import './index.css';
var crit = require("./functions/Crit.js");
var rolldice = require("./functions/Roll.js");

var channel = window.location.pathname.slice(1).toLowerCase(),
    user = window.location.search.slice(1),
    diceOrder = ['yellow', 'green', 'blue', 'red', 'purple', 'black', 'white'],
    symbols = ['success', 'advantage', 'triumph', 'fail', 'threat', 'despair', 'lightside', 'darkside'],
    symbolOrder = ['s', 'a', '!', 'f', 't', 'd', 'l', 'n'];

class Dice extends Component {
  constructor(props) {
    super(props);
    this.state = {
      diceRoll: {},
      rollRef: firebase.database().ref().child(`${channel}`).child('roll'),
      message: {},
      messageRef: firebase.database().ref().child(`${channel}`).child('message'),
      showOptions: 'none',
      optionsRef: firebase.database().ref().child(`${channel}`).child('options'),
      destinyRef: firebase.database().ref().child(`${channel}`).child('destiny')
    };
  }

  componentDidMount() {
    this.reset();
    this.state.rollRef.on('value', snap => {
      this.setState({
        diceRoll: snap.val()
        });
      });
    this.state.optionsRef.on('value', snap => {
      this.setState({
        showOptions: snap.val()
        });
      });
  }

  addDie(diceColor) {
    this.state.rollRef.child(diceColor).set(this.state.diceRoll[diceColor]+1);
  }
  removeDie(diceColor) {
    if (this.state.diceRoll[diceColor] > 0) {
      this.state.rollRef.child(diceColor).set(this.state.diceRoll[diceColor]-1);
    }
  }
  reset() {
    this.state.rollRef.set({yellow:0, green:0, blue:0, red:0, purple:0, black:0, white:0, polyhedral:0});
    diceOrder = ['yellow', 'green', 'blue', 'red', 'purple', 'black', 'white'];
    this.state.optionsRef.set('none');
    this.refs.caption.value = '';
  }
  expandExtras() {
    if (diceOrder.length < 8) {
      diceOrder.push('success', 'advantage', 'triumph', 'fail', 'threat', 'despair', 'lightside', 'darkside');
      this.state.rollRef.set({yellow:this.state.diceRoll['yellow'], green:this.state.diceRoll['green'], blue:this.state.diceRoll['blue'], red:this.state.diceRoll['red'], purple:this.state.diceRoll['purple'], black:this.state.diceRoll['black'], white:this.state.diceRoll['white'], polyhedral:this.state.diceRoll['polyhedral'], success:0, advantage:0, triumph:0, fail:0, threat:0, despair:0, lightside:0, darkside:0});
    } else {
      this.reset();
    }
  }

  dropMenu() {
    if (this.state.showOptions !== 'none'){
      this.state.optionsRef.set('none');
    } else {
      this.state.optionsRef.set('inline-block');
    }
  }

  critical(critical, stop) {
    stop.preventDefault();
    var critRoll = crit.d100(this.refs.modifier.value);
    var critText = ''
    if (critical === 'critical') {
      critText = crit.crit(critRoll[0]);
    } else {
      critText = crit.shipcrit(critRoll[0]);
    }
    critText = user + ' ' + critRoll[1] + `<p>` + critText + `</p>`
    this.state.messageRef.push().set(critText);
    this.refs.modifier.value = '';

  }

  roll() {
      var message = rolldice.roll(this.state.diceRoll, this.refs.polyhedral.value, this.refs.caption.value, diceOrder, symbols, symbolOrder, user)[0];
      if (message !== undefined) {
        this.state.messageRef.push().set(message);
      }
      if (this.refs.resetCheck.checked === false){
        this.reset()
      }
    }

  destinyRoll(){
    var destinyResult = rolldice.roll({white:1}, this.refs.polyhedral.value, '', diceOrder, symbols, symbolOrder, user);
    console.log(destinyResult);

    var message = destinyResult[0] + `<br/> Adding to the Destiny Pool`;
    destinyResult = destinyResult[1]['white'][0];
    switch(destinyResult) {
      case 'l':
        this.state.destinyRef.push().set('lightside');
        break;
      case 'll':
        this.state.destinyRef.push().set('lightside');
        this.state.destinyRef.push().set('lightside');
        break;
      case "n":
        this.state.destinyRef.push().set('darkside');
        break;
      case 'nn':
        this.state.destinyRef.push().set('darkside');
        this.state.destinyRef.push().set('darkside');
        break;
      default:
        break;
    }
    this.state.messageRef.push().set(message);
  }

  render() {
    return (

      {diceOrder.map((diceColor) =>

            <button className='btnAdd' onClick={this.addDie.bind(this, diceColor)}>↑</button>
            <button className='btnAdd' onClick={this.removeDie.bind(this, diceColor)}>↓</button>

 {this.state.diceRoll[diceColor]}

              <img
              className='dice'
              key={diceColor}
              style={{float: 'left', marginLeft: 15}}
              onClick={this.addDie.bind(this, diceColor)}
              src={`/images/${diceColor}.png`}
              alt={`${diceColor}`} />

      )}

          <button className='btnAdd' onClick={this.addDie.bind(this, 'polyhedral')}>↑</button>
          <button className='btnAdd' onClick={this.removeDie.bind(this, 'polyhedral')}>↓</button>

 {this.state.diceRoll.polyhedral}

          <input className='textinput' style={{float: 'left', marginLeft: 15, width: '3em', textAlign: 'center', margin: '15px 0px 15px 25px'}} ref='polyhedral' defaultValue='100' />

      <input type='button' ref='extras' className='lrgButton' style={{verticalAlign:'bottom', margin: '5px'}} onClick={this.expandExtras.bind(this)} value='Symbols' />

        <input type='button' ref='roll' className='lrgButton' onClick={this.roll.bind(this)} value='Roll' />
        <input className='textinput' ref='caption' name='caption' placeholder='caption' style={{width: '70px', paddingLeft: '5px'}}/>
        <input type='button' ref='reset' className='lrgButton' style={{background: '#9e9e9e'}} onClick={this.reset.bind(this)} value='Reset' />
        <input type='button' ref='specialRollsDropDown' onClick={this.dropMenu.bind(this)} className='lrgButton' style={{width: '100px'}} value='Special Rolls'/>
        <input type="checkbox" ref='resetCheck' />Save dice pool

        <form onSubmit={this.critical.bind(this, 'critical')}>
          <button className='lrgButton' style={{width: '100px'}}>Critical</button>
          <button className='lrgButton' style={{width: '100px'}}>Ship Critical</button>
          <input className='textinput' ref='modifier' name='modifier' placeholder='modifier' style={{width: '70px', paddingLeft: '5px'}}/>
        </form>
          <input type='button' style={{width: '100px'}} ref='destinyRoll' className='lrgButton' onClick={this.destinyRoll.bind(this)} value='Roll Destiny' />

    );
  }
}
  export default Dice;
<|endoftext|>


<commit_before>// ==UserScript==
// @name         Booru Tag Parser
// @namespace    http://above.average.website
// @version      1.0.3
// @description  Copy current post tags and rating on boorus and illustration2vec in to the clipboard for easy import in to a program or another booru.
// @author       William Moodhe
// @downloadURL  https://github.com/JetBoom/boorutagparser/raw/master/boorutagparser.user.js
// @updateURL    https://github.com/JetBoom/boorutagparser/raw/master/boorutagparser.user.js

// Illustration2Vec
// @include      *demo.illustration2vec.net*

// Catch-all for boorus
// @include      *booru*/post*
// @include      *booru*/*?page=post*
// @include      *booru*/?page=post

// Boorus with weird names
// @include     *rule34.xxx/index.php?page=post*

// @run-at       document-end
// @grant        GM_setClipboard

// ==/UserScript==
/* jshint -W097 */
'use strict';

///////

var copy_key_code = 221; // ] key
var copy_sound = 'http://heavy.noxiousnet.com/boorucopy.ogg';
var iv2_confidence_rating = 20.0;

///////

var tags_selector = 'h5, b';
var copy_button_style = 'position:fixed;width:100px;right:2px;top:2px;font:11px monospace;font-weight:0;border:1px solid black;background:rgba(255,255,255,0.8);z-index:9999;';

function replaceAll(str, originalstr, newstr)
{
    return str.split(originalstr).join(newstr);
}

function insertTags(tags, selector, prefix)
{
    var elements = document.querySelectorAll(selector);

    for (var i=0; i < elements.length; i++)
    {
        var element = elements[i];
        var text = element.innerHTML;
        if (text.length > 1) // Don't copy - + or ?
        {
            text = replaceAll(text, '_', ' ');
            text = replaceAll(text, '>', '>');
            text = replaceAll(text, '<', '<');

            tags[tags.length] = prefix+text;
        }
    }
}

function insertRating(tags, selector)
{
    var elements = document.querySelectorAll(selector);

    for (var i=0; i < elements.length; i++)
    {
        var element = elements[i];
        var text = element.innerHTML;
        text = replaceAll(text.toLowerCase().trim(), ' ', '');

        if (text.indexOf('rating:explicit') >= 0)
        {
            tags[tags.length] = 'rating:explicit';
            break;
        }
        else if (text.indexOf('rating:questionable') >= 0)
        {
            tags[tags.length] = 'rating:questionable';
            break;
        }
        else if (text.indexOf('rating:safe') >= 0)
        {
            tags[tags.length] = 'rating:safe';
            break;
        }
    }
}

function copyBooruTags(tags, noRating)
{
    // Instead of having a list of boorus and their tag structure I just make a big catch-all.

    // danbooru-like
    insertTags(tags, '#tag-list li.category-3 > a.search-tag', 'series:');
    insertTags(tags, '#tag-list li.category-1 > a.search-tag', 'creator:');
    insertTags(tags, '#tag-list li.category-4 > a.search-tag', 'character:');
    insertTags(tags, '#tag-list li.category-0 > a.search-tag', '');

    // lolibooru-like
    insertTags(tags, 'li.tag-type-copyright > a', 'series:');
    insertTags(tags, 'li.tag-type-author > a', 'creator:');
    insertTags(tags, 'li.tag-type-character > a', 'character:');
    insertTags(tags, 'li.tag-type-general > a', '');

    // booru.org-like
    insertTags(tags, '#tag_list li a', '');

    if (!noRating)
    {
        // danbooru-like
        insertRating(tags, '#post-information > ul li');

        // lolibooru-like
        insertRating(tags, '#stats > ul li');

        // booru.org-like
        insertRating(tags, '#tag_list ul');
    }

    /*var elements = document.querySelectorAll(tags_selector);
    for (var i=0; i < elements.length; i++)
    {
        var element = elements[i];
        if (element.innerHTML == 'Tags')
        {
            element.innerHTML = 'Tags (Copied)';
            element.style.color = 'lime';
            break;
        }
    }*/
}

function insertI2VTags(tags, selector, prefix, confidenceRequired)
{
    var elements = document.querySelectorAll(selector);
    for (var i=1; i < elements.length; i++)
    {
        var element = elements[i];

        if (confidenceRequired > 0)
        {
            var confidence = element.children[3].children[0].innerHTML;
            confidence = confidence.substr(0, confidence.length - 1);
            confidence = Number(confidence);

            if (confidence < confidenceRequired)
                continue;
        }

        var tag = element.children[1].innerHTML;

        tag = replaceAll(tag, '_', ' ');

        if (prefix)
            tag = prefix + tag;

        tags[tags.length] = tag;

        if (prefix == 'rating') // only add one rating
            break;
    }
}

function copyI2VTags(tags, confidenceRequired, noGeneral, noRating)
{
    insertI2VTags(tags, 'table#copyright_root tr', 'series:', confidenceRequired);
    insertI2VTags(tags, 'table#character_root tr', 'character:', confidenceRequired);

    if (!noGeneral)
        insertI2VTags(tags, 'table#general_root tr', '', confidenceRequired);

    if (!noRating)
        insertI2VTags(tags, 'table#rating_root tr', 'rating:', confidenceRequired);
}

function doCopyAll()
{
    var tags = [];

    copyBooruTags(tags);
    copyI2VTags(tags, iv2_confidence_rating, false);

    GM_setClipboard(tags.join('\n'));

    var buttontext = '';

    if (tags.length > 0)
    {
        playCopySound();
        buttontext = 'copied ' +  tags.length +' tag(s)'
    }
    else
        buttontext = 'nothing to copy!';

    var button = document.querySelector('button#copytagsbutton');
    if (button)
    {
        button.innerHTML = buttontext;
        setTimeout(function(){ button.innerHTML = 'copy tags'; }, 3000);
    }
}

function doc_keyUp(e)
{
    if (e.keyCode == copy_key_code)
    {
        doCopyAll();
    }
}
document.addEventListener('keyup', doc_keyUp, false);

var elements = document.querySelectorAll(tags_selector);
for (var i=0; i < elements.length; i++)
{
    var element = elements[i];
    if (element.innerHTML == 'Tags')
    {
        element.onclick = copyBooruTags;

        // hydrus stuff
        //element.outerHTML = '
<form enctype="multipart/form-data" action="http://localhost:45866" method="POST">'
        //    + '<input name="file" type="file" />'
        //    + '<input type="submit" value="Send" />'
        //    + '</form>
 '
        //    + element.outerHTML;

        break;
    }
}

if (copy_sound.length > 0)
{
    var audio = document.createElement("audio");
    audio.src = copy_sound;
    audio.preload = false;
}

function playCopySound()
{
    if (audio)
        audio.play();
}

var copyButtonArea = document.createElement('button');
copyButtonArea.innerHTML = 'copy tags';
copyButtonArea.id = 'copytagsbutton';
copyButtonArea.setAttribute('style', copy_button_style);
copyButtonArea.onclick = doCopyAll;
document.body.appendChild(copyButtonArea);
<commit_msg>Support more sites and tag types<commit_after>// ==UserScript==
// @name         Booru Tag Parser
// @namespace    http://above.average.website
// @version      1.0.4
// @description  Copy current post tags and rating on boorus and illustration2vec in to the clipboard for easy import in to a program or another booru.
// @author       William Moodhe
// @downloadURL  https://github.com/JetBoom/boorutagparser/raw/master/boorutagparser.user.js
// @updateURL    https://github.com/JetBoom/boorutagparser/raw/master/boorutagparser.user.js

// Illustration2Vec
// @include      *demo.illustration2vec.net*

// Catch-all for boorus
// @include      *booru*/post*
// @include      *booru*/*?page=post*
// @include      *booru*/?page=post

// Boorus with weird names
// @include      *rule34.xxx/index.php?page=post*
// @include      *chan.sankakucomplex.com/post/show/*
// @include      *chan.sankakucomplex.com/?tags=*
// @include      *e621.net/post/*
// @include      *konachan.com/post/*
// @include      *shimmie.katawa-shoujo.com/post/*
// @include      *rule34.paheal.net/post/*
// @include      *rule34hentai.net/post/*
// @include      *tbib.org/index.php?page=post*
// @include      *yande.re/post/*

// @run-at       document-end
// @grant        GM_setClipboard

// ==/UserScript==
/* jshint -W097 */
'use strict';

///////

var copy_key_code = 221; // ] key
var copy_sound = 'http://heavy.noxiousnet.com/boorucopy.ogg';
var iv2_confidence_rating = 20.0;

///////

var tags_selector = 'h5, b';
var copy_button_style = 'position:fixed;width:100px;right:2px;top:2px;font:11px monospace;font-weight:0;border:1px solid black;background:rgba(255,255,255,0.8);z-index:9999;';

function replaceAll(str, originalstr, newstr)
{
    return str.split(originalstr).join(newstr);
}

function insertTags(tags, selector, prefix)
{
    var elements = document.querySelectorAll(selector);

    for (var i=0; i < elements.length; i++)
    {
        var element = elements[i];
        var text = element.innerHTML;
        if (text.length > 1) // Don't copy - + or ?
        {
            text = replaceAll(text, '_', ' ');
            text = replaceAll(text, '>', '>');
            text = replaceAll(text, '<', '<');

            tags[tags.length] = prefix+text;
        }
    }
}

function insertRating(tags, selector)
{
    var elements = document.querySelectorAll(selector);

    for (var i=0; i < elements.length; i++)
    {
        var element = elements[i];
        var text = element.innerHTML;
        text = replaceAll(text.toLowerCase().trim(), ' ', '');

        if (text.indexOf('rating:explicit') >= 0)
        {
            tags[tags.length] = 'rating:explicit';
            break;
        }
        else if (text.indexOf('rating:questionable') >= 0)
        {
            tags[tags.length] = 'rating:questionable';
            break;
        }
        else if (text.indexOf('rating:safe') >= 0)
        {
            tags[tags.length] = 'rating:safe';
            break;
        }
    }
}

function copyBooruTags(tags, noRating)
{
    // Instead of having a list of boorus and their tags and tag structures I just make a big catch-all.

    // danbooru-like
    insertTags(tags, '#tag-list li.category-3 > a.search-tag', 'series:');
    insertTags(tags, '#tag-list li.category-1 > a.search-tag', 'creator:');
    insertTags(tags, '#tag-list li.category-4 > a.search-tag', 'character:');
    insertTags(tags, '#tag-list li.category-0 > a.search-tag', '');

    // lolibooru-like
    insertTags(tags, 'li.tag-type-copyright > a', 'series:');
    insertTags(tags, 'li.tag-type-author > a', 'creator:');
    insertTags(tags, 'li.tag-type-artist > a', 'creator:');
    insertTags(tags, 'li.tag-type-character > a', 'character:');
    insertTags(tags, 'li.tag-type-general > a', '');
    insertTags(tags, 'li.tag-type-studio > a', 'studio:');
    insertTags(tags, 'li.tag-type-circle > a', 'studio:');
    insertTags(tags, 'li.tag-type-medium > a', 'medium:');
    insertTags(tags, 'li.tag-type-style > a', 'medium:');
    insertTags(tags, 'li.tag-type-meta > a', 'meta:');
    insertTags(tags, 'li.tag-type-species > a', 'species:');
    insertTags(tags, 'li.tag-type-faults > a', 'fault:');

    // booru.org-like
    insertTags(tags, '#tag_list li a', '');

    // paheal-like
    insertTags(tags, 'a.tag_name', '');

    if (!noRating)
    {
        // danbooru-like
        insertRating(tags, '#post-information > ul li');

        // lolibooru-like
        insertRating(tags, '#stats > ul li');

        // booru.org-like
        insertRating(tags, '#tag_list ul');
    }

    /*var elements = document.querySelectorAll(tags_selector);
    for (var i=0; i < elements.length; i++)
    {
        var element = elements[i];
        if (element.innerHTML == 'Tags')
        {
            element.innerHTML = 'Tags (Copied)';
            element.style.color = 'lime';
            break;
        }
    }*/
}

function insertI2VTags(tags, selector, prefix, confidenceRequired)
{
    var elements = document.querySelectorAll(selector);
    for (var i=1; i < elements.length; i++)
    {
        var element = elements[i];

        if (confidenceRequired > 0)
        {
            var confidence = element.children[3].children[0].innerHTML;
            confidence = confidence.substr(0, confidence.length - 1);
            confidence = Number(confidence);

            if (confidence < confidenceRequired)
                continue;
        }

        var tag = element.children[1].innerHTML;

        tag = replaceAll(tag, '_', ' ');

        if (prefix)
            tag = prefix + tag;

        tags[tags.length] = tag;

        if (prefix == 'rating') // only add one rating
            break;
    }
}

function copyI2VTags(tags, confidenceRequired, noGeneral, noRating)
{
    insertI2VTags(tags, 'table#copyright_root tr', 'series:', confidenceRequired);
    insertI2VTags(tags, 'table#character_root tr', 'character:', confidenceRequired);

    if (!noGeneral)
        insertI2VTags(tags, 'table#general_root tr', '', confidenceRequired);

    if (!noRating)
        insertI2VTags(tags, 'table#rating_root tr', 'rating:', confidenceRequired);
}

function doCopyAll()
{
    var tags = [];

    copyBooruTags(tags);
    copyI2VTags(tags, iv2_confidence_rating, false);

    GM_setClipboard(tags.join('\n'));

    var buttontext = '';

    if (tags.length > 0)
    {
        playCopySound();
        buttontext = 'copied ' +  tags.length +' tag(s)'
    }
    else
        buttontext = 'nothing to copy!';

    var button = document.querySelector('button#copytagsbutton');
    if (button)
    {
        button.innerHTML = buttontext;
        setTimeout(function(){ button.innerHTML = 'copy tags'; }, 3000);
    }
}

function doc_keyUp(e)
{
    if (e.keyCode == copy_key_code)
    {
        doCopyAll();
    }
}
document.addEventListener('keyup', doc_keyUp, false);

var elements = document.querySelectorAll(tags_selector);
for (var i=0; i < elements.length; i++)
{
    var element = elements[i];
    if (element.innerHTML == 'Tags')
    {
        element.onclick = copyBooruTags;

        // hydrus stuff
        //element.outerHTML = '
<form enctype="multipart/form-data" action="http://localhost:45866" method="POST">'
        //    + '<input name="file" type="file" />'
        //    + '<input type="submit" value="Send" />'
        //    + '</form>
 '
        //    + element.outerHTML;

        break;
    }
}

if (copy_sound.length > 0)
{
    var audio = document.createElement("audio");
    audio.src = copy_sound;
    audio.preload = false;
}

function playCopySound()
{
    if (audio)
        audio.play();
}

var copyButtonArea = document.createElement('button');
copyButtonArea.innerHTML = 'copy tags';
copyButtonArea.id = 'copytagsbutton';
copyButtonArea.setAttribute('style', copy_button_style);
copyButtonArea.onclick = doCopyAll;
document.body.appendChild(copyButtonArea);
<|endoftext|>