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.

1012 lines
33 KiB

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