Skip to content
Snippets Groups Projects
index.ejs 27 KiB
Newer Older
Moises Sacal's avatar
Moises Sacal committed
<!DOCTYPE html>
<html>
<head>
PTSEFTON's avatar
PTSEFTON committed
<style type="text/css">
Mike Lynch's avatar
Mike Lynch committed

PTSEFTON's avatar
PTSEFTON committed
body {
 
    margin: 0px;  /*removes default style*/
    display: flex;  /*enables flex content for its children*/
    box-sizing: border-box;
}
PTSEFTON's avatar
PTSEFTON committed

PTSEFTON's avatar
PTSEFTON committed
.main {
   overflow: hidden;  /*makes the body non-scrollable (we will add scrolling to the sidebar and main content containers)*/
  
   overflow:scroll;
}
PTSEFTON's avatar
PTSEFTON committed

PTSEFTON's avatar
PTSEFTON committed

.column {
    height: 50%;  /*allows both columns to span the full height of the browser window*/
    display: flex;
    flex-direction: column;  /*places the left and right headers above the bottom content*/
}

.left {
    flex-shrink: 0;  /*makes sure that content is not cut off in a smaller browser window*/
}


.scroll {
  overflow-y: auto;
  height: 400;
  flex-grow: 1; 
}

.p2 {
  height: 80%;
}

#topbar {
  background-color: #eee;
  flex-shrink: 0;
  flex-grow: 1; 
  position: fixed;
  left: 10px;
  top: 10px;
}



</style>
  <title><%= title %></title>
  <link rel='stylesheet' href='/stylesheets/style.css' />
  <script src="https://unpkg.com/vue"></script>

  <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
  <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.0/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-PDle/QlgIONtM1aqA2Qemk5gPOE7wFq8+Em+G/hmo5Iq0CCmYZLv3fVRDJ4MMwEA" crossorigin="anonymous">
  <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.0/js/bootstrap.bundle.min.js" integrity="sha384-VoPFvGr9GxhDT3n8vqqZ46twP5lgex+raTCfICQy73NLhN7ZqSfCtfSn4mLA2EFA" crossorigin="anonymous"></script></head>
<body>

  
PTSEFTON's avatar
PTSEFTON committed
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
PTSEFTON's avatar
PTSEFTON committed
        <!-- Vue app element -->
PTSEFTON's avatar
PTSEFTON committed
<div id="app" class="wrapper" >

    <nav id="topbar">
  
        <ul class="nav navbar-nav">
          <li>
        <a href="" class="active"><button type="button" class="btn btn-default btn-sm"><span class="glyphicon glyphicon-home"></span> Descrio! </button></a></li>
        </ul>
       <button> 
       <a v-bind:href="'/?path=' + display_path + '&dataset_path=' +  display_path">
        <template v-if="files.existing_describo">
            Edit existing data package
PTSEFTON's avatar
PTSEFTON committed
        <template v-else>
            Package a dataset here
PTSEFTON's avatar
PTSEFTON committed
        </template>
PTSEFTON's avatar
PTSEFTON committed
       </a> 
    
      
      </button>
      <button v-on:click="hide_all()">Hide all</button>
      
         
          <a v-if="files.existing_catalog"  v-bind:href="'/import_crate?path=' + display_path + '&dataset_path=' +  display_path">
              <button v-on:click="import_crate(context)">
                <template v-if="files.existing_describo">
                    Re-import CATALOG
                </template>
                <template v-else>
                   Import CATALOG
                </template>
              </button>
            </a>
          
         </a>
         <template v-if="context">
         <form v-on:submit.prevent="addContext(context)">
PTSEFTON's avatar
PTSEFTON committed
            <select  v-model="type" >
               <option v-for="type_name in Object.getOwnPropertyNames(schema.types).filter(type => {return schema.types[type].create})" v-bind:value="type_name"> {{ type_name }} </option>
PTSEFTON's avatar
PTSEFTON committed
              </select>
            <button type="submit">Add contextual item</button>
        </form>
PTSEFTON's avatar
PTSEFTON committed
    
         <div class="dropdown">
    
          
            <button  class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
              Show
            </button>
            
            <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
             <template  v-for="(item, id) in context.entities">
              <a class="dropdown-item" v-on:click="show_entity(id)" v-bind:href = "'#' + id" v-if="item.properties.name && !item.properties.type[0].value.includes('PropertyValue')"> 
                    {{item.properties.name[0].value}}     
              </a>
            </template>
              
            </div>
    
          </div>
PTSEFTON's avatar
PTSEFTON committed
    
        </nav>
      
        <div class="d-flex flex-row main">

            <div class="p-2 mr-auto">
              <div class="scroll">
               
                {{ changed }} 
                <h1><a v-bind:href="'?path=' + files.up +  '&dataset_path=' +  dataset_path">.. </a></h1>
                <template v-if="context && context.entities">
                  <template v-for="(item, id) in context.entities">
                    <template v-if="item.properties.type && !item.properties.type[0].value.includes('PropertyValue')">
                      <span v-bind:id="id"></span>
                      <template v-if="item.show || id === context.root">
                          <h2 >
                            <form v-on:submit.prevent="removeItem(id)">
                              <button type="submit">-</button> {{ id }} 
                          </form>
                          <form v-if="id === context.root" v-on:submit.prevent="crate(item)">
                              <button type="submit" title="Make / Update a DataCrate here (generate CATALOG.json and CATALOG.html)">[crate]</button> 
                              <a v-if="item.crate_href" v-bind:href="item.crate_href">View CATALOG.html</a>
                          </form>
                        </h2>
                        <img width="60%" v-bind:src='item._img'>
                        <show-props v-bind:item="item" v-bind:schema="schema" v-bind:context="context" v-bind:type="type" v-bind:methods="methods" v-bind:changed="changed"></show-props>
                          <form v-on:submit.prevent="addContextProp(item)">
                              <select  v-model="prop_name"  >
                                <template v-for="(prop, prop_name) in schema.properties">
                                  <option v-if="!item.properties[prop_name]">{{prop_name}}</option>
                                </template>
                                
                                </select>
                              <button type="submit">+</button>
                          </form>
                          <br><br>
                        </template> 
                      </template>
                    </template>
                </template>


                  </div>
PTSEFTON's avatar
PTSEFTON committed
            </div>
           
            <nav class="p-2 scroll">


                <div class="dropdown" v-for="(items, type) in context_by_type">
              
          
                  
                        <button  class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                            {{type}}
                          </button>
                      
                        <ul class="dropdown-menu" aria-labelledby="dropdownMenuButton">
                            
                        <button  class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                            {{type}}
                          </button>
                      <template v-for="item in items">
                      
                        <a class="dropdown-item" v-on:click="show_entity(item.id)" v-bind:href = "'#' + item.id"  v-if="item.properties.name"> 
                              {{item.properties.name[0].value}}     
                        </a>
                      </template>
                </div>
                  
                  <div class="dropdown" v-for="(items, type) in context_by_type">
                        <button  class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                            {{type}}
                        </button>
                        
                        <ul class="dropdown-menu" aria-labelledby="dropdownMenuButton">
                              
                          <button  class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                                {{type}}
                          </button>
                          <template v-for="item in items">
                          
                            <a class="dropdown-item" v-on:click="show_entity(item.id)" v-bind:href = "'#' + item.id"  v-if="item.properties.name"> 
                                  {{item.properties.name[0].value}}     
                            </a>
                          </template>
                        </ul>
                  </div>
              </nav>
        </div>
        
      
  
           
            
    
       
        
          
        
  
            

        
    <!-- TODO - use responsive divs here instead -->
PTSEFTON's avatar
PTSEFTON committed
          <tr>
          <th>Directories</th>
          <th>Files</th>
        </tr>
        <tr>
            <td>
                <div id="dirlist">
                    <ul v-for="dir in files.dirs">
                      <li><a v-bind:href="'?path=' + dir.absolute + '&dataset_path=' +  dataset_path">{{ dir.name }}</a>
                        <form v-if="context.entities" v-on:submit.prevent="addDir(dir)" style="display:inline;">
                            <button type="submit" title="Add directory to package">+</button>
                        </form></li>
                    </ul>
                </div>
              </td>
PTSEFTON's avatar
PTSEFTON committed
           
       <div id="filelist">
         <div v-for="(file, path) in files.files">
           <template v-if="context && !(context.entities[file.id])">
           <h4>{{ file.filename }}  <form v-if="context.entities" v-on:submit.prevent="addFile(file)" style="display:inline;">
              <button type="submit" title="Add file to package">+</button></h4>
           <img width="50%" v-bind:src='file._img'>
          
          </form>
        </template>
        <h4 v-else>
            {{ file.filename }}
        </h4>
         </div>
        </div>
      </td>
   
PTSEFTON's avatar
PTSEFTON committed
      </div>
PTSEFTON's avatar
PTSEFTON committed
      </tr>
    </table>
</div>
<!-- end Vue app element -->
Mike Lynch's avatar
Mike Lynch committed

Moises Sacal's avatar
Moises Sacal committed
  <script>
Mike Lynch's avatar
Mike Lynch committed


PTSEFTON's avatar
PTSEFTON committed
  var vue;
  
  var files = {};
Moises Sacal's avatar
Moises Sacal committed
  document.addEventListener("DOMContentLoaded", function(event) {
     const urlParams = new URLSearchParams(window.location.search);
PTSEFTON's avatar
PTSEFTON committed
     const display_path = urlParams.get('path');
     const dataset_path = urlParams.get('dataset_path');
PTSEFTON's avatar
PTSEFTON committed
     const addRootDir = function addRootDir(context) {
PTSEFTON's avatar
PTSEFTON committed
     if (context.entities && !context.entities[dataset_path]) {
PTSEFTON's avatar
PTSEFTON committed
        var new_item = JSON.parse(JSON.stringify(schema.types.Dataset.template));
        new_item.properties.name[0].value = "Untitled";
        new_item.properties.hasPart = [];
        context.entities[dataset_path] = new_item;
      }
      }
    axios.get('/files?path=' + display_path)
Moises Sacal's avatar
Moises Sacal committed
    .then(function (response) {
PTSEFTON's avatar
PTSEFTON committed
      var params =  dataset_path ? '?dataset_path=' + dataset_path : "";
      axios.get("/schema").then(function(response){
        schema = response.data;
        axios.get("/context_entities" + params).then(function(response){
        var context = response.data;
        if (context.root) {
          if (context.root != dataset_path) {
            context.entities[dataset_path] = JSON.parse(JSON.stringify(context.entities[context.root]));
            delete context.entities[context.root];
            context.root = dataset_path;
           
          }
        }
        else {
            context.root = dataset_path;
            addRootDir(context);
         }
        Vue.component('show-entity-name' , {
          props: ['name', 'item', 'methods', 'changed'],
          template: `
                <span>
                <form v-on:submit.prevent="methods.removeProp(item, name)" style='display:inline;' v-bind:data="changed">                     
                    <button type="submit">-</button>
                </form>
                {{ name }}
                </span>
                `
        });
        Vue.component('paginate-values', {
          props: ['name', 'item', 'methods', 'changed', 'context', 'values', 'schema', 'page'],
PTSEFTON's avatar
PTSEFTON committed
          <div>    
          <template v-if="values.length > 20 && values.length > (page) * 20" >
               
                <details>
                  
                    <summary>
                       {{context.entities[values[page * 20].id].properties.name[0].value}} ... 
                    </summary>
                  
                  
                  <div v-for="i_index in 20">
                    
                    <show-value v-if="i_index + page * 20 < values.length" v-bind:name="name"  
                                v-bind:item="item" 
                                v-bind:methods="methods" 
                                v-bind:changed="changed" 
                                v-bind:context="context" 
                                v-bind:values="values"
                                v-bind:schema="schema"
                                v-bind:val="values[i_index + page * 20]"
                                v-bind:i_index="i_index"
                                > 
                  </show-value>    
                 </div>
                </details>
               
                <paginate-values  v-bind:name="name"  
                                  v-bind:item="item" 
                                  v-bind:methods="methods" 
                                  v-bind:changed="changed" 
                                  v-bind:context="context" 
                                  v-bind:values="values"
                                  v-bind:schema="schema"
                                  v-bind:page="page + 1"> 
                  </paginate-values>
             
          </template>
          <template v-else-if="page === 0">
            <div v-for="(val, i_index) in values">
                <show-value v-bind:name="name"  
                            v-bind:item="item" 
                            v-bind:methods="methods" 
                            v-bind:changed="changed" 
                            v-bind:context="context" 
                            v-bind:values="values"
                            v-bind:schema="schema"
                            v-bind:val="val"
                            v-bind:i_index="i_index"> 
                    </show-value>    
            </div>
           </template>
    
          </div>
          `
        });

        Vue.component('show-value', {
          props: ['name', 'item', 'methods', 'changed', 'context', 'values', 'schema', 'i_index','val'],

          template: 
          `
          <div>
         
            <form v-on:submit.prevent="methods.removeValue(values, i_index)" style='display:inline;'>                     
                <button type="submit" title="Delete this property">-</button>
            </form>
            
            <!-- Input form -->
            <template v-if="schema.properties[name] && schema.properties[name].input === 'text'">
                    <input v-model="val.value" type="text" size="45" v-on:keyup="methods.trigger_change_on_enter"/>
                
            </template>
            <!-- UNLINK button for linked props -->
            <template v-else-if="val && val.id && context.entities[val.id] && context.entities[val.id].properties.name">
PTSEFTON's avatar
PTSEFTON committed
                <a v-on:click="methods.show_entity(val.id)" v-bind:href="'#' + val.id">{{  context.entities[val.id].properties.name[0].value }}</b></a>
                <form v-on:submit.prevent="methods.unlinkProp(val)" style='display:inline;'>                     
                    <button type="submit">unlink</button>
                </form>
            </template>

            <!-- FALLBACK - DISPLAY -->
            <b v-else>
                <input v-model="val.value" type="text" size="45" v-on:keyup="methods.trigger_change_on_enter"/>

            </b>
          
          
            <template v-if="schema.properties[name] && schema.properties[name].input === 'link' " >
                <form v-on:submit.prevent="methods.linkProp(val)" style='display:inline;'  v-if="!val.id">      
                  <button type="submit"> -> </button>               
                  <select  v-model="val.selection">
                    
                    <template v-for="(link_item, link_id) in context.entities">
                      {{link_id}}
                      <template v-for="other_name in link_item.properties.name">  
                        <option v-if="(!val.id || link_id != val.id) && schema.properties[name] && link_item.properties.type && schema.properties[name].type.includes(link_item.properties.type[0].value)" v-bind:value="link_id">{{ other_name.value  }}</option>
                      </template>
                    </template>
                  </select>
                  
                </form>
        

                
                <form v-on:submit.prevent="methods.linkToNew(val)" style='display:inline;'  v-if="!val.id && schema.properties[name] && schema.properties[name].type && schema.properties[name].type.filter(t => {return (schema.types[t] && schema.types[t].create)}).length > 0">      
                    ... or ...
                    <button type="submit" text="Link to a new contextual item"> -> New -> </button>               
                    <select  v-model="val.new_type" v-for="type in schema.properties[name].type.filter(type => {return (schema.types[type] && schema.types[type].create)})">
                        <option> {{ type  }} </option>                        
                    </select>       
                </form>

            </template>          
          
          </div>
          `
        });

        Vue.component('show-props', {
          props: ['item', 'schema', 'context', 'type', 'methods', 'changed'],
          template: `
          
        <table width="100%">
         
          <tr v-for="(values, name) in item.properties" v-bind:data="changed">
            <td>
                
               <show-entity-name v-bind:name="name"  v-bind:item="item" v-bind:methods="methods" v-bind:changed="changed"></show-entity-name>
            </td>
            <td>
                <form v-on:submit.prevent="methods.addProp(item, name)" style='display:inline;'>                     
                    <button type="submit">+</button>
                </form>
            </td>
            <td>               
                  <!-- Put in summary here / Paginate-->
                  <paginate-values v-bind:name="name"  
                                    v-bind:item="item" 
                                    v-bind:methods="methods" 
                                    v-bind:changed="changed" 
                                    v-bind:context="context" 
                                    v-bind:values="values"
                                    v-bind:schema="schema"
                                    v-bind:page="0"> 
                  </paginate-values>
             
           
            </td>
          </tr>
        </table>
          `

        });

        const methods = {
        
        "save_context": function save_context() {
              var params = '?dataset_path=' + dataset_path;
              axios.post('/context_entities' + params, context).then(
                function () {
PTSEFTON's avatar
PTSEFTON committed
                  data.changed += 1;  
                }
              )
            },

        "show_entity": function save_context(id) {
              context.entities[id].show = true
              data.changed += 1;  
            },
        "hide_all": function save_context() {
              for (let [id, entity] of  Object.entries(context.entities)) {
                if (id != context.root) {
                  entity.show = false
                }
              }
              data.changed += 1;  
            },
        "import_crate": function import_crate(context) {
              var params = '?dataset_path=' + dataset_path;
              axios.get('/import_crate' + params).then(
                function () {
                  data.context = JSON.parse(JSON.stringify(response.data));
                  console.log(context);
PTSEFTON's avatar
PTSEFTON committed
                  data.changed += 1; 
PTSEFTON's avatar
PTSEFTON committed
                }
          "crate": function crate(item) {
              var params = '?dataset_path=' + dataset_path;
              var href = "/preview?path=" + dataset_path + "/CATALOG.html"
              var save = this.save_context;
              axios.get('/crate' + params).then(
                function () {
                  item.crate_href = href;
PTSEFTON's avatar
PTSEFTON committed
                  data.changed += 1;                
PTSEFTON's avatar
PTSEFTON committed
            },
        "addContextProp": function addContextProp(item){
          // Warning! Repetition!
          var prop_name = this.prop_name;
          if (item.properties[prop_name]) {
              item.properties[prop_name].push({"value": "", "id": ""});
          } else {
              item.properties[prop_name] = [{"value": "", "id": ""}];
          }
          this.changed += 1;                
          this.save_context();         
        },
        "addProp": function addProp(item, prop_name){
          if (item.properties[prop_name]) {
              item.properties[prop_name].push({"value": "", "id": ""});
          } else {
              item.properties[prop_name] = [{"value": "", "id": ""}];
          }
          data.changed += 1;                
          methods.save_context();
        },

        "removeValue": function removeValue(values,i) {
          values.splice(i,1)
          data.changed += 1;   
          methods.save_context();         
        
        },

        "removeProp": function removeProp(item, prop_name){
          delete item.properties[prop_name];
          data.changed += 1;   
          methods.save_context();         
        
        },

        "trigger_change_on_enter": function(e) {
          if (e.keyCode === 13) {
          data.changed += 1;
          methods.save_context();
          } 
          
        },
        "linkProp": function linkProp(val) { 
          val.value = context.entities[val.selection].properties.name[0].value;
          val.id = val.selection;
          this.changed += 1;  
          this.save_context();     
        },
        "unlinkProp": function unlinkProp(val) {  
  
          val.value = "";
          val.id = "";
          data.changed += 1;  
          methods.save_context();     
        },
        "directory_path" : function directory(file) {
            var container_dir_array = file.id.split("/");
            container_dir_array.pop();
            var container_dir = container_dir_array.join("/");
            var dir_name = container_dir_array.pop();
            return({"dir_name": dir_name, dir_path: container_dir })
        },
        "addParentDataset": function addParentDataset(file) {
            var dir_details = this.directory_path(file);
            var diff = dir_details.dir_path.replace(context.root, "");
          // Don't make dirs outside of the package
            if (diff != dir_details.dir_path && !context.entities[dir_details.dir_path]) {
              var dir = {"id": dir_details.dir_path, "name": dir_details.dir_name};
              this.addDir(dir);
              context.entities[dir_details.dir_path].properties.hasPart = [];
            context.entities[dir_details.dir_path].properties.hasPart.push({"id": file.id});
            this.changed += 1;  
            this.save_context();
          },
        
        "addFile": function addFile(file) {
          if (!context.entities[file.id]) {
            var new_item = JSON.parse(JSON.stringify(schema.types.File.template));
            new_item.properties.name[0].value = file.name;
            new_item._img = file._img;
            context.entities[file.id] = new_item;
            this.addParentDataset(file);
          }
        },
        "addRootDir":  addRootDir,
        "addDir": function addDir(file) {
          if (!context.entities[file.id]) {
            var new_item = JSON.parse(JSON.stringify(schema.types.Dataset.template));
            new_item.properties.name[0].value = file.name;
            new_item.properties.hasPart = [];
            context.entities[file.id] = new_item;
            this.addParentDataset(file);

            this.changed += 1;  
            this.save_context();
          }
          else {
            alert("Already got that directory"); //Should never fire but you never know
        },
        "removeItem": function remove_item(id) {    
          delete context.entities[id];
          this.changed += 1;
          this.save_context();
          
        },
        "linkToNew" : function linkToNew(val) {
            this.type = val.new_type;
            val.id = this.addContext(context);
            this.save_context();

          },
        "addContext": function addContext(context){
          while (context.entities[data.latest_id.toString()]) {
            data.latest_id += 1;
          }
          var id = data.latest_id.toString();
          context.entities[id] = JSON.parse(JSON.stringify(schema.types[this.type].template));
          context.entities[id].properties.name=[{"value": this.type + "(" + id + ")"}]
PTSEFTON's avatar
PTSEFTON committed
          context.entities[id].show = true;
          data.changed += 1;
          this.save_context();
          return(id);

        }
      };
      const data = {
            files: files,
            context: context,
            schema: schema,
            prop: {"name":"nom", "value": "val"},
            type: "Person",
            prop_name: "",
            link_to_id: {},
            link_id: "",
            changed: 0,
            latest_id: 0,
            display_path: display_path,
            dataset_path: dataset_path,
            methods: methods
          };

        vue = new Vue({
          el: "#app",
          data: data,
PTSEFTON's avatar
PTSEFTON committed
          methods: methods,
          computed: {
            context_by_type: function () {
              by_type = {}
              if (this.context && this.context.entities){
                for (let [id, item] of Object.entries(this.context.entities)){
                  item.id = id
                  if (item.properties && item.properties.type) {
                    for (let type of item.properties.type) {
                      if (!by_type[type.value]) {
                        by_type[type.value] = []
                      }
                      by_type[type.value].push(item);
                    }
                }
              }
              }
              return by_type;
            }
          }
Mike Lynch's avatar
Mike Lynch committed
      //document.getElementById("files").innerHTML = response['data'].model;
Moises Sacal's avatar
Moises Sacal committed
    })
    .catch(function (error) {
      // handle error
      console.log(error);
    })
    .then(function () {
PTSEFTON's avatar
PTSEFTON committed
      console.log("done")
Moises Sacal's avatar
Moises Sacal committed
      // always executed
    });
Moises Sacal's avatar
Moises Sacal committed
  </script>
</body>

</html>