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.

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