json-object-editor
Version:
JOE the Json Object Editor | Platform Edition
596 lines (568 loc) • 26.8 kB
JavaScript
var usStates = [
{ name: 'Alabama', code: 'AL'},
{ name: 'Alaska', code: 'AK'},
{ name: 'American Samoa', code: 'AS'},
{ name: 'Arizona', code: 'AZ'},
{ name: 'Arkansas', code: 'AR'},
{ name: 'California', code: 'CA'},
{ name: 'Colorado', code: 'CO'},
{ name: 'Connecticut', code: 'CT'},
{ name: 'Delaware', code: 'DE'},
{ name: 'District of Columbia', code: 'DC'},
{ name: 'Federated States of Micronesia', code: 'FM'},
{ name: 'Florida', code: 'FL'},
{ name: 'Georgia', code: 'GA'},
{ name: 'Guam', code: 'GU'},
{ name: 'Hawaii', code: 'HI'},
{ name: 'Idaho', code: 'ID'},
{ name: 'Illinois', code: 'IL'},
{ name: 'Indiana', code: 'IN'},
{ name: 'Iowa', code: 'IA'},
{ name: 'Kansas', code: 'KS'},
{ name: 'Kentucky', code: 'KY'},
{ name: 'Louisiana', code: 'LA'},
{ name: 'Maine', code: 'ME'},
{ name: 'Marshall Islands', code: 'MH'},
{ name: 'Maryland', code: 'MD'},
{ name: 'Massachusetts', code: 'MA'},
{ name: 'Michigan', code: 'MI'},
{ name: 'Minnesota', code: 'MN'},
{ name: 'Mississippi', code: 'MS'},
{ name: 'Missouri', code: 'MO'},
{ name: 'Montana', code: 'MT'},
{ name: 'Nebraska', code: 'NE'},
{ name: 'Nevada', code: 'NV'},
{ name: 'New Hampshire', code: 'NH'},
{ name: 'New Jersey', code: 'NJ'},
{ name: 'New Mexico', code: 'NM'},
{ name: 'New York', code: 'NY'},
{ name: 'North Carolina', code: 'NC'},
{ name: 'North Dakota', code: 'ND'},
{ name: 'Northern Mariana Islands', code: 'MP'},
{ name: 'Ohio', code: 'OH'},
{ name: 'Oklahoma', code: 'OK'},
{ name: 'Oregon', code: 'OR'},
{ name: 'Palau', code: 'PW'},
{ name: 'Pennsylvania', code: 'PA'},
{ name: 'Puerto Rico', code: 'PR'},
{ name: 'Rhode Island', code: 'RI'},
{ name: 'South Carolina', code: 'SC'},
{ name: 'South Dakota', code: 'SD'},
{ name: 'Tennessee', code: 'TN'},
{ name: 'Texas', code: 'TX'},
{ name: 'Utah', code: 'UT'},
{ name: 'Vermont', code: 'VT'},
{ name: 'Virgin Islands', code: 'VI'},
{ name: 'Virginia', code: 'VA'},
{ name: 'Washington', code: 'WA'},
{ name: 'West Virginia', code: 'WV'},
{ name: 'Wisconsin', code: 'WI'},
{ name: 'Wyoming', code: 'WY' }
];
var fields = {
'_id':{type:'guid',width:'50%'},
'token':{type:'guid'},
url:{type:'url'},
'description':{type:'wysiwyg'},
'coords':{type:'geo'},
'state':{type:'select',values:usStates,idprop:'code',template:'${code}',width:'25%',minwidth:'100px',blank:true},
'name':{type:'text',onblur:'_joe.TITLE.set()'},
instructions_format:{type:'select',values:['wysiwyg','code','text',], rerender:'instructions',display:'Instructions Format'},
instructions:{
height:'500px',
display:'Instructions',
default:'code',
type:function(item){
if(!item.instructions_format || item.instructions_format == 'text'){
return 'rendering';
}
else if(["code"].indexOf(item.instructions_format) != -1){
return 'code';
}
return item.instructions_format;
}
},
template:{
height:'600px',
type:function(item){
if(!item.template_type){
return 'code';
}
if(["code","module"].indexOf(item.template_type) != -1){
return 'code';
}
return item.template_type;
}
},
created:{locked:true,width:'50%'},
itemtype:{locked:true, hidden:true},
priority:{type:'select',values:[{name:'',value:1000},{name:1},{name:2},{name:3}]},
site:{type:'select',values:'site',goto:'site',idprop:'_id',blank:true,icon:'site'},
group:{name:'group',type:'select',values:'group',blank:true, idprop:'_id',icon:'group'},
members:{type:'objectReference',values:'user',icon:'user',
after:function(item){
var action = `_joe.getField('members').methods.addUser('${_joe.User._id}');`;
var template = `<joe-button class="joe-button joe-svg-button joe-orangegrey-button joe-svg-button joe-left-button" onclick="${action}">
${_joe.schemas.user.menuicon}
assign this to me
</joe-button>`;
return template;
},
template:'<joe-subtext>${name}</joe-subtext><joe-title>${fullname}</joe-title><joe-subtext>${info}</joe-subtext>',
autocomplete_template:'<joe-title>${name}</joe-title><joe-subtitle>${fullname}</joe-subtitle>',
reference_specs:{stripecolor:'${color}'},
methods:{
addUser:function(userid,goto){
var current = _jco(true);
if(current.members.indexOf(userid) == -1){
current.members.push(userid);
_joe.Fields.rerender('members',{members:current.members});
}
}
}
},
people:{name:'people',type:'objectList',width:'50%',properties:[
'name',
{name:'role',width:'30%'}
]},
user_comments:{
type:'comments',
label:'add a comment'
},
content_type:{type:'select',values:['wysiwyg','code'], rerender:'content'},
content:{type:'code',height:'600px'},
template_type:{type:'select',values:['wysiwyg','code','module'], rerender:'template'},
updated:{type:'content', label:false,run:function(item){
if(item.joeUpdated){
return '<joe-subtext><small>updated</small> '+_joe.Utils.prettyPrintDTS(item.joeUpdated)
+`<a class="fright" href="/API/object/${item.itemtype}/_id/${item._id}" target="_blank">[JSON]</a></joe-subtext><clear-fix></clear-fix>`;
}
return 'new item';
}},
status:{type:'select',rerender:'status',icon:'status',
after:function(item){
if(item.joeUpdated){
var cont =`
<joe-subtext style="padding-top:5px; text-align: right;"><a class="fleft" href="/API/object/${item.itemtype}/_id/${item._id}" target="_blank">[JSON]</a><small>updated</small> ${_joe.Utils.prettyPrintDTS(item.joeUpdated)}</joe-subtext> <clear-fix></clear-fix>`
return cont;
}
return '';
},
values:function(item){
var statuses = _joe.Data.status.where({datasets:{$in:[item.itemtype]}}).sortBy('index');
return statuses;
},blank:true,idprop:'_id',
value:function(item){
var status = _joe.Data.status.where({datasets:{$in:[item.itemtype]},default:true}).sortBy('index')[0];
if(!status){return null;}
return status._id;
},
hidden:function(item){
var statuses = _joe.Data.status.where({datasets:{$in:[item.itemtype]}})
return !statuses.length;
},
containercolor:function(item,statusid){
var status = _joe.getDataItem(statusid,'status');
return status.color||'';
}
},
tags:{type:'group',icon:'tag',hidden:function(obj){
let tags = _joe.Data.tag.filter(function(tag){
return !(tag.datasets.indexOf(item.itemtype) == -1);
});
if(!tags.length){
return true;
}
},
values:function(item){
var tags = _joe.Data.tag.filter(function(tag){
return !(tag.datasets.indexOf(item.itemtype) == -1);
});
return tags.sortBy('name');
}
},
reports:{type:'content', icon:'report',run:function(item){
var html = '';
var rep_url;
var standard_reports = [];
//single report structure
if(_joe.schemas[item.itemtype] && _joe.schemas[item.itemtype].report){
var rep = _joe.schemas[item.itemtype].report
rep_url = `/API/plugin/reportbuilder/standard?itemid=${item._id}`;
html += _joe.renderFieldListItem(rep,
'<joe-title>${name}</joe-title>\
<joe-subtext>${info}</joe-subtext>',
'report',
{action:'onclick="window.open(\''+rep_url+'\');"'});
}
//multiple report structure
if(_joe.schemas[item.itemtype] && _joe.schemas[item.itemtype].reports){
Object.keys(_joe.schemas[item.itemtype].reports).map(repId=>{
var repObj = _joe.schemas[item.itemtype].reports[repId];
rep_url = `/API/plugin/reportbuilder?reportid=${repId}&itemid=${item._id}`;
html += _joe.renderFieldListItem(repObj,
`<joe-title>${repObj.name}</joe-title>
<joe-subtext>${repObj.info || repId || ''}</joe-subtext>`,
'report',
{action:'onclick="window.open(\''+rep_url+'\');"'});
})
}
//get any reports for this schema type
var reportReference = '';
var report_options = _joe.Data.report.filter(function(rep){
if(!rep.content_items || !rep.content_items.length) {
return false;
}
for(var r = 0,tot = rep.content_items.length; r < tot; r++){
if(rep.content_items[r].itemtype == item.itemtype){
reportReference = rep.content_items[r].reference;
return true;
}
}
return false;
})
report_options.map(function(rep){
console.log(rep);
rep_url = '/API/plugin/reportbuilder'+'?reportid='+rep._id+'&'+reportReference+'='+item._id;
html += _joe.renderFieldListItem(rep,
'<joe-title>${name}</joe-title>\
<joe-subtext>${info}</joe-subtext>',
'report',
{action:'onclick="window.open(\''+rep_url+'\');"'});
})
return html;
}},
milestone:{name:'milestone',width:'100px'},
tasks:{display:'Incomplete Tasks',type:'content',icon:'task',reloadable:true, run:function(item){
var tasks = (_joe.Data.task.where({$and:[{project:item._id},{complete:{$in:[false,'false',undefined]}}]})||[]).sortBy('priority,!due_date,project_phase');
var html = '';
html+= _joe.renderMenuButtons({display:'view all '+tasks.length+' tasks',
css:'joe-view-button joe-iconed-button full-width',
action:'goJoe(_joe.Data.task,{schema:\'task\', subset:\''+(item.name||'').replace(/\'/g,"\\'")+'\'})'
});
tasks.map(function(t){
var phasename = '';
if(t.project_phase){
phasename = (item.phases.where({id:t.project_phase})[0]||{name: '['+ t.project_phase+']'}).name;
}
html += _joe.renderFieldListItem(t,
'<joe-subtext>'+phasename+' ${due_date}</joe-subtext>'+
'<joe-subtitle>${name}</joe-subtitle>'
+'<joe-subtext>${info}</joe-subtext>',
'task',
{bgcolor:function(task){
if(task.status){
var status = _joe.Cache.get(task.status)||false;
if(status){
return {color:status.color,title:status.name}
}
return false;
}
return false;
},stripecolor:function(item){
if(item.priority && item.priority < 100){
return {
title:`P${item.priority}`,
color:_joe.Colors.priority[item.priority]
};
}
},
});
});
return html;
}},
includes:{type:'objectReference',values:'include',icon:'include',
template:'<joe-title>${name} (.${filetype})</joe-title><joe-subtext>${info}</joe-subtext><joe-subtext>${_id}</joe-subtext>',
autocomplete_template:'<joe-title>${name} (.${filetype})</joe-title><joe-subtext>${info}</joe-subtext>'
},
/* file_upload:{type:'uploader',allowmultiple:true, height:'300px',comment:'drag files here to upload', onConfirm:_joe.SERVER.Plugins.awsFileUpload,use_legacy:true},*/
datasets:{type:'group',cols:2,
comment:'which itemtypes(schemas) does this pertain to',values:function(){
if(typeof(__collectionNames) != undefined){
var opts = [],name;
__collectionNames.map(function(collName){
let schemaObj = _joe.schemas[collName] || {name:collName};
name = (schemaObj && schemaObj.menuicon)?
'<joe-icon class="fleft icon-30 icon-grey" style="padding-bottom:5px; padding-right:5px;">'
+schemaObj.menuicon+'</joe-icon>'+(schemaObj.display||schemaObj.name)
:'<joe-icon class="fleft icon-30 icon-grey" style="padding-bottom:5px; padding-right:5px;">'+'</joe-icon>'+(schemaObj.display||schemaObj.name);
opts.push({name:name,_id:collName});
})
return opts;
}
return ['none'];
}
},
dataset:{type:'select',blank:true,values:function(){
return (typeof(__collectionNames) != undefined && __collectionNames) || ['none'];
}},
notes:{type:'objectReference',values:'note',schema:'note',icon:'note',
template:'<joe-title>${name}</joe-title><joe-subtitle>${info}</joe-subtitle>',
callback:function(values){
logit(values);
}},
timezone_offset:{
format:function(i,v){
var item = _jco(true);
var event_date =(item.date)? new Date(item.date) : new Date();
var tzo = event_date.getTimezoneOffset()/-60;
var absNum = Math.abs(tzo);
var nnum = ('0'+absNum).substr(-2);
f = ((tzo < 0)?'-':'+')+nnum+':00';
if(item.timezone_offset && f!=item.timezone_offset){alert('timezone offset will change from '+item.timezone_offset+' to '+f);}
return f;
},
value:function(item){
if(item.timezone_offset){return item.timezone_offset;}
var event_date =(item.date)? new Date(item.date) : new Date();
var tzo = event_date.getTimezoneOffset()/-60;
var absNum = Math.abs(tzo);
var nnum = ('0'+absNum).substr(-2);
f = ((tzo < 0)?'-':'+')+nnum+':00';
/*if(item.timezone_offset && f!=item.timezone_offset){alert('timezone offset will change from '+item.timezone_offset+' to '+f);}*/
return f;
},
comment:function(item){
var event_date =(item.date)? new Date(item.date) : new Date();
var tzo = event_date.getTimezoneOffset()/-60;
return 'offset on event date is <b>'+tzo+'</b>';
}
},//end timezone_offset
blocks:{type:'objectList',
reinherit:function(){
var item = _jco(true);
var blocks = item.blocks.slice(0);
if(item.layout){
if(item.syncLayoutBlocks){
var lblocks = (_joe.getDataItem(item.layout,'layout')||{blocks:[]}).blocks.slice(0);
//remove layout blocks
blocks = blocks.filter(function(bl){
return bl.src != 'layout';
})
blocks = blocks.concat(lblocks);
}
}
_joe.Fields.rerender('blocks',{'blocks':blocks});
},
sectsToArray:function(sect_item,raw){
console.log('itemtype',sect_item.itemtype);
var sects = _joe.schemas.layout.methods.getSections(sect_item.content || sect_item.template);
sect_item.blocks.map(function(block){
sects.push(block.section);
})
if(raw){
return sects;
}
sects = sects.condense(true).sort();
return sects;
},
comment:function(item){
var btns=['<joe-button class="joe-button fleft" style="margin-bottom:5px;" onclick="_joe.Fields.rerender(\'blocks\');">update sections</joe-button>']
if(item.itemtype == "page"){
btns.push('<joe-button class="joe-button fright" style="margin-bottom:5px;" onclick="_joe.getField(\'blocks\').reinherit();" title="reinherit blocks from layout or site">reinherit</joe-button>');
}
return btns.join(' ');},
value:function(item){
if(item.layout){
var blocks = (_joe.getDataItem(item.layout,'layout')||{blocks:[]}).blocks.slice(0);
//blocks.map(function(block){
// block.layout = true;
//})
return blocks;
}
_joe.Fields.rerender('blocks',{'blocks':_joe.getField('blocks').reinherit()});
return [];
},
properties:[
{name:'block',type:'select',values:function(){
return _joe.Data.block.where({is_template:{$in:[false,'',null]}}).sortBy('name');
},idprop:'_id'},
{name:'section',type:'select',values:function(item){
var new_item = _jco(true);
var sections = [];
//get layout sections (if layout)
if(new_item.layout){//something that uses layout only
var layout = _joe.getDataItem(new_item.layout,'layout');
sections = sections.concat(_joe.getField('blocks').sectsToArray(layout));
}
var con_str = new_item.template || new_item.content;
if(con_str){//page or layout
sections = sections.concat(_joe.getField('blocks').sectsToArray(new_item));
}
sections = [''].concat(sections);
return sections;
}},
{name:'src',width:'15%',type:'select',locked:true,
values:['page','layout','site'],
value:function(item){
if(_jco().itemtype =="layout"){return 'layout'}
}}
]
},//end blocks
_protected:{
type:'boolean',
display:'protect item',
icon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="-6 -4 36 36"><path d="M12 1C8.7 1 6 3.7 6 7L6 8C4.9 8 4 8.9 4 10L4 20C4 21.1 4.9 22 6 22L18 22C19.1 22 20 21.1 20 20L20 10C20 8.9 19.1 8 18 8L18 7C18 3.7 15.3 1 12 1ZM12 3C14.3 3 16 4.7 16 7L16 8 8 8 8 7C8 4.7 9.7 3 12 3ZM12 13C13.1 13 14 13.9 14 15 14 16.1 13.1 17 12 17 10.9 17 10 16.1 10 15 10 13.9 10.9 13 12 13Z"/></svg>',
label:'users must be logged in to view this item and reports'
},
favorite:{
type:'boolean',
byUser:true,
display:'favorite',
icon:'<?xml version="1.0" encoding="UTF-8"?><svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M0 0h24v24H0z" fill="none"/><path d="M4 2h16a1 1 0 0 1 1 1v19.276a.5.5 0 0 1-.704.457L12 19.03l-8.296 3.702A.5.5 0 0 1 3 22.276V3a1 1 0 0 1 1-1zm8 11.5l2.939 1.545-.561-3.272 2.377-2.318-3.286-.478L12 6l-1.47 2.977-3.285.478 2.377 2.318-.56 3.272L12 13.5z"/></svg>',
label:'one of your favorites',
hidelabel:true
},
// favorite:{
// type:'userfavorite',
// display:'favorite',
// icon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="-6 -4 36 36"><path d="M12 1C8.7 1 6 3.7 6 7L6 8C4.9 8 4 8.9 4 10L4 20C4 21.1 4.9 22 6 22L18 22C19.1 22 20 21.1 20 20L20 10C20 8.9 19.1 8 18 8L18 7C18 3.7 15.3 1 12 1ZM12 3C14.3 3 16 4.7 16 7L16 8 8 8 8 7C8 4.7 9.7 3 12 3ZM12 13C13.1 13 14 13.9 14 15 14 16.1 13.1 17 12 17 10.9 17 10 16.1 10 15 10 13.9 10.9 13 12 13Z"/></svg>',
// label:'items that have been favorited'
// },
// favorite:{
// type:'objectReference',values:'user',icon:'user',
// after:function(item){
// var action = `_joe.getField('members').methods.addUser('${_joe.User._id}');`;
// var template = `<joe-button class="joe-button joe-svg-button joe-orangegrey-button joe-svg-button" onclick="${action}">
// favorite this
// </joe-button>`;
// return template;
// },
// disabled:true,
// template:'<joe-subtext>${name}</joe-subtext><joe-title>${fullname}</joe-title><joe-subtext>${info}</joe-subtext>',
// autocomplete_template:'<joe-title>${name}</joe-title><joe-subtitle>${fullname}</joe-subtitle>',
// reference_specs:{stripecolor:'${color}'},
// methods:{
// toggleFavorite:function(userid){
// },
// addUser:function(userid,goto){
// var current = _jco(true);
// if(current.favorite.indexOf(userid) == -1){
// current.favorite.push(userid);
// _joe.Fields.rerender('members',{members:current.members});
// }
// }
// }
// },
references:{
display:'related joe items',
type:'objectReference',
values:function(list){
var haystack = [];
for(var d in _joe.Data){
haystack = haystack.concat(_joe.Data[d]);
}
return haystack;
}
},
reference:{
display:'Instance of',
type:'objectReference',
values:function(list){
var haystack = [];
for(var d in _joe.Data){
haystack = haystack.concat(_joe.Data[d]);
}
return haystack;
}
},
referencedBy:{
type:'content',
/*display:function(item){
var items = _joe.getData.where({referneces:{$in:[item._id]}});
return ('<joe-title>'+items.length
+(items.length == 1 && ' item references'||' items reference')
+' this item</joe-title>');
},*/
run:function(item){
var items = _joe.getData().filter(i=>{
if(!i.references || !i.references.length || i.references.indexOf(item._id) == -1){
return false;
}
return true;
})
var html = '';
items.map(function(item){
let schema = item.itemtype && _joe.schemas[item.itemtype];
let temp = `${(schema && schema.menuicon) || ''}
<joe-subtitle>${item.name}</joe-subtitle>
<joe-subtext>${item.info||item.date||''}</joe-subtext>`
html += _joe.renderFieldListItem(item,temp,item.itemtype);
});
return html;
}},
//AI Fields
ai_model:{
type: "select",
display: "Ai Model",
values: [
{ value:"gpt-5.1", name: "GPT-5.1 (Strong, 128k)" },
{ value:"gpt-5", name: "GPT-5 (Strong, 128K)" },
{ value:"gpt-5-mini", name: "GPT-5-mini (Cheap, 1M)" },
{ value:"gpt-5-nano", name: "GPT-5-nano (Fastest, light tasks)" },
{ value: "gpt-4o", name: "GPT-4o (Fast, 128k)" },
{ value: "gpt-4.1", name: "GPT-4.1 (Strong, 1M)" },
{ value: "gpt-4.1-mini", name: "4.1-mini (Cheap, 1M)" },
{ value: "gpt-4.1-nano", name: "4.1-nano (Fastest, light tasks)" }
],
tooltip:`Ai Model Guide -
GPT-4o is the default for fast, responsive tasks and supports up to 128k tokens. It’s ideal for short completions, summaries, and dynamic UI tools.
GPT-4.1 and 4.1-mini support a massive 1 million token context, making them perfect for large inputs like full business profiles, long strategy texts, and multi-object analysis.
4.1-mini is significantly cheaper than full 4.1, with great balance for most structured AI workflows.
4.1-nano is best for lightweight classification or routing logic where speed and cost matter more than depth.`,
default: "gpt-4o",
},
objectChat:{
type:'button',
display:'Start Chat',
icon:'ai_assistant',
onclick:function(object){
if (!object || !object._id) return '';
return `_joe.Ai.spawnChatHelper('${object._id}');`;
},
},
listConversations:{display:'Ai Conversations', type:"content",reloadable:true,run:function(obj){
return _joe.schemas.ai_conversation.methods.listConversations(obj,true);
}},
ai_responses:{
display:'AI Responses',
type:'content',
reloadable:true,
run:function(obj){
return _joe.schemas.ai_response.methods.listResponses(obj);
}
},
proposeThought:{
display:'Propose Thought',
type:'content',
reloadable:true,
run:function(obj){
if (!obj || !obj._id) {
return '<joe-text>Save this item before proposing Thoughts.</joe-text>';
}
var schema = _joe.current && _joe.current.schema || null;
var itemtype = (obj && obj.itemtype) || (schema && schema.name) || 'item';
// Allow schemas to override the default prompt via extend:'proposeThought',specs:{prompt:'...'}
var fieldDef = null;
if (_joe && typeof _joe.getField === 'function') {
try { fieldDef = _joe.getField('proposeThought'); } catch(_e) {}
}
var overridePrompt = fieldDef && fieldDef.prompt;
var defaultPrompt = overridePrompt || (
'Propose 1–2 concise hypotheses or links about this ' + itemtype +
' and its related objects (risks, dependencies, or next steps). ' +
'Base them only on the object fields and related records. ' +
'Avoid meta-thoughts about prompts or schemas.'
);
var taId = 'propose_thought_prompt_' + obj._id;
var html = '';
html += '<joe-text>Thought prompt</joe-text>';
html += '<textarea id="'+taId+'" style="width:100%;min-height:80px;">'+defaultPrompt+'</textarea>';
// For now, use the generic Thought agent; scope_id is the current object id.
html += "<joe-button class=\"joe-button joe-blue-button\" ";
html += "onclick=\"_joe.Ai.runProposeThought('"+obj._id+"','"+taId+"')\">Run Thought Agent</joe-button>";
return html;
}
},
};
module.exports = fields;