12479: Refactoring to simplify the code.
authorLucas Di Pentima <ldipentima@veritasgenetics.com>
Tue, 19 Dec 2017 18:16:30 +0000 (15:16 -0300)
committerLucas Di Pentima <ldipentima@veritasgenetics.com>
Tue, 19 Dec 2017 18:16:30 +0000 (15:16 -0300)
Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <ldipentima@veritasgenetics.com>

apps/workbench/app/assets/javascripts/components/edit_tags.js
apps/workbench/app/assets/javascripts/models/session_db.js
apps/workbench/app/assets/javascripts/models/tags.js [deleted file]
apps/workbench/app/views/collections/_show_tags.html.erb
apps/workbench/public/vocabulary.json

index 040779629296517a5479435e5b10aebf009108d3..228a649f8e372edcefe1659bb1d55fb843178f12 100644 (file)
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-window.IntegerField = {
+window.SelectOrAutocomplete = {
     view: function(vnode) {
-        var tags = vnode.attrs.tags
-        var voc = vnode.attrs.voc
-        var tagName = tags.getName(vnode.attrs.tagIdx)
-        var tagDef = voc.getDef(tagName)
-        var min = tagDef.min || false
-        var max = tagDef.max || false
         return m("input", {
-            type: 'number',
-            style: {
-                width: '100%',
-            },
-            oninput: m.withAttr("value", function(val) {
-                // Validations
-                if (isNaN(parseInt(val))) { return }
-                if (min && val < min) { return }
-                if (max && val > max) { return }
-                // Value accepted
-                tags.data[vnode.attrs.tagIdx]["value"] = parseInt(val)
-            }),
-            value: tags.data[vnode.attrs.tagIdx]["value"]
-        }, tags.data[vnode.attrs.tagIdx]["value"])
-    }
-}
-
-window.TextField = {
-    view: function(vnode) {
-        var tags = vnode.attrs.tags
-        var voc = vnode.attrs.voc
-        var tagName = tags.getName(vnode.attrs.tagIdx)
-        var tagDef = voc.getDef(tagName)
-        var max_length = tagDef.max_length || false
-        return m("input", {
-            type: 'text',
-            style: {
-                width: '100%',
-            },
-            oninput: m.withAttr("value", function(val) {
-                // Validation
-                if (max_length && val.length > max_length) { return }
-                // Value accepted
-                tags.data[vnode.attrs.tagIdx]["value"] = val
-            }),
-            value: tags.data[vnode.attrs.tagIdx]["value"]
-        }, tags.data[vnode.attrs.tagIdx]["value"])
-    }
-}
-
-window.SelectNameField = {
-    view: function(vnode) {
-        return m("input[type=text]", {
             style: {
                 width: '100%'
             },
-        })
+            type: 'text',
+            value: vnode.attrs.value
+        }, vnode.attrs.value)
     },
     oncreate: function(vnode) {
-        var tags = vnode.attrs.tags
-        var voc = vnode.attrs.voc
-        var opts = voc.getTypes().map(function(x) {
-            return {
-                value: x,
-                label: x
-            }
-        })
-        // Tag name not included on vocabulary, add it to the options
-        var tagName = tags.getName(vnode.attrs.tagIdx)
-        if (!voc.getTypes().includes(tagName)) {
-            opts = opts.concat([{value: tagName, label: tagName}])
-        }
-        $(vnode.dom).selectize({
-            options: opts,
-            persist: false,
-            maxItems: 1,
-            labelField: 'label',
+        vnode.state.selector = $(vnode.dom).selectize({
+            labelField: 'value',
             valueField: 'value',
-            items: [tags.data[vnode.attrs.tagIdx]["name"]],
-            create: function(input) {
-                return {
-                    value: input,
-                    label: input
-                }
-            },
+            searchField: 'value',
+            sortField: 'value',
+            maxItems: 1,
+            create: vnode.attrs.create ? function(input) {
+                return {value: input}
+            } : false,
+            items: [vnode.attrs.value()],
+            options: vnode.attrs.options.map(function(option) {
+                return {value: option}
+            }),
             onChange: function(val) {
-                tags.data[vnode.attrs.tagIdx]["name"] = val
+                vnode.state.dirty = true
+                vnode.attrs.value(val)
                 m.redraw()
             }
-        })
+        }).data('selectize')
     }
 }
 
-window.SelectField = {
-    view: function(vnode) {
-        var tags = vnode.attrs.tags
-        var voc = vnode.attrs.voc
-        var tagName = tags.getName(vnode.attrs.tagIdx)
-        var overridable = voc.getDef(tagName).overridable || false
-        var opts = voc.getDef(tagName).options
-        // If current value isn't listed and it's an overridable type, add
-        // it to the available options
-        if (!opts.includes(tags.data[vnode.attrs.tagIdx]["value"]) &&
-            overridable) {
-            opts = opts.concat([tags.data[vnode.attrs.tagIdx]["value"]])
-        }
-        // Wrap the select inside a div element so it can be replaced
-        return m("div", {
-            style: {
-                width: '100%'
-            },
-        }, [
-            m("select", {
-                style: {
-                    width: '100%'
-                },
-                oncreate: function(v) {
-                    $(v.dom).selectize({
-                        create: overridable,
-                        onChange: function(val) {
-                            tags.data[vnode.attrs.tagIdx]["value"] = val
-                            m.redraw() // 3rd party event handlers need to do this
-                        }
-                    })
-                },
-            }, opts.map(function(k) {
-                    return m("option", {
-                        value: k,
-                        selected: tags.data[vnode.attrs.tagIdx]["value"] === k,
-                    }, k)
-                })
-            )
-        ])
-    }
-}
-
-// Maps tag types against editor components
-var typeMap = {
-    "select": SelectField,
-    "text": TextField,
-    "integer": IntegerField
-}
-
 // When in edit mode, present a tag name selector and tag value
 // selector/editor depending of the tag type.
-window.TagEditor = {
+window.TagEditorRow = {
     view: function(vnode) {
-        var tags = vnode.attrs.tags
-        var voc = vnode.attrs.voc
-        var tagIdx = vnode.attrs.tagIdx
-        if (tagIdx in tags.data) {
-            var tagName = tags.getName(vnode.attrs.tagIdx)
-            var tagType = voc.getDef(tagName).type
-            return m("tr.collection-tag-"+tagName, [
-                m("td",
-                    vnode.attrs.editMode() ?
-                    m("i.glyphicon.glyphicon-remove.collection-tag-remove", {
-                        style: "cursor: pointer;",
-                        onclick: function(e) {
-                            // Erase tag
-                            tags.removeTag(tagIdx)
-                        }
-                    })
-                : ""),
-            m("td.collection-tag-field.collection-tag-field-key",
-                // Tag name
-                vnode.attrs.editMode() ? 
-                m(SelectNameField, {
-                    tagIdx: tagIdx,
-                    tags: tags,
-                    voc: voc
-                })
-                : tags.data[tagIdx]["name"]),
-            m("td.collection-tag-field.collection-tag-field-value",
-                // Tag value
-                vnode.attrs.editMode() ? 
-                m(typeMap[tagType], {
-                    tagIdx: tagIdx,
-                    tags: tags,
-                    voc: voc
+        return m("tr", [
+            // Erase tag
+            m("td",
+            vnode.attrs.editMode ?
+            m("i.glyphicon.glyphicon-remove", {
+                style: "cursor: pointer;",
+                onclick: function(e) {
+                    console.log('Erase tag clicked')
+                }
+            })
+            : ""),
+            // Tag name
+            m("td",
+            vnode.attrs.editMode ?
+            m("div", [m(SelectOrAutocomplete, {
+                options: (vnode.attrs.name() in vnode.attrs.vocabulary().types)
+                    ? Object.keys(vnode.attrs.vocabulary().types)
+                    : Object.keys(vnode.attrs.vocabulary().types).concat(vnode.attrs.name()),
+                value: vnode.attrs.name,
+                create: vnode.attrs.vocabulary().strict
+            })])
+            : vnode.attrs.name),
+            // Tag value
+            m("td",
+            vnode.attrs.editMode ?
+            m("div", {key: vnode.attrs.name()}, [m(SelectOrAutocomplete, {
+                options: (vnode.attrs.name() in vnode.attrs.vocabulary().types) 
+                    ? vnode.attrs.vocabulary().types[vnode.attrs.name()].options.concat(vnode.attrs.value())
+                    : [vnode.attrs.value()],
+                value: vnode.attrs.value,
+                create: (vnode.attrs.name() in vnode.attrs.vocabulary().types)
+                    ? vnode.attrs.vocabulary().types[vnode.attrs.name()].overridable || false
+                    : true, // If tag not in vocabulary, we should accept any value
                 })
-                : tags.data[tagIdx]["value"])
             ])
-        }
+            : vnode.attrs.value)
+        ])
     }
 }
 
-window.TagTable = {
+window.TagEditorTable = {
+    oninit: function(vnode) {
+        vnode.state.tags = vnode.attrs.tags
+    },
     view: function(vnode) {
         return m("table.table.table-condensed", {
             border: "1"
@@ -205,83 +99,76 @@ window.TagTable = {
                     m("th", "Value"),
                 ])
             ]),
-            m("tbody.collection-tag-rows", [
-                Object.keys(vnode.attrs.tags.data).map(function(k) {
-                    return m(TagEditor, {
-                        tagIdx: k,
-                        key: k,
+            m("tbody", [
+                vnode.state.tags.map(function(tag, idx) {
+                    return m(TagEditorRow, {
+                        key: idx,
                         editMode: vnode.attrs.editMode,
-                        tags: vnode.attrs.tags,
-                        voc: vnode.attrs.voc
+                        name: tag.name,
+                        value: tag.value,
+                        vocabulary: vnode.attrs.vocabulary
                     })
                 })
             ]),
-            ]
-        )
+        ])
     }
 }
 
 window.TagEditorApp = {
     oninit: function(vnode) {
         vnode.state.sessionDB = new SessionDB()
-        vnode.state.url = new URL(document.URL)
-        var pathname = vnode.state.url.pathname.split("/")
-        vnode.state.uuid = pathname.pop()
-        vnode.state.objType = pathname.pop()
-        vnode.state.tags = new Tags(vnode.state.sessionDB, vnode.state.uuid, vnode.state.objType)
-        vnode.state.tags.load()
-        vnode.state.vocabulary = new Vocabulary(vnode.state.url)
-        vnode.state.vocabulary.load()
-        vnode.state.editMode = m.stream(false)
-        vnode.state.tagTable = TagTable
+        // Get vocabulary
+        vnode.state.vocabulary = m.stream({"strict":false, "types":{}})
+        m.request('/vocabulary.json').then(vnode.state.vocabulary)
+        vnode.state.editMode = vnode.attrs.targetEditable || true
+        // Get tags
+        vnode.state.tags = []
+        var objPath = '/arvados/v1/'+vnode.attrs.targetController+'/'+vnode.attrs.targetUuid
+        vnode.state.sessionDB.request(
+            vnode.state.sessionDB.loadLocal(), objPath).then(function(obj) {
+                Object.keys(obj.properties).forEach(function(k) {
+                    vnode.state.tags.push({
+                        name: m.stream(k),
+                        value: m.stream(obj.properties[k])
+                    })
+                })
+            }
+        )
     },
     view: function(vnode) {
         return [
-            m("p", [
-                // Edit button
-                m("a.btn.btn-primary"+(vnode.state.editMode() ? '.disabled':''), {
-                    onclick: function(e) {
-                        vnode.state.editMode(true)
-                    }
-                }, " Edit "),
-            ]),
             // Tags table
-            m(vnode.state.tagTable, {
+            m(TagEditorTable, {
                 editMode: vnode.state.editMode,
                 tags: vnode.state.tags,
-                voc: vnode.state.vocabulary
+                vocabulary: vnode.state.vocabulary
             }),
-            vnode.state.editMode() ? 
+            vnode.state.editMode ?
             m("div", [
                 m("div.pull-left", [
                     // Add tag button
                     m("a.btn.btn-primary.btn-sm", {
                         onclick: function(e) {
-                            vnode.state.tags.addTag(vnode.state.vocabulary.getTypes()[0])
+                            vnode.state.tags.push({
+                                name: m.stream('new tag'),
+                                value: m.stream('new tag value')
+                            })
                         }
                     }, [
                         m("i.glyphicon.glyphicon-plus"),
                         " Add new tag "
-                    ])         
+                    ])
                 ]),
                 m("div.pull-right", [
                     // Save button
                     m("a.btn.btn-primary.btn-sm", {
                         onclick: function(e) {
-                            vnode.state.editMode(false)
-                            vnode.state.tags.save().then(function() {
-                                vnode.state.tags.load()
-                            })
-                        }
-                    }, " Save "),
-                    // Cancel button
-                    m("a.btn.btn-primary.btn-sm", {
-                        onclick: function(e) {
-                            vnode.state.editMode(false)
-                            e.redraw = false
-                            vnode.state.tags.load().then(m.redraw())
+                            console.log('Save button clicked')
+                            // vnode.state.tags.save().then(function() {
+                            //     vnode.state.tags.load()
+                            // })
                         }
-                    }, " Cancel ")                    
+                    }, " Save ")
                 ])
             ])
             : ""
index 7ca46922c63a93fb1d089a838a1ec61e64256e93..54a61c23ecdad7b9dbab6045c7b432c55dcb73c6 100644 (file)
@@ -31,9 +31,9 @@ window.SessionDB = function() {
         loadLocal: function() {
             var sessions = db.loadActive()
             var s = false
-            Object.keys(sessions).forEach(function(key) {
-                if (sessions[key].isFromRails) {
-                    s = sessions[key]
+            Object.values(sessions).forEach(function(session) {
+                if (session.isFromRails) {
+                    s = session
                     return
                 }
             })
diff --git a/apps/workbench/app/assets/javascripts/models/tags.js b/apps/workbench/app/assets/javascripts/models/tags.js
deleted file mode 100644 (file)
index ddf0728..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-// Fallback vocabulary that accepts any tag type. Will be used if
-// no custom vocabulary provided.
-var vocabulary = {
-    "strict": false,
-    "types": {}
-}
-
-window.Vocabulary = function(url) {
-    var v = this
-    Object.assign(v, {
-        url: url,
-        data: vocabulary, // Default vocabulary
-        load: function() {
-            // Load vocabulary from rails' public directory
-            m.request(v.url.origin + '/vocabulary.json').then(function(resp) {
-                v.data = resp
-            })
-        },
-        getDef: function(tagName) {
-            if (tagName in v.data.types) {
-                return v.data.types[tagName]
-            } else {
-                return {"type": "text"} // Default 
-            }
-        },
-        getTypes: function() {
-            return Object.keys(v.data.types)
-        }
-    })
-}
-
-window.Tags = function(db, uuid, objType) {
-    var t = this
-    Object.assign(t, {
-        db: db,
-        uuid: uuid,
-        objType: objType,
-        objPath: '/arvados/v1/' + objType + '/' + uuid,
-        tagIdx: 0, // Will use this as the tag access key
-        data: {},
-        clear: function() {
-            t.data = {}
-        },
-        load: function() {
-            // Get the tag list from the API server
-            return db.request(
-                db.loadLocal(), 
-                t.objPath).then(function(obj){
-                    t.clear()
-                    Object.keys(obj.properties).map(function(k) {
-                        t.addTag(k, obj.properties[k])
-                    })
-                }
-            )
-        },
-        save: function() {
-            return db.request(
-                db.loadLocal(),
-                t.objPath, {
-                    method: "PUT",
-                    data: {properties: JSON.stringify(t.getAll())}
-                }
-            )
-        },
-        getAll: function() {
-            // return hash to be POSTed to API server
-            var tags = {}
-            Object.keys(t.data).map(function(k) {
-                a_tag = t.data[k]
-                tags[a_tag.name] = a_tag.value
-            })
-            return tags
-        },
-        addTag: function(name, value) {
-            name = name || ""
-            value = value || ""
-            t.data[t.tagIdx] = {
-                "name": name,
-                "value": value
-            },
-            t.tagIdx++
-        },
-        removeTag: function(tagIdx) {
-            if (tagIdx in t.data) {
-                delete t.data[tagIdx]
-            }
-        },
-        getName: function(tagIdx) {
-            if (tagIdx in t.data) {
-                return t.data[tagIdx].name
-            }
-        },
-        get: function(tagIdx) {
-            if (tagIdx in t.data) {
-                return t.data[tagIdx]
-            }
-        }
-    })
-}
index 4dd2aac6be8ac8752589fa24c997bba636d1ac85..e3432d2d3f667e19ef7a934059b0915e5079844d 100644 (file)
@@ -3,6 +3,6 @@
 SPDX-License-Identifier: AGPL-3.0 %>
 
   <div class="collection-tags-container" style="padding-left:2em;padding-right:2em;">
-    <div data-mount-mithril="TagEditorApp" data-target-controller="<%= controller_name %>" data-target-uuid="<%= @object.uuid %>"></div>
+    <div data-mount-mithril="TagEditorApp" data-target-controller="<%= controller_name %>" data-target-uuid="<%= @object.uuid %>" data-target-editable="<%= @object.editable? %>"></div>
   </div>
  
\ No newline at end of file
index 0e09f20f44771cbd4c69f20cd3ff85eaae293a20..c17cc18ecdf5acd0b04fccbb648fa456b58b21b4 100644 (file)
@@ -1,27 +1,21 @@
 {
     "strict": "false",
     "types": {
-        "opt1": {
+        "fruit": {
             "type": "select",
-            "options": ["val1", "val2", "val3"],
+            "options": ["pineapple", "tomato", "orange", "banana"],
             "overridable": "true"
         },
-        "opt2": {
+        "animal": {
             "type": "select",
-            "options": ["val21", "val22", "val23"]
+            "options": ["human", "dog", "elefant", "eagle"]
         },
-        "opt3": {
+        "color": {
             "type": "select",
-            "options": ["val31", "val32", "val33"]
+            "options": ["yellow", "red", "magenta", "green"]
         },
         "text tag": {
-            "type": "text",
-            "max_length": "80"
-        },
-        "int tag": {
-            "type": "integer",
-            "min": "0",
-            "max": "1000"
+            "type": "text"
         }
     }
 }
\ No newline at end of file