Elgg  Version 2.3
Ajax.js
Go to the documentation of this file.
1 define(function (require) {
2  var $ = require('jquery');
3  var elgg = require('elgg');
4  var spinner = require('elgg/spinner');
5 
6  var site_url = elgg.get_site_url(),
7  action_base = site_url + 'action/',
8  fragment_pattern = /#.*$/,
9  query_pattern = /\?.*$/,
10  leading_slash_pattern = /^\//,
11  slashes_pattern = /(^\/|\/$)/g;
12 
20  function Ajax(use_spinner) {
21 
22  use_spinner = elgg.isNullOrUndefined(use_spinner) ? true : !!use_spinner;
23 
24  var that = this;
25  var spinner_starts = 0;
26 
44  function fetch(options, hook_type) {
45  var orig_options,
46  params,
47  jqXHR,
48  metadata_extracted = false,
49  error_displayed = false;
50 
56  function extract_metadata(data) {
57  if (!metadata_extracted) {
58  var m = data._elgg_msgs;
59  if (m && m.error) {
60  elgg.register_error(m.error);
61  error_displayed = true;
62  data.status = -1;
63  } else {
64  data.status = 0;
65  }
66  m && m.success && elgg.system_message(m.success);
67  delete data._elgg_msgs;
68 
69  var deps = data._elgg_deps;
70  deps && deps.length && Ajax._require(deps);
71  delete data._elgg_deps;
72 
73  metadata_extracted = true;
74  }
75  }
76 
82  that._fetch_args = {
83  options: options,
84  hook_type: hook_type
85  };
86 
87  hook_type = hook_type || '';
88 
89  if (!$.isPlainObject(options)) {
90  throw new Error('options must be a plain object with key "url"');
91  }
92  if (!options.url && hook_type !== 'path:') {
93  throw new Error('options must be a plain object with key "url"');
94  }
95 
96  // ease hook filtering by making these keys always available
97  if (options.data === undefined) {
98  options.data = {};
99  }
100  if ($.isPlainObject(options.data)) {
101  options.data = options.data || {};
102  } else {
103  if (typeof options.data !== 'string') {
104  throw new Error('if defined, options.data must be a plain object or string');
105  }
106  }
107 
108  options.dataType = 'json';
109 
110  // copy of original options
111  orig_options = $.extend({}, options);
112 
113  params = {
114  options: options
115  };
116  if (hook_type && typeof options.data !== 'string') {
117  options.data = elgg.trigger_hook(Ajax.REQUEST_DATA_HOOK, hook_type, params, options.data);
118  }
119 
120  // we do this here because hook may have made data non-empty, in which case we'd need to
121  // default to POST
122  if (!options.method) {
123  options.method = 'GET';
124  if (options.data && !$.isEmptyObject(options.data)) {
125  options.method = 'POST';
126  }
127  }
128 
129  if (use_spinner) {
130  options.beforeSend = function () {
131  orig_options.beforeSend && orig_options.beforeSend.apply(null, arguments);
132  spinner_starts++;
133  spinner.start();
134  };
135  options.complete = function () {
136  spinner_starts--;
137  if (spinner_starts < 1) {
138  spinner.stop();
139  }
140  orig_options.complete && orig_options.complete.apply(null, arguments);
141  };
142  }
143 
144  if (!options.error) {
145  options.error = function (jqXHR, textStatus, errorThrown) {
146  if (!jqXHR.getAllResponseHeaders()) {
147  // user aborts (like refresh or navigate) do not have headers
148  return;
149  }
150 
151  try {
152  var data = $.parseJSON(jqXHR.responseText);
153  if ($.isPlainObject(data)) {
154  extract_metadata(data);
155  }
156  } catch (e) {
157  if (window.console) {
158  console.warn(e.message);
159  }
160  }
161 
162  if (!error_displayed) {
163  elgg.register_error(elgg.echo('ajax:error'));
164  }
165  };
166  }
167 
168  options.dataFilter = function (data, type) {
169  if (type !== 'json') {
170  return data;
171  }
172 
173  data = $.parseJSON(data);
174 
175  extract_metadata(data);
176 
177  var params = {
178  options: orig_options
179  };
180  if (hook_type) {
181  data = elgg.trigger_hook(Ajax.RESPONSE_DATA_HOOK, hook_type, params, data);
182  }
183 
184  jqXHR.AjaxData = data;
185 
186  return JSON.stringify(data.value);
187  };
188 
189  options.url = elgg.normalize_url(options.url);
190  options.headers = {
191  'X-Elgg-Ajax-API': '2'
192  };
193 
199  that._ajax_options = options;
200 
201  jqXHR = $.ajax(options);
202 
203  return jqXHR;
204  }
205 
215  this.path = function (path, options) {
216  elgg.assertTypeOf('string', path);
217 
218  // https://example.org/elgg/foo/?arg=1#bar => foo/?arg=1
219  if (path.indexOf(site_url) === 0) {
220  path = path.substr(site_url.length);
221  }
222  path = path.replace(fragment_pattern, '');
223 
224  assertNotUrl(path);
225 
226  options = options || {};
227  options.url = path;
228 
229  // /foo/?arg=1 => foo
230  path = path.replace(query_pattern, '').replace(slashes_pattern, '');
231 
232  return fetch(options, 'path:' + path);
233  };
234 
244  this.view = function (view, options) {
245  elgg.assertTypeOf('string', view);
246  if (view === '') {
247  throw new Error('view cannot be empty');
248  }
249 
250  assertNotUrl(view);
251 
252  options = options || {};
253  options.url = 'ajax/view/' + view;
254  options.method = options.method || 'GET';
255 
256  // remove query
257  view = view.replace(query_pattern, '').replace(slashes_pattern, '');
258 
259  return fetch(options, 'view:' + view);
260  };
261 
271  this.form = function (action, options) {
272  elgg.assertTypeOf('string', action);
273  if (action === '') {
274  throw new Error('action cannot be empty');
275  }
276 
277  action = action.replace(leading_slash_pattern, '').replace(fragment_pattern, '');
278 
279  assertNotUrl(action);
280 
281  options = options || {};
282  options.url = 'ajax/form/' + action;
283  options.method = options.method || 'GET';
284 
285  // remove query
286  action = action.replace(query_pattern, '').replace(slashes_pattern, '');
287 
288  return fetch(options, 'form:' + action);
289  };
290 
300  this.action = function (action, options) {
301  elgg.assertTypeOf('string', action);
302  if (action === '') {
303  throw new Error('action cannot be empty');
304  }
305 
306  // https://example.org/elgg/action/foo/?arg=1#bar => foo/?arg=1
307  if (action.indexOf(action_base) === 0) {
308  action = action.substr(action_base.length);
309  }
310  action = action.replace(leading_slash_pattern, '').replace(fragment_pattern, '');
311 
312  assertNotUrl(action);
313 
314  options = options || {};
315  options.data = options.data || {};
316 
317  // add tokens?
318  var m = action.match(/\?(.+)$/);
319  if (m && /(^|&)__elgg_ts=/.test(m[1])) {
320  // token will be in the URL
321  } else {
322  options.data = elgg.security.addToken(options.data);
323  }
324 
325  options.method = options.method || 'POST';
326  options.url = 'action/' + action;
327 
328  // /foo/?arg=1 => foo
329  action = action.replace(query_pattern, '').replace(slashes_pattern, '');
330 
331  return fetch(options, 'action:' + action);
332  };
333 
341  this.objectify = function (el) {
342  // http://stackoverflow.com/a/1186309/3779
343  var o = {};
344  var a = $(el).serializeArray();
345 
346  $.each(a, function() {
347  if (o[this.name] !== undefined) {
348  if (!o[this.name].push) {
349  o[this.name] = [o[this.name]];
350  }
351  o[this.name].push(this.value || '');
352  } else {
353  o[this.name] = this.value || '';
354  }
355  });
356 
357  return o;
358  };
359  }
360 
366  function assertNotUrl(arg) {
367  if (/^https?:/.test(arg)) {
368  throw new Error('elgg/Ajax cannot be used with external URLs');
369  }
370  }
371 
376  Ajax.REQUEST_DATA_HOOK = 'ajax_request_data';
377 
384  Ajax.RESPONSE_DATA_HOOK = 'ajax_response_data';
385 
389  Ajax._require = require;
390 
391  return Ajax;
392 });
elgg
Definition: install.js:23
$CONFIG view
The current view type.
Definition: config.php:149
elgg isPlainObject
Check if the value is a "plain" object (i.e., created by {} or new Object())
Definition: elgglib.js:75
a
Definition: admin.css.php:97
$CONFIG path
The full path where Elgg is installed.
Definition: config.php:16
list style type
Definition: admin.css.php:808
elgg river item form
elgg require
Throw an error if the required package isn&#39;t present.
Definition: elgglib.js:164
$site name
define(function(require){var $=require('jquery');$(document).on('change', '#elgg-river-selector', function(){var url=window.location.href;if(window.location.search.length){url=url.substring(0, url.indexOf('?'));}url+= '?'+$(this).val();window.location.href=url;});})
Initiates page reload when river selector value changes core/river/filter.
elgg action
Definition: ajax.js:200