mirror of https://github.com/rancher/api-ui.git
1806 lines
42 KiB
JavaScript
1806 lines
42 KiB
JavaScript
"use strict";
|
|
|
|
//data, schemasUrl, docs, user, curlUser, cb
|
|
|
|
function HTMLApi(opt, cb)
|
|
{
|
|
var self = this;
|
|
this._schemas = null;
|
|
this._schemaDocs = null;
|
|
this._data = opt.data;
|
|
this._docsPage = opt.docsPage;
|
|
this._docsJson = opt.docsJson;
|
|
this._user = opt.user;
|
|
this._curlUser = opt.curlUser || '${GDAPI_ACCESS_KEY}:${GDAPI_SECRET_KEY}';
|
|
this._logout = opt.logout !== false;
|
|
|
|
this._filterId = 0;
|
|
this._reqModal = null;
|
|
this._editSchema = null;
|
|
this._editData = null;
|
|
this._lastMethod = null;
|
|
this._lastMode = null;
|
|
this._lastType = null;
|
|
this._lastOpt = null;
|
|
this._lastRequestBody = null;
|
|
this._error = null;
|
|
|
|
this._referenceDropdownLimit = 100;
|
|
this._magicNull = "__-*NULL*-__";
|
|
this._magicNullRegex= new RegExp(this._escapeRegex(this._magicNull)+'$');
|
|
|
|
this._formatter = new JSONFormatter({
|
|
baseUrl: window.location.protocol +"//" + window.location.host,
|
|
keyFormatter: this.keyFormatter.bind(this),
|
|
valueFormatter: this.valueFormatter.bind(this)
|
|
});
|
|
|
|
async.auto({
|
|
title: this.titleUpdate.bind(this),
|
|
rawSchema: this.schemasLoad.bind(this, opt.schemasUrl),
|
|
schema: ['rawSchema', this.schemasMunge.bind(this) ],
|
|
docs: ['schema', this.docsLoad.bind(this, opt.docsJson) ],
|
|
}, initDone);
|
|
|
|
function initDone(err, results)
|
|
{
|
|
self._error = err;
|
|
cb();
|
|
}
|
|
}
|
|
|
|
HTMLApi.prototype.show = function(cb)
|
|
{
|
|
var self = this;
|
|
async.auto({
|
|
render: this.render.bind(this) ,
|
|
filters: ['render', this.filterInit.bind(this) ],
|
|
}, showDone);
|
|
|
|
function showDone(err, results)
|
|
{
|
|
if ( err )
|
|
{
|
|
}
|
|
|
|
if ( self._error )
|
|
{
|
|
$('#header-body').css('display','none');
|
|
$('#header-error').css('display','');
|
|
}
|
|
else
|
|
{
|
|
$('#header-body').css('visibility','visible');
|
|
}
|
|
|
|
if ( cb )
|
|
cb();
|
|
}
|
|
}
|
|
|
|
HTMLApi.prototype.showModal = function(body,opt,cb)
|
|
{
|
|
var self = this;
|
|
|
|
if ( !this.onKeys )
|
|
{
|
|
this.onKeys = function(e) {
|
|
if ( e.keyCode == 13 )
|
|
{
|
|
// Find the first primary button and click it
|
|
var actions = self._reqModal._actions;
|
|
for (var i = 0 ; i < actions.length ; i++ )
|
|
{
|
|
if ( actions[i].primary )
|
|
self.modalAction(actions[i].id);
|
|
}
|
|
}
|
|
else if ( e.keyCode == 27 )
|
|
{
|
|
var actions = self._reqModal._actions;
|
|
for (var i = 0 ; i < actions.length ; i++ )
|
|
{
|
|
if ( actions[i].cancel )
|
|
self.modalAction(actions[i].id);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}.bind(this);
|
|
}
|
|
|
|
|
|
this.hideModal();
|
|
|
|
if ( !body )
|
|
{
|
|
body = '<div class="loading"></div>';
|
|
}
|
|
|
|
opt.body = body;
|
|
|
|
var modalHtml = Handlebars.templates['modal'](opt);
|
|
var modal = $(modalHtml);
|
|
this._reqModal = modal;
|
|
$('.modal-dialog',modal).css('width',opt.width||'750px');
|
|
this.setModalActions(opt.actions);
|
|
modal.bind('keydown', this.onKeys);
|
|
modal.modal({backdrop: 'static', keyboard: false});
|
|
|
|
if ( cb )
|
|
{
|
|
modal.on('shown.bs.modal', function() { cb(modal); });
|
|
}
|
|
|
|
}
|
|
|
|
HTMLApi.prototype.replaceModal = function(html)
|
|
{
|
|
$('.modal-body', this._reqModal).html(html);
|
|
}
|
|
|
|
HTMLApi.prototype.modalAction = function(id) {
|
|
var action;
|
|
this._reqModal._actions.forEach(function(candidate) {
|
|
if ( candidate.id == id )
|
|
action = candidate;
|
|
});
|
|
|
|
if ( action && action.onClick)
|
|
{
|
|
action.onClick();
|
|
}
|
|
else if ( action && action.cancel )
|
|
{
|
|
this.hideModal();
|
|
}
|
|
}
|
|
|
|
HTMLApi.prototype.hideModal = function() {
|
|
var self = this;
|
|
var old = self._reqModal;
|
|
self._reqModal = null;
|
|
|
|
if ( !old )
|
|
return;
|
|
|
|
old.unbind('keydown', self.onKeys);
|
|
old.modal('hide');
|
|
old.on('hidden.bs.modal', function() {
|
|
old.remove();
|
|
});
|
|
}
|
|
|
|
HTMLApi.prototype.setModalActions = function(actions)
|
|
{
|
|
actions = actions || [];
|
|
this._reqModal._actions = actions;
|
|
var html = '';
|
|
|
|
actions.forEach(function(action) {
|
|
color = 'btn-default';
|
|
if ( action.primary )
|
|
color = 'btn-primary';
|
|
else if ( action.cancel )
|
|
color = 'btn-link';
|
|
|
|
html += '<button type="button" class="btn '+color+'" onclick="htmlapi.modalAction(\''+ action.id +'\');">'+ action.text + '</button>';
|
|
});
|
|
|
|
$('.modal-footer', this._reqModal).html(html);
|
|
}
|
|
|
|
HTMLApi.prototype.titleUpdate = function(cb)
|
|
{
|
|
var title = "API";
|
|
if ( this._data )
|
|
title += ": " + (this._data.displayName || this._data.name || this._data.id || this._data.resourceType || this._data.type);
|
|
|
|
document.title = title;
|
|
|
|
if ( cb )
|
|
async.nextTick(cb);
|
|
}
|
|
|
|
HTMLApi.prototype.schemasLoad = function(link, cb)
|
|
{
|
|
if ( !this._data )
|
|
return async.nextTick(function() { cb("No data") });
|
|
|
|
// Link may come from the page <script>, but override it if one is in the JSON body.
|
|
if ( this._data.links && this._data.links.schemas )
|
|
link = this._data.links.schemas;
|
|
|
|
if ( link )
|
|
{
|
|
this.ajax('GET', link, function(err,res) {
|
|
if ( err )
|
|
cb("Error loading schema from [" + link + "]: " + err);
|
|
else
|
|
cb(null,res);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
return async.nextTick(function() { cb("No schemas link") });
|
|
}
|
|
}
|
|
|
|
|
|
HTMLApi.prototype.schemasMunge = function(cb, results)
|
|
{
|
|
var schemas = results.rawSchema;
|
|
|
|
if ( !schemas || !schemas.data )
|
|
return async.nextTick(function() { cb("No schema data") });
|
|
|
|
var out = {};
|
|
var i, schema;
|
|
for ( i = 0 ; i < schemas.data.length ; i++ )
|
|
{
|
|
schema = schemas.data[i];
|
|
out[schema.id] = this._schemaMunge(schema);
|
|
}
|
|
|
|
this._schemas = out;
|
|
return async.nextTick(function() { cb(undefined,out); });
|
|
}
|
|
|
|
HTMLApi.prototype._schemaMunge = function(schema)
|
|
{
|
|
// Split complex types like reference[something] into base and sub type
|
|
if ( schema.resourceFields )
|
|
{
|
|
for ( var k in schema.resourceFields )
|
|
{
|
|
this._fieldMunge(schema.resourceFields[k]);
|
|
}
|
|
}
|
|
|
|
// Sort filter modifiers
|
|
var filter;
|
|
var order = {'': 1, 'eq':2, 'ne': 3, 'lte':4, 'lt':5, 'gt':6, 'gte':7};
|
|
for ( var k in schema.collectionFilters )
|
|
{
|
|
filter = schema.collectionFilters[k];
|
|
|
|
if ( filter && filter.modifiers)
|
|
{
|
|
filter.modifiers.sort(function(a,b) {
|
|
var ia = order[a];
|
|
if ( !ia )
|
|
return 1;
|
|
|
|
var ib = order[b];
|
|
if ( !ib )
|
|
return -1;
|
|
|
|
return ia-ib;
|
|
});
|
|
}
|
|
}
|
|
|
|
return schema;
|
|
}
|
|
|
|
HTMLApi.prototype._fieldMunge = function(field)
|
|
{
|
|
field._typeList = field.type.replace(/\]+$/,'').split(/\[/);
|
|
return field;
|
|
}
|
|
|
|
HTMLApi.prototype.docsLoad = function(link, cb, results)
|
|
{
|
|
var schemas = results.schema;
|
|
|
|
if ( link )
|
|
{
|
|
this.ajax('GET', link, function(err,res) {
|
|
if ( err )
|
|
{
|
|
//cb("Error loading docs from [" + link + "]: " + err);
|
|
cb();
|
|
return;
|
|
}
|
|
|
|
res.data.forEach(function(doc) {
|
|
var schema = schemas[doc.id];
|
|
var field;
|
|
|
|
if ( !schema )
|
|
return;
|
|
|
|
if ( doc.description )
|
|
schema.description = doc.description;
|
|
|
|
if ( !doc.resourceFields )
|
|
return;
|
|
|
|
var keys = Object.keys(doc.resourceFields);
|
|
var key, field;
|
|
for (var i = 0 ; i < keys.length ; i++ )
|
|
{
|
|
key = keys[i];
|
|
field = doc.resourceFields[key];
|
|
|
|
if ( !field || !field.description || !schema.resourceFields[key] )
|
|
continue;
|
|
|
|
schema.resourceFields[key].description = field.description;
|
|
schema.resourceFields[key].placeholder = field.placeholder;
|
|
}
|
|
|
|
});
|
|
|
|
cb(null,res);
|
|
});
|
|
}
|
|
else
|
|
{
|
|
return async.nextTick(function() { cb() });
|
|
}
|
|
}
|
|
|
|
HTMLApi.prototype.render = function(cb)
|
|
{
|
|
var data = this._data;
|
|
var jsonHtml = this._formatter.valueToHTML(data)
|
|
var schema = null;
|
|
if ( data.resourceType )
|
|
schema = this.getSchema(data.resourceType);
|
|
else if ( data.type )
|
|
schema = this.getSchema(data.type);
|
|
|
|
var operations = {
|
|
up: true,
|
|
reload: true,
|
|
};
|
|
|
|
if ( data.type == 'collection' && (data.resourceType||'').toLowerCase() == 'apiversion' )
|
|
operations.up = false;
|
|
|
|
if ( schema )
|
|
{
|
|
var methods = ( data.type == 'collection' ? schema.collectionMethods : schema.resourceMethods ) || [];
|
|
var methodMap = {};
|
|
methods.forEach(function(method) {
|
|
operations[ method.toLowerCase() ] = true;
|
|
});
|
|
|
|
if ( data.createTypes && Object.keys(data.createTypes).length )
|
|
{
|
|
operations.post = true;
|
|
}
|
|
}
|
|
|
|
var actions = {};
|
|
var allActions = {};
|
|
if ( schema )
|
|
allActions = ( data.type == 'collection' ? schema.collectionActions : schema.resourceActions ) || {};
|
|
|
|
Object.keys(allActions).sort().forEach(function(key) {
|
|
// Set the action to true if it's available on this object or false if it isn't
|
|
actions[key] = !!data.actions[key];
|
|
});
|
|
|
|
var tpl = {
|
|
data: this._data,
|
|
docsPage: this._docsPage,
|
|
user: this._user,
|
|
logout: this._logout,
|
|
error: this._error,
|
|
schema: schema,
|
|
operations: operations,
|
|
actions: actions,
|
|
explorer: Cookie.get('debug') || false
|
|
};
|
|
|
|
document.body.innerHTML = Handlebars.templates['body'](tpl);
|
|
$('#json').html(jsonHtml);
|
|
|
|
this._addCollapsers();
|
|
|
|
$('#filters').html('<span class="inactive">Not available</span>');
|
|
|
|
return async.nextTick(cb);
|
|
}
|
|
|
|
HTMLApi.prototype._addCollapsers = function()
|
|
{
|
|
var items = $('UL.collapsible');
|
|
for( var i = 0; i < items.length; i++)
|
|
{
|
|
this._addCollapser($(items[i]).parent()[0]);
|
|
}
|
|
}
|
|
|
|
HTMLApi.prototype._addCollapser = function(item)
|
|
{
|
|
// This mainly filters out the root object (which shouldn't be collapsible)
|
|
if ( item.nodeName != 'LI' )
|
|
return;
|
|
|
|
var collapser = $('<i/>', {
|
|
"class": "glyphicon glyphicon-minus",
|
|
click: JSONFormatter.prototype.collapse
|
|
});
|
|
|
|
collapser.insertBefore(item.firstChild);
|
|
}
|
|
|
|
HTMLApi.prototype.getSchema = function(type, obj)
|
|
{
|
|
if ( !obj )
|
|
obj = this._data;
|
|
|
|
// support this.getSchema() for the top-level resource
|
|
if ( !type && obj )
|
|
{
|
|
if ( obj.type == 'collection' )
|
|
type = obj.resourceType;
|
|
else
|
|
type = obj.type;
|
|
}
|
|
|
|
if ( type && this._schemas && this._schemas[type] )
|
|
return this._schemas[type];
|
|
|
|
return null;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
HTMLApi.prototype.showAction = function(button)
|
|
{
|
|
this.actionLoad(button.getAttribute('data-action'), undefined, {});
|
|
}
|
|
|
|
HTMLApi.prototype.actionLoad = function(name, obj, body)
|
|
{
|
|
var self = this;
|
|
|
|
this._lastRequestBody = body||this._lastRequestBody||{};
|
|
|
|
if ( !obj )
|
|
obj = this._data;
|
|
|
|
var isCollection = (obj.type == 'collection');
|
|
|
|
// The schema for the type of object we have
|
|
var objSchema = this.getSchema(null,obj);
|
|
|
|
// The description of the input and output for this action
|
|
var actionSchema = (isCollection ? objSchema.collectionActions[name] : objSchema.resourceActions[name]);
|
|
|
|
|
|
// The schema for the input
|
|
var actionInput = {};
|
|
if ( actionSchema.input )
|
|
actionInput = this.getSchema(actionSchema.input);
|
|
|
|
this._editSchema = actionInput;
|
|
var url = obj.actions[name];
|
|
var title = 'Action: ' + name;
|
|
|
|
self.showModal(null, {title: title}, shown);
|
|
|
|
function shown(modal)
|
|
{
|
|
self.loadReferenceOptions(actionInput, ready);
|
|
|
|
function ready()
|
|
{
|
|
var rows = [];
|
|
|
|
var tpl = {};
|
|
var mode = 'action';
|
|
tpl.fields = self._flattenFields(mode, actionInput, self._lastRequestBody);
|
|
tpl.hasFields = tpl.fields.length > 0;
|
|
tpl.mode = mode;
|
|
tpl.createTypes = false;
|
|
|
|
var retry = function()
|
|
{
|
|
self.actionLoad(name, obj);
|
|
}
|
|
|
|
var html = Handlebars.templates['edit'](tpl);
|
|
var popinActions = [
|
|
{id: 'ok', text: 'Show Request', primary: true, onClick: function() { self.showRequest(mode,'POST',actionInput,retry,url); }.bind(self) },
|
|
{id: 'cancel', text: 'Cancel', cancel: true }
|
|
];
|
|
|
|
|
|
self.replaceModal(html);
|
|
self.setModalActions(popinActions);
|
|
self.editOrActionShown();
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
HTMLApi.prototype.sortChange = function(elem)
|
|
{
|
|
var name = $(elem).val();
|
|
|
|
var links = this._data.sortLinks;
|
|
if ( links && links[name] )
|
|
window.location.href = links[name];
|
|
}
|
|
|
|
HTMLApi.prototype.sortOrderChange = function()
|
|
{
|
|
if ( this._data.sort && this._data.sort.reverse )
|
|
window.location.href = this._data.sort.reverse;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
HTMLApi.prototype.setLimit = function(limit)
|
|
{
|
|
var url = URLParse.updateQuery(window.location.href, {limit: limit});
|
|
window.location.href = url;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
HTMLApi.prototype.filterInit = function(cb)
|
|
{
|
|
var name, list, i, v, modifier, pos;
|
|
var schema = this.getSchema();
|
|
|
|
if ( this._data.type != 'collection' || !schema || !schema.collectionFilters )
|
|
return async.nextTick(cb);
|
|
|
|
var filters = [];
|
|
var canFilter = false;
|
|
this._filterId = 0;
|
|
if ( this._data.filters )
|
|
{
|
|
for ( name in this._data.filters )
|
|
{
|
|
if ( schema.collectionFilters[name] )
|
|
canFilter = true;
|
|
|
|
list = this._data.filters[name];
|
|
|
|
if ( !list )
|
|
continue;
|
|
|
|
for ( i = 0 ; i < list.length ; i++ )
|
|
{
|
|
v = list[i];
|
|
filters.push({
|
|
id: this._filterId++,
|
|
name: name,
|
|
modifier: (v.modifier == "eq" ? "" : v.modifier) || "",
|
|
value: v.value
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
var html = Handlebars.templates['filters']({
|
|
canFilter: canFilter,
|
|
hasFilters: (filters.length > 0)
|
|
});
|
|
$('#filters').html(html);
|
|
|
|
var $elem;
|
|
var options;
|
|
for ( var i = 0 ; i < filters.length ; i++ )
|
|
{
|
|
v = filters[i];
|
|
|
|
if ( schema.collectionFilters[v.name] && schema.collectionFilters[v.name].options )
|
|
options = schema.collectionFilters[v.name].options;
|
|
else if ( schema.resourceFields[v.name] && schema.resourceFields[v.name].options )
|
|
options = schema.resourceFields[v.name].options;
|
|
else
|
|
options = null;
|
|
|
|
html = Handlebars.templates['filter']({
|
|
allFilterSchema: schema.collectionFilters,
|
|
thisFilterSchema: schema.collectionFilters[v.name],
|
|
options: options,
|
|
cur: v
|
|
});
|
|
$elem = $(html);
|
|
$('#filter-body').append($elem);
|
|
this.modifierChange($elem);
|
|
}
|
|
|
|
async.nextTick(cb);
|
|
}
|
|
|
|
HTMLApi.prototype.filterAdd = function(name, modifier, value, before)
|
|
{
|
|
var schema = this.getSchema();
|
|
var schemaFilters = schema.collectionFilters;
|
|
|
|
if ( !name )
|
|
{
|
|
// Get the first filter name
|
|
name = Object.keys(schemaFilters)[0];
|
|
}
|
|
|
|
if ( !modifier && schemaFilters[name] && schemaFilters[name]['modifiers'] )
|
|
{
|
|
modifier = schemaFilters[name]['modifiers'][0];
|
|
}
|
|
|
|
if ( !modifier )
|
|
modifier = 'eq';
|
|
|
|
var cur = {
|
|
name: name,
|
|
modifier: modifier,
|
|
value: value || ''
|
|
};
|
|
|
|
var options = null;
|
|
if ( schema.collectionFilters[name] && schema.collectionFilters[name].options )
|
|
options = schema.collectionFilters[name].options;
|
|
else if ( schema.resourceFields[name] && schema.resourceFields[name].options )
|
|
options = schema.resourceFields[name].options;
|
|
else
|
|
options = null;
|
|
|
|
var html = Handlebars.templates['filter']({
|
|
allFilterSchema: schemaFilters,
|
|
thisFilterSchema: schemaFilters[name],
|
|
options: options,
|
|
cur: cur
|
|
});
|
|
|
|
var $elem = $(html);
|
|
if( before )
|
|
$elem = $(before).before($elem);
|
|
else
|
|
$lem = $('#filter-body').append($elem);
|
|
|
|
$('#no-filters').hide();
|
|
this.modifierChange($elem);
|
|
return $elem;
|
|
}
|
|
|
|
HTMLApi.prototype.filterRemove = function(elem)
|
|
{
|
|
var $div = $(elem).parents('.filter');
|
|
$div.remove();
|
|
|
|
var $rows = $('#filter-body DIV');
|
|
$('#no-filters').toggle($rows.length == 0);
|
|
}
|
|
|
|
HTMLApi.prototype.filterModifierChange = function(elem)
|
|
{
|
|
var $elem = $(elem);
|
|
var filter = $elem.closest('.filter');
|
|
var input = filter.find('.filter-modifier-input');
|
|
var label = filter.find('.filter-modifier-label');
|
|
|
|
input.val(elem.getAttribute('data-value'));
|
|
label.html(elem.getAttribute('data-label'));
|
|
this.modifierChange(filter);
|
|
}
|
|
|
|
HTMLApi.prototype.filterChange = function(elem)
|
|
{
|
|
var $elem = $(elem);
|
|
|
|
var name = $elem.val();
|
|
var $row = $elem.parents('.filter');
|
|
var prefix = $row.data('prefix');
|
|
var next = $row.next()[0];
|
|
|
|
var modifier = $('#'+prefix+'_modifier').val();
|
|
var value = $('#'+prefix+'_value').val();
|
|
|
|
this.filterRemove(elem);
|
|
$elem = this.filterAdd(name, modifier, value, next);
|
|
}
|
|
|
|
HTMLApi.prototype.modifierChange = function(inElem)
|
|
{
|
|
var $elem, $row, prefix;
|
|
if ( inElem.tagName == 'select' )
|
|
{
|
|
$elem = $(inElem);
|
|
$row = $elem.parents('.filter');
|
|
prefix = $row.data('prefix');
|
|
}
|
|
else
|
|
{
|
|
$row = $(inElem);
|
|
prefix = $row.data('prefix');
|
|
$elem = $('#'+prefix+'_modifier');
|
|
}
|
|
|
|
var modifier = $elem.val();
|
|
var $input = $('#'+prefix+'_value');
|
|
var on = (modifier != 'null' && modifier != 'notnull');
|
|
|
|
$input.toggle(on);
|
|
}
|
|
|
|
HTMLApi.prototype.filterApply = function(clear)
|
|
{
|
|
var $rows = $('#filters DIV.filter');
|
|
var $row,prefix,name,modifier,value;
|
|
|
|
var query = '';
|
|
|
|
if ( !clear )
|
|
{
|
|
for ( var i = 0 ; i < $rows.length ; i++ )
|
|
{
|
|
$row = $($rows[i]);
|
|
prefix = $row.data('prefix');
|
|
name = $('#'+prefix+'_name').val();
|
|
modifier = $('#'+prefix+'_modifier').val();
|
|
value = $('#'+prefix+'_value').val();
|
|
|
|
// Null/NotNull have no value
|
|
if ( modifier === 'null' || modifier === 'notnull' )
|
|
{
|
|
value = '';
|
|
}
|
|
else if ( !value )
|
|
{
|
|
// Ignore filters with empty values
|
|
continue;
|
|
}
|
|
|
|
// Equals doesn't need an explicit modifier name
|
|
if ( modifier == 'eq' )
|
|
modifier = false;
|
|
|
|
query += (query ? '&' : '?') + escape(name) + (modifier ? '_'+modifier : '') + (value ? '=' + escape(value) : '');
|
|
}
|
|
}
|
|
|
|
window.location.href = window.location.href.replace(/\?.*$/,'') + query;
|
|
}
|
|
|
|
HTMLApi.prototype.filterClear = function()
|
|
{
|
|
this.filterApply(true);
|
|
}
|
|
|
|
// ------------------------------
|
|
|
|
HTMLApi.prototype.keyFormatter = function(key,obj, path)
|
|
{
|
|
var html = this._formatter.jsString(key);
|
|
|
|
path = path||[];
|
|
var parentKey = path[path.length-1] || '';
|
|
|
|
if ( parentKey == 'createTypes' )
|
|
{
|
|
var schema = this.getSchema(key);
|
|
if ( schema )
|
|
{
|
|
html = '<a class="keylink" href="' + schema.links['self'] + '">' + html + '</a>';
|
|
}
|
|
}
|
|
else if ( parentKey == 'actions' )
|
|
{
|
|
var dataVar = 'htmlapi._data';
|
|
for ( var i = 0 ; i < path.length-1 ; i++ )
|
|
{
|
|
dataVar += "['"+ path[i] + "']";
|
|
}
|
|
|
|
html = '<a class="keylink" href="#" onclick="htmlapi.actionLoad(\''+ key + '\',' + dataVar + ',{}); return false;">' + html + '</a>';
|
|
}
|
|
|
|
return html;
|
|
}
|
|
|
|
HTMLApi.prototype.valueFormatter = function(key,obj, path)
|
|
{
|
|
path = (path||[]).slice(0);
|
|
path.push(key);
|
|
|
|
var schema = null;
|
|
if ( obj.resourceType )
|
|
schema = this.getSchema(obj.resourceType);
|
|
else if ( obj.type )
|
|
schema = this.getSchema(obj.type);
|
|
|
|
var html = this._formatter.valueToHTML(obj[key], path);
|
|
|
|
if ( !obj[key] )
|
|
return html;
|
|
|
|
if ( key == 'id' && obj.links && obj.links['self'] )
|
|
{
|
|
html = '<a class="valuelink" href="' + obj.links['self'] + '">' + html + '</a>';
|
|
}
|
|
else if ( schema && schema.resourceFields && schema.resourceFields[key] )
|
|
{
|
|
var field = schema.resourceFields[key];
|
|
if ( field._typeList && field._typeList[0] == 'reference' )
|
|
{
|
|
var subtype = this.getSchema(field._typeList[1]);
|
|
if ( subtype && subtype.links.collection )
|
|
{
|
|
var url = subtype.links.collection.replace(/\/+$/,'') + '/' + escape(obj[key]);
|
|
html = '<a class="valuelink" href="' + url + '">' + html + '</a>';
|
|
}
|
|
}
|
|
}
|
|
else if (schema && (key == 'type' || key == 'resourceType') )
|
|
{
|
|
html = '<a class="valuelink" href="' + schema.links['self'] + '">' + html + '</a>';
|
|
}
|
|
|
|
return html;
|
|
}
|
|
|
|
HTMLApi.prototype.ajax = function(method, url, body, cb)
|
|
{
|
|
method = method || 'GET';
|
|
|
|
if ( typeof body == 'function' )
|
|
{
|
|
cb = body;
|
|
body = null;
|
|
}
|
|
|
|
if ( body && typeof body == 'object' )
|
|
{
|
|
body = JSON.stringify(body);
|
|
}
|
|
|
|
var headers = {
|
|
'Accept' : 'application/json'
|
|
};
|
|
|
|
var csrf = Cookie.get('CSRF');
|
|
if ( method != 'GET' && csrf)
|
|
{
|
|
headers['X-API-CSRF'] = csrf;
|
|
}
|
|
|
|
var res;
|
|
res = jQuery.ajax({
|
|
type: method,
|
|
data: body,
|
|
contentType: 'application/json',
|
|
headers: headers,
|
|
url: url,
|
|
dataType: 'json',
|
|
success: function(data, msg, jqxhr) { cb(null,data, jqxhr); },
|
|
error: function(jqxhr, msg, exception) {
|
|
var body = null;
|
|
try {
|
|
body = jQuery.parseJSON(jqxhr.responseText);
|
|
}
|
|
catch (e) {
|
|
body = jqxhr.responseText;
|
|
}
|
|
|
|
cb(msg, body, jqxhr);
|
|
}
|
|
});
|
|
}
|
|
|
|
// ------------------------------
|
|
|
|
HTMLApi.prototype.up = function()
|
|
{
|
|
window.location.href = window.location.href.replace(/[^\/]+\/?$/,'');
|
|
}
|
|
|
|
HTMLApi.prototype.reload = function()
|
|
{
|
|
window.location.href = window.location.href.replace(/#.*/,'');
|
|
}
|
|
|
|
HTMLApi.prototype.logout = function()
|
|
{
|
|
window.location.href = window.location.href.replace(/\/\//,"//logout@");
|
|
}
|
|
|
|
HTMLApi.prototype.request = function(method,body,opt,really)
|
|
{
|
|
var self = this;
|
|
method = (method || 'GET').toUpperCase();
|
|
opt = opt || {};
|
|
|
|
this._lastOpt = opt;
|
|
this._lastRequestBody = body;
|
|
this._lastMethod = method;
|
|
|
|
var url = opt.url;
|
|
if ( !url && this._data.links )
|
|
url = this._data.links.self;
|
|
|
|
if ( !url )
|
|
{
|
|
alert("I don't know what URL to send a request to, did you specify a 'self' link?");
|
|
return
|
|
}
|
|
|
|
var urlParts = URLParse.parse(url);
|
|
|
|
if ( really )
|
|
{
|
|
this.setModalActions([
|
|
{id: 'cancel', text: 'Cancel', cancel: true}
|
|
]);
|
|
|
|
$('#notsent').hide();
|
|
$('#waiting').show();
|
|
$('#result' ).hide();
|
|
|
|
if ( opt.blobs )
|
|
{
|
|
var form = new FormData();
|
|
var fields = form.getElementsByTagName('INPUT');
|
|
var field;
|
|
|
|
for ( var i = 0 ; i < fields.length ; i++ )
|
|
{
|
|
field = fields[i];
|
|
if ( field.type == 'file' )
|
|
form.append(field.name, field.files[0]);
|
|
else
|
|
form.append(field.name, field.value)
|
|
}
|
|
|
|
this.ajax(method, url, form, function(err, body, jqxhr) { self.requestDone(err,body,jqxhr) });
|
|
}
|
|
else
|
|
{
|
|
this.ajax(method, url, body||'', function(err,body,jqxhr) { self.requestDone(err,body,jqxhr); });
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
var tpl = {
|
|
curl_user: this._curlUser,
|
|
method: method,
|
|
host: urlParts.host,
|
|
path: urlParts.requestUri,
|
|
baseUrl: urlParts.protocol+'//'+urlParts.host,
|
|
};
|
|
|
|
if ( opt.blobs )
|
|
{
|
|
var rawBody = {};
|
|
var keys = Object.keys(body);
|
|
var key;
|
|
for ( var i = 0 ; i < keys.length ; i++ )
|
|
{
|
|
key = keys[i];
|
|
if ( opt.blobs[i] )
|
|
rawBody[key] = { blob: true, value: body[key] };
|
|
else
|
|
rawBody[key] = { blob: false, value: body[key] };
|
|
}
|
|
|
|
var boundary = URLParse.generateHash(16);
|
|
tpl.rawBody = rawBody;
|
|
tpl.boundary = boundary;
|
|
tpl.contentType = 'multipart/form-data; boundary='+ boundary;
|
|
}
|
|
else if ( typeof body == 'object' )
|
|
{
|
|
var json = JSON.stringify(body);
|
|
var formatted = this._formatter.valueToHTML(body);
|
|
tpl.body = json.replace(/,/g,", ");
|
|
tpl.prettyBody = formatted;
|
|
tpl.contentLength = json.length;
|
|
tpl.contentType = 'application/json';
|
|
}
|
|
|
|
var html = Handlebars.templates['request'](tpl);
|
|
|
|
self.showModal(html, {
|
|
destroyOnClose: false,
|
|
title: 'API Request',
|
|
actions: [
|
|
{id: 'ok', text: 'Send Request', primary: true, onClick: function() { self.request(method,body,opt,true); } },
|
|
{id: 'cancel', text: 'Cancel', cancel: true}
|
|
]
|
|
});
|
|
}
|
|
|
|
HTMLApi.prototype.postDone = function()
|
|
{
|
|
var body = $('#post_iframe').contents()[0].body;
|
|
var text = body.innerText || body.textContent;
|
|
var json = JSON.parse(text);
|
|
this.requestDone(undefined,json);
|
|
}
|
|
|
|
HTMLApi.prototype.requestDone = function(err, body, res)
|
|
{
|
|
var tpl = {};
|
|
|
|
if ( err && !body )
|
|
{
|
|
alert('Error: ' + err);
|
|
return;
|
|
}
|
|
|
|
if ( res )
|
|
{
|
|
var headers = [];
|
|
var lines = res.getAllResponseHeaders().trim().split(/\r?\n/);
|
|
var parts;
|
|
for ( var i = 0 ; i < lines.length ; i++ )
|
|
{
|
|
parts = lines[i].splitLimit(':',2);
|
|
headers.push({name: parts[0].trim(), value: parts[1].trim()});
|
|
}
|
|
|
|
tpl.res = res;
|
|
tpl.responseHeaders = headers;
|
|
}
|
|
|
|
var html = Handlebars.templates['response'](tpl);
|
|
|
|
var out = '';
|
|
var selfUrl = false;
|
|
if ( body )
|
|
{
|
|
if ( typeof body == 'object' )
|
|
{
|
|
if ( body.links && body.links.self )
|
|
selfUrl = body.links.self;
|
|
|
|
out = '<div class="json">'+this._formatter.valueToHTML(body)+'</div>';
|
|
}
|
|
else
|
|
{
|
|
out = $('<div/>').text(body.toString()).html();
|
|
}
|
|
}
|
|
|
|
var loc;
|
|
if ( res )
|
|
{
|
|
loc = res.getResponseHeader('Location');
|
|
}
|
|
|
|
var retry = (this._lastOpt.retry && (res.status >= 400));
|
|
|
|
var primary = 'reload';
|
|
var popinActions = [
|
|
{id: 'reload', text: 'Reload', onClick: this.reload.bind(this)},
|
|
{id: 'up', text: 'Go Up', onClick: function() { this.up(); }.bind(this) },
|
|
{id: 'cancel', text: 'Close', cancel: true}
|
|
];
|
|
|
|
if ( loc )
|
|
{
|
|
primary = 'follow';
|
|
popinActions.unshift({id: 'follow', text: 'Follow Location', onClick: function() { window.location.href = loc }});
|
|
}
|
|
else if ( selfUrl )
|
|
{
|
|
primary = 'followSelf';
|
|
popinActions.unshift({id: 'followSelf', text: 'Follow Self Link', onClick: function() { window.location.href = selfUrl }});
|
|
}
|
|
|
|
if ( retry )
|
|
{
|
|
primary = 'edit';
|
|
popinActions.unshift({id: 'edit', text: 'Edit & Retry', onClick: this._lastOpt.retry.bind(this) });
|
|
}
|
|
|
|
// Default to "Go Up" on successful delete
|
|
if ( (this._lastMethod||"").toUpperCase() == 'DELETE' && res.status >= 200 && res.status <= 299)
|
|
{
|
|
primary = 'up';
|
|
}
|
|
|
|
for ( var i = 0 ; i < popinActions.length ; i++ )
|
|
{
|
|
if ( popinActions[i].id == primary )
|
|
{
|
|
popinActions[i].primary = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
this.setModalActions(popinActions);
|
|
$('#notsent').hide();
|
|
$('#waiting').hide();
|
|
$('#result').html(html);
|
|
$('#response-body').html(out);
|
|
$('#result' ).show();
|
|
}
|
|
|
|
HTMLApi.prototype.create = function()
|
|
{
|
|
var self = this;
|
|
var data = {};
|
|
var k, v;
|
|
var schema = this.getSchema();
|
|
|
|
// Apply schema defaults
|
|
for ( k in schema.resourceFields )
|
|
{
|
|
v = schema.resourceFields[k];
|
|
data[k] = (v['nullable'] ? null : '');
|
|
if ( v['default'] )
|
|
{
|
|
data[k] = v['default'];
|
|
}
|
|
}
|
|
|
|
// Apply response defaults
|
|
if ( this._data.createDefaults )
|
|
{
|
|
for ( k in this._data.createDefaults )
|
|
{
|
|
data[k] = this._data.createDefaults[k];
|
|
}
|
|
}
|
|
|
|
this._lastMode = 'create';
|
|
if ( this._data.createTypes )
|
|
{
|
|
// Make sure the selected type exists in the createTypes list
|
|
var type = data.type;
|
|
if ( !type || !this._data.createTypes[type] )
|
|
type = Object.keys(this._data.createTypes)[0];
|
|
|
|
this._lastType = type;
|
|
this._lastRequestBody = data;
|
|
this.createTypeChanged(type,true);
|
|
}
|
|
else
|
|
{
|
|
this.showEdit(data, false, schema);
|
|
}
|
|
}
|
|
|
|
HTMLApi.prototype.loadReferenceOptions = function(schema,doneCb)
|
|
{
|
|
var self = this;
|
|
|
|
function getReferences(task, cb)
|
|
{
|
|
function gotReferences(err,res)
|
|
{
|
|
if ( res.pagination && res.pagination.partial )
|
|
{
|
|
// Too many...
|
|
}
|
|
else
|
|
{
|
|
var opt = {};
|
|
var i, obj, label;
|
|
for ( i = 0 ; i < res.data.length ; i++ )
|
|
{
|
|
obj = res.data[i];
|
|
if ( obj.displayName )
|
|
label = obj.displayName + ' (' + obj.id + ')';
|
|
else if ( obj.name )
|
|
label = obj.name + ' (' + obj.id + ')';
|
|
else
|
|
label = obj.id;
|
|
|
|
opt[ obj.id ] = label;
|
|
}
|
|
|
|
schema.resourceFields[task.field].options = opt;
|
|
}
|
|
|
|
cb();
|
|
}
|
|
|
|
self.ajax('GET', URLParse.updateQuery(task.url,{limit: self._referenceDropdownLimit}), gotReferences);
|
|
}
|
|
|
|
var q = async.queue(getReferences, 1);
|
|
q.drain = doneCb;
|
|
|
|
var k, field, idxj;
|
|
for ( k in schema.resourceFields )
|
|
{
|
|
field = schema.resourceFields[k];
|
|
idx = field._typeList.indexOf('reference');
|
|
if ( idx >= 0 )
|
|
{
|
|
if ( field.referenceCollection )
|
|
{
|
|
// Explicit collection URL to load
|
|
q.push({field: k, url: field.referenceCollection});
|
|
}
|
|
else if ( field._typeList[idx+1] )
|
|
{
|
|
// Look for a collection URL in the schema for the referenced type
|
|
var referenceSchema = this.getSchema(field._typeList[idx+1]);
|
|
if ( referenceSchema && referenceSchema.links.collection)
|
|
{
|
|
q.push({field: k, url: referenceSchema.links.collection});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( q.length() == 0 )
|
|
q.drain();
|
|
}
|
|
|
|
|
|
HTMLApi.prototype.showEdit = function(data,update,schema,url)
|
|
{
|
|
var self = this;
|
|
if ( !schema )
|
|
{
|
|
return;
|
|
}
|
|
|
|
var mode = (update ? 'update' : 'create');
|
|
|
|
this.loadReferenceOptions(schema, display);
|
|
this._editSchema = schema;
|
|
this._editData = data;
|
|
|
|
function display()
|
|
{
|
|
var rows = [];
|
|
|
|
var tpl = {};
|
|
tpl.description = schema.description;
|
|
tpl.fields = self._flattenFields(mode, schema, data);
|
|
tpl.hasFields = tpl.fields.length > 0;
|
|
tpl.mode = mode;
|
|
tpl.createTypes = self._data.createTypes || false;
|
|
|
|
if ( self._data.createTypes && Object.keys(self._data.createTypes).length > 1 )
|
|
{
|
|
var typeField = {
|
|
required: true,
|
|
create: true,
|
|
type: '__type__',
|
|
_typeList: ['__type__'],
|
|
options: Object.keys(self._data.createTypes)
|
|
};
|
|
|
|
tpl.typeField = self._flattenField('create', 'type', typeField, self._lastType, 0);
|
|
}
|
|
|
|
var retry = function()
|
|
{
|
|
self.showEdit(self._lastRequestBody||data, update, schema, url);
|
|
}
|
|
|
|
var title = (update ? 'Edit' : 'Create') +' '+ schema.id;
|
|
var html = Handlebars.templates['edit'](tpl);
|
|
var method = (update ? 'PUT' : 'POST');
|
|
var popinActions = [
|
|
{id: 'ok', text: 'Show Request', primary: true, onClick: function() { self.showRequest(mode, method,schema,retry,url); }.bind(self) },
|
|
{id: 'cancel', text: 'Cancel', cancel: true }
|
|
];
|
|
|
|
self.showModal(html, {
|
|
title: title,
|
|
actions: popinActions
|
|
}, self.editOrActionShown.bind(self));
|
|
}
|
|
}
|
|
|
|
HTMLApi.prototype.editOrActionShown = function() {
|
|
var self = this;
|
|
|
|
// Focus the first regular input
|
|
var input = $(":input:not(input[type=button],input[type=submit],button):visible:first", htmlapi._reqModal);
|
|
if ( input )
|
|
input.focus();
|
|
|
|
// Make the null checkboxes clear the field, and field clear null
|
|
$(htmlapi._reqModal).on('keyup','input[type="text"], input[type="number"], textarea', function(event) {
|
|
if ( event.keyCode < 32 )
|
|
return;
|
|
|
|
var selector = 'INPUT[name="'+ event.target.name + self._magicNull + '"]';
|
|
var checks = $(selector, htmlapi._reqModal)
|
|
if ( checks && checks[0] )
|
|
checks[0].checked = false;
|
|
});
|
|
|
|
$('.tip').tooltip({placement: 'right'});
|
|
}
|
|
|
|
HTMLApi.prototype._escapeRegex = function(str)
|
|
{
|
|
return str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&')
|
|
}
|
|
|
|
HTMLApi.prototype._flattenFields = function(mode,schema,data)
|
|
{
|
|
var rows = [];
|
|
|
|
if ( !schema || !schema.resourceFields )
|
|
return [];
|
|
|
|
var keys = Object.keys(schema.resourceFields);
|
|
var name, field, row;
|
|
for ( var i = 0 ; i < keys.length ; i++ )
|
|
{
|
|
name = keys[i];
|
|
field = schema.resourceFields[name];
|
|
|
|
if ( name == "type" )
|
|
continue;
|
|
|
|
row = this._flattenField(mode, name, field, data[name]);
|
|
if ( row )
|
|
rows.push(row);
|
|
}
|
|
|
|
return rows;
|
|
}
|
|
|
|
HTMLApi.prototype._flattenField = function(mode, name, field, data, depth)
|
|
{
|
|
depth = depth || 0;
|
|
|
|
var type = field._typeList[depth];
|
|
|
|
if ( mode == 'update' || (mode == 'create' && field.create) || (mode == 'action') )
|
|
{
|
|
// The value input's name
|
|
var formFieldName = name;
|
|
|
|
// The key input's name, for maps
|
|
var formFieldName2 = null;
|
|
var subType;
|
|
for ( var i = 0 ; i <= depth; i++ )
|
|
{
|
|
subType = field._typeList[i];
|
|
if ( subType == 'array' )
|
|
{
|
|
formFieldName += '[]';
|
|
}
|
|
else if ( subType == 'map' )
|
|
{
|
|
formFieldName2 = formFieldName+'.key{}';
|
|
formFieldName += '.value{}';
|
|
}
|
|
|
|
if ( subType == 'json' )
|
|
{
|
|
formFieldName += '.json{}';
|
|
}
|
|
}
|
|
|
|
var row = {
|
|
name: name,
|
|
formFieldName: formFieldName,
|
|
formFieldName2: formFieldName2,
|
|
formFieldNameNull: formFieldName+this._magicNull,
|
|
required: field.required || false,
|
|
writable: (mode == 'action') || (mode == 'update' && field.update) || (mode != 'update' && field.create),
|
|
description: field.description,
|
|
placeholder: field.placeholder||"",
|
|
enlargeable: (type == 'string' && (!field.maxLength || field.maxLength > 63)),
|
|
nullCheck: (field.nullable && !field.options && ['string','data','password','number','int','float','reference'].indexOf(field.type) >= 0 ),
|
|
type: type,
|
|
field: field,
|
|
children: null,
|
|
value: ''
|
|
};
|
|
|
|
var displayType = field._typeList[ field._typeList.length - 1];
|
|
var parentType = field._typeList[ field._typeList.length - 2];
|
|
if ( parentType && parentType == 'reference' )
|
|
{
|
|
var link = null;
|
|
if ( field.referenceCollection )
|
|
{
|
|
link = field.referenceCollection;
|
|
}
|
|
else
|
|
{
|
|
var displaySchema = this.getSchema(displayType);
|
|
if ( displaySchema )
|
|
{
|
|
link = displaySchema.links['collection'] || displaySchema.links['self'];
|
|
}
|
|
}
|
|
|
|
if ( link )
|
|
displayType = '<a tabindex="-1" href="' + link + '" target="_blank">' + displayType + '</a>';
|
|
}
|
|
|
|
for ( var i = field._typeList.length - 2 ; i >= depth ; i-- )
|
|
{
|
|
displayType = field._typeList[i] + '[' + displayType + ']';
|
|
}
|
|
|
|
row.displayType = displayType;
|
|
|
|
if ( type == 'map' )
|
|
{
|
|
row.children = [];
|
|
var keys = Object.keys(data||{});
|
|
var child;
|
|
for ( var i = 0 ; i < keys.length ; i++ )
|
|
{
|
|
child = this._flattenField(mode, name, field, data[keys[i]], depth+1);
|
|
child.value2 = keys[i];
|
|
child.parentIsMap = true;
|
|
row.children.push(child);
|
|
}
|
|
}
|
|
else if ( type == 'array' )
|
|
{
|
|
row.children = [];
|
|
for ( var i = 0 ; i < (data||[]).length ; i++ )
|
|
{
|
|
row.children.push( this._flattenField(mode, name, field, data[i], depth+1) );
|
|
}
|
|
}
|
|
else if ( type == 'json' )
|
|
{
|
|
row.value = JSON.stringify(data);
|
|
}
|
|
else
|
|
{
|
|
row.value = data;
|
|
}
|
|
|
|
return row;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
|
|
HTMLApi.prototype.remove = function(really)
|
|
{
|
|
this.request('DELETE');
|
|
}
|
|
|
|
HTMLApi.prototype.update = function()
|
|
{
|
|
var schema = this.getSchema(this._data.type);
|
|
|
|
var data = {};
|
|
var k, v;
|
|
for ( k in schema.resourceFields )
|
|
{
|
|
data[k] = this._data[k];
|
|
}
|
|
|
|
this.showEdit(data, true, schema)
|
|
}
|
|
|
|
HTMLApi.prototype.createTypeChanged = function(type,first)
|
|
{
|
|
var self = this;
|
|
var schema = this.getSchema(type);
|
|
|
|
// Save the current values
|
|
if ( first !== true )
|
|
{
|
|
var values = self.getFormValues(self._lastMode, null, schema);
|
|
self._lastRequestBody = values.body;
|
|
}
|
|
|
|
self._lastType = type;
|
|
self.showEdit(self._lastRequestBody, false, schema, this._data.createTypes[type] );
|
|
}
|
|
|
|
HTMLApi.prototype._flattenInputs = function($form)
|
|
{
|
|
var i, j;
|
|
var serialized = $form.serializeArray();
|
|
|
|
// serializeArray doesn't include unchecked checkboxes... so add those.
|
|
var checkboxes = $("input:checkbox:not(:checked)",$form);
|
|
var check;
|
|
for ( i = 0 ; i < checkboxes.length ; i++ )
|
|
{
|
|
check = checkboxes[i];
|
|
|
|
// But ignore the magic null checkboxes
|
|
if ( check.name.match(this._magicNullRegex) )
|
|
continue;
|
|
|
|
serialized.push({name: check.name, value: false});
|
|
}
|
|
|
|
var $files = $("INPUT[type='file']",$form);
|
|
for ( i = 0 ; i < $files.length ; i++ )
|
|
{
|
|
serialized.push({name: $files[i].name, value: $($files[i]).val()});
|
|
}
|
|
|
|
var inputs = {};
|
|
var k, field, v;
|
|
var isArray, isMapKey, isMapValue, isJsonValue, name, values;
|
|
|
|
var maps = {};
|
|
|
|
for ( i = 0 ; i < serialized.length ; i++ )
|
|
{
|
|
field = serialized[i];
|
|
k = field.name;
|
|
v = field.value;
|
|
isArray = k.indexOf('[]') >= 0;
|
|
isMapKey = k.indexOf('.key{}') >= 0;
|
|
isMapValue = k.indexOf('.value{}') >= 0;
|
|
isJsonValue = k.indexOf('.json{}') >= 0;
|
|
|
|
if ( isJsonValue )
|
|
{
|
|
try {
|
|
v = JSON.parse(v);
|
|
}
|
|
catch(e)
|
|
{
|
|
alert(e);
|
|
}
|
|
}
|
|
|
|
if ( isArray )
|
|
{
|
|
name = k.replace(/\[\]$/,'');
|
|
if ( typeof inputs[name] === "undefined" )
|
|
inputs[name] = [];
|
|
|
|
inputs[name].push(v);
|
|
}
|
|
else if ( isMapKey || isMapValue )
|
|
{
|
|
name = k;
|
|
if ( isJsonValue )
|
|
name = name.replace(/\.json\{\}$/,'');
|
|
if ( isMapKey )
|
|
name = name.replace(/\.key\{\}$/,'');
|
|
else if ( isMapValue )
|
|
name = name.replace(/\.value\{\}$/,'');
|
|
|
|
if ( typeof maps[name] === 'undefined' )
|
|
{
|
|
maps[name] = {keys: [], values: []};
|
|
}
|
|
|
|
if ( isMapKey )
|
|
maps[name].keys.push(v);
|
|
if ( isMapValue )
|
|
maps[name].values.push(v);
|
|
}
|
|
else if ( isJsonValue )
|
|
{
|
|
name = k;
|
|
name = name.replace(/\.json\{\}$/,'');
|
|
inputs[name] = v;
|
|
}
|
|
else
|
|
{
|
|
inputs[k] = v;
|
|
}
|
|
}
|
|
|
|
var keys = Object.keys(maps);
|
|
var map, subK;
|
|
for ( i = 0 ; i < keys.length ; i++ )
|
|
{
|
|
k = keys[i];
|
|
map = maps[k];
|
|
inputs[k] = {};
|
|
|
|
for ( j = 0 ; j < map.keys.length ; j++ )
|
|
{
|
|
subK = map.keys[j];
|
|
if ( subK )
|
|
inputs[k][subK] = map.values[j];
|
|
}
|
|
}
|
|
|
|
return inputs;
|
|
}
|
|
|
|
HTMLApi.prototype.getFormValues = function(mode, method, schema)
|
|
{
|
|
var $form = $('#edit-form')
|
|
var inputs = this._flattenInputs($form);
|
|
|
|
var body = {};
|
|
var blobs = null;
|
|
var k, field, v;
|
|
var isNull;
|
|
for ( k in schema.resourceFields )
|
|
{
|
|
field = schema.resourceFields[k];
|
|
v = inputs[k];
|
|
|
|
// Ignore the null checkboxes
|
|
if ( k.match(this._magicNullRegex) )
|
|
continue;
|
|
|
|
// Don't send fields that can't be set on create
|
|
if ( mode == 'create' && !field.create )
|
|
continue;
|
|
|
|
// Set the value to the magicNull if the checkbox is checked
|
|
if ( inputs[k+this._magicNull] )
|
|
v = this._magicNull;
|
|
|
|
if ( field._typeList[0] == 'array' )
|
|
{
|
|
// Make sure it's an array
|
|
if ( !v || v.length == 0 )
|
|
v = [];
|
|
|
|
// Remove empty items
|
|
for ( var i = v.length ; i >= 0 ; i-- )
|
|
{
|
|
if ( v[i] === "" )
|
|
{
|
|
v.splice(i,1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( v === this._magicNull )
|
|
{
|
|
// Don't send nullable fields if null, unless the current value is not null
|
|
if ( field.nullable && this._editData && !this._editData[k] )
|
|
continue;
|
|
else
|
|
v = null;
|
|
}
|
|
|
|
if ( field.type == 'blob' )
|
|
{
|
|
if ( v )
|
|
{
|
|
var filename = this.extractFilename(v);
|
|
if ( !blobs )
|
|
{
|
|
blobs = {};
|
|
}
|
|
blobs[k] = 1;
|
|
body[k] = filename;
|
|
}
|
|
}
|
|
else if ( field.type == 'boolean' )
|
|
{
|
|
if ( typeof v != 'undefined')
|
|
{
|
|
body[k] = (v == 1);
|
|
}
|
|
}
|
|
else if ( typeof v != 'undefined' )
|
|
{
|
|
body[k] = v;
|
|
}
|
|
else if ( method == 'PUT' && typeof this._data[k] != 'undefined' )
|
|
{
|
|
// Copy fields from the original for edit
|
|
body[k] = this._data[k];
|
|
}
|
|
}
|
|
|
|
return {
|
|
body: body,
|
|
blobs: blobs
|
|
};
|
|
}
|
|
|
|
HTMLApi.prototype.showRequest = function(mode, method, schema, retry, url)
|
|
{
|
|
var values = this.getFormValues(mode,method,schema);
|
|
|
|
var opt = {blobs: values.blobs};
|
|
|
|
if ( retry )
|
|
opt.retry = retry;
|
|
|
|
if ( url )
|
|
opt.url = url;
|
|
|
|
this.request(method, values.body, opt);
|
|
}
|
|
|
|
HTMLApi.prototype.extractFilename = function(path)
|
|
{
|
|
var sep = /[\/\\:]/; // If nothing matches, use the any of the delimiters
|
|
if ( path.match(/^\\/) || path.match(/^[^\\]:\\/) )
|
|
{
|
|
sep = "\\";
|
|
}
|
|
else if ( path.match(/^\//) )
|
|
{
|
|
sep = "/";
|
|
}
|
|
else if ( path.match(":") )
|
|
{
|
|
sep = ":";
|
|
}
|
|
|
|
var parts = path.split(sep);
|
|
return parts[parts.length-1];
|
|
}
|
|
|
|
HTMLApi.prototype.subAdd = function(button, name)
|
|
{
|
|
var schema = this._editSchema;
|
|
var schemaField = schema.resourceFields[name];
|
|
|
|
var parentField = this._flattenField('update',name,schemaField,{},0);
|
|
|
|
var field = this._flattenField('update',name,schemaField,'',1);
|
|
field.parentIsMap = parentField.type == 'map';
|
|
field.enlargeable = false;
|
|
if ( field.type == 'json' )
|
|
field.value = '{}';
|
|
|
|
var par = {
|
|
type: parentField.type,
|
|
addingField: true,
|
|
children: [field]
|
|
}
|
|
|
|
var html = Handlebars.templates['field'](par);
|
|
|
|
// html = '<div><input type="button" onclick="htmlapi.subRemove(this);" value="-">' + html + '</div>';
|
|
$(button).before(html);
|
|
}
|
|
|
|
HTMLApi.prototype.subRemove = function(button)
|
|
{
|
|
var $div = $(button).parents('DIV');
|
|
$($div[0]).remove();
|
|
}
|
|
|
|
HTMLApi.prototype.toggleNull = function(check)
|
|
{
|
|
var $check = $(check);
|
|
var name = check.name.replace(this._magicNullRegex,'');
|
|
var selector = 'INPUT[name="'+ name +'"], TEXTAREA[name="'+ name +'"]';
|
|
var $input = $(selector, htmlapi._reqModal);
|
|
|
|
if ( !$input || !$input[0] )
|
|
return;
|
|
|
|
if ( check.checked )
|
|
$input.val('');
|
|
else
|
|
$input[0].focus();
|
|
}
|
|
|
|
HTMLApi.prototype.switchToTextarea = function(button)
|
|
{
|
|
var $button = $(button);
|
|
var $par = $($button.parent());
|
|
var $input = $('INPUT[type="text"]', $par);
|
|
|
|
if ( !$input[0] )
|
|
return;
|
|
|
|
var val = $input.val();
|
|
|
|
var $textarea = $('<textarea>', {name: $input.attr('name') }).addClass('expandedTextarea');
|
|
$input.replaceWith($textarea);
|
|
$textarea.val(val);
|
|
$textarea.on('keydown', function(e) { if ( e.keyCode == 13 ) { e.stopPropagation(); return true; } });
|
|
$button.hide();
|
|
}
|
|
|
|
HTMLApi.prototype.setLocalCookie = function(on) {
|
|
if ( on === false )
|
|
{
|
|
Cookie.remove('js.url');
|
|
Cookie.remove('css.url');
|
|
}
|
|
else
|
|
{
|
|
Cookie.set('js.url','http://localhost:3000/ui.js',3650);
|
|
Cookie.set('css.url','http://localhost:3000/ui.css',3650);
|
|
}
|
|
}
|