├── .bowerrc ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── bower.json ├── coffeelint.json ├── gulpfile.coffee ├── gulpfile.js ├── package.json ├── src └── index.coffee ├── standalone └── react-loadqueueloader.js ├── test ├── index.html └── tests.coffee ├── vendor └── react │ └── react-with-addons.js └── webpack.config.coffee /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "vendor" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /test/tests.js 3 | /lib/ 4 | /vendor/react/*.json 5 | /vendor/react/*.min.js 6 | /vendor/react/*.min.js 7 | /vendor/react/react.js 8 | /vendor/react/JSXTransformer.js 9 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /gulpfile.* 3 | /test/ 4 | /vendor/ 5 | /bower.json 6 | /coffeelint.json 7 | /src/ 8 | /standalone/ 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2014 HZDG 2 | http://hzdg.com 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | react-loadqueueloader 2 | ===================== 3 | 4 | Sometimes you want to take the decisions about what to load and when from the 5 | browser, but still reap the benefits of queuing and prioritizing that the 6 | browser is capable of. A load queue (such as [queueup.js]) allows you to manage 7 | and prioritize loads in just such a way, and this [React] component allows an 8 | easy way of hooking asset loading for components into a load queue. 9 | 10 | 11 | Basic Usage 12 | ----------- 13 | 14 | ```javascript 15 | var LoadQueueLoader = require('react-loadqueueloader'); 16 | 17 | // ... 18 | 19 | 24 | 25 | ``` 26 | 27 | While the above example nicely illustrates basic usage, it neglects a crucial 28 | piece of the puzzle: the load queue! 29 | 30 | 31 | Load Queue 32 | ---------- 33 | 34 | When you render the LoadQueueLoader component, you want to do so with a 35 | `loadQueue` (such as [queueup.js]) in context (using `React.withContext`). 36 | For example: 37 | 38 | ```javascript 39 | var LoadQueueLoader = require('react-loadqueueloader'); 40 | var queue = require('queueup')(); 41 | var LoadQueue = React.createClass({ 42 | render: function() { 43 | React.withContext({loadQueue: queue},
{ this.props.children }
); 44 | } 45 | }); 46 | 47 | // ... 48 | 49 | 50 | 51 | 52 | 53 | ``` 54 | 55 | 56 | Context 57 | ------- 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 74 | 75 | 76 |
NameTypeDescription
loadQueueobjectAn object that manages loads in a queue. It is expected to have an 70 | enqueue method that takes a function that performs the 71 | load. When the load queue is ready to load an asset, it should call 72 | the provided function, passing it a callback. That callback will be 73 | called when the load completes or errors.
77 | 78 | 79 | Props 80 | ----- 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 |
NameTypeDescription
loaderfunctionA React class or other function that returns a component instance 93 | that loads a src. The instance should also accept 94 | onLoad and onError callbacks. Required.
srcstringThe URL of the image to be loaded.
prioritynumberThe priority to assign to this load, relative to other loads in the 105 | queue. This prop has no effect if there is no loadQueue 106 | in the component context. Defaults to 0
onLoadfunctionAn optional callback to be called when the load finishes.
onErrorfunctionAn optional callback to be called if the load fails.
120 | 121 | 122 | [React]: http://facebook.github.io/react/ 123 | [queueup.js]: http://github.com/hzdg/queueup.js/ 124 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-loadqueueloader", 3 | "version": "1.0.3", 4 | "authors": [ 5 | "Eric Eldredge " 6 | ], 7 | "description": "A React component for managing loads with a load queue", 8 | "main": "./standalone/react-loadqueueloader.js", 9 | "keywords": [ 10 | "react-component", 11 | "react", 12 | "loader", 13 | "load", 14 | "queue", 15 | "queueup", 16 | "component" 17 | ], 18 | "license": "MIT", 19 | "homepage": "https://github.com/hzdg/react-loadqueueloader", 20 | "ignore": [ 21 | "**/.*", 22 | "node_modules", 23 | "vendor", 24 | "test" 25 | ], 26 | "devDependencies": { 27 | "react": "~0.10.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /coffeelint.json: -------------------------------------------------------------------------------- 1 | { 2 | "coffeescript_error": { 3 | "level": "error" 4 | }, 5 | "arrow_spacing": { 6 | "name": "arrow_spacing", 7 | "level": "error" 8 | }, 9 | "no_tabs": { 10 | "name": "no_tabs", 11 | "level": "error" 12 | }, 13 | "no_trailing_whitespace": { 14 | "name": "no_trailing_whitespace", 15 | "level": "error", 16 | "allowed_in_comments": false, 17 | "allowed_in_empty_lines": false 18 | }, 19 | "max_line_length": { 20 | "name": "max_line_length", 21 | "value": 80, 22 | "level": "ignore", 23 | "limitComments": true 24 | }, 25 | "line_endings": { 26 | "name": "line_endings", 27 | "level": "error", 28 | "value": "unix" 29 | }, 30 | "no_trailing_semicolons": { 31 | "name": "no_trailing_semicolons", 32 | "level": "error" 33 | }, 34 | "indentation": { 35 | "name": "indentation", 36 | "value": 2, 37 | "level": "error" 38 | }, 39 | "camel_case_classes": { 40 | "name": "camel_case_classes", 41 | "level": "error" 42 | }, 43 | "colon_assignment_spacing": { 44 | "name": "colon_assignment_spacing", 45 | "level": "error", 46 | "spacing": { 47 | "left": 0, 48 | "right": 1 49 | } 50 | }, 51 | "no_implicit_braces": { 52 | "name": "no_implicit_braces", 53 | "level": "ignore", 54 | "strict": true 55 | }, 56 | "no_plusplus": { 57 | "name": "no_plusplus", 58 | "level": "error" 59 | }, 60 | "no_throwing_strings": { 61 | "name": "no_throwing_strings", 62 | "level": "error" 63 | }, 64 | "no_backticks": { 65 | "name": "no_backticks", 66 | "level": "error" 67 | }, 68 | "no_implicit_parens": { 69 | "name": "no_implicit_parens", 70 | "level": "ignore" 71 | }, 72 | "no_empty_param_list": { 73 | "name": "no_empty_param_list", 74 | "level": "error" 75 | }, 76 | "no_stand_alone_at": { 77 | "name": "no_stand_alone_at", 78 | "level": "error" 79 | }, 80 | "space_operators": { 81 | "name": "space_operators", 82 | "level": "error" 83 | }, 84 | "duplicate_key": { 85 | "name": "duplicate_key", 86 | "level": "error" 87 | }, 88 | "empty_constructor_needs_parens": { 89 | "name": "empty_constructor_needs_parens", 90 | "level": "ignore" 91 | }, 92 | "cyclomatic_complexity": { 93 | "name": "cyclomatic_complexity", 94 | "value": 10, 95 | "level": "ignore" 96 | }, 97 | "newlines_after_classes": { 98 | "name": "newlines_after_classes", 99 | "value": 2, 100 | "level": "warn" 101 | }, 102 | "no_unnecessary_fat_arrows": { 103 | "name": "no_unnecessary_fat_arrows", 104 | "level": "error" 105 | }, 106 | "missing_fat_arrows": { 107 | "name": "missing_fat_arrows", 108 | "level": "ignore" 109 | }, 110 | "non_empty_constructor_needs_parens": { 111 | "name": "non_empty_constructor_needs_parens", 112 | "level": "ignore" 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /gulpfile.coffee: -------------------------------------------------------------------------------- 1 | gulp = require 'gulp' 2 | chalk = require 'chalk' 3 | gbump = require 'gulp-bump' 4 | coffee = require 'gulp-coffee' 5 | rename = require 'gulp-rename' 6 | connect = require 'gulp-connect' 7 | open = require 'open' 8 | path = require 'path' 9 | webpack = require 'webpack' 10 | webpackConfig = require './webpack.config' 11 | 12 | yargs = require 'yargs' 13 | .wrap 80 14 | .alias 'h', 'help' 15 | .describe 'help', 'Show this help information.' 16 | .check (argv) -> if argv.help then throw new Error 'Help!' 17 | 18 | 19 | log = (plugin, msg) -> console.log chalk.reset("[#{chalk.green plugin}]"), msg 20 | log.warn = (plugin, msg) -> console.warn chalk.reset("[#{chalk.yellow plugin}]"), msg 21 | log.error = (plugin, error) -> 22 | prefix = chalk.reset "[#{chalk.red plugin}]" 23 | if error.stack 24 | console.error prefix, line for line in error.stack.split '\n' 25 | else console.error prefix, error.message or error 26 | 27 | 28 | gulp.task 'build:node', -> 29 | gulp.src './src/*.?(lit)coffee' 30 | .pipe coffee bare: true 31 | .on 'error', (error) -> 32 | log.error 'coffee', error 33 | @end() 34 | .pipe gulp.dest './lib' 35 | 36 | 37 | gulp.task 'build:browser', (done) -> 38 | web = webpack webpackConfig 39 | .run (err, stats) -> 40 | if err then log.error('webpack', err) and throw err 41 | log 'webpack', line for line in stats 42 | .toString colors: true, chunks: false 43 | .split '\n' 44 | connect.reload() 45 | done() 46 | 47 | 48 | gulp.task 'build:tests', -> 49 | gulp.src './test/**/*.?(lit)coffee' 50 | .pipe coffee() 51 | .on 'error', (error) -> 52 | log.error 'coffee', error 53 | @end() 54 | .pipe gulp.dest('./test/') 55 | .pipe connect.reload() 56 | 57 | 58 | gulp.task 'build', ['build:node', 'build:browser'] 59 | 60 | 61 | gulp.task 'bump', -> 62 | argv = yargs 63 | .usage ''' 64 | 65 | Bump the package version. 66 | 67 | With no options, bumps to the next patch version. 68 | 69 | Usage: gulp bump [--major|--minor|--patch|--to x.x.x] 70 | ''' 71 | .options 72 | major: 73 | describe: 'Bump the package to the next major version (1.x.x to 2.0.0)' 74 | minor: 75 | describe: 'Bump the package to the next minor version (1.0.x to 1.1.0)' 76 | patch: 77 | describe: 'Bump the package to the next patch version (1.0.0 to 1.0.1)' 78 | to: 79 | describe: 'Bump the package to the specified version' 80 | .check (argv) -> 81 | if argv.major and argv.minor 82 | throw new Error 'Cannot specify both major and minor' 83 | else if argv.major and argv.patch 84 | throw new Error 'Cannot specify both major and patch' 85 | else if argv.major and argv.to 86 | throw new Error 'Cannot specify both major and version' 87 | else if argv.minor and argv.patch 88 | throw new Error 'Cannot specify both minor and patch' 89 | else if argv.minor and argv.to 90 | throw new Error 'Cannot specify both minor and version' 91 | else if argv.patch and argv.to 92 | throw new Error 'Cannot specify both patch and version' 93 | .argv 94 | 95 | opts = 96 | if argv.to 97 | version: argv.to 98 | else if argv.major 99 | type: 'major' 100 | else if argv.minor 101 | type: 'minor' 102 | else 103 | type: 'patch' 104 | 105 | gulp.src ['./bower.json', './package.json'] 106 | .pipe gbump opts 107 | .pipe gulp.dest './' 108 | 109 | 110 | # A server for the test page 111 | gulp.task 'testserver', -> 112 | connect.server opts = 113 | root: __dirname 114 | host: 'localhost' 115 | port: 1337 116 | livereload: true 117 | url = "http://#{opts.host}:#{opts.port}/test/index.html" 118 | browser = 'Google Chrome' 119 | open url, browser, (error) -> 120 | if error 121 | log.error 'open', error 122 | else 123 | log 'open', "Opened #{chalk.magenta url} in #{chalk.green browser}" 124 | file: 'index.html' 125 | 126 | 127 | gulp.task 'test', ['build:browser', 'build:tests', 'testserver'] 128 | 129 | 130 | gulp.task 'watch', -> 131 | gulp.watch './src/**/*.?(lit)coffee', ['build'] 132 | gulp.watch './test/**/*.?(lit)coffee', ['build:tests'] 133 | 134 | 135 | gulp.task 'dev', ['test', 'watch'] 136 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | // A shim for loading the CoffeeScript gulpfile 2 | require('coffee-script/register'); 3 | require('./gulpfile.coffee'); 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-loadqueueloader", 3 | "version": "1.0.3", 4 | "description": "A React component for managing loads with a load queue", 5 | "main": "./lib/index.js", 6 | "scripts": { 7 | "prepublish": "gulp build", 8 | "test": "gulp test" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/hzdg/react-loadqueueloader.git" 13 | }, 14 | "keywords": [ 15 | "react-component", 16 | "react", 17 | "loader", 18 | "load", 19 | "queue", 20 | "queueup", 21 | "component" 22 | ], 23 | "author": "Eric Eldredge ", 24 | "license": "MIT", 25 | "bugs": { 26 | "url": "https://github.com/hzdg/react-loadqueueloader/issues" 27 | }, 28 | "homepage": "https://github.com/hzdg/react-loadqueueloader", 29 | "dependencies": {}, 30 | "devDependencies": { 31 | "coffee-script": "^1.7.1", 32 | "gulp-coffee": "^1.4.3", 33 | "gulp-bump": "^0.1.8", 34 | "gulp": "^3.6.2", 35 | "gulp-rename": "^1.2.0", 36 | "chalk": "^0.4.0", 37 | "yargs": "^1.2.2", 38 | "gulp-connect": "^2.0.5", 39 | "open": "0.0.5", 40 | "chai": "^1.9.1", 41 | "mocha": "^1.20.0", 42 | "envify": "^1.2.1", 43 | "react-loadermixin": "^1.0.4", 44 | "coffee-loader": "^0.7.2", 45 | "webpack": "^1.3.2-beta3" 46 | }, 47 | "peerDependencies": { 48 | "react": "*", 49 | "react-loadermixin": "*" 50 | }, 51 | "browserify-shim": { 52 | "react": "global:React" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/index.coffee: -------------------------------------------------------------------------------- 1 | React = require 'react' 2 | LoaderMixin = require 'react-loadermixin' 3 | 4 | {PropTypes} = React 5 | {span, img} = React.DOM 6 | 7 | Status = 8 | PENDING: 'pending' 9 | LOADING: 'loading' 10 | LOADED: 'loaded' 11 | FAILED: 'failed' 12 | 13 | ContextTypes = 14 | loadQueue: (props, propName, componentName) -> 15 | loadQueue = props[propName] 16 | if loadQueue? and typeof loadQueue['enqueue'] isnt 'function' 17 | console.warn "Context `#{propName}` must have an `enqueue` method for `#{componentName}`" 18 | return false 19 | true 20 | 21 | 22 | module.exports = LoadQueueLoader = React.createClass 23 | displayName: 'LoadQueueLoader' 24 | mixins: [LoaderMixin] 25 | propTypes: 26 | loader: PropTypes.func.isRequired 27 | priority: PropTypes.number 28 | contextTypes: 29 | loadQueue: ContextTypes.loadQueue 30 | getInitialState: -> 31 | status: Status.PENDING 32 | getDefaultProps: -> 33 | priority: 0 34 | componentDidMount: -> 35 | return unless @state.status is Status.PENDING 36 | if @props.src? 37 | @enqueueLoad @props.src, @props.priority 38 | componentWillUnmount: -> 39 | @state.loadResult?.cancel?() 40 | componentWillReceiveProps: (nextProps) -> 41 | # If a new `src` has been provided, try to cancel a pending load (if there 42 | # is one), and initiate a new load. Otherwise, if a new `priorty` has been 43 | # provided, try to update the existing load's priority. Both cancelling and 44 | # priority are dependent on a `loadQueue` being in context. 45 | if nextProps.src isnt @props.src 46 | @state.loadResult?.cancel?() 47 | @enqueueLoad nextProps.src, nextProps.priority 48 | else if nextProps.priority isnt @props.priority 49 | @state.loadResult?.priority nextProps.priority 50 | enqueueLoad: (src, priority) -> 51 | if @context?.loadQueue? 52 | loader = (callback) => 53 | # If the component has been unmounted since the load was enqueued, don't 54 | # bother starting the load now. 55 | return unless @isMounted() 56 | @setState 57 | status: Status.LOADING 58 | load: {src, callback} 59 | loadResult = @context.loadQueue.enqueue loader, {priority} 60 | @setState {loadResult} 61 | else 62 | @setState 63 | status: Status.LOADING 64 | load: {src} 65 | loaderDidLoad: (args...) -> 66 | @state.load.callback? null, args... 67 | # If the component has been unmounted since the load was enqueued, don't 68 | # bother handling the load now. 69 | return unless @isMounted() 70 | @setState status: Status.LOADED 71 | loaderDidError: (args...) -> 72 | @state.load.callback? args... 73 | # If the component has been unmounted since the load was enqueued, don't 74 | # bother handling the error now. 75 | return unless @isMounted() 76 | @setState status: Status.FAILED 77 | render: -> 78 | if not @context.loadQueue or @state.load? then @renderLoader @props.loader 79 | else @props.loader() 80 | -------------------------------------------------------------------------------- /standalone/react-loadqueueloader.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("React")):"function"==typeof define&&define.amd?define(["React"],e):"object"==typeof exports?exports.ReactLoadQueueLoader=e(require("React")):t.ReactLoadQueueLoader=e(t.React)}(this,function(t){return function(t){function e(r){if(o[r])return o[r].exports;var n=o[r]={exports:{},id:r,loaded:!1};return t[r].call(n.exports,n,n.exports,e),n.loaded=!0,n.exports}var o={};return e.m=t,e.c=o,e.p="",e(0)}([function(t,e,o){var r,n,a,u,i,s,l,c,p,d=[].slice;i=o(1),a=o(2),u=i.PropTypes,p=i.DOM,c=p.span,l=p.img,s={PENDING:"pending",LOADING:"loading",LOADED:"loaded",FAILED:"failed"},r={loadQueue:function(t,e,o){var r;return r=t[e],null!=r&&"function"!=typeof r.enqueue?(console.warn("Context `"+e+"` must have an `enqueue` method for `"+o+"`"),!1):!0}},t.exports=n=i.createClass({displayName:"LoadQueueLoader",mixins:[a],propTypes:{loader:u.func.isRequired,priority:u.number},contextTypes:{loadQueue:r.loadQueue},getInitialState:function(){return{status:s.PENDING}},getDefaultProps:function(){return{priority:0}},componentDidMount:function(){return this.state.status===s.PENDING&&null!=this.props.src?this.enqueueLoad(this.props.src,this.props.priority):void 0},componentWillUnmount:function(){var t;return null!=(t=this.state.loadResult)&&"function"==typeof t.cancel?t.cancel():void 0},componentWillReceiveProps:function(t){var e,o;return t.src!==this.props.src?(null!=(e=this.state.loadResult)&&"function"==typeof e.cancel&&e.cancel(),this.enqueueLoad(t.src,t.priority)):t.priority!==this.props.priority&&null!=(o=this.state.loadResult)?o.priority(t.priority):void 0},enqueueLoad:function(t,e){var o,r,n;return null!=(null!=(n=this.context)?n.loadQueue:void 0)?(r=function(e){return function(o){return e.isMounted()?e.setState({status:s.LOADING,load:{src:t,callback:o}}):void 0}}(this),o=this.context.loadQueue.enqueue(r,{priority:e}),this.setState({loadResult:o})):this.setState({status:s.LOADING,load:{src:t}})},loaderDidLoad:function(){var t,e;return t=1<=arguments.length?d.call(arguments,0):[],"function"==typeof(e=this.state.load).callback&&e.callback.apply(e,[null].concat(d.call(t))),this.isMounted()?this.setState({status:s.LOADED}):void 0},loaderDidError:function(){var t,e;return t=1<=arguments.length?d.call(arguments,0):[],"function"==typeof(e=this.state.load).callback&&e.callback.apply(e,t),this.isMounted()?this.setState({status:s.FAILED}):void 0},render:function(){return this.context.loadQueue&&null==this.state.load?this.props.loader():this.renderLoader(this.props.loader)}})},function(e){e.exports=t},function(t,e,o){var r,n,a,u,i=[].slice;n=o(1),u=o(3),r=n.PropTypes,t.exports=a={propTypes:{src:r.string,onLoad:r.func,onError:r.func},renderLoader:function(t,e){return t(u(e,{src:this.props.src,onLoad:function(t){return function(){var e,o;return e=1<=arguments.length?i.call(arguments,0):[],"function"==typeof t.loaderDidLoad&&t.loaderDidLoad.apply(t,e),"function"==typeof(o=t.props).onLoad?o.onLoad.apply(o,e):void 0}}(this),onError:function(t){return function(){var e,o;return e=1<=arguments.length?i.call(arguments,0):[],"function"==typeof t.loaderDidError&&t.loaderDidError.apply(t,e),"function"==typeof(o=t.props).onError?o.onError.apply(o,e):void 0}}(this)}))}}},function(t){function e(){for(var t={},e=0;e 2 | 3 | 4 | Mocha 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/tests.coffee: -------------------------------------------------------------------------------- 1 | {assert} = chai 2 | {img} = React.DOM 3 | 4 | 5 | describe 'ReactLoadQueueLoader', -> 6 | div = null 7 | queue = null 8 | component = null 9 | 10 | 11 | makeLoadQueueLoaderClass = (queue) -> 12 | React.createClass 13 | getDefaultProps: -> loader: img 14 | renderLoader: -> 15 | @transferPropsTo (ReactLoadQueueLoader null) 16 | render: -> React.withContext loadQueue: queue, @renderLoader 17 | 18 | 19 | class LoadResult 20 | constructor: (@loader, @_priority = 0) -> 21 | then: -> this 22 | priority: (value) -> if value? then @_priority = value else @_priority 23 | 24 | 25 | class LoadQueue 26 | constructor: -> 27 | @_loadResults = [] 28 | @enqueue.callCount = 0 29 | enqueue: (loader, opts) -> 30 | @enqueue.callCount += 1 31 | @_loadResults.push loadResult = new LoadResult loader, opts?.priority 32 | loadResult 33 | run: -> loader @run if {loader} = @_loadResults.shift() 34 | 35 | 36 | beforeEach -> 37 | div = document.createElement 'div' 38 | component = makeLoadQueueLoaderClass queue = new LoadQueue() 39 | 40 | it 'queues a load', -> 41 | React.renderComponent (component src: 'test.png'), div 42 | assert queue.enqueue.callCount is 1, 43 | 'Expected enquque to have been called once' 44 | 45 | it 'sets priority in the queue for a load', -> 46 | React.renderComponent (component src: 'test.png', priority: 1), div 47 | assert.equal queue._loadResults[0].priority(), 1, 48 | 'Expected priority to be 1' 49 | 50 | it 'updates load priority in the queue', -> 51 | React.renderComponent (component src: 'test.png'), div 52 | assert.equal queue._loadResults[0].priority(), 0, 53 | 'Expected priority to be 0' 54 | React.renderComponent (component src: 'test.png', priority: 1), div 55 | assert.equal queue._loadResults[0].priority(), 1, 56 | 'Expected priority to change to 1' 57 | 58 | it 'renders a loader not in a load queue with a src', (done) -> 59 | React.renderComponent (ReactLoadQueueLoader 60 | src: 'test.png' 61 | loader: (props) -> 62 | if props.src 63 | assert.equal props.src, 'test.png', 'Expected loader to have src' 64 | done() 65 | (img null) 66 | ), div 67 | 68 | it 'renders a loader in a load queue without a src', -> 69 | React.renderComponent (component null), div 70 | image = div.getElementsByTagName('img')[0] 71 | assert.notOk image.attributes.src, 'Expected loader not to have src' 72 | 73 | it 'renders a loader in a load queue with a src when dequeued', (done) -> 74 | React.renderComponent React.withContext( 75 | loadQueue: queue 76 | -> (ReactLoadQueueLoader 77 | src: 'test.png' 78 | loader: (props) -> 79 | if props?.src 80 | assert.equal props.src, 'test.png', 'Expected loader to have src' 81 | done() 82 | (img null) 83 | )), 84 | div 85 | image = div.getElementsByTagName('img')[0] 86 | assert.notOk image.attributes.src, 'Expected loader not to have src' 87 | queue.run() 88 | -------------------------------------------------------------------------------- /webpack.config.coffee: -------------------------------------------------------------------------------- 1 | path = require 'path' 2 | webpack = require 'webpack' 3 | 4 | module.exports = 5 | entry: path.join __dirname, 'src', 'index.coffee' 6 | output: 7 | path: path.join __dirname, 'standalone' 8 | filename: 'react-loadqueueloader.js' 9 | library: 'ReactLoadQueueLoader' 10 | libraryTarget: 'umd' 11 | target: 'web' 12 | externals: ['React', react: 'React'] 13 | module: 14 | loaders: [ 15 | {test: /\.coffee$/, loader: 'coffee'} 16 | ] 17 | plugins: [ 18 | new webpack.optimize.UglifyJsPlugin() 19 | ] 20 | --------------------------------------------------------------------------------