Progress Bar Lib

Create and manage simple progress bars, minimal JavaScript

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

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