ProgressBar

Create and manage simple progress bars, minimal JavaScript

当前为 2019-10-20 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/391236/742417/ProgressBar.js

  1. // ProgressBar library
  2. //
  3. // Create and manage simple progress bars. CSS, minimal JavaScript.
  4. //
  5. // https://greasyfork.org/scripts/391236-progressbar
  6. // Copyright (C) 2019, Guido Villa
  7. // Original version of the code is taken from IMDb 'My Movies' enhancer:
  8. // Copyright (C) 2008-2018, Ricardo Mendonça Ferreira (ric@mpcnet.com.br)
  9. // Released under the GPL license - http://www.gnu.org/copyleft/gpl.html
  10. //
  11. // For instructions on user scripts, see:
  12. // https://greasyfork.org/help/installing-user-scripts
  13. //
  14. // To use this library in a userscript you must add to script header:
  15. // @require https://greasyfork.org/scripts/391236-progressbar/code/ProgressBar.js
  16. // @grant GM_addStyle
  17. //
  18. // --------------------------------------------------------------------
  19. //
  20. // ==UserScript==
  21. // @namespace https://greasyfork.org/users/373199-guido-villa
  22. // @exclude *
  23. //
  24. // ==UserLibrary==
  25. // @name ProgressBar
  26. // @description Create and manage simple progress bars, minimal JavaScript
  27. // @version 1.1
  28. // @author guidovilla
  29. // @date 19.10.2019
  30. // @copyright 2019, Guido Villa (https://greasyfork.org/users/373199-guido-villa)
  31. // @license GPL-3.0-or-later
  32. // @homepageURL https://greasyfork.org/scripts/391236-progressbar
  33. // @supportURL https://gitlab.com/gv-browser/userscripts/issues
  34. // @contributionURL https://tinyurl.com/gv-donate-2e
  35. // @attribution Ricardo Mendonça Ferreira (https://openuserjs.org/users/AltoRetrato)
  36. // ==/UserScript==
  37. //
  38. // ==/UserLibrary==
  39. //
  40. // --------------------------------------------------------------------
  41. //
  42. // To-do (priority: [H]igh, [M]edium, [L]ow):
  43. // - [H] width must be an integer multiple of background-size, otherwise animation will skip => address
  44. // - [M] speed of the animation depends on width => fix?
  45. // - [m] speed of transition is not constant (time is constant, regardless of the "space" to be travelled) => can it be fixed?
  46. // - [H] wrap in order to hide global variables
  47. // - [M] nicer presentation style (maybe small vertical bars), graphical improvements
  48. // - [M] different styles
  49. // - [M] log if current > finish?
  50. // - [M] handle case finish == 0
  51. //
  52. // Changelog:
  53. // ----------
  54. // 2019.10.19 [1.1] Add possibility to update finish value
  55. // Change default value for "current" parameter in update()
  56. // 2019.10.16 [1.0] First version
  57. // 2019.10.14 [0.1] First test version, private use only
  58. //
  59.  
  60. /* jshint esversion: 6, supernew: true, laxbreak: true */
  61. /* exported EL, Library_Version_PROGRESSBAR */
  62.  
  63. const Library_Version_PROGRESSBAR = '1.1';
  64.  
  65. /* How to use the library
  66.  
  67. - Create a new progress bar:
  68. var pb = new ProgressBar(...)
  69.  
  70. - Change the progress:
  71. pb.update(...)
  72. pb.advance(...)
  73.  
  74. - Remove the progress bar:
  75. pb.close()
  76.  
  77. Details
  78. Progress bars are defined by three main parameters:
  79. - finish: value that defines what is 100%
  80. this is set at creation time and can be changed with update()
  81. - progress: value that defines current completion status (must be <= finish)
  82. initial progress is set a creation time, then it can be updated
  83. with update() and advance()
  84. When progress = -1, the bar shows an indeterminate progress
  85. - message: the message printed inside the bar (e.g. "Loading...")
  86. initial message is set a creation time, then it can be changed
  87. with update() and advance().
  88. The message can contain a few placeholders that are replaced with
  89. actual progress data:
  90. - {#}: replace with current progress number
  91. - {$}: replace with finish value
  92. - {%}: replace with completion percentage (= 100*progress/finish)
  93. E.g.: "Loading {#} of {$}..." => "Loading 7 of 23..."
  94.  
  95. All numbers are integers.
  96.  
  97. Information for changing styles:
  98. The HTML id of the container DIV can be accessed through the 'id' property
  99. of the progress bar object.
  100. All elements that constitute the bar have a generic "pb-progress-bar" class and
  101. a specific "pb-progress-bar-XXX" class different for each element.
  102. Indeterminate progress style is enabled by applying a "pb-indeterminate" class
  103. to the container DIV.
  104.  
  105. Parameters (all parameters are optional):
  106.  
  107. - ProgressBar(finish, msg, options)
  108. Create a new progress bar. Parameters:
  109. - finish: maximum value that can be reached (default is 100)
  110. - msg: message written in the bar, see above for substitutions
  111. default is "Loading {#}/{$}..."
  112. - options: an object that may contain:
  113. - id: HTML id of container DIV (default: autogenerated)
  114. - start: initial progress status (default is 0, i.e. the beginning)
  115. - container: positioned element where the bar will be centered
  116. null (the default): center bar on the screen
  117. - width: width in pixels of the progress bar (default is 226.3)
  118. - height: height in pixels of the progress bar (default is 30)
  119.  
  120. - update(progress, msg, finish)
  121. Optionally update parameters of the progress bar. Only non-undefined,
  122. non-null parameters are changed.
  123.  
  124. - advance(value, msg)
  125. Increment the progress bar status. Parameters:
  126. - value: the increment value, can be negative (default is 1)
  127. - msg: an optional new message (default is: don't change message)
  128.  
  129. - close()
  130. Close the progress bar and remove it from the DOM.
  131.  
  132. */
  133.  
  134.  
  135. var progress_bar_style_has_been_loaded = false;
  136. var progress_bar_index = 0;
  137. // Create progress bar
  138. // eslint-disable-next-line max-statements
  139. function ProgressBar(finish = 100, msg = 'Loading {#}/{$}...', options) {
  140. // style definition
  141. var STYLE = '.pb-progress-bar.pb-progress-bar-box{border:2px solid black;background-color:white;padding:4px;outline:white solid 6px;}'
  142. + '.pb-progress-bar.pb-progress-bar-bar{background-color:green;height:100%;transition:width 300ms linear;}'
  143. + '.pb-progress-bar.pb-progress-bar-txtcont{position:absolute;top:0;left:0;width:100%;height:100%;display:table;}'
  144. + '.pb-progress-bar.pb-progress-bar-txt{display:table-cell;text-align:center;vertical-align:middle;font:16px verdana,sans-serif;color:black;}'
  145. + '.pb-progress-bar.pb-progress-bar-box.pb-indeterminate{background:repeating-linear-gradient(-45deg,#F0F0F0 0 20px,#ccc 20px 40px);background-size:56.56854px;animation:2s linear infinite loading;}'
  146. + '.pb-progress-bar.pb-progress-bar-box.pb-indeterminate .pb-progress-bar-bar{background-color:transparent;transition:none}'
  147. + '@keyframes loading{from{background-position-x:0%;} to{background-position-x:100%;}}';
  148. if (!progress_bar_style_has_been_loaded) {
  149. GM_addStyle(STYLE);
  150. progress_bar_style_has_been_loaded = true;
  151. }
  152.  
  153. var self = this;
  154.  
  155. // basic configuration
  156. this.id = 'pb-progress-bar-' + ++progress_bar_index; // 'id' is public
  157. var start = 0;
  158. var container = null;
  159. var width = 226.27417;
  160. var height = 30;
  161. var message = msg;
  162.  
  163. var current; // completion status of the progress bar
  164.  
  165. var pbBox, pb, pbTxtCont, pbTxt; // elements of the progress bar
  166.  
  167. // helper function to create the elements
  168. function createElement(father, elementType, className, id) {
  169. var elem = document.createElement(elementType);
  170. if (typeof id !== 'undefined') elem.id = id;
  171. elem.className = 'pb-progress-bar ' + className;
  172. father.appendChild(elem);
  173. return elem;
  174. }
  175.  
  176. // initialization function
  177. function init() {
  178. // check for options in the call
  179. if (options && typeof options === 'object') {
  180. if (typeof options.id !== 'undefined') self.id = options.id;
  181. if (typeof options.start !== 'undefined') start = options.start;
  182. if (typeof options.container !== 'undefined') container = options.container;
  183. if (typeof options.width !== 'undefined') width = options.width;
  184. if (typeof options.height !== 'undefined') height = options.height;
  185. }
  186.  
  187. // calculate positioning
  188. var containerWidth, containerHeight,
  189. cntElem,
  190. positioningStyle;
  191.  
  192. function setPositioningVars(cnt, pos, w, h) {
  193. containerWidth = w;
  194. containerHeight = h;
  195. cntElem = cnt;
  196. positioningStyle = pos;
  197. }
  198.  
  199. if (container) {
  200. var rect = container.getBoundingClientRect();
  201. setPositioningVars(container, 'absolute', rect.width, rect.height);
  202. } else {
  203. setPositioningVars(document.body, 'fixed', window.innerWidth, window.innerHeight);
  204. }
  205. var top = containerHeight / 2 - height / 2;
  206. var left = containerWidth / 2 - width / 2;
  207.  
  208. // create the elements
  209. pbBox = createElement(cntElem, 'div', 'pb-progress-bar-box', self.id);
  210. pbBox.style.cssText = 'position:' + positioningStyle
  211. + '; height:' + height + 'px;width:' + width
  212. + 'px;top:' + top + 'px;left:' + left + 'px;';
  213.  
  214. pb = createElement(pbBox, 'div', 'pb-progress-bar-bar');
  215. pbTxtCont = createElement(pbBox, 'div', 'pb-progress-bar-txtcont');
  216. pbTxt = createElement(pbTxtCont, 'div', 'pb-progress-bar-txt');
  217.  
  218. // set the initial progress
  219. self.update(start);
  220. }
  221.  
  222.  
  223. /* PUBLIC members */
  224.  
  225. // optionally update progress status, message, finish
  226. this.update = function(currentVal, newMsg, newFinish) {
  227. if (newMsg) message = newMsg;
  228. if (typeof newFinish !== 'undefined' && newFinish !== null) finish = newFinish;
  229. var newVal;
  230. if (typeof currentVal !== 'undefined' && currentVal !== null) newVal = currentVal;
  231. else newVal = current;
  232. if (newVal > finish) newVal = finish;
  233.  
  234. if (newVal < 0) {
  235. // setting the width to zero is not really needed, but ensures a
  236. // more consistent behaviour in cases where the delay (see
  237. // below) is not enough.
  238. pb.style.width = '0';
  239. // try to make the message better for indeterminate progress
  240. pbTxt.textContent = message
  241. .replace(/ *{#}.*{\$} */g, '')
  242. .replace(/ *{#} */g, '')
  243. .replace(/ *{\$} */g, '')
  244. .replace(/ *{%} *%? */g, '');
  245. pbBox.classList.add('pb-indeterminate');
  246. } else {
  247. pb.style.width = (100*newVal/finish) + '%';
  248. if (current < 0) {
  249. // if exiting from indeterminate progress a small delay is
  250. // needed, otherwise the class may be removed when changing
  251. // the width, and the width transition takes place anyway
  252. setTimeout(function() {
  253. pbBox.classList.remove('pb-indeterminate');
  254. }, 33);
  255. } else {
  256. pbBox.classList.remove('pb-indeterminate');
  257. }
  258. // replace placeholders with actual numbers
  259. pbTxt.textContent = message
  260. .replace(/{#}/g, newVal)
  261. .replace(/{\$}/g, finish)
  262. .replace(/{%}/g, Math.round(100*newVal/finish));
  263. }
  264. current = newVal;
  265. };
  266.  
  267.  
  268. // advance the progress by "value" and optionally change the message
  269. this.advance = function(value = 1, newMsg) {
  270. self.update(current + value, newMsg);
  271. };
  272.  
  273.  
  274. // close/remove the progress bar
  275. this.close = function() {
  276. pbBox.parentNode.removeChild(pbBox);
  277. };
  278.  
  279.  
  280. /* INITIALIZATION */
  281. init();
  282. }