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