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