json-object-editor 0.10.653 → 0.10.657

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/js/joe.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /* --------------------------------------------------------
2
2
  *
3
- * json-object-editor - v0.10.653
3
+ * json-object-editor - v0.10.657
4
4
  * Created by: Corey Hadden
5
5
  *
6
6
  * -------------------------------------------------------- */
@@ -174,6 +174,27 @@ function JsonObjectEditor(specs){
174
174
  }});
175
175
  }
176
176
 
177
+ };
178
+ this.uploaderSetFileRole = function(uploader_id, filename, role){
179
+ try{
180
+ var joe_uploader = self.uploaders && self.uploaders[uploader_id];
181
+ if (!joe_uploader || !Array.isArray(joe_uploader.files)) { return; }
182
+ var file = joe_uploader.files.where({filename:filename})[0];
183
+ if (!file) { return; }
184
+ if (role) {
185
+ file.file_role = role;
186
+ } else {
187
+ delete file.file_role;
188
+ }
189
+ if (joe_uploader.preview) {
190
+ $(joe_uploader.preview).html(_renderUploaderFilePreviews(joe_uploader.files, joe_uploader.cuid));
191
+ }
192
+ if (typeof self.updateObject === 'function') {
193
+ self.updateObject(null, null, true);
194
+ }
195
+ }catch(e){
196
+ console.warn('uploaderSetFileRole error', e && e.message || e);
197
+ }
177
198
  };
178
199
  window._joes.push(this);
179
200
 
@@ -5335,6 +5356,19 @@ this.renderHTMLContent = function(specs){
5335
5356
  if(files && !$c.isArray(files)){
5336
5357
  files = [files];
5337
5358
  }
5359
+
5360
+ // Register uploader object (including optional file_roles) BEFORE we render
5361
+ // previews so that _renderUploaderFilePreviews can access role metadata.
5362
+ var uploader_obj = {
5363
+ cuid: uploader_id,
5364
+ prop: prop.name,
5365
+ files: files,
5366
+ max: prop.max || 0,
5367
+ // Optional file role configuration from schema (e.g. [{value,label,default}])
5368
+ roles: prop.file_roles || []
5369
+ };
5370
+ self.uploaders[uploader_id] = uploader_obj;
5371
+
5338
5372
  if(prop.field && self.current.object[prop.field]){
5339
5373
  var kfield = self.getField(prop.field);
5340
5374
  var preview = self.current.object[prop.field]
@@ -5357,18 +5391,11 @@ this.renderHTMLContent = function(specs){
5357
5391
  '<div class="joe-uploader-preview">'+dz_message+'</div>'+
5358
5392
  '</div>'+
5359
5393
  '<div class="joe-uploader-message">add a file</div>'+
5360
- '<div class="joe-button joe-green-button joe-upload-cofirm-button hidden" onclick="_joe.uploaderConfirm(\''+uploader_id+'\',\''+prop.name+'\');">Upload File</div>'+__clearDiv__+
5394
+ '<div class="joe-button joe-green-button joe-upload-cofirm-button ui-helper-hidden hidden" onclick="_joe.uploaderConfirm(\''+uploader_id+'\',\''+prop.name+'\');">Upload File</div>'+__clearDiv__+
5361
5395
  '</div>'
5362
5396
  ;
5363
5397
  //var idprop = prop.idprop || '_id';
5364
5398
  //html+= __clearDiv__;
5365
- var uploader_obj = {
5366
- cuid:uploader_id,
5367
- prop:prop.name,
5368
- files:files,
5369
- max:prop.max || 0
5370
- }
5371
- self.uploaders[uploader_id] = uploader_obj;
5372
5399
  return html;
5373
5400
  };
5374
5401
  function _removeUploaderFile(id,filename){
@@ -5384,9 +5411,10 @@ this.renderHTMLContent = function(specs){
5384
5411
  var jup_template,html= '';
5385
5412
  var alink ="<a href='${url}${base64}' class='file-link' target='_blank'></a>";
5386
5413
  var delete_btn = '<joe-uploader-file-delete-btn class="svg-shadow" onclick="_joe.Fields.uploader.remove(\''+cuid+'\',\'${filename}\');">'+self.SVG.icon.close+'</joe-uploader-delete-btn>';
5387
- var label = '<joe-uploader-file-label>${filename}</joe-uploader-file-label>';
5388
-
5389
-
5414
+ var label = '';
5415
+ var uploader = self.uploaders && self.uploaders[cuid];
5416
+ var roles = uploader && uploader.roles;
5417
+
5390
5418
  files.map(function(file){
5391
5419
  var filesize = '<joe-uploader-file-label>${filename}</joe-uploader-file-label>';
5392
5420
  if(file.size){
@@ -5409,18 +5437,49 @@ this.renderHTMLContent = function(specs){
5409
5437
 
5410
5438
 
5411
5439
 
5440
+ // Build the label with filename and optional OpenAI file id on a new line
5441
+ label = '<joe-uploader-file-label>'+(file.filename || '')+'</joe-uploader-file-label>';
5442
+ if(file.openai_file_id){
5443
+ label += '<joe-uploader-file-oaid >'+file.openai_file_id+'</joe-uploader-file-oaid>';
5444
+ }
5445
+
5446
+ // Optional file role selector when roles are configured on this uploader
5447
+ var roleSelect = '';
5448
+ if (roles && roles.length) {
5449
+ var currentRole = file.file_role || '';
5450
+ var safeFilename = String(file.filename || '').replace(/'/g, "\\'");
5451
+ var optsHtml = '<option value=""></option>';
5452
+ roles.forEach(function(r){
5453
+ if (!r || !r.value) { return; }
5454
+ var lbl = r.label || r.value;
5455
+ var sel = (currentRole === r.value) ? ' selected' : '';
5456
+ optsHtml += '<option value="'+r.value.replace(/\"/g,'&quot;')+'"'+sel+'>'+lbl.replace(/</g,'&lt;').replace(/>/g,'&gt;')+'</option>';
5457
+ });
5458
+ var onchange = "_joe.uploaderSetFileRole('"+cuid+"','"+safeFilename+"', this.value)";
5459
+ roleSelect = '<div class=\"joe-uploader-file-role\"><select onchange=\"'+onchange+'\">'+optsHtml+'</select></div>';
5460
+ }
5461
+
5412
5462
  if((file.type && file.type.contains('image')) || (!file.type && (file.url||file.base64).split('.').contains(['jpg','jpeg','png','gif','svg']))){
5413
- jup_template = '<joe-uploader-file class="'+(file.uploaded && 'uploaded'||'')+'" style="background-image:url(${url}${base64})">'+alink+label+filesize+delete_btn+'</joe-uploader-file>';
5463
+ jup_template = '<joe-uploader-file class="'+(file.uploaded && 'uploaded'||'')+'" style="background-image:url(${url}${base64})">'+alink+label+roleSelect+filesize+delete_btn+'</joe-uploader-file>';
5414
5464
  }else if (docTypes.includes((file.url || file.base64).split('.').pop())) {
5415
5465
  jup_template = '<joe-uploader-file class="'+(file.uploaded && 'uploaded'||'')+'">'+
5416
5466
  '<joe-uploader-file-extension >.'+(file.type.split('/')[1] || 'docx')+'</joe-uploader-file-extension>'+
5417
- alink+label+filesize+delete_btn+'</joe-uploader-file>';
5467
+ alink+label+roleSelect+filesize+delete_btn+'</joe-uploader-file>';
5418
5468
  }else{
5419
5469
  jup_template = '<joe-uploader-file class="'+(file.uploaded && 'uploaded'||'')+'">'+
5420
5470
  '<joe-uploader-file-extension >.'+(file.type.split('/')[1] || '???')+'</joe-uploader-file-extension>'+
5421
- alink+label+filesize+delete_btn+'</joe-uploader-file>';
5471
+ alink+label+roleSelect+filesize+delete_btn+'</joe-uploader-file>';
5422
5472
  }
5423
- html += fillTemplate(jup_template,file);
5473
+ // Build optional OpenAI retry/upload button
5474
+ var openaiBtn = '';
5475
+ var needRetry = (!file.openai_file_id || file.openai_status == 'error');
5476
+ var hasUrl = !!file.url;
5477
+ if(hasUrl && needRetry){
5478
+ var openaiLabel = (file.openai_status == 'error') ? 'Retry OpenAI' : 'Upload to OpenAI';
5479
+ var safeFilename = (file.filename || '').replace(/'/g,"\\'");
5480
+ openaiBtn = '<joe-uploader-openai-btn class="joe-button" style="margin-left:6px;" onclick="_joe.SERVER.Plugins.openaiRetryFromUrl(\''+cuid+'\',\''+safeFilename+'\');">'+openaiLabel+'</joe-uploader-openai-btn>';
5481
+ }
5482
+ html += fillTemplate(jup_template,file) + openaiBtn;
5424
5483
  })
5425
5484
  return html+'<div class="clear"></div>';
5426
5485
  }
@@ -5437,7 +5496,7 @@ this.renderHTMLContent = function(specs){
5437
5496
  $(joe_uploader.dropzone).find('img,div').replaceWith('<img src="' + base64 + '">')
5438
5497
  //joe_uploader.dropzone.html('<img src="' + base64 + '">');
5439
5498
  joe_uploader.message.html('<b>'+file.name+'</b> selected');
5440
- joe_uploader.confirmBtn.removeClass('hidden');
5499
+ joe_uploader.confirmBtn.removeClass('ui-helper-hidden hidden');
5441
5500
  }else {
5442
5501
  results.innerHTML = 'Nothing to upload.';
5443
5502
  }
@@ -5468,7 +5527,7 @@ this.renderHTMLContent = function(specs){
5468
5527
  }
5469
5528
  joe_uploader.files.push(temp_file);
5470
5529
  $(joe_uploader.preview).html(_renderUploaderFilePreviews(joe_uploader.files,joe_uploader.cuid));
5471
- joe_uploader.confirmBtn.removeClass('hidden');
5530
+ joe_uploader.confirmBtn.removeClass('ui-helper-hidden hidden');
5472
5531
  }else {
5473
5532
  results.innerHTML = 'Nothing to upload.';
5474
5533
  }
@@ -5536,6 +5595,20 @@ this.renderHTMLContent = function(specs){
5536
5595
  var fileobj = joe_uploader.files.where({filename:filename})[0];
5537
5596
  fileobj.uploaded = new Date();
5538
5597
  fileobj.url = url;
5598
+ // Apply captured OpenAI info if present
5599
+ var key = self.current.object._id + '/' + filename;
5600
+ var oi = _joe._lastOpenAI && _joe._lastOpenAI[key];
5601
+ fileobj.openai_purpose = 'assistants';
5602
+ if(oi){
5603
+ fileobj.openai_file_id = oi.openai_file_id || null;
5604
+ fileobj.openai_status = (oi.openai_file_id && 'ok') || (oi.openai_error && 'error') || 'not_uploaded';
5605
+ fileobj.openai_error = oi.openai_error || null;
5606
+ if(oi.openai_error){
5607
+ joe_uploader.message.append('<div>OpenAI error: '+oi.openai_error+'</div>');
5608
+ }
5609
+ }else{
5610
+ fileobj.openai_status = fileobj.openai_status || 'not_uploaded';
5611
+ }
5539
5612
  delete fileobj.base64;
5540
5613
  }
5541
5614
 
@@ -5606,6 +5679,14 @@ this.renderHTMLContent = function(specs){
5606
5679
  self.uploaders[id].dropzone = $(this).find('.joe-uploader-dropzone');
5607
5680
  self.uploaders[id].confirmBtn = $(this).find('.joe-upload-cofirm-button');
5608
5681
 
5682
+ // If there are pending files (not uploaded or missing url), show the confirm button
5683
+ var pending = (self.uploaders[id].files || []).some(function(file){
5684
+ return $c.isObject(file) && (!file.uploaded || !file.url);
5685
+ });
5686
+ if(pending){
5687
+ self.uploaders[id].confirmBtn.removeClass('ui-helper-hidden hidden');
5688
+ }
5689
+
5609
5690
  });
5610
5691
  };
5611
5692
  encapsulateFieldType('uploader',self.renderUploaderField,
@@ -9942,6 +10023,13 @@ logit(intent)
9942
10023
  url = prefix+response.Key;
9943
10024
  }
9944
10025
  var filename = response.Key.replace(self.current.object._id+'/','');
10026
+ // Stash OpenAI info for use during finalization
10027
+ _joe._lastOpenAI = _joe._lastOpenAI || {};
10028
+ _joe._lastOpenAI[response.Key] = {
10029
+ openai_file_id: response.openai_file_id,
10030
+ openai_purpose: response.openai_purpose,
10031
+ openai_error: response.openai_error
10032
+ };
9945
10033
  if(field.url_field){
9946
10034
  var nprop = {};
9947
10035
  _joe.current.object[field.url_field] = url;
@@ -9966,6 +10054,53 @@ logit(intent)
9966
10054
  file: { base64:data.base64, extension:data.extension, type:data.contentType, filename: (_joe.current.object._id+data.extension), name: (_joe.current.object._id+data.extension) },
9967
10055
  field: data.field
9968
10056
  }, callback);
10057
+ },
10058
+ // Retry uploading a specific file to OpenAI using its URL.
10059
+ // Invoked by the uploader row action.
10060
+ openaiRetryFromUrl:function(uploader_id, filename){
10061
+ try{
10062
+ var joe_uploader = self.uploaders[uploader_id];
10063
+ if(!joe_uploader){ return alert('Uploader not found'); }
10064
+ var file = joe_uploader.files.where({filename:filename})[0];
10065
+ if(!file){ return alert('File not found'); }
10066
+ if(!file.url){ return alert('No URL to upload to OpenAI'); }
10067
+ var btn = $(joe_uploader.preview).find("joe-uploader-openai-btn:contains('"+filename+"')");
10068
+ joe_uploader.message.append('<div>Uploading to OpenAI...</div>');
10069
+ $.ajax({
10070
+ url: (location.port && '//'+__jsc.hostname+':'+__jsc.port+'/API/plugin/chatgpt/filesRetryFromUrl/') || '//'+__jsc.hostname+'/API/plugin/chatgpt/filesRetryFromUrl/',
10071
+ type:'POST',
10072
+ data:{
10073
+ url:file.url,
10074
+ filename:file.filename,
10075
+ contentType:file.type
10076
+ },
10077
+ success:function(resp){
10078
+ if(resp && resp.success && resp.openai_file_id){
10079
+ file.openai_file_id = resp.openai_file_id;
10080
+ file.openai_purpose = resp.openai_purpose || 'assistants';
10081
+ file.openai_status = 'ok';
10082
+ file.openai_error = null;
10083
+ joe_uploader.message.append('<div>OpenAI file attached</div>');
10084
+ }else{
10085
+ file.openai_status = 'error';
10086
+ file.openai_error = (resp && resp.error) || 'Upload failed';
10087
+ joe_uploader.message.append('<div>OpenAI error: '+(file.openai_error)+'</div>');
10088
+ }
10089
+ // Refresh preview to reflect new status/button
10090
+ $(joe_uploader.preview).html(_renderUploaderFilePreviews(joe_uploader.files,joe_uploader.cuid));
10091
+ // Save object so file metadata is persisted
10092
+ self.updateObject(null, null, true);
10093
+ },
10094
+ error:function(xhr,status,err){
10095
+ var message = (xhr && (xhr.responseJSON && (xhr.responseJSON.error || xhr.responseJSON.code) || xhr.responseText)) || err || status || 'Retry failed';
10096
+ file.openai_status = 'error';
10097
+ file.openai_error = message;
10098
+ joe_uploader.message.append('<div>OpenAI error: '+message+'</div>');
10099
+ }
10100
+ });
10101
+ }catch(e){
10102
+ alert(e && e.message || e);
10103
+ }
9969
10104
  }
9970
10105
  },
9971
10106
  ready:{