module Mushy::Builder::Index

Public Class Methods

file() click to toggle source
# File lib/mushy/builder/index.rb, line 4
      def self.file

<<-ENDEND
<html>
    <head>
        <link rel="stylesheet" href="/dark.css">
        <script src="/vue.js"></script>
        <script src="/axios.js"></script>
    </head>
    <body>
        <div id="app">
            <table v-if="setup.showFlux == false">
                <tr>
                    <th>Name</th>
                    <th>Receives Events From</th>
                    <th>Actions</th>
                </tr>
                <tr v-for="flux in flow.fluxs">
                    <td>{{flux.name}}</td>
                    <td>{{flux_name_for(flux.parents, flow.fluxs)}}</td>
                    <td>
                        <button v-on:click.prevent.stop="editFlux({ flux: flux, setup: setup, configs: configs })">Edit</button>
                        <button v-on:click.prevent.stop="deleteFlux({ flux: flux, flow: flow })">Delete</button>
                    </td>
                </tr>
            </table>
            <button v-if="setup.showFlux == false" v-on:click.prevent.stop="startNew({ setup: setup, configs: configs })">Add a New Flux To This Flow</button>
            <div v-if="setup.showFlux">
                <mip-heavy :data="setup"></mip-heavy>
                <mip-heavy v-for="(data, id) in configs" v-show="setup.flux.value === id" :data="data"></mip-heavy>
                <div v-if="results.errorMessage">{{results.errorMessage}}</div>
                <div v-else>{{results.length}} result{{results.length == 1 ? "" : "s"}}</div>
                <mip-heavy v-for="data in results" :data="data"></mip-heavy>
            </div>
        </div>
    </body>
</html>

<script type="text/javascript">

   var fancyName = function(id) { return '(' + id + '.split(/[ _]/).map(function(x){return x[0].toUpperCase() + x.substring(1)}).join(\\' \\'))' };
   var thingToData = function(thing) {
                         var record = {};
                         for (var key in thing)
                             if (thing[key].value)
                                 if (thing[key].type == 'json')
                                     record[key] = JSON.parse(thing[key].value);
                                 else
                                     record[key] = thing[key].value;
                         return record;
                     };

   var components = {
       label: {
           props: ['label', 'description', 'hide_description'],
           template: '<label :for="id" v-if="label != \\'\\'">{{label || ' + fancyName('id') + '}} <i v-show="description && !hide_description">({{description}})</i></label>'
       },
       h1: {
           props: ['label', 'description', 'hide_description'],
           template: '<h1 v-if="label != \\'\\'">{{label || ' + fancyName('id') + '}} <i v-show="description && !hide_description">({{description}})</i></h1>'
       },
       h2: {
           props: ['label', 'description', 'hide_description'],
           template: '<h2 v-if="label != \\'\\'">{{label || ' + fancyName('id') + '}} <i v-show="description && !hide_description">({{description}})</i></h2>'
       },
       h3: {
           props: ['label', 'description', 'hide_description'],
           template: '<h3 v-if="label != \\'\\'">{{label || ' + fancyName('id') + '}} <i v-show="description && !hide_description">({{description}})</i></h3>'
       },
       h4: {
           props: ['label', 'description', 'hide_description'],
           template: '<h4 v-if="label != \\'\\'">{{label || ' + fancyName('id') + '}} <i v-show="description && !hide_description">({{description}})</i></h4>'
       },
       text:  {
           props: ['label', 'placeholder', 'disabled', 'readonly', 'value', 'description', 'shrink'],
           template: '<div><mip-label :id="id" :label="label" :description="description" :hide_description="shrink"></mip-label> <a href="#" v-on:click.prevent.stop="shrink=false" v-show="shrink && !value">[^]</a><input type="text" :name="id" v-if="value || !shrink" :placeholder="placeholder" v-bind:value="value" v-on:input="$emit(\\'update:value\\', $event.target.value);" :disabled="disabled == \\'true\\'" :readonly="readonly == \\'true\\'"></div>'
       },
       hide:  {
           props: ['label', 'description'],
           template: '<mip-label :id="id" :label="label" :description="description" v-if="false"></mip-label>'
       },
       integer:  {
           props: ['label', 'placeholder', 'disabled', 'readonly', 'value', 'description', 'shrink'],
           template: '<div><mip-label :id="id" :label="label" :description="description" :hide_description="shrink"></mip-label> <a href="#" v-on:click.prevent.stop="shrink=false" v-show="shrink && !value">[^]</a><input type="text" :name="id" v-if="value || !shrink" :placeholder="placeholder" v-bind:value="value" v-on:input="$emit(\\'update:value\\', $event.target.value);" :disabled="disabled == \\'true\\'" :readonly="readonly == \\'true\\'"></div>'
       },
       email: {
           props: ['label', 'placeholder', 'disabled', 'readonly', 'value', 'description', 'shrink'],
           template: '<div><mip-label :id="id" :label="label" :description="description" :hide_description="shrink"></mip-label> <a href="#" v-on:click.prevent.stop="shrink=false" v-show="shrink && !value">[^]</a><input type="email" :name="id" v-if="value || !shrink" :placeholder="placeholder" v-bind:value="value" v-on:input="$emit(\\'update:value\\', $event.target.value)" :disabled="disabled == \\'true\\'" :readonly="readonly == \\'true\\'"></div>'
       },
       textarea: {
           props: ['label', 'placeholder', 'disabled', 'readonly', 'value', 'description', 'shrink'],
           template: '<div><mip-label :id="id" :label="label" :description="description" :hide_description="shrink"></mip-label> <a href="#" v-on:click.prevent.stop="shrink=false" v-show="shrink && !value">[^]</a><textarea :name="id" v-if="value || !shrink" :placeholder="placeholder" v-bind:value="value" v-on:input="$emit(\\'update:value\\', $event.target.value)" :disabled="disabled == \\'true\\'" :readonly="readonly == \\'true\\'"></textarea></div>'
       },
       json: {
           props: ['label', 'placeholder', 'disabled', 'readonly', 'value', 'description', 'shrink'],
           template: '<div><mip-label :id="id" :label="label" :description="description" :hide_description="shrink"></mip-label> <a href="#" v-on:click.prevent.stop="shrink=false" v-show="shrink && !value">[^]</a><textarea :name="id" v-if="value || !shrink" :placeholder="placeholder" v-bind:value="value" v-on:input="$emit(\\'update:value\\', $event.target.value)" :disabled="disabled == \\'true\\'" :readonly="readonly == \\'true\\'"></textarea></div>'
       },
       jsonview: {
           data: function() {
               return {
                   toggle: function(view) { return view == 'beautiful' ? 'thin' : 'beautiful'; },
                   copy: function(view, json) { return navigator.clipboard.writeText((view == 'beautiful' ? JSON.stringify(json, null, "  ") : JSON.stringify(json))); },
                   show: function(view, json) { return view == 'beautiful' ? JSON.stringify(json, null, "  ") : JSON.stringify(json); },
               };
           },
           props: ['label', 'placeholder', 'disabled', 'readonly', 'value', 'description', 'view'],
           template: '<div><mip-h4 :id="id" :label="label" :description="description"></mip-h4> <pre><code>{{show(view, value)}}</code></pre><button :disabled="view==\\'beautiful\\'" v-on:click.prevent.stop="view=toggle(view)">View Pretty</button><button :disabled="view!=\\'beautiful\\'" v-on:click.prevent.stop="view=toggle(view)">View Smaller</button><button v-on:click.prevent.stop="copy(view, value)">Copy</button></div>'
       },
       radio: {
           props: ['label', 'value', 'options', 'description', 'shrink'],
           template: '<div><mip-label :id="id" :label="label" :description="description" :hide_description="shrink"></mip-label> <a href="#" v-on:click.prevent.stop="shrink=false" v-show="shrink && !value">[^]</a><div v-for="option in options"><input type="radio" :name="id" v-bind:value="option" v-if="value || !shrink" v-on:input="$emit(\\'update:value\\', $event.target.value)" :checked="value == option"> <label for="option">{{option}}</label></div></div>'
       },
       select: {
           props: ['label', 'value', 'options', 'description', 'shrink'],
           template: '<div><mip-label :id="id" :label="label" :description="description" :hide_description="shrink"></mip-label> <a href="#" v-on:click.prevent.stop="shrink=false" v-show="shrink && !value">[^]</a><select :name="id" v-if="value || !shrink" v-on:input="$emit(\\'update:value\\', $event.target.value)"><option v-for="option in options" v-bind:value="option" :selected="value == option">{{option}}</option></select></div>'
       },
       selectrecord: {
           props: ['label', 'value', 'options', 'description', 'shrink'],
           template: '<div><mip-label :id="id" :label="label" :description="description" :hide_description="shrink"></mip-label> <a href="#" v-on:click.prevent.stop="shrink=false" v-show="shrink && !value">[^]</a><select :name="id" v-if="value || !shrink" v-on:input="$emit(\\'update:value\\', $event.target.value)"><option v-for="option in options" v-bind:value="option.id" :selected="value == option.id">{{option.name}}</option></select></div>'
       },
       selectmanyrecords: {
           data: function() {
               return {
                   selectedValue: '',
                   remove: function(value, set) {
                       if (set.includes(value) == false) return;
                       for(var i = 0; i < set.length; i++)
                       {
                           if (set[i] === value)
                           {
                               set.splice(i, 1);
                               i--;
                           }
                       }
                       return set;
                    },
                   doit: function(value, set) {
                       if (set.includes(value) == false)
                           set.push(value);
                       return set;
                    },
               };
           },
           props: ['label', 'value', 'options', 'description', 'shrink'],
           template: '<div><mip-label :id="id" :label="label" :description="description" :hide_description="shrink"></mip-label> <a href="#" v-on:click.prevent.stop="shrink=false" v-show="shrink && !value">[^]</a> <span v-for="option in options" v-if="value && value.includes(option.id)">{{option.name}} <a href="#" v-on:click.prevent.stop="remove(option.id, value);$emit(\\'update:value\\', value)">[X]</a> </span> <a href="#" v-on:click.prevent.stop="doit(selectedValue, value);$emit(\\'update:value\\', value)">ADD</a> <select :name="id" v-if="value || !shrink" v-on:input="selectedValue=$event.target.value;"><option v-for="option in options" v-bind:value="option.id">{{option.name}}</option></select></div>'
       },
       boolean: {
           props: ['label', 'value', 'options', 'description', 'shrink'],
           template: '<div><mip-label :id="id" :label="label" :description="description" :hide_description="shrink"></mip-label> <a href="#" v-on:click.prevent.stop="shrink=false" v-show="shrink && !value">[^]</a><select :name="id" v-if="(value != undefined && value != null && value != \\'\\') || !shrink" v-on:input="$emit(\\'update:value\\', $event.target.value)"><option v-for="option in [true, false]" v-bind:value="option" :selected="value == option">{{option}}</option></select></div>'
       },
       table: {
           props: ['value', 'description'],
           template: '<table><tr><th v-for="(d, i) in value[0]">{{' + fancyName('i') + '}}</th></tr><tr v-for="v in value"><td v-for="(d, i) in v">{{d}}</td></tr></table>'
       },
       editgrid: {
           data: function() {
               return {
                   removeRecord: function(record, records, index) { records.splice(index, 1); },
                   addRecord: function(editors, records) {
                                                    var record = {};
                                                    for (var i = 0; i < editors.length; i++)
                                                    {
                                                        record[editors[i].target] = editors[i].field.value;
                                                        editors[i].field.value = editors[i].field.default ? editors[i].field.default : '';
                                                    }
                                                    records.push(record);
                                                }
               };
           },
           props: ['value', 'editors', 'label', 'description', 'shrink'],
           template: '<div><mip-label :id="id" :label="label" :description="description" :hide_description="shrink"></mip-label> <a href="#" v-on:click.prevent.stop="shrink=false" v-show="shrink && value.length == 0">[^]</a><table v-if="value.length > 0 || !shrink"><tr><th v-for="(d, i) in value[0]">{{' + fancyName('i') + '}}</th></tr><tr v-for="(v, z) in value"><td v-for="(d, i) in v">{{d}}</td><td><a href="#" v-on:click.prevent.stop="removeRecord(v, value, z)">[x]</a></td></tr><tr><td v-for="editor in editors"><mip-thing :data="editor.field" :id="editor.id"></mip-thing></td><td><a href="#" v-on:click.prevent.stop="addRecord(editors, value)">[Add]</a></td></tr></table></div>'
       },
       keyvalue: {
           data: function() {
               return {
                   actionText: function(value, others) { found = false; for(var i in others){ if (i == value) found = true; } return found ? 'Replace' : 'Add'; },
                   removeRecord: function(data, key) { Vue.delete(data, key) },
                   addRecord: function(editors, data) {
                                                   Vue.set(data, editors[0].field.value, editors[1].field.value)
                                                   editors[0].field.value = editors[0].field.default;
                                                   editors[1].field.value = editors[1].field.default;
                                                }
               };
           },
           props: ['value', 'label', 'editors', 'description', 'shrink'],
           template: '<div><mip-label :id="id" :label="label" :description="description" :hide_description="shrink"></mip-label> <a href="#" v-on:click.prevent.stop="shrink=false" v-show="shrink">[^]</a><table v-if="JSON.stringify(value) != \\'{}\\' || !shrink"><tr v-for="(v, k) in value"><td>{{k}}</td><td>{{v}}</td><td><button v-on:click.prevent.stop="removeRecord(value, k)">Remove {{k}}</button></td></tr><tr><td v-for="editor in editors"><mip-thing :data="editor.field" :id="editor.id"></mip-thing></td><td><button v-on:click.prevent.stop="addRecord(editors, value)" v-show="editors[0].field.value">{{actionText(editors[0].field.value, value)}} {{editors[0].field.value}}</button></td></tr></table></div>'
       },
       button: {
           props: ['click', 'description', 'name'],
           template: '<button v-on:click.prevent.stop="click(pull(this), thisComponent())">{{name || id}}</button>'
       }
   };

   for(var property in components)
   {
       var props = JSON.parse(JSON.stringify(components[property].props));
       props.push('id');
       Vue.component('mip-' + property, {
            data: components[property].data ?? function () {
                      var foundIt = this.$parent;
                      var counter = 0;
                      while (foundIt.$parent.constructor.name == "VueComponent" && counter < 10)
                      {
                        foundIt = foundIt.$parent;
                        counter += 1;
                      }
                      return {
                          console: console,
                          pull: function(x) { return thingToData(foundIt.data); },
                          thisComponent: function() { return foundIt.data; },
                      }
                  },
            props: props,
            template: components[property].template
       });
   }

   var thingTemplate = '<div>';
   for (var property in components)
       thingTemplate = thingTemplate + '<mip-' + property + ' v-if="data.type == \\'' + property + '\\'" :id="id" ' + components[property].props.map(function(x){ return ':' + x + '.sync="data.' + x + '"';}).join(' ') + '></mip-' + property + '>'
   thingTemplate = thingTemplate + '</div>';

   Vue.component('mip-thing', {
        data: function () {
                  return {
                      console: console,
                  }
        },
        props: ['data', 'value', 'id', 'model'],
        template: thingTemplate
    });

   Vue.component('mip-heavy', {
        data: function () {
                  return {
                      console: console,
                  }
        },
        props: ['data'],
        template: '<div><mip-thing v-for="(d, id) in data" :data="d" :id="id"></mip-thing></div>',
    });

    var app = null;

    axios.get('/fluxs')
         .then(function(fluxdata){

    axios.get('/flow')
         .then(function(flowdata){

             fluxdata = fluxdata.data;
             flowdata = flowdata.data;

             var configs = {};
             fluxdata.fluxs.map(function(x){
                 configs[x.name] = x.config;
             });

             var options = [''];
             fluxTypes = fluxdata.fluxs.map(function(x){ return x.name });
             for(var type in fluxTypes)
                options.push(fluxTypes[type]);

             var saveTheFlux = function(input)
             {
                 var theApp = input.app;
                 var config = input.config;
                 delete config.test_event;
                 var setup = thingToData(theApp.setup);
                 var flux = {
                                id: setup.id,
                                name: setup.name,
                                flux: setup.flux,
                                parents: setup.parents,
                                config: config,
                 };
                 var index = -1;
                 for(var i = 0; i < theApp.flow.fluxs.length; i++)
                     if (theApp.flow.fluxs[i].id == flux.id)
                        index = i;

                 if (index < 0)
                     theApp.flow.fluxs.push(flux);
                 else
                     theApp.flow.fluxs[index] = flux;
             };

             var saveTheFlow = function(input)
             {
                 var setup = input.setup;
                 var flow = input.flow;
                 axios.post('/save', flow)
                     .then(function(result){});
             };

             var saveFlux = function(config) {
             };

             var ignoreFlux = function(config) {
             };

             var setup = {
                   showFlux: false,
                   id: { type: 'hide', value: '' },
                   name: { type: 'text', value: '' },
                   flux: { type: 'select', value: fluxdata.fluxs[0].name, options: options},
                   parents: { type: 'selectmanyrecords', label: 'Receive Events From', value: '', options: flowdata.fluxs },
             };

             for (var key in configs)
             {
                 configs[key].save = { type: 'button', name: 'Save Changes', click: function(config) {
                         saveTheFlux({ app: app, config: config });
                         saveTheFlow({ setup: app.setup, flow: app.flow });
                         app.setup.showFlux = false;
                     }
                 };
                 configs[key].cancel = { type: 'button', name: 'Ignore Changes', click: function() {
                         app.setup.showFlux = false;
                     }
                 };

                 configs[key].test_event = { type: 'json', value: '{}', default: '{}' };

                 configs[key].run_test = { type: 'button', name: 'Test Run This Flux', click: function(c, hey) {
                                      var previousName = hey.run_test.name;
                                      Vue.set(hey.run_test, 'name', 'Loading');
                                      app.results = [];
                                      Vue.set(app.results, 'errorMessage', '');
                                      var the_setup = thingToData(app.setup);
                                      the_setup.event = c.test_event;
                                      axios.post('/run', { config: c, setup: the_setup })
                                       .then(function(r){
                                           var index = 1;
                                           for (var key in r.data.result)
                                           {
                                              var result = {};
                                              result['event_' + index] = { type: 'jsonview', label: 'Event ' + index + ' of ' + r.data.result.length, value: r.data.result[key], view: 'thin' };
                                              app.results.push(result);
                                              index += 1;
                                           }
                                        }).catch(function(r){
                                            console.log(r);
                                            Vue.set(app.results, 'errorMessage', 'This app failed while trying to execute your flux.');
                                        }).then(function(){
                                           Vue.set(hey.run_test, 'name', previousName);
                                        });
                                     } };
             }

             var loadThisFlux = function(args)
             {
                 var flux = args.flux;
                 var setup = args.setup;
                 var config = args.config;

                 Vue.set(setup.id, 'value', flux.id);
                 Vue.set(setup.name, 'value', flux.name);
                 Vue.set(setup.flux, 'value', flux.flux);
                 Vue.set(setup.parents, 'value', flux.parents);

                 var applicable_config = configs[flux.flux];
                 for(var key in applicable_config)
                     if (flux.config[key])
                         Vue.set(applicable_config[key], 'value', flux.config[key]);
                     else
                         Vue.set(applicable_config[key], 'value', applicable_config[key].default);

                 if (applicable_config)
                     Vue.set(applicable_config.test_event, 'value', '{}');

                 options = flowdata.fluxs.filter(function(x){ return x.id != flux.id });
                 options.unshift( { id: '', name: '' } );
                 setup.parents.options = options;

                 Vue.set(setup, 'showFlux', true);
                 app.results = [];
             };

             function uuidv4() {
                 return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c =>
                     (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
                 );
             }

             app = new Vue({
                 el: '#app',
                 data: {
                     flow: flowdata,
                     startNew: function(x) {
                         flux = {
                             id: uuidv4(),
                             name: '',
                             parents: [],
                             config: {}
                         };
                         loadThisFlux({ flux: flux, setup: x.setup, configs: x.configs });
                     },
                     editFlux: function(x) {
                         var flux = x.flux;

                         loadThisFlux({ flux: x.flux, setup: x.setup, configs: x.configs });
                     },
                     deleteFlux: function(x) {
                         var set = x.flow.fluxs.filter( function(v){ return v.id != x.flux.id } );
                         Vue.set(x.flow, 'fluxs', set);
                     },
                     saveFlow: function(input)
                     {
                         var setup = input.setup;
                         var flow = input.flow;
                         axios.post('/save', flow)
                            .then(function(result){
                                Vue.set(setup, 'showFlux', false);
                            });
                     },
                     configs: configs,
                     setup: setup,
                     flux_name_for: function(ids, fluxes) {
                         var fluxs = fluxes.filter(function(x){ return ids.includes(x.id) });
                         return fluxs.map(function(x){ return x.name }).join(', ');
                     },
                     results: [],
                 }
            });
    });
    });
</script>
ENDEND

      end