Elgg  Version 5.1
Ajax.js
Go to the documentation of this file.
1 define(['jquery', 'elgg', 'elgg/spinner', 'elgg/system_messages', 'elgg/security', 'elgg/i18n', 'elgg/hooks'], function ($, elgg, spinner, system_messages, security, i18n, hooks) {
2  var site_url = elgg.get_site_url(),
3  action_base = site_url + 'action/',
4  fragment_pattern = /#.*$/,
5  query_pattern = /\?.*$/,
6  leading_slash_pattern = /^\//,
7  slashes_pattern = /(^\/|\/$)/g;
8 
16  function Ajax(use_spinner) {
17 
18  use_spinner = (use_spinner == null) ? true : !!use_spinner;
19 
20  var that = this;
21  var spinner_starts = 0;
22 
40  function fetch(options, hook_type) {
41  var orig_options,
42  params,
43  jqXHR,
44  metadata_extracted = false,
45  error_displayed = false;
46 
53  function extract_metadata(data, status_code) {
54 
55  status_code = status_code || 200;
56 
57  if (!metadata_extracted) {
58  var m = data._elgg_msgs;
59  if (m && m.error) {
60  data.error = m.error;
61  }
62 
63  if (data.error && options.showErrorMessages) {
64  system_messages.error(data.error);
65  error_displayed = true;
66  }
67 
68  if (data.error || status_code !== 200) {
69  data.status = -1;
70  } else {
71  data.status = 0;
72  }
73 
74  m && m.success && options.showSuccessMessages && system_messages.success(m.success);
75  delete data._elgg_msgs;
76 
77  var deps = data._elgg_deps;
78  deps && deps.length && Ajax._require(deps);
79  delete data._elgg_deps;
80 
81  metadata_extracted = true;
82  }
83  }
84 
90  that._fetch_args = {
91  options: options,
92  hook_type: hook_type
93  };
94 
95  hook_type = hook_type || '';
96 
97  if (!$.isPlainObject(options)) {
98  throw new Error('options must be a plain object with key "url"');
99  }
100 
101  if (!options.url && hook_type !== 'path:') {
102  throw new Error('options must be a plain object with key "url"');
103  }
104 
105  // ease hook filtering by making these keys always available
106  if (options.data === undefined) {
107  options.data = {};
108  }
109 
110  if (options.showSuccessMessages === undefined) {
111  options.showSuccessMessages = true;
112  }
113 
114  if (options.showErrorMessages === undefined) {
115  options.showErrorMessages = true;
116  }
117 
118  if ($.isPlainObject(options.data)) {
119  options.data = options.data || {};
120  } else if (options.data instanceof FormData) {
121  options.processData = false;
122  options.contentType = false;
123  } else {
124  if (typeof options.data !== 'string') {
125  throw new Error('if defined, options.data must be a plain object or string');
126  }
127  }
128 
129  options.dataType = 'json';
130 
131  // copy of original options
132  orig_options = $.extend({}, options);
133 
134  params = {
135  options: options
136  };
137  if (hook_type && typeof options.data !== 'string') {
138  options.data = hooks.trigger(Ajax.REQUEST_DATA_HOOK, hook_type, params, options.data);
139  }
140 
141  // we do this here because hook may have made data non-empty, in which case we'd need to
142  // default to POST
143  if (!options.method) {
144  options.method = 'GET';
145  if (options.data && !$.isEmptyObject(options.data)) {
146  options.method = 'POST';
147  }
148  }
149 
150  if (use_spinner) {
151  options.beforeSend = function () {
152  orig_options.beforeSend && orig_options.beforeSend.apply(null, arguments);
153  spinner_starts++;
154  spinner.start();
155  };
156  options.complete = function () {
157  spinner_starts--;
158  if (spinner_starts < 1) {
159  spinner.stop();
160  }
161 
162  orig_options.complete && orig_options.complete.apply(null, arguments);
163  };
164  }
165 
166  var custom_error = function() {};
167  if (options.error) {
168  custom_error = options.error;
169  }
170 
171  options.error = function (jqXHR, textStatus, errorThrown) {
172  if (!jqXHR.getAllResponseHeaders()) {
173  // trigger custom error
174  custom_error(jqXHR, textStatus, errorThrown);
175 
176  // user aborts (like refresh or navigate) do not have headers
177  return;
178  }
179 
180  try {
181  var data = $.parseJSON(jqXHR.responseText);
182  if ($.isPlainObject(data)) {
183  extract_metadata(data, jqXHR.status);
184  }
185  } catch (e) {
186  if (window.console) {
187  console.warn(e.message);
188  }
189  }
190 
191  if (!error_displayed && options.showErrorMessages) {
192  system_messages.error(i18n.echo('ajax:error'));
193  }
194 
195  // trigger custom error
196  custom_error(jqXHR, textStatus, errorThrown);
197  };
198 
199  options.dataFilter = function (data, type) {
200  if (type !== 'json') {
201  return data;
202  }
203 
204  data = $.parseJSON(data);
205 
206  extract_metadata(data, 200);
207 
208  var params = {
209  options: orig_options
210  };
211  if (hook_type) {
212  data = hooks.trigger(Ajax.RESPONSE_DATA_HOOK, hook_type, params, data);
213  }
214 
215  jqXHR.AjaxData = data;
216 
217  if (data.value !== undefined) {
218  // regular JSON responses wrap the 'data' in 'value'
219  return JSON.stringify(data.value);
220  }
221 
222  return JSON.stringify(data);
223  };
224 
225  options.url = elgg.normalize_url(options.url);
226  options.headers = {
227  'X-Elgg-Ajax-API': '2'
228  };
229 
235  that._ajax_options = options;
236 
237  jqXHR = $.ajax(options);
238 
239  return jqXHR;
240  }
241 
251  this.path = function (path, options) {
252  elgg.assertTypeOf('string', path);
253 
254  // https://example.org/elgg/foo/?arg=1#bar => foo/?arg=1
255  if (path.indexOf(site_url) === 0) {
256  path = path.substring(site_url.length);
257  }
258 
259  path = path.replace(fragment_pattern, '');
260 
261  assertNotUrl(path);
262 
263  options = options || {};
264  options.url = path;
265 
266  // /foo/?arg=1 => foo
267  path = path.replace(query_pattern, '').replace(slashes_pattern, '');
268 
269  return fetch(options, 'path:' + path);
270  };
271 
281  this.view = function (view, options) {
282  elgg.assertTypeOf('string', view);
283  if (view === '') {
284  throw new Error('view cannot be empty');
285  }
286 
287  assertNotUrl(view);
288 
289  options = options || {};
290  options.url = 'ajax/view/' + view;
291  options.method = options.method || 'GET';
292 
293  // remove query
294  view = view.replace(query_pattern, '').replace(slashes_pattern, '');
295 
296  return fetch(options, 'view:' + view);
297  };
298 
308  this.form = function (action, options) {
309  elgg.assertTypeOf('string', action);
310  if (action === '') {
311  throw new Error('action cannot be empty');
312  }
313 
314  action = action.replace(leading_slash_pattern, '').replace(fragment_pattern, '');
315 
316  assertNotUrl(action);
317 
318  options = options || {};
319  options.url = 'ajax/form/' + action;
320  options.method = options.method || 'GET';
321 
322  // remove query
323  action = action.replace(query_pattern, '').replace(slashes_pattern, '');
324 
325  return fetch(options, 'form:' + action);
326  };
327 
337  this.action = function (action, options) {
338  elgg.assertTypeOf('string', action);
339  if (action === '') {
340  throw new Error('action cannot be empty');
341  }
342 
343  // https://example.org/elgg/action/foo/?arg=1#bar => foo/?arg=1
344  if (action.indexOf(action_base) === 0) {
345  action = action.substring(action_base.length);
346  }
347 
348  action = action.replace(leading_slash_pattern, '').replace(fragment_pattern, '');
349 
350  assertNotUrl(action);
351 
352  options = options || {};
353  options.data = options.data || {};
354 
355  // add tokens?
356  var m = action.match(/\?(.+)$/);
357  if (m && /(^|&)__elgg_ts=/.test(m[1])) {
358  // token will be in the URL
359  } else {
360  options.data = security.addToken(options.data);
361  }
362 
363  options.method = options.method || 'POST';
364  options.url = 'action/' + action;
365 
366  // /foo/?arg=1 => foo
367  action = action.replace(query_pattern, '').replace(slashes_pattern, '');
368 
369  return fetch(options, 'action:' + action);
370  };
371 
378  this.objectify = function (el) {
379 
380  /*
381  * Triggering an event to allow preparation of the form to happen.
382  * Plugins like CKEditor can use this to populate the fields with actual values.
383  */
384  $(el).trigger('elgg-ajax-objectify');
385 
386  return new FormData($(el)[0]);
387  };
388 
395  this.forward = function(destination) {
396  spinner_starts++;
397  spinner.start();
398  elgg.forward(destination);
399  };
400  }
401 
407  function assertNotUrl(arg) {
408  if (/^https?:/.test(arg)) {
409  throw new Error('elgg/Ajax cannot be used with external URLs');
410  }
411  }
412 
417  Ajax.REQUEST_DATA_HOOK = 'ajax_request_data';
418 
425  Ajax.RESPONSE_DATA_HOOK = 'ajax_response_data';
426 
430  Ajax._require = require;
431 
432  return Ajax;
433 });
elgg forward
Meant to mimic the php forward() function by simply redirecting the user to another page...
Definition: elgglib.js:114
define(['jquery', 'elgg/spinner', 'elgg/Ajax', 'elgg/system_messages', 'elgg/security', 'elgg/i18n', 'jquery-ui/widgets/sortable'], function($, spinner, Ajax, system_messages, security, i18n){function freezePlugins(){$('#elgg-plugin-list-cover').css('display', 'block');};function unfreezePlugins(){$('#elgg-plugin-list-cover').css('display', 'none');};function initPluginReordering(){$('#elgg-plugin-list >.elgg-list-container > ul').sortable({items: 'li:has(>.elgg-state-draggable)', handle: '.elgg-body', forcePlaceholderSize:true, placeholder: 'elgg-plugin-placeholder', opacity:0.8, revert:500, stop:movePlugin});};function toggleSinglePlugin(){spinner.start();freezePlugins();};function toggleAllPlugins(event){event.preventDefault();if(!confirm(i18n.echo('question:areyousure'))){return;}freezePlugins();var guids=[], state=$(this).data('desiredState'), find_state=state== 'active'? 'inactive': 'active';$('.elgg-plugin.elgg-state-'+find_state+ ':visible').each(function(){var guid=$(this).data('guid');if(guid){guids.push(guid);}});if(!guids.length){system_messages.error(i18n.echo('admin:plugins:already:'+state));unfreezePlugins();return;}spinner.start();var $form=$('< form method="post"/>');$form.prop('action', security.addToken(this.href));$form.append('< input type="hidden"name="guids"value="' + guids.join(',') + '"/>');$form.appendTo("body").submit();};function movePlugin(e, ui){freezePlugins();var pluginGuid=ui.item.attr('id');pluginGuid=pluginGuid.replace('elgg-object-', '');var ajax=new Ajax();ajax.action('admin/plugins/set_priority',{data:{plugin_guid:pluginGuid, priority:ui.item.index()+1}, success:function(){unfreezePlugins();}});};function filterPluginCategory(event){event.preventDefault();$(".elgg-admin-plugins-categories > li").removeClass("elgg-state-selected");$(".elgg-plugin").hide();$(".elgg-plugin-category-"+$(this).attr("rel")).show();$(this).closest('li').addClass("elgg-state-selected");};initPluginReordering();$(document).on('click', '.elgg-admin-plugins-categories a', filterPluginCategory);$(document).on('click', '.elgg-plugins-toggle', toggleAllPlugins);$(document).on('click', '.elgg-plugin-state-change', toggleSinglePlugin);})
var elgg
Definition: elgglib.js:4