1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 window.SelectOrAutocomplete = {
6 view: function(vnode) {
12 value: vnode.attrs.value
15 oncreate: function(vnode) {
16 this.selectized = $(vnode.dom).selectize({
22 placeholder: vnode.attrs.placeholder,
23 create: vnode.attrs.create ? function(input) {
26 items: [vnode.attrs.value()],
27 options: vnode.attrs.options.map(function(option) {
28 return {value: option}
30 onChange: function(val) {
32 vnode.attrs.value(val)
36 if (vnode.attrs.setFocus) {
37 this.selectized[0].selectize.focus()
42 window.TagEditorRow = {
43 view: function(vnode) {
45 var nameOpts = Object.keys(vnode.attrs.vocabulary().tags)
46 if (vnode.attrs.name() != '' && !(vnode.attrs.name() in vnode.attrs.vocabulary().tags)) {
47 nameOpts.push(vnode.attrs.name())
51 if (vnode.attrs.name() in vnode.attrs.vocabulary().tags &&
52 'values' in vnode.attrs.vocabulary().tags[vnode.attrs.name()]) {
53 valueOpts = vnode.attrs.vocabulary().tags[vnode.attrs.name()].values
55 if (vnode.attrs.value() != '') {
56 valueOpts.push(vnode.attrs.value())
61 vnode.attrs.editMode &&
62 m('div.text-center', m('a.btn.btn-default.btn-sm', {
66 onclick: function(e) { vnode.attrs.removeTag() }
67 }, m('i.fa.fa-fw.fa-trash-o')))
71 vnode.attrs.editMode ?
72 m("div", {key: 'name-'+vnode.attrs.name()},[m(SelectOrAutocomplete, {
74 value: vnode.attrs.name,
75 // Allow any tag name unless "strict" is set to true.
76 create: !vnode.attrs.vocabulary().strict,
77 placeholder: 'new tag',
78 // Focus on tag name field when adding a new tag that's not
80 setFocus: !vnode.attrs.firstRow && vnode.attrs.name() === ''
85 vnode.attrs.editMode ?
86 m("div", {key: 'value-'+vnode.attrs.name()}, [m(SelectOrAutocomplete, {
88 value: vnode.attrs.value,
89 placeholder: 'new value',
90 // Allow any value on tags not listed on the vocabulary.
91 // Allow any value on tags without values, or the ones that
92 // aren't explicitly declared to be strict.
93 create: !(vnode.attrs.name() in vnode.attrs.vocabulary().tags)
94 || !vnode.attrs.vocabulary().tags[vnode.attrs.name()].values
95 || vnode.attrs.vocabulary().tags[vnode.attrs.name()].values.length === 0
96 || !vnode.attrs.vocabulary().tags[vnode.attrs.name()].strict,
97 // Focus on tag value field when new tag name is set
98 setFocus: vnode.attrs.name() !== '' && vnode.attrs.value() === ''
106 window.TagEditorTable = {
107 view: function(vnode) {
108 return m("table.table.table-condensed", {
112 m("col", {width:"5%"}),
113 m("col", {width:"25%"}),
114 m("col", {width:"70%"}),
124 vnode.attrs.tags.length > 0
125 ? vnode.attrs.tags.map(function(tag, idx) {
126 return m(TagEditorRow, {
128 removeTag: function() {
129 vnode.attrs.tags.splice(idx, 1)
130 vnode.attrs.dirty(true)
132 editMode: vnode.attrs.editMode,
133 firstRow: vnode.attrs.tags.length === 1,
136 vocabulary: vnode.attrs.vocabulary
139 : m("tr", m("td[colspan=3]", m("center","loading tags...")))
145 window.TagEditorApp = {
146 appendTag: function(vnode, name, value) {
147 var tag = {name: m.stream(name), value: m.stream(value)}
148 vnode.state.tags.push(tag)
149 // Set dirty flag when any of name/value changes to non empty string
150 tag.name.map(function(v) {
152 vnode.state.dirty(true)
155 tag.value.map(function(v) {
157 vnode.state.dirty(true)
160 tag.name.map(m.redraw)
162 oninit: function(vnode) {
163 vnode.state.sessionDB = new SessionDB()
165 vnode.state.vocabulary = m.stream({"strict":false, "tags":{}})
166 m.request('/vocabulary.json').then(vnode.state.vocabulary)
167 vnode.state.editMode = vnode.attrs.targetEditable
168 vnode.state.tags = []
169 vnode.state.dirty = m.stream(false)
170 vnode.state.dirty.map(m.redraw)
171 vnode.state.objPath = '/arvados/v1/'+vnode.attrs.targetController+'/'+vnode.attrs.targetUuid
173 vnode.state.sessionDB.request(
174 vnode.state.sessionDB.loadLocal(),
175 '/arvados/v1/'+vnode.attrs.targetController,
178 filters: JSON.stringify([['uuid', '=', vnode.attrs.targetUuid]]),
179 select: JSON.stringify(['properties'])
181 }).then(function(obj) {
182 if (obj.items.length == 1) {
184 Object.keys(o.properties).forEach(function(k) {
185 vnode.state.appendTag(vnode, k, o.properties[k])
187 // Data synced with server, so dirty state should be false
188 vnode.state.dirty(false)
189 // Add new tag row when the last one is completed
190 vnode.state.dirty.map(function() {
191 if (!vnode.state.editMode) { return }
192 lastTag = vnode.state.tags.slice(-1).pop()
193 if (lastTag === undefined || (lastTag.name() !== '' && lastTag.value() !== '')) {
194 vnode.state.appendTag(vnode, '', '')
201 view: function(vnode) {
203 vnode.state.editMode &&
205 m("a.btn.btn-primary.btn-sm"+(vnode.state.dirty() ? '' : '.disabled'), {
209 onclick: function(e) {
211 vnode.state.tags.forEach(function(t) {
212 if (t.name() != '' && t.value() != '') {
213 tags[t.name()] = t.value()
216 vnode.state.sessionDB.request(
217 vnode.state.sessionDB.loadLocal(),
218 vnode.state.objPath, {
220 data: {properties: JSON.stringify(tags)}
223 vnode.state.dirty(false)
226 }, vnode.state.dirty() ? ' Save changes ' : ' Saved ')
230 editMode: vnode.state.editMode,
231 tags: vnode.state.tags,
232 vocabulary: vnode.state.vocabulary,
233 dirty: vnode.state.dirty