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.

864 lines
28 KiB

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