Core.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. import Utils from '../core/Utils'
  2. import Translator from '../core/Translator'
  3. import ee from 'event-emitter'
  4. /**
  5. * Main Uppy core
  6. *
  7. * @param {object} opts general options, like locales, to show modal or not to show
  8. */
  9. export default class Core {
  10. constructor (opts) {
  11. // set default options
  12. const defaultOptions = {
  13. // load English as the default locales
  14. locales: require('../locales/en_US.js'),
  15. autoProceed: true,
  16. debug: false
  17. }
  18. // Merge default options with the ones set by user
  19. this.opts = Object.assign({}, defaultOptions, opts)
  20. // Dictates in what order different plugin types are ran:
  21. this.types = [ 'presetter', 'view', 'progress', 'acquire', 'uploader', 'presenter' ]
  22. this.type = 'core'
  23. // Container for different types of plugins
  24. this.plugins = {}
  25. this.translator = new Translator({locales: this.opts.locales})
  26. this.i18n = this.translator.translate.bind(this.translator)
  27. // console.log(this.i18n('filesChosen', {smart_count: 3}))
  28. // Set up an event EventEmitter
  29. this.emitter = ee()
  30. }
  31. /**
  32. * Registers a plugin with Core
  33. *
  34. * @param {Class} Plugin object
  35. * @param {Object} options object that will be passed to Plugin later
  36. * @return {Object} self for chaining
  37. */
  38. use (Plugin, opts) {
  39. // Instantiate
  40. const plugin = new Plugin(this, opts)
  41. this.plugins[plugin.type] = this.plugins[plugin.type] || []
  42. if (!plugin.constructor.name) {
  43. throw new Error('Your plugin must have a name')
  44. }
  45. if (!plugin.type) {
  46. throw new Error('Your plugin must have a type')
  47. }
  48. let existsPluginAlready = this.getPlugin(plugin.constructor.name)
  49. if (existsPluginAlready) {
  50. let msg = `Already found a plugin named '${existsPluginAlready.name}'. `
  51. msg += `Tried to use: '${plugin.constructor.name}'. `
  52. msg += 'Uppy is currently limited to running one of every plugin. '
  53. msg += 'Share your use case with us over at '
  54. msg += 'https://github.com/transloadit/uppy/issues/ '
  55. msg += 'if you want us to reconsider. '
  56. throw new Error(msg)
  57. }
  58. this.plugins[plugin.type].push(plugin)
  59. return this
  60. }
  61. /**
  62. * Find one Plugin by name
  63. *
  64. * @param string name description
  65. */
  66. getPlugin (name) {
  67. let foundPlugin = false
  68. this.iteratePlugins(plugin => {
  69. if (plugin.constructor.name === name) {
  70. foundPlugin = plugin
  71. return false
  72. }
  73. })
  74. return foundPlugin
  75. }
  76. /**
  77. * Iterate through all `use`d plugins
  78. *
  79. * @param function method description
  80. */
  81. iteratePlugins (method) {
  82. Object.keys(this.plugins).forEach(pluginType => {
  83. this.plugins[pluginType].forEach(method)
  84. })
  85. }
  86. /**
  87. * Sets plugin’s progress, like for uploads
  88. *
  89. * @param {object} plugin that wants to set progress
  90. * @param {integer} percentage
  91. * @return {object} self for chaining
  92. */
  93. setProgress (plugin, percentage) {
  94. // Any plugin can call this via `this.core.setProgress(this, precentage)`
  95. console.log(plugin.type + ' plugin ' + plugin.name + ' set the progress to ' + percentage)
  96. return this
  97. }
  98. /**
  99. * Logs stuff to console, only if `debug` is set to true. Silent in production.
  100. *
  101. * @return {String|Object} to log
  102. */
  103. log (msg) {
  104. if (!this.opts.debug) {
  105. return
  106. }
  107. if (msg === `${msg}`) {
  108. console.log(`DEBUG LOG: ${msg}`)
  109. } else {
  110. console.log(`DEBUG LOG`)
  111. console.dir(msg)
  112. }
  113. }
  114. /**
  115. * Runs all plugins of the same type in parallel
  116. *
  117. * @param {string} type that wants to set progress
  118. * @param {array} files
  119. * @return {Promise} of all methods
  120. */
  121. runType (type, method, files) {
  122. const methods = this.plugins[type].map(
  123. plugin => plugin[method](files)
  124. )
  125. return Promise.all(methods)
  126. .catch(error => console.error(error))
  127. }
  128. /**
  129. * Runs a waterfall of runType plugin packs, like so:
  130. * All preseters(data) --> All acquires(data) --> All uploaders(data) --> done
  131. */
  132. run () {
  133. this.log({
  134. class: this.constructor.name,
  135. method: 'run'
  136. })
  137. // Forse set `autoProceed` option to false if there are multiple selector Plugins active
  138. if (this.plugins.acquire && this.plugins.acquire.length > 1) {
  139. this.opts.autoProceed = false
  140. }
  141. // Each Plugin can have `run` and/or `install` methods.
  142. // `install` adds event listeners and does some non-blocking work, useful for `progress`,
  143. // `run` waits for the previous step to finish (user selects files) before proceeding
  144. ['install', 'run'].forEach(method => {
  145. // First we select only plugins of current type,
  146. // then create an array of runType methods of this plugins
  147. const typeMethods = this.types.filter(type => {
  148. return this.plugins[type]
  149. }).map(type => this.runType.bind(this, type, method))
  150. // Run waterfall of typeMethods
  151. return Utils.promiseWaterfall(typeMethods)
  152. .then(result => { return result })
  153. .catch(error => console.error(error))
  154. })
  155. }
  156. }