Merge pull request #5453 from colemanw/CRM-16148
[civicrm-core.git] / templates / CRM / Admin / Page / APIExplorer.js
CommitLineData
e4176358 1(function($, _, undefined) {
89ee60d5
CW
2 "use strict";
3 /* jshint validthis: true */
e4176358
CW
4 var
5 entity,
6 action,
a14e9d08 7 actions = {values: ['get']},
e4176358 8 fields = [],
fc6a6a51 9 getFieldData = {},
e4176358 10 params = {},
2b6e1174 11 smartyStub,
65f22b4b 12 entityDoc,
8ffa1387 13 fieldTpl = _.template($('#api-param-tpl').html()),
c275764e 14 optionsTpl = _.template($('#api-options-tpl').html()),
b07af612 15 returnTpl = _.template($('#api-return-tpl').html()),
77099ee0 16 chainTpl = _.template($('#api-chain-tpl').html()),
bc4aa590 17 docCodeTpl = _.template($('#doc-code-tpl').html()),
77099ee0 18
f76eb8ae 19 // These types of entityRef don't require any input to open
a39f2446 20 // FIXME: ought to be in getfields metadata
f76eb8ae
CW
21 OPEN_IMMEDIATELY = ['RelationshipType', 'Event', 'Group', 'Tag'],
22
65b8c1db
CW
23 // Actions that don't support fancy operators
24 NO_OPERATORS = ['create', 'update', 'delete', 'setvalue', 'getoptions', 'getactions', 'getfields'],
25
5bd06c36 26 // Actions that don't support multiple values
acadf548 27 NO_MULTI = ['delete', 'getoptions', 'getactions', 'getfields', 'setvalue'],
5bd06c36 28
77099ee0
CW
29 // Operators with special properties
30 BOOL = ['IS NULL', 'IS NOT NULL'],
31 TEXT = ['LIKE', 'NOT LIKE'],
32 MULTI = ['IN', 'NOT IN', 'BETWEEN', 'NOT BETWEEN'];
0c3a6e64 33
1080dff4
CW
34 /**
35 * Call prettyPrint function and perform additional formatting
36 * @param ele
37 */
38 function prettyPrint(ele) {
39 if (typeof window.prettyPrint === 'function') {
40 $(ele).removeClass('prettyprinted').addClass('prettyprint');
41
42 window.prettyPrint();
43
44 // Highlight errors in api result
45 $('span:contains("error_code"),span:contains("error_message")', '#api-result')
46 .siblings('span.str').css('color', '#B00');
47 }
48 }
49
b07af612
CW
50 /**
51 * Add a "fields" row
52 * @param name
53 */
e4176358 54 function addField(name) {
65b8c1db 55 $('#api-params').append($(fieldTpl({name: name || '', noOps: _.includes(NO_OPERATORS, action)})));
e4176358 56 var $row = $('tr:last-child', '#api-params');
77099ee0 57 $('input.api-param-name', $row).crmSelect2({
5c7169eb 58 data: fields.concat({id: '-', text: ts('Other') + '...', description: ts('Choose a field not in this list')}),
29956c59
CW
59 formatSelection: function(field) {
60 return field.text +
61 (field.required ? ' <span class="crm-marker">*</span>' : '');
62 },
5c7169eb 63 formatResult: function(field) {
29956c59
CW
64 return field.text +
65 (field.required ? ' <span class="crm-marker">*</span>' : '') +
66 '<div class="api-field-desc">' + field.description + '</div>';
5c7169eb 67 }
b07af612 68 }).change();
8ffa1387
CW
69 }
70
c275764e
CW
71 /**
72 * Add a new "options" row
c275764e 73 */
cb6d8859 74 function addOptionField() {
c275764e
CW
75 if ($('.api-options-row', '#api-params').length) {
76 $('.api-options-row:last', '#api-params').after($(optionsTpl({})));
77 } else {
78 $('#api-params').append($(optionsTpl({})));
79 }
80 var $row = $('.api-options-row:last', '#api-params');
81 $('.api-option-name', $row).crmSelect2({data: [
82 {id: 'limit', text: 'limit'},
83 {id: 'offset', text: 'offset'},
84 {id: 'sort', text: 'sort'},
85 {id: 'metadata', text: 'metadata'},
86 {id: '-', text: ts('Other') + '...'}
87 ]});
88 }
89
b07af612
CW
90 /**
91 * Add an "api chain" row
92 */
8ffa1387
CW
93 function addChainField() {
94 $('#api-params').append($(chainTpl({})));
95 var $row = $('tr:last-child', '#api-params');
96 $('.api-chain-entity', $row).crmSelect2({
97 formatSelection: function(item) {
3d9f5b7f
CW
98 return '<span class="icon ui-icon-link"></span> API ' +
99 ($(item.element).hasClass('strikethrough') ? '<span class="strikethrough">' + item.text + '</span>' : item.text);
2b6e1174
CW
100 },
101 placeholder: '<span class="icon ui-icon-link"></span> ' + ts('Entity'),
ff887074 102 escapeMarkup: function(m) {return m;}
8ffa1387 103 });
0c3a6e64
CW
104 }
105
3d9f5b7f
CW
106 /**
107 * Fetch available actions for selected chained entity
108 */
109 function getChainedAction() {
110 var
111 $selector = $(this),
112 entity = $selector.val(),
113 $row = $selector.closest('tr');
114 if (entity) {
115 $selector.prop('disabled', true);
116 CRM.api3(entity, 'getactions')
117 .done(function(actions) {
118 $selector.prop('disabled', false);
ff887074 119 CRM.utils.setOptions($('.api-chain-action', $row), _.transform(actions.values, function(ret, item) {ret.push({value: item, key: item});}));
3d9f5b7f
CW
120 });
121 }
122 }
123
b07af612
CW
124 /**
125 * Fetch fields for entity+action
126 */
a14e9d08 127 function getFields(changedElement) {
e4176358
CW
128 var required = [];
129 fields = [];
932630fd 130 getFieldData = {};
e4176358
CW
131 // Special case for getfields
132 if (action === 'getfields') {
133 fields.push({
134 id: 'api_action',
a39f2446
CW
135 text: 'Action',
136 options: _.reduce(actions.values, function(ret, item) {
137 ret[item] = item;
138 return ret;
139 }, {})
e4176358 140 });
e4176358 141 showFields(['api_action']);
0c3a6e64
CW
142 return;
143 }
b432ddaa 144 CRM.api3(entity, 'getfields', {'api_action': action, options: {get_options: 'all', get_options_context: 'match'}}).done(function(data) {
e4176358
CW
145 _.each(data.values, function(field) {
146 if (field.name) {
932630fd 147 getFieldData[field.name] = field;
e4176358
CW
148 fields.push({
149 id: field.name,
150 text: field.title || field.name,
7b6c5043 151 multi: !!field['api.multiple'],
5c7169eb 152 description: field.description || '',
7b6c5043 153 required: !(!field['api.required'] || field['api.required'] === '0')
e4176358 154 });
7b6c5043 155 if (field['api.required'] && field['api.required'] !== '0') {
e4176358
CW
156 required.push(field.name);
157 }
e4176358
CW
158 }
159 });
a14e9d08
CW
160 if ($(changedElement).is('#api-entity') && data.deprecated) {
161 CRM.alert(data.deprecated, entity + ' Deprecated');
162 }
e4176358 163 showFields(required);
3a721dfc
CW
164 if (action === 'get' || action === 'getsingle' || action == 'getvalue' || action === 'getstat') {
165 showReturn();
b07af612 166 }
0c3a6e64
CW
167 });
168 }
169
b07af612
CW
170 /**
171 * For "get" actions show the "return" options
3a721dfc
CW
172 *
173 * TODO: Too many hard-coded actions here. Need a way to fetch this from metadata
b07af612 174 */
3a721dfc
CW
175 function showReturn() {
176 var title = ts('Fields to return'),
177 params = {
178 data: fields,
179 multiple: true,
180 placeholder: ts('Leave blank for default')
181 };
182 if (action == 'getstat') {
183 title = ts('Group by');
184 }
185 if (action == 'getvalue') {
186 title = ts('Return Value');
187 params.placeholder = ts('Select field');
188 params.multiple = false;
189 }
29956c59 190 $('#api-params').prepend($(returnTpl({title: title, required: action == 'getvalue'})));
3a721dfc 191 $('#api-return-value').crmSelect2(params);
b07af612
CW
192 }
193
194 /**
195 * Fetch actions for entity
196 */
2b6e1174
CW
197 function getActions() {
198 if (entity) {
754927e4 199 $('#api-action').addClass('loading');
2b6e1174 200 CRM.api3(entity, 'getactions').done(function(data) {
a14e9d08 201 actions = data;
2b6e1174
CW
202 populateActions();
203 });
204 } else {
a14e9d08 205 actions = {values: ['get']};
2b6e1174
CW
206 populateActions();
207 }
208 }
209
7231faaf
CW
210 /**
211 * Test whether an action is deprecated
212 * @param action
213 * @returns {boolean}
214 */
15cbe793
CW
215 function isActionDeprecated(action) {
216 return !!(typeof actions.deprecated === 'object' && actions.deprecated[action]);
217 }
218
7231faaf
CW
219 /**
220 * Render action text depending on deprecation status
221 * @param option
222 * @returns {string}
223 */
a14e9d08 224 function renderAction(option) {
15cbe793 225 return isActionDeprecated(option.id) ? '<span class="strikethrough">' + option.text + '</span>' : option.text;
a14e9d08
CW
226 }
227
b07af612
CW
228 /**
229 * Called after getActions to populate action list
b07af612 230 */
cb6d8859 231 function populateActions() {
2f929504 232 var val = $('#api-action').val();
754927e4 233 $('#api-action').removeClass('loading').select2({
ff887074 234 data: _.transform(actions.values, function(ret, item) {ret.push({text: item, id: item});}),
a14e9d08
CW
235 formatSelection: renderAction,
236 formatResult: renderAction
2b6e1174 237 });
2f929504 238 // If previously selected action is not available, set it to 'get' if possible
ed0eaccc
CW
239 if (!_.includes(actions.values, val)) {
240 $('#api-action').select2('val', !_.includes(actions.values, 'get') ? actions.values[0] : 'get', true);
a14e9d08
CW
241 }
242 }
243
7231faaf
CW
244 /**
245 * Check for and display action-specific deprecation notices
246 * @param action
247 */
a14e9d08 248 function onChangeAction(action) {
15cbe793 249 if (isActionDeprecated(action)) {
a14e9d08 250 CRM.alert(actions.deprecated[action], action + ' deprecated');
2f929504 251 }
2b6e1174
CW
252 }
253
b07af612
CW
254 /**
255 * Called after getfields to show buttons and required fields
256 * @param required
257 */
e4176358 258 function showFields(required) {
e4176358 259 $('#api-params').empty();
8ffa1387 260 $('#api-param-buttons').show();
e4176358
CW
261 if (required.length) {
262 _.each(required, addField);
263 } else {
264 addField();
0c3a6e64
CW
265 }
266 }
267
4aed36b2
CW
268 function isYesNo(fieldName) {
269 return getFieldData[fieldName] && getFieldData[fieldName].type === 16;
270 }
271
d5a9020d
CW
272 /**
273 * Should we render a select or textfield?
274 *
275 * @param fieldName
276 * @param operator
277 * @returns boolean
278 */
279 function isSelect(fieldName, operator) {
a39f2446
CW
280 var fieldSpec = getFieldData[fieldName] || {};
281 return (isYesNo(fieldName) || fieldSpec.options || fieldSpec.FKApiName) && !_.includes(TEXT, operator);
d5a9020d
CW
282 }
283
284 /**
285 * Should we render a select as single or multi?
286 *
287 * @param fieldName
288 * @param operator
289 * @returns boolean
290 */
7b6c5043 291 function isMultiSelect(fieldName, operator) {
5bd06c36 292 if (isYesNo(fieldName) || _.includes(NO_MULTI, action)) {
4aed36b2
CW
293 return false;
294 }
ed0eaccc 295 if (_.includes(MULTI, operator)) {
d5a9020d
CW
296 return true;
297 }
298 // The = operator is ambiguous but all others can be safely assumed to be single
299 if (operator !== '=') {
300 return false;
301 }
302 return true;
303 /*
304 * Attempt to resolve the ambiguity of the = operator using metadata
305 * commented out because there is not enough metadata in the api at this time
306 * to accurately figure it out.
307 */
308 // var field = fieldName && _.find(fields, 'id', fieldName);
309 // return field && field.multi;
7b6c5043
CW
310 }
311
c275764e 312 /**
fc6a6a51 313 * Render value input as a textfield, option list, entityRef, or hidden,
77099ee0 314 * Depending on selected param name and operator
c275764e 315 */
77099ee0
CW
316 function renderValueField() {
317 var $row = $(this).closest('tr'),
318 name = $('input.api-param-name', $row).val(),
319 operator = $('.api-param-op', $row).val(),
77099ee0 320 $valField = $('input.api-param-value', $row),
7b6c5043 321 multiSelect = isMultiSelect(name, operator),
77099ee0
CW
322 currentVal = $valField.val();
323 // Boolean fields only have 1 possible value
ed0eaccc 324 if (_.includes(BOOL, operator)) {
77099ee0
CW
325 if ($valField.data('select2')) {
326 $valField.select2('destroy');
327 }
328 $valField.css('visibility', 'hidden').val('1');
329 return;
330 }
331 $valField.css('visibility', '');
fc6a6a51 332 // Option list or entityRef input
d5a9020d 333 if (isSelect(name, operator)) {
77099ee0
CW
334 // Reset value before switching to a select from something else
335 if ($(this).is('.api-param-name') || !$valField.data('select2')) {
336 $valField.val('');
337 }
338 // When switching from multi-select to single select
ed0eaccc 339 else if (!multiSelect && _.includes(currentVal, ',')) {
77099ee0
CW
340 $valField.val(currentVal.split(',')[0]);
341 }
4aed36b2
CW
342 // Yes-No options
343 if (isYesNo(name)) {
344 $valField.select2({
345 data: [{id: 1, text: ts('Yes')}, {id: 0, text: ts('No')}]
346 });
347 }
fc6a6a51 348 // Select options
a39f2446 349 else if (getFieldData[name].options) {
fc6a6a51 350 $valField.select2({
7b6c5043 351 multiple: multiSelect,
a39f2446 352 data: _.map(getFieldData[name].options, function (value, key) {
fc6a6a51
CW
353 return {id: key, text: value};
354 })
355 });
356 }
357 // EntityRef
358 else {
359 $valField.crmEntityRef({
360 entity: getFieldData[name].FKApiName,
f76eb8ae 361 select: {
7b6c5043 362 multiple: multiSelect,
ed0eaccc 363 minimumInputLength: _.includes(OPEN_IMMEDIATELY, getFieldData[name].FKApiName) ? 0 : 1
f76eb8ae 364 }
fc6a6a51
CW
365 });
366 }
77099ee0 367 return;
e4176358 368 }
77099ee0
CW
369 // Plain text input
370 if ($valField.data('select2')) {
e4176358
CW
371 $valField.select2('destroy');
372 }
e4176358 373 }
0c3a6e64 374
e4176358
CW
375 /**
376 * Attempt to parse a string into a value of the intended type
babf9678
CW
377 * @param val string
378 * @param makeArray bool
e4176358
CW
379 */
380 function evaluate(val, makeArray) {
381 try {
382 if (!val.length) {
77099ee0 383 return makeArray ? [] : '';
e4176358
CW
384 }
385 var first = val.charAt(0),
386 last = val.slice(-1);
387 // Simple types
51f197bf 388 if (val === 'true' || val === 'false' || val === 'null') {
cb6d8859 389 /* jshint evil: true */
e4176358
CW
390 return eval(val);
391 }
392 // Quoted strings
393 if ((first === '"' || first === "'") && last === first) {
394 return val.slice(1, -1);
0c3a6e64 395 }
7231faaf 396 // Parse json - use eval rather than $.parseJSON because it's less strict about formatting
e4176358
CW
397 if ((first === '[' && last === ']') || (first === '{' && last === '}')) {
398 return eval('(' + val + ')');
399 }
400 // Transform csv to array
77099ee0
CW
401 if (makeArray) {
402 var result = [];
403 $.each(val.split(','), function(k, v) {
404 result.push(evaluate($.trim(v)) || v);
405 });
406 return result;
407 }
7231faaf 408 // Integers - skip any multidigit number that starts with 0 to avoid oddities (it will be treated as a string below)
77099ee0
CW
409 if (!isNaN(val) && val.search(/[^\d]/) < 0 && (val.length === 1 || first !== '0')) {
410 return parseInt(val, 10);
e4176358
CW
411 }
412 // Ok ok it's really a string
413 return val;
414 } catch(e) {
415 // If eval crashed return undefined
416 return undefined;
0c3a6e64 417 }
e4176358 418 }
0c3a6e64 419
e4176358
CW
420 /**
421 * Format value to look like php code
422 * @param val
423 */
424 function phpFormat(val) {
425 var ret = '';
426 if ($.isPlainObject(val)) {
427 $.each(val, function(k, v) {
c275764e 428 ret += (ret ? ', ' : '') + "'" + k + "' => " + phpFormat(v);
e4176358
CW
429 });
430 return 'array(' + ret + ')';
0c3a6e64 431 }
e4176358
CW
432 if ($.isArray(val)) {
433 $.each(val, function(k, v) {
434 ret += (ret ? ', ' : '') + phpFormat(v);
435 });
436 return 'array(' + ret + ')';
437 }
54c512e0 438 return JSON.stringify(val).replace(/\$/g, '\\$');
e4176358
CW
439 }
440
441 /**
442 * Smarty doesn't support array literals so we provide a stub
443 * @param js string
d5a9020d 444 * @param key string
e4176358
CW
445 */
446 function smartyFormat(js, key) {
ed0eaccc 447 if (_.includes(js, '[') || _.includes(js, '{')) {
2b6e1174 448 smartyStub = true;
e4176358
CW
449 return '$' + key.replace(/[. -]/g, '_');
450 }
451 return js;
452 }
453
c275764e
CW
454 /**
455 * Create the params array from user input
456 * @param e
457 */
e4176358
CW
458 function buildParams(e) {
459 params = {};
460 $('.api-param-checkbox:checked').each(function() {
461 params[this.name] = 1;
462 });
c275764e 463 $('input.api-param-value, input.api-option-value').each(function() {
e4176358 464 var $row = $(this).closest('tr'),
d5a9020d 465 input = $(this).val(),
77099ee0 466 op = $('select.api-param-op', $row).val() || '=',
e4176358 467 name = $('input.api-param-name', $row).val(),
d5a9020d 468 // Workaround for ambiguity of the = operator
ed0eaccc 469 makeArray = (op === '=' && isSelect(name, op)) ? _.includes(input, ',') : op !== '=' && isMultiSelect(name, op),
d5a9020d 470 val = evaluate(input, makeArray);
b07af612
CW
471
472 // Ignore blank values for the return field
473 if ($(this).is('#api-return-value') && !val) {
474 return;
475 }
8ffa1387
CW
476 // Special syntax for api chaining
477 if (!name && $('select.api-chain-entity', $row).val()) {
478 name = 'api.' + $('select.api-chain-entity', $row).val() + '.' + $('select.api-chain-action', $row).val();
479 }
c275764e
CW
480 // Special handling for options
481 if ($(this).is('.api-option-value')) {
482 op = $('input.api-option-name', $row).val();
483 if (op) {
484 name = 'options';
485 }
486 }
e4176358 487 if (name && val !== undefined) {
c275764e 488 params[name] = op === '=' ? val : (params[name] || {});
e4176358
CW
489 if (op !== '=') {
490 params[name][op] = val;
491 }
babf9678
CW
492 if ($(this).hasClass('crm-error')) {
493 clearError(this);
494 }
e4176358
CW
495 }
496 else if (name && (!e || e.type !== 'keyup')) {
497 setError(this);
498 }
499 });
500 if (entity && action) {
501 formatQuery();
0c3a6e64 502 }
e4176358 503 }
0c3a6e64 504
7231faaf
CW
505 /**
506 * Display error message on incorrectly-formatted params
507 * @param el
508 */
e4176358
CW
509 function setError(el) {
510 if (!$(el).hasClass('crm-error')) {
babf9678 511 var msg = ts('Syntax error: input should be valid JSON or a quoted string.');
e4176358
CW
512 $(el)
513 .addClass('crm-error')
2b6e1174 514 .css('width', '82%')
babf9678
CW
515 .attr('title', msg)
516 .before('<div class="icon red-icon ui-icon-alert" title="'+msg+'"/>')
517 .tooltip();
e4176358
CW
518 }
519 }
0c3a6e64 520
7231faaf
CW
521 /**
522 * Remove error message
523 * @param el
524 */
e4176358
CW
525 function clearError(el) {
526 $(el)
527 .removeClass('crm-error')
528 .attr('title', '')
2b6e1174 529 .css('width', '85%')
babf9678 530 .tooltip('destroy')
e4176358
CW
531 .siblings('.ui-icon-alert').remove();
532 }
0c3a6e64 533
7231faaf
CW
534 /**
535 * Render the api request in various formats
536 */
e4176358
CW
537 function formatQuery() {
538 var i = 0, q = {
539 smarty: "{crmAPI var='result' entity='" + entity + "' action='" + action + "'",
540 php: "$result = civicrm_api3('" + entity + "', '" + action + "'",
541 json: "CRM.api3('" + entity + "', '" + action + "'",
2dad2b26
TO
542 drush: "drush cvapi " + entity + '.' + action + ' ',
543 wpcli: "wp cv api " + entity + '.' + action + ' ',
d461a2ad 544 rest: CRM.config.resourceBase + "extern/rest.php?entity=" + entity + "&action=" + action + "&json=" + JSON.stringify(params) + "&api_key=yourkey&key=sitekey"
e4176358 545 };
2b6e1174 546 smartyStub = false;
e4176358
CW
547 $.each(params, function(key, value) {
548 var js = JSON.stringify(value);
cb6d8859 549 if (!(i++)) {
e4176358
CW
550 q.php += ", array(\n";
551 q.json += ", {\n";
552 } else {
553 q.json += ",\n";
554 }
555 q.php += " '" + key + "' => " + phpFormat(value) + ",\n";
556 q.json += " \"" + key + '": ' + js;
e4176358 557 q.smarty += ' ' + key + '=' + smartyFormat(js, key);
77099ee0 558 // FIXME: This is not totally correct cli syntax
555d256f 559 q.drush += key + '=' + js + ' ';
2dad2b26 560 q.wpcli += key + '=' + js + ' ';
e4176358
CW
561 });
562 if (i) {
563 q.php += ")";
564 q.json += "\n}";
565 }
566 q.php += ");";
567 q.json += ").done(function(result) {\n // do something\n});";
568 q.smarty += "}\n{foreach from=$result.values item=" + entity.toLowerCase() + "}\n {$" + entity.toLowerCase() + ".some_field}\n{/foreach}";
ed0eaccc 569 if (!_.includes(action, 'get')) {
e4176358 570 q.smarty = '{* Smarty API only works with get actions *}';
2b6e1174 571 } else if (smartyStub) {
9405a07b 572 q.smarty = "{* Smarty does not have a syntax for array literals; assign complex variables from php *}\n" + q.smarty;
e4176358
CW
573 }
574 $.each(q, function(type, val) {
1080dff4 575 $('#api-' + type).text(val);
e4176358 576 });
1080dff4 577 prettyPrint('#api-generated pre');
e4176358 578 }
0c3a6e64 579
7231faaf
CW
580 /**
581 * Submit button handler
582 * @param e
583 */
e4176358
CW
584 function submit(e) {
585 e.preventDefault();
586 if (!entity || !action) {
2b6e1174 587 alert(ts('Select an entity.'));
e4176358
CW
588 return;
589 }
ed0eaccc 590 if (!_.includes(action, 'get') && action != 'check') {
e4176358
CW
591 var msg = action === 'delete' ? ts('This will delete data from CiviCRM. Are you sure?') : ts('This will write to the database. Continue?');
592 CRM.confirm({title: ts('Confirm %1', {1: action}), message: msg}).on('crmConfirm:yes', execute);
0c3a6e64 593 } else {
e4176358 594 execute();
0c3a6e64 595 }
0c3a6e64
CW
596 }
597
77099ee0
CW
598 /**
599 * Execute api call and display the results
600 * Note: We have to manually execute the ajax in order to add the secret extra "prettyprint" param
601 */
e4176358
CW
602 function execute() {
603 $('#api-result').html('<div class="crm-loading-element"></div>');
604 $.ajax({
605 url: CRM.url('civicrm/ajax/rest'),
606 data: {
607 entity: entity,
608 action: action,
609 prettyprint: 1,
610 json: JSON.stringify(params)
611 },
ed0eaccc 612 type: _.includes(action, 'get') ? 'GET' : 'POST',
e4176358
CW
613 dataType: 'text'
614 }).done(function(text) {
1080dff4
CW
615 $('#api-result').text(text);
616 prettyPrint('#api-result');
e4176358 617 });
0c3a6e64 618 }
e4176358 619
89ee60d5
CW
620 /**
621 * Fetch list of example files for a given entity
622 */
623 function getExamples() {
624 CRM.utils.setOptions($('#example-action').prop('disabled', true).addClass('loading'), []);
625 $.getJSON(CRM.url('civicrm/ajax/apiexample', {entity: $(this).val()}))
626 .done(function(result) {
627 CRM.utils.setOptions($('#example-action').prop('disabled', false).removeClass('loading'), result);
628 });
629 }
630
631 /**
632 * Fetch and display an example file
633 */
634 function getExample() {
635 var
636 entity = $('#example-entity').val(),
637 action = $('#example-action').val();
638 if (entity && action) {
639 $('#example-result').block();
640 $.get(CRM.url('civicrm/ajax/apiexample', {file: entity + '/' + action}))
641 .done(function(result) {
1080dff4
CW
642 $('#example-result').unblock().text(result);
643 prettyPrint('#example-result');
89ee60d5
CW
644 });
645 } else {
65f22b4b 646 $('#example-result').text($('#example-result').attr('placeholder'));
89ee60d5
CW
647 }
648 }
649
bc4aa590
CW
650 /**
651 * Fetch entity docs & actions
652 */
653 function getDocEntity() {
654 CRM.utils.setOptions($('#doc-action').prop('disabled', true).addClass('loading'), []);
655 $.getJSON(CRM.url('civicrm/ajax/apidoc', {entity: $(this).val()}))
656 .done(function(result) {
65f22b4b 657 entityDoc = result.doc;
bc4aa590
CW
658 CRM.utils.setOptions($('#doc-action').prop('disabled', false).removeClass('loading'), result.actions);
659 $('#doc-result').html(result.doc);
fea52a54 660 prettyPrint('#doc-result pre');
bc4aa590
CW
661 });
662 }
663
664 /**
665 * Fetch entity+action docs & code
666 */
667 function getDocAction() {
668 var
669 entity = $('#doc-entity').val(),
670 action = $('#doc-action').val();
671 if (entity && action) {
672 $('#doc-result').block();
673 $.get(CRM.url('civicrm/ajax/apidoc', {entity: entity, action: action}))
674 .done(function(result) {
675 $('#doc-result').unblock().html(result.doc);
676 if (result.code) {
677 $('#doc-result').append(docCodeTpl(result));
678 }
fea52a54 679 prettyPrint('#doc-result pre');
bc4aa590
CW
680 });
681 } else {
65f22b4b 682 $('#doc-result').html(entityDoc);
fea52a54 683 prettyPrint('#doc-result pre');
bc4aa590
CW
684 }
685 }
686
e4176358 687 $(document).ready(function() {
89ee60d5
CW
688 // Set up tabs - bind active tab to document hash because... it's cool?
689 document.location.hash = document.location.hash || 'explorer';
690 $('#mainTabContainer')
691 .tabs({
692 active: $(document.location.hash + '-tab').index() - 1
693 })
694 .on('tabsactivate', function(e, ui) {
695 if (ui.newPanel) {
696 document.location.hash = ui.newPanel.attr('id').replace('-tab', '');
697 }
698 });
bc4aa590
CW
699 $(window).on('hashchange', function() {
700 $('#mainTabContainer').tabs('option', 'active', $(document.location.hash + '-tab').index() - 1);
701 });
89ee60d5
CW
702
703 // Initialize widgets
bc4aa590 704 $('#api-entity, #example-entity, #doc-entity').crmSelect2({
a14e9d08
CW
705 // Add strikethough class to selection to indicate deprecated apis
706 formatSelection: function(option) {
707 return $(option.element).hasClass('strikethrough') ? '<span class="strikethrough">' + option.text + '</span>' : option.text;
708 }
709 });
e4176358
CW
710 $('form#api-explorer')
711 .on('change', '#api-entity, #api-action', function() {
712 entity = $('#api-entity').val();
a14e9d08 713 action = $('#api-action').val();
2b6e1174 714 if ($(this).is('#api-entity')) {
2b6e1174 715 getActions();
a14e9d08
CW
716 } else {
717 onChangeAction(action);
2b6e1174 718 }
e4176358
CW
719 if (entity && action) {
720 $('#api-params').html('<tr><td colspan="4" class="crm-loading-element"></td></tr>');
721 $('#api-params-table thead').show();
a14e9d08 722 getFields(this);
e4176358
CW
723 buildParams();
724 } else {
725 $('#api-params, #api-generated pre').empty();
8ffa1387 726 $('#api-param-buttons, #api-params-table thead').hide();
e4176358
CW
727 }
728 })
2b6e1174 729 .on('change keyup', 'input.api-input, #api-params select', buildParams)
e4176358
CW
730 .on('submit', submit);
731 $('#api-params')
77099ee0
CW
732 .on('change', 'input.api-param-name, select.api-param-op', renderValueField)
733 .on('change', 'input.api-param-name, .api-option-name', function() {
f76eb8ae 734 if ($(this).val() === '-' && $(this).data('select2')) {
c275764e
CW
735 $(this).select2('destroy');
736 $(this).val('').focus();
737 }
738 })
e4176358
CW
739 .on('click', '.api-param-remove', function(e) {
740 e.preventDefault();
741 $(this).closest('tr').remove();
742 buildParams();
3d9f5b7f
CW
743 })
744 .on('change', 'select.api-chain-entity', getChainedAction);
89ee60d5
CW
745 $('#example-entity').on('change', getExamples);
746 $('#example-action').on('change', getExample);
bc4aa590
CW
747 $('#doc-entity').on('change', getDocEntity);
748 $('#doc-action').on('change', getDocAction);
e4176358 749 $('#api-params-add').on('click', function(e) {
8ffa1387 750 e.preventDefault();
e4176358 751 addField();
8ffa1387 752 });
c275764e
CW
753 $('#api-option-add').on('click', function(e) {
754 e.preventDefault();
755 addOptionField();
756 });
8ffa1387 757 $('#api-chain-add').on('click', function(e) {
e4176358 758 e.preventDefault();
8ffa1387 759 addChainField();
e4176358
CW
760 });
761 $('#api-entity').change();
0c3a6e64 762 });
e4176358 763}(CRM.$, CRM._));