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)
21 oncreate: function(vnode) {
22 if (vnode.attrs.setFocus) {
28 window.SelectOrAutocomplete = {
29 view: function(vnode) {
30 return m("input.form-control", {
35 value: vnode.attrs.value,
36 placeholder: (vnode.attrs.create ? 'Add or select ': 'Select ') + vnode.attrs.placeholder,
39 oncreate: function(vnode) {
40 var awesomplete = new Awesomplete(vnode.dom, {
41 list: vnode.attrs.options,
45 // Option is selected from the list.
46 $(vnode.dom).on('awesomplete-selectcomplete', function(event) {
47 vnode.attrs.value(this.value)
49 $(vnode.dom).on('change', function(event) {
50 if (!vnode.attrs.create && !(this.value in vnode.attrs.options)) {
51 this.value = vnode.attrs.value()
53 if (vnode.attrs.value() !== this.value) {
54 vnode.attrs.value(this.value)
58 $(vnode.dom).on('focusin', function(event) {
59 // Open list when focusing on empty strict fields
60 if (!vnode.attrs.create && this.value === '') {
61 // minChars = 0 && evaluate() makes the list open without
63 awesomplete.evaluate()
66 if (vnode.attrs.setFocus) {
72 window.TagEditorRow = {
73 view: function(vnode) {
74 var nameOpts = Object.keys(vnode.attrs.vocabulary().tags)
76 var inputComponent = SelectOrAutocomplete
77 if (nameOpts.length === 0) {
78 // If there's not vocabulary defined, switch to a simple input field
79 inputComponent = SimpleInput
82 if (vnode.attrs.name() != '' && !(vnode.attrs.name() in vnode.attrs.vocabulary().tags)) {
83 nameOpts.push(vnode.attrs.name())
86 if (vnode.attrs.name() in vnode.attrs.vocabulary().tags &&
87 'values' in vnode.attrs.vocabulary().tags[vnode.attrs.name()]) {
88 valueOpts = vnode.attrs.vocabulary().tags[vnode.attrs.name()].values
94 vnode.attrs.editMode &&
95 m('div.text-center', m('a.btn.btn-default.btn-sm', {
99 onclick: function(e) { vnode.attrs.removeTag() }
100 }, m('i.fa.fa-fw.fa-trash-o')))
104 vnode.attrs.editMode ?
108 value: vnode.attrs.name,
109 // Allow any tag name unless "strict" is set to true.
110 create: !vnode.attrs.vocabulary().strict,
119 vnode.attrs.editMode ?
120 m("div", {key: 'value-'+vnode.attrs.name()}, [
123 value: vnode.attrs.value,
124 placeholder: 'value',
125 // Allow any value on tags not listed on the vocabulary.
126 // Allow any value on tags without values, or the ones
127 // that aren't explicitly declared to be strict.
128 create: !(vnode.attrs.name() in vnode.attrs.vocabulary().tags)
129 || !vnode.attrs.vocabulary().tags[vnode.attrs.name()].values
130 || vnode.attrs.vocabulary().tags[vnode.attrs.name()].values.length === 0
131 || !vnode.attrs.vocabulary().tags[vnode.attrs.name()].strict,
132 // Focus on tag value field when new tag name is set
133 setFocus: vnode.attrs.name() !== '' && vnode.attrs.value() === ''
142 window.TagEditorTable = {
143 view: function(vnode) {
144 return m("table.table.table-condensed.table-justforlayout", [
146 m("col", {width:"5%"}),
147 m("col", {width:"25%"}),
148 m("col", {width:"70%"}),
158 vnode.attrs.tags.length > 0
159 ? vnode.attrs.tags.map(function(tag, idx) {
160 return m(TagEditorRow, {
162 removeTag: function() {
163 vnode.attrs.tags.splice(idx, 1)
164 vnode.attrs.dirty(true)
166 editMode: vnode.attrs.editMode,
169 vocabulary: vnode.attrs.vocabulary
172 : m("tr", m("td[colspan=3]", m("center", "Loading tags...")))
178 window.TagEditorApp = {
179 appendTag: function(vnode, name, value) {
180 var tag = {name: m.stream(name), value: m.stream(value)}
181 vnode.state.tags.push(tag)
182 // Set dirty flag when any of name/value changes to non empty string
183 tag.name.map(function(v) {
185 vnode.state.dirty(true)
188 tag.value.map(function(v) {
190 vnode.state.dirty(true)
193 tag.name.map(m.redraw)
195 oninit: function(vnode) {
196 vnode.state.sessionDB = new SessionDB()
198 vnode.state.vocabulary = m.stream({"strict":false, "tags":{}})
199 m.request('/vocabulary.json').then(vnode.state.vocabulary)
200 vnode.state.editMode = vnode.attrs.targetEditable
201 vnode.state.tags = []
202 vnode.state.dirty = m.stream(false)
203 vnode.state.dirty.map(m.redraw)
204 vnode.state.objPath = '/arvados/v1/'+vnode.attrs.targetController+'/'+vnode.attrs.targetUuid
206 vnode.state.sessionDB.request(
207 vnode.state.sessionDB.loadLocal(),
208 '/arvados/v1/'+vnode.attrs.targetController,
211 filters: JSON.stringify([['uuid', '=', vnode.attrs.targetUuid]]),
212 select: JSON.stringify(['properties'])
214 }).then(function(obj) {
215 if (obj.items.length == 1) {
217 Object.keys(o.properties).forEach(function(k) {
218 vnode.state.appendTag(vnode, k, o.properties[k])
220 // Data synced with server, so dirty state should be false
221 vnode.state.dirty(false)
222 // Add new tag row when the last one is completed
223 vnode.state.dirty.map(function() {
224 if (!vnode.state.editMode) { return }
225 lastTag = vnode.state.tags.slice(-1).pop()
226 if (lastTag === undefined || (lastTag.name() !== '' || lastTag.value() !== '')) {
227 vnode.state.appendTag(vnode, '', '')
234 view: function(vnode) {
236 vnode.state.editMode &&
238 m("a.btn.btn-primary.btn-sm"+(vnode.state.dirty() ? '' : '.disabled'), {
242 onclick: function(e) {
244 vnode.state.tags.forEach(function(t) {
245 if (t.name() != '' && t.value() != '') {
246 tags[t.name()] = t.value()
249 vnode.state.sessionDB.request(
250 vnode.state.sessionDB.loadLocal(),
251 vnode.state.objPath, {
253 data: {properties: JSON.stringify(tags)}
256 vnode.state.dirty(false)
259 }, vnode.state.dirty() ? ' Save changes ' : ' Saved ')
263 editMode: vnode.state.editMode,
264 tags: vnode.state.tags,
265 vocabulary: vnode.state.vocabulary,
266 dirty: vnode.state.dirty