1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
6 view: function(vnode) {
7 return m("input.form-control", {
12 placeholder: 'Add ' + vnode.attrs.placeholder,
13 value: vnode.attrs.value,
14 onchange: function() {
15 if (this.value != '') {
16 vnode.attrs.value(this.value)
23 window.SelectOrAutocomplete = {
24 view: function(vnode) {
25 return m("input.form-control", {
30 value: vnode.attrs.value,
31 placeholder: (vnode.attrs.create ? 'Add or select ': 'Select ') + vnode.attrs.placeholder,
34 oncreate: function(vnode) {
35 vnode.state.awesomplete = new Awesomplete(vnode.dom, {
36 list: vnode.attrs.options,
40 vnode.state.create = vnode.attrs.create
41 vnode.state.options = vnode.attrs.options
42 // Option is selected from the list.
43 $(vnode.dom).on('awesomplete-selectcomplete', function(event) {
44 vnode.attrs.value(this.value)
46 $(vnode.dom).on('change', function(event) {
47 if (!vnode.state.create && !(this.value in vnode.state.options)) {
48 this.value = vnode.attrs.value()
50 if (vnode.attrs.value() !== this.value) {
51 vnode.attrs.value(this.value)
55 $(vnode.dom).on('focusin', function(event) {
56 if (this.value === '') {
57 vnode.state.awesomplete.evaluate()
58 vnode.state.awesomplete.open()
62 onupdate: function(vnode) {
63 vnode.state.awesomplete.list = vnode.attrs.options
64 vnode.state.create = vnode.attrs.create
65 vnode.state.options = vnode.attrs.options
69 window.TagEditorRow = {
70 view: function(vnode) {
71 var nameOpts = Object.keys(vnode.attrs.vocabulary().tags)
73 var inputComponent = SelectOrAutocomplete
74 if (nameOpts.length === 0) {
75 // If there's not vocabulary defined, switch to a simple input field
76 inputComponent = SimpleInput
79 if (vnode.attrs.name() != '' && !(vnode.attrs.name() in vnode.attrs.vocabulary().tags)) {
80 nameOpts.push(vnode.attrs.name())
83 if (vnode.attrs.name() in vnode.attrs.vocabulary().tags &&
84 'values' in vnode.attrs.vocabulary().tags[vnode.attrs.name()]) {
85 valueOpts = vnode.attrs.vocabulary().tags[vnode.attrs.name()].values
91 vnode.attrs.editMode &&
92 m('div.text-center', m('a.btn.btn-default.btn-sm', {
96 onclick: function(e) { vnode.attrs.removeTag() }
97 }, m('i.fa.fa-fw.fa-trash-o')))
101 vnode.attrs.editMode ?
102 m("div", {key: 'key'}, [
105 value: vnode.attrs.name,
106 // Allow any tag name unless "strict" is set to true.
107 create: !vnode.attrs.vocabulary().strict,
115 vnode.attrs.editMode ?
116 m("div", {key: 'value'}, [
119 value: vnode.attrs.value,
120 placeholder: 'value',
121 // Allow any value on tags not listed on the vocabulary.
122 // Allow any value on tags without values, or the ones
123 // that aren't explicitly declared to be strict.
124 create: !(vnode.attrs.name() in vnode.attrs.vocabulary().tags)
125 || !vnode.attrs.vocabulary().tags[vnode.attrs.name()].values
126 || vnode.attrs.vocabulary().tags[vnode.attrs.name()].values.length === 0
127 || !vnode.attrs.vocabulary().tags[vnode.attrs.name()].strict,
136 window.TagEditorTable = {
137 view: function(vnode) {
138 return m("table.table.table-condensed.table-justforlayout", [
140 m("col", {width:"5%"}),
141 m("col", {width:"25%"}),
142 m("col", {width:"70%"}),
152 vnode.attrs.tags.length > 0
153 ? vnode.attrs.tags.map(function(tag, idx) {
154 return m(TagEditorRow, {
156 removeTag: function() {
157 vnode.attrs.tags.splice(idx, 1)
158 vnode.attrs.dirty(true)
160 editMode: vnode.attrs.editMode,
163 vocabulary: vnode.attrs.vocabulary
166 : m("tr", m("td[colspan=3]", m("center", "Loading tags...")))
174 window.TagEditorApp = {
175 appendTag: function(vnode, name, value) {
176 var tag = {name: m.stream(name), value: m.stream(value), rowKey: uniqueID++}
177 vnode.state.tags.push(tag)
178 // Set dirty flag when any of name/value changes to non empty string
179 tag.name.map(function() { vnode.state.dirty(true) })
180 tag.value.map(function() { vnode.state.dirty(true) })
181 tag.name.map(m.redraw)
183 oninit: function(vnode) {
184 vnode.state.sessionDB = new SessionDB()
186 vnode.state.vocabulary = m.stream({"strict":false, "tags":{}})
187 m.request('/vocabulary.json').then(vnode.state.vocabulary)
188 vnode.state.editMode = vnode.attrs.targetEditable
189 vnode.state.tags = []
190 vnode.state.dirty = m.stream(false)
191 vnode.state.dirty.map(m.redraw)
192 vnode.state.objPath = '/arvados/v1/'+vnode.attrs.targetController+'/'+vnode.attrs.targetUuid
194 vnode.state.sessionDB.request(
195 vnode.state.sessionDB.loadLocal(),
196 '/arvados/v1/'+vnode.attrs.targetController,
199 filters: JSON.stringify([['uuid', '=', vnode.attrs.targetUuid]]),
200 select: JSON.stringify(['properties'])
202 }).then(function(obj) {
203 if (obj.items.length == 1) {
205 Object.keys(o.properties).forEach(function(k) {
206 vnode.state.appendTag(vnode, k, o.properties[k])
208 if (vnode.state.editMode) {
209 vnode.state.appendTag(vnode, '', '')
211 // Data synced with server, so dirty state should be false
212 vnode.state.dirty(false)
213 // Add new tag row when the last one is completed
214 vnode.state.dirty.map(function() {
215 if (!vnode.state.editMode) { return }
216 lastTag = vnode.state.tags.slice(-1).pop()
217 if (lastTag === undefined || (lastTag.name() !== '' || lastTag.value() !== '')) {
218 vnode.state.appendTag(vnode, '', '')
225 view: function(vnode) {
227 vnode.state.editMode &&
229 m("a.btn.btn-primary.btn-sm"+(vnode.state.dirty() ? '' : '.disabled'), {
233 onclick: function(e) {
235 vnode.state.tags.forEach(function(t) {
236 // Only ignore tags with empty key
237 if (t.name() != '') {
238 tags[t.name()] = t.value()
241 vnode.state.sessionDB.request(
242 vnode.state.sessionDB.loadLocal(),
243 vnode.state.objPath, {
245 data: {properties: JSON.stringify(tags)}
248 vnode.state.dirty(false)
251 }, vnode.state.dirty() ? ' Save changes ' : ' Saved ')
255 editMode: vnode.state.editMode,
256 tags: vnode.state.tags,
257 vocabulary: vnode.state.vocabulary,
258 dirty: vnode.state.dirty