You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

862 lines
28 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. /*
  2. * # Semantic - API
  3. * http://github.com/semantic-org/semantic-ui/
  4. *
  5. *
  6. * Copyright 2014 Contributor
  7. * Released under the MIT license
  8. * http://opensource.org/licenses/MIT
  9. *
  10. */
  11. ;(function ( $, window, document, undefined ) {
  12. $.api = $.fn.api = function(parameters) {
  13. var
  14. // use window context if none specified
  15. $allModules = $.isFunction(this)
  16. ? $(window)
  17. : $(this),
  18. moduleSelector = $allModules.selector || '',
  19. time = new Date().getTime(),
  20. performance = [],
  21. query = arguments[0],
  22. methodInvoked = (typeof query == 'string'),
  23. queryArguments = [].slice.call(arguments, 1),
  24. returnedValue
  25. ;
  26. $allModules
  27. .each(function() {
  28. var
  29. settings = ( $.isPlainObject(parameters) )
  30. ? $.extend(true, {}, $.fn.api.settings, parameters)
  31. : $.extend({}, $.fn.api.settings),
  32. // internal aliases
  33. namespace = settings.namespace,
  34. metadata = settings.metadata,
  35. selector = settings.selector,
  36. error = settings.error,
  37. className = settings.className,
  38. // define namespaces for modules
  39. eventNamespace = '.' + namespace,
  40. moduleNamespace = 'module-' + namespace,
  41. // element that creates request
  42. $module = $(this),
  43. $form = $module.closest(selector.form),
  44. // context used for state
  45. $context = (settings.stateContext)
  46. ? $(settings.stateContext)
  47. : $module,
  48. // request details
  49. ajaxSettings,
  50. requestSettings,
  51. url,
  52. data,
  53. // standard module
  54. element = this,
  55. context = $context.get(),
  56. instance = $module.data(moduleNamespace),
  57. module
  58. ;
  59. module = {
  60. initialize: function() {
  61. var
  62. triggerEvent = module.get.event()
  63. ;
  64. // bind events
  65. if(!methodInvoked) {
  66. if( triggerEvent ) {
  67. module.debug('Attaching API events to element', triggerEvent);
  68. $module
  69. .on(triggerEvent + eventNamespace, module.event.trigger)
  70. ;
  71. }
  72. else if(settings.on == 'now') {
  73. module.debug('Querying API now', triggerEvent);
  74. module.query();
  75. }
  76. }
  77. module.instantiate();
  78. },
  79. instantiate: function() {
  80. module.verbose('Storing instance of module', module);
  81. instance = module;
  82. $module
  83. .data(moduleNamespace, instance)
  84. ;
  85. },
  86. destroy: function() {
  87. module.verbose('Destroying previous module for', element);
  88. $module
  89. .removeData(moduleNamespace)
  90. .off(eventNamespace)
  91. ;
  92. },
  93. query: function() {
  94. if(module.is.disabled()) {
  95. module.debug('Element is disabled API request aborted');
  96. return;
  97. }
  98. // determine if an api event already occurred
  99. if(module.is.loading() && settings.throttle === 0 ) {
  100. module.debug('Cancelling request, previous request is still pending');
  101. return;
  102. }
  103. // pass element metadata to url (value, text)
  104. if(settings.defaultData) {
  105. $.extend(true, settings.urlData, module.get.defaultData());
  106. }
  107. // Add form content
  108. if(settings.serializeForm !== false || $context.is('form')) {
  109. if(settings.serializeForm == 'json') {
  110. $.extend(true, settings.data, module.get.formData());
  111. }
  112. else {
  113. settings.data = module.get.formData();
  114. }
  115. }
  116. // call beforesend and get any settings changes
  117. requestSettings = module.get.settings();
  118. // check if before send cancelled request
  119. if(requestSettings === false) {
  120. module.cancelled = true;
  121. module.error(error.beforeSend);
  122. return;
  123. }
  124. else {
  125. module.cancelled = false;
  126. }
  127. if(settings.url) {
  128. // override with url if specified
  129. module.debug('Using specified url', url);
  130. url = module.add.urlData( settings.url );
  131. }
  132. else {
  133. // otherwise find url from api endpoints
  134. url = module.add.urlData( module.get.templateURL() );
  135. module.debug('Added URL Data to url', url);
  136. }
  137. // exit conditions reached, missing url parameters
  138. if( !url ) {
  139. if($module.is('form')) {
  140. module.debug('No url or action specified, defaulting to form action');
  141. url = $module.attr('action');
  142. }
  143. else {
  144. module.error(error.missingURL, settings.action);
  145. return;
  146. }
  147. }
  148. // add loading state
  149. module.set.loading();
  150. // look for jQuery ajax parameters in settings
  151. ajaxSettings = $.extend(true, {}, settings, {
  152. type : settings.method || settings.type,
  153. data : data,
  154. url : settings.base + url,
  155. beforeSend : settings.beforeXHR,
  156. success : function() {},
  157. failure : function() {},
  158. complete : function() {}
  159. });
  160. module.debug('Querying URL', ajaxSettings.url);
  161. module.debug('Sending data', data, ajaxSettings.method);
  162. module.verbose('Using AJAX settings', ajaxSettings);
  163. if( module.is.loading() ) {
  164. // throttle additional requests
  165. module.timer = setTimeout(function() {
  166. module.request = module.create.request();
  167. module.xhr = module.create.xhr();
  168. settings.onRequest.call(context, module.request, module.xhr);
  169. }, settings.throttle);
  170. }
  171. else {
  172. // immediately on first request
  173. module.request = module.create.request();
  174. module.xhr = module.create.xhr();
  175. settings.onRequest.call(context, module.request, module.xhr);
  176. }
  177. },
  178. is: {
  179. disabled: function() {
  180. return ($module.filter(settings.filter).length > 0);
  181. },
  182. loading: function() {
  183. return (module.request && module.request.state() == 'pending');
  184. }
  185. },
  186. was: {
  187. cancelled: function() {
  188. return (module.cancelled || false);
  189. },
  190. succesful: function() {
  191. return (module.request && module.request.state() == 'resolved');
  192. },
  193. failure: function() {
  194. return (module.request && module.request.state() == 'rejected');
  195. },
  196. complete: function() {
  197. return (module.request && (module.request.state() == 'resolved' || module.request.state() == 'rejected') );
  198. }
  199. },
  200. add: {
  201. urlData: function(url, urlData) {
  202. var
  203. requiredVariables,
  204. optionalVariables
  205. ;
  206. if(url) {
  207. requiredVariables = url.match(settings.regExp.required);
  208. optionalVariables = url.match(settings.regExp.optional);
  209. urlData = urlData || settings.urlData;
  210. if(requiredVariables) {
  211. module.debug('Looking for required URL variables', requiredVariables);
  212. $.each(requiredVariables, function(index, templatedString) {
  213. var
  214. // allow legacy {$var} style
  215. variable = (templatedString.indexOf('$') !== -1)
  216. ? templatedString.substr(2, templatedString.length - 3)
  217. : templatedString.substr(1, templatedString.length - 2),
  218. value = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
  219. ? urlData[variable]
  220. : ($module.data(variable) !== undefined)
  221. ? $module.data(variable)
  222. : ($context.data(variable) !== undefined)
  223. ? $context.data(variable)
  224. : urlData[variable]
  225. ;
  226. // remove value
  227. if(value === undefined) {
  228. module.error(error.requiredParameter, variable, url);
  229. url = false;
  230. return false;
  231. }
  232. else {
  233. module.verbose('Found required variable', variable, value);
  234. url = url.replace(templatedString, value);
  235. }
  236. });
  237. }
  238. if(optionalVariables) {
  239. module.debug('Looking for optional URL variables', requiredVariables);
  240. $.each(optionalVariables, function(index, templatedString) {
  241. var
  242. // allow legacy {/$var} style
  243. variable = (templatedString.indexOf('$') !== -1)
  244. ? templatedString.substr(3, templatedString.length - 4)
  245. : templatedString.substr(2, templatedString.length - 3),
  246. value = ($.isPlainObject(urlData) && urlData[variable] !== undefined)
  247. ? urlData[variable]
  248. : ($module.data(variable) !== undefined)
  249. ? $module.data(variable)
  250. : ($context.data(variable) !== undefined)
  251. ? $context.data(variable)
  252. : urlData[variable]
  253. ;
  254. // optional replacement
  255. if(value !== undefined) {
  256. module.verbose('Optional variable Found', variable, value);
  257. url = url.replace(templatedString, value);
  258. }
  259. else {
  260. module.verbose('Optional variable not found', variable);
  261. // remove preceding slash if set
  262. if(url.indexOf('/' + templatedString) !== -1) {
  263. url = url.replace('/' + templatedString, '');
  264. }
  265. else {
  266. url = url.replace(templatedString, '');
  267. }
  268. }
  269. });
  270. }
  271. }
  272. return url;
  273. }
  274. },
  275. event: {
  276. trigger: function(event) {
  277. module.query();
  278. if(event.type == 'submit' || event.type == 'click') {
  279. event.preventDefault();
  280. }
  281. },
  282. xhr: {
  283. always: function() {
  284. // calculate if loading time was below minimum threshold
  285. },
  286. done: function(response) {
  287. var
  288. context = this,
  289. elapsedTime = (new Date().getTime() - time),
  290. timeLeft = (settings.loadingDuration - elapsedTime)
  291. ;
  292. timeLeft = (timeLeft > 0)
  293. ? timeLeft
  294. : 0
  295. ;
  296. setTimeout(function() {
  297. module.request.resolveWith(context, [response]);
  298. }, timeLeft);
  299. },
  300. fail: function(xhr, status, httpMessage) {
  301. var
  302. context = this,
  303. elapsedTime = (new Date().getTime() - time),
  304. timeLeft = (settings.loadingDuration - elapsedTime)
  305. ;
  306. timeLeft = (timeLeft > 0)
  307. ? timeLeft
  308. : 0
  309. ;
  310. // page triggers abort on navigation, dont show error
  311. setTimeout(function() {
  312. if(status !== 'abort') {
  313. module.request.rejectWith(context, [xhr, status, httpMessage]);
  314. }
  315. else {
  316. module.reset();
  317. }
  318. }, timeLeft);
  319. }
  320. },
  321. request: {
  322. complete: function(response) {
  323. module.remove.loading();
  324. settings.onComplete.call(context, response, $module);
  325. },
  326. done: function(response) {
  327. module.debug('API Response Received', response);
  328. if(settings.dataType == 'json') {
  329. if( $.isFunction(settings.successTest) ) {
  330. module.debug('Checking JSON returned success', settings.successTest, response);
  331. if( settings.successTest(response) ) {
  332. settings.onSuccess.call(context, response, $module);
  333. }
  334. else {
  335. module.debug('JSON test specified by user and response failed', response);
  336. settings.onFailure.call(context, response, $module);
  337. }
  338. }
  339. else {
  340. settings.onSuccess.call(context, response, $module);
  341. }
  342. }
  343. else {
  344. settings.onSuccess.call(context, response, $module);
  345. }
  346. },
  347. error: function(xhr, status, httpMessage) {
  348. var
  349. errorMessage = (settings.error[status] !== undefined)
  350. ? settings.error[status]
  351. : httpMessage,
  352. response
  353. ;
  354. // let em know unless request aborted
  355. if(xhr !== undefined) {
  356. // readyState 4 = done, anything less is not really sent
  357. if(xhr.readyState !== undefined && xhr.readyState == 4) {
  358. // if http status code returned and json returned error, look for it
  359. if( xhr.status != 200 && httpMessage !== undefined && httpMessage !== '') {
  360. module.error(error.statusMessage + httpMessage, ajaxSettings.url);
  361. }
  362. else {
  363. if(status == 'error' && settings.dataType == 'json') {
  364. try {
  365. response = $.parseJSON(xhr.responseText);
  366. if(response && response.error !== undefined) {
  367. errorMessage = response.error;
  368. }
  369. }
  370. catch(e) {
  371. module.error(error.JSONParse);
  372. }
  373. }
  374. }
  375. module.remove.loading();
  376. module.set.error();
  377. // show error state only for duration specified in settings
  378. if(settings.errorDuration) {
  379. setTimeout(module.remove.error, settings.errorDuration);
  380. }
  381. module.debug('API Request error:', errorMessage);
  382. settings.onError.call(context, errorMessage, $module);
  383. }
  384. else {
  385. settings.onAbort.call(context, errorMessage, $module);
  386. module.debug('Request Aborted (Most likely caused by page change or CORS Policy)', status, httpMessage);
  387. }
  388. }
  389. }
  390. }
  391. },
  392. create: {
  393. request: function() {
  394. return $.Deferred()
  395. .always(module.event.request.complete)
  396. .done(module.event.request.done)
  397. .fail(module.event.request.error)
  398. ;
  399. },
  400. xhr: function() {
  401. return $.ajax(ajaxSettings)
  402. .always(module.event.xhr.always)
  403. .done(module.event.xhr.done)
  404. .fail(module.event.xhr.fail)
  405. ;
  406. }
  407. },
  408. set: {
  409. error: function() {
  410. module.verbose('Adding error state to element', $context);
  411. $context.addClass(className.error);
  412. },
  413. loading: function() {
  414. module.verbose('Adding loading state to element', $context);
  415. $context.addClass(className.loading);
  416. }
  417. },
  418. remove: {
  419. error: function() {
  420. module.verbose('Removing error state from element', $context);
  421. $context.removeClass(className.error);
  422. },
  423. loading: function() {
  424. module.verbose('Removing loading state from element', $context);
  425. $context.removeClass(className.loading);
  426. }
  427. },
  428. get: {
  429. request: function() {
  430. return module.request || false;
  431. },
  432. xhr: function() {
  433. return module.xhr || false;
  434. },
  435. settings: function() {
  436. var
  437. runSettings
  438. ;
  439. runSettings = settings.beforeSend.call($module, settings);
  440. if(runSettings) {
  441. if(runSettings.success !== undefined) {
  442. module.debug('Legacy success callback detected', runSettings);
  443. module.error(error.legacyParameters, runSettings.success);
  444. runSettings.onSuccess = runSettings.success;
  445. }
  446. if(runSettings.failure !== undefined) {
  447. module.debug('Legacy failure callback detected', runSettings);
  448. module.error(error.legacyParameters, runSettings.failure);
  449. runSettings.onFailure = runSettings.failure;
  450. }
  451. if(runSettings.complete !== undefined) {
  452. module.debug('Legacy complete callback detected', runSettings);
  453. module.error(error.legacyParameters, runSettings.complete);
  454. runSettings.onComplete = runSettings.complete;
  455. }
  456. }
  457. if(runSettings === undefined) {
  458. module.error(error.noReturnedValue);
  459. }
  460. return (runSettings !== undefined)
  461. ? runSettings
  462. : settings
  463. ;
  464. },
  465. defaultData: function() {
  466. var
  467. data = {}
  468. ;
  469. if( !$.isWindow(element) ) {
  470. if( $module.is('input') ) {
  471. data.value = $module.val();
  472. }
  473. else if( $module.is('form') ) {
  474. }
  475. else {
  476. data.text = $module.text();
  477. }
  478. }
  479. return data;
  480. },
  481. event: function() {
  482. if( $.isWindow(element) || settings.on == 'now' ) {
  483. module.debug('API called without element, no events attached');
  484. return false;
  485. }
  486. else if(settings.on == 'auto') {
  487. if( $module.is('input') ) {
  488. return (element.oninput !== undefined)
  489. ? 'input'
  490. : (element.onpropertychange !== undefined)
  491. ? 'propertychange'
  492. : 'keyup'
  493. ;
  494. }
  495. else if( $module.is('form') ) {
  496. return 'submit';
  497. }
  498. else {
  499. return 'click';
  500. }
  501. }
  502. else {
  503. return settings.on;
  504. }
  505. },
  506. formData: function() {
  507. var
  508. formData
  509. ;
  510. if($(this).serializeObject() !== undefined) {
  511. formData = $form.serializeObject();
  512. }
  513. else {
  514. module.error(error.missingSerialize);
  515. formData = $form.serialize();
  516. }
  517. module.debug('Retrieved form data', formData);
  518. return formData;
  519. },
  520. templateURL: function(action) {
  521. var
  522. url
  523. ;
  524. action = action || $module.data(metadata.action) || settings.action || false;
  525. if(action) {
  526. module.debug('Looking up url for action', action, settings.api);
  527. if(settings.api[action] !== undefined) {
  528. url = settings.api[action];
  529. module.debug('Found template url', url);
  530. }
  531. else {
  532. module.error(error.missingAction, settings.action, settings.api);
  533. }
  534. }
  535. return url;
  536. }
  537. },
  538. abort: function() {
  539. var
  540. xhr = module.get.xhr()
  541. ;
  542. if( xhr && xhr.state() !== 'resolved') {
  543. module.debug('Cancelling API request');
  544. xhr.abort();
  545. module.request.rejectWith(settings.apiSettings);
  546. }
  547. },
  548. // reset state
  549. reset: function() {
  550. module.remove.error();
  551. module.remove.loading();
  552. },
  553. setting: function(name, value) {
  554. module.debug('Changing setting', name, value);
  555. if( $.isPlainObject(name) ) {
  556. $.extend(true, settings, name);
  557. }
  558. else if(value !== undefined) {
  559. settings[name] = value;
  560. }
  561. else {
  562. return settings[name];
  563. }
  564. },
  565. internal: function(name, value) {
  566. if( $.isPlainObject(name) ) {
  567. $.extend(true, module, name);
  568. }
  569. else if(value !== undefined) {
  570. module[name] = value;
  571. }
  572. else {
  573. return module[name];
  574. }
  575. },
  576. debug: function() {
  577. if(settings.debug) {
  578. if(settings.performance) {
  579. module.performance.log(arguments);
  580. }
  581. else {
  582. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  583. module.debug.apply(console, arguments);
  584. }
  585. }
  586. },
  587. verbose: function() {
  588. if(settings.verbose && settings.debug) {
  589. if(settings.performance) {
  590. module.performance.log(arguments);
  591. }
  592. else {
  593. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  594. module.verbose.apply(console, arguments);
  595. }
  596. }
  597. },
  598. error: function() {
  599. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  600. module.error.apply(console, arguments);
  601. },
  602. performance: {
  603. log: function(message) {
  604. var
  605. currentTime,
  606. executionTime,
  607. previousTime
  608. ;
  609. if(settings.performance) {
  610. currentTime = new Date().getTime();
  611. previousTime = time || currentTime;
  612. executionTime = currentTime - previousTime;
  613. time = currentTime;
  614. performance.push({
  615. 'Name' : message[0],
  616. 'Arguments' : [].slice.call(message, 1) || '',
  617. //'Element' : element,
  618. 'Execution Time' : executionTime
  619. });
  620. }
  621. clearTimeout(module.performance.timer);
  622. module.performance.timer = setTimeout(module.performance.display, 100);
  623. },
  624. display: function() {
  625. var
  626. title = settings.name + ':',
  627. totalTime = 0
  628. ;
  629. time = false;
  630. clearTimeout(module.performance.timer);
  631. $.each(performance, function(index, data) {
  632. totalTime += data['Execution Time'];
  633. });
  634. title += ' ' + totalTime + 'ms';
  635. if(moduleSelector) {
  636. title += ' \'' + moduleSelector + '\'';
  637. }
  638. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  639. console.groupCollapsed(title);
  640. if(console.table) {
  641. console.table(performance);
  642. }
  643. else {
  644. $.each(performance, function(index, data) {
  645. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  646. });
  647. }
  648. console.groupEnd();
  649. }
  650. performance = [];
  651. }
  652. },
  653. invoke: function(query, passedArguments, context) {
  654. var
  655. object = instance,
  656. maxDepth,
  657. found,
  658. response
  659. ;
  660. passedArguments = passedArguments || queryArguments;
  661. context = element || context;
  662. if(typeof query == 'string' && object !== undefined) {
  663. query = query.split(/[\. ]/);
  664. maxDepth = query.length - 1;
  665. $.each(query, function(depth, value) {
  666. var camelCaseValue = (depth != maxDepth)
  667. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  668. : query
  669. ;
  670. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  671. object = object[camelCaseValue];
  672. }
  673. else if( object[camelCaseValue] !== undefined ) {
  674. found = object[camelCaseValue];
  675. return false;
  676. }
  677. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  678. object = object[value];
  679. }
  680. else if( object[value] !== undefined ) {
  681. found = object[value];
  682. return false;
  683. }
  684. else {
  685. module.error(error.method, query);
  686. return false;
  687. }
  688. });
  689. }
  690. if ( $.isFunction( found ) ) {
  691. response = found.apply(context, passedArguments);
  692. }
  693. else if(found !== undefined) {
  694. response = found;
  695. }
  696. if($.isArray(returnedValue)) {
  697. returnedValue.push(response);
  698. }
  699. else if(returnedValue !== undefined) {
  700. returnedValue = [returnedValue, response];
  701. }
  702. else if(response !== undefined) {
  703. returnedValue = response;
  704. }
  705. return found;
  706. }
  707. };
  708. if(methodInvoked) {
  709. if(instance === undefined) {
  710. module.initialize();
  711. }
  712. module.invoke(query);
  713. }
  714. else {
  715. if(instance !== undefined) {
  716. instance.invoke('destroy');
  717. }
  718. module.initialize();
  719. }
  720. })
  721. ;
  722. return (returnedValue !== undefined)
  723. ? returnedValue
  724. : this
  725. ;
  726. };
  727. $.api.settings = {
  728. name : 'API',
  729. namespace : 'api',
  730. debug : true,
  731. verbose : false,
  732. performance : true,
  733. // event binding
  734. on : 'auto',
  735. filter : '.disabled',
  736. stateContext : false,
  737. // state
  738. loadingDuration : 0,
  739. errorDuration : 2000,
  740. // templating
  741. action : false,
  742. url : false,
  743. base : '',
  744. // data
  745. urlData : {},
  746. // ui
  747. defaultData : true,
  748. serializeForm : false,
  749. throttle : 0,
  750. // jQ ajax
  751. method : 'get',
  752. data : {},
  753. dataType : 'json',
  754. // callbacks
  755. beforeSend : function(settings) { return settings; },
  756. beforeXHR : function(xhr) {},
  757. onRequest : function(promise, xhr) {},
  758. onSuccess : function(response, $module) {},
  759. onComplete : function(response, $module) {},
  760. onFailure : function(errorMessage, $module) {},
  761. onError : function(errorMessage, $module) {},
  762. onAbort : function(errorMessage, $module) {},
  763. successTest : false,
  764. // errors
  765. error : {
  766. beforeSend : 'The before send function has aborted the request',
  767. error : 'There was an error with your request',
  768. exitConditions : 'API Request Aborted. Exit conditions met',
  769. JSONParse : 'JSON could not be parsed during error handling',
  770. legacyParameters : 'You are using legacy API success callback names',
  771. method : 'The method you called is not defined',
  772. missingAction : 'API action used but no url was defined',
  773. missingSerialize : 'Required dependency jquery-serialize-object missing, using basic serialize',
  774. missingURL : 'No URL specified for api event',
  775. noReturnedValue : 'The beforeSend callback must return a settings object, beforeSend ignored.',
  776. parseError : 'There was an error parsing your request',
  777. requiredParameter : 'Missing a required URL parameter: ',
  778. statusMessage : 'Server gave an error: ',
  779. timeout : 'Your request timed out'
  780. },
  781. regExp : {
  782. required: /\{\$*[A-z0-9]+\}/g,
  783. optional: /\{\/\$*[A-z0-9]+\}/g,
  784. },
  785. className: {
  786. loading : 'loading',
  787. error : 'error'
  788. },
  789. selector: {
  790. form: 'form'
  791. },
  792. metadata: {
  793. action : 'action'
  794. }
  795. };
  796. $.api.settings.api = {};
  797. })( jQuery, window , document );