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.

792 lines
25 KiB

  1. /*!
  2. * # Semantic UI 2.0.0 - Progress
  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. $.fn.progress = function(parameters) {
  14. var
  15. $allModules = $(this),
  16. moduleSelector = $allModules.selector || '',
  17. time = new Date().getTime(),
  18. performance = [],
  19. query = arguments[0],
  20. methodInvoked = (typeof query == 'string'),
  21. queryArguments = [].slice.call(arguments, 1),
  22. returnedValue
  23. ;
  24. $allModules
  25. .each(function() {
  26. var
  27. settings = ( $.isPlainObject(parameters) )
  28. ? $.extend(true, {}, $.fn.progress.settings, parameters)
  29. : $.extend({}, $.fn.progress.settings),
  30. className = settings.className,
  31. metadata = settings.metadata,
  32. namespace = settings.namespace,
  33. selector = settings.selector,
  34. error = settings.error,
  35. eventNamespace = '.' + namespace,
  36. moduleNamespace = 'module-' + namespace,
  37. $module = $(this),
  38. $bar = $(this).find(selector.bar),
  39. $progress = $(this).find(selector.progress),
  40. $label = $(this).find(selector.label),
  41. element = this,
  42. instance = $module.data(moduleNamespace),
  43. animating = false,
  44. transitionEnd,
  45. module
  46. ;
  47. module = {
  48. initialize: function() {
  49. module.debug('Initializing progress bar', settings);
  50. module.set.duration();
  51. module.set.transitionEvent();
  52. module.read.metadata();
  53. module.read.settings();
  54. module.instantiate();
  55. },
  56. instantiate: function() {
  57. module.verbose('Storing instance of progress', module);
  58. instance = module;
  59. $module
  60. .data(moduleNamespace, module)
  61. ;
  62. },
  63. destroy: function() {
  64. module.verbose('Destroying previous progress for', $module);
  65. clearInterval(instance.interval);
  66. module.remove.state();
  67. $module.removeData(moduleNamespace);
  68. instance = undefined;
  69. },
  70. reset: function() {
  71. module.set.percent(0);
  72. },
  73. complete: function() {
  74. if(module.percent === undefined || module.percent < 100) {
  75. module.set.percent(100);
  76. }
  77. },
  78. read: {
  79. metadata: function() {
  80. var
  81. data = {
  82. percent : $module.data(metadata.percent),
  83. total : $module.data(metadata.total),
  84. value : $module.data(metadata.value)
  85. }
  86. ;
  87. if(data.percent) {
  88. module.debug('Current percent value set from metadata', data.percent);
  89. module.set.percent(data.percent);
  90. }
  91. if(data.total) {
  92. module.debug('Total value set from metadata', data.total);
  93. module.set.total(data.total);
  94. }
  95. if(data.value) {
  96. module.debug('Current value set from metadata', data.value);
  97. module.set.value(data.value);
  98. }
  99. },
  100. settings: function() {
  101. if(settings.total !== false) {
  102. module.debug('Current total set in settings', settings.total);
  103. module.set.total(settings.total);
  104. }
  105. if(settings.value !== false) {
  106. module.debug('Current value set in settings', settings.value);
  107. module.set.value(settings.value);
  108. module.set.progress(module.value);
  109. }
  110. if(settings.percent !== false) {
  111. module.debug('Current percent set in settings', settings.percent);
  112. module.set.percent(settings.percent);
  113. }
  114. }
  115. },
  116. increment: function(incrementValue) {
  117. var
  118. maxValue,
  119. startValue,
  120. newValue
  121. ;
  122. if( module.has.total() ) {
  123. startValue = module.get.value();
  124. incrementValue = incrementValue || 1;
  125. newValue = startValue + incrementValue;
  126. maxValue = module.get.total();
  127. module.debug('Incrementing value', startValue, newValue, maxValue);
  128. if(newValue > maxValue ) {
  129. module.debug('Value cannot increment above total', maxValue);
  130. newValue = maxValue;
  131. }
  132. }
  133. else {
  134. startValue = module.get.percent();
  135. incrementValue = incrementValue || module.get.randomValue();
  136. newValue = startValue + incrementValue;
  137. maxValue = 100;
  138. module.debug('Incrementing percentage by', startValue, newValue);
  139. if(newValue > maxValue ) {
  140. module.debug('Value cannot increment above 100 percent');
  141. newValue = maxValue;
  142. }
  143. }
  144. module.set.progress(newValue);
  145. },
  146. decrement: function(decrementValue) {
  147. var
  148. total = module.get.total(),
  149. startValue,
  150. newValue
  151. ;
  152. if(total) {
  153. startValue = module.get.value();
  154. decrementValue = decrementValue || 1;
  155. newValue = startValue - decrementValue;
  156. module.debug('Decrementing value by', decrementValue, startValue);
  157. }
  158. else {
  159. startValue = module.get.percent();
  160. decrementValue = decrementValue || module.get.randomValue();
  161. newValue = startValue - decrementValue;
  162. module.debug('Decrementing percentage by', decrementValue, startValue);
  163. }
  164. if(newValue < 0) {
  165. module.debug('Value cannot decrement below 0');
  166. newValue = 0;
  167. }
  168. module.set.progress(newValue);
  169. },
  170. has: {
  171. total: function() {
  172. return (module.get.total() !== false);
  173. }
  174. },
  175. get: {
  176. text: function(templateText) {
  177. var
  178. value = module.value || 0,
  179. total = module.total || 0,
  180. percent = (animating)
  181. ? module.get.displayPercent()
  182. : module.percent || 0,
  183. left = (module.total > 0)
  184. ? (total - value)
  185. : (100 - percent)
  186. ;
  187. templateText = templateText || '';
  188. templateText = templateText
  189. .replace('{value}', value)
  190. .replace('{total}', total)
  191. .replace('{left}', left)
  192. .replace('{percent}', percent)
  193. ;
  194. module.debug('Adding variables to progress bar text', templateText);
  195. return templateText;
  196. },
  197. randomValue: function() {
  198. module.debug('Generating random increment percentage');
  199. return Math.floor((Math.random() * settings.random.max) + settings.random.min);
  200. },
  201. numericValue: function(value) {
  202. return (typeof value === 'string')
  203. ? (value.replace(/[^\d.]/g, '') !== '')
  204. ? +(value.replace(/[^\d.]/g, ''))
  205. : false
  206. : value
  207. ;
  208. },
  209. transitionEnd: function() {
  210. var
  211. element = document.createElement('element'),
  212. transitions = {
  213. 'transition' :'transitionend',
  214. 'OTransition' :'oTransitionEnd',
  215. 'MozTransition' :'transitionend',
  216. 'WebkitTransition' :'webkitTransitionEnd'
  217. },
  218. transition
  219. ;
  220. for(transition in transitions){
  221. if( element.style[transition] !== undefined ){
  222. return transitions[transition];
  223. }
  224. }
  225. },
  226. // gets current displayed percentage (if animating values this is the intermediary value)
  227. displayPercent: function() {
  228. var
  229. barWidth = $bar.width(),
  230. totalWidth = $module.width(),
  231. minDisplay = parseInt($bar.css('min-width'), 10),
  232. displayPercent = (barWidth > minDisplay)
  233. ? (barWidth / totalWidth * 100)
  234. : module.percent
  235. ;
  236. return (settings.precision > 0)
  237. ? Math.round(displayPercent * (10 * settings.precision)) / (10 * settings.precision)
  238. : Math.round(displayPercent)
  239. ;
  240. },
  241. percent: function() {
  242. return module.percent || 0;
  243. },
  244. value: function() {
  245. return module.value || 0;
  246. },
  247. total: function() {
  248. return module.total || false;
  249. }
  250. },
  251. is: {
  252. success: function() {
  253. return $module.hasClass(className.success);
  254. },
  255. warning: function() {
  256. return $module.hasClass(className.warning);
  257. },
  258. error: function() {
  259. return $module.hasClass(className.error);
  260. },
  261. active: function() {
  262. return $module.hasClass(className.active);
  263. },
  264. visible: function() {
  265. return $module.is(':visible');
  266. }
  267. },
  268. remove: {
  269. state: function() {
  270. module.verbose('Removing stored state');
  271. delete module.total;
  272. delete module.percent;
  273. delete module.value;
  274. },
  275. active: function() {
  276. module.verbose('Removing active state');
  277. $module.removeClass(className.active);
  278. },
  279. success: function() {
  280. module.verbose('Removing success state');
  281. $module.removeClass(className.success);
  282. },
  283. warning: function() {
  284. module.verbose('Removing warning state');
  285. $module.removeClass(className.warning);
  286. },
  287. error: function() {
  288. module.verbose('Removing error state');
  289. $module.removeClass(className.error);
  290. }
  291. },
  292. set: {
  293. barWidth: function(value) {
  294. if(value > 100) {
  295. module.error(error.tooHigh, value);
  296. }
  297. else if (value < 0) {
  298. module.error(error.tooLow, value);
  299. }
  300. else {
  301. $bar
  302. .css('width', value + '%')
  303. ;
  304. $module
  305. .attr('data-percent', parseInt(value, 10))
  306. ;
  307. }
  308. },
  309. duration: function(duration) {
  310. duration = duration || settings.duration;
  311. duration = (typeof duration == 'number')
  312. ? duration + 'ms'
  313. : duration
  314. ;
  315. module.verbose('Setting progress bar transition duration', duration);
  316. $bar
  317. .css({
  318. 'transition-duration': duration
  319. })
  320. ;
  321. },
  322. percent: function(percent) {
  323. percent = (typeof percent == 'string')
  324. ? +(percent.replace('%', ''))
  325. : percent
  326. ;
  327. // round display percentage
  328. percent = (settings.precision > 0)
  329. ? Math.round(percent * (10 * settings.precision)) / (10 * settings.precision)
  330. : Math.round(percent)
  331. ;
  332. module.percent = percent;
  333. if( !module.has.total() ) {
  334. module.value = (settings.precision > 0)
  335. ? Math.round( (percent / 100) * module.total * (10 * settings.precision)) / (10 * settings.precision)
  336. : Math.round( (percent / 100) * module.total * 10) / 10
  337. ;
  338. if(settings.limitValues) {
  339. module.value = (module.value > 100)
  340. ? 100
  341. : (module.value < 0)
  342. ? 0
  343. : module.value
  344. ;
  345. }
  346. }
  347. module.set.barWidth(percent);
  348. module.set.labelInterval();
  349. module.set.labels();
  350. settings.onChange.call(element, percent, module.value, module.total);
  351. },
  352. labelInterval: function() {
  353. var
  354. animationCallback = function() {
  355. module.verbose('Bar finished animating, removing continuous label updates');
  356. clearInterval(module.interval);
  357. animating = false;
  358. module.set.labels();
  359. }
  360. ;
  361. clearInterval(module.interval);
  362. $bar.one(transitionEnd + eventNamespace, animationCallback);
  363. module.timer = setTimeout(animationCallback, settings.duration + 100);
  364. animating = true;
  365. module.interval = setInterval(module.set.labels, settings.framerate);
  366. },
  367. labels: function() {
  368. module.verbose('Setting both bar progress and outer label text');
  369. module.set.barLabel();
  370. module.set.state();
  371. },
  372. label: function(text) {
  373. text = text || '';
  374. if(text) {
  375. text = module.get.text(text);
  376. module.debug('Setting label to text', text);
  377. $label.text(text);
  378. }
  379. },
  380. state: function(percent) {
  381. percent = (percent !== undefined)
  382. ? percent
  383. : module.percent
  384. ;
  385. if(percent === 100) {
  386. if(settings.autoSuccess && !(module.is.warning() || module.is.error())) {
  387. module.set.success();
  388. module.debug('Automatically triggering success at 100%');
  389. }
  390. else {
  391. module.verbose('Reached 100% removing active state');
  392. module.remove.active();
  393. }
  394. }
  395. else if(percent > 0) {
  396. module.verbose('Adjusting active progress bar label', percent);
  397. module.set.active();
  398. }
  399. else {
  400. module.remove.active();
  401. module.set.label(settings.text.active);
  402. }
  403. },
  404. barLabel: function(text) {
  405. if(text !== undefined) {
  406. $progress.text( module.get.text(text) );
  407. }
  408. else if(settings.label == 'ratio' && module.total) {
  409. module.debug('Adding ratio to bar label');
  410. $progress.text( module.get.text(settings.text.ratio) );
  411. }
  412. else if(settings.label == 'percent') {
  413. module.debug('Adding percentage to bar label');
  414. $progress.text( module.get.text(settings.text.percent) );
  415. }
  416. },
  417. active: function(text) {
  418. text = text || settings.text.active;
  419. module.debug('Setting active state');
  420. if(settings.showActivity && !module.is.active() ) {
  421. $module.addClass(className.active);
  422. }
  423. module.remove.warning();
  424. module.remove.error();
  425. module.remove.success();
  426. if(text) {
  427. module.set.label(text);
  428. }
  429. settings.onActive.call(element, module.value, module.total);
  430. },
  431. success : function(text) {
  432. text = text || settings.text.success;
  433. module.debug('Setting success state');
  434. $module.addClass(className.success);
  435. module.remove.active();
  436. module.remove.warning();
  437. module.remove.error();
  438. module.complete();
  439. if(text) {
  440. module.set.label(text);
  441. }
  442. settings.onSuccess.call(element, module.total);
  443. },
  444. warning : function(text) {
  445. text = text || settings.text.warning;
  446. module.debug('Setting warning state');
  447. $module.addClass(className.warning);
  448. module.remove.active();
  449. module.remove.success();
  450. module.remove.error();
  451. module.complete();
  452. if(text) {
  453. module.set.label(text);
  454. }
  455. settings.onWarning.call(element, module.value, module.total);
  456. },
  457. error : function(text) {
  458. text = text || settings.text.error;
  459. module.debug('Setting error state');
  460. $module.addClass(className.error);
  461. module.remove.active();
  462. module.remove.success();
  463. module.remove.warning();
  464. module.complete();
  465. if(text) {
  466. module.set.label(text);
  467. }
  468. settings.onError.call(element, module.value, module.total);
  469. },
  470. transitionEvent: function() {
  471. transitionEnd = module.get.transitionEnd();
  472. },
  473. total: function(totalValue) {
  474. module.total = totalValue;
  475. },
  476. value: function(value) {
  477. module.value = value;
  478. },
  479. progress: function(value) {
  480. var
  481. numericValue = module.get.numericValue(value),
  482. percentComplete
  483. ;
  484. if(numericValue === false) {
  485. module.error(error.nonNumeric, value);
  486. }
  487. if( module.has.total() ) {
  488. module.set.value(numericValue);
  489. percentComplete = (numericValue / module.total) * 100;
  490. module.debug('Calculating percent complete from total', percentComplete);
  491. module.set.percent( percentComplete );
  492. }
  493. else {
  494. percentComplete = numericValue;
  495. module.debug('Setting value to exact percentage value', percentComplete);
  496. module.set.percent( percentComplete );
  497. }
  498. }
  499. },
  500. setting: function(name, value) {
  501. module.debug('Changing setting', name, value);
  502. if( $.isPlainObject(name) ) {
  503. $.extend(true, settings, name);
  504. }
  505. else if(value !== undefined) {
  506. settings[name] = value;
  507. }
  508. else {
  509. return settings[name];
  510. }
  511. },
  512. internal: function(name, value) {
  513. if( $.isPlainObject(name) ) {
  514. $.extend(true, module, name);
  515. }
  516. else if(value !== undefined) {
  517. module[name] = value;
  518. }
  519. else {
  520. return module[name];
  521. }
  522. },
  523. debug: function() {
  524. if(settings.debug) {
  525. if(settings.performance) {
  526. module.performance.log(arguments);
  527. }
  528. else {
  529. module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
  530. module.debug.apply(console, arguments);
  531. }
  532. }
  533. },
  534. verbose: function() {
  535. if(settings.verbose && settings.debug) {
  536. if(settings.performance) {
  537. module.performance.log(arguments);
  538. }
  539. else {
  540. module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
  541. module.verbose.apply(console, arguments);
  542. }
  543. }
  544. },
  545. error: function() {
  546. module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
  547. module.error.apply(console, arguments);
  548. },
  549. performance: {
  550. log: function(message) {
  551. var
  552. currentTime,
  553. executionTime,
  554. previousTime
  555. ;
  556. if(settings.performance) {
  557. currentTime = new Date().getTime();
  558. previousTime = time || currentTime;
  559. executionTime = currentTime - previousTime;
  560. time = currentTime;
  561. performance.push({
  562. 'Name' : message[0],
  563. 'Arguments' : [].slice.call(message, 1) || '',
  564. 'Element' : element,
  565. 'Execution Time' : executionTime
  566. });
  567. }
  568. clearTimeout(module.performance.timer);
  569. module.performance.timer = setTimeout(module.performance.display, 500);
  570. },
  571. display: function() {
  572. var
  573. title = settings.name + ':',
  574. totalTime = 0
  575. ;
  576. time = false;
  577. clearTimeout(module.performance.timer);
  578. $.each(performance, function(index, data) {
  579. totalTime += data['Execution Time'];
  580. });
  581. title += ' ' + totalTime + 'ms';
  582. if(moduleSelector) {
  583. title += ' \'' + moduleSelector + '\'';
  584. }
  585. if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
  586. console.groupCollapsed(title);
  587. if(console.table) {
  588. console.table(performance);
  589. }
  590. else {
  591. $.each(performance, function(index, data) {
  592. console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
  593. });
  594. }
  595. console.groupEnd();
  596. }
  597. performance = [];
  598. }
  599. },
  600. invoke: function(query, passedArguments, context) {
  601. var
  602. object = instance,
  603. maxDepth,
  604. found,
  605. response
  606. ;
  607. passedArguments = passedArguments || queryArguments;
  608. context = element || context;
  609. if(typeof query == 'string' && object !== undefined) {
  610. query = query.split(/[\. ]/);
  611. maxDepth = query.length - 1;
  612. $.each(query, function(depth, value) {
  613. var camelCaseValue = (depth != maxDepth)
  614. ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
  615. : query
  616. ;
  617. if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
  618. object = object[camelCaseValue];
  619. }
  620. else if( object[camelCaseValue] !== undefined ) {
  621. found = object[camelCaseValue];
  622. return false;
  623. }
  624. else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
  625. object = object[value];
  626. }
  627. else if( object[value] !== undefined ) {
  628. found = object[value];
  629. return false;
  630. }
  631. else {
  632. module.error(error.method, query);
  633. return false;
  634. }
  635. });
  636. }
  637. if ( $.isFunction( found ) ) {
  638. response = found.apply(context, passedArguments);
  639. }
  640. else if(found !== undefined) {
  641. response = found;
  642. }
  643. if($.isArray(returnedValue)) {
  644. returnedValue.push(response);
  645. }
  646. else if(returnedValue !== undefined) {
  647. returnedValue = [returnedValue, response];
  648. }
  649. else if(response !== undefined) {
  650. returnedValue = response;
  651. }
  652. return found;
  653. }
  654. };
  655. if(methodInvoked) {
  656. if(instance === undefined) {
  657. module.initialize();
  658. }
  659. module.invoke(query);
  660. }
  661. else {
  662. if(instance !== undefined) {
  663. instance.invoke('destroy');
  664. }
  665. module.initialize();
  666. }
  667. })
  668. ;
  669. return (returnedValue !== undefined)
  670. ? returnedValue
  671. : this
  672. ;
  673. };
  674. $.fn.progress.settings = {
  675. name : 'Progress',
  676. namespace : 'progress',
  677. debug : true,
  678. verbose : false,
  679. performance : true,
  680. random : {
  681. min : 2,
  682. max : 5
  683. },
  684. duration : 300,
  685. autoSuccess : true,
  686. showActivity : true,
  687. limitValues : true,
  688. label : 'percent',
  689. precision : 0,
  690. framerate : (1000 / 30), /// 30 fps
  691. percent : false,
  692. total : false,
  693. value : false,
  694. onChange : function(percent, value, total){},
  695. onSuccess : function(total){},
  696. onActive : function(value, total){},
  697. onError : function(value, total){},
  698. onWarning : function(value, total){},
  699. error : {
  700. method : 'The method you called is not defined.',
  701. nonNumeric : 'Progress value is non numeric',
  702. tooHigh : 'Value specified is above 100%',
  703. tooLow : 'Value specified is below 0%'
  704. },
  705. regExp: {
  706. variable: /\{\$*[A-z0-9]+\}/g
  707. },
  708. metadata: {
  709. percent : 'percent',
  710. total : 'total',
  711. value : 'value'
  712. },
  713. selector : {
  714. bar : '> .bar',
  715. label : '> .label',
  716. progress : '.bar > .progress'
  717. },
  718. text : {
  719. active : false,
  720. error : false,
  721. success : false,
  722. warning : false,
  723. percent : '{percent}%',
  724. ratio : '{value} of {total}'
  725. },
  726. className : {
  727. active : 'active',
  728. error : 'error',
  729. success : 'success',
  730. warning : 'warning'
  731. }
  732. };
  733. })( jQuery, window , document );