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.

644 lines
20 KiB

10 years ago
  1. /*
  2. * # Semantic - Progress
  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. "use strict";
  13. $.fn.progress = function(parameters) {
  14. var
  15. $allModules = $(this),
  16. moduleSelector = $allModules.selector || '',
  17. hasTouch = ('ontouchstart' in document.documentElement),
  18. time = new Date().getTime(),
  19. performance = [],
  20. query = arguments[0],
  21. methodInvoked = (typeof query == 'string'),
  22. queryArguments = [].slice.call(arguments, 1),
  23. returnedValue
  24. ;
  25. $allModules
  26. .each(function() {
  27. var
  28. settings = ( $.isPlainObject(parameters) )
  29. ? $.extend(true, {}, $.fn.progress.settings, parameters)
  30. : $.extend({}, $.fn.progress.settings),
  31. className = settings.className,
  32. metadata = settings.metadata,
  33. namespace = settings.namespace,
  34. selector = settings.selector,
  35. error = settings.error,
  36. eventNamespace = '.' + namespace,
  37. moduleNamespace = 'module-' + namespace,
  38. $module = $(this),
  39. $bar = $(this).find(selector.bar),
  40. $progress = $(this).find(selector.progress),
  41. $label = $(this).find(selector.label),
  42. element = this,
  43. instance = $module.data(moduleNamespace),
  44. module
  45. ;
  46. module = {
  47. initialize: function() {
  48. module.debug('Initializing progress', settings);
  49. module.read.metadata();
  50. module.set.initials();
  51. module.instantiate();
  52. },
  53. instantiate: function() {
  54. module.verbose('Storing instance of progress', module);
  55. instance = module;
  56. $module
  57. .data(moduleNamespace, module)
  58. ;
  59. },
  60. destroy: function() {
  61. module.verbose('Destroying previous dropdown for', $module);
  62. $module
  63. .removeData(moduleNamespace)
  64. ;
  65. instance = undefined;
  66. },
  67. reset: function() {
  68. module.set.percent(0);
  69. },
  70. complete: function() {
  71. if(module.percent === undefined || module.percent < 100) {
  72. module.set.percent(100);
  73. }
  74. },
  75. read: {
  76. metadata: function() {
  77. if( $module.data(metadata.percent) ) {
  78. module.verbose('Current percent value set from metadata');
  79. module.percent = $module.data(metadata.percent);
  80. }
  81. if( $module.data(metadata.total) ) {
  82. module.verbose('Total value set from metadata');
  83. module.total = $module.data(metadata.total);
  84. }
  85. if( $module.data(metadata.value) ) {
  86. module.verbose('Current value set from metadata');
  87. module.value = $module.data(metadata.value);
  88. }
  89. },
  90. currentValue: function() {
  91. return (module.value !== undefined)
  92. ? module.value
  93. : false
  94. ;
  95. }
  96. },
  97. increment: function(incrementValue) {
  98. var
  99. total = module.total || false,
  100. edgeValue,
  101. startValue,
  102. newValue
  103. ;
  104. if(total) {
  105. startValue = module.value || 0;
  106. incrementValue = incrementValue || 1;
  107. newValue = startValue + incrementValue;
  108. edgeValue = module.total;
  109. module.debug('Incrementing value by', incrementValue, startValue, edgeValue);
  110. if(newValue > edgeValue ) {
  111. module.debug('Value cannot increment above total', edgeValue);
  112. newValue = edgeValue;
  113. }
  114. module.set.progress(newValue);
  115. }
  116. else {
  117. startValue = module.percent || 0;
  118. incrementValue = incrementValue || module.get.randomValue();
  119. newValue = startValue + incrementValue;
  120. edgeValue = 100;
  121. module.debug('Incrementing percentage by', incrementValue, startValue);
  122. if(newValue > edgeValue ) {
  123. module.debug('Value cannot increment above 100 percent');
  124. newValue = edgeValue;
  125. }
  126. module.set.progress(newValue);
  127. }
  128. },
  129. decrement: function(decrementValue) {
  130. var
  131. total = module.total || false,
  132. edgeValue = 0,
  133. startValue,
  134. newValue
  135. ;
  136. if(total) {
  137. startValue = module.value || 0;
  138. decrementValue = decrementValue || 1;
  139. newValue = startValue - decrementValue;
  140. module.debug('Decrementing value by', decrementValue, startValue);
  141. }
  142. else {
  143. startValue = module.percent || 0;
  144. decrementValue = decrementValue || module.get.randomValue();
  145. newValue = startValue - decrementValue;
  146. module.debug('Decrementing percentage by', decrementValue, startValue);
  147. }
  148. if(newValue < edgeValue) {
  149. module.debug('Value cannot decrement below 0');
  150. newValue = 0;
  151. }
  152. module.set.progress(newValue);
  153. },
  154. get: {
  155. text: function(templateText) {
  156. var
  157. value = module.value || 0,
  158. total = module.total || 0,
  159. percent = module.percent || 0
  160. ;
  161. templateText = templateText || '';
  162. templateText = templateText
  163. .replace('{value}', value)
  164. .replace('{total}', total)
  165. .replace('{percent}', percent)
  166. ;
  167. module.debug('Adding variables to progress bar text', templateText);
  168. return templateText;
  169. },
  170. randomValue: function() {
  171. module.debug('Generating random increment percentage');
  172. return Math.floor((Math.random() * settings.random.max) + settings.random.min);
  173. },
  174. percent: function() {
  175. return module.percent || 0;
  176. },
  177. value: function() {
  178. return module.value || false;
  179. },
  180. total: function() {
  181. return module.total || false;
  182. }
  183. },
  184. is: {
  185. success: function() {
  186. return $module.hasClass(className.success);
  187. },
  188. warning: function() {
  189. return $module.hasClass(className.warning);
  190. },
  191. error: function() {
  192. return $module.hasClass(className.error);
  193. }
  194. },
  195. remove: {
  196. active: function() {
  197. module.verbose('Removing active state');
  198. $module.removeClass(className.active);
  199. },
  200. success: function() {
  201. module.verbose('Removing success state');
  202. $module.removeClass(className.success);
  203. },
  204. warning: function() {
  205. module.verbose('Removing warning state');
  206. $module.removeClass(className.warning);
  207. },
  208. error: function() {
  209. module.verbose('Removing error state');
  210. $module.removeClass(className.error);
  211. }
  212. },
  213. set: {
  214. barWidth: function(value) {
  215. if(value > 100) {
  216. module.error(error.tooHigh, value);
  217. }
  218. $bar
  219. .css('width', value + '%')
  220. ;
  221. },
  222. initials: function() {
  223. if(settings.value) {
  224. module.verbose('Current value set in settings', settings.value);
  225. module.value = settings.value;
  226. }
  227. if(settings.total) {
  228. module.verbose('Current total set in settings', settings.total);
  229. module.total = settings.total;
  230. }
  231. if(settings.percent) {
  232. module.verbose('Current percent set in settings', settings.percent);
  233. module.percent = settings.percent;
  234. }
  235. if(module.percent) {
  236. module.set.percent(module.percent);
  237. }
  238. else if(module.value) {
  239. module.set.progress(module.value);
  240. }
  241. },
  242. percent: function(percent) {
  243. percent = (typeof percent == 'string')
  244. ? +(percent.replace('%', ''))
  245. : percent
  246. ;
  247. if(percent > 0 && percent < 1) {
  248. module.verbose('Module percentage passed as decimal, converting');
  249. percent = percent * 100;
  250. }
  251. // round percentage
  252. if(settings.precision === 0) {
  253. percent = Math.round(percent);
  254. }
  255. else {
  256. percent = Math.round(percent * (10 * settings.precision) / (10 * settings.precision) );
  257. }
  258. module.percent = percent;
  259. if(module.total) {
  260. module.value = Math.round( (percent / 100) * module.total);
  261. }
  262. module.set.barWidth(percent);
  263. module.set.barLabel();
  264. if(percent === 100) {
  265. if(settings.autoSuccess && !(module.is.warning() || module.is.error())) {
  266. module.set.success();
  267. module.debug('Automatically triggering success at 100%');
  268. }
  269. else {
  270. module.remove.active();
  271. }
  272. }
  273. else {
  274. module.set.active();
  275. }
  276. $.proxy(settings.onChange, element)(percent, module.value, module.total);
  277. },
  278. label: function(text) {
  279. text = text || '';
  280. if(text) {
  281. text = module.get.text(text);
  282. module.debug('Setting label to text', text);
  283. $label.text(text);
  284. }
  285. },
  286. barLabel: function(text) {
  287. if(text !== undefined) {
  288. $progress.text( module.get.text(text) );
  289. }
  290. else if(settings.label == 'ratio' && module.total) {
  291. module.debug('Adding ratio to bar label');
  292. $progress.text( module.get.text(settings.text.ratio) );
  293. }
  294. else if(settings.label == 'percent') {
  295. module.debug('Adding percentage to bar label');
  296. $progress.text( module.get.text(settings.text.percent) );
  297. }
  298. },
  299. active: function(text) {
  300. text = text || settings.text.active;
  301. module.debug('Setting active state');
  302. if(settings.showActivity) {
  303. $module.addClass(className.active);
  304. }
  305. module.remove.warning();
  306. module.remove.error();
  307. module.remove.success();
  308. if(text) {
  309. module.set.label(text);
  310. }
  311. },
  312. success : function(text) {
  313. text = text || settings.text.success;
  314. module.debug('Setting success state');
  315. $module.addClass(className.success);
  316. module.remove.active();
  317. module.remove.warning();
  318. module.remove.error();
  319. module.complete();
  320. if(text) {
  321. module.set.label(text);
  322. }
  323. },
  324. warning : function(text) {
  325. text = text || settings.text.warning;
  326. module.debug('Setting warning state');
  327. $module.addClass(className.warning);
  328. module.remove.active();
  329. module.remove.success();
  330. module.remove.error();
  331. module.complete();
  332. if(text) {
  333. module.set.label(text);
  334. }
  335. },
  336. error : function(text) {
  337. text = text || settings.text.error;
  338. module.debug('Setting error state');
  339. $module.addClass(className.error);
  340. module.remove.active();
  341. module.remove.success();
  342. module.remove.warning();
  343. module.complete();
  344. if(text) {
  345. module.set.label(text);
  346. }
  347. },
  348. total: function(totalValue) {
  349. module.total = totalValue;
  350. },
  351. progress: function(value) {
  352. var
  353. numericValue = (typeof value === 'string')
  354. ? (value.replace(/[^\d.]/g, '') !== '')
  355. ? +(value.replace(/[^\d.]/g, ''))
  356. : false
  357. : value,
  358. percentComplete
  359. ;
  360. if(!numericValue) {
  361. module.error(error.nonNumeric);
  362. }
  363. if(module.total) {
  364. module.value = numericValue;
  365. percentComplete = (numericValue / module.total) * 100;
  366. module.debug('Calculating percent complete from total', percentComplete);
  367. module.set.percent( percentComplete );
  368. }
  369. else {
  370. percentComplete = numericValue;
  371. module.debug('Setting value to exact percentage value', percentComplete);
  372. module.set.percent( percentComplete );
  373. }
  374. }
  375. },
  376. setting: function(name, value) {
  377. module.debug('Changing setting', name, value);
  378. if( $.isPlainObject(name) ) {
  379. $.extend(true, settings, name);
  380. }
  381. else if(value !== undefined) {
  382. settings[name] = value;
  383. }
  384. else {
  385. return settings[name];
  386. }
  387. },
  388. internal: function(name, value) {
  389. if( $.isPlainObject(name) ) {
  390. $.extend(true, module, name);
  391. }
  392. else if(value !== undefined) {
  393. module[name] = value;
  394. }
  395. else {
  396. return module[name];
  397. }
  398. },
  399. debug: function() {
  400. if(settings.debug) {
  401. if(settings.performance) {
  402. module.performance.log(arguments);
  403. }
  404. else {
  405. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  406. module.debug.apply(console, arguments);
  407. }
  408. }
  409. },
  410. verbose: function() {
  411. if(settings.verbose && settings.debug) {
  412. if(settings.performance) {
  413. module.performance.log(arguments);
  414. }
  415. else {
  416. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  417. module.verbose.apply(console, arguments);
  418. }
  419. }
  420. },
  421. error: function() {
  422. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  423. module.error.apply(console, arguments);
  424. },
  425. performance: {
  426. log: function(message) {
  427. var
  428. currentTime,
  429. executionTime,
  430. previousTime
  431. ;
  432. if(settings.performance) {
  433. currentTime = new Date().getTime();
  434. previousTime = time || currentTime;
  435. executionTime = currentTime - previousTime;
  436. time = currentTime;
  437. performance.push({
  438. 'Name' : message[0],
  439. 'Arguments' : [].slice.call(message, 1) || '',
  440. 'Element' : element,
  441. 'Execution Time' : executionTime
  442. });
  443. }
  444. clearTimeout(module.performance.timer);
  445. module.performance.timer = setTimeout(module.performance.display, 100);
  446. },
  447. display: function() {
  448. var
  449. title = settings.name + ':',
  450. totalTime = 0
  451. ;
  452. time = false;
  453. clearTimeout(module.performance.timer);
  454. $.each(performance, function(index, data) {
  455. totalTime += data['Execution Time'];
  456. });
  457. title += ' ' + totalTime + 'ms';
  458. if(moduleSelector) {
  459. title += ' \'' + moduleSelector + '\'';
  460. }
  461. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  462. console.groupCollapsed(title);
  463. if(console.table) {
  464. console.table(performance);
  465. }
  466. else {
  467. $.each(performance, function(index, data) {
  468. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  469. });
  470. }
  471. console.groupEnd();
  472. }
  473. performance = [];
  474. }
  475. },
  476. invoke: function(query, passedArguments, context) {
  477. var
  478. object = instance,
  479. maxDepth,
  480. found,
  481. response
  482. ;
  483. passedArguments = passedArguments || queryArguments;
  484. context = element || context;
  485. if(typeof query == 'string' && object !== undefined) {
  486. query = query.split(/[\. ]/);
  487. maxDepth = query.length - 1;
  488. $.each(query, function(depth, value) {
  489. var camelCaseValue = (depth != maxDepth)
  490. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  491. : query
  492. ;
  493. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  494. object = object[camelCaseValue];
  495. }
  496. else if( object[camelCaseValue] !== undefined ) {
  497. found = object[camelCaseValue];
  498. return false;
  499. }
  500. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  501. object = object[value];
  502. }
  503. else if( object[value] !== undefined ) {
  504. found = object[value];
  505. return false;
  506. }
  507. else {
  508. module.error(error.method, query);
  509. return false;
  510. }
  511. });
  512. }
  513. if ( $.isFunction( found ) ) {
  514. response = found.apply(context, passedArguments);
  515. }
  516. else if(found !== undefined) {
  517. response = found;
  518. }
  519. if($.isArray(returnedValue)) {
  520. returnedValue.push(response);
  521. }
  522. else if(returnedValue !== undefined) {
  523. returnedValue = [returnedValue, response];
  524. }
  525. else if(response !== undefined) {
  526. returnedValue = response;
  527. }
  528. return found;
  529. }
  530. };
  531. if(methodInvoked) {
  532. if(instance === undefined) {
  533. module.initialize();
  534. }
  535. module.invoke(query);
  536. }
  537. else {
  538. if(instance !== undefined) {
  539. module.destroy();
  540. }
  541. module.initialize();
  542. }
  543. })
  544. ;
  545. return (returnedValue !== undefined)
  546. ? returnedValue
  547. : this
  548. ;
  549. };
  550. $.fn.progress.settings = {
  551. name : 'Progress',
  552. namespace : 'progress',
  553. debug : false,
  554. verbose : true,
  555. performance : true,
  556. random : {
  557. min : 2,
  558. max : 5
  559. },
  560. autoSuccess : true,
  561. showActivity : true,
  562. label : 'percent',
  563. precision : 1,
  564. percent : false,
  565. total : false,
  566. value : false,
  567. onChange : function(percent, value, total){},
  568. error : {
  569. method : 'The method you called is not defined.',
  570. nonNumeric : 'Progress value is non numeric'
  571. },
  572. regExp: {
  573. variable: /\{\$*[A-z0-9]+\}/g
  574. },
  575. metadata: {
  576. percent : 'percent',
  577. total : 'total',
  578. value : 'value'
  579. },
  580. selector : {
  581. bar : '> .bar',
  582. label : '> .label',
  583. progress : '.bar > .progress'
  584. },
  585. text : {
  586. active : false,
  587. error : false,
  588. success : false,
  589. warning : false,
  590. percent : '{percent}%',
  591. ratio : '{value} of {total}'
  592. },
  593. className : {
  594. active : 'active',
  595. error : 'error',
  596. success : 'success',
  597. warning : 'warning'
  598. }
  599. };
  600. })( jQuery, window , document );